Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 67 additions & 1 deletion libs/encryption/base58/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,79 @@ plugins {
id("com.android.kotlin.multiplatform.library")
}

/**
* Compiles the cross-platform fixtures into `commonTest` as Kotlin constants.
*
* The parity gate is only worth something if it runs on *both* platforms, and Kotlin/Native test
* binaries ship no resource bundle -- `NSBundle.pathForResource` finds nothing there, so a
* resource-based loader quietly only ever runs on the JVM. Generating a source file instead makes
* the same fixtures readable from every target with no platform code at all.
*/
abstract class GenerateTestFixtures : DefaultTask() {

@get:InputDirectory
abstract val fixtures: DirectoryProperty

@get:OutputDirectory
abstract val outputDirectory: DirectoryProperty

@TaskAction
fun generate() {
val files = fixtures.get().asFile.listFiles().orEmpty().sortedBy { it.name }
val destination = outputDirectory.get().asFile
.resolve("com/getcode/vendor/TestFixtures.kt")
destination.parentFile.mkdirs()

destination.writeText(
buildString {
appendLine("package com.getcode.vendor")
appendLine()
appendLine("// Generated from src/commonTest/resources -- do not edit.")
appendLine()
appendLine("private val FIXTURES: Map<String, String> = mapOf(")
files.forEach { file ->
append(" \"").append(file.name).append("\" to \"")
append(file.readText().escapeForKotlin())
appendLine("\",")
}
appendLine(")")
appendLine()
appendLine("/** Reads a fixture compiled in from `src/commonTest/resources/`. */")
appendLine("fun readTestResource(name: String): String =")
append(" requireNotNull(FIXTURES[name]) { \"unknown fixture '")
appendLine("\$name'\" }")
}
)
}

private fun String.escapeForKotlin(): String = buildString(length) {
this@escapeForKotlin.forEach { character ->
when (character) {
'\\' -> append("\\\\")
'"' -> append("\\\"")
'$' -> append("\\$")
'\n' -> append("\\n")
'\r' -> append("\\r")
'\t' -> append("\\t")
else -> append(character)
}
}
}
}

val generateTestFixtures = tasks.register<GenerateTestFixtures>("generateTestFixtures") {
fixtures.set(layout.projectDirectory.dir("src/commonTest/resources"))
outputDirectory.set(layout.buildDirectory.dir("generated/testFixtures"))
}

kotlin {
android {
namespace = "com.getcode.encryption.base58"
compileSdk = 37
minSdk = 29
withHostTest {}
}

iosArm64()
iosSimulatorArm64()
iosX64()
Expand All @@ -23,6 +88,7 @@ kotlin {
// MessageDigest + BigInteger -- JDK only; no extra Gradle deps.
}
commonTest {
kotlin.srcDir(generateTestFixtures)
dependencies {
implementation(kotlin("test"))
implementation(libs.kotlinx.serialization.json)
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

11 changes: 9 additions & 2 deletions test-vectors/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl must reproduce the fixtures before the native duplicates are deleted.
| Fixture | Android | iOS |
|---|---|---|
| `ed25519.json` | `:libs:encryption:ed25519` androidTest → `connectedAndroidTest` (device/emulator, JNI) — **green** | `CrossPlatformVectors` → `swift test` (host) — **green** |
| `base58.json` | `:libs:encryption:base58` → `test` (host JVM) — **green** | `FlipcashCoreVectors` → xcodebuild on iOS Simulator — **green** |
| `base58.json` | `:libs:encryption:base58` → `testAndroidHostTest` (host JVM) **and** `iosSimulatorArm64Test` (Kotlin/Native) — **green** | `FlipcashCoreVectors` → xcodebuild on iOS Simulator — **green** |
| `slip10.json` | `:libs:encryption:mnemonic` androidTest → `connectedAndroidTest` (device, wordlist + JNI) — **green** | `FlipcashCoreVectors` → xcodebuild on iOS Simulator — **green** |
| `curve.json` | `:libs:currency-math` androidTest → `connectedAndroidTest` (device, loads .bin tables) — **green** | `FlipcashCoreVectors` → xcodebuild on iOS Simulator — **green** |
| `solana_message.json` | `:services:opencode` → `testDebugUnitTest` (host JVM) — **green** | `FlipcashCoreVectors` → xcodebuild on iOS Simulator — **green** |
Expand Down Expand Up @@ -61,6 +61,13 @@ Bitcoin/Solana Base58 (`gen_base58.py`). Each: `bytes` (hex) → `base58`. Both
`encode(bytes)==base58` and `decode(base58)==bytes`. Anchored to the Solana all-ones address
(32 zero bytes → `111…1`) and cross-linked to the ed25519 public keys (real 32-byte Solana addresses).

Base58 is a KMP module, so its vector test is the one fixture asserted by **two toolchains inside this
repo**: the JVM host test and a Kotlin/Native `iosSimulatorArm64` run. Kotlin/Native test binaries ship
no resource bundle (`NSBundle.pathForResource` finds nothing), so the fixture is compiled into
`commonTest` as Kotlin constants by the `generateTestFixtures` task in the module's `build.gradle.kts` —
that is why there is no resource loader here. Keep dropping the canonical JSON into
`src/commonTest/resources/`; the task picks it up. (The same pattern is used by `:libs:codes:kikcode`.)

## slip10 (`slip10.json`) — the C1 gate

BIP39 + SLIP-0010 (ed25519) key derivation (`gen_slip10.py`): mnemonic + passphrase + path →
Expand Down Expand Up @@ -150,7 +157,7 @@ python3 gen_compact_message.py > compact_message.json # intent-signing compact

# Sync to Android per-module copies (from the repo root):
cp test-vectors/ed25519.json libs/encryption/ed25519/src/androidTest/assets/
cp test-vectors/base58.json libs/encryption/base58/src/test/resources/
cp test-vectors/base58.json libs/encryption/base58/src/commonTest/resources/
cp test-vectors/slip10.json libs/encryption/mnemonic/src/androidTest/assets/
cp test-vectors/curve.json libs/currency-math/src/androidTest/assets/
cp test-vectors/curve_fractional.json libs/currency-math/src/androidTest/assets/
Expand Down
Loading