diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..68ce6ba --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,176 @@ +name: "Build Packages" + +"on": + release: + types: [created] + workflow_dispatch: + inputs: + publish: + description: "Publish packages" + required: true + default: "false" + +jobs: + build-manylinux: + name: Build Library (Manylinux) + + strategy: + matrix: + include: + - os: ubuntu-latest + lib: libindy_credx.so + container: andrewwhitehead/manylinux2014-base + + container: ${{ matrix.container }} + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + + - name: Cache cargo resources + uses: Swatinem/rust-cache@v1 + + - name: Build library + env: + BUILD_FEATURES: vendored + BUILD_TARGET: ${{ matrix.target }} + run: sh ./build.sh + + - name: Upload library artifacts + uses: actions/upload-artifact@v2 + with: + name: library-${{ runner.os }} + path: target/release/${{ matrix.lib }} + + build-other: + name: Build Library (MacOS/Win) + + strategy: + matrix: + include: + - os: macos-latest # macos-11.0 for universal + lib: libindy_credx.dylib + # target: apple-darwin + toolchain: stable # beta for universal + - os: windows-latest + lib: indy_credx.dll + toolchain: stable + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.toolchain }} + + - name: Cache cargo resources + uses: Swatinem/rust-cache@v1 + + # pre-build so that openssl dependency is cached, otherwise it will complain: + # "This perl implementation doesn't produce Windows like paths" + - if: "runner.os == 'Windows'" + name: Pre-build (Windows) + uses: actions-rs/cargo@v1 + env: + OPENSSL_STATIC: 1 + with: + command: build + args: --release --manifest-path indy-credx/Cargo.toml --features vendored + + - name: Build library + env: + BUILD_FEATURES: vendored + BUILD_TARGET: ${{ matrix.target }} + BUILD_TOOLCHAIN: ${{ matrix.toolchain }} + OPENSSL_STATIC: 1 + run: sh ./build.sh + + - name: Upload library artifacts + uses: actions/upload-artifact@v2 + with: + name: library-${{ runner.os }} + path: target/release/${{ matrix.lib }} + + build-py: + name: Build Python + needs: [build-manylinux, build-other] + + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + python-version: [3.6] + include: + - os: ubuntu-latest + plat-name: manylinux2014_x86_64 + - os: macos-latest + plat-name: macosx_10_9_x86_64 # macosx_10_9_universal2 + - os: windows-latest + plat-name: win_amd64 + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine auditwheel + + - name: Fetch library artifacts + uses: actions/download-artifact@v2 + with: + name: library-${{ runner.os }} + path: wrappers/python/indy_credx/ + + - name: Build python package + run: | + python setup.py bdist_wheel --python-tag=py3 --plat-name=${{ matrix.plat-name }} + working-directory: wrappers/python + + - name: Test python package + shell: sh + run: | + cd wrappers/python + pip install --upgrade pip + pip install dist/* + python -m demo.test + + - if: "runner.os == 'Linux'" + name: Auditwheel + run: auditwheel show wrappers/python/dist/* + + - name: Upload python package + uses: actions/upload-artifact@v2 + with: + name: python-${{ runner.os }} + path: wrappers/python/dist/* + + - if: | + (github.event_name == 'release' || + (github.event_name == 'workflow_dispatch' && + github.event.inputs.publish == 'true')) + name: Publish python package + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + twine upload --skip-existing wrappers/python/dist/* diff --git a/.github/workflows/rusttest.yml b/.github/workflows/rusttest.yml index 02b23d6..dd2b7f4 100644 --- a/.github/workflows/rusttest.yml +++ b/.github/workflows/rusttest.yml @@ -1,17 +1,17 @@ -name: "Unit Tests" +name: "Run Tests" on: push: - branches: - - main + branches: [main] pull_request: - branches: - - main + branches: [main] jobs: lints: name: Lints + runs-on: ubuntu-latest + steps: - name: Checkout sources uses: actions/checkout@v2 @@ -21,23 +21,31 @@ jobs: with: profile: minimal toolchain: stable - override: true - components: rustfmt - - name: Run cargo fmt + - name: Cache cargo resources + uses: Swatinem/rust-cache@v1 + + - name: Cargo check uses: actions-rs/cargo@v1 with: - command: fmt - args: --all -- --check + command: check - - name: Run cargo check + - name: Cargo fmt uses: actions-rs/cargo@v1 with: - command: check + command: fmt + args: --all -- --check test: name: Test Suite - runs-on: ubuntu-latest + needs: [lints] + + strategy: + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + + runs-on: ${{ matrix.os }} + steps: - name: Checkout sources uses: actions/checkout@v2 @@ -47,21 +55,36 @@ jobs: with: profile: minimal toolchain: stable - override: true - - name: Test default features + - name: Cache cargo resources + uses: Swatinem/rust-cache@v1 + + - name: Debug build + uses: actions-rs/cargo@v1 + with: + command: build + args: --manifest-path indy-credx/Cargo.toml --features vendored + + - name: Test indy-utils uses: actions-rs/cargo@v1 with: command: test + args: --manifest-path indy-utils/Cargo.toml - - name: Test CL + - name: Test indy-data-types (CL) uses: actions-rs/cargo@v1 with: command: test args: --manifest-path indy-data-types/Cargo.toml --features cl - - name: Test CL-native + - name: Test indy-data-types (CL-native) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path indy-data-types/Cargo.toml --features cl_native,vendored + + - name: Test indy-credx (vendored) uses: actions-rs/cargo@v1 with: command: test - args: --manifest-path indy-data-types/Cargo.toml --features cl_native + args: --manifest-path indy-credx/Cargo.toml --features vendored diff --git a/.gitignore b/.gitignore index a9d37c5..8094f6e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +.vscode target Cargo.lock diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..8955f32 --- /dev/null +++ b/build.sh @@ -0,0 +1,96 @@ +#!/bin/sh + +# NOTE: +# MacOS universal build currently requires MacOS 11 (Big Sur) for the appropriate SDK, +# and `sudo xcode-select --install` must be run to install the command line utilities. +# Rust's `beta` channel must be installed because aarch64 is still a tier-2 target: +# `rustup toolchain install beta`. +# The build command becomes `BUILD_TARGET=apple-darwin BUILD_TOOLCHAIN=beta ./build.sh` + +RUSTUP=${RUSTUP:-`command -v rustup`} +PROJECT=indy-credx +LIB_NAME=indy_credx +FEATURES="${BUILD_FEATURES:-default}" + +if [ ! -x "$RUSTUP" ]; then + echo "rustup command not found: it can be obtained from https://rustup.rs/" + exit 1 +fi + +TOOLCHAIN=`$RUSTUP default` +TARGET="${BUILD_TARGET-}" + +if [ -z "$BUILD_TOOLCHAIN" ]; then + BUILD_TOOLCHAIN=${TOOLCHAIN%%-*} + if [ -z "$BUILD_TOOLCHAIN" ]; then + echo "Error: Could not determine default Rust toolchain" + exit 1 + fi +fi + +MACOS_UNIVERSAL_TARGETS="aarch64-apple-darwin x86_64-apple-darwin" + +# Fail on any execution errors +set -e + +if [ "$TARGET" = "apple-darwin" ]; then + # MacOS universal build + INSTALLED_TARGETS=`$RUSTUP +$BUILD_TOOLCHAIN target list --installed` + # Install target(s) as needed + echo "Checking install targets for MacOS universal build .." + for target in $MACOS_UNIVERSAL_TARGETS; do + if ! `echo "$INSTALLED_TARGETS" | grep -q $target`; then + $RUSTUP +$BUILD_TOOLCHAIN target add $target + fi + done +elif [ -z "$TARGET" ]; then + case "$TOOLCHAIN" in + *apple-darwin*) + # Check if the required targets for a universal build are installed + INSTALLED_TARGETS=`$RUSTUP +$BUILD_TOOLCHAIN target list --installed` + TARGET="apple-darwin" + for target in $MACOS_UNIVERSAL_TARGETS; do + if ! `echo "$INSTALLED_TARGETS" | grep -q $target`; then + TARGET= + break + fi + done + if [ "$TARGET" = "apple-darwin" ]; then + echo "Automatically enabled MacOS universal build" + else + echo "Universal MacOS build not enabled" + fi + esac +fi + +if [ "$TARGET" = "apple-darwin" ]; then + MAJOR_VER=`sw_vers | grep ProductVersion | cut -f 2 | cut -f 1 -d .` + if [ "$MAJOR_VER" -lt 11 ]; then + echo "MacOS universal build requires OS 11 (Big Sur) or newer" + TARGET= + fi +fi + +if [ "$TARGET" = "apple-darwin" ]; then + # Build both targets and combine them into a universal library with `lipo` + TARGET_LIBS= + for target in $MACOS_UNIVERSAL_TARGETS; do + echo "Building $PROJECT for toolchain '$BUILD_TOOLCHAIN', target '$target'.." + $RUSTUP run $BUILD_TOOLCHAIN cargo build --manifest-path indy-credx/Cargo.toml --release --features $FEATURES --target $target + TARGET_LIBS="./target/$target/release/lib${LIB_NAME}.dylib $TARGET_LIBS" + done + + mkdir -p ./target/release + OUTPUT="./target/release/lib${LIB_NAME}.dylib" + echo "Combining targets into universal library" + lipo -create -output $OUTPUT $TARGET_LIBS +else + # Build normal target + echo "Building $PROJECT for toolchain '$BUILD_TOOLCHAIN'.." + CMD="$RUSTUP run $BUILD_TOOLCHAIN cargo build --manifest-path indy-credx/Cargo.toml --release --features $FEATURES" + if [ -n "$TARGET" ]; then + $CMD --target "$TARGET" + else + $CMD + fi +fi diff --git a/indy-credx/Cargo.toml b/indy-credx/Cargo.toml index f7203b7..b08b864 100644 --- a/indy-credx/Cargo.toml +++ b/indy-credx/Cargo.toml @@ -19,6 +19,7 @@ crate-type = ["staticlib", "rlib", "cdylib"] default = ["ffi"] ffi = ["ffi-support", "logger", "zeroize"] logger = ["env_logger"] +vendored = ["indy-data-types/vendored"] [dependencies] env_logger = { version = "0.7.1", optional = true } diff --git a/indy-data-types/Cargo.toml b/indy-data-types/Cargo.toml index 4550987..7f5a58e 100644 --- a/indy-data-types/Cargo.toml +++ b/indy-data-types/Cargo.toml @@ -23,8 +23,10 @@ hash = ["indy-utils/hash"] merkle_tree = ["hash"] rich_schema = [] serde_support = ["indy-utils/serde_support", "serde", "serde_json"] +vendored = ["openssl", "openssl/vendored"] [dependencies] +openssl = { version = "0.10", optional = true } once_cell = "1.4" regex = "1.3" serde = { version = "1.0", optional = true, features = ["derive"] } diff --git a/wrappers/python/demo/test.py b/wrappers/python/demo/test.py index 9ff2ff8..b0d4aae 100644 --- a/wrappers/python/demo/test.py +++ b/wrappers/python/demo/test.py @@ -20,12 +20,20 @@ test_did = "55GkHamhTU1ZbTbV2ab9DE" schema = Schema.create(test_did, "schema name", "schema version", ["attr"], seq_no=15) -print(schema.to_dict()) +assert schema.to_dict() == { + "ver": "1.0", + "id": f"{test_did}:2:schema name:schema version", + "name": "schema name", + "version": "schema version", + "attrNames": ["attr"], + "seqNo": 15, +} + cred_def, cred_def_pvt, key_proof = CredentialDefinition.create( test_did, schema, "CL", tag="tag", support_revocation=True ) -print(cred_def) +assert cred_def.id == f"{test_did}:3:CL:15:tag" ( rev_reg_def, @@ -33,20 +41,20 @@ rev_reg, rev_reg_init_delta, ) = RevocationRegistryDefinition.create(test_did, cred_def, "default", "CL_ACCUM", 100) -print("Tails file hash:", rev_reg_def.tails_hash) +# print("Tails file hash:", rev_reg_def.tails_hash) master_secret = MasterSecret.create() master_secret_id = "my id" cred_offer = CredentialOffer.create(schema.id, cred_def, key_proof) -print("Credential offer:") -print(cred_offer.to_json()) +# print("Credential offer:") +# print(cred_offer.to_json()) cred_req, cred_req_metadata = CredentialRequest.create( test_did, cred_def, master_secret, master_secret_id, cred_offer ) -print("Credential request:") -print(cred_req.to_json()) +# print("Credential request:") +# print(cred_req.to_json()) issuer_rev_index = 1 @@ -66,12 +74,12 @@ rev_reg_def.tails_location, ), ) -print("Issued credential:") -print(cred.to_json()) +# print("Issued credential:") +# print(cred.to_json()) cred_received = cred.process(cred_req_metadata, master_secret, cred_def, rev_reg_def) -print("Processed credential:") -print(cred_received.to_json()) +# print("Processed credential:") +# print(cred_received.to_json()) timestamp = int(time()) @@ -111,16 +119,14 @@ ) # print(presentation.to_json()) -print( - "Verified:", - presentation.verify( - pres_req, - [schema], - [cred_def], - [rev_reg_def], - {rev_reg_def.id: {timestamp: rev_reg}}, - ), +verified = presentation.verify( + pres_req, + [schema], + [cred_def], + [rev_reg_def], + {rev_reg_def.id: {timestamp: rev_reg}}, ) +assert verified # rev_delta_2 = rev_reg.revoke_credential( @@ -142,13 +148,13 @@ pres_req, present_creds, {}, master_secret, [schema], [cred_def] ) -print( - "Verified:", - presentation.verify( - pres_req, - [schema], - [cred_def], - [rev_reg_def], - {rev_reg_def.id: {timestamp: rev_reg}}, - ), +verified = presentation.verify( + pres_req, + [schema], + [cred_def], + [rev_reg_def], + {rev_reg_def.id: {timestamp: rev_reg}}, ) +assert not verified + +print("ok")