diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..bf3cad0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# Reproducible builds need identical bytes on every platform: never convert +# line endings on checkout or commit. +* -text diff --git a/.github/workflows/native.yml b/.github/workflows/native.yml new file mode 100644 index 0000000..574fbc7 --- /dev/null +++ b/.github/workflows/native.yml @@ -0,0 +1,26 @@ +# Builds the Rust library on every reference host, fails if any host or the +# committed dnix.lock disagrees, and publishes the release the lock names +# when it reaches the default branch. +name: Native library + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: write + +# A newer push supersedes runs on a branch, never on main: a run there may +# be publishing. +concurrency: + group: native-${{ github.ref }} + cancel-in-progress: ${{ github.ref != format('refs/heads/{0}', github.event.repository.default_branch) }} + +jobs: + native: + # The same dnix commit as pubspec.yaml and flake.lock. + uses: cypherstack/dnix/.github/workflows/native.yml@e91763ef25d084b3a5cf271a0cfee69159b1f779 + with: + # Xcode 27 lives on its own public-preview image, not on macos-26. + macos-runner: xcode-27 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8fbf4b8..b55a27d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,24 +2,66 @@ name: Test native assets on: push: + branches: [main] pull_request: workflow_dispatch: +permissions: + contents: read + +concurrency: + group: test-${{ github.ref }} + cancel-in-progress: true + jobs: test: strategy: + fail-fast: false matrix: - os: [ubuntu-24.04, ubuntu-24.04-arm, macos-latest, windows-latest] + os: [ubuntu-24.04, ubuntu-24.04-arm, xcode-27, windows-latest] runs-on: ${{ matrix.os }} + timeout-minutes: 90 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 + with: + persist-credentials: false - uses: dart-lang/setup-dart@v1 with: sdk: '3.13.0' - uses: dtolnay/rust-toolchain@1.99.1 + if: matrix.os == 'ubuntu-24.04' + # The hook builds through the Nix flake; before the first native + # release that is the only path, and it is unavailable on Windows. + - uses: DeterminateSystems/nix-installer-action@v23 + if: runner.os != 'Windows' - run: dart pub get --no-example - - run: dart analyze lib test hook - - run: dart test + - run: dart analyze lib test hook tool + # Prebuilt libraries exist once the lock's release is published from the + # default branch of the repository dnix.json names; until then only + # hosts with Nix can build them. + - name: Check whether the lock's release is published + id: release + shell: bash + run: | + repository=$(jq -r .repository dnix.json | sed 's/\.git$//; s#/$##') + tag="native-$(jq -r .release dnix.lock)" + url="$repository/releases/tag/$tag" + echo "published=$([ "$(curl -s -o /dev/null -w '%{http_code}' "$url")" = 200 ] && echo true || echo false)" >> "$GITHUB_OUTPUT" + # Prebuilt once published; until then Linux and macOS verify the source + # build against the lock, and Windows, without Nix, skips the tests. + - name: Choose the build mode + shell: bash + run: echo '${{ steps.release.outputs.published == 'true' && 'prebuilt' || 'source' }}' > .dnix-build + # On failure the last lines become an annotation, readable without + # access to the logs. + - name: Run dart test + if: runner.os != 'Windows' || steps.release.outputs.published == 'true' + shell: bash + run: | + if ! dart test 2>&1 | tee test.log; then + echo "::error::$(tail -c 1500 test.log | tr '\n' ' ')" + exit 1 + fi - name: Rust tests if: matrix.os == 'ubuntu-24.04' working-directory: src/serai/hrf diff --git a/.gitignore b/.gitignore index 37e1713..a6b6d3d 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,9 @@ build/ .flutter-plugins .flutter-plugins-dependencies + +# Nix build outputs +/result +/result-* +# Per-checkout dnix build mode override (see README). +.dnix-build diff --git a/CHANGELOG.md b/CHANGELOG.md index bca0eea..3f4d0e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ ## Unreleased +* The build hook, the Nix build, and the workflows now come from the shared `dnix` package; the package keeps `dnix.json`, `dnix.lock`, a two-line hook, and a two-line flake. +* The hook obtains the native library in one of three modes: `prebuilt` downloads the library of the pinned native release and verifies its hash, `source` builds it with the Nix flake and verifies the same hash, and `dev` builds with Nix without the Xcode pin or hash check. Static linking is no longer offered. +* `dnix.lock` is a source file: `update_lock` records what the Rust source builds to and names the release, derived from the hashes, that holds those bytes. CI fails when source and lock disagree and publishes the release when the lock reaches the default branch; nothing is committed by CI. +* Reproducible builds: source paths are remapped onto canonical prefixes, Apple dylibs get an `@rpath` install name with header padding and are linked without a debug map instead of stripped afterwards, PE timestamps derive from content, C objects carry no debug info or compiler identification, and deployment targets are fixed at the floors in `dnix.json`. +* Nix flake with sandboxed reference builds of the Linux, Android, and Windows targets from hash-pinned toolchains, plus an Apple build under a pinned Xcode. +* Cross-host identity: cargo gets a fixed host triple through a rustc wrapper so crate metadata hashes do not depend on the build host, and the Windows target compiles its C code with zig's clang like the Linux targets. +* CI builds the reference targets on four hosts, rebuilds each from scratch, and fails on any cross-host difference. * Pin Rust 1.99.1. Earlier toolchains strip Apple dynamic libraries with a misaligned `__LINKEDIT` string pool, which iOS 27 and macOS 27 refuse to load. ## 0.3.0 diff --git a/README.md b/README.md index ac19067..36e5385 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ # frostdart Dart bindings for the Serai FROST multisig implementation. Rust sources live in -`src/serai`; native assets build and bundle them automatically. +`src/serai`; the package ships the compiled library through native assets. -Requires Dart 3.13+ (or a Flutter SDK containing it), Rust installed with rustup, -and the platform's C build tools: Xcode for Apple, the Android NDK, a Linux C -compiler, or Visual Studio's C++ build tools on Windows. Put `rustup` on PATH. -The hook locates the pinned Rust 1.99.1 tools and installs the target as needed. +Requires Dart 3.13+ (or a Flutter SDK containing it). Nothing else is needed +to use the package: the build hook downloads the library built for this +version and checks its hash. ```sh dart pub get --no-example @@ -17,3 +16,76 @@ Consuming Flutter apps use normal `flutter run` / `flutter build` commands. The example requires iOS 15+ or macOS 13+ for Apple builds. Regenerate bindings with `dart run ffigen --config ffigen.yaml` (requires libclang). + +## Native library + +The hook obtains the Rust library in one of three modes, chosen with the +`build` user define in the consuming app's `pubspec.yaml`, or for this +checkout with a `.dnix-build` file holding the mode: + +```yaml +hooks: + user_defines: + frostdart: + build: prebuilt # the default; or source, or dev +``` + +- `prebuilt` downloads the library of the release `dnix.lock` names and + verifies it against the hash recorded there. A mismatch is an error, never + a fallback. Downloads are cached by hash. +- `source` builds the library with Nix, the same build CI uses, and verifies + it against the same hash. Needs Nix on Linux or an Apple Silicon Mac; the + Apple targets need Xcode 27.0. +- `dev` builds with Nix but skips the Xcode pin and the hash check, for + working on the Rust code. The hook notices a `.dnix-build` file from its + next run on, so touch `dnix.json` after creating it. + +Only dynamic libraries are provided, built for macOS 12, iOS 13 (the arm64 +simulator 14), Android API 24, and glibc 2.28 at minimum. iOS stays at 13 +because Flutter 3.47 asks build hooks for iOS 13 and writes 13.0 into each +native asset's framework as its minimum. The floors, the Rust and Xcode +versions, and the location of the Rust workspace live in `dnix.json`. + +### Releases + +`dnix.lock` records the hash of every library the Rust source builds to and +names the GitHub release, `native-`, that holds those bytes. After +changing the Rust code, run `dart tool/update_lock.dart` on a Mac with Nix +and Xcode 27.0 and commit the lock with the change; without a Mac, push and +copy the lock CI prints once every host is green. CI +(`.github/workflows/native.yml`) builds on four hosts, fails if any host or +the lock disagrees, and publishes the release when the lock reaches the +default branch. Nothing is ever committed by CI, and old releases stay. + +Anyone can rebuild one platform with Nix and compare: +`dart tool/verify_release.dart linux` (or android, windows, macos, ios, or a +target triple), on Windows inside WSL2 for the non-Apple platforms. The Apple +platforms use the Xcode 27.0 bundle under `/Applications`, whatever +`xcode-select` points at. + +### How the build stays reproducible + +The build comes from the shared [dnix](https://github.com/cypherstack/dnix) +package: `flake.nix` hands it `dnix.json`, and dnix builds every non-Apple +target in a Nix sandbox from toolchains pinned by hash, with the same bytes +on Linux, macOS, and WSL2, and the Apple targets with the pinned Xcode. See +dnix's documentation for how the output is kept independent of the host. + +```sh +nix build .#x86_64-unknown-linux-gnu # one target, output in result/lib +nix build .#all # every target this host can build +cat result/SHA256SUMS +nix develop .#apple --command dnix-apple-build out # Apple targets, pinned Xcode +``` + +Bumping Rust means changing `toolchains.rust` in `dnix.json`, plus +`src/serai/hrf/rust-toolchain.toml` and the toolchain step in +`.github/workflows/release.yml`, which serve `cargo test`. Bumping dnix means +the same commit in `pubspec.yaml`, `flake.lock`, and +`.github/workflows/native.yml`. Either is followed by `update_lock`. + +### Tests + +`.github/workflows/release.yml` runs the Dart tests on every OS: against the +prebuilt library once the lock's release is published, from source before +that (Windows waits for the release), and the Rust tests on Linux. diff --git a/dnix.json b/dnix.json new file mode 100644 index 0000000..f1da851 --- /dev/null +++ b/dnix.json @@ -0,0 +1,33 @@ +{ + "name": "frostdart", + "repository": "https://github.com/cypherstack/frostdart", + "library": "hrf_api", + "asset": "frostdart_bindings_generated.dart", + "rust": { + "workspace": "src/serai", + "crate": "hrf-api" + }, + "toolchains": { + "rust": "1.99.1", + "xcode": "27.0" + }, + "floors": { + "macos": 12, + "ios": 13, + "android": 24, + "glibc": "2.28" + }, + "targets": [ + "aarch64-apple-darwin", + "x86_64-apple-darwin", + "aarch64-apple-ios", + "aarch64-apple-ios-sim", + "x86_64-apple-ios", + "aarch64-linux-android", + "armv7-linux-androideabi", + "x86_64-linux-android", + "aarch64-unknown-linux-gnu", + "x86_64-unknown-linux-gnu", + "x86_64-pc-windows-msvc" + ] +} diff --git a/dnix.lock b/dnix.lock new file mode 100644 index 0000000..076a0ff --- /dev/null +++ b/dnix.lock @@ -0,0 +1,17 @@ +{ + "release": "780746010c71", + "date": "2026-10-23", + "files": { + "aarch64-apple-darwin/libhrf_api.dylib": "62b494f6ddccd034e1c8d16f28ded45b1737e367df5869e2fb0dbec0ee59fdef", + "aarch64-apple-ios-sim/libhrf_api.dylib": "7fcddfeaab9cd9dfc7f75d734a78144adc2e7ff6cb09f19a06a65938447f86de", + "aarch64-apple-ios/libhrf_api.dylib": "f80ea3d4bfbab97683a0ca45928bf99cb2741fa681d61048193ad278ca0255ac", + "aarch64-linux-android/libhrf_api.so": "17c4aaa8659efc92ee37abd8d040e73b4cca34fc404776ac266bfe6be2150525", + "aarch64-unknown-linux-gnu/libhrf_api.so": "f2c083c7bf06b3c03fc268136bd0e1a9b3f6d683041106eac38af322d897fa38", + "armv7-linux-androideabi/libhrf_api.so": "6a0f91a0d6f1964744011cf8e68258f9123b7cce335fc5edbe1aa2546ce02a84", + "x86_64-apple-darwin/libhrf_api.dylib": "14adca4a9ba9711c868c229dc8386d1fb38814746374df6e23d9e971f4966dce", + "x86_64-apple-ios/libhrf_api.dylib": "a9304f7c22b244135029ba1c02e453c3d092db95df23a83e4343266f0ca9123f", + "x86_64-linux-android/libhrf_api.so": "d3cd02cd9a0b7de631cca5ec9f68d1a59e6ddca7c545c7e10b186d1bc08a2058", + "x86_64-pc-windows-msvc/hrf_api.dll": "461731d3c79d1e99fe9472ef20165e793476b6de1092f07673735bdac14a5278", + "x86_64-unknown-linux-gnu/libhrf_api.so": "73e8812fa3b39cd034b83e3c4e0b6f5a2d621f97dd962d6777bf731c35ad9e23" + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock index 0ddc45c..3a1bd9a 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -65,6 +65,15 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + dnix: + dependency: transitive + description: + path: "." + ref: e91763ef25d084b3a5cf271a0cfee69159b1f779 + resolved-ref: e91763ef25d084b3a5cf271a0cfee69159b1f779 + url: "https://github.com/cypherstack/dnix.git" + source: git + version: "0.1.0" fake_async: dependency: transitive description: diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..928b931 --- /dev/null +++ b/flake.lock @@ -0,0 +1,123 @@ +{ + "nodes": { + "dnix": { + "inputs": { + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay", + "zig-overlay": "zig-overlay" + }, + "locked": { + "lastModified": 1793124403, + "narHash": "sha256-Nf8+4VKQWYAE7telMnxGa2no18Yu4BemMZJ2PCeG+II=", + "owner": "cypherstack", + "repo": "dnix", + "rev": "e91763ef25d084b3a5cf271a0cfee69159b1f779", + "type": "github" + }, + "original": { + "owner": "cypherstack", + "repo": "dnix", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1792502884, + "narHash": "sha256-IoNisKcGbUjdd45qCqywGMCYmZ5EnkxezvLII0FWwNA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "02c087f3fe673c9e317f206a627cb6b910336dbe", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "dnix": "dnix" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "dnix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1792567368, + "narHash": "sha256-mrM+zFDRO1pI85t2SEq5R739BIq7Nv5XkFtVZK9nkZM=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "83dd732f2428827f35d997deae108a2ced5125f4", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "flake": false, + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "zig-overlay": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "dnix", + "nixpkgs" + ], + "systems": "systems" + }, + "locked": { + "lastModified": 1792585651, + "narHash": "sha256-Kg8Qdi6ur+K5hNJwav0pEqdylGd7vXxEPeBvaKW5nxA=", + "owner": "mitchellh", + "repo": "zig-overlay", + "rev": "98819174f1bb7c9961c3ebbb00d19fc4e1c1960d", + "type": "github" + }, + "original": { + "owner": "mitchellh", + "repo": "zig-overlay", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..38773d3 --- /dev/null +++ b/flake.nix @@ -0,0 +1,10 @@ +{ + description = "Reproducible reference builds of frostdart's Rust library (hrf-api)"; + + inputs.dnix.url = "github:cypherstack/dnix"; + + outputs = { self, dnix, ... }: dnix.lib.package { + src = self; + config = ./dnix.json; + }; +} diff --git a/hook/build.dart b/hook/build.dart index c6afc28..7997f79 100644 --- a/hook/build.dart +++ b/hook/build.dart @@ -1,167 +1,3 @@ -import 'dart:io'; +import 'package:dnix/dnix.dart'; -import 'package:code_assets/code_assets.dart'; -import 'package:hooks/hooks.dart'; - -Future main(List args) async { - await build(args, (input, output) async { - if (!input.config.buildCodeAssets) return; - - final config = input.config.code; - final target = switch ((config.targetOS, config.targetArchitecture)) { - (OS.android, Architecture.arm) => 'armv7-linux-androideabi', - (OS.android, Architecture.arm64) => 'aarch64-linux-android', - (OS.android, Architecture.x64) => 'x86_64-linux-android', - (OS.iOS, Architecture.arm64) - when config.iOS.targetSdk == IOSSdk.iPhoneSimulator => - 'aarch64-apple-ios-sim', - (OS.iOS, Architecture.arm64) => 'aarch64-apple-ios', - (OS.iOS, Architecture.x64) - when config.iOS.targetSdk == IOSSdk.iPhoneSimulator => - 'x86_64-apple-ios', - (OS.macOS, Architecture.arm64) => 'aarch64-apple-darwin', - (OS.macOS, Architecture.x64) => 'x86_64-apple-darwin', - (OS.linux, Architecture.arm64) => 'aarch64-unknown-linux-gnu', - (OS.linux, Architecture.x64) => 'x86_64-unknown-linux-gnu', - (OS.windows, Architecture.x64) => 'x86_64-pc-windows-msvc', - _ => throw UnsupportedError( - 'Unsupported target: ${config.targetOS}/${config.targetArchitecture}', - ), - }; - final workspace = input.packageRoot.resolve('src/serai/'); - final crate = workspace.resolve('hrf/'); - final targetDirectory = input.outputDirectory.resolve('target/'); - final environment = { - if (Platform.isMacOS) - 'PATH': Platform.environment['PATH']! - .split(':') - .where((entry) => !entry.contains('Contents/Developer/')) - .join(':'), - if (config.targetOS == OS.macOS) - 'MACOSX_DEPLOYMENT_TARGET': '${config.macOS.targetVersion}.0', - if (config.targetOS == OS.iOS) - 'IPHONEOS_DEPLOYMENT_TARGET': '${config.iOS.targetVersion}.0', - }; - final targetVariable = target.replaceAll('-', '_'); - if (config.targetOS == OS.android) { - final compiler = config.cCompiler; - if (compiler == null) { - throw StateError('The Android build must provide an NDK compiler.'); - } - final ndkTarget = target.replaceFirst('armv7-', 'armv7a-'); - final api = config.android.targetNdkApi; - final suffix = Platform.isWindows ? '.cmd' : ''; - final cc = - compiler.compiler.resolve('$ndkTarget$api-clang$suffix').toFilePath(); - environment.addAll({ - 'CC_$targetVariable': cc, - 'CXX_$targetVariable': compiler.compiler - .resolve('$ndkTarget$api-clang++$suffix') - .toFilePath(), - 'AR_$targetVariable': compiler.archiver.toFilePath(), - 'CARGO_TARGET_${targetVariable.toUpperCase()}_LINKER': cc, - }); - } else if (config.targetOS == OS.linux && config.cCompiler != null) { - environment.addAll({ - 'CC_$targetVariable': config.cCompiler!.compiler.toFilePath(), - 'AR_$targetVariable': config.cCompiler!.archiver.toFilePath(), - 'CARGO_TARGET_${targetVariable.toUpperCase()}_LINKER': - config.cCompiler!.compiler.toFilePath(), - }); - } - - // rustup reads the pinned toolchain from the crate directory. - await run('rustup', ['target', 'add', target], crate, environment); - final cargoResult = await Process.run( - 'rustup', - ['which', 'cargo'], - workingDirectory: crate.toFilePath(), - environment: environment, - ); - if (cargoResult.exitCode != 0) { - throw ProcessException( - 'rustup', - ['which', 'cargo'], - cargoResult.stderr as String, - cargoResult.exitCode, - ); - } - final cargo = (cargoResult.stdout as String).trim(); - // Cargo also needs the matching rustc and rustdoc on its PATH. - final pathSeparator = Platform.isWindows ? ';' : ':'; - final path = environment['PATH'] ?? Platform.environment['PATH'] ?? ''; - environment['PATH'] = '${File(cargo).parent.path}$pathSeparator$path'; - await run( - cargo, - [ - 'build', - '--locked', - '--release', - '--lib', - '--package', - 'hrf-api', - '--target', - target, - '--target-dir', - targetDirectory.toFilePath(), - ], - crate, - environment, - ); - - // Track the whole small workspace, including manifests, headers, and newly - // added files. Build products are kept outside the source tree. - Future track(Directory directory) async { - output.dependencies.add(directory.uri); - await for (final entry in directory.list(followLinks: false)) { - if (entry is Directory) { - if (!entry.path.endsWith('${Platform.pathSeparator}target') && - !entry.path.endsWith('${Platform.pathSeparator}.git')) { - await track(entry); - } - } else if (entry is File) { - output.dependencies.add(entry.uri); - } - } - } - - await track(Directory.fromUri(workspace)); - final linkMode = switch (config.linkModePreference) { - LinkModePreference.static || - LinkModePreference.preferStatic => - StaticLinking(), - _ => DynamicLoadingBundled(), - }; - final library = config.targetOS.libraryFileName('hrf_api', linkMode); - output.assets.code.add( - CodeAsset( - package: input.packageName, - name: 'frostdart_bindings_generated.dart', - linkMode: linkMode, - file: targetDirectory.resolve('$target/release/$library'), - ), - ); - }); -} - -Future run( - String executable, - List arguments, - Uri directory, - Map environment, -) async { - final process = await Process.start( - executable, - arguments, - workingDirectory: directory.toFilePath(), - environment: environment, - ); - await Future.wait([ - process.stdout.forEach(stderr.add), - process.stderr.forEach(stderr.add), - ]); - final code = await process.exitCode; - if (code != 0) { - throw ProcessException(executable, arguments, 'Native build failed', code); - } -} +void main(List args) => buildNativeLibrary(args); diff --git a/pubspec.yaml b/pubspec.yaml index fa794b8..1f8a6c5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,6 +10,10 @@ dependencies: ffi: ^2.0.1 hooks: ^2.2.0 code_assets: ^1.2.1 + dnix: + git: + url: https://github.com/cypherstack/dnix.git + ref: e91763ef25d084b3a5cf271a0cfee69159b1f779 dev_dependencies: test: ^1.26.3 diff --git a/src/serai/Cargo.toml b/src/serai/Cargo.toml index 7ca9483..c8ce356 100644 --- a/src/serai/Cargo.toml +++ b/src/serai/Cargo.toml @@ -28,3 +28,7 @@ multiexp = { opt-level = 3 } [profile.release] panic = "unwind" +# Explicit so reproducibility does not depend on toolchain defaults. +debug = false +strip = "debuginfo" +incremental = false diff --git a/test/keys_test.dart b/test/keys_test.dart index 9df9d49..633560a 100644 --- a/test/keys_test.dart +++ b/test/keys_test.dart @@ -7,7 +7,8 @@ import 'package:test/test.dart'; // src/serai/crypto/frost/src/tests/literal/vectors/frost-secp256k1-sha256.json // Serialized by ThresholdCore using vectors_to_multisig_keys in // src/serai/crypto/frost/src/tests/vectors.rs; Rust checked the group public key. -const _keys = '09000000736563703235366b31020003000100' +const _keys = + '09000000736563703235366b31020003000100' '08f89ffe80ac94dcb920c26f3f46140bfc7f95b493f8310f5fc1ea2b01f4254c' '026baee4bf7d4b9c4567dfff6f3c2c76df5c082e9320cd8187d6ab5965bc5a119a' '03dacc9463e5186f3c81ae1b314f7b09001a22b28bb56ad0abd3f376818f9604ab' diff --git a/tool/update_lock.dart b/tool/update_lock.dart new file mode 100644 index 0000000..1f3e92c --- /dev/null +++ b/tool/update_lock.dart @@ -0,0 +1,5 @@ +import 'dart:io'; + +import 'package:dnix/tools.dart'; + +void main(List args) async => exit(await updateLock(args)); diff --git a/tool/verify_release.dart b/tool/verify_release.dart new file mode 100644 index 0000000..b707f08 --- /dev/null +++ b/tool/verify_release.dart @@ -0,0 +1,5 @@ +import 'dart:io'; + +import 'package:dnix/tools.dart'; + +void main(List args) async => exit(await verifyRelease(args));