Skip to content

Nested multi sign [Feature]#6368

Open
RichardAH wants to merge 15 commits intoXRPLF:developfrom
RichardAH:nested-multi-sign-rippled
Open

Nested multi sign [Feature]#6368
RichardAH wants to merge 15 commits intoXRPLF:developfrom
RichardAH:nested-multi-sign-rippled

Conversation

@RichardAH
Copy link
Contributor

@RichardAH RichardAH commented Feb 13, 2026

NestedMultiSign Amendment

Note: this is a back port of Xahau/xahaud#675 to Xrpld. It may need further attention to ensure compatibility with Xrpld-only features.

Specification: XRPLF/XRPL-Standards#472

Overview

This amendment introduces nested multi-signature validation, allowing signer lists to delegate signing authority to other accounts that themselves have signer lists. This enables hierarchical organizational structures where, for example, a company account can require signatures from department accounts, which in turn require signatures from authorized individuals.

Key Features

Hierarchical Signing (up to 4 levels deep)

  • Signers can now delegate to their own signer lists rather than providing a direct signature
  • Enables corporate governance structures: Company → Departments → Teams → Individuals
  • Each level validates against its own signer list and quorum requirements
  • Weight is only contributed to the parent level when a signer's own quorum is satisfied

Cycle Detection & Quorum Relaxation

  • Detects circular dependencies in signer list configurations (e.g., A→B→A)
  • Prevents permanent fund lockout when accounts accidentally create cyclical signer relationships
  • When a cycle makes certain signers unavailable, the effective quorum is relaxed to the maximum achievable weight
  • Accounts with effectiveQuorum == 0 (all signers cyclic) correctly fail with tefBAD_QUORUM

Transaction Structure

Nested signers are represented by including an sfSigners array instead of sfSigningPubKey/sfTxnSignature:

{
  "Signers": [{
    "Signer": {
      "Account": "DepartmentA",
      "Signers": [{
        "Signer": {
          "Account": "Employee1",
          "SigningPubKey": "...",
          "TxnSignature": "..."
        }
      }]
    }
  }]
}

Validation Rules

  • Maximum nesting depth: 4 levels
  • Signers at each level must be in strict ascending order (consensus requirement)
  • A signer entry cannot have both nested signers AND signature fields
  • Leaf signers must provide valid SigningPubKey and TxnSignature
  • Each level's accumulated weight must meet its quorum (or relaxed quorum if cycles exist)

Error Codes

Code Condition
tefBAD_SIGNATURE Depth limit exceeded, invalid signer, or malformed signer entry
tefBAD_QUORUM Insufficient weight to meet quorum, or all signers are cyclic
tefNOT_MULTI_SIGNING Nested signer account has no signer list configured
tefMASTER_DISABLED Leaf signer's master key is disabled
tefINTERNAL Invariant violation (should never occur)

Backward Compatibility

When featureNestedMultiSign is disabled, nested signer arrays are rejected with temMALFORMED, preserving existing behavior.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (non-breaking change that only restructures code)
  • Performance (increase or change in throughput and/or latency)
  • Tests (you added tests for code that already exists, or your new feature included in this PR)
  • Documentation update
  • Chore (no impact to binary, e.g. .gitignore, formatting, dropping support for older tooling)
  • Release

API Impact

  • Public API: New feature (new methods and/or new fields)
  • Public API: Breaking change (in general, breaking changes should only impact the next api_version)
  • libxrpl change (any change that may affect libxrpl or dependents of libxrpl)
  • Peer protocol change (must be backward compatible or bump the peer protocol version)

RichardAH and others added 15 commits February 10, 2026 12:44
Transplant 3 xahaud review-fix commits (dbd5903e8, 23fd210e2, 8d9a38e44)
on top of the initial rippled backport (0b5220b).

- Add shared helper functions (isLeafSigner, isNestedSigner,
  isValidSignerEntry, countPresentFields) and depth constants
  (nestedMultiSignMaxDepth, legacyMultiSignMaxDepth) to STTx.h
- Rewrite Transactor::checkMultiSign with recursive validation,
  cycle detection via ancestors set, and cyclic lockout relaxation
- Update calculateBaseFee with depth guard and helper usage
- Refactor STTx.cpp multiSignHelper to use helpers and add
  malformed signer entry detection
- Replace TransactionSign.cpp signer validation with recursive
  validateSignersRecursive using helpers
- Add test_countPresentFields unit test and nested fee calculation
  test cases (Test Cases 16-17)
- Update STTx_test.cpp for optional sfSigningPubKey with
  validation-layer helper checks
- Fix feature-disabled test expectation (telENV_RPC_FAILED)
- fix nested signer array bounds check using wrong variable (signers vs
  signersArray) in STTx.cpp multiSignHelper
- move auth check before cyclic skip in Transactor.cpp so unauthorized
  cyclic signer entries are rejected instead of silently ignored
