e22a8ae hoisted completion above the approval gate on the strength of one
claim: closing a session needs nothing secret, and nothing the member would
have had to publish. Were that false -- were the aggregated nonce, the
signer set or a share needed to check the result -- the gate would have to
stay where it was, and the member a quorum did not need would go on being
asked to sign something already signed. Nothing checked the claim.
FrostSigningCompletionTest builds a real 2-of-3 signature from members 0
and 1, then works entirely from member 2's row: never approved, not in the
signer set, aggregatedNonce and signerIds deliberately null. From that
alone it pins that they can verify what the group signed, that the finished
event is the proposed one unaltered rather than rebuilt or rehashed, that
there is no finished event before the signature arrives, that the arrived
signature is what stops the session asking, and that a signature over a
different event is refused -- which is what the check in complete() is for.
Both new assertions about isAwaitingApproval were mutation-checked: with
the `signature != null` guard removed, exactly two tests fail and the rest
of the suite still passes, so they guard the change rather than restating
it.
Not covered, and not coverable here: advance() itself -- that the branch
fires on an inbound SIGNATURE rather than stopping at the gate. It is
Room-backed, and this project has no harness for that (no Robolectric, and
the in-memory builder's android actual needs a Context). The pure half of
the claim is what this pins instead.
Also corrects e22a8ae's message, which said sixteen new tests. It was
eleven: eight in TranscriptRequestStateTest and three added to
FrostSigningSessionTest, which has eight in total.
Verified: :composeApp:compileDebugKotlinAndroid succeeds, and
:composeApp:testDebugUnitTest passes -- 176 tests across 24 classes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This is a Kotlin Multiplatform project targeting Android, iOS, Desktop (JVM).
-
/composeApp is for code that will be shared across your Compose Multiplatform applications. It contains several subfolders:
- commonMain is for code that’s common for all targets.
- Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name. For example, if you want to use Apple’s CoreCrypto for the iOS part of your Kotlin app, the iosMain folder would be the right place for such calls. Similarly, if you want to edit the Desktop (JVM) specific part, the jvmMain folder is the appropriate location.
-
/iosApp contains iOS applications. Even if you’re sharing your UI with Compose Multiplatform, you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project.
Build and Run Android Application
To build and run the development version of the Android app, use the run configuration from the run widget in your IDE’s toolbar or build it directly from the terminal:
- on macOS/Linux
./gradlew :composeApp:assembleDebug - on Windows
.\gradlew.bat :composeApp:assembleDebug
Build and Run Desktop (JVM) Application
To build and run the development version of the desktop app, use the run configuration from the run widget in your IDE’s toolbar or run it directly from the terminal:
- on macOS/Linux
./gradlew :composeApp:run - on Windows
.\gradlew.bat :composeApp:run
Build and Run iOS Application
To build and run the development version of the iOS app, use the run configuration from the run widget in your IDE’s toolbar or open the /iosApp directory in Xcode and run it from there.
Learn more about Kotlin Multiplatform…