Skip to content

Repository files navigation

EntropyStudio

EntropyStudio is a React Native TurboModule scaffold for selected, typed EntropyLab operations. It uses UniFFI and uniffi-bindgen-react-native to generate the JavaScript, C++, Android, and iOS binding layers.

The Rust wrapper depends on the pinned EntropyLab Git submodule at entropylab/entropylab-wasm. It calls that crate internally while exposing safe, typed UniFFI functions to React Native.

Studio's visible and accessibility copy is audited against the pinned upstream UI sources, including app.js, i18n-labels.js, and content-keyed localization data. Static aliases, direct label tables, and dynamic upstream templates are centralized in example/src/features/upstreamUiCopy.ts.

Layout

src/lib.rs          UniFFI-facing Rust API
js/                 Generated TypeScript entrypoint and bindings
cpp/                Generated JSI bindings
android/            Generated Android TurboModule integration
ios/                Generated iOS TurboModule integration
ubrn.config.yaml    UBRN build and generation configuration

The current public API intentionally stays small:

  • sha256(input: ArrayBuffer): ArrayBuffer
  • mnemonicToEntropy(normalizedPhrase: string): ArrayBuffer
  • entropyToMnemonic(entropy: ArrayBuffer): string
  • diceRollsToEntropy(rolls: string, method: DiceRollMethod, targetWords: number): ArrayBuffer
  • directDiceState(rolls: string, method: DirectDiceMethod, targetWords: number): DirectDiceState
  • cardTranscriptToEntropy(transcript: string, method: CardHashMethod, targetWords: number): ArrayBuffer
  • directCardState(transcript: string, targetWords: number): DirectCardState
  • privateKeyEntropy(value: string, format: PrivateKeyFormat, trimBrainWalletBoundaryWhitespace: boolean): ArrayBuffer

mnemonicToEntropy expects an NFKD-normalized English BIP39 phrase. In TypeScript, call phrase.normalize("NFKD") before passing text to it.

Dice rolls

diceRollsToEntropy ports EntropyLab's two SHA-256 dice transcript methods. It accepts faces 1 through 6 with whitespace, commas, semicolons, or pipes as separators, and supports 12, 15, 18, 21, and 24 BIP39 words.

  • DiceRollMethod.Coldcard hashes the original dice digits, matching the COLDCARD and SeedSigner convention.
  • DiceRollMethod.Coleman maps every 6 to 0 before hashing, matching the Keystone-compatible dice convention used by EntropyLab.

Both methods hash every accepted roll, then return the leading 128-256 bits of the SHA-256 digest for the chosen BIP39 length. EntropyLab recommends 50, 62, 75, 87, or 99 fair six-sided rolls respectively. entropyToMnemonic converts that returned buffer into the checksum-valid English BIP39 phrase.

directDiceState supports EntropyLab's direct BitBox diceware and D++ word-selection workflows. It returns the completed words, eligible final checksum words, validation counts, and the next input step for a native UI.

Cards

cardTranscriptToEntropy ports EntropyLab's hashed card methods. It accepts rank-and-suit tokens such as AS, 10H, and TD, including Unicode suit symbols, and rejects invalid or repeated cards within a shuffle. It supports the same 12, 15, 18, 21, and 24 BIP39 word counts as dice.

  • CardHashMethod.Ascii hashes the canonical ASCII transcript, such as As 2c Td.
  • CardHashMethod.Coleman hashes the equivalent Ian Coleman suit-symbol transcript, such as A♠ 2♣ T♦.

directCardState implements rank-only direct word selection. It accepts draws from the currently required rank set, produces each completed BIP39 word, and calculates the checksum-valid final-word candidates from the final rank draw sequence.

Private keys

