v1.0.7-r001: Cross-platform builds, FFI expansion, license, and bug fixes
- Version all crates at 1.0.7-r001 (tracking Salvium C++ 1.0.7)
- Source-available license: free for author/designees and non-commercial
use; annual commercial license required for any revenue-generating use
- Build scripts for Android/iOS/Linux/macOS/Windows producing both
libsalvium_crypto and libsalvium_ffi
- CI workflow publishes platform archives to salvium-rs-releases
- 23 new FFI functions: storage ops, address book, tx notes, attributes,
staked balance, key derivation, mnemonic
- Fix stake detection: count owned outputs regardless of spent state
- Auto-resume testnet integration tests from current chain height
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
name: Build & Publish Libraries
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ['v*']
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_tag:
|
||||
description: 'Release tag (e.g. v0.1.0)'
|
||||
required: true
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
# ── Android (arm64-v8a, armeabi-v7a, x86_64) ─────────────────────────────
|
||||
android:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: aarch64-linux-android, armv7-linux-androideabi, x86_64-linux-android
|
||||
|
||||
- uses: nttld/setup-ndk@v1
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: r26d
|
||||
|
||||
- name: Build Android libraries
|
||||
env:
|
||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
run: ./scripts/build-android.sh
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: android
|
||||
path: prebuilt/android/
|
||||
|
||||
# ── Linux x86_64 ──────────────────────────────────────────────────────────
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Build Linux libraries
|
||||
run: ./scripts/build-linux.sh
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-x86_64
|
||||
path: prebuilt/linux-x86_64/
|
||||
|
||||
# ── macOS (universal arm64 + x86_64) ──────────────────────────────────────
|
||||
macos:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: aarch64-apple-darwin, x86_64-apple-darwin
|
||||
|
||||
- name: Build macOS libraries
|
||||
run: ./scripts/build-macos.sh
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos
|
||||
path: prebuilt/macos/
|
||||
|
||||
# ── iOS (device + simulator xcframework) ──────────────────────────────────
|
||||
ios:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: aarch64-apple-ios, aarch64-apple-ios-sim, x86_64-apple-ios
|
||||
|
||||
- name: Build iOS frameworks
|
||||
run: ./scripts/build-ios.sh
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ios
|
||||
path: prebuilt/ios/
|
||||
|
||||
# ── Windows x86_64 (cross-compiled from Linux) ───────────────────────────
|
||||
windows:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: x86_64-pc-windows-gnu
|
||||
|
||||
- name: Install MinGW
|
||||
run: sudo apt-get update && sudo apt-get install -y gcc-mingw-w64-x86-64
|
||||
|
||||
- name: Build Windows libraries
|
||||
run: ./scripts/build-windows.sh
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-x86_64
|
||||
path: prebuilt/windows-x86_64/
|
||||
|
||||
# ── Publish to salvium-rs-release ─────────────────────────────────────────
|
||||
publish:
|
||||
needs: [android, linux, macos, ios, windows]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts/
|
||||
|
||||
- name: Determine tag
|
||||
id: tag
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo "tag=${{ github.event.inputs.release_tag }}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "tag=${{ github.ref_name }}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Package platform archives
|
||||
run: |
|
||||
TAG="${{ steps.tag.outputs.tag }}"
|
||||
mkdir -p release
|
||||
|
||||
# Android
|
||||
cd artifacts/android
|
||||
tar czf "../../release/salvium-libs-android-${TAG}.tar.gz" .
|
||||
cd ../..
|
||||
|
||||
# Linux
|
||||
cd artifacts/linux-x86_64
|
||||
tar czf "../../release/salvium-libs-linux-x86_64-${TAG}.tar.gz" .
|
||||
cd ../..
|
||||
|
||||
# macOS
|
||||
cd artifacts/macos
|
||||
tar czf "../../release/salvium-libs-macos-universal-${TAG}.tar.gz" .
|
||||
cd ../..
|
||||
|
||||
# iOS
|
||||
cd artifacts/ios
|
||||
tar czf "../../release/salvium-libs-ios-${TAG}.tar.gz" .
|
||||
cd ../..
|
||||
|
||||
# Windows
|
||||
cd artifacts/windows-x86_64
|
||||
zip -r "../../release/salvium-libs-windows-x86_64-${TAG}.zip" .
|
||||
cd ../..
|
||||
|
||||
ls -lh release/
|
||||
|
||||
- name: Push to salvium-rs-release
|
||||
env:
|
||||
RELEASE_TOKEN: ${{ secrets.RELEASES_PAT }}
|
||||
run: |
|
||||
TAG="${{ steps.tag.outputs.tag }}"
|
||||
REPO="salvium-project/salvium-rs-releases"
|
||||
|
||||
# Create release via GitHub API
|
||||
RELEASE_ID=$(curl -s -X POST \
|
||||
-H "Authorization: token $RELEASE_TOKEN" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
"https://api.github.com/repos/${REPO}/releases" \
|
||||
-d "{\"tag_name\":\"${TAG}\",\"name\":\"${TAG}\",\"body\":\"Pre-built Salvium Rust libraries for all platforms.\n\nBuilt from salvium-rs ${TAG}.\",\"draft\":false}" \
|
||||
| jq -r '.id')
|
||||
|
||||
echo "Release ID: $RELEASE_ID"
|
||||
|
||||
# Upload each archive as a release asset
|
||||
for file in release/*; do
|
||||
filename=$(basename "$file")
|
||||
echo "Uploading $filename..."
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $RELEASE_TOKEN" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
"https://uploads.github.com/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${filename}" \
|
||||
--data-binary "@$file"
|
||||
done
|
||||
|
||||
echo "==> Published to https://github.com/${REPO}/releases/tag/${TAG}"
|
||||
Generated
+14
-13
@@ -1928,7 +1928,7 @@ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||
|
||||
[[package]]
|
||||
name = "salvium-cli"
|
||||
version = "0.1.0"
|
||||
version = "1.0.7-r001"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"dirs",
|
||||
@@ -1949,7 +1949,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "salvium-consensus"
|
||||
version = "0.1.0"
|
||||
version = "1.0.7-r001"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"salvium-types",
|
||||
@@ -1961,7 +1961,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "salvium-crypto"
|
||||
version = "0.1.0"
|
||||
version = "1.0.7-r001"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"argon2",
|
||||
@@ -1974,6 +1974,7 @@ dependencies = [
|
||||
"log",
|
||||
"p256",
|
||||
"rusqlite",
|
||||
"salvium-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
@@ -1985,7 +1986,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "salvium-ffi"
|
||||
version = "0.1.0"
|
||||
version = "1.0.7-r001"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"log",
|
||||
@@ -2002,7 +2003,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "salvium-miner"
|
||||
version = "0.1.0"
|
||||
version = "1.0.7-r001"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"hex",
|
||||
@@ -2017,7 +2018,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "salvium-miner-gr"
|
||||
version = "0.1.0"
|
||||
version = "1.0.7-r001"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"clap",
|
||||
@@ -2029,7 +2030,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "salvium-miner-v2"
|
||||
version = "0.1.0"
|
||||
version = "1.0.7-r001"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"clap",
|
||||
@@ -2044,7 +2045,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "salvium-multisig"
|
||||
version = "0.1.0"
|
||||
version = "1.0.7-r001"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"rand 0.8.5",
|
||||
@@ -2055,7 +2056,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "salvium-rpc"
|
||||
version = "0.1.0"
|
||||
version = "1.0.7-r001"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"hex",
|
||||
@@ -2069,7 +2070,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "salvium-sync-bench"
|
||||
version = "0.1.0"
|
||||
version = "1.0.7-r001"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"env_logger",
|
||||
@@ -2086,7 +2087,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "salvium-tx"
|
||||
version = "0.1.0"
|
||||
version = "1.0.7-r001"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"hex",
|
||||
@@ -2103,7 +2104,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "salvium-types"
|
||||
version = "0.1.0"
|
||||
version = "1.0.7-r001"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"serde",
|
||||
@@ -2113,7 +2114,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "salvium-wallet"
|
||||
version = "0.1.0"
|
||||
version = "1.0.7-r001"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"dirs",
|
||||
|
||||
+2
-1
@@ -17,8 +17,9 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.0.7-r001"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
license = "LicenseRef-Salvium-RS"
|
||||
repository = "https://github.com/salvium/salvium-rs"
|
||||
|
||||
[workspace.dependencies]
|
||||
|
||||
@@ -1,99 +1,96 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
Salvium-RS Source-Available License
|
||||
Version 1.0, February 2026
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
Copyright (c) 2024-2026 The Salvium Project. All rights reserved.
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
1. Definitions.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
"Software" means the source code, object code, documentation, and
|
||||
any associated files contained in this repository.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
"Author" means The Salvium Project and its principal copyright holders.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
"Designee" means any individual, organization, or entity that has
|
||||
received express written authorization from the Author to use the
|
||||
Software under the terms of Section 2.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
"Commercial License" means a separate written agreement between the
|
||||
Author and a licensee, executed under the terms described in Section 3
|
||||
and the accompanying LICENSE-COMMERCIAL file.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
2. Author and Designee Rights.
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
The Author and all Designees are granted a perpetual, worldwide,
|
||||
royalty-free, irrevocable license to use, copy, modify, distribute,
|
||||
sublicense, and create derivative works of the Software, without
|
||||
restriction, for any purpose including commercial use.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner.
|
||||
3. Non-Commercial Use — Free.
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
Any individual or entity that is not the Author or a Designee may
|
||||
use, copy, modify, and create derivative works of the Software free
|
||||
of charge, provided that ALL of the following conditions are met:
|
||||
|
||||
2. Grant of Copyright License.
|
||||
Subject to the terms and conditions of this License, each Contributor
|
||||
hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable copyright license to reproduce,
|
||||
prepare Derivative Works of, publicly display, publicly perform,
|
||||
sublicense, and distribute the Work and such Derivative Works in
|
||||
Source or Object form.
|
||||
(a) The Software and any derivative works are used solely for
|
||||
non-commercial purposes. "Non-commercial" means the user does
|
||||
not charge fees, generate revenue, accept donations, or derive
|
||||
any direct or indirect monetary benefit from any product or
|
||||
service that incorporates the Software.
|
||||
|
||||
3. Grant of Patent License.
|
||||
Subject to the terms and conditions of this License, each Contributor
|
||||
hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
and otherwise transfer the Work.
|
||||
(b) Any public distribution of the Software or derivative works
|
||||
must retain this license notice in its entirety and clearly
|
||||
attribute The Salvium Project as the original author.
|
||||
|
||||
4. Redistribution.
|
||||
You may reproduce and distribute copies of the Work or Derivative
|
||||
Works thereof in any medium, with or without modifications.
|
||||
(c) The Software is not sublicensed to any third party for
|
||||
commercial use.
|
||||
|
||||
5. Submission of Contributions.
|
||||
Unless You explicitly state otherwise, any Contribution intentionally
|
||||
submitted for inclusion in the Work shall be under the terms of this
|
||||
License.
|
||||
If at any time the user begins to derive monetary benefit from a
|
||||
product or service incorporating the Software, they must obtain a
|
||||
Commercial License within 30 days or cease all use. There is no
|
||||
revenue threshold — any monetary benefit triggers this requirement.
|
||||
|
||||
6. Trademarks.
|
||||
This License does not grant permission to use the trade names,
|
||||
trademarks, service marks, or product names of the Licensor.
|
||||
4. Commercial Use — License Required.
|
||||
|
||||
7. Disclaimer of Warranty.
|
||||
Unless required by applicable law or agreed to in writing, Licensor
|
||||
provides the Work on an "AS IS" BASIS.
|
||||
Any use of the Software that does not qualify under Section 2
|
||||
(Author/Designee) or Section 3 (Non-Commercial) requires a
|
||||
Commercial License obtained directly from the Author. This includes
|
||||
but is not limited to:
|
||||
|
||||
8. Limitation of Liability.
|
||||
In no event shall any Contributor be liable for damages.
|
||||
(a) incorporating the Software into any product or service that
|
||||
generates revenue, directly or indirectly,
|
||||
(b) distributing compiled libraries or binaries of the Software
|
||||
as part of a commercial offering,
|
||||
(c) offering paid services built on the Software, or
|
||||
(d) using the Software within an organization that charges
|
||||
customers for products or services that depend on it.
|
||||
|
||||
9. Accepting Warranty or Additional Liability.
|
||||
While redistributing the Work, You may choose to offer support or
|
||||
warranty obligations on Your own behalf.
|
||||
See the LICENSE-COMMERCIAL file for details on obtaining a
|
||||
Commercial License.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
5. Third-Party Components.
|
||||
|
||||
This Software incorporates open-source dependencies governed by their
|
||||
own licenses (MIT, Apache-2.0, BSD, ISC, etc.). Those licenses apply
|
||||
solely to their respective components and are unaffected by this
|
||||
license. Run `cargo license` for a full list of third-party licenses.
|
||||
|
||||
6. No Warranty.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES, OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,
|
||||
ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
7. Termination.
|
||||
|
||||
Any use of the Software that violates the terms of this license
|
||||
automatically and immediately terminates all rights granted herein.
|
||||
Upon termination, the violating party must cease all use and destroy
|
||||
all copies of the Software in their possession.
|
||||
|
||||
|
||||
+64
-10
@@ -1,16 +1,70 @@
|
||||
Commercial License Notice
|
||||
Salvium-RS Commercial License
|
||||
|
||||
This software is available under a commercial license for proprietary use,
|
||||
closed-source redistribution, or other use cases not compatible with the
|
||||
Apache License, Version 2.0.
|
||||
Copyright (c) 2024-2026 The Salvium Project. All rights reserved.
|
||||
|
||||
Commercial licenses are offered by the Salvium project authors and may
|
||||
include alternative terms, warranties, indemnification, or support.
|
||||
|
||||
For commercial licensing inquiries, contact:
|
||||
OVERVIEW
|
||||
|
||||
mxhess@proton.me
|
||||
The Salvium-RS Software is source-available under the terms described
|
||||
in the LICENSE file. Use beyond personal educational study of the source
|
||||
code requires a Commercial License from the Author.
|
||||
|
||||
Use of this software without a commercial license is governed solely by
|
||||
the Apache License, Version 2.0.
|
||||
This file describes the terms under which a Commercial License may be
|
||||
obtained.
|
||||
|
||||
|
||||
WHO NEEDS A COMMERCIAL LICENSE
|
||||
|
||||
You need a Commercial License if you are not the Author or a Designee
|
||||
(as defined in the LICENSE file) and you intend to:
|
||||
|
||||
- Use the Software or its compiled libraries in any application,
|
||||
product, or service (whether commercial or non-commercial)
|
||||
- Distribute the Software or derivative works to third parties
|
||||
- Deploy the Software's compiled binaries (libsalvium_crypto,
|
||||
libsalvium_ffi, or any other build artifact) in any environment
|
||||
- Integrate the Software's APIs into your own software
|
||||
|
||||
|
||||
COMMERCIAL LICENSE TERMS
|
||||
|
||||
Commercial licenses are issued on an annual basis and must be renewed
|
||||
each year for continued use. A lapsed license requires the licensee to
|
||||
cease all use of the Software until renewed or renegotiated.
|
||||
|
||||
License terms are negotiated individually and may include, at the
|
||||
Author's discretion:
|
||||
|
||||
- An annual license fee (the specific amount is set by contract)
|
||||
- Per-seat, per-deployment, or revenue-based pricing
|
||||
- Scope restrictions (single product, single organization, etc.)
|
||||
- Source code access and modification rights
|
||||
- Technical support and maintenance
|
||||
- Warranty and indemnification provisions
|
||||
|
||||
There are no free commercial licenses. Any use that generates revenue,
|
||||
directly or indirectly, requires a paid annual license from the Author.
|
||||
|
||||
|
||||
HOW TO OBTAIN A COMMERCIAL LICENSE
|
||||
|
||||
Contact the Author:
|
||||
|
||||
The Salvium Project
|
||||
Email: mxhess@proton.me
|
||||
Web: https://salvium.io
|
||||
|
||||
Please include:
|
||||
|
||||
- Your name and organization
|
||||
- A description of your intended use
|
||||
- Expected scale (users, deployments, revenue)
|
||||
|
||||
|
||||
DESIGNEE STATUS
|
||||
|
||||
The Author may grant Designee status in writing, which confers the
|
||||
same rights as the Author under the LICENSE file. Designee status
|
||||
is granted at the Author's sole discretion and may be revoked with
|
||||
reasonable written notice.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "salvium-cli"
|
||||
version = "0.1.0"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
description = "Command-line wallet for Salvium"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "salvium-consensus"
|
||||
version = "0.1.0"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
description = "Blockchain validation rules, tree hash, oracle pricing, and mining utilities for Salvium"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "salvium-crypto"
|
||||
version = "0.1.0"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
description = "Crypto primitives for salvium-rs"
|
||||
|
||||
@@ -27,6 +27,7 @@ aes-gcm = "0.10"
|
||||
serde_json = "1"
|
||||
hex = "0.4"
|
||||
log = "0.4"
|
||||
salvium-types = { path = "../salvium-types" }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
p256 = { version = "0.13", features = ["ecdsa"] }
|
||||
|
||||
@@ -2140,6 +2140,674 @@ pub unsafe extern "C" fn salvium_storage_delete_stakes_above(
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Storage: Output Freeze / Thaw / Unspend ────────────────────────────────
|
||||
|
||||
/// Mark an output as unspent (reverse a previous mark_spent).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_mark_unspent(
|
||||
handle: u32,
|
||||
key_image: *const u8,
|
||||
ki_len: usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let ki_str = match std::str::from_utf8(slice::from_raw_parts(key_image, ki_len)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
match crate::storage::with_db(handle, |db| db.mark_unspent(ki_str)) {
|
||||
Ok(()) => 0,
|
||||
Err(e) => { log::error!("salvium_storage_mark_unspent: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Freeze an output (exclude from selection).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_freeze_output(
|
||||
handle: u32,
|
||||
key_image: *const u8,
|
||||
ki_len: usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let ki_str = match std::str::from_utf8(slice::from_raw_parts(key_image, ki_len)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
match crate::storage::with_db(handle, |db| db.freeze_output(ki_str)) {
|
||||
Ok(()) => 0,
|
||||
Err(e) => { log::error!("salvium_storage_freeze_output: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Thaw a previously frozen output.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_thaw_output(
|
||||
handle: u32,
|
||||
key_image: *const u8,
|
||||
ki_len: usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let ki_str = match std::str::from_utf8(slice::from_raw_parts(key_image, ki_len)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
match crate::storage::with_db(handle, |db| db.thaw_output(ki_str)) {
|
||||
Ok(()) => 0,
|
||||
Err(e) => { log::error!("salvium_storage_thaw_output: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Look up an output by its one-time public key (hex).
|
||||
/// Returns JSON OutputRow or -1 if not found.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_get_output_by_public_key(
|
||||
handle: u32,
|
||||
public_key: *const u8,
|
||||
pk_len: usize,
|
||||
out_ptr: *mut *mut u8,
|
||||
out_len: *mut usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let pk_str = match std::str::from_utf8(slice::from_raw_parts(public_key, pk_len)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
match crate::storage::with_db(handle, |db| db.get_output_by_public_key(pk_str)) {
|
||||
Ok(Some(row)) => {
|
||||
let json = match serde_json::to_vec(&row) {
|
||||
Ok(j) => j,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
let len = json.len();
|
||||
let buf = json.into_boxed_slice();
|
||||
let raw = Box::into_raw(buf);
|
||||
*out_ptr = (*raw).as_mut_ptr();
|
||||
*out_len = len;
|
||||
0
|
||||
}
|
||||
Ok(None) => -1,
|
||||
Err(e) => { log::error!("salvium_storage_get_output_by_public_key: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Storage: Transaction Notes ─────────────────────────────────────────────
|
||||
|
||||
/// Set a note on a transaction.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_set_tx_note(
|
||||
handle: u32,
|
||||
tx_hash: *const u8,
|
||||
th_len: usize,
|
||||
note: *const u8,
|
||||
note_len: usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let hash_str = match std::str::from_utf8(slice::from_raw_parts(tx_hash, th_len)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
let note_str = match std::str::from_utf8(slice::from_raw_parts(note, note_len)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
match crate::storage::with_db(handle, |db| db.set_tx_note(hash_str, note_str)) {
|
||||
Ok(()) => 0,
|
||||
Err(e) => { log::error!("salvium_storage_set_tx_note: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get notes for multiple transactions.
|
||||
/// Input: JSON array of tx hashes: ["hash1","hash2"]
|
||||
/// Returns JSON object: {"hash1":"note1","hash2":"note2"}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_get_tx_notes(
|
||||
handle: u32,
|
||||
json_buf: *const u8,
|
||||
json_len: usize,
|
||||
out_ptr: *mut *mut u8,
|
||||
out_len: *mut usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let json_str = match std::str::from_utf8(slice::from_raw_parts(json_buf, json_len)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
let hashes: Vec<String> = match serde_json::from_str(json_str) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
let hash_refs: Vec<&str> = hashes.iter().map(|s| s.as_str()).collect();
|
||||
match crate::storage::with_db(handle, |db| db.get_tx_notes(&hash_refs)) {
|
||||
Ok(notes) => {
|
||||
let json = match serde_json::to_vec(¬es) {
|
||||
Ok(j) => j,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
let len = json.len();
|
||||
let buf = json.into_boxed_slice();
|
||||
let raw = Box::into_raw(buf);
|
||||
*out_ptr = (*raw).as_mut_ptr();
|
||||
*out_len = len;
|
||||
0
|
||||
}
|
||||
Err(e) => { log::error!("salvium_storage_get_tx_notes: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Storage: Address Book ──────────────────────────────────────────────────
|
||||
|
||||
/// Add an address book entry. Returns the new row_id on success (as i32), -1 on error.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_add_address_book_entry(
|
||||
handle: u32,
|
||||
json_buf: *const u8,
|
||||
json_len: usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let json_str = match std::str::from_utf8(slice::from_raw_parts(json_buf, json_len)) {
|
||||
Ok(s) => s,
|
||||
Err(e) => { log::error!("salvium_storage_add_address_book_entry: invalid UTF-8: {e}"); return -1; }
|
||||
};
|
||||
let data: serde_json::Value = match serde_json::from_str(json_str) {
|
||||
Ok(v) => v,
|
||||
Err(e) => { log::error!("salvium_storage_add_address_book_entry: JSON parse error: {e}"); return -1; }
|
||||
};
|
||||
let address = data["address"].as_str().unwrap_or("");
|
||||
let label = data["label"].as_str().unwrap_or("");
|
||||
let description = data["description"].as_str().unwrap_or("");
|
||||
let payment_id = data["paymentId"].as_str().unwrap_or("");
|
||||
match crate::storage::with_db(handle, |db| {
|
||||
db.add_address_book_entry(address, label, description, payment_id)
|
||||
}) {
|
||||
Ok(row_id) => row_id as i32,
|
||||
Err(e) => { log::error!("salvium_storage_add_address_book_entry: DB error: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get all address book entries. Returns JSON array.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_get_address_book(
|
||||
handle: u32,
|
||||
out_ptr: *mut *mut u8,
|
||||
out_len: *mut usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
match crate::storage::with_db(handle, |db| db.get_address_book()) {
|
||||
Ok(entries) => {
|
||||
let json = match serde_json::to_vec(&entries) {
|
||||
Ok(j) => j,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
let len = json.len();
|
||||
let buf = json.into_boxed_slice();
|
||||
let raw = Box::into_raw(buf);
|
||||
*out_ptr = (*raw).as_mut_ptr();
|
||||
*out_len = len;
|
||||
0
|
||||
}
|
||||
Err(e) => { log::error!("salvium_storage_get_address_book: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a single address book entry by row_id. Returns JSON object or -1 if not found.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_get_address_book_entry(
|
||||
handle: u32,
|
||||
row_id: i64,
|
||||
out_ptr: *mut *mut u8,
|
||||
out_len: *mut usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
match crate::storage::with_db(handle, |db| db.get_address_book_entry(row_id)) {
|
||||
Ok(Some(entry)) => {
|
||||
let json = match serde_json::to_vec(&entry) {
|
||||
Ok(j) => j,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
let len = json.len();
|
||||
let buf = json.into_boxed_slice();
|
||||
let raw = Box::into_raw(buf);
|
||||
*out_ptr = (*raw).as_mut_ptr();
|
||||
*out_len = len;
|
||||
0
|
||||
}
|
||||
Ok(None) => -1,
|
||||
Err(e) => { log::error!("salvium_storage_get_address_book_entry: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Edit an address book entry. Input: JSON with "rowId" and optional "address","label","description","paymentId".
|
||||
/// Returns 1 if updated, 0 if not found, -1 on error.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_edit_address_book_entry(
|
||||
handle: u32,
|
||||
json_buf: *const u8,
|
||||
json_len: usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let json_str = match std::str::from_utf8(slice::from_raw_parts(json_buf, json_len)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
let data: serde_json::Value = match serde_json::from_str(json_str) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
let row_id = data["rowId"].as_i64().unwrap_or(-1);
|
||||
if row_id < 0 { return -1; }
|
||||
let address = data.get("address").and_then(|v| v.as_str());
|
||||
let label = data.get("label").and_then(|v| v.as_str());
|
||||
let description = data.get("description").and_then(|v| v.as_str());
|
||||
let payment_id = data.get("paymentId").and_then(|v| v.as_str());
|
||||
match crate::storage::with_db(handle, |db| {
|
||||
db.edit_address_book_entry(row_id, address, label, description, payment_id)
|
||||
}) {
|
||||
Ok(true) => 1,
|
||||
Ok(false) => 0,
|
||||
Err(e) => { log::error!("salvium_storage_edit_address_book_entry: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Delete an address book entry by row_id. Returns 1 if deleted, 0 if not found, -1 on error.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_delete_address_book_entry(
|
||||
handle: u32,
|
||||
row_id: i64,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
match crate::storage::with_db(handle, |db| db.delete_address_book_entry(row_id)) {
|
||||
Ok(true) => 1,
|
||||
Ok(false) => 0,
|
||||
Err(e) => { log::error!("salvium_storage_delete_address_book_entry: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Storage: Key-Value Attributes ──────────────────────────────────────────
|
||||
|
||||
/// Set a wallet attribute (key-value pair).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_set_attribute(
|
||||
handle: u32,
|
||||
key: *const u8,
|
||||
key_len: usize,
|
||||
value: *const u8,
|
||||
value_len: usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let key_str = match std::str::from_utf8(slice::from_raw_parts(key, key_len)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
let val_str = match std::str::from_utf8(slice::from_raw_parts(value, value_len)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
match crate::storage::with_db(handle, |db| db.set_attribute(key_str, val_str)) {
|
||||
Ok(()) => 0,
|
||||
Err(e) => { log::error!("salvium_storage_set_attribute: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a wallet attribute by key. Returns the value string or -1 if not found.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_get_attribute(
|
||||
handle: u32,
|
||||
key: *const u8,
|
||||
key_len: usize,
|
||||
out_ptr: *mut *mut u8,
|
||||
out_len: *mut usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let key_str = match std::str::from_utf8(slice::from_raw_parts(key, key_len)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
match crate::storage::with_db(handle, |db| db.get_attribute(key_str)) {
|
||||
Ok(Some(value)) => {
|
||||
let bytes = value.into_bytes().into_boxed_slice();
|
||||
let len = bytes.len();
|
||||
let raw = Box::into_raw(bytes);
|
||||
*out_ptr = (*raw).as_mut_ptr();
|
||||
*out_len = len;
|
||||
0
|
||||
}
|
||||
Ok(None) => -1,
|
||||
Err(e) => { log::error!("salvium_storage_get_attribute: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Storage: Staked Balance ────────────────────────────────────────────────
|
||||
|
||||
/// Get total staked balance for a specific asset type.
|
||||
/// Returns JSON: {"stakedBalance":"<atomic_units>"}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_get_staked_balance(
|
||||
handle: u32,
|
||||
asset_type: *const u8,
|
||||
at_len: usize,
|
||||
out_ptr: *mut *mut u8,
|
||||
out_len: *mut usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let at_str = match std::str::from_utf8(slice::from_raw_parts(asset_type, at_len)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
match crate::storage::with_db(handle, |db| db.get_staked_balance(at_str)) {
|
||||
Ok(balance) => {
|
||||
let result = serde_json::json!({ "stakedBalance": balance.to_string() });
|
||||
let json = serde_json::to_vec(&result).unwrap();
|
||||
let len = json.len();
|
||||
let buf = json.into_boxed_slice();
|
||||
let raw = Box::into_raw(buf);
|
||||
*out_ptr = (*raw).as_mut_ptr();
|
||||
*out_len = len;
|
||||
0
|
||||
}
|
||||
Err(e) => { log::error!("salvium_storage_get_staked_balance: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get staked balances for all asset types.
|
||||
/// Returns JSON: {"SAL1":"<amount>", ...}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_get_all_staked_balances(
|
||||
handle: u32,
|
||||
out_ptr: *mut *mut u8,
|
||||
out_len: *mut usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
match crate::storage::with_db(handle, |db| db.get_all_staked_balances()) {
|
||||
Ok(balances) => {
|
||||
let string_map: std::collections::HashMap<String, String> = balances
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.to_string()))
|
||||
.collect();
|
||||
let json = serde_json::to_vec(&string_map).unwrap();
|
||||
let len = json.len();
|
||||
let buf = json.into_boxed_slice();
|
||||
let raw = Box::into_raw(buf);
|
||||
*out_ptr = (*raw).as_mut_ptr();
|
||||
*out_len = len;
|
||||
0
|
||||
}
|
||||
Err(e) => { log::error!("salvium_storage_get_all_staked_balances: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Look up a locked stake by its expected return output key.
|
||||
/// Returns JSON StakeRow or -1 if not found.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_get_locked_stake_by_return_output_key(
|
||||
handle: u32,
|
||||
key_buf: *const u8,
|
||||
key_len: usize,
|
||||
out_ptr: *mut *mut u8,
|
||||
out_len: *mut usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let key_str = match std::str::from_utf8(slice::from_raw_parts(key_buf, key_len)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
match crate::storage::with_db(handle, |db| db.get_locked_stake_by_return_output_key(key_str)) {
|
||||
Ok(Some(stake)) => {
|
||||
let json = match serde_json::to_vec(&stake) {
|
||||
Ok(j) => j,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
let len = json.len();
|
||||
let buf = json.into_boxed_slice();
|
||||
let raw = Box::into_raw(buf);
|
||||
*out_ptr = (*raw).as_mut_ptr();
|
||||
*out_len = len;
|
||||
0
|
||||
}
|
||||
Ok(None) => -1,
|
||||
Err(e) => { log::error!("salvium_storage_get_locked_stake_by_return_output_key: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Storage: Rekey ─────────────────────────────────────────────────────────
|
||||
|
||||
/// Change the database encryption key. new_key must be exactly 32 bytes.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_rekey(
|
||||
handle: u32,
|
||||
new_key: *const u8,
|
||||
key_len: usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let key = slice::from_raw_parts(new_key, key_len);
|
||||
match crate::storage::with_db(handle, |db| {
|
||||
db.rekey(key).map_err(|e| e)
|
||||
}) {
|
||||
Ok(()) => 0,
|
||||
Err(e) => { log::error!("salvium_storage_rekey: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Wallet Key Derivation ──────────────────────────────────────────────────
|
||||
|
||||
/// Derive full wallet keys (CryptoNote + CARROT) from a 32-byte seed.
|
||||
///
|
||||
/// Output: 416 bytes (13 × 32):
|
||||
/// [ 0.. 32] cn_spend_secret_key
|
||||
/// [ 32.. 64] cn_spend_public_key
|
||||
/// [ 64.. 96] cn_view_secret_key
|
||||
/// [ 96..128] cn_view_public_key
|
||||
/// [128..416] CARROT keys (288 bytes = 9 × 32, same layout as derive_carrot_keys_batch)
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_derive_wallet_keys_from_seed(
|
||||
seed: *const u8,
|
||||
out: *mut u8,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let seed_bytes = crate::to32(slice::from_raw_parts(seed, 32));
|
||||
|
||||
// CryptoNote key derivation
|
||||
let spend_secret = crate::sc_reduce32(&seed_bytes);
|
||||
let spend_secret_32 = crate::to32(&spend_secret);
|
||||
let spend_public = crate::scalar_mult_base(&spend_secret_32);
|
||||
let view_secret = crate::sc_reduce32(&crate::keccak256(&spend_secret_32));
|
||||
let view_secret_32 = crate::to32(&view_secret);
|
||||
let view_public = crate::scalar_mult_base(&view_secret_32);
|
||||
|
||||
// CARROT key derivation (master_secret = spend_secret)
|
||||
let carrot = crate::carrot_keys::derive_carrot_keys(&spend_secret_32);
|
||||
|
||||
// Write CN keys (128 bytes)
|
||||
ptr::copy_nonoverlapping(spend_secret.as_ptr(), out, 32);
|
||||
ptr::copy_nonoverlapping(spend_public.as_ptr(), out.add(32), 32);
|
||||
ptr::copy_nonoverlapping(view_secret.as_ptr(), out.add(64), 32);
|
||||
ptr::copy_nonoverlapping(view_public.as_ptr(), out.add(96), 32);
|
||||
// Write CARROT keys (288 bytes)
|
||||
ptr::copy_nonoverlapping(carrot.as_ptr(), out.add(128), 288);
|
||||
0
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Address Generation & Parsing ───────────────────────────────────────────
|
||||
|
||||
/// Create an address string from components.
|
||||
///
|
||||
/// Parameters:
|
||||
/// network: 0 = mainnet, 1 = testnet, 2 = stagenet
|
||||
/// format: 0 = legacy, 1 = carrot
|
||||
/// addr_type: 0 = standard, 1 = integrated, 2 = subaddress
|
||||
/// spend_pub: 32 bytes
|
||||
/// view_pub: 32 bytes
|
||||
/// payment_id: 16 bytes (only for integrated), null otherwise
|
||||
/// payment_id_len: 0 or 16
|
||||
///
|
||||
/// Returns the address string via out_ptr/out_len.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_create_address(
|
||||
network: u8,
|
||||
format: u8,
|
||||
addr_type: u8,
|
||||
spend_pub: *const u8,
|
||||
view_pub: *const u8,
|
||||
payment_id: *const u8,
|
||||
payment_id_len: usize,
|
||||
out_ptr: *mut *mut u8,
|
||||
out_len: *mut usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
use salvium_types::constants::{AddressFormat, AddressType, Network};
|
||||
|
||||
let net = match network {
|
||||
0 => Network::Mainnet,
|
||||
1 => Network::Testnet,
|
||||
2 => Network::Stagenet,
|
||||
_ => return -1,
|
||||
};
|
||||
let fmt = match format {
|
||||
0 => AddressFormat::Legacy,
|
||||
1 => AddressFormat::Carrot,
|
||||
_ => return -1,
|
||||
};
|
||||
let at = match addr_type {
|
||||
0 => AddressType::Standard,
|
||||
1 => AddressType::Integrated,
|
||||
2 => AddressType::Subaddress,
|
||||
_ => return -1,
|
||||
};
|
||||
|
||||
let spend = slice::from_raw_parts(spend_pub, 32);
|
||||
let view = slice::from_raw_parts(view_pub, 32);
|
||||
let pid = if payment_id_len > 0 && !payment_id.is_null() {
|
||||
Some(slice::from_raw_parts(payment_id, payment_id_len))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
match salvium_types::address::create_address_raw(net, fmt, at, spend, view, pid) {
|
||||
Ok(addr) => {
|
||||
let bytes = addr.into_bytes().into_boxed_slice();
|
||||
let len = bytes.len();
|
||||
let raw = Box::into_raw(bytes);
|
||||
*out_ptr = (*raw).as_mut_ptr();
|
||||
*out_len = len;
|
||||
0
|
||||
}
|
||||
Err(e) => { log::error!("salvium_create_address: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse an address string into its components.
|
||||
///
|
||||
/// Returns JSON:
|
||||
/// {
|
||||
/// "network": "mainnet"|"testnet"|"stagenet",
|
||||
/// "format": "legacy"|"carrot",
|
||||
/// "addressType": "standard"|"integrated"|"subaddress",
|
||||
/// "spendPublicKey": "hex...",
|
||||
/// "viewPublicKey": "hex...",
|
||||
/// "paymentId": "hex..." | null
|
||||
/// }
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_parse_address(
|
||||
addr: *const u8,
|
||||
addr_len: usize,
|
||||
out_ptr: *mut *mut u8,
|
||||
out_len: *mut usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let addr_str = match std::str::from_utf8(slice::from_raw_parts(addr, addr_len)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
match salvium_types::address::parse_address(addr_str) {
|
||||
Ok(parsed) => {
|
||||
let pid = parsed.payment_id.map(|p| hex::encode(p));
|
||||
let result = serde_json::json!({
|
||||
"network": format!("{:?}", parsed.network).to_lowercase(),
|
||||
"format": format!("{:?}", parsed.format).to_lowercase(),
|
||||
"addressType": format!("{:?}", parsed.address_type).to_lowercase(),
|
||||
"spendPublicKey": hex::encode(parsed.spend_public_key),
|
||||
"viewPublicKey": hex::encode(parsed.view_public_key),
|
||||
"paymentId": pid,
|
||||
});
|
||||
let json = serde_json::to_vec(&result).unwrap();
|
||||
let len = json.len();
|
||||
let buf = json.into_boxed_slice();
|
||||
let raw = Box::into_raw(buf);
|
||||
*out_ptr = (*raw).as_mut_ptr();
|
||||
*out_len = len;
|
||||
0
|
||||
}
|
||||
Err(e) => { log::error!("salvium_parse_address: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Mnemonic Seed Encoding ─────────────────────────────────────────────────
|
||||
|
||||
/// Convert a 32-byte seed to a 25-word mnemonic (English).
|
||||
/// Returns the mnemonic string via out_ptr/out_len.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_seed_to_mnemonic(
|
||||
seed: *const u8,
|
||||
out_ptr: *mut *mut u8,
|
||||
out_len: *mut usize,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let seed_arr = crate::to32(slice::from_raw_parts(seed, 32));
|
||||
match salvium_types::mnemonic::seed_to_mnemonic(&seed_arr, Some("english")) {
|
||||
Ok(words) => {
|
||||
let bytes = words.into_bytes().into_boxed_slice();
|
||||
let len = bytes.len();
|
||||
let raw = Box::into_raw(bytes);
|
||||
*out_ptr = (*raw).as_mut_ptr();
|
||||
*out_len = len;
|
||||
0
|
||||
}
|
||||
Err(e) => { log::error!("salvium_seed_to_mnemonic: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert a mnemonic string to a 32-byte seed.
|
||||
/// out: must be at least 32 bytes.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_mnemonic_to_seed(
|
||||
mnemonic: *const u8,
|
||||
mnemonic_len: usize,
|
||||
out: *mut u8,
|
||||
) -> i32 {
|
||||
catch_ffi(|| {
|
||||
let words = match std::str::from_utf8(slice::from_raw_parts(mnemonic, mnemonic_len)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
match salvium_types::mnemonic::mnemonic_to_seed(words, None) {
|
||||
Ok(result) => {
|
||||
ptr::copy_nonoverlapping(result.seed.as_ptr(), out, 32);
|
||||
0
|
||||
}
|
||||
Err(e) => { log::error!("salvium_mnemonic_to_seed: {e}"); -1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Free Rust-allocated result buffer.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn salvium_storage_free_buf(buf_ptr: *mut u8, len: usize) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "salvium-ffi"
|
||||
version = "0.1.0"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
description = "C FFI shim for wallet, daemon, and transaction operations"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "salvium-miner-gr"
|
||||
version = "0.1.0"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
description = "GhostRider CPU miner for Salvium cryptocurrency (experimental)"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "salvium-miner-v2"
|
||||
version = "0.1.0"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
description = "RandomX v2 CPU miner for Salvium cryptocurrency (experimental)"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "salvium-miner"
|
||||
version = "0.1.0"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
description = "Native RandomX CPU miner for Salvium cryptocurrency"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "salvium-multisig"
|
||||
version = "0.1.0"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "salvium-rpc"
|
||||
version = "0.1.0"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
description = "Async and blocking RPC clients for Salvium daemon and wallet"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "salvium-sync-bench"
|
||||
version = "0.1.0"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[[bin]]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "salvium-tx"
|
||||
version = "0.1.0"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
description = "Transaction construction, parsing, and analysis for Salvium"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "salvium-types"
|
||||
version = "0.1.0"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
description = "Core types and constants for the Salvium cryptocurrency"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "salvium-wallet"
|
||||
version = "0.1.0"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
description = "Wallet core: key management, output scanning, sync engine, balance, UTXO selection"
|
||||
|
||||
|
||||
@@ -1285,10 +1285,13 @@ fn detect_spent_outputs(
|
||||
.get_output(ki_hex)
|
||||
.map_err(|e| WalletError::Storage(e.to_string()))?;
|
||||
if let Some(row) = output {
|
||||
// Count all outputs that belong to us, even if already marked spent
|
||||
// (e.g. by mark_inputs_spent after TX submission). This ensures
|
||||
// stake recording still triggers when the block is later synced.
|
||||
spent_count += 1;
|
||||
if !row.is_spent {
|
||||
db.mark_spent(ki_hex, tx_hash_hex, block_height as i64)
|
||||
.map_err(|e| WalletError::Storage(e.to_string()))?;
|
||||
spent_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +136,23 @@ fn resume_from_hf() -> u8 {
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Auto-detect the appropriate resume HF based on the daemon's current height.
|
||||
///
|
||||
/// If the chain has already progressed past a fork boundary, the daemon will
|
||||
/// reject transactions built with that fork's parameters (wrong rct_type, etc.).
|
||||
/// Find the latest fork whose height the chain has passed, and resume from there.
|
||||
fn auto_resume_hf(daemon_height: u64) -> u8 {
|
||||
let mut resume = 0u8;
|
||||
for (i, fork) in FORKS.iter().enumerate() {
|
||||
let next_height = FORKS.get(i + 1).map(|f| f.height).unwrap_or(u64::MAX);
|
||||
if daemon_height >= next_height {
|
||||
// Chain is past this fork AND the next one — skip this fork
|
||||
resume = fork.hf + 1;
|
||||
}
|
||||
}
|
||||
resume
|
||||
}
|
||||
|
||||
async fn get_daemon_height(daemon: &DaemonRpc) -> u64 {
|
||||
let info = daemon.get_info().await.expect("failed to get daemon info");
|
||||
info.height
|
||||
@@ -1265,7 +1282,7 @@ async fn full_testnet_hardfork_progression() {
|
||||
println!("=== Phase 0: Setup ===\n");
|
||||
|
||||
let url = daemon_url();
|
||||
let resume_hf = resume_from_hf();
|
||||
let env_resume_hf = resume_from_hf();
|
||||
let daemon = DaemonRpc::new(&url);
|
||||
|
||||
let info = daemon.get_info().await.expect("cannot connect to daemon");
|
||||
@@ -1276,8 +1293,16 @@ async fn full_testnet_hardfork_progression() {
|
||||
);
|
||||
assert!(info.synchronized, "daemon is not synchronized");
|
||||
|
||||
// Auto-detect resume point from chain state if not explicitly set.
|
||||
// If the chain has already advanced past fork boundaries, skip those
|
||||
// forks — the daemon will reject TXs built with outdated parameters.
|
||||
let resume_hf = if env_resume_hf > 0 {
|
||||
env_resume_hf
|
||||
} else {
|
||||
auto_resume_hf(info.height)
|
||||
};
|
||||
if resume_hf > 0 {
|
||||
println!(" Resuming from HF{}", resume_hf);
|
||||
println!(" Resuming from HF{} (chain already at height {})", resume_hf, info.height);
|
||||
}
|
||||
|
||||
let fixture = TestFixture::create();
|
||||
|
||||
+39
-14
@@ -1,20 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build libsalvium_crypto.so for Android (arm64, armv7, x86_64)
|
||||
# Build Salvium shared libraries for Android (arm64, armv7, x86_64)
|
||||
#
|
||||
# Produces:
|
||||
# prebuilt/android/arm64-v8a/libsalvium_crypto.so
|
||||
# prebuilt/android/arm64-v8a/libsalvium_ffi.so
|
||||
# prebuilt/android/armeabi-v7a/libsalvium_crypto.so
|
||||
# prebuilt/android/armeabi-v7a/libsalvium_ffi.so
|
||||
# prebuilt/android/x86_64/libsalvium_crypto.so
|
||||
# prebuilt/android/x86_64/libsalvium_ffi.so
|
||||
#
|
||||
# Prerequisites:
|
||||
# rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android
|
||||
# Set ANDROID_NDK_HOME to your NDK path (e.g. ~/Android/Sdk/ndk/26.1.10909125)
|
||||
#
|
||||
# Produces:
|
||||
# prebuilt/android/arm64-v8a/libsalvium_crypto.so
|
||||
# prebuilt/android/armeabi-v7a/libsalvium_crypto.so
|
||||
# prebuilt/android/x86_64/libsalvium_crypto.so
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT_DIR="$SCRIPT_DIR/.."
|
||||
CRATE_DIR="$ROOT_DIR/crates/salvium-crypto"
|
||||
OUT_DIR="$ROOT_DIR/prebuilt/android"
|
||||
|
||||
if [ -z "${ANDROID_NDK_HOME:-}" ]; then
|
||||
@@ -40,38 +42,61 @@ declare -A TARGET_CC=(
|
||||
# Find the NDK toolchain bin directory
|
||||
TOOLCHAIN="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin"
|
||||
if [ ! -d "$TOOLCHAIN" ]; then
|
||||
# Try macOS path
|
||||
TOOLCHAIN="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin"
|
||||
fi
|
||||
if [ ! -d "$TOOLCHAIN" ]; then
|
||||
TOOLCHAIN="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/darwin-aarch64/bin"
|
||||
fi
|
||||
if [ ! -d "$TOOLCHAIN" ]; then
|
||||
echo "Error: Cannot find NDK toolchain in $ANDROID_NDK_HOME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> Building salvium-crypto for Android targets..."
|
||||
# Crates to build (order matters: ffi depends on crypto, but cargo handles that)
|
||||
CRATES=("salvium-crypto" "salvium-ffi")
|
||||
|
||||
echo "==> Building Salvium libraries for Android"
|
||||
echo " NDK: $ANDROID_NDK_HOME"
|
||||
echo " Crates: ${CRATES[*]}"
|
||||
echo ""
|
||||
|
||||
for target in "${!TARGET_ABI[@]}"; do
|
||||
abi="${TARGET_ABI[$target]}"
|
||||
cc="${TARGET_CC[$target]}"
|
||||
echo " -> $target ($abi)"
|
||||
echo " ── $target ($abi) ──"
|
||||
|
||||
# Set linker for this target via CARGO_TARGET env vars
|
||||
# Set linker/compiler for this target via CARGO_TARGET env vars
|
||||
target_upper="${target//-/_}"
|
||||
target_upper="${target_upper^^}"
|
||||
export "CARGO_TARGET_${target_upper}_LINKER=$TOOLCHAIN/$cc"
|
||||
export "CC_${target//-/_}=$TOOLCHAIN/$cc"
|
||||
export "AR_${target//-/_}=$TOOLCHAIN/llvm-ar"
|
||||
|
||||
cargo build --release --target "$target" --manifest-path "$CRATE_DIR/Cargo.toml"
|
||||
for crate in "${CRATES[@]}"; do
|
||||
echo " Building $crate..."
|
||||
cargo build --release \
|
||||
--target "$target" \
|
||||
-p "$crate" \
|
||||
--manifest-path "$ROOT_DIR/Cargo.toml"
|
||||
done
|
||||
|
||||
# Copy built .so files to prebuilt directory
|
||||
mkdir -p "$OUT_DIR/$abi"
|
||||
cp "$CRATE_DIR/target/$target/release/libsalvium_crypto.so" \
|
||||
|
||||
# salvium-crypto -> libsalvium_crypto.so
|
||||
cp "$ROOT_DIR/target/$target/release/libsalvium_crypto.so" \
|
||||
"$OUT_DIR/$abi/libsalvium_crypto.so"
|
||||
|
||||
# salvium-ffi -> libsalvium_ffi.so
|
||||
cp "$ROOT_DIR/target/$target/release/libsalvium_ffi.so" \
|
||||
"$OUT_DIR/$abi/libsalvium_ffi.so"
|
||||
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo "==> Done. Libraries:"
|
||||
for target in "${!TARGET_ABI[@]}"; do
|
||||
abi="${TARGET_ABI[$target]}"
|
||||
ls -lh "$OUT_DIR/$abi/libsalvium_crypto.so"
|
||||
echo " $abi:"
|
||||
ls -lh "$OUT_DIR/$abi/"*.so
|
||||
done
|
||||
|
||||
+48
-24
@@ -1,16 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build libsalvium_crypto.a for iOS (device + simulator) as an xcframework.
|
||||
# Build Salvium static libraries for iOS (device + simulator) as xcframeworks.
|
||||
#
|
||||
# Produces:
|
||||
# prebuilt/ios/SalviumCrypto.xcframework
|
||||
# prebuilt/ios/SalviumFfi.xcframework
|
||||
#
|
||||
# Prerequisites:
|
||||
# rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios
|
||||
#
|
||||
# Produces: prebuilt/ios/SalviumCrypto.xcframework
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT_DIR="$SCRIPT_DIR/.."
|
||||
CRATE_DIR="$ROOT_DIR/crates/salvium-crypto"
|
||||
OUT_DIR="$ROOT_DIR/prebuilt/ios"
|
||||
WORK_DIR="$OUT_DIR/build"
|
||||
|
||||
@@ -20,37 +21,60 @@ TARGETS=(
|
||||
x86_64-apple-ios # Simulator (Intel)
|
||||
)
|
||||
|
||||
echo "==> Building salvium-crypto for iOS targets..."
|
||||
# Libraries to build and package
|
||||
declare -A LIB_NAMES=(
|
||||
[salvium-crypto]="libsalvium_crypto"
|
||||
[salvium-ffi]="libsalvium_ffi"
|
||||
)
|
||||
|
||||
declare -A FRAMEWORK_NAMES=(
|
||||
[salvium-crypto]="SalviumCrypto"
|
||||
[salvium-ffi]="SalviumFfi"
|
||||
)
|
||||
|
||||
echo "==> Building Salvium libraries for iOS targets..."
|
||||
|
||||
for target in "${TARGETS[@]}"; do
|
||||
echo " -> $target"
|
||||
cargo build --release --target "$target" --manifest-path "$CRATE_DIR/Cargo.toml"
|
||||
for crate in "${!LIB_NAMES[@]}"; do
|
||||
cargo build --release \
|
||||
--target "$target" \
|
||||
-p "$crate" \
|
||||
--manifest-path "$ROOT_DIR/Cargo.toml"
|
||||
done
|
||||
done
|
||||
|
||||
echo "==> Creating xcframework..."
|
||||
echo "==> Creating xcframeworks..."
|
||||
mkdir -p "$WORK_DIR"
|
||||
|
||||
# Device .a (single arch, no lipo needed)
|
||||
DEVICE_LIB="$CRATE_DIR/target/aarch64-apple-ios/release/libsalvium_crypto.a"
|
||||
for crate in "${!LIB_NAMES[@]}"; do
|
||||
lib="${LIB_NAMES[$crate]}"
|
||||
framework="${FRAMEWORK_NAMES[$crate]}"
|
||||
|
||||
# Merge simulator slices (aarch64-sim + x86_64) into one fat .a
|
||||
SIM_FAT="$WORK_DIR/libsalvium_crypto-sim.a"
|
||||
lipo -create \
|
||||
"$CRATE_DIR/target/aarch64-apple-ios-sim/release/libsalvium_crypto.a" \
|
||||
"$CRATE_DIR/target/x86_64-apple-ios/release/libsalvium_crypto.a" \
|
||||
-output "$SIM_FAT"
|
||||
echo " -> $framework.xcframework"
|
||||
|
||||
# Remove old xcframework if present
|
||||
rm -rf "$OUT_DIR/SalviumCrypto.xcframework"
|
||||
# Device .a (single arch, no lipo needed)
|
||||
DEVICE_LIB="$ROOT_DIR/target/aarch64-apple-ios/release/${lib}.a"
|
||||
|
||||
# Create xcframework from device .a + simulator fat .a
|
||||
xcodebuild -create-xcframework \
|
||||
-library "$DEVICE_LIB" \
|
||||
-library "$SIM_FAT" \
|
||||
-output "$OUT_DIR/SalviumCrypto.xcframework"
|
||||
# Merge simulator slices (aarch64-sim + x86_64) into one fat .a
|
||||
SIM_FAT="$WORK_DIR/${lib}-sim.a"
|
||||
lipo -create \
|
||||
"$ROOT_DIR/target/aarch64-apple-ios-sim/release/${lib}.a" \
|
||||
"$ROOT_DIR/target/x86_64-apple-ios/release/${lib}.a" \
|
||||
-output "$SIM_FAT"
|
||||
|
||||
# Remove old xcframework if present
|
||||
rm -rf "$OUT_DIR/$framework.xcframework"
|
||||
|
||||
# Create xcframework from device .a + simulator fat .a
|
||||
xcodebuild -create-xcframework \
|
||||
-library "$DEVICE_LIB" \
|
||||
-library "$SIM_FAT" \
|
||||
-output "$OUT_DIR/$framework.xcframework"
|
||||
done
|
||||
|
||||
# Clean up work dir
|
||||
rm -rf "$WORK_DIR"
|
||||
|
||||
echo "==> Done: $OUT_DIR/SalviumCrypto.xcframework"
|
||||
|
||||
echo "==> Done:"
|
||||
ls -d "$OUT_DIR"/*.xcframework
|
||||
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build Salvium shared libraries for Linux (x86_64)
|
||||
#
|
||||
# Produces:
|
||||
# prebuilt/linux-x86_64/libsalvium_crypto.so
|
||||
# prebuilt/linux-x86_64/libsalvium_ffi.so
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT_DIR="$SCRIPT_DIR/.."
|
||||
OUT_DIR="$ROOT_DIR/prebuilt/linux-x86_64"
|
||||
|
||||
echo "==> Building Salvium libraries for Linux x86_64..."
|
||||
|
||||
cargo build --release \
|
||||
-p salvium-crypto \
|
||||
-p salvium-ffi \
|
||||
--manifest-path "$ROOT_DIR/Cargo.toml"
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
cp "$ROOT_DIR/target/release/libsalvium_crypto.so" "$OUT_DIR/"
|
||||
cp "$ROOT_DIR/target/release/libsalvium_ffi.so" "$OUT_DIR/"
|
||||
|
||||
echo "==> Done:"
|
||||
ls -lh "$OUT_DIR/"*.so
|
||||
Executable
+50
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build Salvium shared/static libraries for macOS (arm64 + x86_64)
|
||||
#
|
||||
# Produces:
|
||||
# prebuilt/macos/libsalvium_crypto.dylib (universal)
|
||||
# prebuilt/macos/libsalvium_ffi.dylib (universal)
|
||||
# prebuilt/macos/libsalvium_crypto.a (universal)
|
||||
# prebuilt/macos/libsalvium_ffi.a (universal)
|
||||
#
|
||||
# Prerequisites:
|
||||
# rustup target add aarch64-apple-darwin x86_64-apple-darwin
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT_DIR="$SCRIPT_DIR/.."
|
||||
OUT_DIR="$ROOT_DIR/prebuilt/macos"
|
||||
|
||||
TARGETS=(aarch64-apple-darwin x86_64-apple-darwin)
|
||||
|
||||
echo "==> Building Salvium libraries for macOS..."
|
||||
|
||||
for target in "${TARGETS[@]}"; do
|
||||
echo " -> $target"
|
||||
cargo build --release \
|
||||
--target "$target" \
|
||||
-p salvium-crypto \
|
||||
-p salvium-ffi \
|
||||
--manifest-path "$ROOT_DIR/Cargo.toml"
|
||||
done
|
||||
|
||||
echo "==> Creating universal binaries..."
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
for lib in libsalvium_crypto libsalvium_ffi; do
|
||||
# Universal dylib
|
||||
lipo -create \
|
||||
"$ROOT_DIR/target/aarch64-apple-darwin/release/${lib}.dylib" \
|
||||
"$ROOT_DIR/target/x86_64-apple-darwin/release/${lib}.dylib" \
|
||||
-output "$OUT_DIR/${lib}.dylib"
|
||||
|
||||
# Universal static lib
|
||||
lipo -create \
|
||||
"$ROOT_DIR/target/aarch64-apple-darwin/release/${lib}.a" \
|
||||
"$ROOT_DIR/target/x86_64-apple-darwin/release/${lib}.a" \
|
||||
-output "$OUT_DIR/${lib}.a"
|
||||
done
|
||||
|
||||
echo "==> Done:"
|
||||
ls -lh "$OUT_DIR/"*
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build Salvium shared libraries for Windows (x86_64, cross-compile from Linux)
|
||||
#
|
||||
# Produces:
|
||||
# prebuilt/windows-x86_64/salvium_crypto.dll
|
||||
# prebuilt/windows-x86_64/salvium_ffi.dll
|
||||
#
|
||||
# Prerequisites:
|
||||
# rustup target add x86_64-pc-windows-gnu
|
||||
# apt install gcc-mingw-w64-x86-64 (or equivalent)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT_DIR="$SCRIPT_DIR/.."
|
||||
OUT_DIR="$ROOT_DIR/prebuilt/windows-x86_64"
|
||||
|
||||
echo "==> Building Salvium libraries for Windows x86_64..."
|
||||
|
||||
cargo build --release \
|
||||
--target x86_64-pc-windows-gnu \
|
||||
-p salvium-crypto \
|
||||
-p salvium-ffi \
|
||||
--manifest-path "$ROOT_DIR/Cargo.toml"
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
cp "$ROOT_DIR/target/x86_64-pc-windows-gnu/release/salvium_crypto.dll" "$OUT_DIR/"
|
||||
cp "$ROOT_DIR/target/x86_64-pc-windows-gnu/release/salvium_ffi.dll" "$OUT_DIR/"
|
||||
|
||||
echo "==> Done:"
|
||||
ls -lh "$OUT_DIR/"*.dll
|
||||
Reference in New Issue
Block a user