diff --git a/.github/workflows/deploy-release.yml b/.github/workflows/deploy-release.yml index 6a40563e..df6137ae 100644 --- a/.github/workflows/deploy-release.yml +++ b/.github/workflows/deploy-release.yml @@ -87,7 +87,7 @@ jobs: - name: Upload Artifacts if: ${{ always() }} uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce - timeout-minutes: 2 + timeout-minutes: 3 with: name: Release binaries path: ~/artifacts diff --git a/.github/workflows/deploy-snapshot.yml b/.github/workflows/deploy-snapshot.yml index 69a9ef87..bdd984da 100644 --- a/.github/workflows/deploy-snapshot.yml +++ b/.github/workflows/deploy-snapshot.yml @@ -94,7 +94,7 @@ jobs: - name: Upload Artifacts if: ${{ always() }} uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce - timeout-minutes: 2 + timeout-minutes: 3 with: name: Snapshot binaries path: ~/artifacts diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 1bd60cab..08a89a4c 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -1,9 +1,11 @@ # Expected secrets # EMULATOR_WTF_API_KEY - Optional API key for emulator.wtf -# FIREBASE_TEST_LAB_PROJECT - Firebase Test Lab project name # FIREBASE_TEST_LAB_SERVICE_ACCOUNT - Email address of Firebase Test Lab service account # FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER - Workload identity provider to generate temporary service account key +# Expected variables +# FIREBASE_TEST_LAB_PROJECT - Firebase Test Lab project name + name: Pull Request on: @@ -45,7 +47,7 @@ jobs: uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - name: Setup id: setup - timeout-minutes: 30 + timeout-minutes: 35 uses: ./.github/actions/setup check_firebase_secrets: @@ -55,7 +57,7 @@ jobs: steps: - id: check_firebase_secrets env: - FIREBASE_TEST_LAB_PROJECT: ${{ secrets.FIREBASE_TEST_LAB_PROJECT }} + FIREBASE_TEST_LAB_PROJECT: ${{ vars.FIREBASE_TEST_LAB_PROJECT }} FIREBASE_TEST_LAB_SERVICE_ACCOUNT: ${{ secrets.FIREBASE_TEST_LAB_SERVICE_ACCOUNT }} FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER }} if: "${{ env.FIREBASE_TEST_LAB_PROJECT != '' && env.FIREBASE_TEST_LAB_SERVICE_ACCOUNT != '' && env.FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER != '' }}" @@ -72,6 +74,24 @@ jobs: if: "${{ env.EMULATOR_WTF_API_KEY != '' }}" run: echo "defined=true" >> $GITHUB_OUTPUT + check_properties: + needs: prime_cache + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + timeout-minutes: 1 + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + - name: Setup + id: setup + timeout-minutes: 5 + uses: ./.github/actions/setup + - name: Check properties + timeout-minutes: 4 + run: | + ./gradlew checkProperties + static_analysis_detekt: needs: prime_cache runs-on: ubuntu-latest @@ -239,7 +259,7 @@ jobs: uses: google-github-actions/auth@ef5d53e30bbcd8d0836f4288f5e50ff3e086997d with: create_credentials_file: true - project_id: ${{ secrets.FIREBASE_TEST_LAB_PROJECT }} + project_id: ${{ vars.FIREBASE_TEST_LAB_PROJECT }} service_account: ${{ secrets.FIREBASE_TEST_LAB_SERVICE_ACCOUNT }} workload_identity_provider: ${{ secrets.FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER }} access_token_lifetime: '1200s' @@ -247,7 +267,7 @@ jobs: timeout-minutes: 30 env: # This first environment variable is used by Flank, since the temporary token is missing the project name - GOOGLE_CLOUD_PROJECT: ${{ secrets.FIREBASE_TEST_LAB_PROJECT }} + GOOGLE_CLOUD_PROJECT: ${{ vars.FIREBASE_TEST_LAB_PROJECT }} ORG_GRADLE_PROJECT_ZCASH_FIREBASE_TEST_LAB_API_KEY_PATH: ${{ steps.auth_test_lab.outputs.credentials_file_path }} # Because Fulladle doesn't allow Test Orchestrator to be enabled/disabled for a specific submodule, it must be enabled for all modules ORG_GRADLE_PROJECT_IS_USE_TEST_ORCHESTRATOR: true @@ -377,7 +397,7 @@ jobs: uses: google-github-actions/auth@ef5d53e30bbcd8d0836f4288f5e50ff3e086997d with: create_credentials_file: true - project_id: ${{ secrets.FIREBASE_TEST_LAB_PROJECT }} + project_id: ${{ vars.FIREBASE_TEST_LAB_PROJECT }} service_account: ${{ secrets.FIREBASE_TEST_LAB_SERVICE_ACCOUNT }} workload_identity_provider: ${{ secrets.FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER }} access_token_lifetime: '900s' @@ -391,7 +411,7 @@ jobs: # Path depends on `release_build` job, plus path of `Download a single artifact` step BINARIES_ZIP_PATH: binaries.zip # This first environment variable is used by Flank, since the temporary token is missing the project name - GOOGLE_CLOUD_PROJECT: ${{ secrets.FIREBASE_TEST_LAB_PROJECT }} + GOOGLE_CLOUD_PROJECT: ${{ vars.FIREBASE_TEST_LAB_PROJECT }} ORG_GRADLE_PROJECT_ZCASH_FIREBASE_TEST_LAB_API_KEY_PATH: ${{ steps.auth_test_lab.outputs.credentials_file_path }} run: | unzip ${BINARIES_ZIP_PATH} diff --git a/CHANGELOG.md b/CHANGELOG.md index aa407ba7..c9fe7e7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,19 @@ Change Log Unreleased ------------------------------------ -- The SDK's internal networking has been refactored to a separate Gradle module (and therefore a separate artifact) which is a transitive dependency of the Zcash Android SDK. +- The SDK's internal networking has been refactored to a separate Gradle module `lightwallet-client-lib` (and + therefore a separate artifact) which is a transitive dependency of the Zcash Android SDK. - The `z.cash.ecc.android.sdk.model.LightWalletEndpoint` class has been moved to `co.electriccoin.lightwallet.client.model.LightWalletEndpoint` + - The new networking module now provides a `BlockingLightWalletClient` for blocking calls and a + `CoroutineLightWalletClient` for asynchronous calls. + - Most unary calls respond with the new `Response` class and its subclasses. Streaming calls will be updated + with the Response class later. -Unreleased ------------------------------------- +## 1.12.0-beta01 +### Changed + - `TransactionOverview`, `Transaction.Sent`, and `Transaction.Received` have `minedHeight` as a nullable field now. This fixes a potential crash when fetching transactions when a transaction is in the mempool +## 1.11.0-beta01 ### Added - `cash.z.ecc.android.sdk`: - `Synchronizer.getUnifiedAddress` diff --git a/build-conventions/build.gradle.kts b/build-conventions/build.gradle.kts index ebd23922..245699f4 100644 --- a/build-conventions/build.gradle.kts +++ b/build-conventions/build.gradle.kts @@ -36,12 +36,9 @@ dependencyLocking { } } -// Per conversation in the KotlinLang Slack, Gradle uses Java 8 compatibility internally -// for all build scripts. -// https://kotlinlang.slack.com/archives/C19FD9681/p1636632870122900?thread_ts=1636572288.117000&cid=C19FD9681 java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } dependencies { diff --git a/build-conventions/gradle.lockfile b/build-conventions/gradle.lockfile index ad37b3ca..3a5317c3 100644 --- a/build-conventions/gradle.lockfile +++ b/build-conventions/gradle.lockfile @@ -1,50 +1,51 @@ # This is a Gradle generated file for dependency locking. # Manual edits can break the build and are not advised. # This file is expected to be part of source control. -androidx.databinding:databinding-common:7.3.0=runtimeClasspath -androidx.databinding:databinding-compiler-common:7.3.0=runtimeClasspath -com.android.databinding:baseLibrary:7.3.0=runtimeClasspath -com.android.tools.analytics-library:crash:30.3.0=runtimeClasspath -com.android.tools.analytics-library:protos:30.3.0=runtimeClasspath -com.android.tools.analytics-library:shared:30.3.0=runtimeClasspath -com.android.tools.analytics-library:tracker:30.3.0=runtimeClasspath +androidx.databinding:databinding-common:7.4.0=runtimeClasspath +androidx.databinding:databinding-compiler-common:7.4.0=runtimeClasspath +com.android.databinding:baseLibrary:7.4.0=runtimeClasspath +com.android.tools.analytics-library:crash:30.4.0=runtimeClasspath +com.android.tools.analytics-library:protos:30.4.0=runtimeClasspath +com.android.tools.analytics-library:shared:30.4.0=runtimeClasspath +com.android.tools.analytics-library:tracker:30.4.0=runtimeClasspath com.android.tools.build.jetifier:jetifier-core:1.0.0-beta10=runtimeClasspath com.android.tools.build.jetifier:jetifier-processor:1.0.0-beta10=runtimeClasspath -com.android.tools.build:aapt2-proto:7.3.0-8691043=runtimeClasspath -com.android.tools.build:aaptcompiler:7.3.0=runtimeClasspath -com.android.tools.build:apksig:7.3.0=compileClasspath,runtimeClasspath -com.android.tools.build:apkzlib:7.3.0=compileClasspath,runtimeClasspath -com.android.tools.build:builder-model:7.3.0=compileClasspath,runtimeClasspath -com.android.tools.build:builder-test-api:7.3.0=runtimeClasspath -com.android.tools.build:builder:7.3.0=compileClasspath,runtimeClasspath -com.android.tools.build:bundletool:1.9.0=runtimeClasspath -com.android.tools.build:gradle-api:7.3.0=compileClasspath,runtimeClasspath -com.android.tools.build:gradle:7.3.0=compileClasspath,runtimeClasspath -com.android.tools.build:manifest-merger:30.3.0=compileClasspath,runtimeClasspath +com.android.tools.build:aapt2-proto:7.4.0-8841542=runtimeClasspath +com.android.tools.build:aaptcompiler:7.4.0=runtimeClasspath +com.android.tools.build:apksig:7.4.0=compileClasspath,runtimeClasspath +com.android.tools.build:apkzlib:7.4.0=compileClasspath,runtimeClasspath +com.android.tools.build:builder-model:7.4.0=compileClasspath,runtimeClasspath +com.android.tools.build:builder-test-api:7.4.0=runtimeClasspath +com.android.tools.build:builder:7.4.0=compileClasspath,runtimeClasspath +com.android.tools.build:bundletool:1.11.4=runtimeClasspath +com.android.tools.build:gradle-api:7.4.0=compileClasspath,runtimeClasspath +com.android.tools.build:gradle-settings-api:7.4.0=runtimeClasspath +com.android.tools.build:gradle:7.4.0=compileClasspath,runtimeClasspath +com.android.tools.build:manifest-merger:30.4.0=compileClasspath,runtimeClasspath com.android.tools.build:transform-api:2.0.0-deprecated-use-gradle-api=runtimeClasspath -com.android.tools.ddms:ddmlib:30.3.0=runtimeClasspath -com.android.tools.layoutlib:layoutlib-api:30.3.0=runtimeClasspath -com.android.tools.lint:lint-model:30.3.0=runtimeClasspath -com.android.tools.lint:lint-typedef-remover:30.3.0=runtimeClasspath -com.android.tools.utp:android-device-provider-ddmlib-proto:30.3.0=runtimeClasspath -com.android.tools.utp:android-device-provider-gradle-proto:30.3.0=runtimeClasspath -com.android.tools.utp:android-test-plugin-host-additional-test-output-proto:30.3.0=runtimeClasspath -com.android.tools.utp:android-test-plugin-host-coverage-proto:30.3.0=runtimeClasspath -com.android.tools.utp:android-test-plugin-host-retention-proto:30.3.0=runtimeClasspath -com.android.tools.utp:android-test-plugin-result-listener-gradle-proto:30.3.0=runtimeClasspath -com.android.tools:annotations:30.3.0=runtimeClasspath -com.android.tools:common:30.3.0=runtimeClasspath -com.android.tools:dvlib:30.3.0=runtimeClasspath -com.android.tools:repository:30.3.0=runtimeClasspath -com.android.tools:sdk-common:30.3.0=runtimeClasspath -com.android.tools:sdklib:30.3.0=runtimeClasspath -com.android:signflinger:7.3.0=runtimeClasspath -com.android:zipflinger:7.3.0=compileClasspath,runtimeClasspath +com.android.tools.ddms:ddmlib:30.4.0=runtimeClasspath +com.android.tools.layoutlib:layoutlib-api:30.4.0=runtimeClasspath +com.android.tools.lint:lint-model:30.4.0=runtimeClasspath +com.android.tools.lint:lint-typedef-remover:30.4.0=runtimeClasspath +com.android.tools.utp:android-device-provider-ddmlib-proto:30.4.0=runtimeClasspath +com.android.tools.utp:android-device-provider-gradle-proto:30.4.0=runtimeClasspath +com.android.tools.utp:android-test-plugin-host-additional-test-output-proto:30.4.0=runtimeClasspath +com.android.tools.utp:android-test-plugin-host-coverage-proto:30.4.0=runtimeClasspath +com.android.tools.utp:android-test-plugin-host-retention-proto:30.4.0=runtimeClasspath +com.android.tools.utp:android-test-plugin-result-listener-gradle-proto:30.4.0=runtimeClasspath +com.android.tools:annotations:30.4.0=runtimeClasspath +com.android.tools:common:30.4.0=runtimeClasspath +com.android.tools:dvlib:30.4.0=runtimeClasspath +com.android.tools:repository:30.4.0=runtimeClasspath +com.android.tools:sdk-common:30.4.0=runtimeClasspath +com.android.tools:sdklib:30.4.0=runtimeClasspath +com.android:signflinger:7.4.0=runtimeClasspath +com.android:zipflinger:7.4.0=compileClasspath,runtimeClasspath com.google.android:annotations:4.1.1.4=runtimeClasspath com.google.api.grpc:proto-google-common-protos:2.0.1=runtimeClasspath com.google.auto.value:auto-value-annotations:1.6.2=runtimeClasspath com.google.code.findbugs:jsr305:3.0.2=runtimeClasspath -com.google.code.gson:gson:2.8.6=runtimeClasspath +com.google.code.gson:gson:2.8.9=runtimeClasspath com.google.crypto.tink:tink:1.3.0-rc2=runtimeClasspath com.google.dagger:dagger:2.28.3=runtimeClasspath com.google.errorprone:error_prone_annotations:2.4.0=runtimeClasspath @@ -56,8 +57,7 @@ com.google.j2objc:j2objc-annotations:1.3=runtimeClasspath com.google.jimfs:jimfs:1.1=runtimeClasspath com.google.protobuf:protobuf-java-util:3.17.2=runtimeClasspath com.google.protobuf:protobuf-java:3.17.2=runtimeClasspath -com.google.testing.platform:core-proto:0.0.8-alpha07=runtimeClasspath -com.googlecode.json-simple:json-simple:1.1=runtimeClasspath +com.google.testing.platform:core-proto:0.0.8-alpha08=runtimeClasspath com.googlecode.juniversalchardet:juniversalchardet:1.0.3=runtimeClasspath com.squareup:javapoet:1.10.0=runtimeClasspath com.squareup:javawriter:2.5.0=compileClasspath,runtimeClasspath @@ -86,7 +86,6 @@ io.netty:netty-handler:4.1.52.Final=runtimeClasspath io.netty:netty-resolver:4.1.52.Final=runtimeClasspath io.netty:netty-transport:4.1.52.Final=runtimeClasspath io.perfmark:perfmark-api:0.23.0=runtimeClasspath -it.unimi.dsi:fastutil:8.4.0=runtimeClasspath jakarta.activation:jakarta.activation-api:1.2.1=runtimeClasspath jakarta.xml.bind:jakarta.xml.bind-api:2.3.2=runtimeClasspath javax.annotation:javax.annotation-api:1.3.2=runtimeClasspath @@ -108,64 +107,59 @@ org.glassfish.jaxb:jaxb-runtime:2.3.2=runtimeClasspath org.glassfish.jaxb:txw2:2.3.2=runtimeClasspath org.jdom:jdom2:2.0.6=runtimeClasspath org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,runtimeClasspath -org.jetbrains.kotlin:kotlin-android-extensions:1.7.21=runtimeClasspath -org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.7.21=runtimeClasspath -org.jetbrains.kotlin:kotlin-build-common:1.7.21=runtimeClasspath +org.jetbrains.kotlin:kotlin-android-extensions:1.8.0=runtimeClasspath +org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.8.0=runtimeClasspath +org.jetbrains.kotlin:kotlin-build-common:1.8.0=runtimeClasspath org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.10=kotlinCompilerClasspath -org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.21=runtimeClasspath -org.jetbrains.kotlin:kotlin-compiler-runner:1.7.21=runtimeClasspath -org.jetbrains.kotlin:kotlin-daemon-client:1.7.21=runtimeClasspath +org.jetbrains.kotlin:kotlin-compiler-embeddable:1.8.0=runtimeClasspath +org.jetbrains.kotlin:kotlin-compiler-runner:1.8.0=runtimeClasspath +org.jetbrains.kotlin:kotlin-daemon-client:1.8.0=runtimeClasspath org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.10=kotlinCompilerClasspath -org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.21=runtimeClasspath +org.jetbrains.kotlin:kotlin-daemon-embeddable:1.8.0=runtimeClasspath org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.7.10=kotlinCompilerPluginClasspathMain -org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.7.21=compileClasspath,runtimeClasspath -org.jetbrains.kotlin:kotlin-gradle-plugin-idea-proto:1.7.21=runtimeClasspath -org.jetbrains.kotlin:kotlin-gradle-plugin-idea:1.7.21=runtimeClasspath +org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.8.0=compileClasspath,runtimeClasspath +org.jetbrains.kotlin:kotlin-gradle-plugin-idea-proto:1.8.0=runtimeClasspath +org.jetbrains.kotlin:kotlin-gradle-plugin-idea:1.8.0=runtimeClasspath org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.7.10=kotlinCompilerPluginClasspathMain -org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.7.21=compileClasspath,runtimeClasspath -org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.21=compileClasspath,runtimeClasspath -org.jetbrains.kotlin:kotlin-klib-commonizer-api:1.7.21=runtimeClasspath +org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.8.0=compileClasspath,runtimeClasspath +org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0=compileClasspath,runtimeClasspath +org.jetbrains.kotlin:kotlin-klib-commonizer-api:1.8.0=runtimeClasspath org.jetbrains.kotlin:kotlin-native-utils:1.7.10=kotlinCompilerPluginClasspathMain -org.jetbrains.kotlin:kotlin-native-utils:1.7.21=compileClasspath,runtimeClasspath +org.jetbrains.kotlin:kotlin-native-utils:1.8.0=compileClasspath,runtimeClasspath org.jetbrains.kotlin:kotlin-project-model:1.7.10=kotlinCompilerPluginClasspathMain -org.jetbrains.kotlin:kotlin-project-model:1.7.21=compileClasspath,runtimeClasspath -org.jetbrains.kotlin:kotlin-reflect:1.5.31=runtimeClasspath -org.jetbrains.kotlin:kotlin-reflect:1.7.10=compileClasspath,kotlinCompilerClasspath +org.jetbrains.kotlin:kotlin-project-model:1.8.0=compileClasspath,runtimeClasspath +org.jetbrains.kotlin:kotlin-reflect:1.7.10=compileClasspath,kotlinCompilerClasspath,runtimeClasspath org.jetbrains.kotlin:kotlin-sam-with-receiver:1.7.10=kotlinCompilerPluginClasspathMain org.jetbrains.kotlin:kotlin-script-runtime:1.7.10=kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain org.jetbrains.kotlin:kotlin-scripting-common:1.7.10=kotlinCompilerPluginClasspathMain -org.jetbrains.kotlin:kotlin-scripting-common:1.7.21=runtimeClasspath +org.jetbrains.kotlin:kotlin-scripting-common:1.8.0=runtimeClasspath org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.7.10=kotlinCompilerPluginClasspathMain -org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.7.21=runtimeClasspath +org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.8.0=runtimeClasspath org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.7.10=kotlinCompilerPluginClasspathMain -org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.7.21=runtimeClasspath +org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.8.0=runtimeClasspath org.jetbrains.kotlin:kotlin-scripting-jvm:1.7.10=kotlinCompilerPluginClasspathMain -org.jetbrains.kotlin:kotlin-scripting-jvm:1.7.21=runtimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-common:1.5.31=runtimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10=compileClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.31=runtimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.10=compileClasspath -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.31=runtimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10=compileClasspath -org.jetbrains.kotlin:kotlin-stdlib:1.5.31=runtimeClasspath -org.jetbrains.kotlin:kotlin-stdlib:1.7.10=compileClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain +org.jetbrains.kotlin:kotlin-scripting-jvm:1.8.0=runtimeClasspath +org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10=compileClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,runtimeClasspath +org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.10=compileClasspath,runtimeClasspath +org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10=compileClasspath,runtimeClasspath +org.jetbrains.kotlin:kotlin-stdlib:1.7.10=compileClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,runtimeClasspath org.jetbrains.kotlin:kotlin-tooling-core:1.7.10=kotlinCompilerPluginClasspathMain -org.jetbrains.kotlin:kotlin-tooling-core:1.7.21=compileClasspath,runtimeClasspath +org.jetbrains.kotlin:kotlin-tooling-core:1.8.0=compileClasspath,runtimeClasspath org.jetbrains.kotlin:kotlin-util-io:1.7.10=kotlinCompilerPluginClasspathMain -org.jetbrains.kotlin:kotlin-util-io:1.7.21=compileClasspath,runtimeClasspath -org.jetbrains.kotlin:kotlin-util-klib:1.7.21=runtimeClasspath +org.jetbrains.kotlin:kotlin-util-io:1.8.0=compileClasspath,runtimeClasspath +org.jetbrains.kotlin:kotlin-util-klib:1.8.0=runtimeClasspath org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.0=runtimeClasspath org.jetbrains:annotations:13.0=compileClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,runtimeClasspath org.json:json:20180813=runtimeClasspath org.jvnet.staxex:stax-ex:1.8.1=runtimeClasspath -org.ow2.asm:asm-analysis:9.1=runtimeClasspath -org.ow2.asm:asm-commons:9.1=runtimeClasspath -org.ow2.asm:asm-tree:9.1=runtimeClasspath -org.ow2.asm:asm-util:9.1=runtimeClasspath -org.ow2.asm:asm:9.1=compileClasspath,runtimeClasspath +org.ow2.asm:asm-analysis:9.2=runtimeClasspath +org.ow2.asm:asm-commons:9.2=runtimeClasspath +org.ow2.asm:asm-tree:9.2=runtimeClasspath +org.ow2.asm:asm-util:9.2=runtimeClasspath +org.ow2.asm:asm:9.2=compileClasspath,runtimeClasspath org.slf4j:slf4j-api:1.7.30=runtimeClasspath org.tensorflow:tensorflow-lite-metadata:0.1.0-rc2=runtimeClasspath -wtf.emulator:gradle-plugin:0.0.12=compileClasspath,runtimeClasspath +wtf.emulator:gradle-plugin:0.0.15=compileClasspath,runtimeClasspath xerces:xercesImpl:2.12.0=runtimeClasspath xml-apis:xml-apis:1.4.01=runtimeClasspath empty=annotationProcessor,kotlinScriptDefExtensions diff --git a/build-conventions/src/main/kotlin/zcash-sdk.android-conventions.gradle.kts b/build-conventions/src/main/kotlin/zcash-sdk.android-conventions.gradle.kts index 04b35584..51115013 100644 --- a/build-conventions/src/main/kotlin/zcash-sdk.android-conventions.gradle.kts +++ b/build-conventions/src/main/kotlin/zcash-sdk.android-conventions.gradle.kts @@ -31,6 +31,7 @@ pluginManager.withPlugin("com.android.library") { defaultConfig { minSdk = project.property("ANDROID_MIN_SDK_VERSION").toString().toInt() + // This is deprecated but there isn't a replacement for it yet with instrumentation tests. targetSdk = project.property("ANDROID_TARGET_SDK_VERSION").toString().toInt() // The last two are for support of pseudolocales in debug builds. diff --git a/build.gradle.kts b/build.gradle.kts index d35f6463..b759e632 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -46,6 +46,48 @@ tasks { } } } + + register("checkProperties") { + doLast { + // Ensure that developers do not change default values of certain properties directly in the repo, but + // instead set them in their local ~/.gradle/gradle.properties file (or use command line arguments) + val expectedPropertyValues = mapOf( + "ZCASH_MAVEN_PUBLISH_USERNAME" to "", + "ZCASH_MAVEN_PUBLISH_PASSWORD" to "", + "ZCASH_ASCII_GPG_KEY" to "", + + "IS_SNAPSHOT" to "true", + + "ZCASH_IS_TREAT_WARNINGS_AS_ERRORS" to "true", + + "IS_USE_TEST_ORCHESTRATOR" to "false", + + "ZCASH_FIREBASE_TEST_LAB_API_KEY_PATH" to "", + "ZCASH_FIREBASE_TEST_LAB_PROJECT" to "", + + "ZCASH_EMULATOR_WTF_API_KEY" to "", + + "IS_MINIFY_SDK_ENABLED" to "false", + "IS_MINIFY_APP_ENABLED" to "true", + + "ZCASH_DEBUG_KEYSTORE_PATH" to "", + "ZCASH_RELEASE_KEYSTORE_PATH" to "", + "ZCASH_RELEASE_KEYSTORE_PASSWORD" to "", + "ZCASH_RELEASE_KEY_ALIAS" to "", + "ZCASH_RELEASE_KEY_ALIAS_PASSWORD" to "", + + "IS_SIGN_RELEASE_BUILD_WITH_DEBUG_KEY" to "false", + ) + + val warnings = expectedPropertyValues.filter { (key, value) -> + project.properties[key].toString() != value + }.map { "Property ${it.key} does not have expected value \"${it.value}\"" } + + if (warnings.isNotEmpty()) { + throw GradleException(warnings.joinToString(separator = "\n")) + } + } + } } val unstableKeywords = listOf("alpha", "beta", "rc", "m", "ea", "build") diff --git a/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/ComposeActivity.kt b/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/ComposeActivity.kt index 100eef54..4170a2a2 100644 --- a/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/ComposeActivity.kt +++ b/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/ComposeActivity.kt @@ -7,7 +7,6 @@ import androidx.activity.viewModels import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable -import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi import androidx.lifecycle.compose.collectAsStateWithLifecycle import cash.z.ecc.android.sdk.demoapp.ui.common.BindCompLocalProvider import cash.z.ecc.android.sdk.demoapp.ui.screen.home.viewmodel.SecretState @@ -33,7 +32,6 @@ class ComposeActivity : ComponentActivity() { } } - @OptIn(ExperimentalLifecycleComposeApi::class) @Composable private fun MainContent() { when (walletViewModel.secretState.collectAsStateWithLifecycle().value) { diff --git a/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/Navigation.kt b/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/Navigation.kt index 1e3037af..70afbfed 100644 --- a/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/Navigation.kt +++ b/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/Navigation.kt @@ -11,7 +11,6 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavHostController import androidx.navigation.NavOptionsBuilder @@ -33,7 +32,6 @@ import cash.z.ecc.android.sdk.model.ZcashNetwork import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -@OptIn(ExperimentalLifecycleComposeApi::class) @Composable @Suppress("LongMethod") internal fun ComposeActivity.Navigation() { diff --git a/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/demos/getprivatekey/GetPrivateKeyFragment.kt b/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/demos/getprivatekey/GetPrivateKeyFragment.kt index 6eac35f4..daee4fa0 100644 --- a/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/demos/getprivatekey/GetPrivateKeyFragment.kt +++ b/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/demos/getprivatekey/GetPrivateKeyFragment.kt @@ -4,7 +4,9 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import cash.z.ecc.android.bip39.Mnemonics import cash.z.ecc.android.bip39.toSeed import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment @@ -42,22 +44,24 @@ class GetPrivateKeyFragment : BaseDemoFragment() { private fun displayKeys() { // derive the keys from the seed: // demonstrate deriving spending keys for five accounts but only take the first one - lifecycleScope.launchWhenStarted { - @Suppress("MagicNumber") - val spendingKey = DerivationTool.deriveUnifiedSpendingKey( - seed, - ZcashNetwork.fromResources(requireApplicationContext()), - Account(5) - ) + lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + @Suppress("MagicNumber") + val spendingKey = DerivationTool.deriveUnifiedSpendingKey( + seed, + ZcashNetwork.fromResources(requireApplicationContext()), + Account(5) + ) - // derive the key that allows you to view but not spend transactions - val viewingKey = DerivationTool.deriveUnifiedFullViewingKey( - spendingKey, - ZcashNetwork.fromResources(requireApplicationContext()) - ) + // derive the key that allows you to view but not spend transactions + val viewingKey = DerivationTool.deriveUnifiedFullViewingKey( + spendingKey, + ZcashNetwork.fromResources(requireApplicationContext()) + ) - // display the keys in the UI - binding.textInfo.setText("Spending Key:\n$spendingKey\n\nViewing Key:\n$viewingKey") + // display the keys in the UI + binding.textInfo.setText("Spending Key:\n$spendingKey\n\nViewing Key:\n$viewingKey") + } } } @@ -75,8 +79,8 @@ class GetPrivateKeyFragment : BaseDemoFragment() { return view } - override fun onResume() { - super.onResume() + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) displayKeys() } diff --git a/docs/CI.md b/docs/CI.md index f1d23318..6d2583ec 100644 --- a/docs/CI.md +++ b/docs/CI.md @@ -5,18 +5,19 @@ Workflows exist for: * Pull request - On pull request, static analysis and testing is performed. * Snapshot deployment - On merge to the main branch, a snapshot release is deployed to Maven Central. Concurrency limits are in place, to ensure that only one snapshot deployment can happen at a time. * Release deployment - Manually invoked workflow to deploy to Maven Central. Concurrency limits are in place, to ensure that only one release deployment can happen at a time. - * Unwedge — If Snapshot deployment fails, it will often be due to multiple unclosed repositories. This workflow can take a given open repository name and attempt to close it. ## Setup -When forking this repository, some secrets need to be defined to set up new continuous integration builds. +When forking this repository, some vars/secrets need to be defined to set up new continuous integration builds. -The secrets passed to GitHub Actions then map to Gradle properties set up within our build scripts. Necessary secrets are documented at the top of each GitHub workflow yml file, as well as reiterated here. +The vars/secrets passed to GitHub Actions then map to Gradle properties set up within our build scripts. Necessary secrets are documented at the top of each GitHub workflow yml file, as well as reiterated here. ### Pull request -* `EMULATOR_WTF_API_KEY` - API key for [Emulator.wtf](https://emulator.wtf) -* `FIREBASE_TEST_LAB_PROJECT` - Firebase Test Lab project name. -* `FIREBASE_TEST_LAB_SERVICE_ACCOUNT` - Email address of Firebase Test Lab service account. -* `FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER` - Workload identity provider to generate temporary service account key. +* Variables + * `FIREBASE_TEST_LAB_PROJECT` - Firebase Test Lab project name. +* Secrets + * `EMULATOR_WTF_API_KEY` - API key for [Emulator.wtf](https://emulator.wtf) + * `FIREBASE_TEST_LAB_SERVICE_ACCOUNT` - Email address of Firebase Test Lab service account. + * `FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER` - Workload identity provider to generate temporary service account key. The Pull Request workflow supports testing of the app and libraries with both Emulator.wtf and Firebase Test Lab. By default Emulator.wtf is used for library instrumentation tests, while Firebase Test Lab is used for a robo test. @@ -27,8 +28,9 @@ To configure Firebase Test Lab, you'll need to enable the necessary Google Cloud Note: Pull requests do not currently run darkside tests. See #361. ### Snapshot deployment -* `MAVEN_CENTRAL_USERNAME` — Username for Maven Central, which maps to the Gradle property `mavenCentralUsername`. -* `MAVEN_CENTRAL_PASSWORD` — Password for Maven Central, which maps to the Gradle property `mavenCentralPassword`. +* Secrets + * `MAVEN_CENTRAL_USERNAME` — Username for Maven Central, which maps to the Gradle property `mavenCentralUsername`. + * `MAVEN_CENTRAL_PASSWORD` — Password for Maven Central, which maps to the Gradle property `mavenCentralPassword`. GPG keys are not needed for snapshot deployment. @@ -37,16 +39,13 @@ Note: For documentation on the Gradle properties for Maven deployment, see [Grad Note: Snapshot builds are configured with a Gradle property `IS_SNAPSHOT`. The workflow automatically sets this property to true for snapshot deployments. This will suffix the version with `-SNAPSHOT` and will upload to the snapshot repository. ### Release deployment -* `MAVEN_CENTRAL_USERNAME` — Username for Maven Central, which maps to the Gradle property `mavenCentralUsername`. -* `MAVEN_CENTRAL_PASSWORD` — Password for Maven Central, which maps to the Gradle property `mavenCentralPassword`. -* `MAVEN_SIGNING_KEYRING_FILE_BASE64` — GPG keyring file, base64 encoded. Maps to Gradle property `signing.secretKeyRingFile`. -* `MAVEN_SIGNING_KEY_ID` — Name of key inside GPG keyring file. Maps to Gradle property `signing.keyId`. -* `MAVEN_SIGNING_PASSWORD` — Password for key inside GPG keyring file. Maps to Gradle property `signing.password`. +* Secrets + * `MAVEN_CENTRAL_USERNAME` — Username for Maven Central, which maps to the Gradle property `mavenCentralUsername`. + * `MAVEN_CENTRAL_PASSWORD` — Password for Maven Central, which maps to the Gradle property `mavenCentralPassword`. + * `MAVEN_SIGNING_KEYRING_FILE_BASE64` — GPG keyring file, base64 encoded. Maps to Gradle property `signing.secretKeyRingFile`. + * `MAVEN_SIGNING_KEY_ID` — Name of key inside GPG keyring file. Maps to Gradle property `signing.keyId`. + * `MAVEN_SIGNING_PASSWORD` — Password for key inside GPG keyring file. Maps to Gradle property `signing.password`. Note: For documentation on the Gradle properties for Maven deployment, see [Gradle Maven Publish Plugin](https://github.com/vanniktech/gradle-maven-publish-plugin). Note: Snapshot builds are configured with a Gradle property `IS_SNAPSHOT`. The workflow automatically sets this property to false for release deployments. - -### Unwedge -* `MAVEN_CENTRAL_USERNAME` — Username for Maven Central, which maps to the Gradle property `mavenCentralUsername`. -* `MAVEN_CENTRAL_PASSWORD` — Password for Maven Central, which maps to the Gradle property `mavenCentralPassword`. \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 6bf12807..a704d8c6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ # Speed up builds. Keep these flags here for quick debugging of issues. org.gradle.caching=true org.gradle.configureondemand=false -org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m +org.gradle.jvmargs=-Xmx3g -XX:MaxMetaspaceSize=1024m org.gradle.parallel=true org.gradle.vfs.watch=true @@ -22,7 +22,7 @@ ZCASH_ASCII_GPG_KEY= # Configures whether release is an unstable snapshot, therefore published to the snapshot repository. IS_SNAPSHOT=true -LIBRARY_VERSION=1.11.0-beta01 +LIBRARY_VERSION=1.12.0-beta01 # Kotlin compiler warnings can be considered errors, failing the build. ZCASH_IS_TREAT_WARNINGS_AS_ERRORS=true @@ -74,48 +74,50 @@ ANDROID_COMPILE_SDK_VERSION=33 # When changing this, be sure to update .github/actions/setup/action.yml ANDROID_NDK_VERSION=22.1.7171670 -ANDROID_GRADLE_PLUGIN_VERSION=7.3.0 +ANDROID_GRADLE_PLUGIN_VERSION=7.4.0 DETEKT_VERSION=1.22.0 DOKKA_VERSION=1.7.20 -EMULATOR_WTF_GRADLE_PLUGIN_VERSION=0.0.12 -FLANK_VERSION=22.10.0 +EMULATOR_WTF_GRADLE_PLUGIN_VERSION=0.0.15 +FLANK_VERSION=23.01.0 FULLADLE_VERSION=0.17.4 GRADLE_VERSIONS_PLUGIN_VERSION=0.44.0 KTLINT_VERSION=0.48.0 -KSP_VERSION=1.7.21-1.0.8 +KSP_VERSION=1.8.0-1.0.8 PROTOBUF_GRADLE_PLUGIN_VERSION=0.8.19 RUST_GRADLE_PLUGIN_VERSION=0.9.3 ANDROIDX_ACTIVITY_VERSION=1.6.1 ANDROIDX_ANNOTATION_VERSION=1.5.0 -ANDROIDX_APPCOMPAT_VERSION=1.5.1 -ANDROIDX_COMPOSE_COMPILER_VERSION=1.4.0-alpha02 -ANDROIDX_COMPOSE_MATERIAL3_VERSION=1.1.0-alpha02 -ANDROIDX_COMPOSE_VERSION=1.3.1 +ANDROIDX_APPCOMPAT_VERSION=1.6.0 +ANDROIDX_COMPOSE_COMPILER_VERSION=1.4.0 +ANDROIDX_COMPOSE_MATERIAL3_VERSION=1.1.0-alpha04 +ANDROIDX_COMPOSE_VERSION=1.3.3 +ANDROIDX_COMPOSE_MATERIAL_ICONS_VERSION=1.3.1 ANDROIDX_CONSTRAINT_LAYOUT_VERSION=2.1.4 ANDROIDX_CORE_VERSION=1.9.0 -ANDROIDX_DATABASE_VERSION=2.2.0 -ANDROIDX_ESPRESSO_VERSION=3.5.0 -ANDROIDX_LIFECYCLE_VERSION=2.6.0-alpha03 +ANDROIDX_DATABASE_VERSION=2.3.0 +ANDROIDX_ESPRESSO_VERSION=3.5.1 +ANDROIDX_LIFECYCLE_VERSION=2.6.0-alpha04 ANDROIDX_MULTIDEX_VERSION=2.0.1 ANDROIDX_NAVIGATION_VERSION=2.5.3 ANDROIDX_NAVIGATION_COMPOSE_VERSION=2.5.3 ANDROIDX_NAVIGATION_FRAGMENT_VERSION=2.4.2 ANDROIDX_PAGING_VERSION=2.1.2 -ANDROIDX_PROFILE_INSTALLER_VERSION=1.3.0-alpha02 -ANDROIDX_ROOM_VERSION=2.4.3 +ANDROIDX_PROFILE_INSTALLER_VERSION=1.3.0-alpha03 +ANDROIDX_ROOM_VERSION=2.5.0 ANDROIDX_SECURITY_CRYPTO_VERSION=1.1.0-alpha04 -ANDROIDX_TEST_JUNIT_VERSION=1.1.4 -ANDROIDX_TEST_MACROBENCHMARK_VERSION=1.2.0-alpha08 +ANDROIDX_TEST_JUNIT_VERSION=1.1.5 +ANDROIDX_TEST_MACROBENCHMARK_VERSION=1.2.0-alpha09 ANDROIDX_TEST_ORCHESTRATOR_VERSION=1.4.2 -ANDROIDX_TEST_VERSION=1.5.0 +ANDROIDX_TEST_CORE_VERSION=1.5.0 +ANDROIDX_TEST_RUNNER_VERSION=1.5.0 ANDROIDX_TRACING_VERSION=1.2.0-alpha01 -ANDROIDX_UI_AUTOMATOR_VERSION=2.3.0-alpha01 +ANDROIDX_UI_AUTOMATOR_VERSION=2.3.0-alpha02 BIP39_VERSION=1.0.4 COROUTINES_OKHTTP=1.0 GOOGLE_MATERIAL_VERSION=1.7.0 GRPC_KOTLIN_VERSION=1.3.0 -GRPC_VERSION=1.51.0 +GRPC_VERSION=1.52.1 GSON_VERSION=2.9.0 GUAVA_VERSION=31.1-android JACOCO_VERSION=0.8.8 @@ -123,7 +125,7 @@ JAVAX_ANNOTATION_VERSION=1.3.2 JUNIT_VERSION=5.9.1 KOTLINX_COROUTINES_VERSION=1.6.4 KOTLINX_DATETIME_VERSION=0.4.0 -KOTLIN_VERSION=1.7.21 +KOTLIN_VERSION=1.8.0 MOCKITO_KOTLIN_VERSION=2.2.0 MOCKITO_VERSION=4.9.0 PROTOC_VERSION=3.21.11 diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/db/DatabaseCoordinatorTest.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/db/DatabaseCoordinatorTest.kt index 0642e371..fa33257a 100644 --- a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/db/DatabaseCoordinatorTest.kt +++ b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/db/DatabaseCoordinatorTest.kt @@ -1,18 +1,12 @@ package cash.z.ecc.android.sdk.db -import androidx.test.filters.FlakyTest -import androidx.test.filters.MediumTest import androidx.test.filters.SmallTest import cash.z.ecc.android.sdk.internal.db.DatabaseCoordinator import cash.z.ecc.android.sdk.internal.ext.existsSuspend -import cash.z.ecc.android.sdk.model.ZcashNetwork import cash.z.ecc.android.sdk.test.getAppContext import cash.z.ecc.fixture.DatabaseNameFixture import cash.z.ecc.fixture.DatabasePathFixture import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -33,50 +27,6 @@ class DatabaseCoordinatorTest { File(noBackupDir).deleteRecursively() } - // Sanity check of the database coordinator instance and its thread-safe implementation. Our aim - // here is to run two jobs in parallel (the second one runs immediately after the first was started) - // to test mutex implementation and correct DatabaseCoordinator function call result. - @Test - @SmallTest - @OptIn(ExperimentalCoroutinesApi::class) - fun mutex_test() = runTest { - var testResult: File? = null - - launch { - delay(1000) - testResult = dbCoordinator.cacheDbFile( - DatabaseNameFixture.TEST_DB_NETWORK, - DatabaseNameFixture.TEST_DB_ALIAS - ) - } - val job2 = launch { - delay(1001) - testResult = dbCoordinator.cacheDbFile( - ZcashNetwork.Mainnet, - "TestZcashSdk" - ) - } - - advanceTimeBy(1002) - - job2.join().also { - assertTrue(testResult != null) - assertTrue(testResult!!.absolutePath.isNotEmpty()) - assertTrue(testResult!!.absolutePath.contains(ZcashNetwork.Mainnet.networkName)) - assertTrue(testResult!!.absolutePath.contains("TestZcashSdk")) - } - } - - @FlakyTest - @Test - @MediumTest - fun mutex_stress_test() { - // We run the mutex test multiple times sequentially to catch a possible problem. - for (x in 0..9) { - mutex_test() - } - } - @Test @SmallTest @OptIn(ExperimentalCoroutinesApi::class) diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/block/CompactBlockProcessor.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/block/CompactBlockProcessor.kt index 42c079df..fe3fac08 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/block/CompactBlockProcessor.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/block/CompactBlockProcessor.kt @@ -445,7 +445,7 @@ class CompactBlockProcessor internal constructor( } } - newTxs.onEach { newTransaction -> + newTxs.filter { it.minedHeight != null }.onEach { newTransaction -> enhance(newTransaction) } twig("Done enhancing transaction details") @@ -461,7 +461,9 @@ class CompactBlockProcessor internal constructor( // TODO [#683]: https://github.com/zcash/zcash-android-wallet-sdk/issues/683 private suspend fun enhance(transaction: TransactionOverview) = withContext(Dispatchers.IO) { - enhanceHelper(transaction.id, transaction.rawId.byteArray, transaction.minedHeight) + transaction.minedHeight?.let { minedHeight -> + enhanceHelper(transaction.id, transaction.rawId.byteArray, minedHeight) + } } private suspend fun enhanceHelper(id: Long, rawTransactionId: ByteArray, minedHeight: BlockHeight) { diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/DatabaseCoordinator.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/DatabaseCoordinator.kt index 45b6c25a..f8a4b0f0 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/DatabaseCoordinator.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/DatabaseCoordinator.kt @@ -392,7 +392,7 @@ internal class DatabaseCoordinator private constructor(context: Context) { * @param databaseFile The database file. * @return A {@code RoomDatabaseBuilder} which you can use to create the database. */ -internal fun commonDatabaseBuilder( +internal fun commonDatabaseBuilder( context: Context, klass: Class, databaseFile: File diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/SQLiteDatabaseExt.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/SQLiteDatabaseExt.kt index deb0f1f4..950105d2 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/SQLiteDatabaseExt.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/SQLiteDatabaseExt.kt @@ -91,12 +91,14 @@ internal fun SupportSQLiteDatabase.queryAndMap( groupBy(groupBy) orderBy(orderBy) - // Counterintuitive but correct. When using the comma syntax, offset comes first. - // When using the keyword syntax, "LIMIT 1 OFFSET 2" then the offset comes second. - if (null == offset) { - limit(limit) - } else { - limit(String.format(Locale.ROOT, "%s,%s", offset, limit)) // NON-NLS + if (null != limit) { + // Counterintuitive but correct. When using the comma syntax, offset comes first. + // When using the keyword syntax, "LIMIT 1 OFFSET 2" then the offset comes second. + if (null == offset) { + limit(limit) + } else { + limit(String.format(Locale.ROOT, "%s,%s", offset, limit)) // NON-NLS + } } } diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/AllTransactionView.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/AllTransactionView.kt index 6f73f716..080a794f 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/AllTransactionView.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/AllTransactionView.kt @@ -20,10 +20,18 @@ internal class AllTransactionView( ) { companion object { + private const val COLUMN_SORT_HEIGHT = "sort_height" // $NON-NLS + + private val COLUMNS = arrayOf( + "*", // $NON-NLS + @Suppress("MaxLineLength") + "IFNULL(${AllTransactionViewDefinition.COLUMN_INTEGER_MINED_HEIGHT}, ${UInt.MAX_VALUE}) AS $COLUMN_SORT_HEIGHT" // $NON-NLS + ) + private val ORDER_BY = String.format( Locale.ROOT, "%s DESC, %s DESC", // $NON-NLS - AllTransactionViewDefinition.COLUMN_INTEGER_MINED_HEIGHT, + COLUMN_SORT_HEIGHT, AllTransactionViewDefinition.COLUMN_INTEGER_ID ) @@ -98,6 +106,7 @@ internal class AllTransactionView( fun getAllTransactions() = sqliteDatabase.queryAndMap( table = AllTransactionViewDefinition.VIEW_NAME, + columns = COLUMNS, orderBy = ORDER_BY, cursorParser = cursorParser ) @@ -105,6 +114,7 @@ internal class AllTransactionView( fun getTransactionRange(blockHeightRange: ClosedRange) = sqliteDatabase.queryAndMap( table = AllTransactionViewDefinition.VIEW_NAME, + columns = COLUMNS, orderBy = ORDER_BY, selection = SELECTION_BLOCK_RANGE, selectionArgs = arrayOf(blockHeightRange.start.value, blockHeightRange.endInclusive.value), @@ -114,6 +124,7 @@ internal class AllTransactionView( suspend fun getOldestTransaction() = sqliteDatabase.queryAndMap( table = AllTransactionViewDefinition.VIEW_NAME, + columns = COLUMNS, orderBy = ORDER_BY, limit = "1", cursorParser = cursorParser diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/ReceivedTransactionView.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/ReceivedTransactionView.kt index 8f64eeeb..625d3716 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/ReceivedTransactionView.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/ReceivedTransactionView.kt @@ -18,10 +18,18 @@ internal class ReceivedTransactionView( ) { companion object { + private const val COLUMN_SORT_HEIGHT = "sort_height" // $NON-NLS + + private val COLUMNS = arrayOf( + "*", // $NON-NLS + @Suppress("MaxLineLength") + "IFNULL(${ReceivedTransactionViewDefinition.COLUMN_INTEGER_MINED_HEIGHT}, ${UInt.MAX_VALUE}) AS $COLUMN_SORT_HEIGHT" // $NON-NLS + ) + private val ORDER_BY = String.format( Locale.ROOT, "%s DESC, %s DESC", // $NON-NLS - ReceivedTransactionViewDefinition.COLUMN_INTEGER_MINED_HEIGHT, + COLUMN_SORT_HEIGHT, ReceivedTransactionViewDefinition.COLUMN_INTEGER_ID ) @@ -37,6 +45,7 @@ internal class ReceivedTransactionView( fun getReceivedTransactions() = sqliteDatabase.queryAndMap( table = ReceivedTransactionViewDefinition.VIEW_NAME, + columns = COLUMNS, orderBy = ORDER_BY, cursorParser = { cursor -> val idColumnIndex = cursor.getColumnIndex(ReceivedTransactionViewDefinition.COLUMN_INTEGER_ID) diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/SentTransactionView.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/SentTransactionView.kt index 374bd1d7..27903667 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/SentTransactionView.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/SentTransactionView.kt @@ -18,10 +18,18 @@ internal class SentTransactionView( ) { companion object { + private const val COLUMN_SORT_HEIGHT = "sort_height" // $NON-NLS + + private val COLUMNS = arrayOf( + "*", // $NON-NLS + @Suppress("MaxLineLength") + "IFNULL(${SentTransactionViewDefinition.COLUMN_INTEGER_MINED_HEIGHT}, ${UInt.MAX_VALUE}) AS $COLUMN_SORT_HEIGHT" // $NON-NLS + ) + private val ORDER_BY = String.format( Locale.ROOT, "%s DESC, %s DESC", // $NON-NLS - SentTransactionViewDefinition.COLUMN_INTEGER_MINED_HEIGHT, + COLUMN_SORT_HEIGHT, SentTransactionViewDefinition.COLUMN_INTEGER_ID ) @@ -37,6 +45,7 @@ internal class SentTransactionView( fun getSentTransactions() = sqliteDatabase.queryAndMap( table = SentTransactionViewDefinition.VIEW_NAME, + columns = COLUMNS, orderBy = ORDER_BY, cursorParser = { cursor -> val idColumnIndex = cursor.getColumnIndex(SentTransactionViewDefinition.COLUMN_INTEGER_ID) diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/model/Transaction.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/model/Transaction.kt index e7afedfe..4c683caf 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/model/Transaction.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/model/Transaction.kt @@ -4,7 +4,7 @@ sealed class Transaction { data class Received internal constructor( val id: Long, val rawId: FirstClassByteArray, - val minedHeight: BlockHeight, + val minedHeight: BlockHeight?, val expiryHeight: BlockHeight?, val index: Long, val raw: FirstClassByteArray?, @@ -20,7 +20,7 @@ sealed class Transaction { data class Sent internal constructor( val id: Long, val rawId: FirstClassByteArray, - val minedHeight: BlockHeight, + val minedHeight: BlockHeight?, val expiryHeight: BlockHeight?, val index: Long, val raw: FirstClassByteArray?, diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/model/TransactionOverview.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/model/TransactionOverview.kt index 397a14cb..c7e08790 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/model/TransactionOverview.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/model/TransactionOverview.kt @@ -9,7 +9,7 @@ package cash.z.ecc.android.sdk.model data class TransactionOverview internal constructor( val id: Long, val rawId: FirstClassByteArray, - val minedHeight: BlockHeight, + val minedHeight: BlockHeight?, val expiryHeight: BlockHeight?, val index: Long, val raw: FirstClassByteArray?, diff --git a/settings.gradle.kts b/settings.gradle.kts index 37c0833b..a177d3d6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -70,6 +70,7 @@ dependencyResolutionManagement { val androidxAppcompatVersion = extra["ANDROIDX_APPCOMPAT_VERSION"].toString() val androidxComposeCompilerVersion = extra["ANDROIDX_COMPOSE_COMPILER_VERSION"].toString() val androidxComposeMaterial3Version = extra["ANDROIDX_COMPOSE_MATERIAL3_VERSION"].toString() + val androidxComposeMaterialIconsVersion = extra["ANDROIDX_COMPOSE_MATERIAL_ICONS_VERSION"].toString() val androidxComposeVersion = extra["ANDROIDX_COMPOSE_VERSION"].toString() val androidxConstraintLayoutVersion = extra["ANDROIDX_CONSTRAINT_LAYOUT_VERSION"].toString() val androidxCoreVersion = extra["ANDROIDX_CORE_VERSION"].toString() @@ -87,7 +88,8 @@ dependencyResolutionManagement { val androidxTestJunitVersion = extra["ANDROIDX_TEST_JUNIT_VERSION"].toString() val androidxTestMacrobenchmarkVersion = extra["ANDROIDX_TEST_MACROBENCHMARK_VERSION"].toString() val androidxTestOrchestratorVersion = extra["ANDROIDX_TEST_ORCHESTRATOR_VERSION"].toString() - val androidxTestVersion = extra["ANDROIDX_TEST_VERSION"].toString() + val androidxTestCoreVersion = extra["ANDROIDX_TEST_CORE_VERSION"].toString() + val androidxTestRunnerVersion = extra["ANDROIDX_TEST_RUNNER_VERSION"].toString() val androidxTracingVersion = extra["ANDROIDX_TRACING_VERSION"].toString() val androidxUiAutomatorVersion = extra["ANDROIDX_UI_AUTOMATOR_VERSION"].toString() val bip39Version = extra["BIP39_VERSION"].toString() @@ -159,7 +161,7 @@ dependencyResolutionManagement { library("guava", "com.google.guava:guava:$guavaVersion") library("javax-annotation", "javax.annotation:javax.annotation-api:$javaxAnnotationVersion") library("kotlin-reflect", "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion") - library("kotlin-stdlib", "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion") + library("kotlin-stdlib", "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") library("kotlinx-coroutines-android", "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinxCoroutinesVersion") library("kotlinx-coroutines-core", "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion") library("kotlinx-datetime", "org.jetbrains.kotlinx:kotlinx-datetime:$kotlinxDateTimeVersion") @@ -169,8 +171,8 @@ dependencyResolutionManagement { // Demo app library("androidx-compose-foundation", "androidx.compose.foundation:foundation:$androidxComposeVersion") library("androidx-compose-material3", "androidx.compose.material3:material3:$androidxComposeMaterial3Version") - library("androidx-compose-material-icons-core", "androidx.compose.material:material-icons-core:$androidxComposeVersion") - library("androidx-compose-material-icons-extended", "androidx.compose.material:material-icons-extended:$androidxComposeVersion") + library("androidx-compose-material-icons-core", "androidx.compose.material:material-icons-core:$androidxComposeMaterialIconsVersion") + library("androidx-compose-material-icons-extended", "androidx.compose.material:material-icons-extended:$androidxComposeMaterialIconsVersion") library("androidx-compose-tooling", "androidx.compose.ui:ui-tooling:$androidxComposeVersion") library("androidx-compose-ui", "androidx.compose.ui:ui:$androidxComposeVersion") library("androidx-compose-ui-fonts", "androidx.compose.ui:ui-text-google-fonts:$androidxComposeVersion") @@ -183,10 +185,10 @@ dependencyResolutionManagement { library("androidx-espresso-contrib", "androidx.test.espresso:espresso-contrib:$androidxEspressoVersion") library("androidx-espresso-core", "androidx.test.espresso:espresso-core:$androidxEspressoVersion") library("androidx-espresso-intents", "androidx.test.espresso:espresso-intents:$androidxEspressoVersion") - library("androidx-test-core", "androidx.test:core:$androidxTestVersion") + library("androidx-test-core", "androidx.test:core:$androidxTestCoreVersion") library("androidx-test-junit", "androidx.test.ext:junit:$androidxTestJunitVersion") library("androidx-test-macrobenchmark", "androidx.benchmark:benchmark-macro-junit4:$androidxTestMacrobenchmarkVersion") - library("androidx-test-runner", "androidx.test:runner:$androidxTestVersion") + library("androidx-test-runner", "androidx.test:runner:$androidxTestRunnerVersion") library("androidx-test-orchestrator", "androidx.test:orchestrator:$androidxTestOrchestratorVersion") library("androidx-tracing", "androidx.tracing:tracing:$androidxTracingVersion") library("androidx-uiAutomator", "androidx.test.uiautomator:uiautomator:$androidxUiAutomatorVersion")