privateKeyEntropy validates a supported private-key input and returns its 32-byte private-key material. It does not recover or create a BIP39 seed phrase. The wrapper uses EntropyLab's Base58Check decoder, secp256k1 secret-key validator, and SHA-256 implementation; Studio only owns the input-format framing and dispatch.

  • PrivateKeyFormat.Wif accepts checksum-valid Bitcoin mainnet WIF, including the required 0x01 compression marker when present.
  • PrivateKeyFormat.Hex accepts a valid 32-byte secp256k1 scalar as 64 hexadecimal characters, with optional whitespace and a 0x prefix.
  • PrivateKeyFormat.MiniKey accepts 22- or 30-character Casascius mini keys, including the published SHA-256 checksum rule.
  • PrivateKeyFormat.BrainWallet hashes exact, nonempty UTF-8 text with SHA-256 by default. Set trimBrainWalletBoundaryWhitespace to true to remove leading and trailing whitespace before hashing; an all-whitespace phrase is then rejected. Brain-wallet phrases are generally unsafe for funds.

Setup

Prerequisites:

  • Node.js 20 or newer
  • Rust and Cargo
  • Git with submodule support
  • Android NDK and cargo-ndk for Android builds
  • macOS, Xcode, and the Rust iOS targets for iOS builds

Initialize the pinned EntropyLab source and install the JavaScript tooling:

git submodule update --init --recursive
npm install

Run the Rust contract tests:

npm test

Update the EntropyLab submodule and automatically validate Studio's selected upstream UI copy whenever its checked-out revision changes:

npm run update:entropylab

The automatic check statically validates Studio's aliases in example/src/features/upstreamUiCopy.ts against pinned upstream source files and runs Studio's copy-provenance test. It does not execute or rewrite upstream synchronization code, and it never imports non-English locale values into React Native. Callers only import aliases from that module, so this is the sole Studio file validated against upstream text. Run npm run check:upstream-ui-copy directly after manually changing the EntropyLab gitlink.

Build native artifacts and regenerate bindings for a mobile platform:

npm run build:android
npm run build:ios

The generated native bridge uses static linking (staticlib) for Android and iOS, following the UBRN configuration used by Nostr SDK's React Native package. The existing EntropyLab web build remains a separate cdylib/WASM consumer of the same underlying crate.

Local Android debug build

The debug variant loads JavaScript from Metro rather than packaging a bundle. With the development machine and device connected to Tailscale, configure the debug APK to use the development machine's Tailscale address when installing it:

METRO_HOST="$(tailscale ip -4):8081" npm run install:android

Then start Metro whenever you want to run or reload the debug app:

cd example
npm start

The device fetches the bundle directly from Metro over Tailscale; it does not need an ADB connection or adb reverse to load JavaScript. Rebuild with a new METRO_HOST only when the development machine's reachable address changes.

APK installation still uses ADB. For a device already paired through Android's Wireless debugging settings, connect it with the reachable device address and the wireless-debugging port shown on the device:

adb connect <device-ip>:<wireless-debugging-port>

Confirm that ADB can see the device before installing:

adb devices -l

Keep METRO_HOST in any Gradle command that might build or repackage the debug APK, including a reinstall after deleting the local APK:

cd example/android
METRO_HOST="$(tailscale ip -4):8081" ./gradlew :app:installDebug

If app-debug.apk already exists and was built with the correct host, ADB can install that exact file without invoking Gradle:

adb install -r example/android/app/build/outputs/apk/debug/app-debug.apk

installDebug does not rebuild the Rust library or start Metro.

To build an APK without installing it, use:

./gradlew :app:assembleDebug

The APK is written to example/android/app/build/outputs/apk/debug/app-debug.apk.

Android APK workflow

Run Build Android APK from the repository's Actions tab and provide a SemVer version such as 0.1.0. The workflow builds the Android UniFFI library, assembles the example app's release variant, uploads the APK as a workflow artifact, and creates a GitHub prerelease with the APK attached. The supplied version becomes Android's versionName; the GitHub run number plus retry attempt supplies a monotonically increasing versionCode.

The prerelease is tagged v<version> at the workflow's commit and is available for download from the GitHub Release page. It can be promoted to a formal release from that page when it is ready.

The workflow commits a required Cargo.lock refresh to the branch that dispatched it before building and tagging the prerelease. Run it from a branch whose GitHub Actions token may write contents; a required refresh from a tag or a protected branch that rejects the push stops the build rather than publishing an APK from an uncommitted dependency graph.

The current Android project signs its release variant with the debug keystore. The uploaded artifact is suitable for development installation, not Play Store distribution.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages