v11.6.0
-
The protocol schema follows rippled develop at
e3c8996e, the 3.4.0-rc1 build the nightly stand is pinned to (#182 bumps the pin).definitions.jsonhad been synced for 3.3.0 and was eight fields behind develop, which definitions-watch had reported as node-only for three weeks. The nightly-pin bump that would have listed them opened with an empty "definitions.json vs the new build" section: the step inheritsbash -efrom the runner, and the diff exits 1 whenever it finds drift, so errexit ended the step at the assignment - before the report was echoed or recorded - in the one case the step exists for. Fixed alongside.- closed-ended vaults (rippled #7921, LendingProtocolV1_1):
VaultCreateandLOVaultcarryVaultKind,SubscriptionDateandRedemptionDate, and theVaultKindenum names the two kinds.ValidateVaultCreatepins rippled's preflight: the dates only on a closed-ended vault, both of them, with the redemption at least three minutes and less than thirty years after the subscription (rippled #8151 raised the floor from one minute; caught by running the closed-ended flow on the nightly stand). Deposits are accepted in the subscription phase only, withdrawals in every phase but investment - confidential MPT key rotation (rippled #7915, ConfidentialMPTKeyRotation):
LOMPTokenIssuancecarriesIssuerKeyEpochandAuditorKeyEpoch, incremented each timeMPTokenIssuanceSetreplaces the key. The transaction is unchanged - the sameIssuerEncryptionKey/AuditorEncryptionKeyfields rotate a key once the amendment is active, and the current key is refused withtecDUPLICATE.LOMPTokencarries the holder side,IssuerKeyMirrorEpochandAuditorKeyMirrorEpoch: the epoch each mirrored encrypted balance was produced under, which rippled compares against the issuance's epoch to decide whether the mirror is still current, so a rotation marks it stale.ContractResult(rippled #7988) is known to the codec but belongs to no format yet - the vendored
ledger_entries.macromoves to developf6b51f0b, the commit that placed the mirror epochs onMPToken.TestULedgerEntryFieldsConformancereads that file in both directions, so moving the pin also named the two Smart Escrow entries it carries, and the models follow them:LOEscrowgainsBytecodeandData,LOFeeSettingsthe votedGasLimit,BytecodeSizeLimitandGasPrice. Ledger-object fields only - theSmartEscrowamendment isSupported::Noon every build, so nothing returns them yet and the transaction side stays out of this release VaultWithdrawandLoanBrokerCoverWithdrawacceptCredentialIDs, for aDestinationthat requires deposit authorization; validated the wayPayment.CredentialIDsis- a protocol field is a member on the transaction's interface as well as on its classes, so
IVaultCreate,IVaultWithdrawandILoanBrokerCoverWithdraweach gained one. Anything outside the SDK that implements one of those interfaces - an adapter, a test double - stops compiling until it declares the new member. No default bodies: on a data contract a default would have to accept a value and drop it, which is the silent outcome these interfaces exist to avoid, and unlikeIXrplClientnothing implements them to add behaviour - the vendored
transactions.macrois pinned to the same develop commit asledger_entries.macroinstead of the 3.3.0 tag, so both conformance tests describe the build the nightly stand runs. Up to 3.3.0 the tag and develop agreed on transaction fields; they no longer do Xrpl.BinaryCodec11.6.0.0 for the new codec entries, numbered withXrplsince both move in this release. The CI stand (3.3.0) knows none of the new fields, so the unit suite covers them with round trips and validation, andTestIClosedEndedVaultdrives them against the nightly stand (LendingProtocolV1_1 at genesis, the pin from #182): a closed-ended vault through its three phases, an open-ended one carrying noVaultKindon the ledger, and aVaultWithdrawto a deposit-authorized destination that istecNO_PERMISSIONwithoutCredentialIDsand succeeds with them. Key rotation has no stand: ConfidentialMPTKeyRotation is Supported::No, and the shared amendment generator cannot preset a name the 3.3.0 binary does not know- the Loan integration suite follows the amendment too. Under LendingProtocolV1_1
LoanBrokerSetrefuses an open-ended vault ("LoanBroker requires a closed-ended Vault",tecNO_PERMISSION) and every Loan test built its broker on one, so all 18 ofTestILoanfailed on the nightly stand - identically on untoucheddev, which is what said it was the node's rule rather than a regression.TestILoanBasenow creates a closed-ended vault where the node asks for one and waits for the investment phase before handing the broker back: rippled originates a loan only there, while the vault deposit that funds it is taken only in the subscription phase before it, and the two dates are measured from the ledger's close time rather than the machine's clock, which on a standalone stand is a different clock. A node without the amendment does not know the fields at all - it answersinvalidTransaction, not a result code - so the open-ended path stays for it, chosen throughAmendmentGuard - what that unblocks, and what it does not: on the nightly stand
TestILoangoes from 0 of 18 to 7 of 18, and all 11 that still failed reportedCounterparty: Invalid signature- the role signing prefixesfixCleanup3_4_0introduces, which is what the entry above this one goes on to implement, and which was the whole of what remained.TestISponsoredVaultLoanis blocked by the same thing on 3.4.x, atSponsor: Invalid signature. Neither failure was about vaults any more, which is what this entry set out to establish; the signatures are the subject of the entry above, and are green there. The CI stand (3.3.0) is untouched by any of it; what the suite looks like there once the signing change lands is counted in the entry above ValidatedCloseTimeAsyncandWaitForCloseTimeAsyncmoved toIntegrationTestConfig: three test classes now need the ledger clock, and each had been carrying its own copy
- closed-ended vaults (rippled #7921, LendingProtocolV1_1):
-
A sponsor's and a counterparty's signature cover bytes of their own (rippled
fixCleanup3_4_0, breaking against nodes without the amendment). Until it, every signature on a transaction covered the same bytes: the submitter'sTxnSignature, the sponsor'sSponsorSignature(XLS-68) and the borrower'sCounterpartySignature(XLS-66) were all made over one preimage, so a signature could be lifted out of one role and pasted into another and still verify. rippled now gives each role its own four-byte hash prefix, and this release signs that way.HashPrefixgainsCounterpartyTransactionSig,CounterpartyTransactionMultiSig,SponsorTransactionSigandSponsorTransactionMultiSig, andEncodeForSigning/EncodeForMultiSigninggain overloads taking one. Overloads rather than an optional parameter, for the binary compatibility reason the SDK has met before. The transaction's own prefixes are untouched, so an ordinary signature - single or multisig, Batch included - is byte-for-byte what it was- the role is not something a caller states. It follows from the method: a wallet named as the
Sponsorsigns as sponsor, a LoanSetCounterpartyas counterparty, and a multi-signature entry on a transaction whose main signature is single can only belong to the co-signing side. One shape is genuinely ambiguous, where the main signature and a co-signature are both multi-signed, and only there does the signer have to say:Sign(tx, multisign, signingFor, SignatureRole.Sponsor). Asking for it in a shape that does not have it is refused rather than signed wrongly - multi-signature entries are no longer portable between sections. The composer's premise - that
tx.Signers,SponsorSignature.SignersandCounterpartySignature.Signersare identical bytes, so routing could be settled at composition time - held only before the amendment. Routing still happens in the composer, by account, but the signer now has to know its side already - what breaks: a signature this release makes is rejected by a node that has
SponsororLendingProtocolenabled but notfixCleanup3_4_0. No public network is in that state - on mainnet and testnet none of the three is enabled, on devnet all of them are - so the affected combination is a private node on a release older than the amendment. The older scheme is not carried, and nothing asks the node which one it wants: signing stays offline - the one place the combination does occur is this repository's own CI stand, a 3.3.0 image with
SponsorandLendingProtocolvoted in at genesis, so the integration tests that produce a role signature now skip there throughAmendmentGuardand run on the nightly stand instead. Sixty of them, measured: the CI stand ran 346 integration tests before this release and runs 289, with 60 skipped. The guard sits on the tests that actually make a role signature, not on their classes - theSponsorshipSettests carry none and keep running there. It is coverage deferred rather than lost, on a stand rather than in the suite, and the guard turns it back on by itself once the CI stand moves to a release carrying the amendment - the composer now checks what it composes. Routing an entry by account is a guess about what its signer meant, and since the amendment a wrong guess is no longer harmless.
ComposeSignaturesverifies every signature in the finished transaction against the bytes that transaction ships, under the prefix of the section the signature landed in, and names the account and the section when one does not verify. It catches the shape the transaction cannot express - the main signature multi-signed as well, so an entry could belong to either side - and the other direction too, a part signed over aSigningPubKeythe composed transaction does not carry. Both were found by a cold review of this branch, on two models independently, and both used to reach the node as a transaction the caller believed was signed SponsorSigningHelper.GetSigningPreimageis nowGetSponsorPreimage, and the internal loan oneGetCounterpartyPreimage. The name returned the bytes of both signatures while they were the same; keeping it would have changed what a call means without changing how it compiles. No[Obsolete]bridge, the same policy the breaks above it follow: a member that refuses is still a member on the public surface, and the compile error a removal gives is the migration notice- pinned by unit tests that read the prefixes out of rippled's own
HashPrefix.h, vendored as a fixture beside the other protocol files, and assert that a role preimage is the transaction's preimage with four bytes changed and nothing else - a pinned blob cannot answer that question, since regenerating it from the same code only agrees with itself. What settles it is the node: on the nightly standTestILoanpasses 18 of 18 and the sponsorship classes are green, where before this release every one of them was refused withInvalid signature - two rules of the same amendment surfaced once the tests could reach them, and are in the tests rather than in the SDK: a loan may only be impaired once a payment is actually late, and a payment on an overdue loan must carry
tfLoanLatePaymentor it istecEXPIRED