Skip to content

v11.5.1

Choose a tag to compare

@github-actions github-actions released this 15 Sep 20:02
bb6aea9
  • Transaction validation is synchronous, and BatchUtils.Build validates what it assembles (breaking). Validation.Validate and all 86 per-transaction validators behind it were declared async Task without ever awaiting anything - every one of them is straight-line field checking. Nothing in the SDK is measurably faster for it, but one caller paid for the disguise: BatchUtils.Build called Validation.Validate(...) and discarded the task. An async method captures every exception into the task it returns, including the ones thrown before the first await, so a discarded task is a discarded verdict: ValidateBatch ran, decided the batch was malformed, and reported it to nobody. A Batch built around a single inner transaction - which rippled answers with temARRAY_EMPTY - came back from Build looking well formed, and so did one with more than eight inners, with a Vault/Loan inner, with an inner missing tfInnerBatchTxn, or with an inner carrying a non-zero Fee. The compiler had been saying so since the method was written (CS4014).
    • the validators now return void and throw on the calling thread. await Validation.Validate(tx) no longer compiles: drop the await. This is the whole migration - the exception type, the message and the conditions are unchanged, and a try/catch around the call keeps working as it is.
    • this is a breaking change released as a patch, deliberately. Semver would call it a major: the return type is part of a method's signature in IL, so an assembly built against 11.5.0.0 meets a MissingMethodException on 11.5.1.0 even where it never wrote await, and its sources need the await dropped before they compile again. It is numbered a patch because the validators are opt-in - nothing inside the SDK calls them, and BatchUtils.Build, the one caller that did, is the method this release fixes. If you call Validation.* or Common.ValidateBaseTransaction directly, treat this upgrade as a major one: rebuild, and drop the await.
    • making the signature honest is what fixes the defect, rather than adding the missing await: Build is synchronous and public, so awaiting would have meant Task<Batch> BuildAsync, and .GetAwaiter().GetResult() would have left the next caller the same trap. A validator that cannot be forgotten is a validator that has no task to forget.
    • TestUCredentialsValidator wrapped the already-synchronous CredentialsValidator.ValidateCredentialsList in Task.Run purely to fit the async assertion helper's Func<Task>. The wrapper worked, because the helper awaited the task - but it is exactly the shape that stops working the moment the assertion becomes synchronous, since Action accepts a lambda whose value is discarded. The nine tests call the validator directly now.