Merge pull request #8278 from Kassaking7/cosign-v1-merge-3

Cosign v1 merge 3
This commit is contained in:
Kassaking7
2026-09-23 15:56:16 -04:00
committed by GitHub
52 changed files with 1182 additions and 686 deletions

View File

@@ -53,6 +53,11 @@ repos:
entry: ./bin/pre-commit/check_doxygen_style.py
language: python
types_or: [c++, c]
- id: fix-gtest-names
name: "fix gtest names: CamelCase suite, snake_case test case"
entry: ./bin/pre-commit/fix_gtest_names.py
language: python
types_or: [c++, c]
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: f4d7745e17a28aad7eed2f4874ca8d1568c11c4c # frozen: v22.1.8

144
bin/pre-commit/fix_gtest_names.py Executable file
View File

@@ -0,0 +1,144 @@
#!/usr/bin/env python3
"""
Rewrites gtest names to the required style in this project: the suite name is
CamelCase, the test-case name is snake_case.
TEST(SuiteName, test_case_name)
The gtest `DISABLED_` prefix is kept verbatim on either name.
Both conversions fold acronyms the way a reader expects:
`SetAndResetAccountTxnID` -> `set_and_reset_account_txn_id`, not
`set_and_reset_account_txn_i_d`.
The first argument of `TEST_F`, `TEST_P`, `TYPED_TEST` and `TYPED_TEST_P` is a
fixture class rather than a free identifier, so rewriting it here would leave
the class it names behind. Those are reported for a human to rename (clang-tidy
checks the class declaration itself, via readability-identifier-naming).
Usage: ./bin/pre-commit/fix_gtest_names.py <file1> <file2> ...
"""
import re
import sys
from collections import Counter
from pathlib import Path
# A test-case definition, `MACRO(SuiteOrFixture, TestName)`, anchored at the
# start of a line so that commented-out definitions and project macros that
# merely look similar (`TEST_EXPECT(...)`) are left alone. The `\s*` between
# arguments allows for a definition clang-format wrapped over several lines.
PATTERN = re.compile(
r"(?P<head>^[ \t]*(?P<macro>TYPED_TEST_P|TYPED_TEST|TEST_F|TEST_P|TEST)\s*\(\s*)"
r"(?P<suite>\w+)(?P<mid>\s*,\s*)(?P<name>\w+)(?P<tail>\s*\))",
re.MULTILINE,
)
# The macros whose first argument names a fixture class, not a free identifier.
FIXTURE_MACROS = ("TEST_F", "TEST_P", "TYPED_TEST", "TYPED_TEST_P")
DISABLED = "DISABLED_"
ACRONYM_BOUNDARY = re.compile(r"([A-Z]+)([A-Z][a-z])")
WORD_BOUNDARY = re.compile(r"([a-z\d])([A-Z])")
def _split_disabled(name: str) -> tuple[str, str]:
"""Splits off gtest's `DISABLED_` prefix, which is kept verbatim."""
if name.startswith(DISABLED):
return DISABLED, name[len(DISABLED) :]
return "", name
def snake_case(name: str) -> str:
"""Returns the name in snake_case, leaving acronyms whole.
`SetAndResetAccountTxnID` -> `set_and_reset_account_txn_id`,
`parseStatRSSkB` -> `parse_stat_rs_sk_b`.
"""
prefix, core = _split_disabled(name)
core = ACRONYM_BOUNDARY.sub(r"\1_\2", core)
return prefix + WORD_BOUNDARY.sub(r"\1_\2", core).lower()
def camel_case(name: str) -> str:
"""Returns the name in CamelCase, capitalizing each underscored word.
Only the letters that have to change are touched, so acronyms survive: a
conversion that went via snake_case would turn `SHAMapTest` into
`ShaMapTest`, whereas here it is already CamelCase and stays put.
`json_value` -> `JsonValue`, `parseStatRSSkB` -> `ParseStatRSSkB`.
"""
prefix, core = _split_disabled(name)
return prefix + "".join(w[:1].upper() + w[1:] for w in core.split("_") if w)
def _corrected(match: re.Match) -> tuple[str, str]:
"""Returns the suite and test-case names this definition should end up with."""
suite = match["suite"]
return (
suite if match["macro"] in FIXTURE_MACROS else camel_case(suite),
snake_case(match["name"]),
)
def fix_source(text: str) -> tuple[str, list[str]]:
"""Returns the corrected text and one `line: message` report per bad name."""
# gtest joins the suite and test names into one class name, so two test
# cases whose joined names agree cannot coexist: `TEST(a, b_c)` and
# `TEST(a_b, c)` both define `a_b_c_Test`. A rename that would introduce
# such a clash is reported for a human instead of applied.
joined = Counter("_".join(_corrected(m)) for m in PATTERN.finditer(text))
reports = []
def rewrite(match: re.Match) -> str:
suite, name = match["suite"], match["name"]
new_suite, new_name = _corrected(match)
line = text.count("\n", 0, match.start()) + 1
if match["macro"] in FIXTURE_MACROS and camel_case(suite) != suite:
reports.append(
f"{line}: fixture '{suite}' is not CamelCase: rename the class "
f"to '{camel_case(suite)}' by hand"
)
if (new_suite, new_name) == (suite, name):
return match[0]
if joined[f"{new_suite}_{new_name}"] > 1:
reports.append(
f"{line}: cannot rename '{suite}, {name}' to '{new_suite}, "
f"{new_name}': another test case already generates that name"
)
return match[0]
if new_suite != suite:
reports.append(f"{line}: renamed suite '{suite}' to '{new_suite}'")
if new_name != name:
reports.append(f"{line}: renamed test case '{name}' to '{new_name}'")
return match["head"] + new_suite + match["mid"] + new_name + match["tail"]
return PATTERN.sub(rewrite, text), reports
def fix_names(path: Path) -> bool:
"""Corrects one file's gtest names, reporting each on stdout."""
original = path.read_text(encoding="utf-8")
fixed, reports = fix_source(original)
for report in reports:
print(f"{path}:{report}")
if fixed != original:
path.write_text(fixed, encoding="utf-8")
return not reports
def main() -> int:
files = [Path(f) for f in sys.argv[1:]]
success = True
for path in files:
success &= fix_names(path)
return 0 if success else 1
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,259 @@
#!/usr/bin/env python3
"""
Tests for fix_gtest_names.py.
Run directly (no test framework needed):
./bin/pre-commit/test_fix_gtest_names.py
or under pytest:
pytest bin/pre-commit/test_fix_gtest_names.py
"""
import sys
import textwrap
from fix_gtest_names import camel_case, fix_source, snake_case
def dedent(text: str) -> str:
"""Removes a fixture's common indentation and its leading newline.
Lets fixtures be written as indented triple-quoted here-docs while keeping
honest 1-based line numbers.
"""
return textwrap.dedent(text).lstrip("\n")
def fixed(text: str) -> str:
return fix_source(dedent(text))[0]
def reports(text: str) -> list[str]:
return fix_source(dedent(text))[1]
# --- conversion --------------------------------------------------------------
def test_snake_case_conversion() -> None:
assert snake_case("BadInputs") == "bad_inputs"
assert snake_case("mulDiv") == "mul_div"
assert snake_case("already_snake") == "already_snake"
assert snake_case("base64") == "base64"
def test_snake_case_keeps_acronyms_whole() -> None:
assert snake_case("SetAndResetAccountTxnID") == "set_and_reset_account_txn_id"
assert snake_case("XRPToIOU") == "xrp_to_iou"
assert snake_case("STAmountMath") == "st_amount_math"
def test_camel_case_conversion() -> None:
assert camel_case("json_value") == "JsonValue"
assert camel_case("mulDiv") == "MulDiv"
assert camel_case("scope") == "Scope"
assert camel_case("base64") == "Base64"
def test_camel_case_leaves_acronyms_alone() -> None:
# A snake_case round-trip would give `ShaMapTest` / `ParseStatmRsSkB` here.
assert camel_case("SHAMapTest") == "SHAMapTest"
assert camel_case("parseStatmRSSkB") == "ParseStatmRSSkB"
assert camel_case("XRPAmount") == "XRPAmount"
assert camel_case("CSPRNG") == "CSPRNG"
def test_disabled_prefix_preserved() -> None:
assert snake_case("DISABLED_FooBar") == "DISABLED_foo_bar"
assert snake_case("DISABLED_foo_bar") == "DISABLED_foo_bar"
assert snake_case("DISABLED_") == "DISABLED_"
assert camel_case("DISABLED_foo_bar") == "DISABLED_FooBar"
assert camel_case("DISABLED_") == "DISABLED_"
# --- what counts as a test definition ---------------------------------------
def test_all_macros_recognized() -> None:
code = """
TEST(Suite, oneName)
TEST_F(Fixture, twoName)
TEST_P(Fixture, threeName)
TYPED_TEST(Fixture, fourName)
TYPED_TEST_P(Fixture, fiveName)
"""
assert fixed(code) == dedent("""
TEST(Suite, one_name)
TEST_F(Fixture, two_name)
TEST_P(Fixture, three_name)
TYPED_TEST(Fixture, four_name)
TYPED_TEST_P(Fixture, five_name)
""")
def test_conforming_definitions_untouched() -> None:
code = """
TEST(AccountSet, bad_inputs)
TEST_F(MutexMakeTest, default_constructor)
TEST(SHAMap, DISABLED_slow_path)
"""
assert reports(code) == []
assert fixed(code) == dedent(code)
def test_lookalikes_ignored() -> None:
code = """
// TEST(Suite, notATest)
TEST_EXPECT(someCall())
TEST_EXPECTS(amount == value, amount.getText())
INSTANTIATE_TEST_SUITE_P(Prefix, Fixture, testValues());
auto x = TEST(Suite, notATest);
TYPED_TEST_SUITE(Fixture, MyTypes);
"""
assert reports(code) == []
assert fixed(code) == dedent(code)
def test_indented_and_wrapped_definitions() -> None:
code = """
namespace ripple {
TEST(Suite, indentedName)
}
TEST_F(
SomeVeryLongFixtureName,
wrappedName)
"""
assert fixed(code) == dedent("""
namespace ripple {
TEST(Suite, indented_name)
}
TEST_F(
SomeVeryLongFixtureName,
wrapped_name)
""")
# --- rewriting --------------------------------------------------------------
def test_only_the_two_names_are_rewritten() -> None:
code = """
TEST(mulDiv, mulDiv)
{
auto const mulDiv = 1; // mulDiv stays
}
"""
assert fixed(code) == dedent("""
TEST(MulDiv, mul_div)
{
auto const mulDiv = 1; // mulDiv stays
}
""")
def test_suite_name_camel_cased() -> None:
code = """
TEST(json_value, limits)
TEST(scope, ScopeExit)
"""
assert reports(code) == [
"1: renamed suite 'json_value' to 'JsonValue'",
"2: renamed suite 'scope' to 'Scope'",
"2: renamed test case 'ScopeExit' to 'scope_exit'",
]
assert fixed(code) == dedent("""
TEST(JsonValue, limits)
TEST(Scope, scope_exit)
""")
def test_fixture_reported_but_not_renamed() -> None:
# The first argument names a class, so only a human (or clang-tidy) can
# rename it; the test-case name is still fixed.
code = """
TEST_F(my_fixture, someTest)
"""
assert reports(code) == [
"1: fixture 'my_fixture' is not CamelCase: rename the class to "
"'MyFixture' by hand",
"1: renamed test case 'someTest' to 'some_test'",
]
assert fixed(code) == dedent("""
TEST_F(my_fixture, some_test)
""")
def test_reports_carry_line_numbers() -> None:
code = """
#include <foo.h>
TEST(Suite, firstName)
TEST(Suite, secondName)
"""
assert reports(code) == [
"3: renamed test case 'firstName' to 'first_name'",
"5: renamed test case 'secondName' to 'second_name'",
]
# --- collisions -------------------------------------------------------------
def test_collision_reported_and_not_applied() -> None:
# Both would define `Suite_mul_div_Test`.
code = """
TEST(Suite, mulDiv)
TEST(Suite, mul_div)
"""
assert reports(code) == [
"1: cannot rename 'Suite, mulDiv' to 'Suite, mul_div': another test "
"case already generates that name"
]
assert fixed(code) == dedent(code)
def test_collision_between_converging_suites() -> None:
# Both suites camel-case to `SuiteA`, so both would define
# `SuiteA_one_test_Test`.
code = """
TEST(SuiteA, oneTest)
TEST(Suite_a, one_test)
"""
assert [r.split(":")[1].strip() for r in reports(code)] == [
"cannot rename 'SuiteA, oneTest' to 'SuiteA, one_test'",
"cannot rename 'Suite_a, one_test' to 'SuiteA, one_test'",
]
assert fixed(code) == dedent(code)
def test_same_name_in_different_suites_is_not_a_collision() -> None:
code = """
TEST(SuiteOne, mulDiv)
TEST(SuiteTwo, mulDiv)
"""
assert fixed(code) == dedent("""
TEST(SuiteOne, mul_div)
TEST(SuiteTwo, mul_div)
""")
def main() -> int:
tests = sorted(
(name, fn)
for name, fn in globals().items()
if name.startswith("test_") and callable(fn)
)
failed = 0
for name, fn in tests:
try:
fn()
print(f"PASS {name}")
except AssertionError as exc:
failed += 1
print(f"FAIL {name}: {exc!r}")
print(f"\n{len(tests) - failed}/{len(tests)} passed")
return 1 if failed else 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -3,28 +3,19 @@
#include <test/jtx/Account.h>
#include <test/jtx/Env.h>
#include <test/jtx/mpt.h>
#include <test/jtx/vault.h>
#include <xrpl/basics/Buffer.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/contract.h>
#include <xrpl/basics/strHex.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
#include <utility/mpt_utility.h>
#include <secp256k1.h>
#include <secp256k1_mpt.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <functional>
#include <optional>
#include <stdexcept>
@@ -54,237 +45,66 @@ protected:
return *value;
}
// Offset where the bulletproof begins in a send proof blob.
// Proof layout: [compact_sigma | bulletproof]
static constexpr size_t kBulletproofOffset = kEcSendProofLength - kEcDoubleBulletproofLength;
// Generate a forged aggregated bulletproof (double bulletproof) for
// the given values and blinding factors. Used to test that splicing
// a bulletproof claiming a different remaining balance is rejected.
// secp256k1 convention: returns 1 on success, 0 on failure.
static Buffer
getForgedBulletproof(
std::array<uint64_t, 2> const& values,
std::array<Buffer, 2> const& blindingFactors,
uint256 const& contextHash)
// Creates the MPT issuance on the given Env, authorizes and funds each
// holder, generates keys for the issuer, holders and optional auditor,
// registers the issuer/auditor keys, and converts part of each holder's
// balance to a confidential balance.
struct ConfidentialEnv
{
auto* const ctx = mpt_secp256k1_context();
// Per-holder configuration: the account, how much MPT to fund it
// with, and how much of that to convert to a confidential balance.
struct HolderInit
{
test::jtx::Account account;
std::uint64_t payAmount = 1000;
std::uint64_t convertAmount = 100;
};
secp256k1_pubkey h;
secp256k1_mpt_get_h_generator(ctx, &h);
test::jtx::MPTTester mpt;
Buffer proof(kEcDoubleBulletproofLength);
size_t proofLen = kEcDoubleBulletproofLength;
ConfidentialEnv(
test::jtx::Env& env,
test::jtx::Account const& issuer,
std::vector<HolderInit> const& holders,
std::uint32_t flags = tfMPTCanLock | tfMPTCanHoldConfidentialBalance | tfMPTCanTransfer,
std::optional<test::jtx::Account> auditor = std::nullopt);
unsigned char blindings[64];
std::memcpy(blindings, blindingFactors[0].data(), 32);
std::memcpy(blindings + 32, blindingFactors[1].data(), 32);
private:
static std::vector<test::jtx::Account>
extractAccounts(std::vector<HolderInit> const& holders);
};
if (secp256k1_bulletproof_prove_agg(
ctx,
proof.data(),
&proofLen,
values.data(),
blindings,
2,
&h,
contextHash.data()) == 0)
Throw<std::runtime_error>("Failed to generate forged bulletproof");
return proof;
}
// Generate a forged single bulletproof for a single value and blinding factor.
// Used to test ConvertBack overdraft prevention via bulletproof verification.
static Buffer
getForgedSingleBulletproof(
uint64_t value,
Buffer const& blindingFactor,
uint256 const& contextHash)
{
auto* const ctx = mpt_secp256k1_context();
secp256k1_pubkey h;
secp256k1_mpt_get_h_generator(ctx, &h);
Buffer proof(kEcSingleBulletproofLength);
size_t proofLen = kEcSingleBulletproofLength;
if (secp256k1_bulletproof_prove_agg(
ctx,
proof.data(),
&proofLen,
&value,
blindingFactor.data(),
1, // m = 1 (single bulletproof)
&h,
contextHash.data()) == 0)
Throw<std::runtime_error>("Failed to generate forged single bulletproof");
return proof;
}
// Forges a ConvertBack proof (compact sigma + single bulletproof) whose
// sigma component claims claimedBalance (which may be wrong) while binding
// to the real pedersen commitment and encrypted spending balance
// ciphertext already on the ledger. The bulletproof component is built
// from realBalance so it stays honest.
// mpt_get_convert_back_proof does not allow to build a proof whose amount
// exceeds the holder's claimed balance.
static Buffer
getForgedConvertBackProof(
// Create an issuance that can hold confidential balances, with the listed
// holders funded and authorized, and a key pair generated for the issuer,
// every holder, and every extra key owner. The keys are
// generated but not registered.
static void
setupConfidentialIssuance(
test::jtx::MPTTester& mpt,
test::jtx::Account const& holder,
uint64_t claimedBalance,
uint64_t realBalance,
uint64_t amt,
Buffer const& pedersenCommitment,
Buffer const& encryptedSpendingBalance,
Buffer const& pcBlindingFactor,
uint256 const& contextHash)
{
if (pedersenCommitment.size() != kCompressedEcPointLength)
Throw<std::runtime_error>("getForgedConvertBackProof: bad pedersenCommitment length");
if (encryptedSpendingBalance.size() != kEcGamalEncryptedTotalLength)
{
Throw<std::runtime_error>(
"getForgedConvertBackProof: bad encryptedSpendingBalance length");
}
if (amt > realBalance)
Throw<std::runtime_error>("getForgedConvertBackProof: amt exceeds realBalance");
test::jtx::Account const& issuer,
std::vector<test::jtx::Account> const& holders,
std::vector<test::jtx::Account> const& keyOwners = {},
std::uint32_t flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance);
auto* const ctx = mpt_secp256k1_context();
auto const holderPubKey = requireOptional(mpt.getPubKey(holder), "Missing holder pubkey");
auto const holderPrivKey =
requireOptional(mpt.getPrivKey(holder), "Missing holder privkey");
secp256k1_pubkey pkHolder;
if (secp256k1_ec_pubkey_parse(
ctx, &pkHolder, holderPubKey.data(), kCompressedEcPointLength) != 1)
Throw<std::runtime_error>("Failed to parse holder's public key");
secp256k1_pubkey pcB;
if (secp256k1_ec_pubkey_parse(
ctx, &pcB, pedersenCommitment.data(), kCompressedEcPointLength) != 1)
Throw<std::runtime_error>("Failed to parse pedersen commitment");
secp256k1_pubkey b1, b2;
if (secp256k1_ec_pubkey_parse(
ctx, &b1, encryptedSpendingBalance.data(), kCompressedEcPointLength) != 1 ||
secp256k1_ec_pubkey_parse(
ctx,
&b2,
encryptedSpendingBalance.data() + kCompressedEcPointLength,
kCompressedEcPointLength) != 1)
Throw<std::runtime_error>("Failed to parse balance ciphertext");
Buffer sigmaProof(SECP256K1_COMPACT_CONVERTBACK_PROOF_SIZE);
if (secp256k1_compact_convertback_prove(
ctx,
sigmaProof.data(),
claimedBalance,
holderPrivKey.data(),
pcBlindingFactor.data(),
&pkHolder,
&b1,
&b2,
&pcB,
contextHash.data()) != 1)
Throw<std::runtime_error>("Failed to generate convertback sigma proof");
auto const forgedBulletproof =
getForgedSingleBulletproof(realBalance - amt, pcBlindingFactor, contextHash);
Buffer proof(kEcConvertBackProofLength);
std::memcpy(proof.data(), sigmaProof.data(), SECP256K1_COMPACT_CONVERTBACK_PROOF_SIZE);
std::memcpy(
proof.data() + SECP256K1_COMPACT_CONVERTBACK_PROOF_SIZE,
forgedBulletproof.data(),
kEcSingleBulletproofLength);
return proof;
}
// Get a bad ciphertext with valid structure but cryptographic invalid for
// testing purposes. For preflight test purposes.
static Buffer const&
getBadCiphertext()
{
static Buffer const kBadCiphertext = []() {
Buffer buf(kEcGamalEncryptedTotalLength);
std::memset(buf.data(), 0xFF, kEcGamalEncryptedTotalLength);
buf.data()[0] = kEcCompressedPrefixEvenY;
buf.data()[kEcCiphertextComponentLength] = kEcCompressedPrefixEvenY;
return buf;
}();
return kBadCiphertext;
}
// Get a trivial buffer that is structurally and mathematically valid, but
// contains invalid data that does not match the ledger state. For preclaim
// test purposes.
static Buffer const&
getTrivialCiphertext()
{
static Buffer const kTrivialCiphertext = []() {
Buffer buf(kEcGamalEncryptedTotalLength);
std::memset(buf.data(), 0, kEcGamalEncryptedTotalLength);
buf.data()[0] = kEcCompressedPrefixEvenY;
buf.data()[kEcCiphertextComponentLength] = kEcCompressedPrefixEvenY;
buf.data()[kEcCiphertextComponentLength - 1] = 0x01;
buf.data()[kEcGamalEncryptedTotalLength - 1] = 0x01;
return buf;
}();
return kTrivialCiphertext;
}
// Returns a valid compressed EC point (33 bytes) that can pass preflight
// validation but contains invalid data for preclaim test purposes.
static Buffer const&
getTrivialCommitment()
{
static Buffer const kTrivialCommitment = []() {
Buffer buf(kEcPedersenCommitmentLength);
std::memset(buf.data(), 0, kEcPedersenCommitmentLength);
buf.data()[0] = kEcCompressedPrefixEvenY;
// Set last byte to make it a valid x-coordinate on the curve
buf.data()[kEcPedersenCommitmentLength - 1] = 0x01;
return buf;
}();
return kTrivialCommitment;
}
static std::string
getTrivialSendProofHex()
{
Buffer buf(kEcSendProofLength);
std::memset(buf.data(), 0, kEcSendProofLength);
for (std::size_t i = 0; i < kEcSendProofLength; i += kEcCiphertextComponentLength)
{
buf.data()[i] = kEcCompressedPrefixEvenY;
if (i + kEcCiphertextComponentLength - 1 < kEcSendProofLength)
buf.data()[i + kEcCiphertextComponentLength - 1] = 0x01;
}
return strHex(buf);
}
// Set up an MPT environment suitable for batch testing.
// alice is issuer; bob has 'bobAmt' in confidential spending; carol has
// 'carolAmt' in confidential spending; dave is initialised with pubkey but
// zero spending/inbox.
static void
setupBatchEnv(
test::jtx::MPTTester& mpt,
test::jtx::Account const& alice,
test::jtx::Account const& bob,
test::jtx::Account const& carol,
test::jtx::Account const& dave,
std::uint64_t bobAmt,
std::uint64_t carolAmt);
// Helper struct to encapsulate common setup for integration tests.
struct ConfidentialSendSetup
{
// Constants
uint64_t sendAmount;
size_t nRecipients;
uint32_t version;
// Blinding factors
@@ -324,55 +144,7 @@ protected:
test::jtx::Account const& dest,
test::jtx::Account const& issuer,
uint64_t amount,
std::optional<std::reference_wrapper<test::jtx::Account const>> auditor = std::nullopt)
: sendAmount(amount)
, nRecipients(auditor ? 4 : 3)
, version(mpt.getMPTokenVersion(sender))
, blindingFactor(generateBlindingFactor())
, amountBlindingFactor(blindingFactor)
, balanceBlindingFactor(generateBlindingFactor())
, senderAmt(mpt.encryptAmount(sender, amount, blindingFactor))
, destAmt(mpt.encryptAmount(dest, amount, blindingFactor))
, issuerAmt(mpt.encryptAmount(issuer, amount, blindingFactor))
, auditorAmt(
auditor ? std::optional<Buffer>(
mpt.encryptAmount(auditor->get(), amount, blindingFactor))
: std::nullopt)
, amountCommitment(mpt.getPedersenCommitment(amount, amountBlindingFactor))
, senderPubKey(requireOptional(mpt.getPubKey(sender), "Missing sender public key"))
, destPubKey(requireOptional(mpt.getPubKey(dest), "Missing destination public key"))
, issuerPubKey(requireOptional(mpt.getPubKey(issuer), "Missing issuer public key"))
, auditorPubKey(auditor ? mpt.getPubKey(auditor->get()) : std::nullopt)
, prevSpending(requireOptional(
mpt.getDecryptedBalance(sender, test::jtx::MPTTester::holderEncryptedSpending),
"Missing sender spending balance"))
, prevEncryptedSpending(requireOptional(
mpt.getEncryptedBalance(sender, test::jtx::MPTTester::holderEncryptedSpending),
"Missing sender encrypted spending balance"))
, balanceCommitment(mpt.getPedersenCommitment(prevSpending, balanceBlindingFactor))
{
recipients.push_back({
.publicKey = Slice(senderPubKey),
.encryptedAmount = senderAmt,
});
recipients.push_back({
.publicKey = Slice(destPubKey),
.encryptedAmount = destAmt,
});
recipients.push_back({
.publicKey = Slice(issuerPubKey),
.encryptedAmount = issuerAmt,
});
if (auditor)
{
recipients.push_back({
.publicKey =
Slice(requireOptionalRef(auditorPubKey, "Missing auditor public key")),
.encryptedAmount =
requireOptionalRef(auditorAmt, "Missing auditor encrypted amount"),
});
}
}
std::optional<std::reference_wrapper<test::jtx::Account const>> auditor = std::nullopt);
// Generate proof with current account sequence
std::optional<Buffer>
@@ -380,54 +152,78 @@ protected:
test::jtx::MPTTester& mpt,
test::jtx::Env& env,
test::jtx::Account const& sender,
test::jtx::Account const& dest) const
{
auto const ctxHash = getSendContextHash(
sender.id(), mpt.issuanceID(), env.seq(sender), dest.id(), version);
return mpt.getConfidentialSendProof(
sender,
sendAmount,
recipients,
blindingFactor,
ctxHash,
{
.pedersenCommitment = amountCommitment,
.amt = sendAmount,
.encryptedAmt = senderAmt,
.blindingFactor = amountBlindingFactor,
},
{
.pedersenCommitment = balanceCommitment,
.amt = prevSpending,
.encryptedAmt = prevEncryptedSpending,
.blindingFactor = balanceBlindingFactor,
});
}
test::jtx::Account const& dest) const;
[[nodiscard]] test::jtx::MPTConfidentialSend
sendArgs(
test::jtx::Account const& sender,
test::jtx::Account const& dest,
Buffer const& proof,
std::optional<TER> err = std::nullopt) const
{
return {
.account = sender,
.dest = dest,
.amt = sendAmount,
.proof = strHex(proof),
.senderEncryptedAmt = senderAmt,
.destEncryptedAmt = destAmt,
.issuerEncryptedAmt = issuerAmt,
.auditorEncryptedAmt = auditorAmt,
.amountCommitment = amountCommitment,
.balanceCommitment = balanceCommitment,
.err = err,
};
}
std::optional<TER> err = std::nullopt) const;
};
// Get a bad ciphertext with valid structure but cryptographic invalid for
// testing purposes. For preflight test purposes.
static Buffer const&
getBadCiphertext();
// Get a trivial buffer that is structurally and mathematically valid, but
// contains invalid data that does not match the ledger state. For preclaim
// test purposes.
static Buffer const&
getTrivialCiphertext();
// Returns a valid compressed EC point (33 bytes) that can pass preflight
// validation but contains invalid data for preclaim test purposes.
static Buffer const&
getTrivialCommitment();
// Returns a hex-encoded send proof of the correct length filled with
// placeholder data. It passes the proof length check in preflight but
// fails proof verification.
static std::string
getTrivialSendProofHex();
// Offset where the bulletproof begins in a send proof blob.
// Proof layout: [compact_sigma | bulletproof]
static constexpr size_t kBulletproofOffset = kEcSendProofLength - kEcDoubleBulletproofLength;
// Generate a forged aggregated bulletproof (double bulletproof) for
// the given values and blinding factors. Used to test that splicing
// a bulletproof claiming a different remaining balance is rejected.
static Buffer
getForgedBulletproof(
std::array<uint64_t, 2> const& values,
std::array<Buffer, 2> const& blindingFactors,
uint256 const& contextHash);
// Generate a forged single bulletproof for a single value and blinding factor.
// Used to test ConvertBack overdraft prevention via bulletproof verification.
static Buffer
getForgedSingleBulletproof(
uint64_t value,
Buffer const& blindingFactor,
uint256 const& contextHash);
// Forges a ConvertBack proof (compact sigma + single bulletproof) whose
// sigma component claims claimedBalance (which may be wrong) while binding
// to the real pedersen commitment and to the encrypted spending balance
// already on the ledger. The bulletproof component is built from the real
// remaining balance (realBalance - amt) so it stays honest.
// mpt_get_convert_back_proof validates its inputs before proving, so it
// cannot be used to build such an inconsistent proof.
static Buffer
getForgedConvertBackProof(
test::jtx::MPTTester& mpt,
test::jtx::Account const& holder,
uint64_t claimedBalance,
uint64_t realBalance,
uint64_t amt,
Buffer const& pedersenCommitment,
Buffer const& encryptedSpendingBalance,
Buffer const& pcBlindingFactor,
uint256 const& contextHash);
// Forges a ConfidentialMPTSend proof (compact sigma + double bulletproof)
// for setup.sendAmount against setup's real balance commitment/ciphertext.
// mpt_get_confidential_send_proof does not allow to build a proof whose amount
@@ -438,265 +234,7 @@ protected:
test::jtx::Env& env,
test::jtx::Account const& sender,
test::jtx::Account const& dest,
ConfidentialSendSetup const& setup)
{
auto* const ctx = mpt_secp256k1_context();
secp256k1_pubkey c1;
std::vector<secp256k1_pubkey> c2Vec(setup.recipients.size());
std::vector<secp256k1_pubkey> pkVec(setup.recipients.size());
for (std::size_t i = 0; i < setup.recipients.size(); ++i)
{
auto const& r = setup.recipients[i];
if (i == 0 &&
secp256k1_ec_pubkey_parse(
ctx, &c1, r.encryptedAmount.data(), kCompressedEcPointLength) != 1)
Throw<std::runtime_error>("Failed to parse C1");
if (secp256k1_ec_pubkey_parse(
ctx,
&c2Vec[i],
r.encryptedAmount.data() + kCompressedEcPointLength,
kCompressedEcPointLength) != 1)
Throw<std::runtime_error>("Failed to parse C2");
if (secp256k1_ec_pubkey_parse(
ctx, &pkVec[i], r.publicKey.data(), kCompressedEcPointLength) != 1)
Throw<std::runtime_error>("Failed to parse recipient pubkey");
}
secp256k1_pubkey pkSender, pcAmount, pcBalance, b1, b2;
if (secp256k1_ec_pubkey_parse(
ctx, &pkSender, setup.senderPubKey.data(), kCompressedEcPointLength) != 1 ||
secp256k1_ec_pubkey_parse(
ctx, &pcAmount, setup.amountCommitment.data(), kCompressedEcPointLength) != 1 ||
secp256k1_ec_pubkey_parse(
ctx, &pcBalance, setup.balanceCommitment.data(), kCompressedEcPointLength) != 1 ||
secp256k1_ec_pubkey_parse(
ctx, &b1, setup.prevEncryptedSpending.data(), kCompressedEcPointLength) != 1 ||
secp256k1_ec_pubkey_parse(
ctx,
&b2,
setup.prevEncryptedSpending.data() + kCompressedEcPointLength,
kCompressedEcPointLength) != 1)
Throw<std::runtime_error>("Failed to parse commitments/ciphertext");
Buffer const senderPrivKey =
requireOptional(mpt.getPrivKey(sender), "Missing sender privkey");
auto const ctxHash = getSendContextHash(
sender.id(), mpt.issuanceID(), env.seq(sender), dest.id(), setup.version);
Buffer sigmaProof(SECP256K1_COMPACT_STANDARD_PROOF_SIZE);
if (secp256k1_compact_standard_prove(
ctx,
sigmaProof.data(),
setup.sendAmount,
setup.prevSpending,
setup.blindingFactor.data(),
senderPrivKey.data(),
setup.balanceBlindingFactor.data(),
setup.recipients.size(),
&c1,
c2Vec.data(),
pkVec.data(),
&pcAmount,
&pkSender,
&pcBalance,
&b1,
&b2,
ctxHash.data()) != 1)
Throw<std::runtime_error>("Failed to generate sigma proof");
// Wraps (mod 2^64) for overdrafts, unlike the ledger's own homomorphic
// commitment subtraction (mod the curve order) — that mismatch is
// exactly what makes the forged proof fail verification.
// Computed without a wrapping `uint64` subtract: Clang UBSan treats
// unsigned overflow as fatal (see incrementConfidentialVersion).
std::uint64_t const remaining = setup.sendAmount <= setup.prevSpending
? setup.prevSpending - setup.sendAmount
: ~setup.sendAmount + setup.prevSpending + 1;
Buffer negAmountBf(kEcBlindingFactorLength);
Buffer remainingBf(kEcBlindingFactorLength);
secp256k1_mpt_scalar_negate(negAmountBf.data(), setup.amountBlindingFactor.data());
secp256k1_mpt_scalar_add(
remainingBf.data(), setup.balanceBlindingFactor.data(), negAmountBf.data());
auto const forgedBulletproof = getForgedBulletproof(
{setup.sendAmount, remaining}, {setup.amountBlindingFactor, remainingBf}, ctxHash);
Buffer combinedProof(kEcSendProofLength);
std::memcpy(combinedProof.data(), sigmaProof.data(), SECP256K1_COMPACT_STANDARD_PROOF_SIZE);
std::memcpy(
combinedProof.data() + SECP256K1_COMPACT_STANDARD_PROOF_SIZE,
forgedBulletproof.data(),
kEcDoubleBulletproofLength);
return combinedProof;
}
// Helper that wraps the boilerplate setup: Env + MPT creation, funding, key
// generation, and seeding each holder with a confidential balance.
// The caller supplies the issuer and any number of holders.
struct ConfidentialEnv
{
// Per-holder configuration: the account, how much MPT to fund it
// with, and how much of that to convert to a confidential balance.
struct HolderInit
{
test::jtx::Account account;
std::uint64_t payAmount = 1000;
std::uint64_t convertAmount = 100;
};
test::jtx::MPTTester mpt;
ConfidentialEnv(
test::jtx::Env& env,
test::jtx::Account const& issuer,
std::vector<HolderInit> const& holders,
std::uint32_t flags = tfMPTCanLock | tfMPTCanHoldConfidentialBalance | tfMPTCanTransfer,
std::optional<test::jtx::Account> auditor = std::nullopt)
: mpt{env, issuer, {.holders = extractAccounts(holders), .auditor = auditor}}
{
mpt.create({.ownerCount = 1, .flags = flags});
for (auto const& h : holders)
{
mpt.authorize({.account = h.account});
if ((flags & tfMPTRequireAuth) != 0)
mpt.authorize({.account = issuer, .holder = h.account});
mpt.pay(issuer, h.account, h.payAmount);
}
mpt.generateKeyPair(issuer);
for (auto const& h : holders)
mpt.generateKeyPair(h.account);
if (auditor)
mpt.generateKeyPair(requireOptionalRef(auditor, "Missing auditor"));
mpt.set({
.account = issuer,
.issuerPubKey = mpt.getPubKey(issuer),
.auditorPubKey = auditor
? mpt.getPubKey(requireOptionalRef(auditor, "Missing auditor"))
: std::optional<Buffer>{},
});
for (auto const& h : holders)
{
mpt.convert({
.account = h.account,
.amt = h.convertAmount,
.holderPubKey = mpt.getPubKey(h.account),
});
mpt.mergeInbox({.account = h.account});
}
}
private:
static std::vector<test::jtx::Account>
extractAccounts(std::vector<HolderInit> const& holders)
{
std::vector<test::jtx::Account> accounts;
accounts.reserve(holders.size());
for (auto const& h : holders)
accounts.push_back(h.account);
return accounts;
}
};
// Create an issuance that can hold confidential balances, with the listed
// holders funded and authorized, and a key pair generated for the issuer,
// every holder, and every extra key owner. The keys are
// generated but not registered.
static void
setupConfidentialIssuance(
test::jtx::MPTTester& mpt,
test::jtx::Account const& issuer,
std::vector<test::jtx::Account> const& holders,
std::vector<test::jtx::Account> const& keyOwners = {},
std::uint32_t flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance);
// Set up an MPT environment suitable for batch testing.
// alice is issuer; bob has 'bobAmt' in confidential spending; carol has
// 'carolAmt' in confidential spending; dave is initialised with pubkey but
// zero spending/inbox.
static void
setupBatchEnv(
test::jtx::MPTTester& mpt,
test::jtx::Account const& alice,
test::jtx::Account const& bob,
test::jtx::Account const& carol,
test::jtx::Account const& dave,
std::uint64_t bobAmt,
std::uint64_t carolAmt)
{
using namespace test::jtx;
mpt.create({
.ownerCount = 1,
.flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
});
mpt.authorize({.account = bob});
mpt.authorize({.account = carol});
mpt.authorize({.account = dave});
if (bobAmt > 0)
mpt.pay(alice, bob, bobAmt);
if (carolAmt > 0)
mpt.pay(alice, carol, carolAmt);
mpt.generateKeyPair(alice);
mpt.generateKeyPair(bob);
mpt.generateKeyPair(carol);
mpt.generateKeyPair(dave);
mpt.set({
.account = alice,
.issuerPubKey = mpt.getPubKey(alice),
});
if (bobAmt > 0)
{
mpt.convert({
.account = bob,
.amt = bobAmt,
.holderPubKey = mpt.getPubKey(bob),
});
mpt.mergeInbox({.account = bob});
}
else
{
mpt.convert({
.account = bob,
.amt = 0,
.holderPubKey = mpt.getPubKey(bob),
});
}
if (carolAmt > 0)
{
mpt.convert({
.account = carol,
.amt = carolAmt,
.holderPubKey = mpt.getPubKey(carol),
});
mpt.mergeInbox({.account = carol});
}
else
{
mpt.convert({
.account = carol,
.amt = 0,
.holderPubKey = mpt.getPubKey(carol),
});
}
// dave: register pubkey only (0 spending/inbox)
mpt.convert({
.account = dave,
.amt = 0,
.holderPubKey = mpt.getPubKey(dave),
});
}
ConfidentialSendSetup const& setup);
};
} // namespace xrpl

View File

@@ -1,13 +1,89 @@
#include <test/jtx/ConfidentialTransfer.h>
#include <test/jtx/Account.h>
#include <test/jtx/Env.h>
#include <test/jtx/mpt.h>
#include <xrpl/basics/Buffer.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/contract.h>
#include <xrpl/basics/strHex.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
#include <utility/mpt_utility.h>
#include <secp256k1.h>
#include <secp256k1_mpt.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <functional>
#include <optional>
#include <stdexcept>
#include <string>
#include <vector>
namespace xrpl {
ConfidentialTransferTestBase::ConfidentialEnv::ConfidentialEnv(
test::jtx::Env& env,
test::jtx::Account const& issuer,
std::vector<HolderInit> const& holders,
std::uint32_t flags,
std::optional<test::jtx::Account> auditor)
: mpt{env, issuer, {.holders = extractAccounts(holders), .auditor = auditor}}
{
mpt.create({.ownerCount = 1, .flags = flags});
for (auto const& h : holders)
{
mpt.authorize({.account = h.account});
if ((flags & tfMPTRequireAuth) != 0)
mpt.authorize({.account = issuer, .holder = h.account});
mpt.pay(issuer, h.account, h.payAmount);
}
mpt.generateKeyPair(issuer);
for (auto const& h : holders)
mpt.generateKeyPair(h.account);
if (auditor)
mpt.generateKeyPair(requireOptionalRef(auditor, "Missing auditor"));
mpt.set({
.account = issuer,
.issuerPubKey = mpt.getPubKey(issuer),
.auditorPubKey = auditor ? mpt.getPubKey(requireOptionalRef(auditor, "Missing auditor"))
: std::optional<Buffer>{},
});
for (auto const& h : holders)
{
mpt.convert({
.account = h.account,
.amt = h.convertAmount,
.holderPubKey = mpt.getPubKey(h.account),
});
mpt.mergeInbox({.account = h.account});
}
}
std::vector<test::jtx::Account>
ConfidentialTransferTestBase::ConfidentialEnv::extractAccounts(
std::vector<HolderInit> const& holders)
{
std::vector<test::jtx::Account> accounts;
accounts.reserve(holders.size());
for (auto const& h : holders)
accounts.push_back(h.account);
return accounts;
}
void
ConfidentialTransferTestBase::setupConfidentialIssuance(
test::jtx::MPTTester& mpt,
@@ -34,4 +110,478 @@ ConfidentialTransferTestBase::setupConfidentialIssuance(
mpt.generateKeyPair(keyOwner);
}
void
ConfidentialTransferTestBase::setupBatchEnv(
test::jtx::MPTTester& mpt,
test::jtx::Account const& alice,
test::jtx::Account const& bob,
test::jtx::Account const& carol,
test::jtx::Account const& dave,
std::uint64_t bobAmt,
std::uint64_t carolAmt)
{
using namespace test::jtx;
mpt.create({
.ownerCount = 1,
.flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
});
mpt.authorize({.account = bob});
mpt.authorize({.account = carol});
mpt.authorize({.account = dave});
if (bobAmt > 0)
mpt.pay(alice, bob, bobAmt);
if (carolAmt > 0)
mpt.pay(alice, carol, carolAmt);
mpt.generateKeyPair(alice);
mpt.generateKeyPair(bob);
mpt.generateKeyPair(carol);
mpt.generateKeyPair(dave);
mpt.set({
.account = alice,
.issuerPubKey = mpt.getPubKey(alice),
});
if (bobAmt > 0)
{
mpt.convert({
.account = bob,
.amt = bobAmt,
.holderPubKey = mpt.getPubKey(bob),
});
mpt.mergeInbox({.account = bob});
}
else
{
mpt.convert({
.account = bob,
.amt = 0,
.holderPubKey = mpt.getPubKey(bob),
});
}
if (carolAmt > 0)
{
mpt.convert({
.account = carol,
.amt = carolAmt,
.holderPubKey = mpt.getPubKey(carol),
});
mpt.mergeInbox({.account = carol});
}
else
{
mpt.convert({
.account = carol,
.amt = 0,
.holderPubKey = mpt.getPubKey(carol),
});
}
// dave: register pubkey only (0 spending/inbox)
mpt.convert({
.account = dave,
.amt = 0,
.holderPubKey = mpt.getPubKey(dave),
});
}
ConfidentialTransferTestBase::ConfidentialSendSetup::ConfidentialSendSetup(
test::jtx::MPTTester& mpt,
test::jtx::Account const& sender,
test::jtx::Account const& dest,
test::jtx::Account const& issuer,
uint64_t amount,
std::optional<std::reference_wrapper<test::jtx::Account const>> auditor)
: sendAmount(amount)
, version(mpt.getMPTokenVersion(sender))
, blindingFactor(generateBlindingFactor())
, amountBlindingFactor(blindingFactor)
, balanceBlindingFactor(generateBlindingFactor())
, senderAmt(mpt.encryptAmount(sender, amount, blindingFactor))
, destAmt(mpt.encryptAmount(dest, amount, blindingFactor))
, issuerAmt(mpt.encryptAmount(issuer, amount, blindingFactor))
, auditorAmt(
auditor ? std::optional<Buffer>(mpt.encryptAmount(auditor->get(), amount, blindingFactor))
: std::nullopt)
, amountCommitment(mpt.getPedersenCommitment(amount, amountBlindingFactor))
, senderPubKey(requireOptional(mpt.getPubKey(sender), "Missing sender public key"))
, destPubKey(requireOptional(mpt.getPubKey(dest), "Missing destination public key"))
, issuerPubKey(requireOptional(mpt.getPubKey(issuer), "Missing issuer public key"))
, auditorPubKey(auditor ? mpt.getPubKey(auditor->get()) : std::nullopt)
, prevSpending(requireOptional(
mpt.getDecryptedBalance(sender, test::jtx::MPTTester::holderEncryptedSpending),
"Missing sender spending balance"))
, prevEncryptedSpending(requireOptional(
mpt.getEncryptedBalance(sender, test::jtx::MPTTester::holderEncryptedSpending),
"Missing sender encrypted spending balance"))
, balanceCommitment(mpt.getPedersenCommitment(prevSpending, balanceBlindingFactor))
{
recipients.push_back({
.publicKey = Slice(senderPubKey),
.encryptedAmount = senderAmt,
});
recipients.push_back({
.publicKey = Slice(destPubKey),
.encryptedAmount = destAmt,
});
recipients.push_back({
.publicKey = Slice(issuerPubKey),
.encryptedAmount = issuerAmt,
});
if (auditor)
{
recipients.push_back({
.publicKey = Slice(requireOptionalRef(auditorPubKey, "Missing auditor public key")),
.encryptedAmount = requireOptionalRef(auditorAmt, "Missing auditor encrypted amount"),
});
}
}
std::optional<Buffer>
ConfidentialTransferTestBase::ConfidentialSendSetup::generateProof(
test::jtx::MPTTester& mpt,
test::jtx::Env& env,
test::jtx::Account const& sender,
test::jtx::Account const& dest) const
{
auto const ctxHash =
getSendContextHash(sender.id(), mpt.issuanceID(), env.seq(sender), dest.id(), version);
return mpt.getConfidentialSendProof(
sender,
sendAmount,
recipients,
blindingFactor,
ctxHash,
{
.pedersenCommitment = amountCommitment,
.amt = sendAmount,
.encryptedAmt = senderAmt,
.blindingFactor = amountBlindingFactor,
},
{
.pedersenCommitment = balanceCommitment,
.amt = prevSpending,
.encryptedAmt = prevEncryptedSpending,
.blindingFactor = balanceBlindingFactor,
});
}
test::jtx::MPTConfidentialSend
ConfidentialTransferTestBase::ConfidentialSendSetup::sendArgs(
test::jtx::Account const& sender,
test::jtx::Account const& dest,
Buffer const& proof,
std::optional<TER> err) const
{
return {
.account = sender,
.dest = dest,
.amt = sendAmount,
.proof = strHex(proof),
.senderEncryptedAmt = senderAmt,
.destEncryptedAmt = destAmt,
.issuerEncryptedAmt = issuerAmt,
.auditorEncryptedAmt = auditorAmt,
.amountCommitment = amountCommitment,
.balanceCommitment = balanceCommitment,
.err = err,
};
}
Buffer const&
ConfidentialTransferTestBase::getBadCiphertext()
{
static Buffer const kBadCiphertext = []() {
Buffer buf(kEcGamalEncryptedTotalLength);
std::memset(buf.data(), 0xFF, kEcGamalEncryptedTotalLength);
buf.data()[0] = kEcCompressedPrefixEvenY;
buf.data()[kEcCiphertextComponentLength] = kEcCompressedPrefixEvenY;
return buf;
}();
return kBadCiphertext;
}
Buffer const&
ConfidentialTransferTestBase::getTrivialCiphertext()
{
static Buffer const kTrivialCiphertext = []() {
Buffer buf(kEcGamalEncryptedTotalLength);
std::memset(buf.data(), 0, kEcGamalEncryptedTotalLength);
buf.data()[0] = kEcCompressedPrefixEvenY;
buf.data()[kEcCiphertextComponentLength] = kEcCompressedPrefixEvenY;
buf.data()[kEcCiphertextComponentLength - 1] = 0x01;
buf.data()[kEcGamalEncryptedTotalLength - 1] = 0x01;
return buf;
}();
return kTrivialCiphertext;
}
Buffer const&
ConfidentialTransferTestBase::getTrivialCommitment()
{
static Buffer const kTrivialCommitment = []() {
Buffer buf(kEcPedersenCommitmentLength);
std::memset(buf.data(), 0, kEcPedersenCommitmentLength);
buf.data()[0] = kEcCompressedPrefixEvenY;
// Set last byte to make it a valid x-coordinate on the curve
buf.data()[kEcPedersenCommitmentLength - 1] = 0x01;
return buf;
}();
return kTrivialCommitment;
}
std::string
ConfidentialTransferTestBase::getTrivialSendProofHex()
{
Buffer buf(kEcSendProofLength);
std::memset(buf.data(), 0, kEcSendProofLength);
for (std::size_t i = 0; i < kEcSendProofLength; i += kEcCiphertextComponentLength)
{
buf.data()[i] = kEcCompressedPrefixEvenY;
if (i + kEcCiphertextComponentLength - 1 < kEcSendProofLength)
buf.data()[i + kEcCiphertextComponentLength - 1] = 0x01;
}
return strHex(buf);
}
Buffer
ConfidentialTransferTestBase::getForgedBulletproof(
std::array<uint64_t, 2> const& values,
std::array<Buffer, 2> const& blindingFactors,
uint256 const& contextHash)
{
auto* const ctx = mpt_secp256k1_context();
secp256k1_pubkey h;
secp256k1_mpt_get_h_generator(ctx, &h);
Buffer proof(kEcDoubleBulletproofLength);
size_t proofLen = kEcDoubleBulletproofLength;
unsigned char blindings[64];
std::memcpy(blindings, blindingFactors[0].data(), 32);
std::memcpy(blindings + 32, blindingFactors[1].data(), 32);
if (secp256k1_bulletproof_prove_agg(
ctx, proof.data(), &proofLen, values.data(), blindings, 2, &h, contextHash.data()) == 0)
Throw<std::runtime_error>("Failed to generate forged bulletproof");
return proof;
}
Buffer
ConfidentialTransferTestBase::getForgedSingleBulletproof(
uint64_t value,
Buffer const& blindingFactor,
uint256 const& contextHash)
{
auto* const ctx = mpt_secp256k1_context();
secp256k1_pubkey h;
secp256k1_mpt_get_h_generator(ctx, &h);
Buffer proof(kEcSingleBulletproofLength);
size_t proofLen = kEcSingleBulletproofLength;
if (secp256k1_bulletproof_prove_agg(
ctx,
proof.data(),
&proofLen,
&value,
blindingFactor.data(),
1, // m = 1 (single bulletproof)
&h,
contextHash.data()) == 0)
Throw<std::runtime_error>("Failed to generate forged single bulletproof");
return proof;
}
Buffer
ConfidentialTransferTestBase::getForgedConvertBackProof(
test::jtx::MPTTester& mpt,
test::jtx::Account const& holder,
uint64_t claimedBalance,
uint64_t realBalance,
uint64_t amt,
Buffer const& pedersenCommitment,
Buffer const& encryptedSpendingBalance,
Buffer const& pcBlindingFactor,
uint256 const& contextHash)
{
if (pedersenCommitment.size() != kCompressedEcPointLength)
Throw<std::runtime_error>("getForgedConvertBackProof: bad pedersenCommitment length");
if (encryptedSpendingBalance.size() != kEcGamalEncryptedTotalLength)
{
Throw<std::runtime_error>("getForgedConvertBackProof: bad encryptedSpendingBalance length");
}
if (amt > realBalance)
Throw<std::runtime_error>("getForgedConvertBackProof: amt exceeds realBalance");
auto* const ctx = mpt_secp256k1_context();
auto const holderPubKey = requireOptional(mpt.getPubKey(holder), "Missing holder pubkey");
auto const holderPrivKey = requireOptional(mpt.getPrivKey(holder), "Missing holder privkey");
secp256k1_pubkey pkHolder;
if (secp256k1_ec_pubkey_parse(ctx, &pkHolder, holderPubKey.data(), kCompressedEcPointLength) !=
1)
Throw<std::runtime_error>("Failed to parse holder's public key");
secp256k1_pubkey pcB;
if (secp256k1_ec_pubkey_parse(ctx, &pcB, pedersenCommitment.data(), kCompressedEcPointLength) !=
1)
Throw<std::runtime_error>("Failed to parse pedersen commitment");
secp256k1_pubkey b1, b2;
if (secp256k1_ec_pubkey_parse(
ctx, &b1, encryptedSpendingBalance.data(), kCompressedEcPointLength) != 1 ||
secp256k1_ec_pubkey_parse(
ctx,
&b2,
encryptedSpendingBalance.data() + kCompressedEcPointLength,
kCompressedEcPointLength) != 1)
Throw<std::runtime_error>("Failed to parse balance ciphertext");
Buffer sigmaProof(SECP256K1_COMPACT_CONVERTBACK_PROOF_SIZE);
if (secp256k1_compact_convertback_prove(
ctx,
sigmaProof.data(),
claimedBalance,
holderPrivKey.data(),
pcBlindingFactor.data(),
&pkHolder,
&b1,
&b2,
&pcB,
contextHash.data()) != 1)
Throw<std::runtime_error>("Failed to generate convertback sigma proof");
auto const forgedBulletproof =
getForgedSingleBulletproof(realBalance - amt, pcBlindingFactor, contextHash);
Buffer proof(kEcConvertBackProofLength);
std::memcpy(proof.data(), sigmaProof.data(), SECP256K1_COMPACT_CONVERTBACK_PROOF_SIZE);
std::memcpy(
proof.data() + SECP256K1_COMPACT_CONVERTBACK_PROOF_SIZE,
forgedBulletproof.data(),
kEcSingleBulletproofLength);
return proof;
}
Buffer
ConfidentialTransferTestBase::getForgedSendProof(
test::jtx::MPTTester& mpt,
test::jtx::Env& env,
test::jtx::Account const& sender,
test::jtx::Account const& dest,
ConfidentialSendSetup const& setup)
{
auto* const ctx = mpt_secp256k1_context();
secp256k1_pubkey c1;
std::vector<secp256k1_pubkey> c2Vec(setup.recipients.size());
std::vector<secp256k1_pubkey> pkVec(setup.recipients.size());
for (std::size_t i = 0; i < setup.recipients.size(); ++i)
{
auto const& r = setup.recipients[i];
if (i == 0 &&
secp256k1_ec_pubkey_parse(
ctx, &c1, r.encryptedAmount.data(), kCompressedEcPointLength) != 1)
Throw<std::runtime_error>("Failed to parse C1");
if (secp256k1_ec_pubkey_parse(
ctx,
&c2Vec[i],
r.encryptedAmount.data() + kCompressedEcPointLength,
kCompressedEcPointLength) != 1)
Throw<std::runtime_error>("Failed to parse C2");
if (secp256k1_ec_pubkey_parse(
ctx, &pkVec[i], r.publicKey.data(), kCompressedEcPointLength) != 1)
Throw<std::runtime_error>("Failed to parse recipient pubkey");
}
secp256k1_pubkey pkSender, pcAmount, pcBalance, b1, b2;
if (secp256k1_ec_pubkey_parse(
ctx, &pkSender, setup.senderPubKey.data(), kCompressedEcPointLength) != 1 ||
secp256k1_ec_pubkey_parse(
ctx, &pcAmount, setup.amountCommitment.data(), kCompressedEcPointLength) != 1 ||
secp256k1_ec_pubkey_parse(
ctx, &pcBalance, setup.balanceCommitment.data(), kCompressedEcPointLength) != 1 ||
secp256k1_ec_pubkey_parse(
ctx, &b1, setup.prevEncryptedSpending.data(), kCompressedEcPointLength) != 1 ||
secp256k1_ec_pubkey_parse(
ctx,
&b2,
setup.prevEncryptedSpending.data() + kCompressedEcPointLength,
kCompressedEcPointLength) != 1)
Throw<std::runtime_error>("Failed to parse commitments/ciphertext");
Buffer const senderPrivKey = requireOptional(mpt.getPrivKey(sender), "Missing sender privkey");
auto const ctxHash = getSendContextHash(
sender.id(), mpt.issuanceID(), env.seq(sender), dest.id(), setup.version);
Buffer sigmaProof(SECP256K1_COMPACT_STANDARD_PROOF_SIZE);
if (secp256k1_compact_standard_prove(
ctx,
sigmaProof.data(),
setup.sendAmount,
setup.prevSpending,
setup.blindingFactor.data(),
senderPrivKey.data(),
setup.balanceBlindingFactor.data(),
setup.recipients.size(),
&c1,
c2Vec.data(),
pkVec.data(),
&pcAmount,
&pkSender,
&pcBalance,
&b1,
&b2,
ctxHash.data()) != 1)
Throw<std::runtime_error>("Failed to generate sigma proof");
// Wraps (mod 2^64) for overdrafts, unlike the ledger's own homomorphic
// commitment subtraction (mod the curve order) — that mismatch is
// exactly what makes the forged proof fail verification.
// Computed without a wrapping `uint64` subtract: Clang UBSan treats
// unsigned overflow as fatal (see incrementConfidentialVersion).
std::uint64_t const remaining = setup.sendAmount <= setup.prevSpending
? setup.prevSpending - setup.sendAmount
: ~setup.sendAmount + setup.prevSpending + 1;
Buffer negAmountBf(kEcBlindingFactorLength);
Buffer remainingBf(kEcBlindingFactorLength);
secp256k1_mpt_scalar_negate(negAmountBf.data(), setup.amountBlindingFactor.data());
secp256k1_mpt_scalar_add(
remainingBf.data(), setup.balanceBlindingFactor.data(), negAmountBf.data());
auto const forgedBulletproof = getForgedBulletproof(
{setup.sendAmount, remaining}, {setup.amountBlindingFactor, remainingBf}, ctxHash);
Buffer combinedProof(kEcSendProofLength);
std::memcpy(combinedProof.data(), sigmaProof.data(), SECP256K1_COMPACT_STANDARD_PROOF_SIZE);
std::memcpy(
combinedProof.data() + SECP256K1_COMPACT_STANDARD_PROOF_SIZE,
forgedBulletproof.data(),
kEcDoubleBulletproofLength);
return combinedProof;
}
} // namespace xrpl

View File

@@ -49,7 +49,7 @@ TEST(MallocTrimReport, structure)
}
#if defined(__GLIBC__) && BOOST_OS_LINUX
TEST(parseStatmRSSkB, standard_format)
TEST(ParseStatmRSSkB, standard_format)
{
using xrpl::detail::parseStatmRSSkB;
@@ -121,7 +121,7 @@ TEST(parseStatmRSSkB, standard_format)
}
#endif
TEST(mallocTrim, without_debug_logging)
TEST(MallocTrim, without_debug_logging)
{
beast::Journal const journal{beast::Journal::getNullSink()};
@@ -144,7 +144,7 @@ TEST(mallocTrim, without_debug_logging)
#endif
}
TEST(mallocTrim, empty_tag)
TEST(MallocTrim, empty_tag)
{
beast::Journal const journal{beast::Journal::getNullSink()};
MallocTrimReport const report = mallocTrim("", journal);
@@ -157,7 +157,7 @@ TEST(mallocTrim, empty_tag)
#endif
}
TEST(mallocTrim, with_debug_logging)
TEST(MallocTrim, with_debug_logging)
{
struct DebugSink : public beast::Journal::Sink
{
@@ -194,7 +194,7 @@ TEST(mallocTrim, with_debug_logging)
#endif
}
TEST(mallocTrim, repeated_calls)
TEST(MallocTrim, repeated_calls)
{
beast::Journal const journal{beast::Journal::getNullSink()};

View File

@@ -10,7 +10,7 @@
using namespace xrpl;
TEST(RangeSet, prevMissing)
TEST(RangeSet, prev_missing)
{
// Set will include:
// [ 0, 5]
@@ -36,7 +36,7 @@ TEST(RangeSet, prevMissing)
}
}
TEST(RangeSet, toString)
TEST(RangeSet, to_string)
{
RangeSet<std::uint32_t> set;
EXPECT_EQ(to_string(set), "empty");
@@ -54,7 +54,7 @@ TEST(RangeSet, toString)
EXPECT_EQ(to_string(set), "1-2,6");
}
TEST(RangeSet, fromString)
TEST(RangeSet, from_string)
{
RangeSet<std::uint32_t> set;

View File

@@ -290,7 +290,7 @@ TEST_F(StringUtilitiesTest, to_string)
EXPECT_EQ(result, "hello");
}
TEST_F(StringUtilitiesTest, trimWhitespace)
TEST_F(StringUtilitiesTest, trim_whitespace)
{
EXPECT_EQ(trimWhitespace(""), "");
EXPECT_EQ(trimWhitespace(" "), "");
@@ -303,7 +303,7 @@ TEST_F(StringUtilitiesTest, trimWhitespace)
EXPECT_EQ(trimWhitespace(" a b\tc "), "a b\tc");
}
TEST_F(StringUtilitiesTest, toLower)
TEST_F(StringUtilitiesTest, to_lower)
{
EXPECT_EQ(toLower(""), "");
EXPECT_EQ(toLower("ABC"), "abc");
@@ -318,7 +318,7 @@ TEST_F(StringUtilitiesTest, toLower)
// Both helpers are documented as depending only on their input. Guard that by
// checking the bytes just outside ASCII, which a locale-aware isspace/tolower
// could classify differently.
TEST_F(StringUtilitiesTest, trimAndLowerIgnoreLocale)
TEST_F(StringUtilitiesTest, trim_and_lower_ignore_locale)
{
// 0xA0 is NO-BREAK SPACE in Latin-1 and is whitespace to some locales.
std::string const nbsp("\xA0", 1);

View File

@@ -14,7 +14,7 @@ check(std::string const& in, std::string const& out)
EXPECT_EQ(base64Decode(encoded), in);
}
TEST(base64, base64)
TEST(Base64, base64)
{
// cspell: disable
check("", "");

View File

@@ -128,7 +128,7 @@ struct BaseUintTest : public ::testing::Test
using BaseUintDeathTest = BaseUintTest;
TEST_F(BaseUintDeathTest, fromRaw_size_mismatch)
TEST_F(BaseUintDeathTest, from_raw_size_mismatch)
{
// ENABLE_VOIDSTAR is a debug build, but does not crash on failed asserts. Rather than twist
// these tests into knots to make them work, just skip them.

View File

@@ -6,7 +6,7 @@
using namespace xrpl;
TEST(contract, contract)
TEST(Contract, contract)
{
try
{

View File

@@ -7,7 +7,7 @@
using namespace xrpl;
TEST(mulDiv, mulDiv)
TEST(MulDiv, mul_div)
{
auto const max = std::numeric_limits<std::uint64_t>::max();
std::uint64_t const max32 = std::numeric_limits<std::uint32_t>::max();

View File

@@ -6,7 +6,7 @@
using namespace xrpl;
TEST(scope, ScopeExit)
TEST(Scope, scope_exit)
{
// ScopeExit always executes the functor on destruction,
// unless release() is called
@@ -56,7 +56,7 @@ TEST(scope, ScopeExit)
EXPECT_EQ(i, 5);
}
TEST(scope, ScopeFail)
TEST(Scope, scope_fail)
{
// ScopeFail executes the functor on destruction only
// if an exception is unwinding, unless release() is called
@@ -106,7 +106,7 @@ TEST(scope, ScopeFail)
EXPECT_EQ(i, 5);
}
TEST(scope, ScopeSuccess)
TEST(Scope, scope_success)
{
// ScopeSuccess executes the functor on destruction only
// if an exception is not unwinding, unless release() is called

View File

@@ -105,7 +105,7 @@ static_assert(
using TagInt = TaggedInteger<std::int32_t, Tag1>;
TEST(tagged_integer, comparison_operators)
TEST(TaggedInteger, comparison_operators)
{
TagInt const zero(0);
TagInt const one(1);
@@ -131,7 +131,7 @@ TEST(tagged_integer, comparison_operators)
EXPECT_FALSE(one <= zero);
}
TEST(tagged_integer, increment_decrement_operators)
TEST(TaggedInteger, increment_decrement_operators)
{
TagInt const zero(0);
TagInt const one(1);
@@ -146,7 +146,7 @@ TEST(tagged_integer, increment_decrement_operators)
EXPECT_EQ(a, zero);
}
TEST(tagged_integer, arithmetic_operators)
TEST(TaggedInteger, arithmetic_operators)
{
TagInt const a{-2};
EXPECT_EQ(+a, TagInt{-2});
@@ -166,7 +166,7 @@ TEST(tagged_integer, arithmetic_operators)
EXPECT_EQ((TagInt{16} >> TagInt{2}), TagInt{4});
}
TEST(tagged_integer, assignment_operators)
TEST(TaggedInteger, assignment_operators)
{
TagInt a{-2};
TagInt b{0};

View File

@@ -6,7 +6,7 @@
using namespace xrpl;
TEST(csprng, get_values)
TEST(Csprng, get_values)
{
auto& engine = cryptoPrng();
auto randVal = engine();

View File

@@ -21,7 +21,7 @@
namespace xrpl {
TEST(json_value, limits)
TEST(JsonValue, limits)
{
using namespace json;
static_assert(Value::kMinInt == Int(~(UInt(-1) / 2)));
@@ -29,7 +29,7 @@ TEST(json_value, limits)
static_assert(Value::kMaxUInt == UInt(-1));
}
TEST(json_value, construct_and_compare_Json_StaticString)
TEST(JsonValue, construct_and_compare_json_static_string)
{
static constexpr char kSample[]{"Contents of a json::StaticString"};
@@ -52,7 +52,7 @@ TEST(json_value, construct_and_compare_Json_StaticString)
EXPECT_NE(kTest3, str);
}
TEST(json_value, different_types)
TEST(JsonValue, different_types)
{
// Exercise ValueType constructor
static constexpr json::StaticString kStaticStr{"staticStr"};
@@ -206,7 +206,7 @@ TEST(json_value, different_types)
}
}
TEST(json_value, compare_strings)
TEST(JsonValue, compare_strings)
{
auto doCompare = [&](json::Value const& lhs,
json::Value const& rhs,
@@ -560,7 +560,7 @@ TEST(json_value, compare_strings)
#pragma pop_macro("DO_COMPARE")
}
TEST(json_value, bool)
TEST(JsonValue, bool)
{
EXPECT_FALSE(json::Value());
@@ -583,7 +583,7 @@ TEST(json_value, bool)
EXPECT_TRUE(bool(object));
}
TEST(json_value, bad_json)
TEST(JsonValue, bad_json)
{
char const* s(R"({"method":"ledger","params":[{"ledger_index":1e300}]})");
@@ -607,7 +607,7 @@ parseValue(std::string const& doc)
} // namespace
TEST(json_value, parse_double_valid)
TEST(JsonValue, parse_double_valid)
{
// 1e300 is large but still representable, so it parses (unlike the out-of-range cases below).
for (auto const& [text, expected] :
@@ -627,14 +627,14 @@ TEST(json_value, parse_double_valid)
}
}
TEST(json_value, parse_double_out_of_range)
TEST(JsonValue, parse_double_out_of_range)
{
// Magnitudes with no finite double representation are rejected.
for (char const* oor : {"1e400", "-1e400", "0.001e500", "1e-400", "-1e-400", "123e-500"})
EXPECT_FALSE(parseValue(oor).has_value()) << oor;
}
TEST(json_value, parse_double_malformed)
TEST(JsonValue, parse_double_malformed)
{
// readNumber() collects any run of digits and '.eE+-' into a single Double
// token, so these malformed tokens reach decodeDouble. Each has a valid
@@ -644,7 +644,7 @@ TEST(json_value, parse_double_malformed)
EXPECT_FALSE(parseValue(bad).has_value()) << bad;
}
TEST(json_value, edge_cases)
TEST(JsonValue, edge_cases)
{
std::uint32_t const maxUInt = std::numeric_limits<std::uint32_t>::max();
std::int32_t const maxInt = std::numeric_limits<std::int32_t>::max();
@@ -791,7 +791,7 @@ TEST(json_value, edge_cases)
}
}
TEST(json_value, copy)
TEST(JsonValue, copy)
{
json::Value v1{2.5};
EXPECT_TRUE(v1.isDouble());
@@ -812,7 +812,7 @@ TEST(json_value, copy)
EXPECT_EQ(v1, v2);
}
TEST(json_value, move)
TEST(JsonValue, move)
{
json::Value v1{2.5};
EXPECT_TRUE(v1.isDouble());
@@ -831,7 +831,7 @@ TEST(json_value, move)
EXPECT_NE(v1, v2); // NOLINT(bugprone-use-after-move)
}
TEST(json_value, comparisons)
TEST(JsonValue, comparisons)
{
json::Value a, b;
auto testEquals = [&](std::string const& name) {
@@ -886,7 +886,7 @@ TEST(json_value, comparisons)
testGreaterThan("big");
}
TEST(json_value, compact)
TEST(JsonValue, compact)
{
json::Value j;
json::Reader r;
@@ -909,7 +909,7 @@ TEST(json_value, compact)
}
}
TEST(json_value, conversions)
TEST(JsonValue, conversions)
{
// We have json::ValueType::Real but json::Value::asDouble.
// TODO: What's the thinking here?
@@ -1125,7 +1125,7 @@ TEST(json_value, conversions)
}
}
TEST(json_value, access_members)
TEST(JsonValue, access_members)
{
json::Value val;
EXPECT_EQ(val.type(), json::ValueType::Null);
@@ -1218,7 +1218,7 @@ TEST(json_value, access_members)
}
}
TEST(json_value, remove_members)
TEST(JsonValue, remove_members)
{
json::Value val;
EXPECT_EQ(val.removeMember(std::string("member")).type(), json::ValueType::Null);
@@ -1245,7 +1245,7 @@ TEST(json_value, remove_members)
EXPECT_EQ(val.size(), 0);
}
TEST(json_value, iterator)
TEST(JsonValue, iterator)
{
{
// Iterating an array.
@@ -1331,7 +1331,7 @@ TEST(json_value, iterator)
}
}
TEST(json_value, nest_limits)
TEST(JsonValue, nest_limits)
{
json::Reader r;
{
@@ -1377,7 +1377,7 @@ TEST(json_value, nest_limits)
}
}
TEST(json_value, memory_leak)
TEST(JsonValue, memory_leak)
{
// When run with the address sanitizer, this test confirms there is no
// memory leak with the scenarios below.

View File

@@ -10,7 +10,7 @@
namespace xrpl::test {
TEST(AMMEntryTests, Constructors)
TEST(AMMEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -8,7 +8,7 @@
namespace xrpl::test {
TEST(AccountRootEntryTests, Constructors)
TEST(AccountRootEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -7,7 +7,7 @@
namespace xrpl::test {
TEST(AmendmentsEntryTests, Constructors)
TEST(AmendmentsEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -11,7 +11,7 @@
namespace xrpl::test {
TEST(BridgeEntryTests, Constructors)
TEST(BridgeEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -8,7 +8,7 @@
namespace xrpl::test {
TEST(CheckEntryTests, Constructors)
TEST(CheckEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -11,7 +11,7 @@
namespace xrpl::test {
TEST(CredentialEntryTests, Constructors)
TEST(CredentialEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -7,7 +7,7 @@
namespace xrpl::test {
TEST(DIDEntryTests, Constructors)
TEST(DIDEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -8,7 +8,7 @@
namespace xrpl::test {
TEST(DelegateEntryTests, Constructors)
TEST(DelegateEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -14,7 +14,7 @@
namespace xrpl::test {
TEST(DepositPreauthEntryTests, Constructors)
TEST(DepositPreauthEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -10,7 +10,7 @@
namespace xrpl::test {
TEST(DirectoryNodeEntryTests, Constructors)
TEST(DirectoryNodeEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -8,7 +8,7 @@
namespace xrpl::test {
TEST(EscrowEntryTests, Constructors)
TEST(EscrowEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -7,7 +7,7 @@
namespace xrpl::test {
TEST(FeeSettingsEntryTests, Constructors)
TEST(FeeSettingsEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -7,7 +7,7 @@
namespace xrpl::test {
TEST(LedgerHashesEntryTests, Constructors)
TEST(LedgerHashesEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -8,7 +8,7 @@
namespace xrpl::test {
TEST(LoanBrokerEntryTests, Constructors)
TEST(LoanBrokerEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -9,7 +9,7 @@
namespace xrpl::test {
TEST(LoanEntryTests, Constructors)
TEST(LoanEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -8,7 +8,7 @@
namespace xrpl::test {
TEST(MPTokenEntryTests, Constructors)
TEST(MPTokenEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -10,7 +10,7 @@
namespace xrpl::test {
TEST(MPTokenIssuanceEntryTests, Constructors)
TEST(MPTokenIssuanceEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -8,7 +8,7 @@
namespace xrpl::test {
TEST(NFTokenOfferEntryTests, Constructors)
TEST(NFTokenOfferEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -8,7 +8,7 @@
namespace xrpl::test {
TEST(NFTokenPageEntryTests, Constructors)
TEST(NFTokenPageEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -7,7 +7,7 @@
namespace xrpl::test {
TEST(NegativeUNLEntryTests, Constructors)
TEST(NegativeUNLEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -8,7 +8,7 @@
namespace xrpl::test {
TEST(OfferEntryTests, Constructors)
TEST(OfferEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -9,7 +9,7 @@
namespace xrpl::test {
TEST(OracleEntryTests, Constructors)
TEST(OracleEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -9,7 +9,7 @@
namespace xrpl::test {
TEST(PayChannelEntryTests, Constructors)
TEST(PayChannelEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -8,7 +8,7 @@
namespace xrpl::test {
TEST(PermissionedDomainEntryTests, Constructors)
TEST(PermissionedDomainEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -10,7 +10,7 @@
namespace xrpl::test {
TEST(RippleStateEntryTests, Constructors)
TEST(RippleStateEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -150,7 +150,7 @@ protected:
}
};
TEST_F(SLEBaseTests, ReadOnly)
TEST_F(SLEBaseTests, read_only)
{
AccountRootEntryR const absent(bob_.id(), env_.getClosedLedger());
EXPECT_FALSE(absent.exists());
@@ -169,7 +169,7 @@ TEST_F(SLEBaseTests, ReadOnly)
EXPECT_EQ(&present.readView(), &env_.getClosedLedger());
}
TEST_F(SLEBaseTests, AdoptSLE)
TEST_F(SLEBaseTests, adopt_sle)
{
auto const sle = env_.getClosedLedger().read(keylet::account(alice_.id()));
ASSERT_NE(sle, nullptr);
@@ -202,7 +202,7 @@ TEST_F(SLEBaseTests, AdoptSLE)
"writable entries must not be constructible from a bare SLE");
}
TEST_F(SLEBaseTests, WritableAccessors)
TEST_F(SLEBaseTests, writable_accessors)
{
ApplyViewImpl av(&env_.getClosedLedger(), TapNone);
beast::Journal const j{beast::Journal::getNullSink()};
@@ -237,7 +237,7 @@ TEST_F(SLEBaseTests, WritableAccessors)
!HasApplyView<AccountRootEntryR>, "applyView() must not exist on a read-only entry");
}
TEST_F(SLEBaseTests, ApplyViewContextCtor)
TEST_F(SLEBaseTests, apply_view_context_ctor)
{
ApplyViewImpl av(&env_.getClosedLedger(), TapNone);
beast::Journal const j{beast::Journal::getNullSink()};
@@ -261,7 +261,7 @@ TEST_F(SLEBaseTests, ApplyViewContextCtor)
EXPECT_EQ(fromCtx.rawSle(), fromView.rawSle());
}
TEST_F(SLEBaseTests, WritableLifecycle)
TEST_F(SLEBaseTests, writable_lifecycle)
{
// A view we never apply, so nothing here reaches the ledger.
ApplyViewImpl av(&env_.getClosedLedger(), TapNone);
@@ -319,7 +319,7 @@ TEST_F(SLEBaseTests, WritableLifecycle)
}
}
TEST_F(SLEBaseTests, Conversion)
TEST_F(SLEBaseTests, conversion)
{
ApplyViewImpl av(&env_.getClosedLedger(), TapNone);
@@ -337,7 +337,7 @@ TEST_F(SLEBaseTests, Conversion)
EXPECT_EQ(generic.type(), ltACCOUNT_ROOT);
}
TEST_F(SLEBaseTests, ResolveEntryPeeks)
TEST_F(SLEBaseTests, resolve_entry_peeks)
{
// getOpenLedger() is an OpenView, which derives from ReadView but not
// from ApplyView, so resolveEntry's dynamic_cast fails and this takes
@@ -369,7 +369,7 @@ TEST_F(SLEBaseTests, ResolveEntryPeeks)
EXPECT_EQ(readOnly->getFieldU32(sfSequence), bumped);
}
TEST_F(SLEBaseTests, ThrowsOnMissingEntry)
TEST_F(SLEBaseTests, throws_on_missing_entry)
{
// A generic read-only entry has no static type to fall back on, so
// type() must read it off the (absent) SLE and throw.
@@ -390,7 +390,7 @@ TEST_F(SLEBaseTests, ThrowsOnMissingEntry)
EXPECT_THROW(std::ignore = (*missing).getType(), std::logic_error);
}
TEST_F(SLEBaseTests, ThrowsOnMissingWritableEntry)
TEST_F(SLEBaseTests, throws_on_missing_writable_entry)
{
// A view we never apply, so nothing here reaches the ledger.
ApplyViewImpl av(&env_.getClosedLedger(), TapNone);

View File

@@ -7,7 +7,7 @@
namespace xrpl::test {
TEST(SignerListEntryTests, Constructors)
TEST(SignerListEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -8,7 +8,7 @@
namespace xrpl::test {
TEST(SponsorshipEntryTests, Constructors)
TEST(SponsorshipEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -8,7 +8,7 @@
namespace xrpl::test {
TEST(TicketEntryTests, Constructors)
TEST(TicketEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -8,7 +8,7 @@
namespace xrpl::test {
TEST(VaultEntryTests, Constructors)
TEST(VaultEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -12,7 +12,7 @@
namespace xrpl::test {
TEST(XChainOwnedClaimIDEntryTests, Constructors)
TEST(XChainOwnedClaimIDEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -13,7 +13,7 @@
namespace xrpl::test {
TEST(XChainOwnedCreateAccountClaimIDEntryTests, Constructors)
TEST(XChainOwnedCreateAccountClaimIDEntryTests, constructors)
{
EntryTestEnv e;

View File

@@ -28,7 +28,7 @@ account(std::string_view hex)
// getText() builds its string from eight substitutions of the same type, so a
// transposed pair would still compile and still type check. Pin the output so
// the field/value pairing is actually verified.
TEST(STXChainBridge, getTextPairsEachFieldWithItsValue)
TEST(STXChainBridge, get_text_pairs_each_field_with_its_value)
{
auto const lockingDoor = account("0102030405060708090A0B0C0D0E0F1011121314");
auto const issuingDoor = account("14131211100F0E0D0C0B0A090807060504030201");
@@ -46,7 +46,7 @@ TEST(STXChainBridge, getTextPairsEachFieldWithItsValue)
EXPECT_EQ(bridge.getText(), expected);
}
TEST(STXChainBridge, getTextOnADefaultBridge)
TEST(STXChainBridge, get_text_on_a_default_bridge)
{
STXChainBridge const bridge;
auto const text = bridge.getText();

View File

@@ -12,7 +12,7 @@ using namespace xrpl;
// by subscribing the real cap through a WebSocket, which would exceed the frame
// limit and drop the connection before the check runs) lets the boundary be
// asserted exactly.
TEST(InfoSubSubscriptionCap, Boundary)
TEST(InfoSubSubscriptionCap, boundary)
{
constexpr std::size_t cap = kMaxSubscriptionsPerConnection;
@@ -30,7 +30,7 @@ TEST(InfoSubSubscriptionCap, Boundary)
EXPECT_TRUE(exceedsSubscriptionCap(cap - 1, 2));
}
TEST(InfoSubSubscriptionCap, NoOverflow)
TEST(InfoSubSubscriptionCap, no_overflow)
{
constexpr std::size_t cap = kMaxSubscriptionsPerConnection;
constexpr std::size_t max = std::numeric_limits<std::size_t>::max();
@@ -41,7 +41,7 @@ TEST(InfoSubSubscriptionCap, NoOverflow)
EXPECT_TRUE(exceedsSubscriptionCap(cap, max));
}
TEST(InfoSubSubscriptionCap, ExplicitCap)
TEST(InfoSubSubscriptionCap, explicit_cap)
{
// A configured override is honored: the boundary tracks the passed cap, not
// the built-in default. This is the seam doSubscribe uses to enforce a

View File

@@ -43,7 +43,7 @@
namespace xrpl::test {
TEST(AccountSet, NullAccountSet)
TEST(AccountSet, null_account_set)
{
TxTest env;
@@ -60,7 +60,7 @@ TEST(AccountSet, NullAccountSet)
EXPECT_EQ(accountRoot.getFlags(), 0);
}
TEST(AccountSet, MostFlags)
TEST(AccountSet, most_flags)
{
Account const alice("alice");
@@ -175,7 +175,7 @@ TEST(AccountSet, MostFlags)
});
}
TEST(AccountSet, SetAndResetAccountTxnID)
TEST(AccountSet, set_and_reset_account_txn_id)
{
TxTest env;
Account const alice("alice");
@@ -206,7 +206,7 @@ TEST(AccountSet, SetAndResetAccountTxnID)
EXPECT_EQ(nowFlags, origFlags);
}
TEST(AccountSet, SetNoFreeze)
TEST(AccountSet, set_no_freeze)
{
TxTest env;
Account const alice("alice");
@@ -249,7 +249,7 @@ TEST(AccountSet, SetNoFreeze)
EXPECT_TRUE(env.getAccountRoot(alice).isFlag(lsfNoFreeze));
}
TEST(AccountSet, Domain)
TEST(AccountSet, domain)
{
TxTest env;
Account const alice("alice");
@@ -317,7 +317,7 @@ TEST(AccountSet, Domain)
}
}
TEST(AccountSet, MessageKey)
TEST(AccountSet, message_key)
{
TxTest env;
Account const alice("alice");
@@ -358,7 +358,7 @@ TEST(AccountSet, MessageKey)
telBAD_PUBLIC_KEY);
}
TEST(AccountSet, WalletID)
TEST(AccountSet, wallet_id)
{
TxTest env;
Account const alice("alice");
@@ -391,7 +391,7 @@ TEST(AccountSet, WalletID)
EXPECT_FALSE(env.getAccountRoot(alice).hasWalletLocator());
}
TEST(AccountSet, EmailHash)
TEST(AccountSet, email_hash)
{
TxTest env;
Account const alice("alice");
@@ -422,7 +422,7 @@ TEST(AccountSet, EmailHash)
EXPECT_FALSE(env.getAccountRoot(alice).hasEmailHash());
}
TEST(AccountSet, TransferRate)
TEST(AccountSet, transfer_rate)
{
struct TestCase
{
@@ -473,7 +473,7 @@ TEST(AccountSet, TransferRate)
}
}
TEST(AccountSet, BadInputs)
TEST(AccountSet, bad_inputs)
{
TxTest env;
Account const alice("alice");
@@ -553,7 +553,7 @@ TEST(AccountSet, BadInputs)
tecNO_ALTERNATIVE_KEY);
}
TEST(AccountSet, RequireAuthWithDir)
TEST(AccountSet, require_auth_with_dir)
{
TxTest env;
Account const alice("alice");
@@ -601,7 +601,7 @@ TEST(AccountSet, RequireAuthWithDir)
tesSUCCESS);
}
TEST(AccountSet, Ticket)
TEST(AccountSet, ticket)
{
TxTest env;
Account const alice("alice");
@@ -660,7 +660,7 @@ TEST(AccountSet, Ticket)
tefNO_TICKET);
}
TEST(AccountSet, BadSigningKey)
TEST(AccountSet, bad_signing_key)
{
TxTest env;
Account const alice("alice");
@@ -684,7 +684,7 @@ TEST(AccountSet, BadSigningKey)
EXPECT_FALSE(result.applied);
}
TEST(AccountSet, Gateway)
TEST(AccountSet, gateway)
{
Account const alice("alice");
Account const bob("bob");

View File

@@ -43,7 +43,7 @@ batchWrapping(STObject inner)
} // namespace
// The happy path — an ordinary Payment is independently submittable.
TEST(ProposalHelpers, PlainPaymentIsValid)
TEST(ProposalHelpers, plain_payment_is_valid)
{
EXPECT_TRUE(proposal::isValidProposal(txOfType(ttPAYMENT)));
}
@@ -52,14 +52,14 @@ TEST(ProposalHelpers, PlainPaymentIsValid)
// this earlier (the payload lacks TransactionProposalCreate's own template
// fields), but the defense here re-checks that guard so the two cannot
// drift apart.
TEST(ProposalHelpers, NestedProposalIsRejected)
TEST(ProposalHelpers, nested_proposal_is_rejected)
{
EXPECT_FALSE(proposal::isValidProposal(txOfType(ttTRANSACTION_PROPOSAL_CREATE)));
}
// Any pseudo-transaction — see STTx::isPseudoTx. Also normally caught earlier
// by STTx construction / preflight0.
TEST(ProposalHelpers, PseudoTxIsRejected)
TEST(ProposalHelpers, pseudo_tx_is_rejected)
{
EXPECT_FALSE(proposal::isValidProposal(txOfType(ttAMENDMENT)));
EXPECT_FALSE(proposal::isValidProposal(txOfType(ttFEE)));
@@ -70,7 +70,7 @@ TEST(ProposalHelpers, PseudoTxIsRejected)
// so it must never stand on its own as a proposed transaction. preflight0
// rejects the standalone case with temINVALID_INNER_BATCH before we get
// here; the guard is re-checked so the two cannot drift apart.
TEST(ProposalHelpers, InnerBatchFlagIsRejected)
TEST(ProposalHelpers, inner_batch_flag_is_rejected)
{
STObject tx = txOfType(ttPAYMENT);
tx.setFieldU32(sfFlags, tfInnerBatchTxn);
@@ -79,7 +79,7 @@ TEST(ProposalHelpers, InnerBatchFlagIsRejected)
// A Flags value that is present but does not include tfInnerBatchTxn must
// not be rejected — the check is bit-specific, not "any flag present".
TEST(ProposalHelpers, OtherFlagsAreAccepted)
TEST(ProposalHelpers, other_flags_are_accepted)
{
STObject tx = txOfType(ttPAYMENT);
tx.setFieldU32(sfFlags, tfFullyCanonicalSig);
@@ -88,19 +88,19 @@ TEST(ProposalHelpers, OtherFlagsAreAccepted)
// A Batch wrapping a plain inner is fine — the loop is only there to catch
// specifically forbidden inner types.
TEST(ProposalHelpers, BatchWithPlainInnerIsValid)
TEST(ProposalHelpers, batch_with_plain_inner_is_valid)
{
EXPECT_TRUE(proposal::isValidProposal(batchWrapping(txOfType(ttPAYMENT))));
}
// A Batch whose inner is itself a proposal must be rejected.
TEST(ProposalHelpers, BatchWithNestedProposalInnerIsRejected)
TEST(ProposalHelpers, batch_with_nested_proposal_inner_is_rejected)
{
EXPECT_FALSE(proposal::isValidProposal(batchWrapping(txOfType(ttTRANSACTION_PROPOSAL_CREATE))));
}
// A Batch whose inner is a pseudo-transaction must be rejected.
TEST(ProposalHelpers, BatchWithPseudoInnerIsRejected)
TEST(ProposalHelpers, batch_with_pseudo_inner_is_rejected)
{
EXPECT_FALSE(proposal::isValidProposal(batchWrapping(txOfType(ttAMENDMENT))));
}
@@ -108,7 +108,7 @@ TEST(ProposalHelpers, BatchWithPseudoInnerIsRejected)
// A Batch with no sfRawTransactions field skips the inner-loop entirely.
// Not something the transactor would ever emit, but the branch exists in the
// helper (the field is optional at the STObject level) and should hold.
TEST(ProposalHelpers, BatchWithoutRawTransactionsIsValid)
TEST(ProposalHelpers, batch_without_raw_transactions_is_valid)
{
EXPECT_TRUE(proposal::isValidProposal(txOfType(ttBATCH)));
}