- add tests for nested array cardinality (empty/oversized)
- add tests for unauthorized vs authorized cyclic signer entries
Add testNestedMultiSignEdgeCases with 10 tests targeting uncovered
code paths across the three validation layers: multiSignHelper
(depth overflow, corrupt pubkey, malformed entries, unsorted signers),
Transactor::checkMultiSign (fee calculation, non-phantom signers,
cyclic quorum), and transactionSubmitMultiSigned (RPC shape and depth
validation). Includes a direct checkSign test for the depth-exceeded
path using manually-constructed STObjects.
Add nestedMultiSignMaxLeafSigners (64) to bound worst-case signature
verification cost. The leaf counter in multiSignHelper increments
before each verify() call and rejects with "Too many leaf signers."
when exceeded. Only enforced when featureNestedMultiSign is enabled.

Tests: leaf cap exceeded (65 leaves → temMALFORMED) and at-cap
succeeds (64 leaves via 2×32 nested tree → tesSUCCESS).
checkSign rejects >64 leaves before the transaction enters the
engine, so env() cannot extract a TER code and returns
telENV_RPC_FAILED rather than temMALFORMED.
Plain assert is compiled out in release builds, dropping these
consensus-critical invariant checks in production.
feat: nested multi-sign tests and review fixes
@RichardAH RichardAH changed the title Nested multi sign rippled Nested multi sign [Feature] Feb 13, 2026
@Tapanito
Copy link
Collaborator

Hi @RichardAH, thanks for the contribution!

Before we can move forward with this PR, we need to track this as an XLS Specification in the XRPL-Standards repository. Could you please open a discussion and a PR there.

You can check out the guidelines here for the full process. Thanks!

@Tapanito Tapanito added the Needs Draft XLS The corresponding draft XLS must have been merged before this PR is merged. label Feb 13, 2026
auto const txnAccountID = &sigObject != this ? std::nullopt : std::optional<AccountID>(getAccountID(sfAccount));

// Set max depth based on feature flag
int const maxDepth = rules.enabled(featureNestedMultiSign) ? 4 : 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable was introduced by the duplicate commit (e45514f3) and is unused — depth limiting is already handled inside multiSignHelper using the nestedMultiSignMaxDepth / legacyMultiSignMaxDepth constants. Safe to drop these 3 lines.

@RichardAH
Copy link
Contributor Author

Hi @RichardAH, thanks for the contribution!

Before we can move forward with this PR, we need to track this as an XLS Specification in the XRPL-Standards repository. Could you please open a discussion and a PR there.

You can check out the guidelines here for the full process. Thanks!

I can write you an XLS if you want, but I would point out, as the creator of the XLS standards system, simple amendments that provide small enhancements in existing functionality were never supposed to become XLS entries. XLS is intended for public-facing standards that many parties need to agree on in advance to have their software work correctly together... similar to RFC. Reply again if you really need one and I'll write one.

@Tapanito
Copy link
Collaborator

Tapanito commented Feb 13, 2026

Hi @RichardAH, thanks for the contribution!
Before we can move forward with this PR, we need to track this as an XLS Specification in the XRPL-Standards repository. Could you please open a discussion and a PR there.
You can check out the guidelines here for the full process. Thanks!

I can write you an XLS if you want, but I would point out, as the creator of the XLS standards system, simple amendments that provide small enhancements in existing functionality were never supposed to become XLS entries. XLS is intended for public-facing standards that many parties need to agree on in advance to have their software work correctly together... similar to RFC. Reply again if you really need one and I'll write one.

Hi @RichardAH,

We genuinely appreciate your role in establishing the XLS framework; it has been foundational for the ecosystem.

However, as the ecosystem has grown, our governance processes have evolved. To ensure consistency and comprehensive documentation for all changes, we mandate an XLS Specification for all new feature amendments, regardless of their scope.

Please proceed with drafting the XLS. For reference, you can find the mandated XLS templates here

Respectfully,
Vito

@RichardAH
Copy link
Contributor Author

Hi @RichardAH, thanks for the contribution!
Before we can move forward with this PR, we need to track this as an XLS Specification in the XRPL-Standards repository. Could you please open a discussion and a PR there.
You can check out the guidelines here for the full process. Thanks!

I can write you an XLS if you want, but I would point out, as the creator of the XLS standards system, simple amendments that provide small enhancements in existing functionality were never supposed to become XLS entries. XLS is intended for public-facing standards that many parties need to agree on in advance to have their software work correctly together... similar to RFC. Reply again if you really need one and I'll write one.

Hi @RichardAH,

We genuinely appreciate your role in establishing the XLS framework; it has been foundational for the ecosystem.

However, as the ecosystem has grown, our governance processes have evolved. To ensure consistency and comprehensive documentation for all changes, we mandate an XLS Specification for all new feature amendments, regardless of their scope.

Please proceed with drafting the XLS. For reference, you can find the mandated XLS templates here

Respectfully, Vito

🫡
XRPLF/XRPL-Standards#472

@Tapanito Tapanito removed the Needs Draft XLS The corresponding draft XLS must have been merged before this PR is merged. label Feb 13, 2026
@mvadari mvadari added the Needs Draft XLS The corresponding draft XLS must have been merged before this PR is merged. label Feb 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs Draft XLS The corresponding draft XLS must have been merged before this PR is merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

Comments