mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-14 08:05:50 +00:00
Compare commits
144 Commits
Bronek/add
...
a1q123456/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4001748ee9 | ||
|
|
f1482d332c | ||
|
|
373121ed78 | ||
|
|
17c10de2ea | ||
|
|
6de7802001 | ||
|
|
56a45506eb | ||
|
|
23029ab2b6 | ||
|
|
d1fe8ed31d | ||
|
|
129166cda5 | ||
|
|
6376f10df7 | ||
|
|
acafed7376 | ||
|
|
4feaa7b279 | ||
|
|
45a4f44dc1 | ||
|
|
211d90dadd | ||
|
|
a4498f084e | ||
|
|
294dae5766 | ||
|
|
d2f01eb755 | ||
|
|
a854a78107 | ||
|
|
c4047690e2 | ||
|
|
893632d330 | ||
|
|
f44d53be16 | ||
|
|
d33691da84 | ||
|
|
1dc3b256e0 | ||
|
|
2c2936fa93 | ||
|
|
dcec5a0bbc | ||
|
|
ce5a6aec7b | ||
|
|
79c3a83088 | ||
|
|
bd91ec7242 | ||
|
|
bb787e3995 | ||
|
|
0223443452 | ||
|
|
79e8c6a158 | ||
|
|
44aa394e1e | ||
|
|
5e16b3df62 | ||
|
|
2f6d133169 | ||
|
|
06c212495d | ||
|
|
9543ccf8e1 | ||
|
|
816089eab7 | ||
|
|
fa0cff3532 | ||
|
|
3ec7596170 | ||
|
|
28ad89ca20 | ||
|
|
e6c5f8338b | ||
|
|
4d0c0ca5c7 | ||
|
|
4f63747f33 | ||
|
|
1a2b7e9b94 | ||
|
|
c2aae2d846 | ||
|
|
458bd8a3bd | ||
|
|
cd8d5d97d1 | ||
|
|
bd7b098409 | ||
|
|
addfae1213 | ||
|
|
89ebb6b495 | ||
|
|
67aa3d5ac9 | ||
|
|
3b2edce813 | ||
|
|
1d3d0c6774 | ||
|
|
f50f76788b | ||
|
|
feae1d6e15 | ||
|
|
7debf3e9f4 | ||
|
|
90f970be46 | ||
|
|
5e060a9e7b | ||
|
|
dca000a60f | ||
|
|
7500d635bb | ||
|
|
3181042f15 | ||
|
|
157aa367f2 | ||
|
|
48cf042258 | ||
|
|
61ff2ba0e7 | ||
|
|
e19d770b86 | ||
|
|
a128571ab5 | ||
|
|
76bb517eb8 | ||
|
|
dc221de60c | ||
|
|
cdf1109558 | ||
|
|
0fe8f3f62d | ||
|
|
ab9e6563e4 | ||
|
|
d0f0789490 | ||
|
|
d36ef0cd18 | ||
|
|
a90bf169bf | ||
|
|
b3f389d918 | ||
|
|
d68f87f968 | ||
|
|
34127593e6 | ||
|
|
9e09595db0 | ||
|
|
856b36d0a5 | ||
|
|
9edba67e64 | ||
|
|
0e4f9a7ccf | ||
|
|
eda9bf1f1a | ||
|
|
43c6e202af | ||
|
|
e4db80f61d | ||
|
|
af9dde4f75 | ||
|
|
f6d7b90b70 | ||
|
|
1774769226 | ||
|
|
92312801f1 | ||
|
|
cfd26f444c | ||
|
|
2c3024716b | ||
|
|
a12f5de68d | ||
|
|
51c5f2bfc9 | ||
|
|
73ff54143d | ||
|
|
08b136528e | ||
|
|
6b8a589447 | ||
|
|
ffeabc9642 | ||
|
|
3cbdf818a7 | ||
|
|
bd834c87e0 | ||
|
|
dc8b37a524 | ||
|
|
617a895af5 | ||
|
|
1af1048c58 | ||
|
|
f07ba87e51 | ||
|
|
e66558a883 | ||
|
|
510314d344 | ||
|
|
37b951859c | ||
|
|
9494fc9668 | ||
|
|
8d01f35eb9 | ||
|
|
1020a32d76 | ||
|
|
17a2606591 | ||
|
|
ccb9f1e42d | ||
|
|
3e4e9a2ddc | ||
|
|
4caebfbd0e | ||
|
|
37c377a1b6 | ||
|
|
bd182c0a3e | ||
|
|
406c26cc72 | ||
|
|
9bd1ce436a | ||
|
|
f69ad4eff6 | ||
|
|
6fe0599cc2 | ||
|
|
e6f8bc720f | ||
|
|
fbd60fc000 | ||
|
|
61d628d654 | ||
|
|
3d92375d12 | ||
|
|
cdbe70b2a7 | ||
|
|
f6426ca183 | ||
|
|
e5f7a8442d | ||
|
|
e67e0395df | ||
|
|
148f669a25 | ||
|
|
f1eaa6a264 | ||
|
|
da4c8c9550 | ||
|
|
bcde2790a4 | ||
|
|
9ebeb413e4 | ||
|
|
6d40b882a4 | ||
|
|
9fe0a154f1 | ||
|
|
cb52c9af00 | ||
|
|
6bf8338038 | ||
|
|
b0f4174e47 | ||
|
|
3865dde0b8 | ||
|
|
811c980821 | ||
|
|
cf5f65b68e | ||
|
|
c38f2a3f2e | ||
|
|
16c2ff97cc | ||
|
|
32043463a8 | ||
|
|
724e9b1313 | ||
|
|
2e6f00aef2 |
@@ -33,5 +33,6 @@ slack_app: false
|
||||
|
||||
ignore:
|
||||
- "src/test/"
|
||||
- "src/tests/"
|
||||
- "include/xrpl/beast/test/"
|
||||
- "include/xrpl/beast/unit_test/"
|
||||
|
||||
33
.github/actions/build-deps/action.yml
vendored
33
.github/actions/build-deps/action.yml
vendored
@@ -1,7 +1,5 @@
|
||||
# This action installs and optionally uploads Conan dependencies to a remote
|
||||
# repository. The dependencies will only be uploaded if the credentials are
|
||||
# provided.
|
||||
name: Build Conan dependencies
|
||||
description: "Install Conan dependencies, optionally forcing a rebuild of all dependencies."
|
||||
|
||||
# Note that actions do not support 'type' and all inputs are strings, see
|
||||
# https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax#inputs.
|
||||
@@ -12,28 +10,10 @@ inputs:
|
||||
build_type:
|
||||
description: 'The build type to use ("Debug", "Release").'
|
||||
required: true
|
||||
conan_remote_name:
|
||||
description: "The name of the Conan remote to use."
|
||||
required: true
|
||||
conan_remote_url:
|
||||
description: "The URL of the Conan endpoint to use."
|
||||
required: true
|
||||
conan_remote_username:
|
||||
description: "The username for logging into the Conan remote. If not provided, the dependencies will not be uploaded."
|
||||
required: false
|
||||
default: ""
|
||||
conan_remote_password:
|
||||
description: "The password for logging into the Conan remote. If not provided, the dependencies will not be uploaded."
|
||||
required: false
|
||||
default: ""
|
||||
force_build:
|
||||
description: 'Force building of all dependencies ("true", "false").'
|
||||
required: false
|
||||
default: "false"
|
||||
force_upload:
|
||||
description: 'Force uploading of all dependencies ("true", "false").'
|
||||
required: false
|
||||
default: "false"
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
@@ -50,13 +30,4 @@ runs:
|
||||
--options:host '&:tests=True' \
|
||||
--options:host '&:xrpld=True' \
|
||||
--settings:all build_type=${{ inputs.build_type }} \
|
||||
--format=json ..
|
||||
- name: Upload Conan dependencies
|
||||
if: ${{ inputs.conan_remote_username != '' && inputs.conan_remote_password != '' }}
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.build_dir }}
|
||||
run: |
|
||||
echo "Logging into Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}."
|
||||
conan remote login ${{ inputs.conan_remote_name }} "${{ inputs.conan_remote_username }}" --password "${{ inputs.conan_remote_password }}"
|
||||
echo 'Uploading dependencies.'
|
||||
conan upload '*' --confirm --check ${{ inputs.force_upload == 'true' && '--force' || '' }} --remote=${{ inputs.conan_remote_name }}
|
||||
..
|
||||
|
||||
1
.github/actions/build-test/action.yml
vendored
1
.github/actions/build-test/action.yml
vendored
@@ -1,6 +1,7 @@
|
||||
# This action build and tests the binary. The Conan dependencies must have
|
||||
# already been installed (see the build-deps action).
|
||||
name: Build and Test
|
||||
description: "Build and test the binary."
|
||||
|
||||
# Note that actions do not support 'type' and all inputs are strings, see
|
||||
# https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax#inputs.
|
||||
|
||||
43
.github/actions/setup-conan/action.yml
vendored
Normal file
43
.github/actions/setup-conan/action.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: Setup Conan
|
||||
description: "Set up Conan configuration, profile, and remote."
|
||||
|
||||
inputs:
|
||||
conan_remote_name:
|
||||
description: "The name of the Conan remote to use."
|
||||
required: false
|
||||
default: xrplf
|
||||
conan_remote_url:
|
||||
description: "The URL of the Conan endpoint to use."
|
||||
required: false
|
||||
default: https://conan.ripplex.io
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
|
||||
steps:
|
||||
- name: Set up Conan configuration
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'Installing configuration.'
|
||||
cat conan/global.conf ${{ runner.os == 'Linux' && '>>' || '>' }} $(conan config home)/global.conf
|
||||
|
||||
echo 'Conan configuration:'
|
||||
conan config show '*'
|
||||
|
||||
- name: Set up Conan profile
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'Installing profile.'
|
||||
conan config install conan/profiles/default -tf $(conan config home)/profiles/
|
||||
|
||||
echo 'Conan profile:'
|
||||
conan profile show
|
||||
|
||||
- name: Set up Conan remote
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Adding Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}."
|
||||
conan remote add --index 0 --force ${{ inputs.conan_remote_name }} ${{ inputs.conan_remote_url }}
|
||||
|
||||
echo 'Listing Conan remotes.'
|
||||
conan remote list
|
||||
@@ -7,9 +7,6 @@ Loop: test.jtx test.unit_test
|
||||
Loop: xrpld.app xrpld.core
|
||||
xrpld.app > xrpld.core
|
||||
|
||||
Loop: xrpld.app xrpld.ledger
|
||||
xrpld.app > xrpld.ledger
|
||||
|
||||
Loop: xrpld.app xrpld.overlay
|
||||
xrpld.overlay > xrpld.app
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@ libxrpl.basics > xrpl.basics
|
||||
libxrpl.crypto > xrpl.basics
|
||||
libxrpl.json > xrpl.basics
|
||||
libxrpl.json > xrpl.json
|
||||
libxrpl.ledger > xrpl.basics
|
||||
libxrpl.ledger > xrpl.json
|
||||
libxrpl.ledger > xrpl.ledger
|
||||
libxrpl.ledger > xrpl.protocol
|
||||
libxrpl.net > xrpl.basics
|
||||
libxrpl.net > xrpl.net
|
||||
libxrpl.protocol > xrpl.basics
|
||||
@@ -21,11 +25,11 @@ test.app > test.unit_test
|
||||
test.app > xrpl.basics
|
||||
test.app > xrpld.app
|
||||
test.app > xrpld.core
|
||||
test.app > xrpld.ledger
|
||||
test.app > xrpld.nodestore
|
||||
test.app > xrpld.overlay
|
||||
test.app > xrpld.rpc
|
||||
test.app > xrpl.json
|
||||
test.app > xrpl.ledger
|
||||
test.app > xrpl.protocol
|
||||
test.app > xrpl.resource
|
||||
test.basics > test.jtx
|
||||
@@ -44,8 +48,8 @@ test.consensus > test.unit_test
|
||||
test.consensus > xrpl.basics
|
||||
test.consensus > xrpld.app
|
||||
test.consensus > xrpld.consensus
|
||||
test.consensus > xrpld.ledger
|
||||
test.consensus > xrpl.json
|
||||
test.consensus > xrpl.ledger
|
||||
test.core > test.jtx
|
||||
test.core > test.toplevel
|
||||
test.core > test.unit_test
|
||||
@@ -63,9 +67,9 @@ test.json > xrpl.json
|
||||
test.jtx > xrpl.basics
|
||||
test.jtx > xrpld.app
|
||||
test.jtx > xrpld.core
|
||||
test.jtx > xrpld.ledger
|
||||
test.jtx > xrpld.rpc
|
||||
test.jtx > xrpl.json
|
||||
test.jtx > xrpl.ledger
|
||||
test.jtx > xrpl.net
|
||||
test.jtx > xrpl.protocol
|
||||
test.jtx > xrpl.resource
|
||||
@@ -75,7 +79,7 @@ test.ledger > test.toplevel
|
||||
test.ledger > xrpl.basics
|
||||
test.ledger > xrpld.app
|
||||
test.ledger > xrpld.core
|
||||
test.ledger > xrpld.ledger
|
||||
test.ledger > xrpl.ledger
|
||||
test.ledger > xrpl.protocol
|
||||
test.nodestore > test.jtx
|
||||
test.nodestore > test.toplevel
|
||||
@@ -134,7 +138,10 @@ test.toplevel > test.csf
|
||||
test.toplevel > xrpl.json
|
||||
test.unit_test > xrpl.basics
|
||||
tests.libxrpl > xrpl.basics
|
||||
tests.libxrpl > xrpl.net
|
||||
xrpl.json > xrpl.basics
|
||||
xrpl.ledger > xrpl.basics
|
||||
xrpl.ledger > xrpl.protocol
|
||||
xrpl.net > xrpl.basics
|
||||
xrpl.protocol > xrpl.basics
|
||||
xrpl.protocol > xrpl.json
|
||||
@@ -151,6 +158,7 @@ xrpld.app > xrpld.consensus
|
||||
xrpld.app > xrpld.nodestore
|
||||
xrpld.app > xrpld.perflog
|
||||
xrpld.app > xrpl.json
|
||||
xrpld.app > xrpl.ledger
|
||||
xrpld.app > xrpl.net
|
||||
xrpld.app > xrpl.protocol
|
||||
xrpld.app > xrpl.resource
|
||||
@@ -163,9 +171,6 @@ xrpld.core > xrpl.basics
|
||||
xrpld.core > xrpl.json
|
||||
xrpld.core > xrpl.net
|
||||
xrpld.core > xrpl.protocol
|
||||
xrpld.ledger > xrpl.basics
|
||||
xrpld.ledger > xrpl.json
|
||||
xrpld.ledger > xrpl.protocol
|
||||
xrpld.nodestore > xrpl.basics
|
||||
xrpld.nodestore > xrpld.core
|
||||
xrpld.nodestore > xrpld.unity
|
||||
@@ -186,9 +191,9 @@ xrpld.perflog > xrpl.basics
|
||||
xrpld.perflog > xrpl.json
|
||||
xrpld.rpc > xrpl.basics
|
||||
xrpld.rpc > xrpld.core
|
||||
xrpld.rpc > xrpld.ledger
|
||||
xrpld.rpc > xrpld.nodestore
|
||||
xrpld.rpc > xrpl.json
|
||||
xrpld.rpc > xrpl.ledger
|
||||
xrpld.rpc > xrpl.net
|
||||
xrpld.rpc > xrpl.protocol
|
||||
xrpld.rpc > xrpl.resource
|
||||
|
||||
47
.github/scripts/strategy-matrix/generate.py
vendored
Normal file → Executable file
47
.github/scripts/strategy-matrix/generate.py
vendored
Normal file → Executable file
@@ -2,7 +2,17 @@
|
||||
import argparse
|
||||
import itertools
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass
|
||||
|
||||
THIS_DIR = Path(__file__).parent.resolve()
|
||||
|
||||
@dataclass
|
||||
class Config:
|
||||
architecture: list[dict]
|
||||
os: list[dict]
|
||||
build_type: list[str]
|
||||
cmake_args: list[str]
|
||||
|
||||
'''
|
||||
Generate a strategy matrix for GitHub Actions CI.
|
||||
@@ -18,9 +28,9 @@ We will further set additional CMake arguments as follows:
|
||||
- Certain Debian Bookworm configurations will change the reference fee, enable
|
||||
codecov, and enable voidstar in PRs.
|
||||
'''
|
||||
def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict], build_type: list[str], cmake_args: list[str]) -> dict:
|
||||
def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
configurations = []
|
||||
for architecture, os, build_type, cmake_args in itertools.product(architecture, os, build_type, cmake_args):
|
||||
for architecture, os, build_type, cmake_args in itertools.product(config.architecture, config.os, config.build_type, config.cmake_args):
|
||||
# The default CMake target is 'all' for Linux and MacOS and 'install'
|
||||
# for Windows, but it can get overridden for certain configurations.
|
||||
cmake_target = 'install' if os["distro_name"] == 'windows' else 'all'
|
||||
@@ -35,7 +45,7 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict]
|
||||
# Only generate a subset of configurations in PRs.
|
||||
if not all:
|
||||
# Debian:
|
||||
# - Bookworm using GCC 13: Release and Unity on linux/arm64, set
|
||||
# - Bookworm using GCC 13: Release and Unity on linux/amd64, set
|
||||
# the reference fee to 500.
|
||||
# - Bookworm using GCC 15: Debug and no Unity on linux/amd64, enable
|
||||
# code coverage (which will be done below).
|
||||
@@ -47,7 +57,7 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict]
|
||||
if os['distro_name'] == 'debian':
|
||||
skip = True
|
||||
if os['distro_version'] == 'bookworm':
|
||||
if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-13' and build_type == 'Release' and '-Dunity=ON' in cmake_args and architecture['platform'] == 'linux/arm64':
|
||||
if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-13' and build_type == 'Release' and '-Dunity=ON' in cmake_args and architecture['platform'] == 'linux/amd64':
|
||||
cmake_args = f'-DUNIT_TEST_REFERENCE_FEE=500 {cmake_args}'
|
||||
skip = False
|
||||
if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-15' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and architecture['platform'] == 'linux/amd64':
|
||||
@@ -158,21 +168,30 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict]
|
||||
'architecture': architecture,
|
||||
})
|
||||
|
||||
return {'include': configurations}
|
||||
return configurations
|
||||
|
||||
|
||||
def read_config(file: Path) -> Config:
|
||||
config = json.loads(file.read_text())
|
||||
if config['architecture'] is None or config['os'] is None or config['build_type'] is None or config['cmake_args'] is None:
|
||||
raise Exception('Invalid configuration file.')
|
||||
|
||||
return Config(**config)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-a', '--all', help='Set to generate all configurations (generally used when merging a PR) or leave unset to generate a subset of configurations (generally used when committing to a PR).', action="store_true")
|
||||
parser.add_argument('-c', '--config', help='Path to the JSON file containing the strategy matrix configurations.', required=True, type=str)
|
||||
parser.add_argument('-c', '--config', help='Path to the JSON file containing the strategy matrix configurations.', required=False, type=Path)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Load the JSON configuration file.
|
||||
config = None
|
||||
with open(args.config, 'r') as f:
|
||||
config = json.load(f)
|
||||
if config['architecture'] is None or config['os'] is None or config['build_type'] is None or config['cmake_args'] is None:
|
||||
raise Exception('Invalid configuration file.')
|
||||
matrix = []
|
||||
if args.config is None or args.config == '':
|
||||
matrix += generate_strategy_matrix(args.all, read_config(THIS_DIR / "linux.json"))
|
||||
matrix += generate_strategy_matrix(args.all, read_config(THIS_DIR / "macos.json"))
|
||||
matrix += generate_strategy_matrix(args.all, read_config(THIS_DIR / "windows.json"))
|
||||
else:
|
||||
matrix += generate_strategy_matrix(args.all, read_config(args.config))
|
||||
|
||||
# Generate the strategy matrix.
|
||||
print(f'matrix={json.dumps(generate_strategy_matrix(args.all, config['architecture'], config['os'], config['build_type'], config['cmake_args']))}')
|
||||
print(f'matrix={json.dumps({"include": matrix})}')
|
||||
|
||||
2
.github/scripts/strategy-matrix/windows.json
vendored
2
.github/scripts/strategy-matrix/windows.json
vendored
@@ -2,7 +2,7 @@
|
||||
"architecture": [
|
||||
{
|
||||
"platform": "windows/amd64",
|
||||
"runner": ["windows-latest"]
|
||||
"runner": ["self-hosted", "Windows", "devbox"]
|
||||
}
|
||||
],
|
||||
"os": [
|
||||
|
||||
80
.github/workflows/build-test.yml
vendored
80
.github/workflows/build-test.yml
vendored
@@ -13,14 +13,6 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ".build"
|
||||
conan_remote_name:
|
||||
description: "The name of the Conan remote to use."
|
||||
required: true
|
||||
type: string
|
||||
conan_remote_url:
|
||||
description: "The URL of the Conan endpoint to use."
|
||||
required: true
|
||||
type: string
|
||||
dependencies_force_build:
|
||||
description: "Force building of all dependencies."
|
||||
required: false
|
||||
@@ -45,12 +37,6 @@ on:
|
||||
codecov_token:
|
||||
description: "The Codecov token to use for uploading coverage reports."
|
||||
required: false
|
||||
conan_remote_username:
|
||||
description: "The username for logging into the Conan remote. If not provided, the dependencies will not be uploaded."
|
||||
required: false
|
||||
conan_remote_password:
|
||||
description: "The password for logging into the Conan remote. If not provided, the dependencies will not be uploaded."
|
||||
required: false
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.os }}
|
||||
@@ -63,20 +49,10 @@ defaults:
|
||||
jobs:
|
||||
# Generate the strategy matrix to be used by the following job.
|
||||
generate-matrix:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
with:
|
||||
python-version: 3.13
|
||||
- name: Generate strategy matrix
|
||||
working-directory: .github/scripts/strategy-matrix
|
||||
id: generate
|
||||
run: python generate.py ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} --config=${{ inputs.os }}.json >> "${GITHUB_OUTPUT}"
|
||||
outputs:
|
||||
matrix: ${{ steps.generate.outputs.matrix }}
|
||||
uses: ./.github/workflows/reusable-strategy-matrix.yml
|
||||
with:
|
||||
os: ${{ inputs.os }}
|
||||
strategy_matrix: ${{ inputs.strategy_matrix }}
|
||||
|
||||
# Build and test the binary.
|
||||
build-test:
|
||||
@@ -85,8 +61,9 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
|
||||
max-parallel: 10
|
||||
runs-on: ${{ matrix.architecture.runner }}
|
||||
container: ${{ inputs.os == 'linux' && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version) || null }}
|
||||
container: ${{ inputs.os == 'linux' && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-5dd7158', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version) || null }}
|
||||
steps:
|
||||
- name: Check strategy matrix
|
||||
run: |
|
||||
@@ -102,21 +79,16 @@ jobs:
|
||||
echo 'CMake target: ${{ matrix.cmake_target }}'
|
||||
echo 'Config name: ${{ matrix.config_name }}'
|
||||
|
||||
- name: Clean workspace (MacOS)
|
||||
if: ${{ inputs.os == 'macos' }}
|
||||
run: |
|
||||
WORKSPACE=${{ github.workspace }}
|
||||
echo "Cleaning workspace '${WORKSPACE}'."
|
||||
if [ -z "${WORKSPACE}" ] || [ "${WORKSPACE}" = "/" ]; then
|
||||
echo "Invalid working directory '${WORKSPACE}'."
|
||||
exit 1
|
||||
fi
|
||||
find "${WORKSPACE}" -depth 1 | xargs rm -rfv
|
||||
- name: Cleanup workspace
|
||||
if: ${{ runner.os == 'macOS' }}
|
||||
uses: XRPLF/actions/.github/actions/cleanup-workspace@3f044c7478548e3c32ff68980eeb36ece02b364e
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/.github/actions/prepare-runner@638e0dc11ea230f91bd26622fb542116bb5254d5
|
||||
with:
|
||||
disable_ccache: false
|
||||
|
||||
- name: Check configuration (Windows)
|
||||
if: ${{ inputs.os == 'windows' }}
|
||||
@@ -153,40 +125,16 @@ jobs:
|
||||
echo 'Checking nproc version.'
|
||||
nproc --version
|
||||
|
||||
- name: Set up Conan configuration
|
||||
run: |
|
||||
echo 'Installing configuration.'
|
||||
cat conan/global.conf ${{ inputs.os == 'linux' && '>>' || '>' }} $(conan config home)/global.conf
|
||||
|
||||
echo 'Conan configuration:'
|
||||
conan config show '*'
|
||||
- name: Set up Conan profile
|
||||
run: |
|
||||
echo 'Installing profile.'
|
||||
conan config install conan/profiles/default -tf $(conan config home)/profiles/
|
||||
|
||||
echo 'Conan profile:'
|
||||
conan profile show
|
||||
- name: Set up Conan remote
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Adding Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}."
|
||||
conan remote add --index 0 --force ${{ inputs.conan_remote_name }} ${{ inputs.conan_remote_url }}
|
||||
|
||||
echo 'Listing Conan remotes.'
|
||||
conan remote list
|
||||
- name: Setup Conan
|
||||
uses: ./.github/actions/setup-conan
|
||||
|
||||
- name: Build dependencies
|
||||
uses: ./.github/actions/build-deps
|
||||
with:
|
||||
build_dir: ${{ inputs.build_dir }}
|
||||
build_type: ${{ matrix.build_type }}
|
||||
conan_remote_name: ${{ inputs.conan_remote_name }}
|
||||
conan_remote_url: ${{ inputs.conan_remote_url }}
|
||||
conan_remote_username: ${{ secrets.conan_remote_username }}
|
||||
conan_remote_password: ${{ secrets.conan_remote_password }}
|
||||
force_build: ${{ inputs.dependencies_force_build }}
|
||||
force_upload: ${{ inputs.dependencies_force_upload }}
|
||||
|
||||
- name: Build and test binary
|
||||
uses: ./.github/actions/build-test
|
||||
with:
|
||||
|
||||
75
.github/workflows/check-format.yml
vendored
75
.github/workflows/check-format.yml
vendored
@@ -1,75 +0,0 @@
|
||||
# This workflow checks if the code is properly formatted.
|
||||
name: Check format
|
||||
|
||||
# This workflow can only be triggered by other workflows.
|
||||
on: workflow_call
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-format
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
pre-commit:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/ci/tools-rippled-pre-commit
|
||||
steps:
|
||||
# The $GITHUB_WORKSPACE and ${{ github.workspace }} might not point to the
|
||||
# same directory for jobs running in containers. The actions/checkout step
|
||||
# is *supposed* to checkout into $GITHUB_WORKSPACE and then add it to
|
||||
# safe.directory (see instructions at https://github.com/actions/checkout)
|
||||
# but that is apparently not happening for some container images. We
|
||||
# therefore preemptively add both directories to safe.directory. See also
|
||||
# https://github.com/actions/runner/issues/2058 for more details.
|
||||
- name: Configure git safe.directory
|
||||
run: |
|
||||
git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||
git config --global --add safe.directory ${{ github.workspace }}
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
- name: Check configuration
|
||||
run: |
|
||||
echo 'Checking path.'
|
||||
echo ${PATH} | tr ':' '\n'
|
||||
|
||||
echo 'Checking environment variables.'
|
||||
env | sort
|
||||
|
||||
echo 'Checking pre-commit version.'
|
||||
pre-commit --version
|
||||
|
||||
echo 'Checking clang-format version.'
|
||||
clang-format --version
|
||||
|
||||
echo 'Checking NPM version.'
|
||||
npm --version
|
||||
|
||||
echo 'Checking Node.js version.'
|
||||
node --version
|
||||
|
||||
echo 'Checking prettier version.'
|
||||
prettier --version
|
||||
- name: Format code
|
||||
run: pre-commit run --show-diff-on-failure --color=always --all-files
|
||||
- name: Check for differences
|
||||
env:
|
||||
MESSAGE: |
|
||||
One or more files did not conform to the formatting. Maybe you did
|
||||
not run 'pre-commit' before committing, or your version of
|
||||
'clang-format' or 'prettier' has an incompatibility with the ones
|
||||
used here (see the "Check configuration" step above).
|
||||
|
||||
Run 'pre-commit run --all-files' in your repo, and then commit and
|
||||
push the changes.
|
||||
run: |
|
||||
DIFF=$(git status --porcelain)
|
||||
if [ -n "${DIFF}" ]; then
|
||||
# Print the files that changed to give the contributor a hint about
|
||||
# what to expect when running pre-commit on their own machine.
|
||||
git status
|
||||
echo "${MESSAGE}"
|
||||
exit 1
|
||||
fi
|
||||
32
.github/workflows/notify-clio.yml
vendored
32
.github/workflows/notify-clio.yml
vendored
@@ -9,12 +9,14 @@ on:
|
||||
inputs:
|
||||
conan_remote_name:
|
||||
description: "The name of the Conan remote to use."
|
||||
required: true
|
||||
required: false
|
||||
type: string
|
||||
default: xrplf
|
||||
conan_remote_url:
|
||||
description: "The URL of the Conan endpoint to use."
|
||||
required: true
|
||||
required: false
|
||||
type: string
|
||||
default: https://conan.ripplex.io
|
||||
secrets:
|
||||
clio_notify_token:
|
||||
description: "The GitHub token to notify Clio about new versions."
|
||||
@@ -38,7 +40,7 @@ jobs:
|
||||
upload:
|
||||
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/ci/ubuntu-noble:gcc-13
|
||||
container: ghcr.io/xrplf/ci/ubuntu-noble:gcc-13-sha-5dd7158
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
@@ -50,21 +52,25 @@ jobs:
|
||||
echo "channel=pr_${{ github.event.pull_request.number }}" >> "${GITHUB_OUTPUT}"
|
||||
echo 'Extracting version.'
|
||||
echo "version=$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')" >> "${GITHUB_OUTPUT}"
|
||||
- name: Add Conan remote
|
||||
- name: Calculate conan reference
|
||||
id: conan_ref
|
||||
run: |
|
||||
echo "Adding Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}."
|
||||
conan remote add --index 0 --force ${{ inputs.conan_remote_name }} ${{ inputs.conan_remote_url }}
|
||||
echo 'Listing Conan remotes.'
|
||||
conan remote list
|
||||
echo "conan_ref=${{ steps.generate.outputs.version }}@${{ steps.generate.outputs.user }}/${{ steps.generate.outputs.channel }}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Set up Conan
|
||||
uses: ./.github/actions/setup-conan
|
||||
with:
|
||||
conan_remote_name: ${{ inputs.conan_remote_name }}
|
||||
conan_remote_url: ${{ inputs.conan_remote_url }}
|
||||
|
||||
- name: Log into Conan remote
|
||||
run: conan remote login ${{ inputs.conan_remote_name }} "${{ secrets.conan_remote_username }}" --password "${{ secrets.conan_remote_password }}"
|
||||
- name: Upload package
|
||||
run: |
|
||||
conan export --user=${{ steps.generate.outputs.user }} --channel=${{ steps.generate.outputs.channel }} .
|
||||
conan upload --confirm --check --remote=${{ inputs.conan_remote_name }} xrpl/${{ steps.generate.outputs.version }}@${{ steps.generate.outputs.user }}/${{ steps.generate.outputs.channel }}
|
||||
conan upload --confirm --check --remote=${{ inputs.conan_remote_name }} xrpl/${{ steps.conan_ref.outputs.conan_ref }}
|
||||
outputs:
|
||||
channel: ${{ steps.generate.outputs.channel }}
|
||||
version: ${{ steps.generate.outputs.version }}
|
||||
conan_ref: ${{ steps.conan_ref.outputs.conan_ref }}
|
||||
|
||||
notify:
|
||||
needs: upload
|
||||
@@ -76,5 +82,5 @@ jobs:
|
||||
run: |
|
||||
gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
/repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \
|
||||
-F "client_payload[version]=${{ needs.upload.outputs.version }}@${{ needs.upload.outputs.user }}/${{ needs.upload.outputs.channel }}" \
|
||||
-F "client_payload[pr]=${{ github.event.pull_request.number }}"
|
||||
-F "client_payload[conan_ref]=${{ needs.upload.outputs.conan_ref }}" \
|
||||
-F "client_payload[pr_url]=${{ github.event.pull_request.html_url }}"
|
||||
|
||||
47
.github/workflows/on-pr.yml
vendored
47
.github/workflows/on-pr.yml
vendored
@@ -23,10 +23,6 @@ defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
CONAN_REMOTE_NAME: xrplf
|
||||
CONAN_REMOTE_URL: https://conan.ripplex.io
|
||||
|
||||
jobs:
|
||||
# This job determines whether the rest of the workflow should run. It runs
|
||||
# when the PR is not a draft (which should also cover merge-group) or
|
||||
@@ -54,18 +50,17 @@ jobs:
|
||||
files: |
|
||||
# These paths are unique to `on-pr.yml`.
|
||||
.github/scripts/levelization/**
|
||||
.github/workflows/check-format.yml
|
||||
.github/workflows/check-levelization.yml
|
||||
.github/workflows/notify-clio.yml
|
||||
.github/workflows/on-pr.yml
|
||||
.clang-format
|
||||
.pre-commit-config.yaml
|
||||
|
||||
# Keep the paths below in sync with those in `on-trigger.yml`.
|
||||
.github/actions/build-deps/**
|
||||
.github/actions/build-test/**
|
||||
.github/actions/setup-conan/**
|
||||
.github/scripts/strategy-matrix/**
|
||||
.github/workflows/build-test.yml
|
||||
.github/workflows/reusable-strategy-matrix.yml
|
||||
.codecov.yml
|
||||
cmake/**
|
||||
conan/**
|
||||
@@ -75,6 +70,7 @@ jobs:
|
||||
tests/**
|
||||
CMakeLists.txt
|
||||
conanfile.py
|
||||
conan.lock
|
||||
- name: Check whether to run
|
||||
# This step determines whether the rest of the workflow should
|
||||
# run. The rest of the workflow will run if this job runs AND at
|
||||
@@ -94,61 +90,40 @@ jobs:
|
||||
outputs:
|
||||
go: ${{ steps.go.outputs.go == 'true' }}
|
||||
|
||||
check-format:
|
||||
needs: should-run
|
||||
if: needs.should-run.outputs.go == 'true'
|
||||
uses: ./.github/workflows/check-format.yml
|
||||
|
||||
check-levelization:
|
||||
needs: should-run
|
||||
if: needs.should-run.outputs.go == 'true'
|
||||
if: ${{ needs.should-run.outputs.go == 'true' }}
|
||||
uses: ./.github/workflows/check-levelization.yml
|
||||
|
||||
# This job works around the limitation that GitHub Actions does not support
|
||||
# using environment variables as inputs for reusable workflows.
|
||||
generate-outputs:
|
||||
needs: should-run
|
||||
if: needs.should-run.outputs.go == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: No-op
|
||||
run: true
|
||||
outputs:
|
||||
conan_remote_name: ${{ env.CONAN_REMOTE_NAME }}
|
||||
conan_remote_url: ${{ env.CONAN_REMOTE_URL }}
|
||||
|
||||
build-test:
|
||||
needs: generate-outputs
|
||||
needs: should-run
|
||||
if: ${{ needs.should-run.outputs.go == 'true' }}
|
||||
uses: ./.github/workflows/build-test.yml
|
||||
strategy:
|
||||
matrix:
|
||||
os: [linux, macos, windows]
|
||||
with:
|
||||
conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }}
|
||||
conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }}
|
||||
os: ${{ matrix.os }}
|
||||
secrets:
|
||||
codecov_token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
notify-clio:
|
||||
needs:
|
||||
- generate-outputs
|
||||
- should-run
|
||||
- build-test
|
||||
if: ${{ needs.should-run.outputs.go == 'true' && contains(fromJSON('["release", "master"]'), github.ref_name) }}
|
||||
uses: ./.github/workflows/notify-clio.yml
|
||||
with:
|
||||
conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }}
|
||||
conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }}
|
||||
secrets:
|
||||
clio_notify_token: ${{ secrets.CLIO_NOTIFY_TOKEN }}
|
||||
conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
|
||||
conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
|
||||
|
||||
passed:
|
||||
if: failure() || cancelled()
|
||||
needs:
|
||||
- build-test
|
||||
- check-format
|
||||
- check-levelization
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: No-op
|
||||
run: true
|
||||
- name: Fail
|
||||
run: false
|
||||
|
||||
41
.github/workflows/on-trigger.yml
vendored
41
.github/workflows/on-trigger.yml
vendored
@@ -21,8 +21,10 @@ on:
|
||||
# Keep the paths below in sync with those in `on-pr.yml`.
|
||||
- ".github/actions/build-deps/**"
|
||||
- ".github/actions/build-test/**"
|
||||
- ".github/actions/setup-conan/**"
|
||||
- ".github/scripts/strategy-matrix/**"
|
||||
- ".github/workflows/build-test.yml"
|
||||
- ".github/workflows/reusable-strategy-matrix.yml"
|
||||
- ".codecov.yml"
|
||||
- "cmake/**"
|
||||
- "conan/**"
|
||||
@@ -32,6 +34,7 @@ on:
|
||||
- "tests/**"
|
||||
- "CMakeLists.txt"
|
||||
- "conanfile.py"
|
||||
- "conan.lock"
|
||||
|
||||
# Run at 06:32 UTC on every day of the week from Monday through Friday. This
|
||||
# will force all dependencies to be rebuilt, which is useful to verify that
|
||||
@@ -65,54 +68,18 @@ defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
CONAN_REMOTE_NAME: xrplf
|
||||
CONAN_REMOTE_URL: https://conan.ripplex.io
|
||||
|
||||
jobs:
|
||||
check-missing-commits:
|
||||
if: ${{ github.event_name == 'push' && github.ref_type == 'branch' && contains(fromJSON('["develop", "release"]'), github.ref_name) }}
|
||||
uses: ./.github/workflows/check-missing-commits.yml
|
||||
|
||||
# This job works around the limitation that GitHub Actions does not support
|
||||
# using environment variables as inputs for reusable workflows. It also sets
|
||||
# outputs that depend on the event that triggered the workflow.
|
||||
generate-outputs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check inputs and set outputs
|
||||
id: generate
|
||||
run: |
|
||||
if [[ '${{ github.event_name }}' == 'push' ]]; then
|
||||
echo 'dependencies_force_build=false' >> "${GITHUB_OUTPUT}"
|
||||
echo 'dependencies_force_upload=false' >> "${GITHUB_OUTPUT}"
|
||||
elif [[ '${{ github.event_name }}' == 'schedule' ]]; then
|
||||
echo 'dependencies_force_build=true' >> "${GITHUB_OUTPUT}"
|
||||
echo 'dependencies_force_upload=false' >> "${GITHUB_OUTPUT}"
|
||||
else
|
||||
echo 'dependencies_force_build=${{ inputs.dependencies_force_build }}' >> "${GITHUB_OUTPUT}"
|
||||
echo 'dependencies_force_upload=${{ inputs.dependencies_force_upload }}' >> "${GITHUB_OUTPUT}"
|
||||
fi
|
||||
outputs:
|
||||
conan_remote_name: ${{ env.CONAN_REMOTE_NAME }}
|
||||
conan_remote_url: ${{ env.CONAN_REMOTE_URL }}
|
||||
dependencies_force_build: ${{ steps.generate.outputs.dependencies_force_build }}
|
||||
dependencies_force_upload: ${{ steps.generate.outputs.dependencies_force_upload }}
|
||||
|
||||
build-test:
|
||||
needs: generate-outputs
|
||||
uses: ./.github/workflows/build-test.yml
|
||||
strategy:
|
||||
matrix:
|
||||
os: [linux, macos, windows]
|
||||
with:
|
||||
conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }}
|
||||
conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }}
|
||||
dependencies_force_build: ${{ needs.generate-outputs.outputs.dependencies_force_build == 'true' }}
|
||||
dependencies_force_upload: ${{ needs.generate-outputs.outputs.dependencies_force_upload == 'true' }}
|
||||
os: ${{ matrix.os }}
|
||||
strategy_matrix: "all"
|
||||
strategy_matrix: ${{ github.event_name == 'schedule' && 'all' || 'minimal' }}
|
||||
secrets:
|
||||
codecov_token: ${{ secrets.CODECOV_TOKEN }}
|
||||
conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
|
||||
conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
|
||||
|
||||
15
.github/workflows/pre-commit.yml
vendored
Normal file
15
.github/workflows/pre-commit.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: Run pre-commit hooks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [develop, release, master]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
# Call the workflow in the XRPLF/actions repo that runs the pre-commit hooks.
|
||||
run-hooks:
|
||||
uses: XRPLF/actions/.github/workflows/pre-commit.yml@af1b0f0d764cda2e5435f5ac97b240d4bd4d95d3
|
||||
with:
|
||||
runs_on: ubuntu-latest
|
||||
container: '{ "image": "ghcr.io/xrplf/ci/tools-rippled-pre-commit:sha-d1496b8" }'
|
||||
2
.github/workflows/publish-docs.yml
vendored
2
.github/workflows/publish-docs.yml
vendored
@@ -27,7 +27,7 @@ env:
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/ci/tools-rippled-documentation
|
||||
container: ghcr.io/xrplf/ci/tools-rippled-documentation:sha-d1496b8
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
|
||||
38
.github/workflows/reusable-strategy-matrix.yml
vendored
Normal file
38
.github/workflows/reusable-strategy-matrix.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: Generate strategy matrix
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
os:
|
||||
description: 'The operating system to use for the build ("linux", "macos", "windows").'
|
||||
required: false
|
||||
type: string
|
||||
strategy_matrix:
|
||||
# TODO: Support additional strategies, e.g. "ubuntu" for generating all Ubuntu configurations.
|
||||
description: 'The strategy matrix to use for generating the configurations ("minimal", "all").'
|
||||
required: false
|
||||
type: string
|
||||
default: "minimal"
|
||||
outputs:
|
||||
matrix:
|
||||
description: "The generated strategy matrix."
|
||||
value: ${{ jobs.generate-matrix.outputs.matrix }}
|
||||
|
||||
jobs:
|
||||
generate-matrix:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.generate.outputs.matrix }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
with:
|
||||
python-version: 3.13
|
||||
|
||||
- name: Generate strategy matrix
|
||||
working-directory: .github/scripts/strategy-matrix
|
||||
id: generate
|
||||
run: ./generate.py ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} ${{ inputs.os != '' && format('--config={0}.json', inputs.os) || '' }} >> "${GITHUB_OUTPUT}"
|
||||
91
.github/workflows/upload-conan-deps.yml
vendored
Normal file
91
.github/workflows/upload-conan-deps.yml
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
name: Upload Conan Dependencies
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 3 * * 2-6"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
force_source_build:
|
||||
description: "Force source build of all dependencies"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
force_upload:
|
||||
description: "Force upload of all dependencies"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
pull_request:
|
||||
branches: [develop]
|
||||
paths:
|
||||
# This allows testing changes to the upload workflow in a PR
|
||||
- .github/workflows/upload-conan-deps.yml
|
||||
push:
|
||||
branches: [develop]
|
||||
paths:
|
||||
- .github/workflows/upload-conan-deps.yml
|
||||
|
||||
- .github/workflows/reusable-strategy-matrix.yml
|
||||
|
||||
- .github/actions/build-deps/action.yml
|
||||
- .github/actions/setup-conan/action.yml
|
||||
- ".github/scripts/strategy-matrix/**"
|
||||
|
||||
- conanfile.py
|
||||
- conan.lock
|
||||
|
||||
env:
|
||||
CONAN_REMOTE_NAME: xrplf
|
||||
CONAN_REMOTE_URL: https://conan.ripplex.io
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
generate-matrix:
|
||||
uses: ./.github/workflows/reusable-strategy-matrix.yml
|
||||
with:
|
||||
strategy_matrix: ${{ github.event_name == 'pull_request' && 'minimal' || 'all' }}
|
||||
|
||||
run-upload-conan-deps:
|
||||
needs:
|
||||
- generate-matrix
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
|
||||
max-parallel: 10
|
||||
runs-on: ${{ matrix.architecture.runner }}
|
||||
container: ${{ contains(matrix.architecture.platform, 'linux') && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-5dd7158', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version) || null }}
|
||||
|
||||
steps:
|
||||
- name: Cleanup workspace
|
||||
if: ${{ runner.os == 'macOS' }}
|
||||
uses: XRPLF/actions/.github/actions/cleanup-workspace@3f044c7478548e3c32ff68980eeb36ece02b364e
|
||||
|
||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/.github/actions/prepare-runner@638e0dc11ea230f91bd26622fb542116bb5254d5
|
||||
with:
|
||||
disable_ccache: false
|
||||
|
||||
- name: Setup Conan
|
||||
uses: ./.github/actions/setup-conan
|
||||
with:
|
||||
conan_remote_name: ${{ env.CONAN_REMOTE_NAME }}
|
||||
conan_remote_url: ${{ env.CONAN_REMOTE_URL }}
|
||||
|
||||
- name: Build dependencies
|
||||
uses: ./.github/actions/build-deps
|
||||
with:
|
||||
build_dir: .build
|
||||
build_type: ${{ matrix.build_type }}
|
||||
force_build: ${{ github.event_name == 'schedule' || github.event.inputs.force_source_build == 'true' }}
|
||||
|
||||
- name: Log into Conan remote
|
||||
if: ${{ github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' }}
|
||||
run: conan remote login ${{ env.CONAN_REMOTE_NAME }} "${{ secrets.CONAN_REMOTE_USERNAME }}" --password "${{ secrets.CONAN_REMOTE_PASSWORD }}"
|
||||
|
||||
- name: Upload Conan packages
|
||||
if: ${{ github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' && github.event_name != 'schedule' }}
|
||||
run: conan upload "*" -r=${{ env.CONAN_REMOTE_NAME }} --confirm ${{ github.event.inputs.force_upload == 'true' && '--force' || '' }}
|
||||
@@ -1,18 +1,5 @@
|
||||
# To run pre-commit hooks, first install pre-commit:
|
||||
# - `pip install pre-commit==${PRE_COMMIT_VERSION}`
|
||||
# - `pip install pre-commit-hooks==${PRE_COMMIT_HOOKS_VERSION}`
|
||||
#
|
||||
# Depending on your system, you can use `brew install` or `apt install` as well
|
||||
# for installing the pre-commit package, but `pip` is needed to install the
|
||||
# hooks; you can also use `pipx` if you prefer.
|
||||
# Next, install the required formatters:
|
||||
# - `pip install clang-format==${CLANG_VERSION}`
|
||||
# - `npm install prettier@${PRETTIER_VERSION}`
|
||||
#
|
||||
# See https://github.com/XRPLF/ci/blob/main/.github/workflows/tools-rippled.yml
|
||||
# for the versions used in the CI pipeline. You will need to have the exact same
|
||||
# versions of the tools installed on your system to produce the same results as
|
||||
# the pipeline.
|
||||
#
|
||||
# Then, run the following command to install the git hook scripts:
|
||||
# - `pre-commit install`
|
||||
@@ -20,45 +7,33 @@
|
||||
# - `pre-commit run --all-files`
|
||||
# To manually run a specific hook, use:
|
||||
# - `pre-commit run <hook_id> --all-files`
|
||||
# To run the hooks against only the files changed in the current commit, use:
|
||||
# To run the hooks against only the staged files, use:
|
||||
# - `pre-commit run`
|
||||
repos:
|
||||
- repo: local
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: mixed-line-ending
|
||||
- id: check-merge-conflict
|
||||
args: [--assume-in-merge]
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: 7d85583be209cb547946c82fbe51f4bc5dd1d017 # frozen: v18.1.8
|
||||
hooks:
|
||||
- id: clang-format
|
||||
name: clang-format
|
||||
language: system
|
||||
entry: clang-format -i
|
||||
files: '\.(cpp|hpp|h|ipp|proto)$'
|
||||
- id: trailing-whitespace
|
||||
name: trailing-whitespace
|
||||
entry: trailing-whitespace-fixer
|
||||
language: system
|
||||
types: [text]
|
||||
- id: end-of-file
|
||||
name: end-of-file
|
||||
entry: end-of-file-fixer
|
||||
language: system
|
||||
types: [text]
|
||||
- id: mixed-line-ending
|
||||
name: mixed-line-ending
|
||||
entry: mixed-line-ending
|
||||
language: system
|
||||
types: [text]
|
||||
- id: check-merge-conflict
|
||||
name: check-merge-conflict
|
||||
entry: check-merge-conflict --assume-in-merge
|
||||
language: system
|
||||
types: [text]
|
||||
- repo: local
|
||||
args: [--style=file]
|
||||
"types_or": [c++, c, proto]
|
||||
|
||||
- repo: https://github.com/rbubley/mirrors-prettier
|
||||
rev: 5ba47274f9b181bce26a5150a725577f3c336011 # frozen: v3.6.2
|
||||
hooks:
|
||||
- id: prettier
|
||||
name: prettier
|
||||
language: system
|
||||
entry: prettier --ignore-unknown --write
|
||||
|
||||
exclude: |
|
||||
(?x)^(
|
||||
external/.*|
|
||||
.github/scripts/levelization/results/.*\.txt
|
||||
.github/scripts/levelization/results/.*\.txt|
|
||||
conan\.lock
|
||||
)$
|
||||
|
||||
41
BUILD.md
41
BUILD.md
@@ -132,7 +132,7 @@ higher index than the default Conan Center remote, so it is consulted first. You
|
||||
can do this by running:
|
||||
|
||||
```bash
|
||||
conan remote add --index 0 xrplf "https://conan.ripplex.io"
|
||||
conan remote add --index 0 xrplf https://conan.ripplex.io
|
||||
```
|
||||
|
||||
Alternatively, you can pull the patched recipes into the repository and use them
|
||||
@@ -158,6 +158,10 @@ updated dependencies with the newer version. However, if we switch to a newer
|
||||
version that no longer requires a patch, no action is required on your part, as
|
||||
the new recipe will be automatically pulled from the official Conan Center.
|
||||
|
||||
> [!NOTE]
|
||||
> You might need to add `--lockfile=""` to your `conan install` command
|
||||
> to avoid automatic use of the existing `conan.lock` file when you run `conan export` manually on your machine
|
||||
|
||||
### Conan profile tweaks
|
||||
|
||||
#### Missing compiler version
|
||||
@@ -466,6 +470,33 @@ tools.build:cxxflags=['-DBOOST_ASIO_DISABLE_CONCEPTS']
|
||||
The location of `rippled` binary in your build directory depends on your
|
||||
CMake generator. Pass `--help` to see the rest of the command line options.
|
||||
|
||||
#### Conan lockfile
|
||||
|
||||
To achieve reproducible dependencies, we use [Conan lockfile](https://docs.conan.io/2/tutorial/versioning/lockfiles.html).
|
||||
|
||||
The `conan.lock` file in the repository contains a "snapshot" of the current dependencies.
|
||||
It is implicitly used when running `conan` commands, you don't need to specify it.
|
||||
|
||||
You have to update this file every time you add a new dependency or change a revision or version of an existing dependency.
|
||||
|
||||
> [!NOTE]
|
||||
> Conan uses local cache by default when creating a lockfile.
|
||||
>
|
||||
> To ensure, that lockfile creation works the same way on all developer machines, you should clear the local cache before creating a new lockfile.
|
||||
|
||||
To create a new lockfile, run the following commands in the repository root:
|
||||
|
||||
```bash
|
||||
conan remove '*' --confirm
|
||||
rm conan.lock
|
||||
# This ensure that xrplf remote is the first to be consulted
|
||||
conan remote add --force --index 0 xrplf https://conan.ripplex.io
|
||||
conan lock create . -o '&:jemalloc=True' -o '&:rocksdb=True'
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> If some dependencies are exclusive for some OS, you may need to run the last command for them adding `--profile:all <PROFILE>`.
|
||||
|
||||
## Coverage report
|
||||
|
||||
The coverage report is intended for developers using compilers GCC
|
||||
@@ -564,7 +595,13 @@ After any updates or changes to dependencies, you may need to do the following:
|
||||
```
|
||||
|
||||
3. Re-run [conan export](#patched-recipes) if needed.
|
||||
4. Re-run [conan install](#build-and-test).
|
||||
4. [Regenerate lockfile](#conan-lockfile).
|
||||
5. Re-run [conan install](#build-and-test).
|
||||
|
||||
#### ERROR: Package not resolved
|
||||
|
||||
If you're seeing an error like `ERROR: Package 'snappy/1.1.10' not resolved: Unable to find 'snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1756234314.246' in remotes.`,
|
||||
please add `xrplf` remote or re-run `conan export` for [patched recipes](#patched-recipes).
|
||||
|
||||
### `protobuf/port_def.inc` file not found
|
||||
|
||||
|
||||
22
README.md
22
README.md
@@ -6,7 +6,7 @@ The [XRP Ledger](https://xrpl.org/) is a decentralized cryptographic ledger powe
|
||||
|
||||
## XRP
|
||||
|
||||
[XRP](https://xrpl.org/xrp.html) is a public, counterparty-free asset native to the XRP Ledger, and is designed to bridge the many different currencies in use worldwide. XRP is traded on the open-market and is available for anyone to access. The XRP Ledger was created in 2012 with a finite supply of 100 billion units of XRP.
|
||||
[XRP](https://xrpl.org/xrp.html) is a public, counterparty-free crypto-asset native to the XRP Ledger, and is designed as a gas token for network services and to bridge different currencies. XRP is traded on the open-market and is available for anyone to access. The XRP Ledger was created in 2012 with a finite supply of 100 billion units of XRP.
|
||||
|
||||
## rippled
|
||||
|
||||
@@ -23,19 +23,19 @@ If you are interested in running an **API Server** (including a **Full History S
|
||||
|
||||
- **[Censorship-Resistant Transaction Processing][]:** No single party decides which transactions succeed or fail, and no one can "roll back" a transaction after it completes. As long as those who choose to participate in the network keep it healthy, they can settle transactions in seconds.
|
||||
- **[Fast, Efficient Consensus Algorithm][]:** The XRP Ledger's consensus algorithm settles transactions in 4 to 5 seconds, processing at a throughput of up to 1500 transactions per second. These properties put XRP at least an order of magnitude ahead of other top digital assets.
|
||||
- **[Finite XRP Supply][]:** When the XRP Ledger began, 100 billion XRP were created, and no more XRP will ever be created. The available supply of XRP decreases slowly over time as small amounts are destroyed to pay transaction costs.
|
||||
- **[Responsible Software Governance][]:** A team of full-time, world-class developers at Ripple maintain and continually improve the XRP Ledger's underlying software with contributions from the open-source community. Ripple acts as a steward for the technology and an advocate for its interests, and builds constructive relationships with governments and financial institutions worldwide.
|
||||
- **[Finite XRP Supply][]:** When the XRP Ledger began, 100 billion XRP were created, and no more XRP will ever be created. The available supply of XRP decreases slowly over time as small amounts are destroyed to pay transaction fees.
|
||||
- **[Responsible Software Governance][]:** A team of full-time developers at Ripple & other organizations maintain and continually improve the XRP Ledger's underlying software with contributions from the open-source community. Ripple acts as a steward for the technology and an advocate for its interests.
|
||||
- **[Secure, Adaptable Cryptography][]:** The XRP Ledger relies on industry standard digital signature systems like ECDSA (the same scheme used by Bitcoin) but also supports modern, efficient algorithms like Ed25519. The extensible nature of the XRP Ledger's software makes it possible to add and disable algorithms as the state of the art in cryptography advances.
|
||||
- **[Modern Features for Smart Contracts][]:** Features like Escrow, Checks, and Payment Channels support cutting-edge financial applications including the [Interledger Protocol](https://interledger.org/). This toolbox of advanced features comes with safety features like a process for amending the network and separate checks against invariant constraints.
|
||||
- **[Modern Features][]:** Features like Escrow, Checks, and Payment Channels support financial applications atop of the XRP Ledger. This toolbox of advanced features comes with safety features like a process for amending the network and separate checks against invariant constraints.
|
||||
- **[On-Ledger Decentralized Exchange][]:** In addition to all the features that make XRP useful on its own, the XRP Ledger also has a fully-functional accounting system for tracking and trading obligations denominated in any way users want, and an exchange built into the protocol. The XRP Ledger can settle long, cross-currency payment paths and exchanges of multiple currencies in atomic transactions, bridging gaps of trust with XRP.
|
||||
|
||||
[Censorship-Resistant Transaction Processing]: https://xrpl.org/xrp-ledger-overview.html#censorship-resistant-transaction-processing
|
||||
[Fast, Efficient Consensus Algorithm]: https://xrpl.org/xrp-ledger-overview.html#fast-efficient-consensus-algorithm
|
||||
[Finite XRP Supply]: https://xrpl.org/xrp-ledger-overview.html#finite-xrp-supply
|
||||
[Responsible Software Governance]: https://xrpl.org/xrp-ledger-overview.html#responsible-software-governance
|
||||
[Secure, Adaptable Cryptography]: https://xrpl.org/xrp-ledger-overview.html#secure-adaptable-cryptography
|
||||
[Modern Features for Smart Contracts]: https://xrpl.org/xrp-ledger-overview.html#modern-features-for-smart-contracts
|
||||
[On-Ledger Decentralized Exchange]: https://xrpl.org/xrp-ledger-overview.html#on-ledger-decentralized-exchange
|
||||
[Censorship-Resistant Transaction Processing]: https://xrpl.org/transaction-censorship-detection.html#transaction-censorship-detection
|
||||
[Fast, Efficient Consensus Algorithm]: https://xrpl.org/consensus-research.html#consensus-research
|
||||
[Finite XRP Supply]: https://xrpl.org/what-is-xrp.html
|
||||
[Responsible Software Governance]: https://xrpl.org/contribute-code.html#contribute-code-to-the-xrp-ledger
|
||||
[Secure, Adaptable Cryptography]: https://xrpl.org/cryptographic-keys.html#cryptographic-keys
|
||||
[Modern Features]: https://xrpl.org/use-specialized-payment-types.html
|
||||
[On-Ledger Decentralized Exchange]: https://xrpl.org/decentralized-exchange.html#decentralized-exchange
|
||||
|
||||
## Source Code
|
||||
|
||||
|
||||
@@ -104,6 +104,11 @@
|
||||
# 2025-08-28, Bronek Kozicki
|
||||
# - fix "At least one COMMAND must be given" CMake warning from policy CMP0175
|
||||
#
|
||||
# 2025-09-03, Jingchen Wu
|
||||
# - remove the unused function append_coverage_compiler_flags and append_coverage_compiler_flags_to_target
|
||||
# - add a new function add_code_coverage_to_target
|
||||
# - remove some unused code
|
||||
#
|
||||
# USAGE:
|
||||
#
|
||||
# 1. Copy this file into your cmake modules path.
|
||||
@@ -112,10 +117,8 @@
|
||||
# using a CMake option() to enable it just optionally):
|
||||
# include(CodeCoverage)
|
||||
#
|
||||
# 3. Append necessary compiler flags for all supported source files:
|
||||
# append_coverage_compiler_flags()
|
||||
# Or for specific target:
|
||||
# append_coverage_compiler_flags_to_target(YOUR_TARGET_NAME)
|
||||
# 3. Append necessary compiler flags and linker flags for all supported source files:
|
||||
# add_code_coverage_to_target(<target> <PRIVATE|PUBLIC|INTERFACE>)
|
||||
#
|
||||
# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og
|
||||
#
|
||||
@@ -204,67 +207,69 @@ endforeach()
|
||||
|
||||
set(COVERAGE_COMPILER_FLAGS "-g --coverage"
|
||||
CACHE INTERNAL "")
|
||||
|
||||
set(COVERAGE_CXX_COMPILER_FLAGS "")
|
||||
set(COVERAGE_C_COMPILER_FLAGS "")
|
||||
set(COVERAGE_CXX_LINKER_FLAGS "")
|
||||
set(COVERAGE_C_LINKER_FLAGS "")
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(CheckCCompilerFlag)
|
||||
include(CheckLinkerFlag)
|
||||
|
||||
set(COVERAGE_CXX_COMPILER_FLAGS ${COVERAGE_COMPILER_FLAGS})
|
||||
set(COVERAGE_C_COMPILER_FLAGS ${COVERAGE_COMPILER_FLAGS})
|
||||
set(COVERAGE_CXX_LINKER_FLAGS ${COVERAGE_COMPILER_FLAGS})
|
||||
set(COVERAGE_C_LINKER_FLAGS ${COVERAGE_COMPILER_FLAGS})
|
||||
|
||||
check_cxx_compiler_flag(-fprofile-abs-path HAVE_cxx_fprofile_abs_path)
|
||||
if(HAVE_cxx_fprofile_abs_path)
|
||||
set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
|
||||
set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_CXX_COMPILER_FLAGS} -fprofile-abs-path")
|
||||
endif()
|
||||
|
||||
check_c_compiler_flag(-fprofile-abs-path HAVE_c_fprofile_abs_path)
|
||||
if(HAVE_c_fprofile_abs_path)
|
||||
set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
|
||||
set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_C_COMPILER_FLAGS} -fprofile-abs-path")
|
||||
endif()
|
||||
|
||||
check_cxx_compiler_flag(-fprofile-update HAVE_cxx_fprofile_update)
|
||||
check_linker_flag(CXX -fprofile-abs-path HAVE_cxx_linker_fprofile_abs_path)
|
||||
if(HAVE_cxx_linker_fprofile_abs_path)
|
||||
set(COVERAGE_CXX_LINKER_FLAGS "${COVERAGE_CXX_LINKER_FLAGS} -fprofile-abs-path")
|
||||
endif()
|
||||
|
||||
check_linker_flag(C -fprofile-abs-path HAVE_c_linker_fprofile_abs_path)
|
||||
if(HAVE_c_linker_fprofile_abs_path)
|
||||
set(COVERAGE_C_LINKER_FLAGS "${COVERAGE_C_LINKER_FLAGS} -fprofile-abs-path")
|
||||
endif()
|
||||
|
||||
check_cxx_compiler_flag(-fprofile-update=atomic HAVE_cxx_fprofile_update)
|
||||
if(HAVE_cxx_fprofile_update)
|
||||
set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-update=atomic")
|
||||
set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_CXX_COMPILER_FLAGS} -fprofile-update=atomic")
|
||||
endif()
|
||||
|
||||
check_c_compiler_flag(-fprofile-update HAVE_c_fprofile_update)
|
||||
check_c_compiler_flag(-fprofile-update=atomic HAVE_c_fprofile_update)
|
||||
if(HAVE_c_fprofile_update)
|
||||
set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-update=atomic")
|
||||
set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_C_COMPILER_FLAGS} -fprofile-update=atomic")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_Fortran_FLAGS_COVERAGE
|
||||
${COVERAGE_COMPILER_FLAGS}
|
||||
CACHE STRING "Flags used by the Fortran compiler during coverage builds."
|
||||
FORCE )
|
||||
set(CMAKE_CXX_FLAGS_COVERAGE
|
||||
${COVERAGE_COMPILER_FLAGS}
|
||||
CACHE STRING "Flags used by the C++ compiler during coverage builds."
|
||||
FORCE )
|
||||
set(CMAKE_C_FLAGS_COVERAGE
|
||||
${COVERAGE_COMPILER_FLAGS}
|
||||
CACHE STRING "Flags used by the C compiler during coverage builds."
|
||||
FORCE )
|
||||
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
""
|
||||
CACHE STRING "Flags used for linking binaries during coverage builds."
|
||||
FORCE )
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
|
||||
""
|
||||
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
|
||||
FORCE )
|
||||
mark_as_advanced(
|
||||
CMAKE_Fortran_FLAGS_COVERAGE
|
||||
CMAKE_CXX_FLAGS_COVERAGE
|
||||
CMAKE_C_FLAGS_COVERAGE
|
||||
CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
|
||||
check_linker_flag(CXX -fprofile-update=atomic HAVE_cxx_linker_fprofile_update)
|
||||
if(HAVE_cxx_linker_fprofile_update)
|
||||
set(COVERAGE_CXX_LINKER_FLAGS "${COVERAGE_CXX_LINKER_FLAGS} -fprofile-update=atomic")
|
||||
endif()
|
||||
|
||||
check_linker_flag(C -fprofile-update=atomic HAVE_c_linker_fprofile_update)
|
||||
if(HAVE_c_linker_fprofile_update)
|
||||
set(COVERAGE_C_LINKER_FLAGS "${COVERAGE_C_LINKER_FLAGS} -fprofile-update=atomic")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG))
|
||||
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
|
||||
endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)
|
||||
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
|
||||
link_libraries(gcov)
|
||||
endif()
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
@@ -454,18 +459,19 @@ function(setup_target_for_coverage_gcovr)
|
||||
)
|
||||
endfunction() # setup_target_for_coverage_gcovr
|
||||
|
||||
function(append_coverage_compiler_flags)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
|
||||
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
|
||||
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
|
||||
endfunction() # append_coverage_compiler_flags
|
||||
function(add_code_coverage_to_target name scope)
|
||||
separate_arguments(COVERAGE_CXX_COMPILER_FLAGS NATIVE_COMMAND "${COVERAGE_CXX_COMPILER_FLAGS}")
|
||||
separate_arguments(COVERAGE_C_COMPILER_FLAGS NATIVE_COMMAND "${COVERAGE_C_COMPILER_FLAGS}")
|
||||
separate_arguments(COVERAGE_CXX_LINKER_FLAGS NATIVE_COMMAND "${COVERAGE_CXX_LINKER_FLAGS}")
|
||||
separate_arguments(COVERAGE_C_LINKER_FLAGS NATIVE_COMMAND "${COVERAGE_C_LINKER_FLAGS}")
|
||||
|
||||
# Setup coverage for specific library
|
||||
function(append_coverage_compiler_flags_to_target name)
|
||||
separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}")
|
||||
target_compile_options(${name} PRIVATE ${_flag_list})
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
|
||||
target_link_libraries(${name} PRIVATE gcov)
|
||||
endif()
|
||||
endfunction()
|
||||
# Add compiler options to the target
|
||||
target_compile_options(${name} ${scope}
|
||||
$<$<COMPILE_LANGUAGE:CXX>:${COVERAGE_CXX_COMPILER_FLAGS}>
|
||||
$<$<COMPILE_LANGUAGE:C>:${COVERAGE_C_COMPILER_FLAGS}>)
|
||||
|
||||
target_link_libraries (${name} ${scope}
|
||||
$<$<LINK_LANGUAGE:CXX>:${COVERAGE_CXX_LINKER_FLAGS} gcov>
|
||||
$<$<LINK_LANGUAGE:C>:${COVERAGE_C_LINKER_FLAGS} gcov>
|
||||
)
|
||||
endfunction() # add_code_coverage_to_target
|
||||
|
||||
@@ -16,16 +16,13 @@ set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
target_compile_definitions (common
|
||||
INTERFACE
|
||||
$<$<CONFIG:Debug>:DEBUG _DEBUG>
|
||||
#[===[
|
||||
NOTE: CMAKE release builds already have NDEBUG defined, so no need to add it
|
||||
explicitly except for the special case of (profile ON) and (assert OFF).
|
||||
Presumably this is because we don't want profile builds asserting unless
|
||||
asserts were specifically requested.
|
||||
]===]
|
||||
$<$<AND:$<BOOL:${profile}>,$<NOT:$<BOOL:${assert}>>>:NDEBUG>
|
||||
# TODO: Remove once we have migrated functions from OpenSSL 1.x to 3.x.
|
||||
OPENSSL_SUPPRESS_DEPRECATED
|
||||
)
|
||||
$<$<AND:$<BOOL:${profile}>,$<NOT:$<BOOL:${assert}>>>:NDEBUG>)
|
||||
# ^^^^ NOTE: CMAKE release builds already have NDEBUG
|
||||
# defined, so no need to add it explicitly except for
|
||||
# this special case of (profile ON) and (assert OFF)
|
||||
# -- presumably this is because we don't want profile
|
||||
# builds asserting unless asserts were specifically
|
||||
# requested
|
||||
|
||||
if (MSVC)
|
||||
# remove existing exception flag since we set it to -EHa
|
||||
|
||||
@@ -111,6 +111,12 @@ target_link_libraries(xrpl.libxrpl.net PUBLIC
|
||||
add_module(xrpl server)
|
||||
target_link_libraries(xrpl.libxrpl.server PUBLIC xrpl.libxrpl.protocol)
|
||||
|
||||
add_module(xrpl ledger)
|
||||
target_link_libraries(xrpl.libxrpl.ledger PUBLIC
|
||||
xrpl.libxrpl.basics
|
||||
xrpl.libxrpl.json
|
||||
xrpl.libxrpl.protocol
|
||||
)
|
||||
|
||||
add_library(xrpl.libxrpl)
|
||||
set_target_properties(xrpl.libxrpl PROPERTIES OUTPUT_NAME xrpl)
|
||||
@@ -131,6 +137,7 @@ target_link_modules(xrpl PUBLIC
|
||||
resource
|
||||
server
|
||||
net
|
||||
ledger
|
||||
)
|
||||
|
||||
# All headers in libxrpl are in modules.
|
||||
|
||||
@@ -33,6 +33,8 @@ setup_target_for_coverage_gcovr(
|
||||
FORMAT ${coverage_format}
|
||||
EXECUTABLE rippled
|
||||
EXECUTABLE_ARGS --unittest$<$<BOOL:${coverage_test}>:=${coverage_test}> --unittest-jobs ${coverage_test_parallelism} --quiet --unittest-log
|
||||
EXCLUDE "src/test" "include/xrpl/beast/test" "include/xrpl/beast/unit_test" "${CMAKE_BINARY_DIR}/pb-xrpl.libpb"
|
||||
EXCLUDE "src/test" "src/tests" "include/xrpl/beast/test" "include/xrpl/beast/unit_test" "${CMAKE_BINARY_DIR}/pb-xrpl.libpb"
|
||||
DEPENDENCIES rippled
|
||||
)
|
||||
|
||||
add_code_coverage_to_target(opts INTERFACE)
|
||||
|
||||
@@ -18,6 +18,7 @@ install (
|
||||
xrpl.libxrpl.json
|
||||
xrpl.libxrpl.protocol
|
||||
xrpl.libxrpl.resource
|
||||
xrpl.libxrpl.ledger
|
||||
xrpl.libxrpl.server
|
||||
xrpl.libxrpl.net
|
||||
xrpl.libxrpl
|
||||
|
||||
@@ -28,15 +28,11 @@ target_compile_options (opts
|
||||
$<$<AND:$<BOOL:${is_gcc}>,$<COMPILE_LANGUAGE:CXX>>:-Wsuggest-override>
|
||||
$<$<BOOL:${is_gcc}>:-Wno-maybe-uninitialized>
|
||||
$<$<BOOL:${perf}>:-fno-omit-frame-pointer>
|
||||
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${coverage}>>:-g --coverage -fprofile-abs-path>
|
||||
$<$<AND:$<BOOL:${is_clang}>,$<BOOL:${coverage}>>:-g --coverage>
|
||||
$<$<BOOL:${profile}>:-pg>
|
||||
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${profile}>>:-p>)
|
||||
|
||||
target_link_libraries (opts
|
||||
INTERFACE
|
||||
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${coverage}>>:-g --coverage -fprofile-abs-path>
|
||||
$<$<AND:$<BOOL:${is_clang}>,$<BOOL:${coverage}>>:-g --coverage>
|
||||
$<$<BOOL:${profile}>:-pg>
|
||||
$<$<AND:$<BOOL:${is_gcc}>,$<BOOL:${profile}>>:-p>)
|
||||
|
||||
|
||||
56
conan.lock
Normal file
56
conan.lock
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"version": "0.5",
|
||||
"requires": [
|
||||
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1756234269.497",
|
||||
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1756234289.683",
|
||||
"sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1756234266.869",
|
||||
"soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1756234262.318",
|
||||
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1756234314.246",
|
||||
"rocksdb/10.0.1#85537f46e538974d67da0c3977de48ac%1756234304.347",
|
||||
"re2/20230301#dfd6e2bf050eb90ddd8729cfb4c844a4%1756234257.976",
|
||||
"protobuf/3.21.12#d927114e28de9f4691a6bbcdd9a529d1%1756234251.614",
|
||||
"openssl/1.1.1w#a8f0792d7c5121b954578a7149d23e03%1756223730.729",
|
||||
"nudb/2.0.9#c62cfd501e57055a7e0d8ee3d5e5427d%1756234237.107",
|
||||
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1756234228.999",
|
||||
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1756223727.64",
|
||||
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1756230911.03",
|
||||
"libarchive/3.8.1#5cf685686322e906cb42706ab7e099a8%1756234256.696",
|
||||
"jemalloc/5.3.0#e951da9cf599e956cebc117880d2d9f8%1729241615.244",
|
||||
"grpc/1.50.1#02291451d1e17200293a409410d1c4e1%1756234248.958",
|
||||
"doctest/2.4.11#a4211dfc329a16ba9f280f9574025659%1756234220.819",
|
||||
"date/3.0.4#f74bbba5a08fa388256688743136cb6f%1756234217.493",
|
||||
"c-ares/1.34.5#b78b91e7cfb1f11ce777a285bbf169c6%1756234217.915",
|
||||
"bzip2/1.0.8#00b4a4658791c1f06914e087f0e792f5%1756234261.716",
|
||||
"boost/1.88.0#8852c0b72ce8271fb8ff7c53456d4983%1756223752.326",
|
||||
"abseil/20230802.1#f0f91485b111dc9837a68972cb19ca7b%1756234220.907"
|
||||
],
|
||||
"build_requires": [
|
||||
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1756234269.497",
|
||||
"strawberryperl/5.32.1.1#707032463aa0620fa17ec0d887f5fe41%1756234281.733",
|
||||
"protobuf/3.21.12#d927114e28de9f4691a6bbcdd9a529d1%1756234251.614",
|
||||
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1756234232.901",
|
||||
"msys2/cci.latest#5b73b10144f73cc5bfe0572ed9be39e1%1751977009.857",
|
||||
"m4/1.4.19#b38ced39a01e31fef5435bc634461fd2%1700758725.451",
|
||||
"cmake/3.31.8#dde3bde00bb843687e55aea5afa0e220%1756234232.89",
|
||||
"b2/5.3.3#107c15377719889654eb9a162a673975%1756234226.28",
|
||||
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
|
||||
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86"
|
||||
],
|
||||
"python_requires": [],
|
||||
"overrides": {
|
||||
"protobuf/3.21.12": [
|
||||
null,
|
||||
"protobuf/3.21.12"
|
||||
],
|
||||
"lz4/1.9.4": [
|
||||
"lz4/1.10.0"
|
||||
],
|
||||
"boost/1.83.0": [
|
||||
"boost/1.88.0"
|
||||
],
|
||||
"sqlite3/3.44.2": [
|
||||
"sqlite3/3.49.1"
|
||||
]
|
||||
},
|
||||
"config_requires": []
|
||||
}
|
||||
@@ -27,7 +27,7 @@ class Xrpl(ConanFile):
|
||||
'grpc/1.50.1',
|
||||
'libarchive/3.8.1',
|
||||
'nudb/2.0.9',
|
||||
'openssl/3.5.2',
|
||||
'openssl/1.1.1w',
|
||||
'soci/4.0.3',
|
||||
'zlib/1.3.1',
|
||||
]
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
namespace ripple {
|
||||
@@ -130,27 +131,14 @@ private:
|
||||
Does nothing if there is no associated system file.
|
||||
*/
|
||||
void
|
||||
write(char const* text);
|
||||
write(std::string_view text);
|
||||
|
||||
/** write to the log file and append an end of line marker.
|
||||
Does nothing if there is no associated system file.
|
||||
*/
|
||||
void
|
||||
writeln(char const* text);
|
||||
writeln(std::string_view text);
|
||||
|
||||
/** Write to the log file using std::string. */
|
||||
/** @{ */
|
||||
void
|
||||
write(std::string const& str)
|
||||
{
|
||||
write(str.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
writeln(std::string const& str)
|
||||
{
|
||||
writeln(str.c_str());
|
||||
}
|
||||
/** @} */
|
||||
|
||||
private:
|
||||
@@ -186,6 +174,14 @@ public:
|
||||
beast::Journal::Sink&
|
||||
operator[](std::string const& name);
|
||||
|
||||
template <typename AttributesFactory>
|
||||
beast::Journal
|
||||
journal(std::string const& name, AttributesFactory&& factory)
|
||||
{
|
||||
return beast::Journal{
|
||||
get(name), name, std::forward<AttributesFactory>(factory)};
|
||||
}
|
||||
|
||||
beast::Journal
|
||||
journal(std::string const& name);
|
||||
|
||||
@@ -237,30 +233,34 @@ public:
|
||||
static LogSeverity
|
||||
fromString(std::string const& s);
|
||||
|
||||
private:
|
||||
enum {
|
||||
// Maximum line length for log messages.
|
||||
// If the message exceeds this length it will be truncated with elipses.
|
||||
maximumMessageCharacters = 12 * 1024
|
||||
};
|
||||
|
||||
static void
|
||||
format(
|
||||
std::string& output,
|
||||
std::string const& message,
|
||||
beast::severities::Severity severity,
|
||||
std::string const& partition);
|
||||
|
||||
private:
|
||||
enum {
|
||||
// Maximum line length for log messages.
|
||||
// If the message exceeds this length it will be truncated with elipses.
|
||||
maximumMessageCharacters = 12 * 1024
|
||||
};
|
||||
};
|
||||
|
||||
// Wraps a Journal::Stream to skip evaluation of
|
||||
// expensive argument lists if the stream is not active.
|
||||
#ifndef JLOG
|
||||
#define JLOG(x) \
|
||||
if (!x) \
|
||||
{ \
|
||||
} \
|
||||
else \
|
||||
x
|
||||
#define JLOG_JOIN_(a, b) a##b
|
||||
#define JLOG_JOIN(a, b) JLOG_JOIN_(a, b)
|
||||
#define JLOG_UNIQUE(base) JLOG_JOIN(base, __LINE__) // line-based unique name
|
||||
|
||||
#define JLOG(x) \
|
||||
if (auto JLOG_UNIQUE(stream) = (x); !JLOG_UNIQUE(stream)) \
|
||||
{ \
|
||||
} \
|
||||
else \
|
||||
std::move(JLOG_UNIQUE(stream))
|
||||
#endif
|
||||
|
||||
#ifndef CLOG
|
||||
|
||||
@@ -150,6 +150,24 @@ public:
|
||||
return (mantissa_ < 0) ? -1 : (mantissa_ ? 1 : 0);
|
||||
}
|
||||
|
||||
Number
|
||||
truncate() const noexcept
|
||||
{
|
||||
if (exponent_ >= 0 || mantissa_ == 0)
|
||||
return *this;
|
||||
|
||||
Number ret = *this;
|
||||
while (ret.exponent_ < 0 && ret.mantissa_ != 0)
|
||||
{
|
||||
ret.exponent_ += 1;
|
||||
ret.mantissa_ /= rep(10);
|
||||
}
|
||||
// We are guaranteed that normalize() will never throw an exception
|
||||
// because exponent is either negative or zero at this point.
|
||||
ret.normalize();
|
||||
return ret;
|
||||
}
|
||||
|
||||
friend constexpr bool
|
||||
operator>(Number const& x, Number const& y) noexcept
|
||||
{
|
||||
|
||||
@@ -632,6 +632,16 @@ to_string(base_uint<Bits, Tag> const& a)
|
||||
return strHex(a.cbegin(), a.cend());
|
||||
}
|
||||
|
||||
template <std::size_t Bits, class Tag>
|
||||
inline std::string
|
||||
to_short_string(base_uint<Bits, Tag> const& a)
|
||||
{
|
||||
static_assert(
|
||||
base_uint<Bits, Tag>::bytes > 4,
|
||||
"For 4 bytes or less, use a native type");
|
||||
return strHex(a.cbegin(), a.cbegin() + 4) + "...";
|
||||
}
|
||||
|
||||
template <std::size_t Bits, class Tag>
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& out, base_uint<Bits, Tag> const& u)
|
||||
|
||||
@@ -28,9 +28,8 @@ namespace ripple {
|
||||
// the destination can hold all values of the source. This is particularly
|
||||
// handy when the source or destination is an enumeration type.
|
||||
|
||||
template <class Dest, class Src>
|
||||
static constexpr bool is_safetocasttovalue_v =
|
||||
(std::is_integral_v<Src> && std::is_integral_v<Dest>) &&
|
||||
template <class Src, class Dest>
|
||||
concept SafeToCast = (std::is_integral_v<Src> && std::is_integral_v<Dest>) &&
|
||||
(std::is_signed<Src>::value || std::is_unsigned<Dest>::value) &&
|
||||
(std::is_signed<Src>::value != std::is_signed<Dest>::value
|
||||
? sizeof(Dest) > sizeof(Src)
|
||||
@@ -78,7 +77,7 @@ inline constexpr std::
|
||||
unsafe_cast(Src s) noexcept
|
||||
{
|
||||
static_assert(
|
||||
!is_safetocasttovalue_v<Dest, Src>,
|
||||
!SafeToCast<Src, Dest>,
|
||||
"Only unsafe if casting signed to unsigned or "
|
||||
"destination is too small");
|
||||
return static_cast<Dest>(s);
|
||||
|
||||
@@ -22,10 +22,266 @@
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <charconv>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <source_location>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
namespace ripple::log {
|
||||
template <typename T>
|
||||
class LogParameter
|
||||
{
|
||||
public:
|
||||
template <typename TArg>
|
||||
LogParameter(char const* name, TArg&& value)
|
||||
: name_(name), value_(std::forward<TArg>(value))
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
char const* name_;
|
||||
T value_;
|
||||
|
||||
template <typename U>
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& os, LogParameter<U> const&);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class LogField
|
||||
{
|
||||
public:
|
||||
template <typename TArg>
|
||||
LogField(char const* name, TArg&& value)
|
||||
: name_(name), value_(std::forward<TArg>(value))
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
char const* name_;
|
||||
T value_;
|
||||
|
||||
template <typename U>
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& os, LogField<U> const&);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, LogField<T> const& param);
|
||||
|
||||
template <typename T>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, LogParameter<T> const& param);
|
||||
} // namespace ripple::log
|
||||
|
||||
namespace beast {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class SimpleJsonWriter
|
||||
{
|
||||
public:
|
||||
explicit SimpleJsonWriter(std::string* buffer) : buffer_(buffer)
|
||||
{
|
||||
}
|
||||
|
||||
SimpleJsonWriter() = default;
|
||||
|
||||
SimpleJsonWriter(SimpleJsonWriter const& other) = default;
|
||||
SimpleJsonWriter&
|
||||
operator=(SimpleJsonWriter const& other) = default;
|
||||
|
||||
std::string&
|
||||
buffer()
|
||||
{
|
||||
return *buffer_;
|
||||
}
|
||||
|
||||
void
|
||||
startObject() const
|
||||
{
|
||||
buffer_->push_back('{');
|
||||
}
|
||||
void
|
||||
endObject() const
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
if (buffer_->back() == ',')
|
||||
buffer_->pop_back();
|
||||
buffer_->append("},"sv);
|
||||
}
|
||||
void
|
||||
writeKey(std::string_view key) const
|
||||
{
|
||||
writeString(key);
|
||||
buffer_->back() = ':';
|
||||
}
|
||||
void
|
||||
startArray() const
|
||||
{
|
||||
buffer_->push_back('[');
|
||||
}
|
||||
void
|
||||
endArray() const
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
if (buffer_->back() == ',')
|
||||
buffer_->pop_back();
|
||||
buffer_->append("],"sv);
|
||||
}
|
||||
void
|
||||
writeString(std::string_view str) const
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
buffer_->push_back('"');
|
||||
escape(str, *buffer_);
|
||||
buffer_->append("\","sv);
|
||||
}
|
||||
std::string_view
|
||||
writeInt(std::int32_t val) const
|
||||
{
|
||||
return pushNumber(val, *buffer_);
|
||||
}
|
||||
std::string_view
|
||||
writeInt(std::int64_t val) const
|
||||
{
|
||||
return pushNumber(val, *buffer_);
|
||||
}
|
||||
std::string_view
|
||||
writeUInt(std::uint32_t val) const
|
||||
{
|
||||
return pushNumber(val, *buffer_);
|
||||
}
|
||||
std::string_view
|
||||
writeUInt(std::uint64_t val) const
|
||||
{
|
||||
return pushNumber(val, *buffer_);
|
||||
}
|
||||
std::string_view
|
||||
writeDouble(double val) const
|
||||
{
|
||||
return pushNumber(val, *buffer_);
|
||||
}
|
||||
std::string_view
|
||||
writeBool(bool val) const
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
auto str = val ? "true,"sv : "false,"sv;
|
||||
buffer_->append(str);
|
||||
return str;
|
||||
}
|
||||
void
|
||||
writeNull() const
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
buffer_->append("null,"sv);
|
||||
}
|
||||
void
|
||||
writeRaw(std::string_view str) const
|
||||
{
|
||||
buffer_->append(str);
|
||||
}
|
||||
|
||||
void
|
||||
finish()
|
||||
{
|
||||
buffer_->pop_back();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
static std::string_view
|
||||
pushNumber(T val, std::string& str)
|
||||
{
|
||||
thread_local char buffer[128];
|
||||
auto result = std::to_chars(std::begin(buffer), std::end(buffer), val);
|
||||
auto ptr = result.ptr;
|
||||
*ptr = ',';
|
||||
auto len = ptr - std::begin(buffer);
|
||||
str.append(buffer, len + 1);
|
||||
return {buffer, static_cast<size_t>(len)};
|
||||
}
|
||||
|
||||
static void
|
||||
escape(std::string_view str, std::string& buffer)
|
||||
{
|
||||
static constexpr char HEX[] = "0123456789ABCDEF";
|
||||
|
||||
char const* p = str.data();
|
||||
char const* end = p + str.size();
|
||||
char const* chunk = p;
|
||||
|
||||
while (p < end)
|
||||
{
|
||||
auto c = static_cast<unsigned char>(*p);
|
||||
|
||||
// JSON requires escaping for <0x20 and the two specials below.
|
||||
bool needsEscape = (c < 0x20) || (c == '"') || (c == '\\');
|
||||
|
||||
if (!needsEscape)
|
||||
{
|
||||
++p;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Flush the preceding safe run in one go.
|
||||
if (chunk != p)
|
||||
buffer.append(chunk, p - chunk);
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
buffer.append("\\\"", 2);
|
||||
break;
|
||||
case '\\':
|
||||
buffer.append("\\\\", 2);
|
||||
break;
|
||||
case '\b':
|
||||
buffer.append("\\b", 2);
|
||||
break;
|
||||
case '\f':
|
||||
buffer.append("\\f", 2);
|
||||
break;
|
||||
case '\n':
|
||||
buffer.append("\\n", 2);
|
||||
break;
|
||||
case '\r':
|
||||
buffer.append("\\r", 2);
|
||||
break;
|
||||
case '\t':
|
||||
buffer.append("\\t", 2);
|
||||
break;
|
||||
default: {
|
||||
// Other C0 controls -> \u00XX (JSON compliant)
|
||||
char buf[6]{
|
||||
'\\', 'u', '0', '0', HEX[(c >> 4) & 0xF], HEX[c & 0xF]};
|
||||
buffer.append(buf, 6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++p;
|
||||
chunk = p;
|
||||
}
|
||||
|
||||
// Flush trailing safe run
|
||||
if (chunk != p)
|
||||
buffer.append(chunk, p - chunk);
|
||||
}
|
||||
|
||||
std::string* buffer_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/** A namespace for easy access to logging severity values. */
|
||||
namespace severities {
|
||||
/** Severity level / threshold of a Journal message. */
|
||||
@@ -42,6 +298,9 @@ enum Severity {
|
||||
kDisabled,
|
||||
kNone = kDisabled
|
||||
};
|
||||
|
||||
std::string_view
|
||||
to_string(Severity severity);
|
||||
} // namespace severities
|
||||
|
||||
/** A generic endpoint for log messages.
|
||||
@@ -59,18 +318,114 @@ enum Severity {
|
||||
class Journal
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
friend std::ostream&
|
||||
ripple::log::operator<<(
|
||||
std::ostream& os,
|
||||
ripple::log::LogField<T> const& param);
|
||||
|
||||
template <typename T>
|
||||
friend std::ostream&
|
||||
ripple::log::operator<<(
|
||||
std::ostream& os,
|
||||
ripple::log::LogParameter<T> const& param);
|
||||
|
||||
class Sink;
|
||||
|
||||
class JsonLogContext
|
||||
{
|
||||
std::string messageBuffer_;
|
||||
detail::SimpleJsonWriter jsonWriter_;
|
||||
bool hasMessageParams_ = false;
|
||||
std::size_t messageOffset_ = 0;
|
||||
|
||||
public:
|
||||
JsonLogContext() : jsonWriter_(&messageBuffer_)
|
||||
{
|
||||
messageBuffer_.reserve(4 * 1024);
|
||||
}
|
||||
|
||||
std::string&
|
||||
messageBuffer()
|
||||
{
|
||||
return messageBuffer_;
|
||||
}
|
||||
|
||||
void
|
||||
startMessageParams()
|
||||
{
|
||||
if (!hasMessageParams_)
|
||||
{
|
||||
writer().writeKey("Dt");
|
||||
writer().startObject();
|
||||
hasMessageParams_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
endMessageParams()
|
||||
{
|
||||
if (hasMessageParams_)
|
||||
{
|
||||
writer().endObject();
|
||||
}
|
||||
}
|
||||
|
||||
detail::SimpleJsonWriter&
|
||||
writer()
|
||||
{
|
||||
return jsonWriter_;
|
||||
}
|
||||
|
||||
void
|
||||
reuseJson();
|
||||
|
||||
void
|
||||
finish();
|
||||
|
||||
void
|
||||
start(
|
||||
std::source_location location,
|
||||
severities::Severity severity,
|
||||
std::string_view moduleName,
|
||||
std::string_view journalAttributes) noexcept;
|
||||
};
|
||||
|
||||
private:
|
||||
// Severity level / threshold of a Journal message.
|
||||
using Severity = severities::Severity;
|
||||
|
||||
std::string name_;
|
||||
std::string attributes_;
|
||||
static std::string globalLogAttributes_;
|
||||
static std::shared_mutex globalLogAttributesMutex_;
|
||||
static bool jsonLogsEnabled_;
|
||||
|
||||
static thread_local JsonLogContext currentJsonLogContext_;
|
||||
|
||||
// Invariant: m_sink always points to a valid Sink
|
||||
Sink* m_sink;
|
||||
Sink* m_sink = nullptr;
|
||||
|
||||
void
|
||||
initMessageContext(
|
||||
std::source_location location,
|
||||
severities::Severity severity) const;
|
||||
|
||||
static std::string&
|
||||
formatLog(std::string const& message);
|
||||
|
||||
public:
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
enableStructuredJournal();
|
||||
|
||||
static void
|
||||
disableStructuredJournal();
|
||||
|
||||
static bool
|
||||
isStructuredJournalEnabled();
|
||||
|
||||
/** Abstraction for the underlying message destination. */
|
||||
class Sink
|
||||
{
|
||||
@@ -261,11 +616,32 @@ public:
|
||||
/** Output stream support. */
|
||||
/** @{ */
|
||||
ScopedStream
|
||||
operator<<(std::ostream& manip(std::ostream&)) const;
|
||||
operator<<(std::ostream& manip(std::ostream&)) const&&
|
||||
{
|
||||
return {*this, manip};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ScopedStream
|
||||
operator<<(T const& t) const;
|
||||
operator<<(T const& t) const&&
|
||||
{
|
||||
return {*this, t};
|
||||
}
|
||||
|
||||
ScopedStream
|
||||
operator<<(std::ostream& manip(std::ostream&)) const&
|
||||
{
|
||||
currentJsonLogContext_.reuseJson();
|
||||
return {*this, manip};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ScopedStream
|
||||
operator<<(T const& t) const&
|
||||
{
|
||||
currentJsonLogContext_.reuseJson();
|
||||
return {*this, t};
|
||||
}
|
||||
/** @} */
|
||||
|
||||
private:
|
||||
@@ -287,11 +663,73 @@ public:
|
||||
/** Journal has no default constructor. */
|
||||
Journal() = delete;
|
||||
|
||||
/** Create a journal that writes to the specified sink. */
|
||||
explicit Journal(Sink& sink) : m_sink(&sink)
|
||||
Journal(Journal const& other)
|
||||
: name_(other.name_)
|
||||
, attributes_(other.attributes_)
|
||||
, m_sink(other.m_sink)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename TAttributesFactory>
|
||||
Journal(Journal const& other, TAttributesFactory&& attributesFactory)
|
||||
: name_(other.name_), m_sink(other.m_sink)
|
||||
{
|
||||
std::string buffer{other.attributes_};
|
||||
detail::SimpleJsonWriter writer{&buffer};
|
||||
if (other.attributes_.empty() && jsonLogsEnabled_)
|
||||
{
|
||||
writer.startObject();
|
||||
}
|
||||
attributesFactory(writer);
|
||||
attributes_ = std::move(buffer);
|
||||
}
|
||||
|
||||
/** Create a journal that writes to the specified sink. */
|
||||
explicit Journal(Sink& sink, std::string const& name = {})
|
||||
: name_(name), m_sink(&sink)
|
||||
{
|
||||
}
|
||||
|
||||
/** Create a journal that writes to the specified sink. */
|
||||
template <typename TAttributesFactory>
|
||||
explicit Journal(
|
||||
Sink& sink,
|
||||
std::string const& name,
|
||||
TAttributesFactory&& attributesFactory)
|
||||
: name_(name), m_sink(&sink)
|
||||
{
|
||||
std::string buffer;
|
||||
buffer.reserve(128);
|
||||
detail::SimpleJsonWriter writer{&buffer};
|
||||
if (jsonLogsEnabled_)
|
||||
{
|
||||
writer.startObject();
|
||||
}
|
||||
attributesFactory(writer);
|
||||
attributes_ = std::move(buffer);
|
||||
}
|
||||
|
||||
Journal&
|
||||
operator=(Journal const& other)
|
||||
{
|
||||
if (&other == this)
|
||||
return *this; // LCOV_EXCL_LINE
|
||||
|
||||
m_sink = other.m_sink;
|
||||
name_ = other.name_;
|
||||
attributes_ = other.attributes_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Journal&
|
||||
operator=(Journal&& other) noexcept
|
||||
{
|
||||
m_sink = other.m_sink;
|
||||
name_ = std::move(other.name_);
|
||||
attributes_ = std::move(other.attributes_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns the Sink associated with this Journal. */
|
||||
Sink&
|
||||
sink() const
|
||||
@@ -301,8 +739,11 @@ public:
|
||||
|
||||
/** Returns a stream for this sink, with the specified severity level. */
|
||||
Stream
|
||||
stream(Severity level) const
|
||||
stream(
|
||||
Severity level,
|
||||
std::source_location location = std::source_location::current()) const
|
||||
{
|
||||
initMessageContext(location, level);
|
||||
return Stream(*m_sink, level);
|
||||
}
|
||||
|
||||
@@ -319,41 +760,69 @@ public:
|
||||
/** Severity stream access functions. */
|
||||
/** @{ */
|
||||
Stream
|
||||
trace() const
|
||||
trace(std::source_location location = std::source_location::current()) const
|
||||
{
|
||||
initMessageContext(location, severities::kTrace);
|
||||
return {*m_sink, severities::kTrace};
|
||||
}
|
||||
|
||||
Stream
|
||||
debug() const
|
||||
debug(std::source_location location = std::source_location::current()) const
|
||||
{
|
||||
initMessageContext(location, severities::kDebug);
|
||||
return {*m_sink, severities::kDebug};
|
||||
}
|
||||
|
||||
Stream
|
||||
info() const
|
||||
info(std::source_location location = std::source_location::current()) const
|
||||
{
|
||||
initMessageContext(location, severities::kInfo);
|
||||
return {*m_sink, severities::kInfo};
|
||||
}
|
||||
|
||||
Stream
|
||||
warn() const
|
||||
warn(std::source_location location = std::source_location::current()) const
|
||||
{
|
||||
initMessageContext(location, severities::kWarning);
|
||||
return {*m_sink, severities::kWarning};
|
||||
}
|
||||
|
||||
Stream
|
||||
error() const
|
||||
error(std::source_location location = std::source_location::current()) const
|
||||
{
|
||||
initMessageContext(location, severities::kError);
|
||||
return {*m_sink, severities::kError};
|
||||
}
|
||||
|
||||
Stream
|
||||
fatal() const
|
||||
fatal(std::source_location location = std::source_location::current()) const
|
||||
{
|
||||
initMessageContext(location, severities::kFatal);
|
||||
return {*m_sink, severities::kFatal};
|
||||
}
|
||||
/** @} */
|
||||
|
||||
static void
|
||||
resetGlobalAttributes()
|
||||
{
|
||||
std::unique_lock lock(globalLogAttributesMutex_);
|
||||
globalLogAttributes_.clear();
|
||||
}
|
||||
|
||||
template <typename TAttributesFactory>
|
||||
static void
|
||||
addGlobalAttributes(TAttributesFactory&& factory)
|
||||
{
|
||||
std::unique_lock lock(globalLogAttributesMutex_);
|
||||
globalLogAttributes_.reserve(1024);
|
||||
auto isEmpty = globalLogAttributes_.empty();
|
||||
detail::SimpleJsonWriter writer{&globalLogAttributes_};
|
||||
if (isEmpty && jsonLogsEnabled_)
|
||||
{
|
||||
writer.startObject();
|
||||
}
|
||||
factory(writer);
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef __INTELLISENSE__
|
||||
@@ -368,7 +837,7 @@ static_assert(std::is_nothrow_destructible<Journal>::value == true, "");
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
Journal::ScopedStream::ScopedStream(Journal::Stream const& stream, T const& t)
|
||||
Journal::ScopedStream::ScopedStream(Stream const& stream, T const& t)
|
||||
: ScopedStream(stream.sink(), stream.level())
|
||||
{
|
||||
m_ostream << t;
|
||||
@@ -384,13 +853,6 @@ Journal::ScopedStream::operator<<(T const& t) const
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
Journal::ScopedStream
|
||||
Journal::Stream::operator<<(T const& t) const
|
||||
{
|
||||
return ScopedStream(*this, t);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <class CharT, class Traits = std::char_traits<CharT>>
|
||||
@@ -460,4 +922,244 @@ using logwstream = basic_logstream<wchar_t>;
|
||||
|
||||
} // namespace beast
|
||||
|
||||
namespace ripple::log {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
concept ToCharsFormattable = requires(T val) {
|
||||
{
|
||||
to_chars(std::declval<char*>(), std::declval<char*>(), val)
|
||||
} -> std::convertible_to<std::to_chars_result>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept StreamFormattable = requires(T val) {
|
||||
{
|
||||
std::declval<std::ostream&>() << val
|
||||
} -> std::convertible_to<std::ostream&>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
setTextValue(
|
||||
beast::detail::SimpleJsonWriter& writer,
|
||||
char const* name,
|
||||
T&& value)
|
||||
{
|
||||
using ValueType = std::decay_t<T>;
|
||||
writer.buffer() += name;
|
||||
writer.buffer() += ": ";
|
||||
if constexpr (
|
||||
std::is_same_v<ValueType, std::string> ||
|
||||
std::is_same_v<ValueType, std::string_view> ||
|
||||
std::is_same_v<ValueType, char const*> ||
|
||||
std::is_same_v<ValueType, char*>)
|
||||
{
|
||||
writer.buffer() += value;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << value;
|
||||
writer.buffer() += value;
|
||||
;
|
||||
}
|
||||
writer.buffer() += " ";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
setJsonValue(
|
||||
beast::detail::SimpleJsonWriter& writer,
|
||||
char const* name,
|
||||
T&& value,
|
||||
std::ostream* outStream)
|
||||
{
|
||||
using ValueType = std::decay_t<T>;
|
||||
writer.writeKey(name);
|
||||
if constexpr (std::is_same_v<ValueType, bool>)
|
||||
{
|
||||
auto sv = writer.writeBool(value);
|
||||
if (outStream)
|
||||
{
|
||||
outStream->write(sv.data(), sv.size());
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_integral_v<ValueType>)
|
||||
{
|
||||
std::string_view sv;
|
||||
if constexpr (std::is_signed_v<ValueType>)
|
||||
{
|
||||
if constexpr (sizeof(ValueType) > 4)
|
||||
{
|
||||
sv = writer.writeInt(static_cast<std::int64_t>(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
sv = writer.writeInt(static_cast<std::int32_t>(value));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (sizeof(ValueType) > 4)
|
||||
{
|
||||
sv = writer.writeUInt(static_cast<std::uint64_t>(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
sv = writer.writeUInt(static_cast<std::uint32_t>(value));
|
||||
}
|
||||
}
|
||||
if (outStream)
|
||||
{
|
||||
outStream->write(sv.data(), sv.size());
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_floating_point_v<ValueType>)
|
||||
{
|
||||
auto sv = writer.writeDouble(value);
|
||||
|
||||
if (outStream)
|
||||
{
|
||||
outStream->write(sv.data(), sv.size());
|
||||
}
|
||||
}
|
||||
else if constexpr (
|
||||
std::is_same_v<ValueType, char const*> ||
|
||||
std::is_same_v<ValueType, char*>)
|
||||
{
|
||||
writer.writeString(value);
|
||||
if (outStream)
|
||||
{
|
||||
outStream->write(value, std::strlen(value));
|
||||
}
|
||||
}
|
||||
else if constexpr (
|
||||
std::is_same_v<ValueType, std::string> ||
|
||||
std::is_same_v<ValueType, std::string_view>)
|
||||
{
|
||||
writer.writeString(value);
|
||||
if (outStream)
|
||||
{
|
||||
outStream->write(value.data(), value.size());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (ToCharsFormattable<ValueType>)
|
||||
{
|
||||
char buffer[1024];
|
||||
std::to_chars_result result =
|
||||
to_chars(std::begin(buffer), std::end(buffer), value);
|
||||
if (result.ec == std::errc{})
|
||||
{
|
||||
std::string_view sv{std::begin(buffer), result.ptr};
|
||||
writer.writeString(sv);
|
||||
if (outStream)
|
||||
{
|
||||
outStream->write(sv.data(), sv.size());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (StreamFormattable<ValueType>)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss.imbue(std::locale::classic());
|
||||
oss << value;
|
||||
|
||||
auto str = oss.str();
|
||||
|
||||
writer.writeString(str);
|
||||
|
||||
if (outStream)
|
||||
{
|
||||
outStream->write(
|
||||
str.c_str(), static_cast<std::streamsize>(str.size()));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static_assert(
|
||||
ToCharsFormattable<ValueType> || StreamFormattable<ValueType>);
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <typename T>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, LogParameter<T> const& param)
|
||||
{
|
||||
if (!beast::Journal::jsonLogsEnabled_)
|
||||
{
|
||||
os << param.value_;
|
||||
return os;
|
||||
}
|
||||
beast::Journal::currentJsonLogContext_.startMessageParams();
|
||||
detail::setJsonValue(
|
||||
beast::Journal::currentJsonLogContext_.writer(),
|
||||
param.name_,
|
||||
param.value_,
|
||||
&os);
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, LogField<T> const& param)
|
||||
{
|
||||
if (!beast::Journal::jsonLogsEnabled_)
|
||||
return os;
|
||||
beast::Journal::currentJsonLogContext_.startMessageParams();
|
||||
detail::setJsonValue(
|
||||
beast::Journal::currentJsonLogContext_.writer(),
|
||||
param.name_,
|
||||
param.value_,
|
||||
nullptr);
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
LogParameter<T>
|
||||
param(char const* name, T&& value)
|
||||
{
|
||||
return LogParameter<T>{name, std::forward<T>(value)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
LogField<T>
|
||||
field(char const* name, T&& value)
|
||||
{
|
||||
return LogField<T>{name, std::forward<T>(value)};
|
||||
}
|
||||
|
||||
template <typename... Pair>
|
||||
[[nodiscard]] auto
|
||||
attributes(Pair&&... pairs)
|
||||
{
|
||||
return [&](beast::detail::SimpleJsonWriter& writer) {
|
||||
if (beast::Journal::isStructuredJournalEnabled())
|
||||
{
|
||||
(detail::setJsonValue(writer, pairs.first, pairs.second, nullptr),
|
||||
...);
|
||||
}
|
||||
else
|
||||
{
|
||||
(detail::setTextValue(writer, pairs.first, pairs.second), ...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] std::pair<char const*, std::decay_t<T>>
|
||||
attr(char const* name, T&& value)
|
||||
{
|
||||
return std::make_pair(name, std::forward<T>(value));
|
||||
}
|
||||
|
||||
} // namespace ripple::log
|
||||
|
||||
#endif
|
||||
|
||||
@@ -39,11 +39,16 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#endif
|
||||
|
||||
#define XRPL_ASSERT ALWAYS_OR_UNREACHABLE
|
||||
#define XRPL_ASSERT_PARTS(cond, function, description, ...) \
|
||||
XRPL_ASSERT(cond, function " : " description)
|
||||
|
||||
// How to use the instrumentation macros:
|
||||
//
|
||||
// * XRPL_ASSERT if cond must be true but the line might not be reached during
|
||||
// fuzzing. Same like `assert` in normal use.
|
||||
// * XRPL_ASSERT_PARTS is for convenience, and works like XRPL_ASSERT, but
|
||||
// splits the message param into "function" and "description", then joins
|
||||
// them with " : " before passing to XRPL_ASSERT.
|
||||
// * ALWAYS if cond must be true _and_ the line must be reached during fuzzing.
|
||||
// Same like `assert` in normal use.
|
||||
// * REACHABLE if the line must be reached during fuzzing
|
||||
|
||||
@@ -20,11 +20,10 @@
|
||||
#ifndef RIPPLE_LEDGER_APPLYVIEW_H_INCLUDED
|
||||
#define RIPPLE_LEDGER_APPLYVIEW_H_INCLUDED
|
||||
|
||||
#include <xrpld/ledger/RawView.h>
|
||||
#include <xrpld/ledger/ReadView.h>
|
||||
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/RawView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -20,9 +20,8 @@
|
||||
#ifndef RIPPLE_LEDGER_APPLYVIEWIMPL_H_INCLUDED
|
||||
#define RIPPLE_LEDGER_APPLYVIEWIMPL_H_INCLUDED
|
||||
|
||||
#include <xrpld/ledger/OpenView.h>
|
||||
#include <xrpld/ledger/detail/ApplyViewBase.h>
|
||||
|
||||
#include <xrpl/ledger/OpenView.h>
|
||||
#include <xrpl/ledger/detail/ApplyViewBase.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
@@ -20,9 +20,8 @@
|
||||
#ifndef RIPPLE_LEDGER_BOOK_DIRS_H_INCLUDED
|
||||
#define RIPPLE_LEDGER_BOOK_DIRS_H_INCLUDED
|
||||
|
||||
#include <xrpld/ledger/ReadView.h>
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -20,10 +20,9 @@
|
||||
#ifndef RIPPLE_LEDGER_CACHEDVIEW_H_INCLUDED
|
||||
#define RIPPLE_LEDGER_CACHEDVIEW_H_INCLUDED
|
||||
|
||||
#include <xrpld/ledger/CachedSLEs.h>
|
||||
#include <xrpld/ledger/ReadView.h>
|
||||
|
||||
#include <xrpl/basics/hardened_hash.h>
|
||||
#include <xrpl/ledger/CachedSLEs.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
@@ -20,12 +20,11 @@
|
||||
#ifndef RIPPLE_APP_MISC_CREDENTIALHELPERS_H_INCLUDED
|
||||
#define RIPPLE_APP_MISC_CREDENTIALHELPERS_H_INCLUDED
|
||||
|
||||
#include <xrpld/ledger/ApplyView.h>
|
||||
#include <xrpld/ledger/ReadView.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/STArray.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
@@ -20,8 +20,7 @@
|
||||
#ifndef RIPPLE_LEDGER_DIR_H_INCLUDED
|
||||
#define RIPPLE_LEDGER_DIR_H_INCLUDED
|
||||
|
||||
#include <xrpld/ledger/ReadView.h>
|
||||
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -20,10 +20,9 @@
|
||||
#ifndef RIPPLE_LEDGER_OPENVIEW_H_INCLUDED
|
||||
#define RIPPLE_LEDGER_OPENVIEW_H_INCLUDED
|
||||
|
||||
#include <xrpld/ledger/RawView.h>
|
||||
#include <xrpld/ledger/ReadView.h>
|
||||
#include <xrpld/ledger/detail/RawStateTable.h>
|
||||
|
||||
#include <xrpl/ledger/RawView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/detail/RawStateTable.h>
|
||||
#include <xrpl/protocol/STArray.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
@@ -20,10 +20,9 @@
|
||||
#ifndef RIPPLE_LEDGER_PAYMENTSANDBOX_H_INCLUDED
|
||||
#define RIPPLE_LEDGER_PAYMENTSANDBOX_H_INCLUDED
|
||||
|
||||
#include <xrpld/ledger/RawView.h>
|
||||
#include <xrpld/ledger/Sandbox.h>
|
||||
#include <xrpld/ledger/detail/ApplyViewBase.h>
|
||||
|
||||
#include <xrpl/ledger/RawView.h>
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/detail/ApplyViewBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
|
||||
#include <map>
|
||||
@@ -20,8 +20,7 @@
|
||||
#ifndef RIPPLE_LEDGER_RAWVIEW_H_INCLUDED
|
||||
#define RIPPLE_LEDGER_RAWVIEW_H_INCLUDED
|
||||
|
||||
#include <xrpld/ledger/ReadView.h>
|
||||
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
|
||||
@@ -20,10 +20,9 @@
|
||||
#ifndef RIPPLE_LEDGER_READVIEW_H_INCLUDED
|
||||
#define RIPPLE_LEDGER_READVIEW_H_INCLUDED
|
||||
|
||||
#include <xrpld/ledger/detail/ReadViewFwdRange.h>
|
||||
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/beast/hash/uhash.h>
|
||||
#include <xrpl/ledger/detail/ReadViewFwdRange.h>
|
||||
#include <xrpl/protocol/Fees.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -280,6 +279,6 @@ makeRulesGivenLedger(
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#include <xrpld/ledger/detail/ReadViewFwdRange.ipp>
|
||||
#include <xrpl/ledger/detail/ReadViewFwdRange.ipp>
|
||||
|
||||
#endif
|
||||
@@ -20,8 +20,8 @@
|
||||
#ifndef RIPPLE_LEDGER_SANDBOX_H_INCLUDED
|
||||
#define RIPPLE_LEDGER_SANDBOX_H_INCLUDED
|
||||
|
||||
#include <xrpld/ledger/RawView.h>
|
||||
#include <xrpld/ledger/detail/ApplyViewBase.h>
|
||||
#include <xrpl/ledger/RawView.h>
|
||||
#include <xrpl/ledger/detail/ApplyViewBase.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -20,11 +20,10 @@
|
||||
#ifndef RIPPLE_LEDGER_VIEW_H_INCLUDED
|
||||
#define RIPPLE_LEDGER_VIEW_H_INCLUDED
|
||||
|
||||
#include <xrpld/ledger/ApplyView.h>
|
||||
#include <xrpld/ledger/OpenView.h>
|
||||
#include <xrpld/ledger/ReadView.h>
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/OpenView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
@@ -562,12 +561,28 @@ createPseudoAccount(
|
||||
[[nodiscard]] bool
|
||||
isPseudoAccount(std::shared_ptr<SLE const> sleAcct);
|
||||
|
||||
// Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account if
|
||||
// set
|
||||
// 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<SField const*> const&
|
||||
getPseudoAccountFields();
|
||||
|
||||
[[nodiscard]] inline bool
|
||||
isPseudoAccount(ReadView const& view, AccountID accountId)
|
||||
{
|
||||
return isPseudoAccount(view.read(keylet::account(accountId)));
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
canAddHolding(ReadView const& view, Asset const& asset);
|
||||
|
||||
/// Any transactors that call addEmptyHolding() in doApply must call
|
||||
/// canAddHolding() in preflight with the same View and Asset
|
||||
[[nodiscard]] TER
|
||||
addEmptyHolding(
|
||||
ApplyView& view,
|
||||
@@ -719,7 +734,8 @@ rippleUnlockEscrowMPT(
|
||||
ApplyView& view,
|
||||
AccountID const& uGrantorID,
|
||||
AccountID const& uGranteeID,
|
||||
STAmount const& saAmount,
|
||||
STAmount const& netAmount,
|
||||
STAmount const& grossAmount,
|
||||
beast::Journal j);
|
||||
|
||||
/** Calls static accountSendIOU if saAmount represents Issue.
|
||||
@@ -912,28 +928,41 @@ deleteAMMTrustLine(
|
||||
std::optional<AccountID> 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.
|
||||
[[nodiscard]] STAmount
|
||||
// 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<STAmount>
|
||||
assetsToSharesDeposit(
|
||||
std::shared_ptr<SLE const> const& vault,
|
||||
std::shared_ptr<SLE const> const& issuance,
|
||||
STAmount const& assets);
|
||||
|
||||
// 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.
|
||||
[[nodiscard]] STAmount
|
||||
// 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<STAmount>
|
||||
sharesToAssetsDeposit(
|
||||
std::shared_ptr<SLE const> const& vault,
|
||||
std::shared_ptr<SLE const> 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<STAmount>
|
||||
assetsToSharesWithdraw(
|
||||
std::shared_ptr<SLE const> const& vault,
|
||||
std::shared_ptr<SLE const> const& issuance,
|
||||
STAmount const& assets);
|
||||
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.
|
||||
[[nodiscard]] STAmount
|
||||
// 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<STAmount>
|
||||
sharesToAssetsWithdraw(
|
||||
std::shared_ptr<SLE const> const& vault,
|
||||
std::shared_ptr<SLE const> const& issuance,
|
||||
@@ -20,11 +20,10 @@
|
||||
#ifndef RIPPLE_LEDGER_APPLYSTATETABLE_H_INCLUDED
|
||||
#define RIPPLE_LEDGER_APPLYSTATETABLE_H_INCLUDED
|
||||
|
||||
#include <xrpld/ledger/OpenView.h>
|
||||
#include <xrpld/ledger/RawView.h>
|
||||
#include <xrpld/ledger/ReadView.h>
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/OpenView.h>
|
||||
#include <xrpl/ledger/RawView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxMeta.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
@@ -20,10 +20,9 @@
|
||||
#ifndef RIPPLE_LEDGER_APPLYVIEWBASE_H_INCLUDED
|
||||
#define RIPPLE_LEDGER_APPLYVIEWBASE_H_INCLUDED
|
||||
|
||||
#include <xrpld/ledger/ApplyView.h>
|
||||
#include <xrpld/ledger/ReadView.h>
|
||||
#include <xrpld/ledger/detail/ApplyStateTable.h>
|
||||
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/detail/ApplyStateTable.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -20,8 +20,8 @@
|
||||
#ifndef RIPPLE_LEDGER_RAWSTATETABLE_H_INCLUDED
|
||||
#define RIPPLE_LEDGER_RAWSTATETABLE_H_INCLUDED
|
||||
|
||||
#include <xrpld/ledger/RawView.h>
|
||||
#include <xrpld/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/RawView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
|
||||
#include <boost/container/pmr/monotonic_buffer_resource.hpp>
|
||||
#include <boost/container/pmr/polymorphic_allocator.hpp>
|
||||
@@ -1,565 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2019 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef BASICS_FEES_H_INCLUDED
|
||||
#define BASICS_FEES_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
#include <boost/operators.hpp>
|
||||
|
||||
#include <iosfwd>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace feeunit {
|
||||
|
||||
/** "drops" are the smallest divisible amount of XRP. This is what most
|
||||
of the code uses. */
|
||||
struct dropTag;
|
||||
/** "fee units" calculations are a not-really-unitless value that is used
|
||||
to express the cost of a given transaction vs. a reference transaction.
|
||||
They are primarily used by the Transactor classes. */
|
||||
struct feeunitTag;
|
||||
/** "fee levels" are used by the transaction queue to compare the relative
|
||||
cost of transactions that require different levels of effort to process.
|
||||
See also: src/ripple/app/misc/FeeEscalation.md#fee-level */
|
||||
struct feelevelTag;
|
||||
/** unitless values are plain scalars wrapped in a TaggedFee. They are
|
||||
used for calculations in this header. */
|
||||
struct unitlessTag;
|
||||
|
||||
template <class T>
|
||||
using enable_if_unit_t = typename std::enable_if_t<
|
||||
std::is_class_v<T> && std::is_object_v<typename T::unit_type> &&
|
||||
std::is_object_v<typename T::value_type>>;
|
||||
|
||||
/** `is_usable_unit_v` is checked to ensure that only values with
|
||||
known valid type tags can be used (sometimes transparently) in
|
||||
non-fee contexts. At the time of implementation, this includes
|
||||
all known tags, but more may be added in the future, and they
|
||||
should not be added automatically unless determined to be
|
||||
appropriate.
|
||||
*/
|
||||
template <class T, class = enable_if_unit_t<T>>
|
||||
constexpr bool is_usable_unit_v =
|
||||
std::is_same_v<typename T::unit_type, feeunitTag> ||
|
||||
std::is_same_v<typename T::unit_type, feelevelTag> ||
|
||||
std::is_same_v<typename T::unit_type, unitlessTag> ||
|
||||
std::is_same_v<typename T::unit_type, dropTag>;
|
||||
|
||||
template <class UnitTag, class T>
|
||||
class TaggedFee : private boost::totally_ordered<TaggedFee<UnitTag, T>>,
|
||||
private boost::additive<TaggedFee<UnitTag, T>>,
|
||||
private boost::equality_comparable<TaggedFee<UnitTag, T>, T>,
|
||||
private boost::dividable<TaggedFee<UnitTag, T>, T>,
|
||||
private boost::modable<TaggedFee<UnitTag, T>, T>,
|
||||
private boost::unit_steppable<TaggedFee<UnitTag, T>>
|
||||
{
|
||||
public:
|
||||
using unit_type = UnitTag;
|
||||
using value_type = T;
|
||||
|
||||
private:
|
||||
value_type fee_;
|
||||
|
||||
protected:
|
||||
template <class Other>
|
||||
static constexpr bool is_compatible_v =
|
||||
std::is_arithmetic_v<Other> && std::is_arithmetic_v<value_type> &&
|
||||
std::is_convertible_v<Other, value_type>;
|
||||
|
||||
template <class OtherFee, class = enable_if_unit_t<OtherFee>>
|
||||
static constexpr bool is_compatiblefee_v =
|
||||
is_compatible_v<typename OtherFee::value_type> &&
|
||||
std::is_same_v<UnitTag, typename OtherFee::unit_type>;
|
||||
|
||||
template <class Other>
|
||||
using enable_if_compatible_t =
|
||||
typename std::enable_if_t<is_compatible_v<Other>>;
|
||||
|
||||
template <class OtherFee>
|
||||
using enable_if_compatiblefee_t =
|
||||
typename std::enable_if_t<is_compatiblefee_v<OtherFee>>;
|
||||
|
||||
public:
|
||||
TaggedFee() = default;
|
||||
constexpr TaggedFee(TaggedFee const& other) = default;
|
||||
constexpr TaggedFee&
|
||||
operator=(TaggedFee const& other) = default;
|
||||
|
||||
constexpr explicit TaggedFee(beast::Zero) : fee_(0)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr TaggedFee&
|
||||
operator=(beast::Zero)
|
||||
{
|
||||
fee_ = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr explicit TaggedFee(value_type fee) : fee_(fee)
|
||||
{
|
||||
}
|
||||
|
||||
TaggedFee&
|
||||
operator=(value_type fee)
|
||||
{
|
||||
fee_ = fee;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Instances with the same unit, and a type that is
|
||||
"safe" to convert to this one can be converted
|
||||
implicitly */
|
||||
template <
|
||||
class Other,
|
||||
class = std::enable_if_t<
|
||||
is_compatible_v<Other> &&
|
||||
is_safetocasttovalue_v<value_type, Other>>>
|
||||
constexpr TaggedFee(TaggedFee<unit_type, Other> const& fee)
|
||||
: TaggedFee(safe_cast<value_type>(fee.fee()))
|
||||
{
|
||||
}
|
||||
|
||||
constexpr TaggedFee
|
||||
operator*(value_type const& rhs) const
|
||||
{
|
||||
return TaggedFee{fee_ * rhs};
|
||||
}
|
||||
|
||||
friend constexpr TaggedFee
|
||||
operator*(value_type lhs, TaggedFee const& rhs)
|
||||
{
|
||||
// multiplication is commutative
|
||||
return rhs * lhs;
|
||||
}
|
||||
|
||||
constexpr value_type
|
||||
operator/(TaggedFee const& rhs) const
|
||||
{
|
||||
return fee_ / rhs.fee_;
|
||||
}
|
||||
|
||||
TaggedFee&
|
||||
operator+=(TaggedFee const& other)
|
||||
{
|
||||
fee_ += other.fee();
|
||||
return *this;
|
||||
}
|
||||
|
||||
TaggedFee&
|
||||
operator-=(TaggedFee const& other)
|
||||
{
|
||||
fee_ -= other.fee();
|
||||
return *this;
|
||||
}
|
||||
|
||||
TaggedFee&
|
||||
operator++()
|
||||
{
|
||||
++fee_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
TaggedFee&
|
||||
operator--()
|
||||
{
|
||||
--fee_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
TaggedFee&
|
||||
operator*=(value_type const& rhs)
|
||||
{
|
||||
fee_ *= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
TaggedFee&
|
||||
operator/=(value_type const& rhs)
|
||||
{
|
||||
fee_ /= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class transparent = value_type>
|
||||
std::enable_if_t<std::is_integral_v<transparent>, TaggedFee&>
|
||||
operator%=(value_type const& rhs)
|
||||
{
|
||||
fee_ %= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
TaggedFee
|
||||
operator-() const
|
||||
{
|
||||
static_assert(
|
||||
std::is_signed_v<T>, "- operator illegal on unsigned fee types");
|
||||
return TaggedFee{-fee_};
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(TaggedFee const& other) const
|
||||
{
|
||||
return fee_ == other.fee_;
|
||||
}
|
||||
|
||||
template <class Other, class = enable_if_compatible_t<Other>>
|
||||
bool
|
||||
operator==(TaggedFee<unit_type, Other> const& other) const
|
||||
{
|
||||
return fee_ == other.fee();
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(value_type other) const
|
||||
{
|
||||
return fee_ == other;
|
||||
}
|
||||
|
||||
template <class Other, class = enable_if_compatible_t<Other>>
|
||||
bool
|
||||
operator!=(TaggedFee<unit_type, Other> const& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
bool
|
||||
operator<(TaggedFee const& other) const
|
||||
{
|
||||
return fee_ < other.fee_;
|
||||
}
|
||||
|
||||
/** Returns true if the amount is not zero */
|
||||
explicit constexpr
|
||||
operator bool() const noexcept
|
||||
{
|
||||
return fee_ != 0;
|
||||
}
|
||||
|
||||
/** Return the sign of the amount */
|
||||
constexpr int
|
||||
signum() const noexcept
|
||||
{
|
||||
return (fee_ < 0) ? -1 : (fee_ ? 1 : 0);
|
||||
}
|
||||
|
||||
/** Returns the number of drops */
|
||||
constexpr value_type
|
||||
fee() const
|
||||
{
|
||||
return fee_;
|
||||
}
|
||||
|
||||
template <class Other>
|
||||
constexpr double
|
||||
decimalFromReference(TaggedFee<unit_type, Other> reference) const
|
||||
{
|
||||
return static_cast<double>(fee_) / reference.fee();
|
||||
}
|
||||
|
||||
// `is_usable_unit_v` is checked to ensure that only values with
|
||||
// known valid type tags can be converted to JSON. At the time
|
||||
// of implementation, that includes all known tags, but more may
|
||||
// be added in the future.
|
||||
std::enable_if_t<is_usable_unit_v<TaggedFee>, Json::Value>
|
||||
jsonClipped() const
|
||||
{
|
||||
if constexpr (std::is_integral_v<value_type>)
|
||||
{
|
||||
using jsontype = std::conditional_t<
|
||||
std::is_signed_v<value_type>,
|
||||
Json::Int,
|
||||
Json::UInt>;
|
||||
|
||||
constexpr auto min = std::numeric_limits<jsontype>::min();
|
||||
constexpr auto max = std::numeric_limits<jsontype>::max();
|
||||
|
||||
if (fee_ < min)
|
||||
return min;
|
||||
if (fee_ > max)
|
||||
return max;
|
||||
return static_cast<jsontype>(fee_);
|
||||
}
|
||||
else
|
||||
{
|
||||
return fee_;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the underlying value. Code SHOULD NOT call this
|
||||
function unless the type has been abstracted away,
|
||||
e.g. in a templated function.
|
||||
*/
|
||||
constexpr value_type
|
||||
value() const
|
||||
{
|
||||
return fee_;
|
||||
}
|
||||
|
||||
friend std::istream&
|
||||
operator>>(std::istream& s, TaggedFee& val)
|
||||
{
|
||||
s >> val.fee_;
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
// Output Fees as just their numeric value.
|
||||
template <class Char, class Traits, class UnitTag, class T>
|
||||
std::basic_ostream<Char, Traits>&
|
||||
operator<<(std::basic_ostream<Char, Traits>& os, TaggedFee<UnitTag, T> const& q)
|
||||
{
|
||||
return os << q.value();
|
||||
}
|
||||
|
||||
template <class UnitTag, class T>
|
||||
std::string
|
||||
to_string(TaggedFee<UnitTag, T> const& amount)
|
||||
{
|
||||
return std::to_string(amount.fee());
|
||||
}
|
||||
|
||||
template <class Source, class = enable_if_unit_t<Source>>
|
||||
constexpr bool can_muldiv_source_v =
|
||||
std::is_convertible_v<typename Source::value_type, std::uint64_t>;
|
||||
|
||||
template <class Dest, class = enable_if_unit_t<Dest>>
|
||||
constexpr bool can_muldiv_dest_v =
|
||||
can_muldiv_source_v<Dest> && // Dest is also a source
|
||||
std::is_convertible_v<std::uint64_t, typename Dest::value_type> &&
|
||||
sizeof(typename Dest::value_type) >= sizeof(std::uint64_t);
|
||||
|
||||
template <
|
||||
class Source1,
|
||||
class Source2,
|
||||
class = enable_if_unit_t<Source1>,
|
||||
class = enable_if_unit_t<Source2>>
|
||||
constexpr bool can_muldiv_sources_v =
|
||||
can_muldiv_source_v<Source1> && can_muldiv_source_v<Source2> &&
|
||||
std::is_same_v<typename Source1::unit_type, typename Source2::unit_type>;
|
||||
|
||||
template <
|
||||
class Source1,
|
||||
class Source2,
|
||||
class Dest,
|
||||
class = enable_if_unit_t<Source1>,
|
||||
class = enable_if_unit_t<Source2>,
|
||||
class = enable_if_unit_t<Dest>>
|
||||
constexpr bool can_muldiv_v =
|
||||
can_muldiv_sources_v<Source1, Source2> && can_muldiv_dest_v<Dest>;
|
||||
// Source and Dest can be the same by default
|
||||
|
||||
template <
|
||||
class Source1,
|
||||
class Source2,
|
||||
class Dest,
|
||||
class = enable_if_unit_t<Source1>,
|
||||
class = enable_if_unit_t<Source2>,
|
||||
class = enable_if_unit_t<Dest>>
|
||||
constexpr bool can_muldiv_commute_v = can_muldiv_v<Source1, Source2, Dest> &&
|
||||
!std::is_same_v<typename Source1::unit_type, typename Dest::unit_type>;
|
||||
|
||||
template <class T>
|
||||
using enable_muldiv_source_t =
|
||||
typename std::enable_if_t<can_muldiv_source_v<T>>;
|
||||
|
||||
template <class T>
|
||||
using enable_muldiv_dest_t = typename std::enable_if_t<can_muldiv_dest_v<T>>;
|
||||
|
||||
template <class Source1, class Source2>
|
||||
using enable_muldiv_sources_t =
|
||||
typename std::enable_if_t<can_muldiv_sources_v<Source1, Source2>>;
|
||||
|
||||
template <class Source1, class Source2, class Dest>
|
||||
using enable_muldiv_t =
|
||||
typename std::enable_if_t<can_muldiv_v<Source1, Source2, Dest>>;
|
||||
|
||||
template <class Source1, class Source2, class Dest>
|
||||
using enable_muldiv_commute_t =
|
||||
typename std::enable_if_t<can_muldiv_commute_v<Source1, Source2, Dest>>;
|
||||
|
||||
template <class T>
|
||||
TaggedFee<unitlessTag, T>
|
||||
scalar(T value)
|
||||
{
|
||||
return TaggedFee<unitlessTag, T>{value};
|
||||
}
|
||||
|
||||
template <
|
||||
class Source1,
|
||||
class Source2,
|
||||
class Dest,
|
||||
class = enable_muldiv_t<Source1, Source2, Dest>>
|
||||
std::optional<Dest>
|
||||
mulDivU(Source1 value, Dest mul, Source2 div)
|
||||
{
|
||||
// Fees can never be negative in any context.
|
||||
if (value.value() < 0 || mul.value() < 0 || div.value() < 0)
|
||||
{
|
||||
// split the asserts so if one hits, the user can tell which
|
||||
// without a debugger.
|
||||
XRPL_ASSERT(
|
||||
value.value() >= 0,
|
||||
"ripple::feeunit::mulDivU : minimum value input");
|
||||
XRPL_ASSERT(
|
||||
mul.value() >= 0, "ripple::feeunit::mulDivU : minimum mul input");
|
||||
XRPL_ASSERT(
|
||||
div.value() >= 0, "ripple::feeunit::mulDivU : minimum div input");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
using desttype = typename Dest::value_type;
|
||||
constexpr auto max = std::numeric_limits<desttype>::max();
|
||||
|
||||
// Shortcuts, since these happen a lot in the real world
|
||||
if (value == div)
|
||||
return mul;
|
||||
if (mul.value() == div.value())
|
||||
{
|
||||
if (value.value() > max)
|
||||
return std::nullopt;
|
||||
return Dest{static_cast<desttype>(value.value())};
|
||||
}
|
||||
|
||||
using namespace boost::multiprecision;
|
||||
|
||||
uint128_t product;
|
||||
product = multiply(
|
||||
product,
|
||||
static_cast<std::uint64_t>(value.value()),
|
||||
static_cast<std::uint64_t>(mul.value()));
|
||||
|
||||
auto quotient = product / div.value();
|
||||
|
||||
if (quotient > max)
|
||||
return std::nullopt;
|
||||
|
||||
return Dest{static_cast<desttype>(quotient)};
|
||||
}
|
||||
|
||||
} // namespace feeunit
|
||||
|
||||
template <class T>
|
||||
using FeeLevel = feeunit::TaggedFee<feeunit::feelevelTag, T>;
|
||||
using FeeLevel64 = FeeLevel<std::uint64_t>;
|
||||
using FeeLevelDouble = FeeLevel<double>;
|
||||
|
||||
template <
|
||||
class Source1,
|
||||
class Source2,
|
||||
class Dest,
|
||||
class = feeunit::enable_muldiv_t<Source1, Source2, Dest>>
|
||||
std::optional<Dest>
|
||||
mulDiv(Source1 value, Dest mul, Source2 div)
|
||||
{
|
||||
return feeunit::mulDivU(value, mul, div);
|
||||
}
|
||||
|
||||
template <
|
||||
class Source1,
|
||||
class Source2,
|
||||
class Dest,
|
||||
class = feeunit::enable_muldiv_commute_t<Source1, Source2, Dest>>
|
||||
std::optional<Dest>
|
||||
mulDiv(Dest value, Source1 mul, Source2 div)
|
||||
{
|
||||
// Multiplication is commutative
|
||||
return feeunit::mulDivU(mul, value, div);
|
||||
}
|
||||
|
||||
template <class Dest, class = feeunit::enable_muldiv_dest_t<Dest>>
|
||||
std::optional<Dest>
|
||||
mulDiv(std::uint64_t value, Dest mul, std::uint64_t div)
|
||||
{
|
||||
// Give the scalars a non-tag so the
|
||||
// unit-handling version gets called.
|
||||
return feeunit::mulDivU(feeunit::scalar(value), mul, feeunit::scalar(div));
|
||||
}
|
||||
|
||||
template <class Dest, class = feeunit::enable_muldiv_dest_t<Dest>>
|
||||
std::optional<Dest>
|
||||
mulDiv(Dest value, std::uint64_t mul, std::uint64_t div)
|
||||
{
|
||||
// Multiplication is commutative
|
||||
return mulDiv(mul, value, div);
|
||||
}
|
||||
|
||||
template <
|
||||
class Source1,
|
||||
class Source2,
|
||||
class = feeunit::enable_muldiv_sources_t<Source1, Source2>>
|
||||
std::optional<std::uint64_t>
|
||||
mulDiv(Source1 value, std::uint64_t mul, Source2 div)
|
||||
{
|
||||
// Give the scalars a dimensionless unit so the
|
||||
// unit-handling version gets called.
|
||||
auto unitresult = feeunit::mulDivU(value, feeunit::scalar(mul), div);
|
||||
|
||||
if (!unitresult)
|
||||
return std::nullopt;
|
||||
|
||||
return unitresult->value();
|
||||
}
|
||||
|
||||
template <
|
||||
class Source1,
|
||||
class Source2,
|
||||
class = feeunit::enable_muldiv_sources_t<Source1, Source2>>
|
||||
std::optional<std::uint64_t>
|
||||
mulDiv(std::uint64_t value, Source1 mul, Source2 div)
|
||||
{
|
||||
// Multiplication is commutative
|
||||
return mulDiv(mul, value, div);
|
||||
}
|
||||
|
||||
template <class Dest, class Src>
|
||||
constexpr std::enable_if_t<
|
||||
std::is_same_v<typename Dest::unit_type, typename Src::unit_type> &&
|
||||
std::is_integral_v<typename Dest::value_type> &&
|
||||
std::is_integral_v<typename Src::value_type>,
|
||||
Dest>
|
||||
safe_cast(Src s) noexcept
|
||||
{
|
||||
// Dest may not have an explicit value constructor
|
||||
return Dest{safe_cast<typename Dest::value_type>(s.value())};
|
||||
}
|
||||
|
||||
template <class Dest, class Src>
|
||||
constexpr std::enable_if_t<
|
||||
std::is_same_v<typename Dest::unit_type, typename Src::unit_type> &&
|
||||
std::is_integral_v<typename Dest::value_type> &&
|
||||
std::is_integral_v<typename Src::value_type>,
|
||||
Dest>
|
||||
unsafe_cast(Src s) noexcept
|
||||
{
|
||||
// Dest may not have an explicit value constructor
|
||||
return Dest{unsafe_cast<typename Dest::value_type>(s.value())};
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif // BASICS_FEES_H_INCLUDED
|
||||
@@ -287,9 +287,11 @@ delegate(AccountID const& account, AccountID const& authorizedAccount) noexcept;
|
||||
Keylet
|
||||
bridge(STXChainBridge const& bridge, STXChainBridge::ChainType chainType);
|
||||
|
||||
// `seq` is stored as `sfXChainClaimID` in the object
|
||||
Keylet
|
||||
xChainClaimID(STXChainBridge const& bridge, std::uint64_t seq);
|
||||
|
||||
// `seq` is stored as `sfXChainAccountCreateCount` in the object
|
||||
Keylet
|
||||
xChainCreateAccountClaimID(STXChainBridge const& bridge, std::uint64_t seq);
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ enum LedgerEntryType : std::uint16_t
|
||||
#pragma push_macro("LEDGER_ENTRY")
|
||||
#undef LEDGER_ENTRY
|
||||
|
||||
#define LEDGER_ENTRY(tag, value, name, rpcName, fields) tag = value,
|
||||
#define LEDGER_ENTRY(tag, value, ...) tag = value,
|
||||
|
||||
#include <xrpl/protocol/detail/ledger_entries.macro>
|
||||
|
||||
@@ -188,6 +188,15 @@ enum LedgerSpecificFlags {
|
||||
lsfMPTCanTransfer = 0x00000020,
|
||||
lsfMPTCanClawback = 0x00000040,
|
||||
|
||||
lmfMPTCanMutateCanLock = 0x00000002,
|
||||
lmfMPTCanMutateRequireAuth = 0x00000004,
|
||||
lmfMPTCanMutateCanEscrow = 0x00000008,
|
||||
lmfMPTCanMutateCanTrade = 0x00000010,
|
||||
lmfMPTCanMutateCanTransfer = 0x00000020,
|
||||
lmfMPTCanMutateCanClawback = 0x00000040,
|
||||
lmfMPTCanMutateMetadata = 0x00010000,
|
||||
lmfMPTCanMutateTransferFee = 0x00020000,
|
||||
|
||||
// ltMPTOKEN
|
||||
lsfMPTAuthorized = 0x00000002,
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#ifndef RIPPLE_PROTOCOL_PERMISSION_H_INCLUDED
|
||||
#define RIPPLE_PROTOCOL_PERMISSION_H_INCLUDED
|
||||
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxFormats.h>
|
||||
|
||||
#include <optional>
|
||||
@@ -53,6 +55,8 @@ class Permission
|
||||
private:
|
||||
Permission();
|
||||
|
||||
std::unordered_map<std::uint16_t, uint256> txFeatureMap_;
|
||||
|
||||
std::unordered_map<std::uint16_t, Delegation> delegatableTx_;
|
||||
|
||||
std::unordered_map<std::string, GranularPermissionType>
|
||||
@@ -70,6 +74,9 @@ public:
|
||||
Permission&
|
||||
operator=(Permission const&) = delete;
|
||||
|
||||
std::optional<std::string>
|
||||
getPermissionName(std::uint32_t const value) const;
|
||||
|
||||
std::optional<std::uint32_t>
|
||||
getGranularValue(std::string const& name) const;
|
||||
|
||||
@@ -80,7 +87,8 @@ public:
|
||||
getGranularTxType(GranularPermissionType const& gpType) const;
|
||||
|
||||
bool
|
||||
isDelegatable(std::uint32_t const& permissionValue) const;
|
||||
isDelegatable(std::uint32_t const& permissionValue, Rules const& rules)
|
||||
const;
|
||||
|
||||
// for tx level permission, permission value is equal to tx type plus one
|
||||
uint32_t
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
#include <xrpl/basics/ByteUtilities.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/partitioned_unordered_map.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -122,6 +121,13 @@ std::size_t constexpr maxDataPayloadLength = 256;
|
||||
/** Vault withdrawal policies */
|
||||
std::uint8_t constexpr vaultStrategyFirstComeFirstServe = 1;
|
||||
|
||||
/** Default IOU scale factor for a Vault */
|
||||
std::uint8_t constexpr vaultDefaultIOUScale = 6;
|
||||
/** Maximum scale factor for a Vault. The number is chosen to ensure that
|
||||
1 IOU can be always converted to shares.
|
||||
10^19 > maxMPTokenAmount (2^64-1) > 10^18 */
|
||||
std::uint8_t constexpr vaultMaximumIOUScale = 18;
|
||||
|
||||
/** Maximum recursion depth for vault shares being put as an asset inside
|
||||
* another vault; counted from 0 */
|
||||
std::uint8_t constexpr maxAssetCheckDepth = 5;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/Units.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
@@ -148,8 +149,10 @@ public:
|
||||
sMD_ChangeNew = 0x02, // new value when it changes
|
||||
sMD_DeleteFinal = 0x04, // final value when it is deleted
|
||||
sMD_Create = 0x08, // value when it's created
|
||||
sMD_Always = 0x10, // value when node containing it is affected at all
|
||||
sMD_BaseTen = 0x20,
|
||||
sMD_Always = 0x10, // value when node containing it is affected at all
|
||||
sMD_BaseTen = 0x20, // value is treated as base 10, overriding behavior
|
||||
sMD_PseudoAccount = 0x40, // if this field is set in an ACCOUNT_ROOT
|
||||
// _only_, then it is a pseudo-account
|
||||
sMD_Default =
|
||||
sMD_ChangeOrig | sMD_ChangeNew | sMD_DeleteFinal | sMD_Create
|
||||
};
|
||||
@@ -184,7 +187,7 @@ public:
|
||||
char const* fn,
|
||||
int meta = sMD_Default,
|
||||
IsSigning signing = IsSigning::yes);
|
||||
explicit SField(private_access_tag_t, int fc);
|
||||
explicit SField(private_access_tag_t, int fc, char const* fn);
|
||||
|
||||
static SField const&
|
||||
getField(int fieldCode);
|
||||
@@ -297,7 +300,7 @@ public:
|
||||
static int
|
||||
compare(SField const& f1, SField const& f2);
|
||||
|
||||
static std::map<int, SField const*> const&
|
||||
static std::unordered_map<int, SField const*> const&
|
||||
getKnownCodeToField()
|
||||
{
|
||||
return knownCodeToField;
|
||||
@@ -305,7 +308,8 @@ public:
|
||||
|
||||
private:
|
||||
static int num;
|
||||
static std::map<int, SField const*> knownCodeToField;
|
||||
static std::unordered_map<int, SField const*> knownCodeToField;
|
||||
static std::unordered_map<std::string, SField const*> knownNameToField;
|
||||
};
|
||||
|
||||
/** A field with a type known at compile time. */
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
namespace ripple {
|
||||
|
||||
class Rules;
|
||||
namespace test {
|
||||
class Invariants_test;
|
||||
}
|
||||
|
||||
class STLedgerEntry final : public STObject, public CountedObject<STLedgerEntry>
|
||||
{
|
||||
@@ -36,6 +38,8 @@ class STLedgerEntry final : public STObject, public CountedObject<STLedgerEntry>
|
||||
public:
|
||||
using pointer = std::shared_ptr<STLedgerEntry>;
|
||||
using ref = std::shared_ptr<STLedgerEntry> const&;
|
||||
using const_pointer = std::shared_ptr<STLedgerEntry const>;
|
||||
using const_ref = std::shared_ptr<STLedgerEntry const> const&;
|
||||
|
||||
/** Create an empty object with the given key and type. */
|
||||
explicit STLedgerEntry(Keylet const& k);
|
||||
@@ -54,7 +58,7 @@ public:
|
||||
getText() const override;
|
||||
|
||||
Json::Value
|
||||
getJson(JsonOptions options) const override;
|
||||
getJson(JsonOptions options = JsonOptions::none) const override;
|
||||
|
||||
/** Returns the 'key' (or 'index') of this item.
|
||||
The key identifies this entry's position in
|
||||
@@ -84,7 +88,8 @@ private:
|
||||
void
|
||||
setSLEType();
|
||||
|
||||
friend Invariants_test; // this test wants access to the private type_
|
||||
friend test::Invariants_test; // this test wants access to the private
|
||||
// type_
|
||||
|
||||
STBase*
|
||||
copy(std::size_t n, void* buf) const override;
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/protocol/FeeUnits.h>
|
||||
#include <xrpl/protocol/HashPrefix.h>
|
||||
#include <xrpl/protocol/SOTemplate.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
@@ -34,6 +33,7 @@
|
||||
#include <xrpl/protocol/STIssue.h>
|
||||
#include <xrpl/protocol/STPathSet.h>
|
||||
#include <xrpl/protocol/STVector256.h>
|
||||
#include <xrpl/protocol/Units.h>
|
||||
#include <xrpl/protocol/detail/STVar.h>
|
||||
|
||||
#include <boost/iterator/transform_iterator.hpp>
|
||||
|
||||
@@ -54,34 +54,6 @@ public:
|
||||
Json::Value error;
|
||||
};
|
||||
|
||||
/** Holds the serialized result of parsing an input JSON array.
|
||||
This does validation and checking on the provided JSON.
|
||||
*/
|
||||
class STParsedJSONArray
|
||||
{
|
||||
public:
|
||||
/** Parses and creates an STParsedJSON array.
|
||||
The result of the parsing is stored in array and error.
|
||||
Exceptions:
|
||||
Does not throw.
|
||||
@param name The name of the JSON field, used in diagnostics.
|
||||
@param json The JSON-RPC to parse.
|
||||
*/
|
||||
STParsedJSONArray(std::string const& name, Json::Value const& json);
|
||||
|
||||
STParsedJSONArray() = delete;
|
||||
STParsedJSONArray(STParsedJSONArray const&) = delete;
|
||||
STParsedJSONArray&
|
||||
operator=(STParsedJSONArray const&) = delete;
|
||||
~STParsedJSONArray() = default;
|
||||
|
||||
/** The STArray if the parse was successful. */
|
||||
std::optional<STArray> array;
|
||||
|
||||
/** On failure, an appropriate set of error values. */
|
||||
Json::Value error;
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/protocol/FeeUnits.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/STObject.h>
|
||||
#include <xrpl/protocol/SecretKey.h>
|
||||
#include <xrpl/protocol/Units.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
@@ -127,6 +127,8 @@ constexpr std::uint32_t tfTrustSetPermissionMask = ~(tfUniversal | tfSetfAuth |
|
||||
// EnableAmendment flags:
|
||||
constexpr std::uint32_t tfGotMajority = 0x00010000;
|
||||
constexpr std::uint32_t tfLostMajority = 0x00020000;
|
||||
constexpr std::uint32_t tfChangeMask =
|
||||
~( tfUniversal | tfGotMajority | tfLostMajority);
|
||||
|
||||
// PaymentChannelClaim flags:
|
||||
constexpr std::uint32_t tfRenew = 0x00010000;
|
||||
@@ -141,7 +143,8 @@ constexpr std::uint32_t const tfTransferable = 0x00000008;
|
||||
constexpr std::uint32_t const tfMutable = 0x00000010;
|
||||
|
||||
// MPTokenIssuanceCreate flags:
|
||||
// NOTE - there is intentionally no flag here for lsfMPTLocked, which this transaction cannot mutate.
|
||||
// Note: tf/lsfMPTLocked is intentionally omitted, since this transaction
|
||||
// is not allowed to modify it.
|
||||
constexpr std::uint32_t const tfMPTCanLock = lsfMPTCanLock;
|
||||
constexpr std::uint32_t const tfMPTRequireAuth = lsfMPTRequireAuth;
|
||||
constexpr std::uint32_t const tfMPTCanEscrow = lsfMPTCanEscrow;
|
||||
@@ -151,6 +154,20 @@ constexpr std::uint32_t const tfMPTCanClawback = lsfMPTCanClawback;
|
||||
constexpr std::uint32_t const tfMPTokenIssuanceCreateMask =
|
||||
~(tfUniversal | tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | tfMPTCanClawback);
|
||||
|
||||
// MPTokenIssuanceCreate MutableFlags:
|
||||
// Indicating specific fields or flags may be changed after issuance.
|
||||
constexpr std::uint32_t const tmfMPTCanMutateCanLock = lmfMPTCanMutateCanLock;
|
||||
constexpr std::uint32_t const tmfMPTCanMutateRequireAuth = lmfMPTCanMutateRequireAuth;
|
||||
constexpr std::uint32_t const tmfMPTCanMutateCanEscrow = lmfMPTCanMutateCanEscrow;
|
||||
constexpr std::uint32_t const tmfMPTCanMutateCanTrade = lmfMPTCanMutateCanTrade;
|
||||
constexpr std::uint32_t const tmfMPTCanMutateCanTransfer = lmfMPTCanMutateCanTransfer;
|
||||
constexpr std::uint32_t const tmfMPTCanMutateCanClawback = lmfMPTCanMutateCanClawback;
|
||||
constexpr std::uint32_t const tmfMPTCanMutateMetadata = lmfMPTCanMutateMetadata;
|
||||
constexpr std::uint32_t const tmfMPTCanMutateTransferFee = lmfMPTCanMutateTransferFee;
|
||||
constexpr std::uint32_t const tmfMPTokenIssuanceCreateMutableMask =
|
||||
~(tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow | tmfMPTCanMutateCanTrade
|
||||
| tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback | tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee);
|
||||
|
||||
// MPTokenAuthorize flags:
|
||||
constexpr std::uint32_t const tfMPTUnauthorize = 0x00000001;
|
||||
constexpr std::uint32_t const tfMPTokenAuthorizeMask = ~(tfUniversal | tfMPTUnauthorize);
|
||||
@@ -161,6 +178,25 @@ constexpr std::uint32_t const tfMPTUnlock = 0x00000002;
|
||||
constexpr std::uint32_t const tfMPTokenIssuanceSetMask = ~(tfUniversal | tfMPTLock | tfMPTUnlock);
|
||||
constexpr std::uint32_t const tfMPTokenIssuanceSetPermissionMask = ~(tfUniversal | tfMPTLock | tfMPTUnlock);
|
||||
|
||||
// MPTokenIssuanceSet MutableFlags:
|
||||
// Set or Clear flags.
|
||||
constexpr std::uint32_t const tmfMPTSetCanLock = 0x00000001;
|
||||
constexpr std::uint32_t const tmfMPTClearCanLock = 0x00000002;
|
||||
constexpr std::uint32_t const tmfMPTSetRequireAuth = 0x00000004;
|
||||
constexpr std::uint32_t const tmfMPTClearRequireAuth = 0x00000008;
|
||||
constexpr std::uint32_t const tmfMPTSetCanEscrow = 0x00000010;
|
||||
constexpr std::uint32_t const tmfMPTClearCanEscrow = 0x00000020;
|
||||
constexpr std::uint32_t const tmfMPTSetCanTrade = 0x00000040;
|
||||
constexpr std::uint32_t const tmfMPTClearCanTrade = 0x00000080;
|
||||
constexpr std::uint32_t const tmfMPTSetCanTransfer = 0x00000100;
|
||||
constexpr std::uint32_t const tmfMPTClearCanTransfer = 0x00000200;
|
||||
constexpr std::uint32_t const tmfMPTSetCanClawback = 0x00000400;
|
||||
constexpr std::uint32_t const tmfMPTClearCanClawback = 0x00000800;
|
||||
constexpr std::uint32_t const tmfMPTokenIssuanceSetMutableMask = ~(tmfMPTSetCanLock | tmfMPTClearCanLock |
|
||||
tmfMPTSetRequireAuth | tmfMPTClearRequireAuth | tmfMPTSetCanEscrow | tmfMPTClearCanEscrow |
|
||||
tmfMPTSetCanTrade | tmfMPTClearCanTrade | tmfMPTSetCanTransfer | tmfMPTClearCanTransfer |
|
||||
tmfMPTSetCanClawback | tmfMPTClearCanClawback);
|
||||
|
||||
// MPTokenIssuanceDestroy flags:
|
||||
constexpr std::uint32_t const tfMPTokenIssuanceDestroyMask = ~tfUniversal;
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ enum TxType : std::uint16_t
|
||||
#pragma push_macro("TRANSACTION")
|
||||
#undef TRANSACTION
|
||||
|
||||
#define TRANSACTION(tag, value, name, delegatable, fields) tag = value,
|
||||
#define TRANSACTION(tag, value, ...) tag = value,
|
||||
|
||||
#include <xrpl/protocol/detail/transactions.macro>
|
||||
|
||||
|
||||
555
include/xrpl/protocol/Units.h
Normal file
555
include/xrpl/protocol/Units.h
Normal file
@@ -0,0 +1,555 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2019 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef PROTOCOL_UNITS_H_INCLUDED
|
||||
#define PROTOCOL_UNITS_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
#include <boost/operators.hpp>
|
||||
|
||||
#include <iosfwd>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace unit {
|
||||
|
||||
/** "drops" are the smallest divisible amount of XRP. This is what most
|
||||
of the code uses. */
|
||||
struct dropTag;
|
||||
/** "fee levels" are used by the transaction queue to compare the relative
|
||||
cost of transactions that require different levels of effort to process.
|
||||
See also: src/ripple/app/misc/FeeEscalation.md#fee-level */
|
||||
struct feelevelTag;
|
||||
/** unitless values are plain scalars wrapped in a ValueUnit. They are
|
||||
used for calculations in this header. */
|
||||
struct unitlessTag;
|
||||
|
||||
/** Units to represent basis points (bips) and 1/10 basis points */
|
||||
class BipsTag;
|
||||
class TenthBipsTag;
|
||||
|
||||
// These names don't have to be too descriptive, because we're in the "unit"
|
||||
// namespace.
|
||||
|
||||
template <class T>
|
||||
concept Valid = std::is_class_v<T> && std::is_object_v<typename T::unit_type> &&
|
||||
std::is_object_v<typename T::value_type>;
|
||||
|
||||
/** `Usable` is checked to ensure that only values with
|
||||
known valid type tags can be used (sometimes transparently) in
|
||||
non-unit contexts. At the time of implementation, this includes
|
||||
all known tags, but more may be added in the future, and they
|
||||
should not be added automatically unless determined to be
|
||||
appropriate.
|
||||
*/
|
||||
template <class T>
|
||||
concept Usable = Valid<T> &&
|
||||
(std::is_same_v<typename T::unit_type, feelevelTag> ||
|
||||
std::is_same_v<typename T::unit_type, unitlessTag> ||
|
||||
std::is_same_v<typename T::unit_type, dropTag> ||
|
||||
std::is_same_v<typename T::unit_type, BipsTag> ||
|
||||
std::is_same_v<typename T::unit_type, TenthBipsTag>);
|
||||
|
||||
template <class Other, class VU>
|
||||
concept Compatible = Valid<VU> && std::is_arithmetic_v<Other> &&
|
||||
std::is_arithmetic_v<typename VU::value_type> &&
|
||||
std::is_convertible_v<Other, typename VU::value_type>;
|
||||
|
||||
template <class T>
|
||||
concept Integral = std::is_integral_v<T>;
|
||||
|
||||
template <class VU>
|
||||
concept IntegralValue = Integral<typename VU::value_type>;
|
||||
|
||||
template <class VU1, class VU2>
|
||||
concept CastableValue = IntegralValue<VU1> && IntegralValue<VU2> &&
|
||||
std::is_same_v<typename VU1::unit_type, typename VU2::unit_type>;
|
||||
|
||||
template <class UnitTag, class T>
|
||||
class ValueUnit : private boost::totally_ordered<ValueUnit<UnitTag, T>>,
|
||||
private boost::additive<ValueUnit<UnitTag, T>>,
|
||||
private boost::equality_comparable<ValueUnit<UnitTag, T>, T>,
|
||||
private boost::dividable<ValueUnit<UnitTag, T>, T>,
|
||||
private boost::modable<ValueUnit<UnitTag, T>, T>,
|
||||
private boost::unit_steppable<ValueUnit<UnitTag, T>>
|
||||
{
|
||||
public:
|
||||
using unit_type = UnitTag;
|
||||
using value_type = T;
|
||||
|
||||
private:
|
||||
value_type value_;
|
||||
|
||||
public:
|
||||
ValueUnit() = default;
|
||||
constexpr ValueUnit(ValueUnit const& other) = default;
|
||||
constexpr ValueUnit&
|
||||
operator=(ValueUnit const& other) = default;
|
||||
|
||||
constexpr explicit ValueUnit(beast::Zero) : value_(0)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr ValueUnit&
|
||||
operator=(beast::Zero)
|
||||
{
|
||||
value_ = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr explicit ValueUnit(value_type value) : value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr ValueUnit&
|
||||
operator=(value_type value)
|
||||
{
|
||||
value_ = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Instances with the same unit, and a type that is
|
||||
"safe" to convert to this one can be converted
|
||||
implicitly */
|
||||
template <Compatible<ValueUnit> Other>
|
||||
constexpr ValueUnit(ValueUnit<unit_type, Other> const& value)
|
||||
requires SafeToCast<Other, value_type>
|
||||
: ValueUnit(safe_cast<value_type>(value.value()))
|
||||
{
|
||||
}
|
||||
|
||||
constexpr ValueUnit
|
||||
operator+(value_type const& rhs) const
|
||||
{
|
||||
return ValueUnit{value_ + rhs};
|
||||
}
|
||||
|
||||
friend constexpr ValueUnit
|
||||
operator+(value_type lhs, ValueUnit const& rhs)
|
||||
{
|
||||
// addition is commutative
|
||||
return rhs + lhs;
|
||||
}
|
||||
|
||||
constexpr ValueUnit
|
||||
operator-(value_type const& rhs) const
|
||||
{
|
||||
return ValueUnit{value_ - rhs};
|
||||
}
|
||||
|
||||
friend constexpr ValueUnit
|
||||
operator-(value_type lhs, ValueUnit const& rhs)
|
||||
{
|
||||
// subtraction is NOT commutative, but (lhs + (-rhs)) is addition, which
|
||||
// is
|
||||
return -rhs + lhs;
|
||||
}
|
||||
|
||||
constexpr ValueUnit
|
||||
operator*(value_type const& rhs) const
|
||||
{
|
||||
return ValueUnit{value_ * rhs};
|
||||
}
|
||||
|
||||
friend constexpr ValueUnit
|
||||
operator*(value_type lhs, ValueUnit const& rhs)
|
||||
{
|
||||
// multiplication is commutative
|
||||
return rhs * lhs;
|
||||
}
|
||||
|
||||
constexpr value_type
|
||||
operator/(ValueUnit const& rhs) const
|
||||
{
|
||||
return value_ / rhs.value_;
|
||||
}
|
||||
|
||||
ValueUnit&
|
||||
operator+=(ValueUnit const& other)
|
||||
{
|
||||
value_ += other.value();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ValueUnit&
|
||||
operator-=(ValueUnit const& other)
|
||||
{
|
||||
value_ -= other.value();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ValueUnit&
|
||||
operator++()
|
||||
{
|
||||
++value_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ValueUnit&
|
||||
operator--()
|
||||
{
|
||||
--value_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ValueUnit&
|
||||
operator*=(value_type const& rhs)
|
||||
{
|
||||
value_ *= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ValueUnit&
|
||||
operator/=(value_type const& rhs)
|
||||
{
|
||||
value_ /= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <Integral transparent = value_type>
|
||||
ValueUnit&
|
||||
operator%=(value_type const& rhs)
|
||||
{
|
||||
value_ %= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ValueUnit
|
||||
operator-() const
|
||||
{
|
||||
static_assert(
|
||||
std::is_signed_v<T>, "- operator illegal on unsigned value types");
|
||||
return ValueUnit{-value_};
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
operator==(ValueUnit const& other) const
|
||||
{
|
||||
return value_ == other.value_;
|
||||
}
|
||||
|
||||
template <Compatible<ValueUnit> Other>
|
||||
constexpr bool
|
||||
operator==(ValueUnit<unit_type, Other> const& other) const
|
||||
{
|
||||
return value_ == other.value();
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
operator==(value_type other) const
|
||||
{
|
||||
return value_ == other;
|
||||
}
|
||||
|
||||
template <Compatible<ValueUnit> Other>
|
||||
constexpr bool
|
||||
operator!=(ValueUnit<unit_type, Other> const& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
operator<(ValueUnit const& other) const
|
||||
{
|
||||
return value_ < other.value_;
|
||||
}
|
||||
|
||||
/** Returns true if the amount is not zero */
|
||||
explicit constexpr
|
||||
operator bool() const noexcept
|
||||
{
|
||||
return value_ != 0;
|
||||
}
|
||||
|
||||
/** Return the sign of the amount */
|
||||
constexpr int
|
||||
signum() const noexcept
|
||||
{
|
||||
return (value_ < 0) ? -1 : (value_ ? 1 : 0);
|
||||
}
|
||||
|
||||
/** Returns the number of drops */
|
||||
// TODO: Move this to a new class, maybe with the old "TaggedFee" name
|
||||
constexpr value_type
|
||||
fee() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
template <class Other>
|
||||
constexpr double
|
||||
decimalFromReference(ValueUnit<unit_type, Other> reference) const
|
||||
{
|
||||
return static_cast<double>(value_) / reference.value();
|
||||
}
|
||||
|
||||
// `Usable` is checked to ensure that only values with
|
||||
// known valid type tags can be converted to JSON. At the time
|
||||
// of implementation, that includes all known tags, but more may
|
||||
// be added in the future.
|
||||
Json::Value
|
||||
jsonClipped() const
|
||||
requires Usable<ValueUnit>
|
||||
{
|
||||
if constexpr (std::is_integral_v<value_type>)
|
||||
{
|
||||
using jsontype = std::conditional_t<
|
||||
std::is_signed_v<value_type>,
|
||||
Json::Int,
|
||||
Json::UInt>;
|
||||
|
||||
constexpr auto min = std::numeric_limits<jsontype>::min();
|
||||
constexpr auto max = std::numeric_limits<jsontype>::max();
|
||||
|
||||
if (value_ < min)
|
||||
return min;
|
||||
if (value_ > max)
|
||||
return max;
|
||||
return static_cast<jsontype>(value_);
|
||||
}
|
||||
else
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the underlying value. Code SHOULD NOT call this
|
||||
function unless the type has been abstracted away,
|
||||
e.g. in a templated function.
|
||||
*/
|
||||
constexpr value_type
|
||||
value() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
friend std::istream&
|
||||
operator>>(std::istream& s, ValueUnit& val)
|
||||
{
|
||||
s >> val.value_;
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
// Output Values as just their numeric value.
|
||||
template <class Char, class Traits, class UnitTag, class T>
|
||||
std::basic_ostream<Char, Traits>&
|
||||
operator<<(std::basic_ostream<Char, Traits>& os, ValueUnit<UnitTag, T> const& q)
|
||||
{
|
||||
return os << q.value();
|
||||
}
|
||||
|
||||
template <class UnitTag, class T>
|
||||
std::string
|
||||
to_string(ValueUnit<UnitTag, T> const& amount)
|
||||
{
|
||||
return std::to_string(amount.value());
|
||||
}
|
||||
|
||||
template <class Source>
|
||||
concept muldivSource = Valid<Source> &&
|
||||
std::is_convertible_v<typename Source::value_type, std::uint64_t>;
|
||||
|
||||
template <class Dest>
|
||||
concept muldivDest = muldivSource<Dest> && // Dest is also a source
|
||||
std::is_convertible_v<std::uint64_t, typename Dest::value_type> &&
|
||||
sizeof(typename Dest::value_type) >= sizeof(std::uint64_t);
|
||||
|
||||
template <class Source2, class Source1>
|
||||
concept muldivSources = muldivSource<Source1> && muldivSource<Source2> &&
|
||||
std::is_same_v<typename Source1::unit_type, typename Source2::unit_type>;
|
||||
|
||||
template <class Dest, class Source1, class Source2>
|
||||
concept muldivable = muldivSources<Source1, Source2> && muldivDest<Dest>;
|
||||
// Source and Dest can be the same by default
|
||||
|
||||
template <class Dest, class Source1, class Source2>
|
||||
concept muldivCommutable = muldivable<Dest, Source1, Source2> &&
|
||||
!std::is_same_v<typename Source1::unit_type, typename Dest::unit_type>;
|
||||
|
||||
template <class T>
|
||||
ValueUnit<unitlessTag, T>
|
||||
scalar(T value)
|
||||
{
|
||||
return ValueUnit<unitlessTag, T>{value};
|
||||
}
|
||||
|
||||
template <class Source1, class Source2, unit::muldivable<Source1, Source2> Dest>
|
||||
std::optional<Dest>
|
||||
mulDivU(Source1 value, Dest mul, Source2 div)
|
||||
{
|
||||
// values can never be negative in any context.
|
||||
if (value.value() < 0 || mul.value() < 0 || div.value() < 0)
|
||||
{
|
||||
// split the asserts so if one hits, the user can tell which
|
||||
// without a debugger.
|
||||
XRPL_ASSERT(
|
||||
value.value() >= 0, "ripple::unit::mulDivU : minimum value input");
|
||||
XRPL_ASSERT(
|
||||
mul.value() >= 0, "ripple::unit::mulDivU : minimum mul input");
|
||||
XRPL_ASSERT(
|
||||
div.value() > 0, "ripple::unit::mulDivU : minimum div input");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
using desttype = typename Dest::value_type;
|
||||
constexpr auto max = std::numeric_limits<desttype>::max();
|
||||
|
||||
// Shortcuts, since these happen a lot in the real world
|
||||
if (value == div)
|
||||
return mul;
|
||||
if (mul.value() == div.value())
|
||||
{
|
||||
if (value.value() > max)
|
||||
return std::nullopt;
|
||||
return Dest{static_cast<desttype>(value.value())};
|
||||
}
|
||||
|
||||
using namespace boost::multiprecision;
|
||||
|
||||
uint128_t product;
|
||||
product = multiply(
|
||||
product,
|
||||
static_cast<std::uint64_t>(value.value()),
|
||||
static_cast<std::uint64_t>(mul.value()));
|
||||
|
||||
auto quotient = product / div.value();
|
||||
|
||||
if (quotient > max)
|
||||
return std::nullopt;
|
||||
|
||||
return Dest{static_cast<desttype>(quotient)};
|
||||
}
|
||||
|
||||
} // namespace unit
|
||||
|
||||
// Fee Levels
|
||||
template <class T>
|
||||
using FeeLevel = unit::ValueUnit<unit::feelevelTag, T>;
|
||||
using FeeLevel64 = FeeLevel<std::uint64_t>;
|
||||
using FeeLevelDouble = FeeLevel<double>;
|
||||
|
||||
// Basis points (Bips)
|
||||
template <class T>
|
||||
using Bips = unit::ValueUnit<unit::BipsTag, T>;
|
||||
using Bips16 = Bips<std::uint16_t>;
|
||||
using Bips32 = Bips<std::uint32_t>;
|
||||
template <class T>
|
||||
using TenthBips = unit::ValueUnit<unit::TenthBipsTag, T>;
|
||||
using TenthBips16 = TenthBips<std::uint16_t>;
|
||||
using TenthBips32 = TenthBips<std::uint32_t>;
|
||||
|
||||
template <class Source1, class Source2, unit::muldivable<Source1, Source2> Dest>
|
||||
std::optional<Dest>
|
||||
mulDiv(Source1 value, Dest mul, Source2 div)
|
||||
{
|
||||
return unit::mulDivU(value, mul, div);
|
||||
}
|
||||
|
||||
template <
|
||||
class Source1,
|
||||
class Source2,
|
||||
unit::muldivCommutable<Source1, Source2> Dest>
|
||||
std::optional<Dest>
|
||||
mulDiv(Dest value, Source1 mul, Source2 div)
|
||||
{
|
||||
// Multiplication is commutative
|
||||
return unit::mulDivU(mul, value, div);
|
||||
}
|
||||
|
||||
template <unit::muldivDest Dest>
|
||||
std::optional<Dest>
|
||||
mulDiv(std::uint64_t value, Dest mul, std::uint64_t div)
|
||||
{
|
||||
// Give the scalars a non-tag so the
|
||||
// unit-handling version gets called.
|
||||
return unit::mulDivU(unit::scalar(value), mul, unit::scalar(div));
|
||||
}
|
||||
|
||||
template <unit::muldivDest Dest>
|
||||
std::optional<Dest>
|
||||
mulDiv(Dest value, std::uint64_t mul, std::uint64_t div)
|
||||
{
|
||||
// Multiplication is commutative
|
||||
return mulDiv(mul, value, div);
|
||||
}
|
||||
|
||||
template <unit::muldivSource Source1, unit::muldivSources<Source1> Source2>
|
||||
std::optional<std::uint64_t>
|
||||
mulDiv(Source1 value, std::uint64_t mul, Source2 div)
|
||||
{
|
||||
// Give the scalars a dimensionless unit so the
|
||||
// unit-handling version gets called.
|
||||
auto unitresult = unit::mulDivU(value, unit::scalar(mul), div);
|
||||
|
||||
if (!unitresult)
|
||||
return std::nullopt;
|
||||
|
||||
return unitresult->value();
|
||||
}
|
||||
|
||||
template <unit::muldivSource Source1, unit::muldivSources<Source1> Source2>
|
||||
std::optional<std::uint64_t>
|
||||
mulDiv(std::uint64_t value, Source1 mul, Source2 div)
|
||||
{
|
||||
// Multiplication is commutative
|
||||
return mulDiv(mul, value, div);
|
||||
}
|
||||
|
||||
template <unit::IntegralValue Dest, unit::CastableValue<Dest> Src>
|
||||
constexpr Dest
|
||||
safe_cast(Src s) noexcept
|
||||
{
|
||||
// Dest may not have an explicit value constructor
|
||||
return Dest{safe_cast<typename Dest::value_type>(s.value())};
|
||||
}
|
||||
|
||||
template <unit::IntegralValue Dest, unit::Integral Src>
|
||||
constexpr Dest
|
||||
safe_cast(Src s) noexcept
|
||||
{
|
||||
// Dest may not have an explicit value constructor
|
||||
return Dest{safe_cast<typename Dest::value_type>(s)};
|
||||
}
|
||||
|
||||
template <unit::IntegralValue Dest, unit::CastableValue<Dest> Src>
|
||||
constexpr Dest
|
||||
unsafe_cast(Src s) noexcept
|
||||
{
|
||||
// Dest may not have an explicit value constructor
|
||||
return Dest{unsafe_cast<typename Dest::value_type>(s.value())};
|
||||
}
|
||||
|
||||
template <unit::IntegralValue Dest, unit::Integral Src>
|
||||
constexpr Dest
|
||||
unsafe_cast(Src s) noexcept
|
||||
{
|
||||
// Dest may not have an explicit value constructor
|
||||
return Dest{unsafe_cast<typename Dest::value_type>(s)};
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif // PROTOCOL_UNITS_H_INCLUDED
|
||||
@@ -24,7 +24,7 @@
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/FeeUnits.h>
|
||||
#include <xrpl/protocol/Units.h>
|
||||
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
#include <boost/operators.hpp>
|
||||
@@ -42,7 +42,7 @@ class XRPAmount : private boost::totally_ordered<XRPAmount>,
|
||||
private boost::additive<XRPAmount, std::int64_t>
|
||||
{
|
||||
public:
|
||||
using unit_type = feeunit::dropTag;
|
||||
using unit_type = unit::dropTag;
|
||||
using value_type = std::int64_t;
|
||||
|
||||
private:
|
||||
|
||||
@@ -32,9 +32,13 @@
|
||||
// If you add an amendment here, then do not forget to increment `numFeatures`
|
||||
// in include/xrpl/protocol/Feature.h.
|
||||
|
||||
XRPL_FIX (PriceOracleOrder, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (IncludeKeyletFields, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(DynamicMPT, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (TokenEscrowV1, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (DelegateV1_1, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (PriceOracleOrder, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (MPTDeliveredAmount, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (AMMClawbackRounding, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (AMMClawbackRounding, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(TokenEscrow, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (EnforceNFTokenTrustlineV2, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (AMMv1_3, Supported::yes, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -120,6 +120,7 @@ LEDGER_ENTRY(ltNFTOKEN_PAGE, 0x0050, NFTokenPage, nft_page, ({
|
||||
// All fields are soeREQUIRED because there is always a SignerEntries.
|
||||
// If there are no SignerEntries the node is deleted.
|
||||
LEDGER_ENTRY(ltSIGNER_LIST, 0x0053, SignerList, signer_list, ({
|
||||
{sfOwner, soeOPTIONAL},
|
||||
{sfOwnerNode, soeREQUIRED},
|
||||
{sfSignerQuorum, soeREQUIRED},
|
||||
{sfSignerEntries, soeREQUIRED},
|
||||
@@ -188,7 +189,7 @@ LEDGER_ENTRY(ltDIR_NODE, 0x0064, DirectoryNode, directory, ({
|
||||
{sfNFTokenID, soeOPTIONAL},
|
||||
{sfPreviousTxnID, soeOPTIONAL},
|
||||
{sfPreviousTxnLgrSeq, soeOPTIONAL},
|
||||
{sfDomainID, soeOPTIONAL}
|
||||
{sfDomainID, soeOPTIONAL} // order book directories
|
||||
}))
|
||||
|
||||
/** The ledger object which lists details about amendments on the network.
|
||||
@@ -343,6 +344,7 @@ LEDGER_ENTRY(ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID, 0x0074, XChainOwnedCreateAc
|
||||
*/
|
||||
LEDGER_ENTRY(ltESCROW, 0x0075, Escrow, escrow, ({
|
||||
{sfAccount, soeREQUIRED},
|
||||
{sfSequence, soeOPTIONAL},
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfCondition, soeOPTIONAL},
|
||||
@@ -365,6 +367,7 @@ LEDGER_ENTRY(ltESCROW, 0x0075, Escrow, escrow, ({
|
||||
LEDGER_ENTRY(ltPAYCHAN, 0x0078, PayChannel, payment_channel, ({
|
||||
{sfAccount, soeREQUIRED},
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfSequence, soeOPTIONAL},
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfBalance, soeREQUIRED},
|
||||
{sfPublicKey, soeREQUIRED},
|
||||
@@ -412,6 +415,7 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
|
||||
{sfPreviousTxnID, soeREQUIRED},
|
||||
{sfPreviousTxnLgrSeq, soeREQUIRED},
|
||||
{sfDomainID, soeOPTIONAL},
|
||||
{sfMutableFlags, soeDEFAULT},
|
||||
}))
|
||||
|
||||
/** A ledger object which tracks MPToken
|
||||
@@ -432,6 +436,7 @@ LEDGER_ENTRY(ltMPTOKEN, 0x007f, MPToken, mptoken, ({
|
||||
*/
|
||||
LEDGER_ENTRY(ltORACLE, 0x0080, Oracle, oracle, ({
|
||||
{sfOwner, soeREQUIRED},
|
||||
{sfOracleDocumentID, soeOPTIONAL},
|
||||
{sfProvider, soeREQUIRED},
|
||||
{sfPriceDataSeries, soeREQUIRED},
|
||||
{sfAssetClass, soeREQUIRED},
|
||||
@@ -499,6 +504,7 @@ LEDGER_ENTRY(ltVAULT, 0x0084, Vault, vault, ({
|
||||
{sfLossUnrealized, soeREQUIRED},
|
||||
{sfShareMPTID, soeREQUIRED},
|
||||
{sfWithdrawalPolicy, soeREQUIRED},
|
||||
{sfScale, soeDEFAULT},
|
||||
// no SharesTotal ever (use MPTIssuance.sfOutstandingAmount)
|
||||
// no PermissionedDomainID ever (use MPTIssuance.sfDomainID)
|
||||
}))
|
||||
|
||||
@@ -114,6 +114,7 @@ TYPED_SFIELD(sfVoteWeight, UINT32, 48)
|
||||
TYPED_SFIELD(sfFirstNFTokenSequence, UINT32, 50)
|
||||
TYPED_SFIELD(sfOracleDocumentID, UINT32, 51)
|
||||
TYPED_SFIELD(sfPermissionValue, UINT32, 52)
|
||||
TYPED_SFIELD(sfMutableFlags, UINT32, 53)
|
||||
|
||||
// 64-bit integers (common)
|
||||
TYPED_SFIELD(sfIndexNext, UINT64, 1)
|
||||
@@ -173,7 +174,8 @@ TYPED_SFIELD(sfNFTokenID, UINT256, 10)
|
||||
TYPED_SFIELD(sfEmitParentTxnID, UINT256, 11)
|
||||
TYPED_SFIELD(sfEmitNonce, UINT256, 12)
|
||||
TYPED_SFIELD(sfEmitHookHash, UINT256, 13)
|
||||
TYPED_SFIELD(sfAMMID, UINT256, 14)
|
||||
TYPED_SFIELD(sfAMMID, UINT256, 14,
|
||||
SField::sMD_PseudoAccount | SField::sMD_Default)
|
||||
|
||||
// 256-bit (uncommon)
|
||||
TYPED_SFIELD(sfBookDirectory, UINT256, 16)
|
||||
@@ -195,7 +197,8 @@ TYPED_SFIELD(sfHookHash, UINT256, 31)
|
||||
TYPED_SFIELD(sfHookNamespace, UINT256, 32)
|
||||
TYPED_SFIELD(sfHookSetTxnID, UINT256, 33)
|
||||
TYPED_SFIELD(sfDomainID, UINT256, 34)
|
||||
TYPED_SFIELD(sfVaultID, UINT256, 35)
|
||||
TYPED_SFIELD(sfVaultID, UINT256, 35,
|
||||
SField::sMD_PseudoAccount | SField::sMD_Default)
|
||||
TYPED_SFIELD(sfParentBatchID, UINT256, 36)
|
||||
|
||||
// number (common)
|
||||
|
||||
@@ -22,14 +22,32 @@
|
||||
#endif
|
||||
|
||||
/**
|
||||
* TRANSACTION(tag, value, name, delegatable, fields)
|
||||
* TRANSACTION(tag, value, name, delegatable, amendments, privileges, fields)
|
||||
*
|
||||
* To ease maintenance, you may replace any unneeded values with "..."
|
||||
* e.g. #define TRANSACTION(tag, value, name, ...)
|
||||
*
|
||||
* You must define a transactor class in the `ripple` namespace named `name`,
|
||||
* and include its header in `src/xrpld/app/tx/detail/applySteps.cpp`.
|
||||
* and include its header alongside the TRANSACTOR definition using this
|
||||
* format:
|
||||
* #if TRANSACTION_INCLUDE
|
||||
* # include <xrpld/app/tx/detail/HEADER.h>
|
||||
* #endif
|
||||
*
|
||||
* The `privileges` parameter of the TRANSACTION macro is a bitfield
|
||||
* defining which operations the transaction can perform.
|
||||
* The values are defined and used in InvariantCheck.cpp
|
||||
*/
|
||||
|
||||
/** This transaction type executes a payment. */
|
||||
TRANSACTION(ttPAYMENT, 0, Payment, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Payment.h>
|
||||
#endif
|
||||
TRANSACTION(ttPAYMENT, 0, Payment,
|
||||
Delegation::delegatable,
|
||||
uint256{},
|
||||
createAcct,
|
||||
({
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||
{sfSendMax, soeOPTIONAL, soeMPTSupported},
|
||||
@@ -42,7 +60,14 @@ TRANSACTION(ttPAYMENT, 0, Payment, Delegation::delegatable, ({
|
||||
}))
|
||||
|
||||
/** This transaction type creates an escrow object. */
|
||||
TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Escrow.h>
|
||||
#endif
|
||||
TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate,
|
||||
Delegation::delegatable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
({
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||
{sfCondition, soeOPTIONAL},
|
||||
@@ -52,7 +77,11 @@ TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, Delegation::delegatable, ({
|
||||
}))
|
||||
|
||||
/** This transaction type completes an existing escrow. */
|
||||
TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, Delegation::delegatable, ({
|
||||
TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish,
|
||||
Delegation::delegatable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
({
|
||||
{sfOwner, soeREQUIRED},
|
||||
{sfOfferSequence, soeREQUIRED},
|
||||
{sfFulfillment, soeOPTIONAL},
|
||||
@@ -62,7 +91,14 @@ TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, Delegation::delegatable, ({
|
||||
|
||||
|
||||
/** This transaction type adjusts various account settings. */
|
||||
TRANSACTION(ttACCOUNT_SET, 3, AccountSet, Delegation::notDelegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/SetAccount.h>
|
||||
#endif
|
||||
TRANSACTION(ttACCOUNT_SET, 3, AccountSet,
|
||||
Delegation::notDelegatable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
({
|
||||
{sfEmailHash, soeOPTIONAL},
|
||||
{sfWalletLocator, soeOPTIONAL},
|
||||
{sfWalletSize, soeOPTIONAL},
|
||||
@@ -76,20 +112,41 @@ TRANSACTION(ttACCOUNT_SET, 3, AccountSet, Delegation::notDelegatable, ({
|
||||
}))
|
||||
|
||||
/** This transaction type cancels an existing escrow. */
|
||||
TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Escrow.h>
|
||||
#endif
|
||||
TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel,
|
||||
Delegation::delegatable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
({
|
||||
{sfOwner, soeREQUIRED},
|
||||
{sfOfferSequence, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transaction type sets or clears an account's "regular key". */
|
||||
TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey, Delegation::notDelegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/SetRegularKey.h>
|
||||
#endif
|
||||
TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey,
|
||||
Delegation::notDelegatable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
({
|
||||
{sfRegularKey, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
// 6 deprecated
|
||||
|
||||
/** This transaction type creates an offer to trade one asset for another. */
|
||||
TRANSACTION(ttOFFER_CREATE, 7, OfferCreate, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CreateOffer.h>
|
||||
#endif
|
||||
TRANSACTION(ttOFFER_CREATE, 7, OfferCreate,
|
||||
Delegation::delegatable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
({
|
||||
{sfTakerPays, soeREQUIRED},
|
||||
{sfTakerGets, soeREQUIRED},
|
||||
{sfExpiration, soeOPTIONAL},
|
||||
@@ -98,14 +155,28 @@ TRANSACTION(ttOFFER_CREATE, 7, OfferCreate, Delegation::delegatable, ({
|
||||
}))
|
||||
|
||||
/** This transaction type cancels existing offers to trade one asset for another. */
|
||||
TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CancelOffer.h>
|
||||
#endif
|
||||
TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel,
|
||||
Delegation::delegatable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
({
|
||||
{sfOfferSequence, soeREQUIRED},
|
||||
}))
|
||||
|
||||
// 9 deprecated
|
||||
|
||||
/** This transaction type creates a new set of tickets. */
|
||||
TRANSACTION(ttTICKET_CREATE, 10, TicketCreate, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CreateTicket.h>
|
||||
#endif
|
||||
TRANSACTION(ttTICKET_CREATE, 10, TicketCreate,
|
||||
Delegation::delegatable,
|
||||
featureTicketBatch,
|
||||
noPriv,
|
||||
({
|
||||
{sfTicketCount, soeREQUIRED},
|
||||
}))
|
||||
|
||||
@@ -114,13 +185,27 @@ TRANSACTION(ttTICKET_CREATE, 10, TicketCreate, Delegation::delegatable, ({
|
||||
/** This transaction type modifies the signer list associated with an account. */
|
||||
// The SignerEntries are optional because a SignerList is deleted by
|
||||
// setting the SignerQuorum to zero and omitting SignerEntries.
|
||||
TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet, Delegation::notDelegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/SetSignerList.h>
|
||||
#endif
|
||||
TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet,
|
||||
Delegation::notDelegatable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
({
|
||||
{sfSignerQuorum, soeREQUIRED},
|
||||
{sfSignerEntries, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction type creates a new unidirectional XRP payment channel. */
|
||||
TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/PayChan.h>
|
||||
#endif
|
||||
TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate,
|
||||
Delegation::delegatable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
({
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfSettleDelay, soeREQUIRED},
|
||||
@@ -130,14 +215,22 @@ TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, Delegation::delegatable,
|
||||
}))
|
||||
|
||||
/** This transaction type funds an existing unidirectional XRP payment channel. */
|
||||
TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund, Delegation::delegatable, ({
|
||||
TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund,
|
||||
Delegation::delegatable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
({
|
||||
{sfChannel, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfExpiration, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction type submits a claim against an existing unidirectional payment channel. */
|
||||
TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, Delegation::delegatable, ({
|
||||
TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim,
|
||||
Delegation::delegatable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
({
|
||||
{sfChannel, soeREQUIRED},
|
||||
{sfAmount, soeOPTIONAL},
|
||||
{sfBalance, soeOPTIONAL},
|
||||
@@ -147,7 +240,14 @@ TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, Delegation::delegatable, (
|
||||
}))
|
||||
|
||||
/** This transaction type creates a new check. */
|
||||
TRANSACTION(ttCHECK_CREATE, 16, CheckCreate, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CreateCheck.h>
|
||||
#endif
|
||||
TRANSACTION(ttCHECK_CREATE, 16, CheckCreate,
|
||||
Delegation::delegatable,
|
||||
featureChecks,
|
||||
noPriv,
|
||||
({
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfSendMax, soeREQUIRED},
|
||||
{sfExpiration, soeOPTIONAL},
|
||||
@@ -156,19 +256,40 @@ TRANSACTION(ttCHECK_CREATE, 16, CheckCreate, Delegation::delegatable, ({
|
||||
}))
|
||||
|
||||
/** This transaction type cashes an existing check. */
|
||||
TRANSACTION(ttCHECK_CASH, 17, CheckCash, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CashCheck.h>
|
||||
#endif
|
||||
TRANSACTION(ttCHECK_CASH, 17, CheckCash,
|
||||
Delegation::delegatable,
|
||||
featureChecks,
|
||||
noPriv,
|
||||
({
|
||||
{sfCheckID, soeREQUIRED},
|
||||
{sfAmount, soeOPTIONAL},
|
||||
{sfDeliverMin, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction type cancels an existing check. */
|
||||
TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CancelCheck.h>
|
||||
#endif
|
||||
TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel,
|
||||
Delegation::delegatable,
|
||||
featureChecks,
|
||||
noPriv,
|
||||
({
|
||||
{sfCheckID, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transaction type grants or revokes authorization to transfer funds. */
|
||||
TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/DepositPreauth.h>
|
||||
#endif
|
||||
TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth,
|
||||
Delegation::delegatable,
|
||||
featureDepositPreauth,
|
||||
noPriv,
|
||||
({
|
||||
{sfAuthorize, soeOPTIONAL},
|
||||
{sfUnauthorize, soeOPTIONAL},
|
||||
{sfAuthorizeCredentials, soeOPTIONAL},
|
||||
@@ -176,14 +297,28 @@ TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth, Delegation::delegatable, ({
|
||||
}))
|
||||
|
||||
/** This transaction type modifies a trustline between two accounts. */
|
||||
TRANSACTION(ttTRUST_SET, 20, TrustSet, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/SetTrust.h>
|
||||
#endif
|
||||
TRANSACTION(ttTRUST_SET, 20, TrustSet,
|
||||
Delegation::delegatable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
({
|
||||
{sfLimitAmount, soeOPTIONAL},
|
||||
{sfQualityIn, soeOPTIONAL},
|
||||
{sfQualityOut, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction type deletes an existing account. */
|
||||
TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete, Delegation::notDelegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/DeleteAccount.h>
|
||||
#endif
|
||||
TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete,
|
||||
Delegation::notDelegatable,
|
||||
uint256{},
|
||||
mustDeleteAcct,
|
||||
({
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfDestinationTag, soeOPTIONAL},
|
||||
{sfCredentialIDs, soeOPTIONAL},
|
||||
@@ -192,7 +327,14 @@ TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete, Delegation::notDelegatable, ({
|
||||
// 22 reserved
|
||||
|
||||
/** This transaction mints a new NFT. */
|
||||
TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenMint.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint,
|
||||
Delegation::delegatable,
|
||||
featureNonFungibleTokensV1,
|
||||
changeNFTCounts,
|
||||
({
|
||||
{sfNFTokenTaxon, soeREQUIRED},
|
||||
{sfTransferFee, soeOPTIONAL},
|
||||
{sfIssuer, soeOPTIONAL},
|
||||
@@ -203,13 +345,27 @@ TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint, Delegation::delegatable, ({
|
||||
}))
|
||||
|
||||
/** This transaction burns (i.e. destroys) an existing NFT. */
|
||||
TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenBurn.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn,
|
||||
Delegation::delegatable,
|
||||
featureNonFungibleTokensV1,
|
||||
changeNFTCounts,
|
||||
({
|
||||
{sfNFTokenID, soeREQUIRED},
|
||||
{sfOwner, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction creates a new offer to buy or sell an NFT. */
|
||||
TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenCreateOffer.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer,
|
||||
Delegation::delegatable,
|
||||
featureNonFungibleTokensV1,
|
||||
noPriv,
|
||||
({
|
||||
{sfNFTokenID, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfDestination, soeOPTIONAL},
|
||||
@@ -218,25 +374,53 @@ TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer, Delegation::delegata
|
||||
}))
|
||||
|
||||
/** This transaction cancels an existing offer to buy or sell an existing NFT. */
|
||||
TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenCancelOffer.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer,
|
||||
Delegation::delegatable,
|
||||
featureNonFungibleTokensV1,
|
||||
noPriv,
|
||||
({
|
||||
{sfNFTokenOffers, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transaction accepts an existing offer to buy or sell an existing NFT. */
|
||||
TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenAcceptOffer.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer,
|
||||
Delegation::delegatable,
|
||||
featureNonFungibleTokensV1,
|
||||
noPriv,
|
||||
({
|
||||
{sfNFTokenBuyOffer, soeOPTIONAL},
|
||||
{sfNFTokenSellOffer, soeOPTIONAL},
|
||||
{sfNFTokenBrokerFee, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction claws back issued tokens. */
|
||||
TRANSACTION(ttCLAWBACK, 30, Clawback, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Clawback.h>
|
||||
#endif
|
||||
TRANSACTION(ttCLAWBACK, 30, Clawback,
|
||||
Delegation::delegatable,
|
||||
featureClawback,
|
||||
noPriv,
|
||||
({
|
||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||
{sfHolder, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction claws back tokens from an AMM pool. */
|
||||
TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMClawback.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback,
|
||||
Delegation::delegatable,
|
||||
featureAMMClawback,
|
||||
mayDeleteAcct | overrideFreeze,
|
||||
({
|
||||
{sfHolder, soeREQUIRED},
|
||||
{sfAsset, soeREQUIRED},
|
||||
{sfAsset2, soeREQUIRED},
|
||||
@@ -244,14 +428,28 @@ TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback, Delegation::delegatable, ({
|
||||
}))
|
||||
|
||||
/** This transaction type creates an AMM instance */
|
||||
TRANSACTION(ttAMM_CREATE, 35, AMMCreate, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMCreate.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_CREATE, 35, AMMCreate,
|
||||
Delegation::delegatable,
|
||||
featureAMM,
|
||||
createPseudoAcct,
|
||||
({
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfAmount2, soeREQUIRED},
|
||||
{sfTradingFee, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transaction type deposits into an AMM instance */
|
||||
TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMDeposit.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit,
|
||||
Delegation::delegatable,
|
||||
featureAMM,
|
||||
noPriv,
|
||||
({
|
||||
{sfAsset, soeREQUIRED},
|
||||
{sfAsset2, soeREQUIRED},
|
||||
{sfAmount, soeOPTIONAL},
|
||||
@@ -262,7 +460,14 @@ TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit, Delegation::delegatable, ({
|
||||
}))
|
||||
|
||||
/** This transaction type withdraws from an AMM instance */
|
||||
TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMWithdraw.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw,
|
||||
Delegation::delegatable,
|
||||
featureAMM,
|
||||
mayDeleteAcct,
|
||||
({
|
||||
{sfAsset, soeREQUIRED},
|
||||
{sfAsset2, soeREQUIRED},
|
||||
{sfAmount, soeOPTIONAL},
|
||||
@@ -272,14 +477,28 @@ TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw, Delegation::delegatable, ({
|
||||
}))
|
||||
|
||||
/** This transaction type votes for the trading fee */
|
||||
TRANSACTION(ttAMM_VOTE, 38, AMMVote, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMVote.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_VOTE, 38, AMMVote,
|
||||
Delegation::delegatable,
|
||||
featureAMM,
|
||||
noPriv,
|
||||
({
|
||||
{sfAsset, soeREQUIRED},
|
||||
{sfAsset2, soeREQUIRED},
|
||||
{sfTradingFee, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transaction type bids for the auction slot */
|
||||
TRANSACTION(ttAMM_BID, 39, AMMBid, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMBid.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_BID, 39, AMMBid,
|
||||
Delegation::delegatable,
|
||||
featureAMM,
|
||||
noPriv,
|
||||
({
|
||||
{sfAsset, soeREQUIRED},
|
||||
{sfAsset2, soeREQUIRED},
|
||||
{sfBidMin, soeOPTIONAL},
|
||||
@@ -288,20 +507,38 @@ TRANSACTION(ttAMM_BID, 39, AMMBid, Delegation::delegatable, ({
|
||||
}))
|
||||
|
||||
/** This transaction type deletes AMM in the empty state */
|
||||
TRANSACTION(ttAMM_DELETE, 40, AMMDelete, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_DELETE, 40, AMMDelete,
|
||||
Delegation::delegatable,
|
||||
featureAMM,
|
||||
mustDeleteAcct,
|
||||
({
|
||||
{sfAsset, soeREQUIRED},
|
||||
{sfAsset2, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transactions creates a crosschain sequence number */
|
||||
TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/XChainBridge.h>
|
||||
#endif
|
||||
TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID,
|
||||
Delegation::delegatable,
|
||||
featureXChainBridge,
|
||||
noPriv,
|
||||
({
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
{sfSignatureReward, soeREQUIRED},
|
||||
{sfOtherChainSource, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transactions initiates a crosschain transaction */
|
||||
TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit, Delegation::delegatable, ({
|
||||
TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit,
|
||||
Delegation::delegatable,
|
||||
featureXChainBridge,
|
||||
noPriv,
|
||||
({
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
{sfXChainClaimID, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
@@ -309,7 +546,11 @@ TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit, Delegation::delegatable, ({
|
||||
}))
|
||||
|
||||
/** This transaction completes a crosschain transaction */
|
||||
TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim, Delegation::delegatable, ({
|
||||
TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim,
|
||||
Delegation::delegatable,
|
||||
featureXChainBridge,
|
||||
noPriv,
|
||||
({
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
{sfXChainClaimID, soeREQUIRED},
|
||||
{sfDestination, soeREQUIRED},
|
||||
@@ -318,7 +559,11 @@ TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim, Delegation::delegatable, ({
|
||||
}))
|
||||
|
||||
/** This transaction initiates a crosschain account create transaction */
|
||||
TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit, Delegation::delegatable, ({
|
||||
TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit,
|
||||
Delegation::delegatable,
|
||||
featureXChainBridge,
|
||||
noPriv,
|
||||
({
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
@@ -326,7 +571,11 @@ TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit, Deleg
|
||||
}))
|
||||
|
||||
/** This transaction adds an attestation to a claim */
|
||||
TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation, Delegation::delegatable, ({
|
||||
TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation,
|
||||
Delegation::delegatable,
|
||||
featureXChainBridge,
|
||||
createAcct,
|
||||
({
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
|
||||
{sfAttestationSignerAccount, soeREQUIRED},
|
||||
@@ -342,7 +591,12 @@ TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation, Deleg
|
||||
}))
|
||||
|
||||
/** This transaction adds an attestation to an account */
|
||||
TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46, XChainAddAccountCreateAttestation, Delegation::delegatable, ({
|
||||
TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46,
|
||||
XChainAddAccountCreateAttestation,
|
||||
Delegation::delegatable,
|
||||
featureXChainBridge,
|
||||
createAcct,
|
||||
({
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
|
||||
{sfAttestationSignerAccount, soeREQUIRED},
|
||||
@@ -359,31 +613,57 @@ TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46, XChainAddAccountCreateA
|
||||
}))
|
||||
|
||||
/** This transaction modifies a sidechain */
|
||||
TRANSACTION(ttXCHAIN_MODIFY_BRIDGE, 47, XChainModifyBridge, Delegation::delegatable, ({
|
||||
TRANSACTION(ttXCHAIN_MODIFY_BRIDGE, 47, XChainModifyBridge,
|
||||
Delegation::delegatable,
|
||||
featureXChainBridge,
|
||||
noPriv,
|
||||
({
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
{sfSignatureReward, soeOPTIONAL},
|
||||
{sfMinAccountCreateAmount, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transactions creates a sidechain */
|
||||
TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge, Delegation::delegatable, ({
|
||||
TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge,
|
||||
Delegation::delegatable,
|
||||
featureXChainBridge,
|
||||
noPriv,
|
||||
({
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
{sfSignatureReward, soeREQUIRED},
|
||||
{sfMinAccountCreateAmount, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction type creates or updates a DID */
|
||||
TRANSACTION(ttDID_SET, 49, DIDSet, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/DID.h>
|
||||
#endif
|
||||
TRANSACTION(ttDID_SET, 49, DIDSet,
|
||||
Delegation::delegatable,
|
||||
featureDID,
|
||||
noPriv,
|
||||
({
|
||||
{sfDIDDocument, soeOPTIONAL},
|
||||
{sfURI, soeOPTIONAL},
|
||||
{sfData, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction type deletes a DID */
|
||||
TRANSACTION(ttDID_DELETE, 50, DIDDelete, Delegation::delegatable, ({}))
|
||||
TRANSACTION(ttDID_DELETE, 50, DIDDelete,
|
||||
Delegation::delegatable,
|
||||
featureDID,
|
||||
noPriv,
|
||||
({}))
|
||||
|
||||
/** This transaction type creates an Oracle instance */
|
||||
TRANSACTION(ttORACLE_SET, 51, OracleSet, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/SetOracle.h>
|
||||
#endif
|
||||
TRANSACTION(ttORACLE_SET, 51, OracleSet,
|
||||
Delegation::delegatable,
|
||||
featurePriceOracle,
|
||||
noPriv,
|
||||
({
|
||||
{sfOracleDocumentID, soeREQUIRED},
|
||||
{sfProvider, soeOPTIONAL},
|
||||
{sfURI, soeOPTIONAL},
|
||||
@@ -393,45 +673,98 @@ TRANSACTION(ttORACLE_SET, 51, OracleSet, Delegation::delegatable, ({
|
||||
}))
|
||||
|
||||
/** This transaction type deletes an Oracle instance */
|
||||
TRANSACTION(ttORACLE_DELETE, 52, OracleDelete, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/DeleteOracle.h>
|
||||
#endif
|
||||
TRANSACTION(ttORACLE_DELETE, 52, OracleDelete,
|
||||
Delegation::delegatable,
|
||||
featurePriceOracle,
|
||||
noPriv,
|
||||
({
|
||||
{sfOracleDocumentID, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transaction type fixes a problem in the ledger state */
|
||||
TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LedgerStateFix.h>
|
||||
#endif
|
||||
TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix,
|
||||
Delegation::delegatable,
|
||||
fixNFTokenPageLinks,
|
||||
noPriv,
|
||||
({
|
||||
{sfLedgerFixType, soeREQUIRED},
|
||||
{sfOwner, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction type creates a MPTokensIssuance instance */
|
||||
TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/MPTokenIssuanceCreate.h>
|
||||
#endif
|
||||
TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate,
|
||||
Delegation::delegatable,
|
||||
featureMPTokensV1,
|
||||
createMPTIssuance,
|
||||
({
|
||||
{sfAssetScale, soeOPTIONAL},
|
||||
{sfTransferFee, soeOPTIONAL},
|
||||
{sfMaximumAmount, soeOPTIONAL},
|
||||
{sfMPTokenMetadata, soeOPTIONAL},
|
||||
{sfDomainID, soeOPTIONAL},
|
||||
{sfMutableFlags, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction type destroys a MPTokensIssuance instance */
|
||||
TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/MPTokenIssuanceDestroy.h>
|
||||
#endif
|
||||
TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy,
|
||||
Delegation::delegatable,
|
||||
featureMPTokensV1,
|
||||
destroyMPTIssuance,
|
||||
({
|
||||
{sfMPTokenIssuanceID, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transaction type sets flags on a MPTokensIssuance or MPToken instance */
|
||||
TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/MPTokenIssuanceSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet,
|
||||
Delegation::delegatable,
|
||||
featureMPTokensV1,
|
||||
noPriv,
|
||||
({
|
||||
{sfMPTokenIssuanceID, soeREQUIRED},
|
||||
{sfHolder, soeOPTIONAL},
|
||||
{sfDomainID, soeOPTIONAL},
|
||||
{sfMPTokenMetadata, soeOPTIONAL},
|
||||
{sfTransferFee, soeOPTIONAL},
|
||||
{sfMutableFlags, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction type authorizes a MPToken instance */
|
||||
TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/MPTokenAuthorize.h>
|
||||
#endif
|
||||
TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize,
|
||||
Delegation::delegatable,
|
||||
featureMPTokensV1,
|
||||
mustAuthorizeMPT,
|
||||
({
|
||||
{sfMPTokenIssuanceID, soeREQUIRED},
|
||||
{sfHolder, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction type create an Credential instance */
|
||||
TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Credentials.h>
|
||||
#endif
|
||||
TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate,
|
||||
Delegation::delegatable,
|
||||
featureCredentials,
|
||||
noPriv,
|
||||
({
|
||||
{sfSubject, soeREQUIRED},
|
||||
{sfCredentialType, soeREQUIRED},
|
||||
{sfExpiration, soeOPTIONAL},
|
||||
@@ -439,54 +772,105 @@ TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate, Delegation::delegatable,
|
||||
}))
|
||||
|
||||
/** This transaction type accept an Credential object */
|
||||
TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept, Delegation::delegatable, ({
|
||||
TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept,
|
||||
Delegation::delegatable,
|
||||
featureCredentials,
|
||||
noPriv,
|
||||
({
|
||||
{sfIssuer, soeREQUIRED},
|
||||
{sfCredentialType, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transaction type delete an Credential object */
|
||||
TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete, Delegation::delegatable, ({
|
||||
TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete,
|
||||
Delegation::delegatable,
|
||||
featureCredentials,
|
||||
noPriv,
|
||||
({
|
||||
{sfSubject, soeOPTIONAL},
|
||||
{sfIssuer, soeOPTIONAL},
|
||||
{sfCredentialType, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transaction type modify a NFToken */
|
||||
TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenModify.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify,
|
||||
Delegation::delegatable,
|
||||
featureDynamicNFT,
|
||||
noPriv,
|
||||
({
|
||||
{sfNFTokenID, soeREQUIRED},
|
||||
{sfOwner, soeOPTIONAL},
|
||||
{sfURI, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction type creates or modifies a Permissioned Domain */
|
||||
TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/PermissionedDomainSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet,
|
||||
Delegation::delegatable,
|
||||
featurePermissionedDomains,
|
||||
noPriv,
|
||||
({
|
||||
{sfDomainID, soeOPTIONAL},
|
||||
{sfAcceptedCredentials, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transaction type deletes a Permissioned Domain */
|
||||
TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/PermissionedDomainDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete,
|
||||
Delegation::delegatable,
|
||||
featurePermissionedDomains,
|
||||
noPriv,
|
||||
({
|
||||
{sfDomainID, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transaction type delegates authorized account specified permissions */
|
||||
TRANSACTION(ttDELEGATE_SET, 64, DelegateSet, Delegation::notDelegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/DelegateSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttDELEGATE_SET, 64, DelegateSet,
|
||||
Delegation::notDelegatable,
|
||||
featurePermissionDelegation,
|
||||
noPriv,
|
||||
({
|
||||
{sfAuthorize, soeREQUIRED},
|
||||
{sfPermissions, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transaction creates a single asset vault. */
|
||||
TRANSACTION(ttVAULT_CREATE, 65, VaultCreate, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultCreate.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_CREATE, 65, VaultCreate,
|
||||
Delegation::delegatable,
|
||||
featureSingleAssetVault,
|
||||
createPseudoAcct | createMPTIssuance,
|
||||
({
|
||||
{sfAsset, soeREQUIRED, soeMPTSupported},
|
||||
{sfAssetsMaximum, soeOPTIONAL},
|
||||
{sfMPTokenMetadata, soeOPTIONAL},
|
||||
{sfDomainID, soeOPTIONAL},
|
||||
{sfWithdrawalPolicy, soeOPTIONAL},
|
||||
{sfData, soeOPTIONAL},
|
||||
{sfScale, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction updates a single asset vault. */
|
||||
TRANSACTION(ttVAULT_SET, 66, VaultSet, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_SET, 66, VaultSet,
|
||||
Delegation::delegatable,
|
||||
featureSingleAssetVault,
|
||||
noPriv,
|
||||
({
|
||||
{sfVaultID, soeREQUIRED},
|
||||
{sfAssetsMaximum, soeOPTIONAL},
|
||||
{sfDomainID, soeOPTIONAL},
|
||||
@@ -494,18 +878,39 @@ TRANSACTION(ttVAULT_SET, 66, VaultSet, Delegation::delegatable, ({
|
||||
}))
|
||||
|
||||
/** This transaction deletes a single asset vault. */
|
||||
TRANSACTION(ttVAULT_DELETE, 67, VaultDelete, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_DELETE, 67, VaultDelete,
|
||||
Delegation::delegatable,
|
||||
featureSingleAssetVault,
|
||||
mustDeleteAcct | destroyMPTIssuance,
|
||||
({
|
||||
{sfVaultID, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transaction trades assets for shares with a vault. */
|
||||
TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultDeposit.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit,
|
||||
Delegation::delegatable,
|
||||
featureSingleAssetVault,
|
||||
mayAuthorizeMPT,
|
||||
({
|
||||
{sfVaultID, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||
}))
|
||||
|
||||
/** This transaction trades shares for assets with a vault. */
|
||||
TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultWithdraw.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw,
|
||||
Delegation::delegatable,
|
||||
featureSingleAssetVault,
|
||||
mayDeleteMPT,
|
||||
({
|
||||
{sfVaultID, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||
{sfDestination, soeOPTIONAL},
|
||||
@@ -513,14 +918,28 @@ TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, Delegation::delegatable, ({
|
||||
}))
|
||||
|
||||
/** This transaction claws back tokens from a vault. */
|
||||
TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback, Delegation::delegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultClawback.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback,
|
||||
Delegation::delegatable,
|
||||
featureSingleAssetVault,
|
||||
mayDeleteMPT,
|
||||
({
|
||||
{sfVaultID, soeREQUIRED},
|
||||
{sfHolder, soeREQUIRED},
|
||||
{sfAmount, soeOPTIONAL, soeMPTSupported},
|
||||
}))
|
||||
|
||||
/** This transaction type batches together transactions. */
|
||||
TRANSACTION(ttBATCH, 71, Batch, Delegation::notDelegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Batch.h>
|
||||
#endif
|
||||
TRANSACTION(ttBATCH, 71, Batch,
|
||||
Delegation::notDelegatable,
|
||||
featureBatch,
|
||||
noPriv,
|
||||
({
|
||||
{sfRawTransactions, soeREQUIRED},
|
||||
{sfBatchSigners, soeOPTIONAL},
|
||||
}))
|
||||
@@ -529,7 +948,14 @@ TRANSACTION(ttBATCH, 71, Batch, Delegation::notDelegatable, ({
|
||||
|
||||
For details, see: https://xrpl.org/amendments.html
|
||||
*/
|
||||
TRANSACTION(ttAMENDMENT, 100, EnableAmendment, Delegation::notDelegatable, ({
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Change.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMENDMENT, 100, EnableAmendment,
|
||||
Delegation::notDelegatable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
({
|
||||
{sfLedgerSequence, soeREQUIRED},
|
||||
{sfAmendment, soeREQUIRED},
|
||||
}))
|
||||
@@ -537,7 +963,11 @@ TRANSACTION(ttAMENDMENT, 100, EnableAmendment, Delegation::notDelegatable, ({
|
||||
/** This system-generated transaction type is used to update the network's fee settings.
|
||||
For details, see: https://xrpl.org/fee-voting.html
|
||||
*/
|
||||
TRANSACTION(ttFEE, 101, SetFee, Delegation::notDelegatable, ({
|
||||
TRANSACTION(ttFEE, 101, SetFee,
|
||||
Delegation::notDelegatable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
({
|
||||
{sfLedgerSequence, soeOPTIONAL},
|
||||
// Old version uses raw numbers
|
||||
{sfBaseFee, soeOPTIONAL},
|
||||
@@ -554,7 +984,11 @@ TRANSACTION(ttFEE, 101, SetFee, Delegation::notDelegatable, ({
|
||||
|
||||
For details, see: https://xrpl.org/negative-unl.html
|
||||
*/
|
||||
TRANSACTION(ttUNL_MODIFY, 102, UNLModify, Delegation::notDelegatable, ({
|
||||
TRANSACTION(ttUNL_MODIFY, 102, UNLModify,
|
||||
Delegation::notDelegatable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
({
|
||||
{sfUNLModifyDisabling, soeREQUIRED},
|
||||
{sfLedgerSequence, soeREQUIRED},
|
||||
{sfUNLModifyValidator, soeREQUIRED},
|
||||
|
||||
@@ -710,7 +710,7 @@ JSS(write_load); // out: GetCounts
|
||||
#pragma push_macro("TRANSACTION")
|
||||
#undef TRANSACTION
|
||||
|
||||
#define TRANSACTION(tag, value, name, delegatable, fields) JSS(name);
|
||||
#define TRANSACTION(tag, value, name, ...) JSS(name);
|
||||
|
||||
#include <xrpl/protocol/detail/transactions.macro>
|
||||
|
||||
@@ -722,11 +722,11 @@ JSS(write_load); // out: GetCounts
|
||||
#pragma push_macro("LEDGER_ENTRY_DUPLICATE")
|
||||
#undef LEDGER_ENTRY_DUPLICATE
|
||||
|
||||
#define LEDGER_ENTRY(tag, value, name, rpcName, fields) \
|
||||
JSS(name); \
|
||||
#define LEDGER_ENTRY(tag, value, name, rpcName, ...) \
|
||||
JSS(name); \
|
||||
JSS(rpcName);
|
||||
|
||||
#define LEDGER_ENTRY_DUPLICATE(tag, value, name, rpcName, fields) JSS(rpcName);
|
||||
#define LEDGER_ENTRY_DUPLICATE(tag, value, name, rpcName, ...) JSS(rpcName);
|
||||
|
||||
#include <xrpl/protocol/detail/ledger_entries.macro>
|
||||
|
||||
|
||||
@@ -47,7 +47,6 @@ protected:
|
||||
Port const& port_;
|
||||
Handler& handler_;
|
||||
endpoint_type remote_address_;
|
||||
beast::WrappedSink sink_;
|
||||
beast::Journal const j_;
|
||||
|
||||
boost::asio::executor_work_guard<boost::asio::executor> work_;
|
||||
@@ -84,13 +83,13 @@ BasePeer<Handler, Impl>::BasePeer(
|
||||
: port_(port)
|
||||
, handler_(handler)
|
||||
, remote_address_(remote_address)
|
||||
, sink_(
|
||||
journal.sink(),
|
||||
[] {
|
||||
static std::atomic<unsigned> id{0};
|
||||
return "##" + std::to_string(++id) + " ";
|
||||
}())
|
||||
, j_(sink_)
|
||||
, j_(journal,
|
||||
log::attributes(log::attr(
|
||||
"PeerID",
|
||||
[] {
|
||||
static std::atomic<unsigned> id{0};
|
||||
return "##" + std::to_string(++id) + " ";
|
||||
}())))
|
||||
, work_(boost::asio::make_work_guard(executor))
|
||||
, strand_(boost::asio::make_strand(executor))
|
||||
{
|
||||
|
||||
@@ -113,14 +113,14 @@ Logs::File::close()
|
||||
}
|
||||
|
||||
void
|
||||
Logs::File::write(char const* text)
|
||||
Logs::File::write(std::string_view text)
|
||||
{
|
||||
if (m_stream != nullptr)
|
||||
(*m_stream) << text;
|
||||
}
|
||||
|
||||
void
|
||||
Logs::File::writeln(char const* text)
|
||||
Logs::File::writeln(std::string_view text)
|
||||
{
|
||||
if (m_stream != nullptr)
|
||||
{
|
||||
@@ -196,11 +196,15 @@ Logs::write(
|
||||
bool console)
|
||||
{
|
||||
std::string s;
|
||||
format(s, text, level, partition);
|
||||
std::string_view result = text;
|
||||
if (!beast::Journal::isStructuredJournalEnabled())
|
||||
{
|
||||
format(s, text, level, partition);
|
||||
result = text;
|
||||
}
|
||||
|
||||
std::lock_guard lock(mutex_);
|
||||
file_.writeln(s);
|
||||
if (!silent_)
|
||||
std::cerr << s << '\n';
|
||||
file_.writeln(result);
|
||||
// VFALCO TODO Fix console output
|
||||
// if (console)
|
||||
// out_.write_console(s);
|
||||
|
||||
@@ -19,12 +19,102 @@
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <ios>
|
||||
#include <ostream>
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace beast {
|
||||
|
||||
namespace {
|
||||
|
||||
// Fast timestamp to ISO string conversion
|
||||
// Returns string like "2024-01-15T10:30:45.123Z"
|
||||
std::string_view
|
||||
fastTimestampToString(std::int64_t milliseconds_since_epoch)
|
||||
{
|
||||
thread_local char buffer[64]; // "2024-01-15T10:30:45.123Z"
|
||||
|
||||
// Precomputed lookup table for 2-digit numbers 00-99
|
||||
static constexpr char digits[200] = {
|
||||
'0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6',
|
||||
'0', '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3',
|
||||
'1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0',
|
||||
'2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7',
|
||||
'2', '8', '2', '9', '3', '0', '3', '1', '3', '2', '3', '3', '3', '4',
|
||||
'3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1',
|
||||
'4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8',
|
||||
'4', '9', '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5',
|
||||
'5', '6', '5', '7', '5', '8', '5', '9', '6', '0', '6', '1', '6', '2',
|
||||
'6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9',
|
||||
'7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6',
|
||||
'7', '7', '7', '8', '7', '9', '8', '0', '8', '1', '8', '2', '8', '3',
|
||||
'8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', '9', '0',
|
||||
'9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7',
|
||||
'9', '8', '9', '9'};
|
||||
|
||||
constexpr std::int64_t UNIX_EPOCH_DAYS =
|
||||
719468; // Days from year 0 to 1970-01-01
|
||||
|
||||
std::int64_t seconds = milliseconds_since_epoch / 1000;
|
||||
int ms = milliseconds_since_epoch % 1000;
|
||||
std::int64_t days = seconds / 86400 + UNIX_EPOCH_DAYS;
|
||||
int sec_of_day = seconds % 86400;
|
||||
|
||||
// Calculate year, month, day from days using Gregorian calendar algorithm
|
||||
int era = (days >= 0 ? days : days - 146096) / 146097;
|
||||
int doe = days - era * 146097;
|
||||
int yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
||||
int year = yoe + era * 400;
|
||||
int doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
||||
int mp = (5 * doy + 2) / 153;
|
||||
int day = doy - (153 * mp + 2) / 5 + 1;
|
||||
int month = mp + (mp < 10 ? 3 : -9);
|
||||
year += (month <= 2);
|
||||
|
||||
// Calculate hour, minute, second
|
||||
int hour = sec_of_day / 3600;
|
||||
int min = (sec_of_day % 3600) / 60;
|
||||
int sec = sec_of_day % 60;
|
||||
|
||||
// Format: "2024-01-15T10:30:45.123Z"
|
||||
buffer[0] = '0' + year / 1000;
|
||||
buffer[1] = '0' + (year / 100) % 10;
|
||||
buffer[2] = '0' + (year / 10) % 10;
|
||||
buffer[3] = '0' + year % 10;
|
||||
buffer[4] = '-';
|
||||
buffer[5] = digits[month * 2];
|
||||
buffer[6] = digits[month * 2 + 1];
|
||||
buffer[7] = '-';
|
||||
buffer[8] = digits[day * 2];
|
||||
buffer[9] = digits[day * 2 + 1];
|
||||
buffer[10] = 'T';
|
||||
buffer[11] = digits[hour * 2];
|
||||
buffer[12] = digits[hour * 2 + 1];
|
||||
buffer[13] = ':';
|
||||
buffer[14] = digits[min * 2];
|
||||
buffer[15] = digits[min * 2 + 1];
|
||||
buffer[16] = ':';
|
||||
buffer[17] = digits[sec * 2];
|
||||
buffer[18] = digits[sec * 2 + 1];
|
||||
buffer[19] = '.';
|
||||
buffer[20] = '0' + ms / 100;
|
||||
buffer[21] = '0' + (ms / 10) % 10;
|
||||
buffer[22] = '0' + ms % 10;
|
||||
buffer[23] = 'Z';
|
||||
|
||||
return {buffer, 24};
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
std::string Journal::globalLogAttributes_;
|
||||
std::shared_mutex Journal::globalLogAttributesMutex_;
|
||||
bool Journal::jsonLogsEnabled_ = false;
|
||||
thread_local Journal::JsonLogContext Journal::currentJsonLogContext_{};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// A Sink that does nothing.
|
||||
@@ -87,6 +177,186 @@ Journal::getNullSink()
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
std::string_view
|
||||
severities::to_string(Severity severity)
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
switch (severity)
|
||||
{
|
||||
case kDisabled:
|
||||
return "disabled"sv;
|
||||
case kTrace:
|
||||
return "trace"sv;
|
||||
case kDebug:
|
||||
return "debug"sv;
|
||||
case kInfo:
|
||||
return "info"sv;
|
||||
case kWarning:
|
||||
return "warning"sv;
|
||||
case kError:
|
||||
return "error"sv;
|
||||
case kFatal:
|
||||
return "fatal"sv;
|
||||
default:
|
||||
UNREACHABLE("Unexpected severity value!");
|
||||
}
|
||||
return ""sv;
|
||||
}
|
||||
|
||||
void
|
||||
Journal::JsonLogContext::start(
|
||||
std::source_location location,
|
||||
severities::Severity severity,
|
||||
std::string_view moduleName,
|
||||
std::string_view journalAttributes) noexcept
|
||||
{
|
||||
struct ThreadIdStringInitializer
|
||||
{
|
||||
std::string value;
|
||||
ThreadIdStringInitializer()
|
||||
{
|
||||
std::stringstream threadIdStream;
|
||||
threadIdStream << std::this_thread::get_id();
|
||||
value = threadIdStream.str();
|
||||
}
|
||||
};
|
||||
thread_local ThreadIdStringInitializer const threadId;
|
||||
|
||||
messageOffset_ = 0;
|
||||
messageBuffer_.clear();
|
||||
jsonWriter_ = detail::SimpleJsonWriter{&messageBuffer_};
|
||||
|
||||
if (!jsonLogsEnabled_)
|
||||
{
|
||||
messageBuffer_ = journalAttributes;
|
||||
return;
|
||||
}
|
||||
|
||||
writer().startObject();
|
||||
|
||||
if (!journalAttributes.empty())
|
||||
{
|
||||
writer().writeKey("Jnl");
|
||||
writer().writeRaw(journalAttributes);
|
||||
writer().endObject();
|
||||
}
|
||||
|
||||
{
|
||||
std::shared_lock lock(globalLogAttributesMutex_);
|
||||
if (!globalLogAttributes_.empty())
|
||||
{
|
||||
writer().writeKey("Glb");
|
||||
writer().writeRaw(globalLogAttributes_);
|
||||
writer().endObject();
|
||||
}
|
||||
}
|
||||
|
||||
writer().writeKey("Mtd");
|
||||
writer().startObject();
|
||||
|
||||
writer().writeKey("Mdl");
|
||||
writer().writeString(moduleName);
|
||||
|
||||
writer().writeKey("Fl");
|
||||
constexpr size_t FILE_NAME_KEEP_CHARS = 20;
|
||||
std::string_view fileName = location.file_name();
|
||||
std::string_view trimmedFileName = (fileName.size() > FILE_NAME_KEEP_CHARS)
|
||||
? fileName.substr(fileName.size() - FILE_NAME_KEEP_CHARS)
|
||||
: fileName;
|
||||
writer().writeString(trimmedFileName);
|
||||
|
||||
writer().writeKey("Ln");
|
||||
writer().writeUInt(location.line());
|
||||
|
||||
writer().writeKey("ThId");
|
||||
writer().writeString(threadId.value);
|
||||
|
||||
auto severityStr = to_string(severity);
|
||||
writer().writeKey("Lv");
|
||||
writer().writeString(severityStr);
|
||||
|
||||
auto nowMs = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
writer().writeKey("Tm");
|
||||
writer().writeString(fastTimestampToString(nowMs));
|
||||
|
||||
writer().endObject();
|
||||
|
||||
hasMessageParams_ = false;
|
||||
}
|
||||
|
||||
void
|
||||
Journal::JsonLogContext::reuseJson()
|
||||
{
|
||||
messageOffset_ = messageBuffer_.size();
|
||||
}
|
||||
|
||||
void
|
||||
Journal::JsonLogContext::finish()
|
||||
{
|
||||
if (messageOffset_ != 0)
|
||||
{
|
||||
messageBuffer_.erase(messageOffset_);
|
||||
}
|
||||
else
|
||||
{
|
||||
messageBuffer_.clear();
|
||||
}
|
||||
|
||||
jsonWriter_ = detail::SimpleJsonWriter{&messageBuffer_};
|
||||
}
|
||||
|
||||
void
|
||||
Journal::initMessageContext(
|
||||
std::source_location location,
|
||||
severities::Severity severity) const
|
||||
{
|
||||
currentJsonLogContext_.start(location, severity, name_, attributes_);
|
||||
}
|
||||
|
||||
std::string&
|
||||
Journal::formatLog(std::string const& message)
|
||||
{
|
||||
if (!jsonLogsEnabled_)
|
||||
{
|
||||
currentJsonLogContext_.writer().buffer() += message;
|
||||
return currentJsonLogContext_.messageBuffer();
|
||||
}
|
||||
|
||||
auto& writer = currentJsonLogContext_.writer();
|
||||
|
||||
currentJsonLogContext_.endMessageParams();
|
||||
|
||||
writer.writeKey("Msg");
|
||||
writer.writeString(message);
|
||||
|
||||
writer.endObject();
|
||||
|
||||
writer.finish();
|
||||
|
||||
return currentJsonLogContext_.messageBuffer();
|
||||
}
|
||||
|
||||
void
|
||||
Journal::enableStructuredJournal()
|
||||
{
|
||||
jsonLogsEnabled_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
Journal::disableStructuredJournal()
|
||||
{
|
||||
jsonLogsEnabled_ = false;
|
||||
resetGlobalAttributes();
|
||||
}
|
||||
|
||||
bool
|
||||
Journal::isStructuredJournalEnabled()
|
||||
{
|
||||
return jsonLogsEnabled_;
|
||||
}
|
||||
|
||||
Journal::Sink::Sink(Severity thresh, bool console)
|
||||
: thresh_(thresh), m_console(console)
|
||||
{
|
||||
@@ -143,13 +413,14 @@ Journal::ScopedStream::ScopedStream(
|
||||
|
||||
Journal::ScopedStream::~ScopedStream()
|
||||
{
|
||||
std::string const& s(m_ostream.str());
|
||||
std::string s = m_ostream.str();
|
||||
if (!s.empty())
|
||||
{
|
||||
if (s == "\n")
|
||||
m_sink.write(m_level, "");
|
||||
else
|
||||
m_sink.write(m_level, s);
|
||||
s = "";
|
||||
|
||||
m_sink.write(m_level, formatLog(s));
|
||||
currentJsonLogContext_.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,12 +430,4 @@ Journal::ScopedStream::operator<<(std::ostream& manip(std::ostream&)) const
|
||||
return m_ostream << manip;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Journal::ScopedStream
|
||||
Journal::Stream::operator<<(std::ostream& manip(std::ostream&)) const
|
||||
{
|
||||
return ScopedStream(*this, manip);
|
||||
}
|
||||
|
||||
} // namespace beast
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/ledger/detail/ApplyStateTable.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/ledger/detail/ApplyStateTable.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/st.h>
|
||||
|
||||
@@ -17,10 +17,9 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/ledger/ApplyView.h>
|
||||
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/ledger/detail/ApplyViewBase.h>
|
||||
#include <xrpl/ledger/detail/ApplyViewBase.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace detail {
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/ledger/ApplyViewImpl.h>
|
||||
#include <xrpl/ledger/ApplyViewImpl.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -18,9 +18,8 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/ledger/BookDirs.h>
|
||||
#include <xrpld/ledger/View.h>
|
||||
|
||||
#include <xrpl/ledger/BookDirs.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -17,9 +17,8 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/ledger/CachedView.h>
|
||||
|
||||
#include <xrpl/basics/TaggedCache.ipp>
|
||||
#include <xrpl/ledger/CachedView.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace detail {
|
||||
@@ -17,9 +17,8 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/app/misc/CredentialHelpers.h>
|
||||
#include <xrpld/ledger/View.h>
|
||||
|
||||
#include <xrpl/ledger/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/ledger/Dir.h>
|
||||
#include <xrpl/ledger/Dir.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -17,9 +17,8 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/ledger/OpenView.h>
|
||||
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/ledger/OpenView.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -17,11 +17,9 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/app/paths/detail/AmountSpec.h>
|
||||
#include <xrpld/ledger/PaymentSandbox.h>
|
||||
#include <xrpld/ledger/View.h>
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -17,9 +17,8 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/ledger/detail/RawStateTable.h>
|
||||
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/ledger/detail/RawStateTable.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace detail {
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -17,14 +17,13 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpld/app/misc/CredentialHelpers.h>
|
||||
#include <xrpld/ledger/ReadView.h>
|
||||
#include <xrpld/ledger/View.h>
|
||||
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
@@ -627,8 +626,8 @@ xrpLiquid(
|
||||
std::uint32_t const ownerCount = confineOwnerCount(
|
||||
view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj);
|
||||
|
||||
// AMMs have no reserve requirement
|
||||
auto const reserve = sle->isFieldPresent(sfAMMID)
|
||||
// Pseudo-accounts have no reserve requirement
|
||||
auto const reserve = isPseudoAccount(sle)
|
||||
? XRPAmount{0}
|
||||
: view.fees().accountReserve(ownerCount);
|
||||
|
||||
@@ -1040,7 +1039,7 @@ adjustOwnerCount(
|
||||
AccountID const id = (*sle)[sfAccount];
|
||||
std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j);
|
||||
view.adjustOwnerCountHook(id, current, adjusted);
|
||||
sle->setFieldU32(sfOwnerCount, adjusted);
|
||||
sle->at(sfOwnerCount) = adjusted;
|
||||
view.update(sle);
|
||||
}
|
||||
|
||||
@@ -1080,15 +1079,51 @@ pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey)
|
||||
return beast::zero;
|
||||
}
|
||||
|
||||
// Note, the list of the pseudo-account designator fields below MUST be
|
||||
// maintained but it does 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
|
||||
// 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.
|
||||
static std::array<SField const*, 2> const pseudoAccountOwnerFields = {
|
||||
&sfAMMID, //
|
||||
&sfVaultID, //
|
||||
};
|
||||
[[nodiscard]] std::vector<SField const*> const&
|
||||
getPseudoAccountFields()
|
||||
{
|
||||
static std::vector<SField const*> const pseudoFields = []() {
|
||||
auto const ar = LedgerFormats::getInstance().findByType(ltACCOUNT_ROOT);
|
||||
if (!ar)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
LogicError(
|
||||
"ripple::isPseudoAccount : unable to find account root ledger "
|
||||
"format");
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
auto const& soTemplate = ar->getSOTemplate();
|
||||
|
||||
std::vector<SField const*> 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<SLE const> sleAcct)
|
||||
{
|
||||
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](SField const* sf) -> bool {
|
||||
return sleAcct->isFieldPresent(*sf);
|
||||
}) > 0;
|
||||
}
|
||||
|
||||
Expected<std::shared_ptr<SLE>, TER>
|
||||
createPseudoAccount(
|
||||
@@ -1096,10 +1131,11 @@ createPseudoAccount(
|
||||
uint256 const& pseudoOwnerKey,
|
||||
SField const& ownerField)
|
||||
{
|
||||
auto const& fields = getPseudoAccountFields();
|
||||
XRPL_ASSERT(
|
||||
std::count_if(
|
||||
pseudoAccountOwnerFields.begin(),
|
||||
pseudoAccountOwnerFields.end(),
|
||||
fields.begin(),
|
||||
fields.end(),
|
||||
[&ownerField](SField const* sf) -> bool {
|
||||
return *sf == ownerField;
|
||||
}) == 1,
|
||||
@@ -1135,18 +1171,42 @@ createPseudoAccount(
|
||||
return account;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isPseudoAccount(std::shared_ptr<SLE const> sleAcct)
|
||||
[[nodiscard]] TER
|
||||
canAddHolding(ReadView const& view, Issue const& issue)
|
||||
{
|
||||
// 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(
|
||||
pseudoAccountOwnerFields.begin(),
|
||||
pseudoAccountOwnerFields.end(),
|
||||
[&sleAcct](SField const* sf) -> bool {
|
||||
return sleAcct->isFieldPresent(*sf);
|
||||
}) > 0;
|
||||
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(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) -> TER {
|
||||
return canAddHolding(view, issue);
|
||||
},
|
||||
asset.value());
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
@@ -2793,58 +2853,113 @@ rippleCredit(
|
||||
saAmount.asset().value());
|
||||
}
|
||||
|
||||
[[nodiscard]] STAmount
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesDeposit(
|
||||
std::shared_ptr<SLE const> const& vault,
|
||||
std::shared_ptr<SLE const> const& issuance,
|
||||
STAmount const& assets)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!assets.negative(),
|
||||
"ripple::assetsToSharesDeposit : non-negative assets");
|
||||
XRPL_ASSERT(
|
||||
assets.asset() == vault->at(sfAsset),
|
||||
"ripple::assetsToSharesDeposit : assets and vault match");
|
||||
Number assetTotal = vault->at(sfAssetsTotal);
|
||||
STAmount shares{vault->at(sfShareMPTID), static_cast<Number>(assets)};
|
||||
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 shares;
|
||||
Number shareTotal = issuance->at(sfOutstandingAmount);
|
||||
shares = shareTotal * (assets / assetTotal);
|
||||
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]] STAmount
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsDeposit(
|
||||
std::shared_ptr<SLE const> const& vault,
|
||||
std::shared_ptr<SLE const> const& issuance,
|
||||
STAmount const& shares)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!shares.negative(),
|
||||
"ripple::sharesToAssetsDeposit : non-negative shares");
|
||||
XRPL_ASSERT(
|
||||
shares.asset() == vault->at(sfShareMPTID),
|
||||
"ripple::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<STAmount>
|
||||
assetsToSharesWithdraw(
|
||||
std::shared_ptr<SLE const> const& vault,
|
||||
std::shared_ptr<SLE const> const& issuance,
|
||||
STAmount const& assets)
|
||||
STAmount const& assets,
|
||||
TruncateShares truncate)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!assets.negative(),
|
||||
"ripple::assetsToSharesDeposit : non-negative assets");
|
||||
XRPL_ASSERT(
|
||||
assets.asset() == vault->at(sfAsset),
|
||||
"ripple::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 shareTotal = issuance->at(sfOutstandingAmount);
|
||||
shares = shareTotal * (assets / assetTotal);
|
||||
Number const shareTotal = issuance->at(sfOutstandingAmount);
|
||||
Number result = shareTotal * (assets / assetTotal);
|
||||
if (truncate == TruncateShares::yes)
|
||||
result = result.truncate();
|
||||
shares = result;
|
||||
return shares;
|
||||
}
|
||||
|
||||
[[nodiscard]] STAmount
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsWithdraw(
|
||||
std::shared_ptr<SLE const> const& vault,
|
||||
std::shared_ptr<SLE const> const& issuance,
|
||||
STAmount const& shares)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!shares.negative(),
|
||||
"ripple::sharesToAssetsDeposit : non-negative shares");
|
||||
XRPL_ASSERT(
|
||||
shares.asset() == vault->at(sfShareMPTID),
|
||||
"ripple::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 shareTotal = issuance->at(sfOutstandingAmount);
|
||||
Number const shareTotal = issuance->at(sfOutstandingAmount);
|
||||
assets = assetTotal * (shares / shareTotal);
|
||||
return assets;
|
||||
}
|
||||
@@ -2951,11 +3066,17 @@ rippleUnlockEscrowMPT(
|
||||
ApplyView& view,
|
||||
AccountID const& sender,
|
||||
AccountID const& receiver,
|
||||
STAmount const& amount,
|
||||
STAmount const& netAmount,
|
||||
STAmount const& grossAmount,
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const issuer = amount.getIssuer();
|
||||
auto const mptIssue = amount.get<MPTIssue>();
|
||||
if (!view.rules().enabled(fixTokenEscrowV1))
|
||||
XRPL_ASSERT(
|
||||
netAmount == grossAmount,
|
||||
"ripple::rippleUnlockEscrowMPT : netAmount == grossAmount");
|
||||
|
||||
auto const& issuer = netAmount.getIssuer();
|
||||
auto const& mptIssue = netAmount.get<MPTIssue>();
|
||||
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
|
||||
auto sleIssuance = view.peek(mptID);
|
||||
if (!sleIssuance)
|
||||
@@ -2976,7 +3097,7 @@ rippleUnlockEscrowMPT(
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
auto const locked = sleIssuance->getFieldU64(sfLockedAmount);
|
||||
auto const redeem = amount.mpt().value();
|
||||
auto const redeem = grossAmount.mpt().value();
|
||||
|
||||
// Underflow check for subtraction
|
||||
if (!canSubtract(
|
||||
@@ -3009,7 +3130,7 @@ rippleUnlockEscrowMPT(
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
auto current = sle->getFieldU64(sfMPTAmount);
|
||||
auto delta = amount.mpt().value();
|
||||
auto delta = netAmount.mpt().value();
|
||||
|
||||
// Overflow check for addition
|
||||
if (!canAdd(STAmount(mptIssue, current), STAmount(mptIssue, delta)))
|
||||
@@ -3027,7 +3148,7 @@ rippleUnlockEscrowMPT(
|
||||
{
|
||||
// Decrease the Issuance OutstandingAmount
|
||||
auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
|
||||
auto const redeem = amount.mpt().value();
|
||||
auto const redeem = netAmount.mpt().value();
|
||||
|
||||
// Underflow check for subtraction
|
||||
if (!canSubtract(
|
||||
@@ -3071,7 +3192,7 @@ rippleUnlockEscrowMPT(
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
auto const locked = sle->getFieldU64(sfLockedAmount);
|
||||
auto const delta = amount.mpt().value();
|
||||
auto const delta = grossAmount.mpt().value();
|
||||
|
||||
// Underflow check for subtraction
|
||||
if (!canSubtract(STAmount(mptIssue, locked), STAmount(mptIssue, delta)))
|
||||
@@ -3089,6 +3210,28 @@ rippleUnlockEscrowMPT(
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -383,7 +383,7 @@ public:
|
||||
static boost::regex reStatus{
|
||||
"\\`HTTP/1\\S+ (\\d{3}) .*\\'"}; // HTTP/1.1 200 OK
|
||||
static boost::regex reSize{
|
||||
"\\`.*\\r\\nContent-Length:\\s+([0-9]+).*\\'"};
|
||||
"\\`.*\\r\\nContent-Length:\\s+([0-9]+).*\\'", boost::regex::icase};
|
||||
static boost::regex reBody{"\\`.*\\r\\n\\r\\n(.*)\\'"};
|
||||
|
||||
boost::smatch smMatch;
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace BuildInfo {
|
||||
// and follow the format described at http://semver.org/
|
||||
//------------------------------------------------------------------------------
|
||||
// clang-format off
|
||||
char const* const versionString = "2.6.0"
|
||||
char const* const versionString = "2.6.1-rc1"
|
||||
// clang-format on
|
||||
|
||||
#if defined(DEBUG) || defined(SANITIZER)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Permissions.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
@@ -25,11 +26,24 @@ namespace ripple {
|
||||
|
||||
Permission::Permission()
|
||||
{
|
||||
txFeatureMap_ = {
|
||||
#pragma push_macro("TRANSACTION")
|
||||
#undef TRANSACTION
|
||||
|
||||
#define TRANSACTION(tag, value, name, delegatable, amendment, ...) \
|
||||
{value, amendment},
|
||||
|
||||
#include <xrpl/protocol/detail/transactions.macro>
|
||||
|
||||
#undef TRANSACTION
|
||||
#pragma pop_macro("TRANSACTION")
|
||||
};
|
||||
|
||||
delegatableTx_ = {
|
||||
#pragma push_macro("TRANSACTION")
|
||||
#undef TRANSACTION
|
||||
|
||||
#define TRANSACTION(tag, value, name, delegatable, fields) {value, delegatable},
|
||||
#define TRANSACTION(tag, value, name, delegatable, ...) {value, delegatable},
|
||||
|
||||
#include <xrpl/protocol/detail/transactions.macro>
|
||||
|
||||
@@ -87,6 +101,22 @@ Permission::getInstance()
|
||||
return instance;
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
Permission::getPermissionName(std::uint32_t const value) const
|
||||
{
|
||||
auto const permissionValue = static_cast<GranularPermissionType>(value);
|
||||
if (auto const granular = getGranularName(permissionValue))
|
||||
return *granular;
|
||||
|
||||
// not a granular permission, check if it maps to a transaction type
|
||||
auto const txType = permissionToTxType(value);
|
||||
if (auto const* item = TxFormats::getInstance().findByType(txType);
|
||||
item != nullptr)
|
||||
return item->getName();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::uint32_t>
|
||||
Permission::getGranularValue(std::string const& name) const
|
||||
{
|
||||
@@ -118,7 +148,9 @@ Permission::getGranularTxType(GranularPermissionType const& gpType) const
|
||||
}
|
||||
|
||||
bool
|
||||
Permission::isDelegatable(std::uint32_t const& permissionValue) const
|
||||
Permission::isDelegatable(
|
||||
std::uint32_t const& permissionValue,
|
||||
Rules const& rules) const
|
||||
{
|
||||
auto const granularPermission =
|
||||
getGranularName(static_cast<GranularPermissionType>(permissionValue));
|
||||
@@ -126,7 +158,27 @@ Permission::isDelegatable(std::uint32_t const& permissionValue) const
|
||||
// granular permissions are always allowed to be delegated
|
||||
return true;
|
||||
|
||||
auto const it = delegatableTx_.find(permissionValue - 1);
|
||||
auto const txType = permissionToTxType(permissionValue);
|
||||
auto const it = delegatableTx_.find(txType);
|
||||
|
||||
if (rules.enabled(fixDelegateV1_1))
|
||||
{
|
||||
if (it == delegatableTx_.end())
|
||||
return false;
|
||||
|
||||
auto const txFeaturesIt = txFeatureMap_.find(txType);
|
||||
XRPL_ASSERT(
|
||||
txFeaturesIt != txFeatureMap_.end(),
|
||||
"ripple::Permissions::isDelegatable : tx exists in txFeatureMap_");
|
||||
|
||||
// fixDelegateV1_1: Delegation is only allowed if the required amendment
|
||||
// for the transaction is enabled. For transactions that do not require
|
||||
// an amendment, delegation is always allowed.
|
||||
if (txFeaturesIt->second != uint256{} &&
|
||||
!rules.enabled(txFeaturesIt->second))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (it != delegatableTx_.end() && it->second == Delegation::notDelegatable)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
|
||||
#include <map>
|
||||
@@ -27,7 +28,8 @@ namespace ripple {
|
||||
// Storage for static const members.
|
||||
SField::IsSigning const SField::notSigning;
|
||||
int SField::num = 0;
|
||||
std::map<int, SField const*> SField::knownCodeToField;
|
||||
std::unordered_map<int, SField const*> SField::knownCodeToField;
|
||||
std::unordered_map<std::string, SField const*> SField::knownNameToField;
|
||||
|
||||
// Give only this translation unit permission to construct SFields
|
||||
struct SField::private_access_tag_t
|
||||
@@ -45,7 +47,7 @@ TypedField<T>::TypedField(private_access_tag_t pat, Args&&... args)
|
||||
}
|
||||
|
||||
// Construct all compile-time SFields, and register them in the knownCodeToField
|
||||
// database:
|
||||
// and knownNameToField databases:
|
||||
|
||||
// Use macros for most SField construction to enforce naming conventions.
|
||||
#pragma push_macro("UNTYPED_SFIELD")
|
||||
@@ -69,8 +71,8 @@ TypedField<T>::TypedField(private_access_tag_t pat, Args&&... args)
|
||||
##__VA_ARGS__);
|
||||
|
||||
// SFields which, for historical reasons, do not follow naming conventions.
|
||||
SField const sfInvalid(access, -1);
|
||||
SField const sfGeneric(access, 0);
|
||||
SField const sfInvalid(access, -1, "");
|
||||
SField const sfGeneric(access, 0, "Generic");
|
||||
// The following two fields aren't used anywhere, but they break tests/have
|
||||
// downstream effects.
|
||||
SField const sfHash(access, STI_UINT256, 257, "hash");
|
||||
@@ -99,19 +101,34 @@ SField::SField(
|
||||
, signingField(signing)
|
||||
, jsonName(fieldName.c_str())
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!knownCodeToField.contains(fieldCode),
|
||||
"ripple::SField::SField(tid,fv,fn,meta,signing) : fieldCode is unique");
|
||||
XRPL_ASSERT(
|
||||
!knownNameToField.contains(fieldName),
|
||||
"ripple::SField::SField(tid,fv,fn,meta,signing) : fieldName is unique");
|
||||
knownCodeToField[fieldCode] = this;
|
||||
knownNameToField[fieldName] = this;
|
||||
}
|
||||
|
||||
SField::SField(private_access_tag_t, int fc)
|
||||
SField::SField(private_access_tag_t, int fc, char const* fn)
|
||||
: fieldCode(fc)
|
||||
, fieldType(STI_UNKNOWN)
|
||||
, fieldValue(0)
|
||||
, fieldName(fn)
|
||||
, fieldMeta(sMD_Never)
|
||||
, fieldNum(++num)
|
||||
, signingField(IsSigning::yes)
|
||||
, jsonName(fieldName.c_str())
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!knownCodeToField.contains(fieldCode),
|
||||
"ripple::SField::SField(fc,fn) : fieldCode is unique");
|
||||
XRPL_ASSERT(
|
||||
!knownNameToField.contains(fieldName),
|
||||
"ripple::SField::SField(fc,fn) : fieldName is unique");
|
||||
knownCodeToField[fieldCode] = this;
|
||||
knownNameToField[fieldName] = this;
|
||||
}
|
||||
|
||||
SField const&
|
||||
@@ -145,11 +162,11 @@ SField::compare(SField const& f1, SField const& f2)
|
||||
SField const&
|
||||
SField::getField(std::string const& fieldName)
|
||||
{
|
||||
for (auto const& [_, f] : knownCodeToField)
|
||||
auto it = knownNameToField.find(fieldName);
|
||||
|
||||
if (it != knownNameToField.end())
|
||||
{
|
||||
(void)_;
|
||||
if (f->fieldName == fieldName)
|
||||
return *f;
|
||||
return *(it->second);
|
||||
}
|
||||
return sfInvalid;
|
||||
}
|
||||
|
||||
@@ -62,8 +62,10 @@ STUInt8::getText() const
|
||||
if (transResultInfo(TER::fromInt(value_), token, human))
|
||||
return human;
|
||||
|
||||
// LCOV_EXCL_START
|
||||
JLOG(debugLog().error())
|
||||
<< "Unknown result code in metadata: " << value_;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return std::to_string(value_);
|
||||
@@ -80,8 +82,10 @@ STUInt8::getJson(JsonOptions) const
|
||||
if (transResultInfo(TER::fromInt(value_), token, human))
|
||||
return token;
|
||||
|
||||
// LCOV_EXCL_START
|
||||
JLOG(debugLog().error())
|
||||
<< "Unknown result code in metadata: " << value_;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return value_;
|
||||
@@ -171,6 +175,13 @@ template <>
|
||||
std::string
|
||||
STUInt32::getText() const
|
||||
{
|
||||
if (getFName() == sfPermissionValue)
|
||||
{
|
||||
auto const permissionName =
|
||||
Permission::getInstance().getPermissionName(value_);
|
||||
if (permissionName)
|
||||
return *permissionName;
|
||||
}
|
||||
return std::to_string(value_);
|
||||
}
|
||||
|
||||
@@ -180,23 +191,10 @@ STUInt32::getJson(JsonOptions) const
|
||||
{
|
||||
if (getFName() == sfPermissionValue)
|
||||
{
|
||||
auto const permissionValue =
|
||||
static_cast<GranularPermissionType>(value_);
|
||||
auto const granular =
|
||||
Permission::getInstance().getGranularName(permissionValue);
|
||||
|
||||
if (granular)
|
||||
{
|
||||
return *granular;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const txType =
|
||||
Permission::getInstance().permissionToTxType(value_);
|
||||
auto item = TxFormats::getInstance().findByType(txType);
|
||||
if (item != nullptr)
|
||||
return item->getName();
|
||||
}
|
||||
auto const permissionName =
|
||||
Permission::getInstance().getPermissionName(value_);
|
||||
if (permissionName)
|
||||
return *permissionName;
|
||||
}
|
||||
|
||||
return value_;
|
||||
|
||||
@@ -83,7 +83,8 @@ constexpr std::
|
||||
return static_cast<U1>(value);
|
||||
}
|
||||
|
||||
static std::string
|
||||
// LCOV_EXCL_START
|
||||
static inline std::string
|
||||
make_name(std::string const& object, std::string const& field)
|
||||
{
|
||||
if (field.empty())
|
||||
@@ -92,7 +93,7 @@ make_name(std::string const& object, std::string const& field)
|
||||
return object + "." + field;
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
not_an_object(std::string const& object, std::string const& field)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -100,20 +101,20 @@ not_an_object(std::string const& object, std::string const& field)
|
||||
"Field '" + make_name(object, field) + "' is not a JSON object.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
not_an_object(std::string const& object)
|
||||
{
|
||||
return not_an_object(object, "");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
not_an_array(std::string const& object)
|
||||
{
|
||||
return RPC::make_error(
|
||||
rpcINVALID_PARAMS, "Field '" + object + "' is not a JSON array.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
unknown_field(std::string const& object, std::string const& field)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -121,7 +122,7 @@ unknown_field(std::string const& object, std::string const& field)
|
||||
"Field '" + make_name(object, field) + "' is unknown.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
out_of_range(std::string const& object, std::string const& field)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -129,7 +130,7 @@ out_of_range(std::string const& object, std::string const& field)
|
||||
"Field '" + make_name(object, field) + "' is out of range.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
bad_type(std::string const& object, std::string const& field)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -137,7 +138,7 @@ bad_type(std::string const& object, std::string const& field)
|
||||
"Field '" + make_name(object, field) + "' has bad type.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
invalid_data(std::string const& object, std::string const& field)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -145,13 +146,13 @@ invalid_data(std::string const& object, std::string const& field)
|
||||
"Field '" + make_name(object, field) + "' has invalid data.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
invalid_data(std::string const& object)
|
||||
{
|
||||
return invalid_data(object, "");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
array_expected(std::string const& object, std::string const& field)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -159,7 +160,7 @@ array_expected(std::string const& object, std::string const& field)
|
||||
"Field '" + make_name(object, field) + "' must be a JSON array.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
string_expected(std::string const& object, std::string const& field)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -167,7 +168,7 @@ string_expected(std::string const& object, std::string const& field)
|
||||
"Field '" + make_name(object, field) + "' must be a string.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
too_deep(std::string const& object)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -175,7 +176,7 @@ too_deep(std::string const& object)
|
||||
"Field '" + object + "' exceeds nesting depth limit.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
singleton_expected(std::string const& object, unsigned int index)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -184,7 +185,7 @@ singleton_expected(std::string const& object, unsigned int index)
|
||||
"]' must be an object with a single key/object value.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
template_mismatch(SField const& sField)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -193,7 +194,7 @@ template_mismatch(SField const& sField)
|
||||
"' contents did not meet requirements for that type.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
non_object_in_array(std::string const& item, Json::UInt index)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -201,6 +202,176 @@ non_object_in_array(std::string const& item, Json::UInt index)
|
||||
"Item '" + item + "' at index " + std::to_string(index) +
|
||||
" is not an object. Arrays may only contain objects.");
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
template <class STResult, class Integer>
|
||||
static std::optional<detail::STVar>
|
||||
parseUnsigned(
|
||||
SField const& field,
|
||||
std::string const& json_name,
|
||||
std::string const& fieldName,
|
||||
SField const* name,
|
||||
Json::Value const& value,
|
||||
Json::Value& error)
|
||||
{
|
||||
std::optional<detail::STVar> ret;
|
||||
|
||||
try
|
||||
{
|
||||
if (value.isString())
|
||||
{
|
||||
ret = detail::make_stvar<STResult>(
|
||||
field,
|
||||
safe_cast<typename STResult::value_type>(
|
||||
beast::lexicalCastThrow<Integer>(value.asString())));
|
||||
}
|
||||
else if (value.isInt())
|
||||
{
|
||||
ret = detail::make_stvar<STResult>(
|
||||
field,
|
||||
to_unsigned<typename STResult::value_type>(value.asInt()));
|
||||
}
|
||||
else if (value.isUInt())
|
||||
{
|
||||
ret = detail::make_stvar<STResult>(
|
||||
field,
|
||||
to_unsigned<typename STResult::value_type>(value.asUInt()));
|
||||
}
|
||||
else
|
||||
{
|
||||
error = bad_type(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
error = invalid_data(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class STResult, class Integer = std::uint16_t>
|
||||
static std::optional<detail::STVar>
|
||||
parseUint16(
|
||||
SField const& field,
|
||||
std::string const& json_name,
|
||||
std::string const& fieldName,
|
||||
SField const* name,
|
||||
Json::Value const& value,
|
||||
Json::Value& error)
|
||||
{
|
||||
std::optional<detail::STVar> ret;
|
||||
|
||||
try
|
||||
{
|
||||
if (value.isString())
|
||||
{
|
||||
std::string const strValue = value.asString();
|
||||
|
||||
if (!strValue.empty() &&
|
||||
((strValue[0] < '0') || (strValue[0] > '9')))
|
||||
{
|
||||
if (field == sfTransactionType)
|
||||
{
|
||||
ret = detail::make_stvar<STResult>(
|
||||
field,
|
||||
safe_cast<typename STResult::value_type>(
|
||||
static_cast<Integer>(
|
||||
TxFormats::getInstance().findTypeByName(
|
||||
strValue))));
|
||||
|
||||
if (*name == sfGeneric)
|
||||
name = &sfTransaction;
|
||||
}
|
||||
else if (field == sfLedgerEntryType)
|
||||
{
|
||||
ret = detail::make_stvar<STResult>(
|
||||
field,
|
||||
safe_cast<typename STResult::value_type>(
|
||||
static_cast<Integer>(
|
||||
LedgerFormats::getInstance().findTypeByName(
|
||||
strValue))));
|
||||
|
||||
if (*name == sfGeneric)
|
||||
name = &sfLedgerEntry;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = invalid_data(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ret)
|
||||
return parseUnsigned<STResult, Integer>(
|
||||
field, json_name, fieldName, name, value, error);
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
error = invalid_data(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class STResult, class Integer = std::uint32_t>
|
||||
static std::optional<detail::STVar>
|
||||
parseUint32(
|
||||
SField const& field,
|
||||
std::string const& json_name,
|
||||
std::string const& fieldName,
|
||||
SField const* name,
|
||||
Json::Value const& value,
|
||||
Json::Value& error)
|
||||
{
|
||||
std::optional<detail::STVar> ret;
|
||||
|
||||
try
|
||||
{
|
||||
if (value.isString())
|
||||
{
|
||||
if (field == sfPermissionValue)
|
||||
{
|
||||
std::string const strValue = value.asString();
|
||||
auto const granularPermission =
|
||||
Permission::getInstance().getGranularValue(strValue);
|
||||
if (granularPermission)
|
||||
{
|
||||
ret = detail::make_stvar<STResult>(
|
||||
field, *granularPermission);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const& txType =
|
||||
TxFormats::getInstance().findTypeByName(strValue);
|
||||
ret = detail::make_stvar<STResult>(
|
||||
field,
|
||||
Permission::getInstance().txToPermissionType(txType));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = detail::make_stvar<STResult>(
|
||||
field,
|
||||
safe_cast<typename STResult::value_type>(
|
||||
beast::lexicalCastThrow<Integer>(value.asString())));
|
||||
}
|
||||
}
|
||||
if (!ret)
|
||||
return parseUnsigned<STResult, Integer>(
|
||||
field, json_name, fieldName, name, value, error);
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
error = invalid_data(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// This function is used by parseObject to parse any JSON type that doesn't
|
||||
// recurse. Everything represented here is a leaf-type.
|
||||
@@ -216,10 +387,13 @@ parseLeaf(
|
||||
|
||||
auto const& field = SField::getField(fieldName);
|
||||
|
||||
// checked in parseObject
|
||||
if (field == sfInvalid)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
error = unknown_field(json_name, fieldName);
|
||||
return ret;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
switch (field.fieldType)
|
||||
@@ -302,130 +476,18 @@ parseLeaf(
|
||||
break;
|
||||
|
||||
case STI_UINT16:
|
||||
try
|
||||
{
|
||||
if (value.isString())
|
||||
{
|
||||
std::string const strValue = value.asString();
|
||||
|
||||
if (!strValue.empty() &&
|
||||
((strValue[0] < '0') || (strValue[0] > '9')))
|
||||
{
|
||||
if (field == sfTransactionType)
|
||||
{
|
||||
ret = detail::make_stvar<STUInt16>(
|
||||
field,
|
||||
static_cast<std::uint16_t>(
|
||||
TxFormats::getInstance().findTypeByName(
|
||||
strValue)));
|
||||
|
||||
if (*name == sfGeneric)
|
||||
name = &sfTransaction;
|
||||
}
|
||||
else if (field == sfLedgerEntryType)
|
||||
{
|
||||
ret = detail::make_stvar<STUInt16>(
|
||||
field,
|
||||
static_cast<std::uint16_t>(
|
||||
LedgerFormats::getInstance().findTypeByName(
|
||||
strValue)));
|
||||
|
||||
if (*name == sfGeneric)
|
||||
name = &sfLedgerEntry;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = invalid_data(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = detail::make_stvar<STUInt16>(
|
||||
field,
|
||||
beast::lexicalCastThrow<std::uint16_t>(strValue));
|
||||
}
|
||||
}
|
||||
else if (value.isInt())
|
||||
{
|
||||
ret = detail::make_stvar<STUInt16>(
|
||||
field, to_unsigned<std::uint16_t>(value.asInt()));
|
||||
}
|
||||
else if (value.isUInt())
|
||||
{
|
||||
ret = detail::make_stvar<STUInt16>(
|
||||
field, to_unsigned<std::uint16_t>(value.asUInt()));
|
||||
}
|
||||
else
|
||||
{
|
||||
error = bad_type(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
error = invalid_data(json_name, fieldName);
|
||||
ret = parseUint16<STUInt16>(
|
||||
field, json_name, fieldName, name, value, error);
|
||||
if (!ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case STI_UINT32:
|
||||
try
|
||||
{
|
||||
if (value.isString())
|
||||
{
|
||||
if (field == sfPermissionValue)
|
||||
{
|
||||
std::string const strValue = value.asString();
|
||||
auto const granularPermission =
|
||||
Permission::getInstance().getGranularValue(
|
||||
strValue);
|
||||
if (granularPermission)
|
||||
{
|
||||
ret = detail::make_stvar<STUInt32>(
|
||||
field, *granularPermission);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const& txType =
|
||||
TxFormats::getInstance().findTypeByName(
|
||||
strValue);
|
||||
ret = detail::make_stvar<STUInt32>(
|
||||
field,
|
||||
Permission::getInstance().txToPermissionType(
|
||||
txType));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = detail::make_stvar<STUInt32>(
|
||||
field,
|
||||
beast::lexicalCastThrow<std::uint32_t>(
|
||||
value.asString()));
|
||||
}
|
||||
}
|
||||
else if (value.isInt())
|
||||
{
|
||||
ret = detail::make_stvar<STUInt32>(
|
||||
field, to_unsigned<std::uint32_t>(value.asInt()));
|
||||
}
|
||||
else if (value.isUInt())
|
||||
{
|
||||
ret = detail::make_stvar<STUInt32>(
|
||||
field, safe_cast<std::uint32_t>(value.asUInt()));
|
||||
}
|
||||
else
|
||||
{
|
||||
error = bad_type(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
error = invalid_data(json_name, fieldName);
|
||||
ret = parseUint32<STUInt32>(
|
||||
field, json_name, fieldName, name, value, error);
|
||||
if (!ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -703,6 +765,12 @@ parseLeaf(
|
||||
AccountID uAccount, uIssuer;
|
||||
Currency uCurrency;
|
||||
|
||||
if (!account && !currency && !issuer)
|
||||
{
|
||||
error = invalid_data(element_name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (account)
|
||||
{
|
||||
// human account id
|
||||
@@ -1096,24 +1164,4 @@ STParsedJSONObject::STParsedJSONObject(
|
||||
object = parseObject(name, json, sfGeneric, 0, error);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
STParsedJSONArray::STParsedJSONArray(
|
||||
std::string const& name,
|
||||
Json::Value const& json)
|
||||
{
|
||||
using namespace STParsedJSONDetail;
|
||||
auto arr = parseArray(name, json, sfGeneric, 0, error);
|
||||
if (!arr)
|
||||
array.reset();
|
||||
else
|
||||
{
|
||||
auto p = dynamic_cast<STArray*>(&arr->get());
|
||||
if (p == nullptr)
|
||||
array.reset();
|
||||
else
|
||||
array = std::move(*p);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -55,7 +55,8 @@ TxFormats::TxFormats()
|
||||
#undef TRANSACTION
|
||||
|
||||
#define UNWRAP(...) __VA_ARGS__
|
||||
#define TRANSACTION(tag, value, name, delegatable, fields) \
|
||||
#define TRANSACTION( \
|
||||
tag, value, name, delegatable, amendment, privileges, fields) \
|
||||
add(jss::name, tag, UNWRAP fields, commonFields);
|
||||
|
||||
#include <xrpl/protocol/detail/transactions.macro>
|
||||
|
||||
@@ -67,7 +67,7 @@ class AMMCalc_test : public beast::unit_test::suite
|
||||
// drops
|
||||
else if (match[1] == "XRPA")
|
||||
return XRPAmount{std::stoll(match[2])};
|
||||
return amountFromString(gw[match[1]], match[2]);
|
||||
return amountFromString(gw[match[1]].asset(), match[2]);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -2442,8 +2442,7 @@ class AMMClawback_test : public beast::unit_test::suite
|
||||
void
|
||||
run() override
|
||||
{
|
||||
FeatureBitset const all{
|
||||
jtx::testable_amendments() | fixAMMClawbackRounding};
|
||||
FeatureBitset const all = jtx::testable_amendments();
|
||||
|
||||
testInvalidRequest();
|
||||
testFeatureDisabled(all - featureAMMClawback);
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
#include <xrpld/app/paths/AMMOffer.h>
|
||||
#include <xrpld/app/paths/Flow.h>
|
||||
#include <xrpld/app/paths/detail/StrandFlow.h>
|
||||
#include <xrpld/ledger/PaymentSandbox.h>
|
||||
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/STParsedJSON.h>
|
||||
|
||||
@@ -181,9 +181,9 @@ private:
|
||||
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, alice, XRP(20'000) - XRP(50) - txfee(env, 1)));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD1(100)));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD2(0)));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD2(50)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, USD1(100)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, USD2(0)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD2(50)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ private:
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, carol, XRP(30'000) - (txfee(env, 1))));
|
||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'000)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
|
||||
|
||||
// Order that can be filled
|
||||
env(offer(carol, XRP(100), USD(100)),
|
||||
@@ -230,7 +230,7 @@ private:
|
||||
XRP(10'000), USD(10'100), ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, carol, XRP(30'000) + XRP(100) - txfee(env, 2)));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(29'900)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(29'900)));
|
||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
||||
},
|
||||
{{XRP(10'100), USD(10'000)}},
|
||||
@@ -254,7 +254,7 @@ private:
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, carol, XRP(30'000) + XRP(100) - txfee(env, 1)));
|
||||
// AMM
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(29'900)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(29'900)));
|
||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
||||
},
|
||||
{{XRP(10'100), USD(10'000)}},
|
||||
@@ -327,7 +327,7 @@ private:
|
||||
USD(49),
|
||||
IOUAmount{273'861'278752583, -8}));
|
||||
|
||||
BEAST_EXPECT(expectLine(env, bob, STAmount{USD, 101}));
|
||||
BEAST_EXPECT(expectHolding(env, bob, STAmount{USD, 101}));
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, bob, XRP(300'000) - xrpTransferred - txfee(env, 1)));
|
||||
BEAST_EXPECT(expectOffers(env, bob, 0));
|
||||
@@ -390,7 +390,7 @@ private:
|
||||
|
||||
BEAST_EXPECT(
|
||||
ammBob.expectBalances(USD(300), XRP(1'000), ammBob.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, alice, USD(0)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, USD(0)));
|
||||
|
||||
auto jrr = ledgerEntryRoot(env, alice);
|
||||
BEAST_EXPECT(
|
||||
@@ -423,7 +423,7 @@ private:
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRPAmount{9'900'990'100}, USD(10'100), ammAlice.tokens()));
|
||||
// initial 30,000 - 10,000AMM - 100pay
|
||||
BEAST_EXPECT(expectLine(env, alice, USD(19'900)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, USD(19'900)));
|
||||
// initial 30,000 - 10,0000AMM + 99.009900pay - fee*3
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env,
|
||||
@@ -453,7 +453,7 @@ private:
|
||||
env(pay(alice, bob, USD(100)), sendmax(XRP(100)));
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'100), USD(10'000), ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(100)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, USD(100)));
|
||||
},
|
||||
{{XRP(10'000), USD(10'100)}},
|
||||
0,
|
||||
@@ -533,7 +533,7 @@ private:
|
||||
STAmount{USD1, UINT64_C(5'030'181086519115), -12},
|
||||
ammCarol.tokens()));
|
||||
BEAST_EXPECT(expectOffers(env, dan, 1, {{Amounts{XRP(200), EUR(20)}}}));
|
||||
BEAST_EXPECT(expectLine(env, bob, STAmount{EUR1, 30}));
|
||||
BEAST_EXPECT(expectHolding(env, bob, STAmount{EUR1, 30}));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -642,7 +642,7 @@ private:
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'000), USD(9'999), ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'101)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(30'101)));
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, carol, XRP(30'000) - XRP(100) - txfee(env, 1)));
|
||||
},
|
||||
@@ -682,7 +682,7 @@ private:
|
||||
env(offer(alice, USD(100), XRP(200)), json(jss::Flags, tfSell));
|
||||
BEAST_EXPECT(
|
||||
ammBob.expectBalances(XRP(1'100), USD(2'000), ammBob.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, alice, USD(200)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, USD(200)));
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(env, alice, XRP(250)));
|
||||
BEAST_EXPECT(expectOffers(env, alice, 0));
|
||||
}
|
||||
@@ -733,7 +733,7 @@ private:
|
||||
STAmount(XTS, UINT64_C(101'010101010101), -12),
|
||||
XXX(99),
|
||||
ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env, bob, STAmount{XTS, UINT64_C(98'989898989899), -12}));
|
||||
}
|
||||
else
|
||||
@@ -742,10 +742,10 @@ private:
|
||||
STAmount(XTS, UINT64_C(101'0101010101011), -13),
|
||||
XXX(99),
|
||||
ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env, bob, STAmount{XTS, UINT64_C(98'9898989898989), -13}));
|
||||
}
|
||||
BEAST_EXPECT(expectLine(env, bob, XXX(101)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, XXX(101)));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -783,8 +783,8 @@ private:
|
||||
XRP(10'100), USD(10'000), ammAlice.tokens()));
|
||||
BEAST_EXPECT(ammBob.expectBalances(
|
||||
XRP(10'000), EUR(10'100), ammBob.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(15'100)));
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(14'900)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(15'100)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, EUR(14'900)));
|
||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
||||
}
|
||||
|
||||
@@ -816,8 +816,8 @@ private:
|
||||
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'100), USD(10'000), ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(15'100)));
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(14'900)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(15'100)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, EUR(14'900)));
|
||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
||||
BEAST_EXPECT(expectOffers(env, bob, 0));
|
||||
}
|
||||
@@ -850,8 +850,8 @@ private:
|
||||
|
||||
BEAST_EXPECT(ammBob.expectBalances(
|
||||
XRP(10'000), EUR(10'100), ammBob.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(15'100)));
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(14'900)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(15'100)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, EUR(14'900)));
|
||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
||||
BEAST_EXPECT(expectOffers(env, alice, 0));
|
||||
}
|
||||
@@ -894,7 +894,7 @@ private:
|
||||
XRP(20'220),
|
||||
STAmount{USD, UINT64_C(197'8239366963403), -13},
|
||||
ammBob.tokens()));
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env, alice, STAmount{USD, UINT64_C(1'002'17606330366), -11}));
|
||||
BEAST_EXPECT(expectOffers(env, alice, 0));
|
||||
}
|
||||
@@ -912,7 +912,7 @@ private:
|
||||
XRP(21'500),
|
||||
STAmount{USD, UINT64_C(186'046511627907), -12},
|
||||
ammBob.tokens()));
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env, alice, STAmount{USD, UINT64_C(1'013'953488372093), -12}));
|
||||
BEAST_EXPECT(expectOffers(env, alice, 0));
|
||||
}
|
||||
@@ -953,7 +953,7 @@ private:
|
||||
// AMM doesn't pay the transfer fee
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'100), USD(10'000), ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'100)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
|
||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
||||
},
|
||||
{{XRP(10'000), USD(10'100)}},
|
||||
@@ -974,7 +974,7 @@ private:
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'000), USD(10'100), ammAlice.tokens()));
|
||||
// Carol pays 25% transfer fee
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(29'875)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(29'875)));
|
||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
||||
},
|
||||
{{XRP(10'100), USD(10'000)}},
|
||||
@@ -1011,9 +1011,9 @@ private:
|
||||
// AMM doesn't pay the transfer fee
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'100), USD(10'000), ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(15'100)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(15'100)));
|
||||
// Carol pays 25% transfer fee.
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(14'875)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, EUR(14'875)));
|
||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
||||
BEAST_EXPECT(expectOffers(env, bob, 0));
|
||||
}
|
||||
@@ -1051,9 +1051,9 @@ private:
|
||||
// AMM doesn't pay the transfer fee
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'050), USD(10'000), ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(15'050)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(15'050)));
|
||||
// Carol pays 25% transfer fee.
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(14'937.5)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, EUR(14'937.5)));
|
||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
||||
BEAST_EXPECT(
|
||||
expectOffers(env, bob, 1, {{Amounts{EUR(50), XRP(50)}}}));
|
||||
@@ -1077,7 +1077,7 @@ private:
|
||||
env(pay(gw, carol, EUR(1'000)), sendmax(EUR(10'000)));
|
||||
env.close();
|
||||
// 1000 / 0.8
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(1'250)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, EUR(1'250)));
|
||||
// The scenario:
|
||||
// o USD/XRP AMM is created.
|
||||
// o EUR/XRP Offer is created.
|
||||
@@ -1096,9 +1096,9 @@ private:
|
||||
// AMM doesn't pay the transfer fee
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'100), USD(10'000), ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(100)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(100)));
|
||||
// Carol pays 25% transfer fee: 1250 - 100(offer) - 25(transfer fee)
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(1'125)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, EUR(1'125)));
|
||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
||||
BEAST_EXPECT(expectOffers(env, bob, 0));
|
||||
}
|
||||
@@ -1120,7 +1120,7 @@ private:
|
||||
env(pay(gw, alice, USD(11'000)));
|
||||
env(pay(gw, carol, EUR(1'000)), sendmax(EUR(10'000)));
|
||||
env.close();
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(1'000)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, EUR(1'000)));
|
||||
// The scenario:
|
||||
// o USD/XRP AMM is created.
|
||||
// o EUR/XRP Offer is created.
|
||||
@@ -1139,9 +1139,9 @@ private:
|
||||
// AMM pay doesn't transfer fee
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'100), USD(10'000), ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(100)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(100)));
|
||||
// Carol pays 25% transfer fee: 1000 - 100(offer) - 25(transfer fee)
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(875)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, EUR(875)));
|
||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
||||
BEAST_EXPECT(expectOffers(env, bob, 0));
|
||||
}
|
||||
@@ -1170,7 +1170,7 @@ private:
|
||||
BEAST_EXPECT(ammBob.expectBalances(
|
||||
XRP(10'100), USD_bob(10'000), ammBob.tokens()));
|
||||
BEAST_EXPECT(expectOffers(env, alice, 0));
|
||||
BEAST_EXPECT(expectLine(env, alice, USD_bob(100)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, USD_bob(100)));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1206,19 +1206,19 @@ private:
|
||||
env.close();
|
||||
env(pay(dan, bob, D_BUX(100)));
|
||||
env.close();
|
||||
BEAST_EXPECT(expectLine(env, bob, D_BUX(100)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, D_BUX(100)));
|
||||
|
||||
env(pay(ann, cam, D_BUX(60)), path(bob, dan), sendmax(A_BUX(200)));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(expectLine(env, ann, A_BUX(none)));
|
||||
BEAST_EXPECT(expectLine(env, ann, D_BUX(none)));
|
||||
BEAST_EXPECT(expectLine(env, bob, A_BUX(72)));
|
||||
BEAST_EXPECT(expectLine(env, bob, D_BUX(40)));
|
||||
BEAST_EXPECT(expectLine(env, cam, A_BUX(none)));
|
||||
BEAST_EXPECT(expectLine(env, cam, D_BUX(60)));
|
||||
BEAST_EXPECT(expectLine(env, dan, A_BUX(none)));
|
||||
BEAST_EXPECT(expectLine(env, dan, D_BUX(none)));
|
||||
BEAST_EXPECT(expectHolding(env, ann, A_BUX(none)));
|
||||
BEAST_EXPECT(expectHolding(env, ann, D_BUX(none)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, A_BUX(72)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, D_BUX(40)));
|
||||
BEAST_EXPECT(expectHolding(env, cam, A_BUX(none)));
|
||||
BEAST_EXPECT(expectHolding(env, cam, D_BUX(60)));
|
||||
BEAST_EXPECT(expectHolding(env, dan, A_BUX(none)));
|
||||
BEAST_EXPECT(expectHolding(env, dan, D_BUX(none)));
|
||||
|
||||
AMM ammBob(env, bob, A_BUX(30), D_BUX(30));
|
||||
|
||||
@@ -1234,12 +1234,12 @@ private:
|
||||
|
||||
BEAST_EXPECT(
|
||||
ammBob.expectBalances(A_BUX(30), D_BUX(30), ammBob.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, ann, A_BUX(none)));
|
||||
BEAST_EXPECT(expectLine(env, ann, D_BUX(0)));
|
||||
BEAST_EXPECT(expectLine(env, cam, A_BUX(none)));
|
||||
BEAST_EXPECT(expectLine(env, cam, D_BUX(60)));
|
||||
BEAST_EXPECT(expectLine(env, dan, A_BUX(0)));
|
||||
BEAST_EXPECT(expectLine(env, dan, D_BUX(none)));
|
||||
BEAST_EXPECT(expectHolding(env, ann, A_BUX(none)));
|
||||
BEAST_EXPECT(expectHolding(env, ann, D_BUX(0)));
|
||||
BEAST_EXPECT(expectHolding(env, cam, A_BUX(none)));
|
||||
BEAST_EXPECT(expectHolding(env, cam, D_BUX(60)));
|
||||
BEAST_EXPECT(expectHolding(env, dan, A_BUX(0)));
|
||||
BEAST_EXPECT(expectHolding(env, dan, D_BUX(none)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1363,7 +1363,7 @@ private:
|
||||
env(pay(gw, bob, USD(50)));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(50)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, USD(50)));
|
||||
|
||||
// Bob's offer should cross Alice's AMM
|
||||
env(offer(bob, XRP(50), USD(50)));
|
||||
@@ -1372,7 +1372,7 @@ private:
|
||||
BEAST_EXPECT(
|
||||
ammAlice.expectBalances(USD(1'050), XRP(1'000), ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectOffers(env, bob, 0));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(0)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, USD(0)));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1403,7 +1403,7 @@ private:
|
||||
|
||||
env(pay(gw, bob, USD(50)));
|
||||
env.close();
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(50)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, USD(50)));
|
||||
|
||||
// Alice should not be able to create AMM without authorization.
|
||||
{
|
||||
@@ -1440,7 +1440,7 @@ private:
|
||||
BEAST_EXPECT(
|
||||
ammAlice.expectBalances(USD(1'050), XRP(1'000), ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectOffers(env, bob, 0));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(0)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, USD(0)));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1535,7 +1535,7 @@ private:
|
||||
// AMM offer is 51.282052XRP/11AUD, 11AUD/1.1 = 10AUD to bob
|
||||
BEAST_EXPECT(
|
||||
ammCarol.expectBalances(XRP(51), AUD(40), ammCarol.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, bob, AUD(10)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, AUD(10)));
|
||||
|
||||
auto const result =
|
||||
find_paths(env, alice, bob, Account(bob)["USD"](25));
|
||||
@@ -1950,10 +1950,10 @@ private:
|
||||
|
||||
env(pay(alice, carol, USD(50)), path(~USD), sendmax(BTC(50)));
|
||||
|
||||
BEAST_EXPECT(expectLine(env, alice, BTC(50)));
|
||||
BEAST_EXPECT(expectLine(env, bob, BTC(0)));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(0)));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(200)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, BTC(50)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, BTC(0)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, USD(0)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(200)));
|
||||
BEAST_EXPECT(
|
||||
ammBob.expectBalances(BTC(150), USD(100), ammBob.tokens()));
|
||||
}
|
||||
@@ -1974,10 +1974,10 @@ private:
|
||||
|
||||
env(pay(alice, carol, USD(50)), path(~XRP, ~USD), sendmax(BTC(50)));
|
||||
|
||||
BEAST_EXPECT(expectLine(env, alice, BTC(50)));
|
||||
BEAST_EXPECT(expectLine(env, bob, BTC(0)));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(0)));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(200)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, BTC(50)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, BTC(0)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, USD(0)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(200)));
|
||||
BEAST_EXPECT(ammBobBTC_XRP.expectBalances(
|
||||
BTC(150), XRP(100), ammBobBTC_XRP.tokens()));
|
||||
BEAST_EXPECT(ammBobXRP_USD.expectBalances(
|
||||
@@ -2003,8 +2003,8 @@ private:
|
||||
env, alice, xrpMinusFee(env, 10'000 - 50)));
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, bob, XRP(10'000) - XRP(100) - ammCrtFee(env)));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(0)));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(200)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, USD(0)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(200)));
|
||||
BEAST_EXPECT(
|
||||
ammBob.expectBalances(XRP(150), USD(100), ammBob.tokens()));
|
||||
}
|
||||
@@ -2024,10 +2024,10 @@ private:
|
||||
|
||||
env(pay(alice, carol, XRP(50)), path(~XRP), sendmax(USD(50)));
|
||||
|
||||
BEAST_EXPECT(expectLine(env, alice, USD(50)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, USD(50)));
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, bob, XRP(10'000) - XRP(150) - ammCrtFee(env)));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(0)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, USD(0)));
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(env, carol, XRP(10'000 + 50)));
|
||||
BEAST_EXPECT(
|
||||
ammBob.expectBalances(USD(150), XRP(100), ammBob.tokens()));
|
||||
@@ -2209,7 +2209,7 @@ private:
|
||||
sendmax(USD(0.4)),
|
||||
txflags(tfNoRippleDirect | tfPartialPayment));
|
||||
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(1)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, EUR(1)));
|
||||
BEAST_EXPECT(ammBob.expectBalances(
|
||||
USD(8.4), XRPAmount{20}, ammBob.tokens()));
|
||||
}
|
||||
@@ -2244,7 +2244,7 @@ private:
|
||||
|
||||
// alice buys 107.1428USD with 120GBP and pays 25% tr fee on 120GBP
|
||||
// 1,000 - 120*1.25 = 850GBP
|
||||
BEAST_EXPECT(expectLine(env, alice, GBP(850)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, GBP(850)));
|
||||
if (!features[fixAMMv1_1])
|
||||
{
|
||||
// 120GBP is swapped in for 107.1428USD
|
||||
@@ -2262,7 +2262,7 @@ private:
|
||||
}
|
||||
// 25% of 85.7142USD is paid in tr fee
|
||||
// 85.7142*1.25 = 107.1428USD
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env, carol, STAmount(USD, UINT64_C(1'085'714285714286), -12)));
|
||||
}
|
||||
|
||||
@@ -2294,10 +2294,10 @@ private:
|
||||
// alice buys 120EUR with 120GBP via the offer
|
||||
// and pays 25% tr fee on 120GBP
|
||||
// 1,000 - 120*1.25 = 850GBP
|
||||
BEAST_EXPECT(expectLine(env, alice, GBP(850)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, GBP(850)));
|
||||
// consumed offer is 120GBP/120EUR
|
||||
// ed doesn't pay tr fee
|
||||
BEAST_EXPECT(expectLine(env, ed, EUR(880), GBP(1'120)));
|
||||
BEAST_EXPECT(expectHolding(env, ed, EUR(880), GBP(1'120)));
|
||||
BEAST_EXPECT(
|
||||
expectOffers(env, ed, 1, {Amounts{GBP(880), EUR(880)}}));
|
||||
// 25% on 96EUR is paid in tr fee 96*1.25 = 120EUR
|
||||
@@ -2307,7 +2307,7 @@ private:
|
||||
STAmount{USD, UINT64_C(912'4087591240876), -13},
|
||||
amm.tokens()));
|
||||
// 25% on 70.0729USD is paid in tr fee 70.0729*1.25 = 87.5912USD
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env, carol, STAmount(USD, UINT64_C(1'070'07299270073), -11)));
|
||||
}
|
||||
{
|
||||
@@ -2333,7 +2333,7 @@ private:
|
||||
txflags(tfNoRippleDirect | tfPartialPayment));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(expectLine(env, alice, GBP(850)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, GBP(850)));
|
||||
if (!features[fixAMMv1_1])
|
||||
{
|
||||
// alice buys 107.1428EUR with 120GBP and pays 25% tr fee on
|
||||
@@ -2367,7 +2367,7 @@ private:
|
||||
amm2.tokens()));
|
||||
}
|
||||
// 25% on 63.1578USD is paid in tr fee 63.1578*1.25 = 78.9473USD
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env, carol, STAmount(USD, UINT64_C(1'063'157894736842), -12)));
|
||||
}
|
||||
{
|
||||
@@ -2386,7 +2386,7 @@ private:
|
||||
BEAST_EXPECT(
|
||||
amm.expectBalances(USD(1'100), EUR(1'000), amm.tokens()));
|
||||
// alice pays 25% tr fee on 100USD 1100-100*1.25 = 975USD
|
||||
BEAST_EXPECT(expectLine(env, alice, USD(975), EUR(1'200)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, USD(975), EUR(1'200)));
|
||||
BEAST_EXPECT(expectOffers(env, alice, 0));
|
||||
}
|
||||
|
||||
@@ -2416,7 +2416,7 @@ private:
|
||||
// alice buys 125USD with 142.8571GBP and pays 25% tr fee
|
||||
// on 142.8571GBP
|
||||
// 1,000 - 142.8571*1.25 = 821.4285GBP
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env, alice, STAmount(GBP, UINT64_C(821'4285714285712), -13)));
|
||||
// 142.8571GBP is swapped in for 125USD
|
||||
BEAST_EXPECT(amm.expectBalances(
|
||||
@@ -2425,7 +2425,7 @@ private:
|
||||
amm.tokens()));
|
||||
// 25% on 100USD is paid in tr fee
|
||||
// 100*1.25 = 125USD
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(1'100)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(1'100)));
|
||||
}
|
||||
{
|
||||
// Payment via AMM with limit quality, deliver less
|
||||
@@ -2456,7 +2456,7 @@ private:
|
||||
// alice buys 28.125USD with 24GBP and pays 25% tr fee
|
||||
// on 24GBP
|
||||
// 1,200 - 24*1.25 = 1,170GBP
|
||||
BEAST_EXPECT(expectLine(env, alice, GBP(1'170)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, GBP(1'170)));
|
||||
// 24GBP is swapped in for 28.125USD
|
||||
BEAST_EXPECT(amm.expectBalances(
|
||||
GBP(1'024), USD(1'171.875), amm.tokens()));
|
||||
@@ -2466,7 +2466,7 @@ private:
|
||||
// alice buys 28.125USD with 24GBP and pays 25% tr fee
|
||||
// on 24GBP
|
||||
// 1,200 - 24*1.25 =~ 1,170GBP
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
alice,
|
||||
STAmount{GBP, UINT64_C(1'169'999999999999), -12}));
|
||||
@@ -2478,7 +2478,7 @@ private:
|
||||
}
|
||||
// 25% on 22.5USD is paid in tr fee
|
||||
// 22.5*1.25 = 28.125USD
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(1'222.5)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(1'222.5)));
|
||||
}
|
||||
{
|
||||
// Payment via offer and AMM with limit quality, deliver less
|
||||
@@ -2513,13 +2513,13 @@ private:
|
||||
// alice buys 70.4210EUR with 70.4210GBP via the offer
|
||||
// and pays 25% tr fee on 70.4210GBP
|
||||
// 1,400 - 70.4210*1.25 = 1400 - 88.0262 = 1311.9736GBP
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
alice,
|
||||
STAmount{GBP, UINT64_C(1'311'973684210527), -12}));
|
||||
// ed doesn't pay tr fee, the balances reflect consumed offer
|
||||
// 70.4210GBP/70.4210EUR
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
ed,
|
||||
STAmount{EUR, UINT64_C(1'329'578947368421), -12},
|
||||
@@ -2543,13 +2543,13 @@ private:
|
||||
// alice buys 70.4210EUR with 70.4210GBP via the offer
|
||||
// and pays 25% tr fee on 70.4210GBP
|
||||
// 1,400 - 70.4210*1.25 = 1400 - 88.0262 = 1311.9736GBP
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
alice,
|
||||
STAmount{GBP, UINT64_C(1'311'973684210525), -12}));
|
||||
// ed doesn't pay tr fee, the balances reflect consumed offer
|
||||
// 70.4210GBP/70.4210EUR
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
ed,
|
||||
STAmount{EUR, UINT64_C(1'329'57894736842), -11},
|
||||
@@ -2569,7 +2569,7 @@ private:
|
||||
amm.tokens()));
|
||||
}
|
||||
// 25% on 59.7321USD is paid in tr fee 59.7321*1.25 = 74.6651USD
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env, carol, STAmount(USD, UINT64_C(1'459'732142857143), -12)));
|
||||
}
|
||||
{
|
||||
@@ -2605,7 +2605,7 @@ private:
|
||||
// alice buys 53.3322EUR with 56.3368GBP via the amm
|
||||
// and pays 25% tr fee on 56.3368GBP
|
||||
// 1,400 - 56.3368*1.25 = 1400 - 70.4210 = 1329.5789GBP
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
alice,
|
||||
STAmount{GBP, UINT64_C(1'329'578947368421), -12}));
|
||||
@@ -2622,7 +2622,7 @@ private:
|
||||
// alice buys 53.3322EUR with 56.3368GBP via the amm
|
||||
// and pays 25% tr fee on 56.3368GBP
|
||||
// 1,400 - 56.3368*1.25 = 1400 - 70.4210 = 1329.5789GBP
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
alice,
|
||||
STAmount{GBP, UINT64_C(1'329'57894736842), -11}));
|
||||
@@ -2636,7 +2636,7 @@ private:
|
||||
}
|
||||
// 25% on 42.6658EUR is paid in tr fee 42.6658*1.25 = 53.3322EUR
|
||||
// 42.6658EUR/59.7321USD
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
ed,
|
||||
STAmount{USD, UINT64_C(1'340'267857142857), -12},
|
||||
@@ -2649,7 +2649,7 @@ private:
|
||||
STAmount{EUR, UINT64_C(957'3341836734693), -13},
|
||||
STAmount{USD, UINT64_C(1'340'267857142857), -12}}}));
|
||||
// 25% on 47.7857USD is paid in tr fee 47.7857*1.25 = 59.7321USD
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env, carol, STAmount(USD, UINT64_C(1'447'785714285714), -12)));
|
||||
}
|
||||
{
|
||||
@@ -2683,7 +2683,7 @@ private:
|
||||
// alice buys 53.3322EUR with 107.5308GBP
|
||||
// 25% on 86.0246GBP is paid in tr fee
|
||||
// 1,400 - 86.0246*1.25 = 1400 - 107.5308 = 1229.4691GBP
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
alice,
|
||||
STAmount{GBP, UINT64_C(1'292'469135802469), -12}));
|
||||
@@ -2704,7 +2704,7 @@ private:
|
||||
// alice buys 53.3322EUR with 107.5308GBP
|
||||
// 25% on 86.0246GBP is paid in tr fee
|
||||
// 1,400 - 86.0246*1.25 = 1400 - 107.5308 = 1229.4691GBP
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
alice,
|
||||
STAmount{GBP, UINT64_C(1'292'469135802466), -12}));
|
||||
@@ -2721,7 +2721,7 @@ private:
|
||||
amm2.tokens()));
|
||||
}
|
||||
// 25% on 66.7432USD is paid in tr fee 66.7432*1.25 = 83.4291USD
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env, carol, STAmount(USD, UINT64_C(1'466'743295019157), -12)));
|
||||
}
|
||||
{
|
||||
@@ -2778,7 +2778,7 @@ private:
|
||||
amm2.tokens()));
|
||||
}
|
||||
// 25% on 81.1111USD is paid in tr fee 81.1111*1.25 = 101.3888USD
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env, carol, STAmount{USD, UINT64_C(1'481'111111111111), -12}));
|
||||
}
|
||||
}
|
||||
@@ -2808,7 +2808,7 @@ private:
|
||||
|
||||
BEAST_EXPECT(
|
||||
ammBob.expectBalances(XRP(1'050), USD(1'000), ammBob.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(2'050)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(2'050)));
|
||||
BEAST_EXPECT(expectOffers(env, bob, 1, {{{XRP(100), USD(50)}}}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,8 +97,8 @@ private:
|
||||
AMM ammAlice(env, alice, USD(20'000), BTC(0.5));
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
USD(20'000), BTC(0.5), IOUAmount{100, 0}));
|
||||
BEAST_EXPECT(expectLine(env, alice, USD(0)));
|
||||
BEAST_EXPECT(expectLine(env, alice, BTC(0)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, USD(0)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, BTC(0)));
|
||||
}
|
||||
|
||||
// Require authorization is set, account is authorized
|
||||
@@ -1394,7 +1394,7 @@ private:
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0}));
|
||||
// 30,000 less deposited 1,000
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(29'000)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(29'000)));
|
||||
// 30,000 less deposited 1,000 and 10 drops tx fee
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, carol, XRPAmount{29'000'000'000 - baseFee}));
|
||||
@@ -1449,7 +1449,8 @@ private:
|
||||
IOUAmount{1, 7} + newLPTokens));
|
||||
|
||||
// 30,000 less deposited depositUSD
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'000) - depositUSD));
|
||||
BEAST_EXPECT(
|
||||
expectHolding(env, carol, USD(30'000) - depositUSD));
|
||||
// 30,000 less deposited depositXRP and 10 drops tx fee
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, carol, XRP(30'000) - depositXRP - txfee(env, 1)));
|
||||
@@ -1553,15 +1554,15 @@ private:
|
||||
AMM ammAlice(env, alice, USD(20'000), BTC(0.5));
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
USD(20'000), BTC(0.5), IOUAmount{100, 0}));
|
||||
BEAST_EXPECT(expectLine(env, alice, USD(0)));
|
||||
BEAST_EXPECT(expectLine(env, alice, BTC(0)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, USD(0)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, BTC(0)));
|
||||
fund(env, gw, {carol}, {USD(2'000), BTC(0.05)}, Fund::Acct);
|
||||
// no transfer fee on deposit
|
||||
ammAlice.deposit(carol, 10);
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
USD(22'000), BTC(0.55), IOUAmount{110, 0}));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(0)));
|
||||
BEAST_EXPECT(expectLine(env, carol, BTC(0)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(0)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, BTC(0)));
|
||||
}
|
||||
|
||||
// Tiny deposits
|
||||
@@ -2281,7 +2282,7 @@ private:
|
||||
BEAST_EXPECT(
|
||||
ammAlice.expectLPTokens(carol, IOUAmount{1'000'000, 0}));
|
||||
// 30,000 less deposited 1,000
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(29'000)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(29'000)));
|
||||
// 30,000 less deposited 1,000 and 10 drops tx fee
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, carol, XRPAmount{29'000'000'000 - baseFee}));
|
||||
@@ -2290,7 +2291,7 @@ private:
|
||||
ammAlice.withdraw(carol, 1'000'000);
|
||||
BEAST_EXPECT(
|
||||
ammAlice.expectLPTokens(carol, IOUAmount(beast::Zero())));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'000)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, carol, XRPAmount{30'000'000'000 - 2 * baseFee}));
|
||||
});
|
||||
@@ -2525,22 +2526,22 @@ private:
|
||||
AMM ammAlice(env, alice, USD(20'000), BTC(0.5));
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
USD(20'000), BTC(0.5), IOUAmount{100, 0}));
|
||||
BEAST_EXPECT(expectLine(env, alice, USD(0)));
|
||||
BEAST_EXPECT(expectLine(env, alice, BTC(0)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, USD(0)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, BTC(0)));
|
||||
fund(env, gw, {carol}, {USD(2'000), BTC(0.05)}, Fund::Acct);
|
||||
// no transfer fee on deposit
|
||||
ammAlice.deposit(carol, 10);
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
USD(22'000), BTC(0.55), IOUAmount{110, 0}));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(0)));
|
||||
BEAST_EXPECT(expectLine(env, carol, BTC(0)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(0)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, BTC(0)));
|
||||
// no transfer fee on withdraw
|
||||
ammAlice.withdraw(carol, 10);
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
USD(20'000), BTC(0.5), IOUAmount{100, 0}));
|
||||
BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0, 0}));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(2'000)));
|
||||
BEAST_EXPECT(expectLine(env, carol, BTC(0.05)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(2'000)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, BTC(0.05)));
|
||||
}
|
||||
|
||||
// Tiny withdraw
|
||||
@@ -3527,7 +3528,7 @@ private:
|
||||
// Alice doesn't have anymore lp tokens
|
||||
env(amm.bid({.account = alice, .bidMin = 500}));
|
||||
BEAST_EXPECT(amm.expectAuctionSlot(100, 0, IOUAmount{500}));
|
||||
BEAST_EXPECT(expectLine(env, alice, STAmount{lpIssue, 0}));
|
||||
BEAST_EXPECT(expectHolding(env, alice, STAmount{lpIssue, 0}));
|
||||
// But trades with the discounted fee since she still owns the slot.
|
||||
// Alice pays 10011 drops in fees
|
||||
env(pay(alice, bob, USD(10)), path(~USD), sendmax(XRP(11)));
|
||||
@@ -3790,7 +3791,7 @@ private:
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'100), USD(10'000), ammAlice.tokens()));
|
||||
// Initial balance 30,000 + 100
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'100)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
|
||||
// Initial balance 30,000 - 100(sendmax) - 10(tx fee)
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, bob, XRP(30'000) - XRP(100) - txfee(env, 1)));
|
||||
@@ -3810,7 +3811,7 @@ private:
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'100), USD(10'000), ammAlice.tokens()));
|
||||
// Initial balance 30,000 + 100
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'100)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
|
||||
// Initial balance 30,000 - 100(sendmax) - 10(tx fee)
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, bob, XRP(30'000) - XRP(100) - txfee(env, 1)));
|
||||
@@ -3831,7 +3832,7 @@ private:
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'100), USD(10'000), ammAlice.tokens()));
|
||||
// Initial balance 30,000 + 100
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'100)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
|
||||
// Initial balance 30,000 - 100(sendmax) - 10(tx fee)
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, bob, XRP(30'000) - XRP(100) - txfee(env, 1)));
|
||||
@@ -3857,7 +3858,7 @@ private:
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'010), USD(10'000), ammAlice.tokens()));
|
||||
// Initial balance 30,000 + 10(limited by limitQuality)
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'010)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(30'010)));
|
||||
// Initial balance 30,000 - 10(limited by limitQuality) - 10(tx
|
||||
// fee)
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
@@ -3897,7 +3898,7 @@ private:
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'010), USD(10'000), ammAlice.tokens()));
|
||||
// 10USD - 10% transfer fee
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
carol,
|
||||
STAmount{USD, UINT64_C(30'009'09090909091), -11}));
|
||||
@@ -3984,7 +3985,7 @@ private:
|
||||
BEAST_EXPECT(expectOffers(env, alice, 1, {{expectedAmounts}}));
|
||||
}
|
||||
// Initial 30,000 + 100
|
||||
BEAST_EXPECT(expectLine(env, carol, STAmount{USD, 30'100}));
|
||||
BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, 30'100}));
|
||||
// Initial 1,000 - 30082730(AMM pool) - 70798251(offer) - 10(tx fee)
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env,
|
||||
@@ -4027,7 +4028,7 @@ private:
|
||||
STAmount(EUR, UINT64_C(49'98750312422), -11),
|
||||
STAmount(USD, UINT64_C(49'98750312422), -11)}}}));
|
||||
// Initial 30,000 + 99.99999999999
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
carol,
|
||||
STAmount{USD, UINT64_C(30'099'99999999999), -11}));
|
||||
@@ -4061,7 +4062,7 @@ private:
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'100), USD(10'000), ammAlice.tokens()));
|
||||
// Initial 30,000 + 200
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'200)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(30'200)));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4069,7 +4070,7 @@ private:
|
||||
XRP(10'100),
|
||||
STAmount(USD, UINT64_C(10'000'00000000001), -11),
|
||||
ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
carol,
|
||||
STAmount(USD, UINT64_C(30'199'99999999999), -11)));
|
||||
@@ -4104,7 +4105,7 @@ private:
|
||||
env.close();
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(1'050), USD(1'000), ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(2'200)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(2'200)));
|
||||
BEAST_EXPECT(expectOffers(env, bob, 0));
|
||||
}
|
||||
|
||||
@@ -4118,7 +4119,7 @@ private:
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'100), USD(10'000), ammAlice.tokens()));
|
||||
// Initial 1,000 + 100
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(1'100)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, USD(1'100)));
|
||||
// Initial 30,000 - 100(offer) - 10(tx fee)
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, bob, XRP(30'000) - XRP(100) - txfee(env, 1)));
|
||||
@@ -4145,9 +4146,9 @@ private:
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
GBP(1'100), EUR(1'000), ammAlice.tokens()));
|
||||
// Initial 30,000 - 100(offer) - 25% transfer fee
|
||||
BEAST_EXPECT(expectLine(env, carol, GBP(29'875)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, GBP(29'875)));
|
||||
// Initial 30,000 + 100(offer)
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(30'100)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, EUR(30'100)));
|
||||
BEAST_EXPECT(expectOffers(env, bob, 0));
|
||||
},
|
||||
{{GBP(1'000), EUR(1'100)}},
|
||||
@@ -4285,12 +4286,12 @@ private:
|
||||
// = 58.825 = ~29941.17
|
||||
// carol bought ~72.93EUR at the cost of ~70.68GBP
|
||||
// the offer is partially consumed
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
carol,
|
||||
STAmount{GBP, UINT64_C(29'941'16770347333), -11}));
|
||||
// Initial 30,000 + ~49.3(offers = 39.3(AMM) + 10(LOB))
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
carol,
|
||||
STAmount{EUR, UINT64_C(30'049'31517120716), -11}));
|
||||
@@ -4324,20 +4325,20 @@ private:
|
||||
// = 88.35 = ~29911.64
|
||||
// carol bought ~72.93EUR at the cost of ~70.68GBP
|
||||
// the offer is partially consumed
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
carol,
|
||||
STAmount{GBP, UINT64_C(29'911'64396400896), -11}));
|
||||
// Initial 30,000 + ~72.93(offers = 62.93(AMM) + 10(LOB))
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
carol,
|
||||
STAmount{EUR, UINT64_C(30'072'93416277865), -11}));
|
||||
}
|
||||
// Initial 2000 + 10 = 2010
|
||||
BEAST_EXPECT(expectLine(env, bob, GBP(2'010)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, GBP(2'010)));
|
||||
// Initial 2000 - 10 * 1.25 = 1987.5
|
||||
BEAST_EXPECT(expectLine(env, ed, EUR(1'987.5)));
|
||||
BEAST_EXPECT(expectHolding(env, ed, EUR(1'987.5)));
|
||||
},
|
||||
{{GBP(1'000), EUR(1'100)}},
|
||||
0,
|
||||
@@ -4363,8 +4364,8 @@ private:
|
||||
env.close();
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
GBP(1'100), EUR(1'000), ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, bob, GBP(75)));
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(30'080)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, GBP(75)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, EUR(30'080)));
|
||||
},
|
||||
{{GBP(1'000), EUR(1'100)}},
|
||||
0,
|
||||
@@ -4401,12 +4402,12 @@ private:
|
||||
sendmax(CAN(195.3125)),
|
||||
txflags(tfPartialPayment));
|
||||
env.close();
|
||||
BEAST_EXPECT(expectLine(env, bob, CAN(0)));
|
||||
BEAST_EXPECT(expectLine(env, dan, CAN(356.25), GBP(43.75)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, CAN(0)));
|
||||
BEAST_EXPECT(expectHolding(env, dan, CAN(356.25), GBP(43.75)));
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
GBP(10'125), EUR(10'000), ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, ed, EUR(300), USD(100)));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(80)));
|
||||
BEAST_EXPECT(expectHolding(env, ed, EUR(300), USD(100)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(80)));
|
||||
},
|
||||
{{GBP(10'000), EUR(10'125)}},
|
||||
0,
|
||||
@@ -4523,7 +4524,7 @@ private:
|
||||
BEAST_EXPECT(btc_usd.expectBalances(
|
||||
BTC(10'100), USD(10'000), btc_usd.tokens()));
|
||||
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(300)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(300)));
|
||||
}
|
||||
|
||||
// Dependent AMM
|
||||
@@ -4594,7 +4595,7 @@ private:
|
||||
STAmount{EUR, UINT64_C(10'917'2945958102), -10},
|
||||
eth_eur.tokens()));
|
||||
}
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(300)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(300)));
|
||||
}
|
||||
|
||||
// AMM offers limit
|
||||
@@ -4620,7 +4621,7 @@ private:
|
||||
XRP(10'030),
|
||||
STAmount{USD, UINT64_C(9'970'089730807577), -12},
|
||||
ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
carol,
|
||||
STAmount{USD, UINT64_C(30'029'91026919241), -11}));
|
||||
@@ -4631,7 +4632,7 @@ private:
|
||||
XRP(10'030),
|
||||
STAmount{USD, UINT64_C(9'970'089730807827), -12},
|
||||
ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
carol,
|
||||
STAmount{USD, UINT64_C(30'029'91026919217), -11}));
|
||||
@@ -4663,14 +4664,14 @@ private:
|
||||
if (!features[fixAMMv1_1])
|
||||
{
|
||||
// Carol gets ~100USD
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
carol,
|
||||
STAmount{USD, UINT64_C(30'099'99999999999), -11}));
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'100)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
|
||||
}
|
||||
BEAST_EXPECT(expectOffers(
|
||||
env,
|
||||
@@ -4717,7 +4718,7 @@ private:
|
||||
1,
|
||||
{{{XRPAmount{50'074'628},
|
||||
STAmount{USD, UINT64_C(50'07512950697), -11}}}}));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'100)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4809,11 +4810,11 @@ private:
|
||||
env(offer(carol, STAmount{token2, 100}, STAmount{token1, 100}));
|
||||
env.close();
|
||||
BEAST_EXPECT(
|
||||
expectLine(env, alice, STAmount{token1, 10'000'100}) &&
|
||||
expectLine(env, alice, STAmount{token2, 9'999'900}));
|
||||
expectHolding(env, alice, STAmount{token1, 10'000'100}) &&
|
||||
expectHolding(env, alice, STAmount{token2, 9'999'900}));
|
||||
BEAST_EXPECT(
|
||||
expectLine(env, carol, STAmount{token2, 1'000'100}) &&
|
||||
expectLine(env, carol, STAmount{token1, 999'900}));
|
||||
expectHolding(env, carol, STAmount{token2, 1'000'100}) &&
|
||||
expectHolding(env, carol, STAmount{token1, 999'900}));
|
||||
BEAST_EXPECT(
|
||||
expectOffers(env, alice, 0) && expectOffers(env, carol, 0));
|
||||
});
|
||||
@@ -5034,7 +5035,7 @@ private:
|
||||
BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{1'000}));
|
||||
ammAlice.withdrawAll(carol, USD(3'000));
|
||||
BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0}));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'000)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
|
||||
// Set fee to 1%
|
||||
ammAlice.vote(alice, 1'000);
|
||||
BEAST_EXPECT(ammAlice.expectTradingFee(1'000));
|
||||
@@ -5043,12 +5044,12 @@ private:
|
||||
ammAlice.deposit(carol, USD(3'000));
|
||||
BEAST_EXPECT(ammAlice.expectLPTokens(
|
||||
carol, IOUAmount{994'981155689671, -12}));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(27'000)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(27'000)));
|
||||
// Set fee to 0
|
||||
ammAlice.vote(alice, 0);
|
||||
ammAlice.withdrawAll(carol, USD(0));
|
||||
// Carol gets back less than the original deposit
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
carol,
|
||||
STAmount{USD, UINT64_C(29'994'96220068281), -11}));
|
||||
@@ -5109,13 +5110,13 @@ private:
|
||||
ammAlice.deposit(carol, USD(3'000));
|
||||
|
||||
BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{1'000}));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(27'000)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(27'000)));
|
||||
// Set fee to 1%
|
||||
ammAlice.vote(alice, 1'000);
|
||||
BEAST_EXPECT(ammAlice.expectTradingFee(1'000));
|
||||
// Single withdrawal. Carol gets ~5USD less than deposited.
|
||||
ammAlice.withdrawAll(carol, USD(0));
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
carol,
|
||||
STAmount{USD, UINT64_C(29'994'97487437186), -11}));
|
||||
@@ -5189,9 +5190,9 @@ private:
|
||||
{USD(1'000), EUR(1'000)},
|
||||
Fund::Acct);
|
||||
// Alice contributed 1010EUR and 1000USD to the pool
|
||||
BEAST_EXPECT(expectLine(env, alice, EUR(28'990)));
|
||||
BEAST_EXPECT(expectLine(env, alice, USD(29'000)));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'000)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, EUR(28'990)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, USD(29'000)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
|
||||
// Carol pays to Alice with no fee
|
||||
env(pay(carol, alice, EUR(10)),
|
||||
path(~EUR),
|
||||
@@ -5199,9 +5200,9 @@ private:
|
||||
txflags(tfNoRippleDirect));
|
||||
env.close();
|
||||
// Alice has 10EUR more and Carol has 10USD less
|
||||
BEAST_EXPECT(expectLine(env, alice, EUR(29'000)));
|
||||
BEAST_EXPECT(expectLine(env, alice, USD(29'000)));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(29'990)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, EUR(29'000)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, USD(29'000)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(29'990)));
|
||||
|
||||
// Set fee to 1%
|
||||
ammAlice.vote(alice, 1'000);
|
||||
@@ -5213,10 +5214,10 @@ private:
|
||||
txflags(tfNoRippleDirect));
|
||||
env.close();
|
||||
// Bob sends 10.1~EUR to pay 10USD
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env, bob, STAmount{EUR, UINT64_C(989'8989898989899), -13}));
|
||||
// Carol got 10USD
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'000)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
USD(1'000),
|
||||
STAmount{EUR, UINT64_C(1'010'10101010101), -11},
|
||||
@@ -5233,8 +5234,8 @@ private:
|
||||
// No fee
|
||||
env(offer(carol, EUR(10), USD(10)));
|
||||
env.close();
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(29'990)));
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(30'010)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(29'990)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, EUR(30'010)));
|
||||
// Change pool composition back
|
||||
env(offer(carol, USD(10), EUR(10)));
|
||||
env.close();
|
||||
@@ -5245,11 +5246,11 @@ private:
|
||||
env.close();
|
||||
// Alice gets fewer ~4.97EUR for ~5.02USD, the difference goes
|
||||
// to the pool
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
carol,
|
||||
STAmount{USD, UINT64_C(29'995'02512562814), -11}));
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
carol,
|
||||
STAmount{EUR, UINT64_C(30'004'97487437186), -11}));
|
||||
@@ -5299,16 +5300,16 @@ private:
|
||||
path(~USD),
|
||||
sendmax(EUR(15)),
|
||||
txflags(tfNoRippleDirect));
|
||||
BEAST_EXPECT(expectLine(env, ed, USD(2'010)));
|
||||
BEAST_EXPECT(expectHolding(env, ed, USD(2'010)));
|
||||
if (!features[fixAMMv1_1])
|
||||
{
|
||||
BEAST_EXPECT(expectLine(env, bob, EUR(1'990)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, EUR(1'990)));
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
USD(1'000), EUR(1'005), ammAlice.tokens()));
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env, bob, STAmount(EUR, UINT64_C(1989'999999999999), -12)));
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
USD(1'000),
|
||||
@@ -5336,10 +5337,10 @@ private:
|
||||
path(~USD),
|
||||
sendmax(EUR(15)),
|
||||
txflags(tfNoRippleDirect));
|
||||
BEAST_EXPECT(expectLine(env, ed, USD(2'010)));
|
||||
BEAST_EXPECT(expectHolding(env, ed, USD(2'010)));
|
||||
if (!features[fixAMMv1_1])
|
||||
{
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
bob,
|
||||
STAmount{EUR, UINT64_C(1'989'987453007618), -12}));
|
||||
@@ -5350,7 +5351,7 @@ private:
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
bob,
|
||||
STAmount{EUR, UINT64_C(1'989'987453007628), -12}));
|
||||
@@ -5381,8 +5382,8 @@ private:
|
||||
path(~USD),
|
||||
sendmax(EUR(15)),
|
||||
txflags(tfNoRippleDirect));
|
||||
BEAST_EXPECT(expectLine(env, ed, USD(2'010)));
|
||||
BEAST_EXPECT(expectLine(env, bob, EUR(1'990)));
|
||||
BEAST_EXPECT(expectHolding(env, ed, USD(2'010)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, EUR(1'990)));
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
USD(1'005), EUR(1'000), ammAlice.tokens()));
|
||||
BEAST_EXPECT(expectOffers(env, carol, 0));
|
||||
@@ -5408,8 +5409,8 @@ private:
|
||||
path(~USD),
|
||||
sendmax(EUR(15)),
|
||||
txflags(tfNoRippleDirect));
|
||||
BEAST_EXPECT(expectLine(env, ed, USD(2'010)));
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(env, ed, USD(2'010)));
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env, bob, STAmount{EUR, UINT64_C(1'989'993923296712), -12}));
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
USD(1'004),
|
||||
@@ -5480,47 +5481,47 @@ private:
|
||||
else
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
XRP(10'000), USD(10'000), IOUAmount{10'000'000}));
|
||||
BEAST_EXPECT(expectLine(env, ben, USD(1'500'000)));
|
||||
BEAST_EXPECT(expectLine(env, simon, USD(1'500'000)));
|
||||
BEAST_EXPECT(expectLine(env, chris, USD(1'500'000)));
|
||||
BEAST_EXPECT(expectLine(env, dan, USD(1'500'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(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
carol,
|
||||
STAmount{USD, UINT64_C(30'000'00000000001), -11}));
|
||||
else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'000)));
|
||||
BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
|
||||
else
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(30'000)));
|
||||
BEAST_EXPECT(expectLine(env, ed, USD(1'500'000)));
|
||||
BEAST_EXPECT(expectLine(env, paul, USD(1'500'000)));
|
||||
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(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
nataly,
|
||||
STAmount{USD, UINT64_C(1'500'000'000000002), -9}));
|
||||
else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
nataly,
|
||||
STAmount{USD, UINT64_C(1'500'000'000000005), -9}));
|
||||
else
|
||||
BEAST_EXPECT(expectLine(env, nataly, USD(1'500'000)));
|
||||
BEAST_EXPECT(expectHolding(env, nataly, USD(1'500'000)));
|
||||
ammAlice.withdrawAll(alice);
|
||||
BEAST_EXPECT(!ammAlice.ammExists());
|
||||
if (!features[fixAMMv1_1])
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
alice,
|
||||
STAmount{USD, UINT64_C(30'000'0000000013), -10}));
|
||||
else if (features[fixAMMv1_3])
|
||||
BEAST_EXPECT(expectLine(
|
||||
BEAST_EXPECT(expectHolding(
|
||||
env,
|
||||
alice,
|
||||
STAmount{USD, UINT64_C(30'000'0000000003), -10}));
|
||||
else
|
||||
BEAST_EXPECT(expectLine(env, alice, USD(30'000)));
|
||||
BEAST_EXPECT(expectHolding(env, alice, USD(30'000)));
|
||||
// alice XRP balance is 30,000initial - 50 ammcreate fee -
|
||||
// 10drops fee
|
||||
BEAST_EXPECT(
|
||||
@@ -5883,7 +5884,7 @@ private:
|
||||
BEAST_EXPECT(amm->expectBalances(
|
||||
USD(1'000), ETH(1'000), amm->tokens()));
|
||||
}
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(2'100)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, USD(2'100)));
|
||||
q[i] = Quality(Amounts{
|
||||
ETH(2'000) - env.balance(carol, ETH),
|
||||
env.balance(bob, USD) - USD(2'000)});
|
||||
@@ -6056,7 +6057,7 @@ private:
|
||||
-13}}}}));
|
||||
}
|
||||
}
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(2'100)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, USD(2'100)));
|
||||
q[i] = Quality(Amounts{
|
||||
ETH(2'000) - env.balance(carol, ETH),
|
||||
env.balance(bob, USD) - USD(2'000)});
|
||||
@@ -6203,7 +6204,7 @@ private:
|
||||
sendmax(ETH(600)));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(2'100)));
|
||||
BEAST_EXPECT(expectHolding(env, bob, USD(2'100)));
|
||||
|
||||
if (i == 2 && !features[fixAMMv1_1])
|
||||
{
|
||||
@@ -7484,7 +7485,7 @@ private:
|
||||
using namespace test::jtx;
|
||||
|
||||
auto const testCase = [&](std::string suffix, FeatureBitset features) {
|
||||
testcase("Failed pseudo-account allocation " + suffix);
|
||||
testcase("Fail pseudo-account allocation " + suffix);
|
||||
std::string logs;
|
||||
Env env{*this, features, std::make_unique<CaptureLogs>(&logs)};
|
||||
env.fund(XRP(30'000), gw, alice);
|
||||
|
||||
@@ -19,10 +19,9 @@
|
||||
|
||||
#include <test/jtx.h>
|
||||
|
||||
#include <xrpld/app/misc/CredentialHelpers.h>
|
||||
#include <xrpld/ledger/ApplyViewImpl.h>
|
||||
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/ledger/ApplyViewImpl.h>
|
||||
#include <xrpl/ledger/CredentialHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
@@ -34,15 +33,6 @@
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
static inline bool
|
||||
checkVL(
|
||||
std::shared_ptr<SLE const> const& sle,
|
||||
SField const& field,
|
||||
std::string const& expected)
|
||||
{
|
||||
return strHex(expected) == strHex(sle->getFieldVL(field));
|
||||
}
|
||||
|
||||
struct Credentials_test : public beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
|
||||
@@ -27,14 +27,6 @@
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
bool
|
||||
checkVL(Slice const& result, std::string expected)
|
||||
{
|
||||
Serializer s;
|
||||
s.addRaw(result);
|
||||
return s.getString() == expected;
|
||||
}
|
||||
|
||||
struct DID_test : public beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user