Skip to content

v11.20.0

Choose a tag to compare

@github-actions github-actions released this 25 Oct 21:00
0ae4d8f
  • XrplAddressCodec.DecodeXAddress verifies the Base58Check checksum and the 35-byte length and raises AddressCodecException on a mismatch, so IsValidXAddress is false and XAddressToClassicAddress throws for a mistyped X-address; it decoded the typo to another account or tag. An invalid Base58 character also raises AddressCodecException (was EncodingFormatException); the EncodingFormatException is its InnerException, through the new AddressCodecException(string, Exception) constructor. B58.DecodeAndCheck is internal instead of private
  • XrplBinaryCodec.EncodeForSigningClaim reads the claim amount as a ulong (NumberStyles.None, invariant culture): an amount above 2,147,483,647 drops threw OverflowException, which broke SignPaymentChannelClaim and VerifyPaymentChannelClaim for claims above ~2147 XRP. The amount may be a JSON string or a JSON number; anything else, a sign, whitespace, a fraction or an exponent raises ArgumentException. int.Parse accepted "+5" and " 5", which are now rejected
  • Breaking: XrplKeypairs.DeriveKeypair honours algorithm, as xrpl.js does (algorithm ?? decoded.Type; anything but ed25519 is secp256k1); it always followed the seed's prefix. What breaks: XrplWallet.FromSeed(seed, masterAddress, algorithm) with an algorithm that differs from the seed's own now returns that algorithm's keys and address. Migration: pass null to keep deriving by the seed prefix
  • Breaking: BufferParser.ReadUIntN and StreamParser.ReadUIntN return uint; they returned the result cast to a byte, so n > 1 lost every byte but the last. Migration: cast the result where a byte was assigned
  • StreamParser.Peek no longer consumes the byte; Amount.FromParser read through StReader.FromStream lost the first byte of every Amount. At the end of the input StreamParser.Peek and BufferParser.Peek raise InvalidOperationException with the position and size (BufferParser.Peek raised IndexOutOfRangeException)
  • ShaMapInner copy-on-write after ShaMap.Copy: PathToIndex decides on the root's DoCoW instead of the deepest inner's, and reads the root after MoveNext. Adding to a copied map threw NullReferenceException when the copy-on-write path ran, and otherwise mutated inner nodes the two maps share
  • Uint16.FromValue(int) and Uint32.FromValue(int) keep the high bytes and raise BinaryCodecException out of range; they truncated the value to a byte. Uint64.FromValue(string) pads the hex on the left ("FF" is 255), as xrpl.js does; it padded on the right. Uint64.FromValue(int) raises BinaryCodecException for a negative value, which it wrapped to a large unsigned one
  • ExtensionHelpers.Exponent and NumFuncAll(...).Exponent include the fraction and the written exponent ("1.5" is -1, "1e5" is 5); they returned the count of trailing zeros
  • Breaking: Bits.ToString overloads print the bytes in order; with the default Bits.IsLittleEndian = false they printed them reversed. What breaks: a caller that relied on the reversed string gets the bytes in array order. Migration: reverse the array before the call where the reversed output was wanted
  • RIPEMD160Managed.HashCore reads full 64-byte blocks from ibStart, not from the start of the array
  • Xrpl.AddressCodec, Xrpl.BinaryCodec and Xrpl.Keypairs are 11.20.0.0
  • Tests: TestUAddressCodec adds the xrpl.js bad-checksum and bad-prefix X-addresses, a one-character typo in the account bytes and a 34- and 36-byte payload with a valid checksum; TestUBaseCodecRegressions (BinaryCodec) covers each codec fix above; TestUKeypairs derives the xrpl.js sh1HiK7SwjS1VxFdXi7qeMHRedrYX vector as ed25519 and secp256k1 and an ed25519 seed as secp256k1; TestURipemd160 hashes the reference 80-byte vector whole and from an offset. TestUInt.TestFromValue64 and TestUSeed.TestEDAlgorithmSeed expected the old behaviour and now expect the xrpl.js result
  • XrpConversion.XrpToDrops accepts at most 6 fractional digits (was 7) and throws ValidationException for an amount that is not a whole number of drops instead of truncating it. Breaking: "1.0000005" used to convert to "1000000"; it now throws, and so does SignPaymentChannelClaim / VerifyPaymentChannelClaim with such an amount. Round the amount to 6 decimals before converting
  • XrpConversion.XrpToDrops(double) formats the value with InvariantCulture, so it no longer throws FormatException under a culture with a decimal comma; XrpConversion.DropsToXrp(string) returns an invariant string ("1.5", not "1,5")
  • GetFeeXrpSugar.GetFeeXrp rounds the fee up to whole drops (xrpl.js rounds half-up to 6 decimals) and floors maxFeeXRP to whole drops before capping; maxFeeXRP is parsed with InvariantCulture. AutofillSugar.CalculateFeePerTransactionType reads the cap through the same helper (GetFeeXrpSugar.MaxFeeDrops) instead of XrpToDrops, which now rejects a sub-drop cap such as "0.0000115". Breaking: a maxFeeXRP below one drop, or one that is not a number, throws ValidationException from both: it would floor to a zero fee, and a zero-fee transaction is never applied. A fractional load_factor (e.g. 1.03515625) no longer makes Autofill throw ValidationException from XrpToDrops
  • AutofillSugar.Autofill fetches Sequence, Fee and LastLedgerSequence concurrently without writing to the transaction, and writes the results after Task.WhenAll; the helpers used to add their field to the same Dictionary from concurrent continuations. Batch normalization (BatchNormalizer.NormalizeBatchTransaction) runs before the fetches instead of alongside them. A non-Batch transaction with TicketSequence and no Sequence gets Sequence 0 instead of the account sequence, which rippled rejects with temSEQ_AND_TICKET; account_info is not requested. xrpl.js fills the account sequence in this case. SetNextValidSequenceNumber, CalculateFeePerTransactionType and SetLatestValidatedLedgerSequence keep their signatures and behaviour
  • BalancesSugar.GetXrpBalance formats the balance with InvariantCulture
  • Tests: TestUFeeRoundingAndCulture covers GetFeeXrp and CalculateFeePerTransactionType with load factor 1.03515625, a sub-drop maxFeeXRP, Autofill filling all three fields and dropping a LastLedgerSequence of 0, a sub-drop cap through CalculateFeePerTransactionType and Autofill, a cap below one drop refused, Sequence 0 with TicketSequence and the account sequence without it, 7-digit rejection in XrpToDrops, and XrpToDrops(double), DropsToXrp, GetXrpBalance and GetFeeXrp under a decimal-comma culture. FeeTestClient takes LoadFactor and AccountRoot; TestUAutofillFees.TestUCalculateFee_DevnetMultiplier_AppliesCorrection expects 24 drops (1.2 drops rounded up to 2, times 12)
  • BatchNormalizer.NormalizeBatchTransaction (reached from Autofill for a Batch) keeps a caller's inner Sequence and continues that account's later inner transactions after it; an inner transaction with TicketSequence gets Sequence 0 and consumes no sequence; an outer Batch with TicketSequence gets Sequence 0 instead of the account's next sequence, so its inner transactions start from the account's ledger Sequence. It used to overwrite every inner Sequence and fill the outer one next to a TicketSequence, which rippled answers with temSEQ_AND_TICKET. An inner Sequence the caller set below one already assigned does not move the account back, one set before the account was looked up does not stop the lookup, and a Sequence of uint.MaxValue with a transaction to follow raises ValidationException instead of OverflowException
  • BatchSigningHelper.MergeBatchSigner copies an incoming single signature into a BatchSigner that has none, and raises InvalidOperationException for two different signatures of one account, a single signature for an account that is multi-signed, and a Signers list for an account that is single-signed; it used to keep the target as it was, or drop its single signature for the list. Inner Signers are merged by signer account: the same signature once, a different signature from the same signer raises InvalidOperationException instead of being kept beside the first (rippled answers a duplicate signer with temBAD_SIGNER)
  • Common.IsMemo and Common.IsSigner read the STArray form {"Memo": {...}} / {"Signer": {...}}; IsMemo treats MemoType, MemoData and MemoFormat as optional hex strings. ValidateBaseTransaction, and with it Validation.Validate on the outer Batch in BatchUtils.Build, rejected every transaction with Memos or Signers
  • Flags.SetTransactionFlagsToNumber keeps int, long and ulong flags as uint and raises ValidationException for a value outside UInt32; it set them to 0, which zeroed the outer Batch mode flag. Flags.ConvertAccountSetFlagsToNumber maps the tf* AccountSet flags (tfRequireDestTag ... tfAllowXRP) instead of the asf* values
  • Common.IsMPTAmount added; Common.IsAmount accepts an MPT amount and Common.IsIssue an MPT issue (mpt_issuance_id alone, 48 hex characters), as xrpl.js does, so the AMM validators (AMMCreate, AMMDeposit, AMMWithdraw, AMMDelete, AMMBid, AMMVote, AMMClawback) accept MPT assets. Validation.ValidateClawBack accepts an MPT Amount, which requires Holder other than Account, and rejects Holder with a trust-line Amount, as rippled's Clawback preflight does
  • Validation.ValidateAMMDelete checks Asset and Asset2 as an Issue (currency, issuer) instead of an Amount; Validation.CheckPartialPayment tests the tfPartialPayment bit of PaymentFlags and numeric Flags instead of comparing for equality; Validation.ValidateSignerListSet accepts SignerQuorum 0 without SignerEntries (deleting the list) and rejects SignerEntries with SignerQuorum 0
  • Tests: TestUBatchNormalizerSequences (ticketed outer and inner, caller Sequence above and below the counter and the ledger, uint.MaxValue), TestUMergeBatchSigner (single and multi-signature conflicts, inner signer dedupe), TestUValidatorRules (wrapped Memos and Signers, a Batch with both passing Validation.Validate, numeric and AccountSet flags, partial payment, signer-list deletion, AMMDelete issues, MPT issues in IsIssue, AMMCreate/AMMDeposit/AMMDelete with an MPT asset, MPT clawback); TestUClawback.TestValidWithHolderForMPT uses an MPT Amount
  • Breaking: LOAmm.AuctionSlot is an AMMAuctionSlot and LOAmm.VoteSlots a List<AMMVoteEntry>, the ledger entry's own shape (Account, AuthAccounts, DiscountedFee, Expiration in Ripple-epoch seconds, Price; Account, TradingFee, VoteWeight). They read AuctionSlot / AuthAccounts / VoteSlots as ledger_entry and transaction metadata write them, where the amm_info models (AuctionSlot, VoteEntry, AuthAccount) lost DiscountedFee, every authorized account and every vote. AMMAuctionSlot.AuthAccounts is a List<AuthAccountEntry>, the type AMMBid uses; AMMAuctionSlot has no TimeInterval, which only amm_info reports. Migration: code reading LOAmm.AuctionSlot.AuthAccounts[i].Account, .DiscountedFee, .Expiration, .Price and LOAmm.VoteSlots[i] members compiles unchanged except where it names the old types (AuctionSlot, VoteEntry, AuthAccount become AMMAuctionSlot, AMMVoteEntry, AuthAccountEntry); code reading LOAmm.AuctionSlot.TimeInterval must call amm_info (AMMInfo.AuctionSlot.TimeInterval) instead, since the ledger entry does not store it; AMMInfo is untouched
  • LOAmendments.Majorities and LONegativeUNL.DisabledValidators unwrap the Majority / DisabledValidator inner objects, so their elements are no longer empty. InnerObjectListConverter<T> reads such an STArray as a list of the inner objects, accepts flat elements too, and writes the wrappers back (MajorityListConverter, DisabledValidatorListConverter, AMMVoteEntryListConverter, AuthAccountEntryListConverter)
  • RippleDateTimeConverter converts a DateTimeKind.Local value to UTC before writing it, so DateTime.Now in Expiration, FinishAfter or CancelAfter is no longer off by the UTC offset. DateTimeUtils.ISOTimeToRippleTime(DateTime) does the same, and ISOTimeToRippleTime(string) parses straight to UTC
  • RippleResponseWarning.Details keeps non-string values as their raw JSON text (WarningDetailsConverter): warning 1001 has a numeric expected_date, and the response failed to parse. Serializing a warning writes those values back as the JSON tokens they were read as; a value replaced by the caller, or a dictionary built by hand, is written as strings
  • HashLedger.HashSignedTx throws ValidationException for a transaction without TxnSignature, Signers, SigningPubKey, BatchSigners, CounterpartySignature and SponsorSignature that is not an inner Batch transaction; the guard was constructed and never thrown
  • LedgerIndexConverter reads a numeric string as a ledger index instead of an undefined LedgerIndexType, and rejects a string that is neither
  • Currency.Equals compares MPTokenIssuanceID (ignoring case), so different MPTs are no longer equal; Currency.GetHashCode is overridden consistently with it. CurrencyExtensions.ToIssued keeps the MPT issuance ID
  • OrderBookStream.TxJson holds the API v2 tx_json envelope; OrderBookStream.Transaction reads it, else TransactionJson, and is null when neither is present instead of throwing
  • ShaMap.InnerNode.GetNode returns null for an empty slot, so SHAMap.AddItem and InnerNode.Hash work
  • Hashes.HashTX is [Obsolete]: it duplicates Hashes.HashTx, the single-signing hash xrpl.js hashTx also returns, not the transaction ID; HashTx documents that and points to HashLedger.HashSignedTx. InnerObjectListConverter<T> throws JsonException when writing a null element
  • Tests: TestULedgerInnerObjectArrays (AMM entry from ledger_entry and from a metadata ModifiedNode, Amendments, NegativeUNL, flat elements, round trip), TestUTimeConversionLocalZone and TestURippleDateTimeConverterLocalZone under substituted local zones (LocalTimeZoneScope), TestUResponseWarnings, TestUHashLedger, TestUCurrencyIdentity, TestUOrderBookStreamEnvelope, TestUSHAMap (the xrpl.js vector), TestUHashTx (the xrpl.js offerCreateSell transaction ID) and numeric-string cases in TestULedgerIndexConverter
  • LoanManagement computes a loan broker's LoanManage offline the way rippled 3.4.1's LoanManage transactor does (#313). Exposure is loanVaultExposure: PrincipalOutstanding for a cash-basis vault (LEVersion 1), TotalValueOutstanding less ManagementFeeOutstanding otherwise. Availability returns a LoanManageAvailability with a LoanManageCheck for impair, unimpair and default - the refusal (tecNO_PERMISSION, tecTOO_SOON) in the node's order of checks and AllowedAt, the first parent close time the time condition holds at (ImpairableAt, DefaultableAt), exclusive under fixCleanup3_4_0 and inclusive before it. Impair, Unimpair and Default return a LoanManageQuote: the vault's AssetsTotal, AssetsAvailable and LossUnrealized before and after, DefaultCovered, VaultLoss, the broker's DebtTotal and CoverAvailable on a default, NextPaymentDueDateAfter, the share value before and after when the shares outstanding are given, and the refusal (tecLIMIT_EXCEEDED for an impairment past the assets lent out). A vault with LEVersion above 1 (rippled develop's FixedPrecision) throws NotSupportedException
  • LoanManagement takes a parent close time to whole seconds before the time conditions, as the ledger does: a time inside the second of an exclusive boundary is not past it
  • PreviewSugar.PreviewLoanManage previews a LoanManage; LoanManageOutcome.FromMetadata reads the action, the loan's flags and the vault and broker deltas from its metadata, a Vault amount rising from zero included. LoanManageAction names the action
  • Tests: TestULoanManagement covers both sides of fixCleanup3_4_0 at NextPaymentDueDate and at NextPaymentDueDate + GracePeriod, both accounting bases, XRP, IOU and MPT vaults, an impaired loan defaulted, cover below the liquidation amount, tecLIMIT_EXCEEDED, the dust and tecINTERNAL branches of a default, a FixedPrecision vault, the due date before the fix, the owner check and the outcome read from metadata. TestILoanManagement impairs, unimpairs, impairs again and defaults an XRP and an IOU loan on the stand, comparing each quote with the preview and the submitted metadata