diff --git a/.github/workflows/dx-e2e-beta.yml b/.github/workflows/dx-e2e-beta.yml index 8fa3efa..5547ffb 100644 --- a/.github/workflows/dx-e2e-beta.yml +++ b/.github/workflows/dx-e2e-beta.yml @@ -62,3 +62,33 @@ jobs: channel: beta journey: ${{ matrix.journey }} github-token: ${{ secrets.GITHUB_TOKEN }} + + java-sdk: + name: beta (ubuntu-24.04 · 12-java-sdk) + needs: warmup + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "21" + cache: maven + - name: Require Maven 3.9 + shell: bash + run: mvn -version | head -n1 | grep -Eq '^Apache Maven 3[.]9[.]' + - uses: ./.github/actions/dx-e2e-run + with: + channel: beta + journey: 12-java-sdk + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Require Java toolchain floors (no skipped coverage) + shell: bash + run: | + source lib/common.sh + channel_bin="${HOME}/.tx3/beta/bin" + tx3c_version="$("${channel_bin}/tx3c" --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)" + trix_version="$("${channel_bin}/trix" --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)" + [[ -n "${tx3c_version}" && -n "${trix_version}" ]] || die "could not read Java toolchain versions" + version_ge "${tx3c_version}" 0.25.0 || die "Java CI requires tx3c >= 0.25.0" + version_ge "${trix_version}" 0.28.0 || die "Java CI requires trix >= 0.28.0" diff --git a/.github/workflows/dx-e2e-stable.yml b/.github/workflows/dx-e2e-stable.yml index 20ec743..361d386 100644 --- a/.github/workflows/dx-e2e-stable.yml +++ b/.github/workflows/dx-e2e-stable.yml @@ -65,3 +65,33 @@ jobs: channel: stable journey: ${{ matrix.journey }} github-token: ${{ secrets.GITHUB_TOKEN }} + + java-sdk: + name: stable (ubuntu-24.04 · 12-java-sdk) + needs: warmup + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "21" + cache: maven + - name: Require Maven 3.9 + shell: bash + run: mvn -version | head -n1 | grep -Eq '^Apache Maven 3[.]9[.]' + - uses: ./.github/actions/dx-e2e-run + with: + channel: stable + journey: 12-java-sdk + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Require Java toolchain floors (no skipped coverage) + shell: bash + run: | + source lib/common.sh + channel_bin="${HOME}/.tx3/stable/bin" + tx3c_version="$("${channel_bin}/tx3c" --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)" + trix_version="$("${channel_bin}/trix" --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)" + [[ -n "${tx3c_version}" && -n "${trix_version}" ]] || die "could not read Java toolchain versions" + version_ge "${tx3c_version}" 0.25.0 || die "Java CI requires tx3c >= 0.25.0" + version_ge "${trix_version}" 0.28.0 || die "Java CI requires trix >= 0.28.0" diff --git a/journeys/12-java-sdk/JavaSdkJourney.java b/journeys/12-java-sdk/JavaSdkJourney.java new file mode 100644 index 0000000..b51af28 --- /dev/null +++ b/journeys/12-java-sdk/JavaSdkJourney.java @@ -0,0 +1,47 @@ +package e2e; + +import java.math.BigInteger; +import java.net.URI; +import land.tx3.generated.demo.DemoClient; +import land.tx3.sdk.Address; +import land.tx3.sdk.CardanoSigner; +import land.tx3.sdk.ClientOptions; +import land.tx3.sdk.Party; +import land.tx3.sdk.PollConfig; + +/** Host application for the generated Java client journey. */ +public final class JavaSdkJourney { + // Public test vector from the Java SDK; funded only by this disposable devnet. + private static final String ALICE_MNEMONIC = + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon " + + "abandon about"; + + private JavaSdkJourney() {} + + /** Runs one generated-client transfer through confirmation. */ + public static void main(String[] args) { + if (args.length != 3) { + throw new IllegalArgumentException( + "usage: JavaSdkJourney "); + } + + var sender = new Address(args[1]); + var signer = new CardanoSigner(ALICE_MNEMONIC, sender); + var client = + new DemoClient(ClientOptions.forEndpoint(URI.create(args[0])), DemoClient.Profile.LOCAL) + .withSender(Party.signer(signer)) + .withReceiver(Party.address(new Address(args[2]))); + + var resolved = + client + .transfer(new DemoClient.TransferParams(BigInteger.valueOf(2_000_000))) + .resolve() + .join(); + var submitted = resolved.sign().submit().join(); + var confirmed = submitted.waitForConfirmed(PollConfig.defaults()).join(); + + System.out.printf( + "{\"hash\":\"%s\",\"stage\":\"%s\",\"confirmations\":%d}%n", + submitted.hash(), confirmed.stage().wireValue(), confirmed.confirmations()); + } +} diff --git a/journeys/12-java-sdk/README.md b/journeys/12-java-sdk/README.md new file mode 100644 index 0000000..1905f52 --- /dev/null +++ b/journeys/12-java-sdk/README.md @@ -0,0 +1,34 @@ +# 12-java-sdk + +The Java generated-client path with real released components: seed trix's +`java-client` plugin, render tx3c's built-in template, compile the generated +Maven project against `land.tx3:tx3-sdk:0.15.0`, and consume that installed +artifact from a separate Java 21 host project. The host then resolves, signs, +submits, and confirms a transfer through the generated `DemoClient` against a +disposable local devnet. + +- **Scope:** runtime. The generated project and consumer use an isolated Maven + repository; normal released-channel validation downloads dependencies from + Maven Central without credentials. No preprod endpoint or secret is used. +- **Toolchain gate:** `#@ min-tx3c: 0.25.0`, the first tx3c release with the + built-in `java-client` template, and trix 0.28.0 or newer for the built-in + Java plugin registration that invokes `--template java-client`. The Java CI + jobs require both floors, so a below-floor skip cannot count as coverage. +- **Host baseline:** CI runs only on `ubuntu-24.04` with Temurin 21 and Maven + 3.9. A local run without Java/Maven skips; CI treats either missing tool as a + failure so absent prerequisites cannot count as coverage. +- **Identity:** the checked-in phrase and address are the Java SDK's public, + checksum-verified CIP-1852 test vector. The address is funded only by this + disposable devnet; the phrase is never a live credential. The receiver is + trix's deterministic `bob` identity. + +Run from the e2e root with `./run.sh --channel stable --journey 12-java-sdk` +or `./run.sh --channel beta --journey 12-java-sdk`. With explicit compatible +`TX3_*_PATH` binaries, use `./run.sh --local --journey 12-java-sdk`. +Failure logs and the consumer workdir are preserved by the harness; devnet +cleanup targets only this journey's unique configuration path. + +For reviewed pre-publication testing, install `0.15.0-SNAPSHOT` into an isolated +Maven repository, set `TX3_JAVA_M2_REPO` to that directory, and set +`TX3_JAVA_SDK_VERSION=0.15.0-SNAPSHOT`. The released-channel acceptance path +leaves both overrides unset and resolves `0.15.0` from Maven Central. diff --git a/journeys/12-java-sdk/devnet.toml b/journeys/12-java-sdk/devnet.toml new file mode 100644 index 0000000..bde8215 --- /dev/null +++ b/journeys/12-java-sdk/devnet.toml @@ -0,0 +1,4 @@ +# Checksum-verified Java SDK signer vector, funded only inside this disposable devnet. +[[utxos]] +address = "addr_test1vq8ac7qqy0vtulyl7wntmsxc6wex80gvcyjy33qffrhm7ss9hjl0y" +value = 100000000000 diff --git a/journeys/12-java-sdk/journey.sh b/journeys/12-java-sdk/journey.sh new file mode 100755 index 0000000..a6feeb0 --- /dev/null +++ b/journeys/12-java-sdk/journey.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash +# +# Journey 12 — generated Java SDK client. See README.md for coverage. +# Run via e2e/run.sh, which provides $TRIX and an isolated working directory. +# +#@ min-tx3c: 0.25.0 + +source "${E2E_LIB:?E2E_LIB not set — run this journey via e2e/run.sh}" + +JOURNEY_HOME="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" + +journey_begin "12-java-sdk" "generate a Java client → compile a clean consumer → resolve, sign, submit, and confirm (devnet)" + +# Java and Maven are host infrastructure. GitHub's selected job installs both; +# a missing CI prerequisite is a failure, while a developer without Java may +# still run the rest of the repository's journeys. +if ! command -v java >/dev/null 2>&1 || ! command -v mvn >/dev/null 2>&1; then + if [[ -n "${CI:-}" ]]; then + die "java and Maven are required for the selected Java SDK CI journey" + fi + skip "java/Maven not found — skipping Java SDK journey" + journey_end + exit 0 +fi + +java -version 2>&1 | head -n1 | grep -Eq 'version "21([.]|\")' \ + || die "Java 21 is required for the Java SDK journey" +mvn -version | head -n1 | grep -Eq '^Apache Maven 3[.]9[.]' \ + || die "Maven 3.9 is required for the Java SDK journey" + +# 1. Scaffold under a stable name so the generated package and artifact are +# deterministic (`land.tx3.generated.demo.DemoClient`, `demo-client`). +mkdir demo && cd demo +run_cmd "trix init -y — scaffold a new project" "${TRIX}" init -y + +# 2. Generate through trix's built-in plugin registration. tx3c renders the +# built-in java-client template; a download would violate released-channel +# hermeticity. +run_cmd "trix codegen --plugin java-client — generate bindings" \ + "${TRIX}" codegen --plugin java-client +assert_output_contains "Added [[codegen]]" "plugin entry seeded into trix.toml" +assert_output_contains "Bindgen successful for 'demo'" +grep -qiF "Reading template from" "${LAST_OUTPUT_FILE}" \ + && die "built-in plugin downloaded templates instead of using tx3c's built-in template" +ok "built-in Java template rendered by tx3c, no template download" + +GENERATED=".tx3/codegen/java-client/demo" +assert_exists "${GENERATED}/pom.xml" "generated Maven project exists" +assert_exists "${GENERATED}/src/main/java/land/tx3/generated/demo/DemoClient.java" \ + "typed DemoClient source exists" +[[ -e ".tx3/codegen/java-client/pom.xml" ]] \ + && die "generated Maven project leaked outside the per-protocol subdir" + +run_cmd "read the generated Java client" \ + cat "${GENERATED}/src/main/java/land/tx3/generated/demo/DemoClient.java" +assert_output_contains 'PROTOCOL_NAME = "demo"' "protocol identity embedded" +assert_output_contains "TRANSFER_TIR" "transfer TIR envelope embedded" +assert_output_contains "class DemoClient" "typed Java client wrapper emitted" + +# 3. Compile the generated project against the released SDK in a fresh local +# repository, then install only that generated artifact for the host fixture. +# An explicit override supports reviewed pre-publication snapshot validation. +M2_REPO="${TX3_JAVA_M2_REPO:-${PWD}/m2}" +SDK_VERSION="${TX3_JAVA_SDK_VERSION:-0.15.0}" +run_cmd "mvn verify — compile the generated Maven project" \ + mvn -B -ntp -Dmaven.repo.local="${M2_REPO}" \ + -Dtx3.sdk.version="${SDK_VERSION}" -f "${GENERATED}/pom.xml" verify +run_cmd "mvn install — stage generated client for a clean consumer" \ + mvn -B -ntp -Dmaven.repo.local="${M2_REPO}" \ + -Dtx3.sdk.version="${SDK_VERSION}" -DskipTests \ + -f "${GENERATED}/pom.xml" install + +mkdir app +cp "${JOURNEY_HOME}/pom.xml" app/ +mkdir -p app/src/main/java/e2e +cp "${JOURNEY_HOME}/JavaSdkJourney.java" app/src/main/java/e2e/ +run_cmd "mvn verify — compile the clean host consumer" \ + mvn -B -ntp -Dmaven.repo.local="${M2_REPO}" \ + -Dtx3.sdk.version="${SDK_VERSION}" -f app/pom.xml verify +run_cmd "mvn dependency:build-classpath — resolve the consumer runtime" \ + mvn -B -ntp -Dmaven.repo.local="${M2_REPO}" \ + -Dtx3.sdk.version="${SDK_VERSION}" -Dmdep.outputFile=target/classpath.txt \ + -f app/pom.xml dependency:build-classpath + +# 4. Fund the Java SDK's checksum-verified CIP-1852 test identity. trix's +# conventional wallets intentionally use a different root-key scheme, so this +# explicit fixture keeps signer/address binding honest while remaining local. +cp "${JOURNEY_HOME}/devnet.toml" devnet.toml +ALICE="addr_test1vq8ac7qqy0vtulyl7wntmsxc6wex80gvcyjy33qffrhm7ss9hjl0y" +BOB="$("${TRIX}" identities bob address-testnet 2>/dev/null | grep '^addr' | head -n1)" +[[ -n "${ALICE}" && -n "${BOB}" ]] || die "could not resolve alice/bob testnet addresses" + +DEVNET_HOME="${PWD}/.tx3/dolos" +cleanup_devnet() { + # Match this journey's unique configuration path, including on startup failure. + pkill -f "dolos.*${DEVNET_HOME}/dolos[.]toml daemon" >/dev/null 2>&1 || true +} +trap cleanup_devnet EXIT +run_cmd "trix devnet --background — start a local devnet" "${TRIX}" devnet --background +assert_output_contains "devnet started in background" +for _ in $(seq 1 30); do + (exec 3<>/dev/tcp/127.0.0.1/8164) 2>/dev/null && { exec 3>&- 3<&-; break; } + sleep 1 +done +sleep 3 + +CLASSPATH="app/target/classes:$(cat app/target/classpath.txt)" +run_cmd "JavaSdkJourney — resolve, sign, submit, and confirm through the generated client" \ + java -cp "${CLASSPATH}" e2e.JavaSdkJourney \ + "http://127.0.0.1:8164" "${ALICE}" "${BOB}" +assert_output_contains '"stage":"confirmed"' "submitted transaction confirmed" +assert_output_contains '"hash":' "confirmed transaction carries a hash" + +journey_end diff --git a/journeys/12-java-sdk/pom.xml b/journeys/12-java-sdk/pom.xml new file mode 100644 index 0000000..aee44d6 --- /dev/null +++ b/journeys/12-java-sdk/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + land.tx3.e2e + java-sdk-journey + 0.0.0-SNAPSHOT + + + 21 + UTF-8 + 0.15.0 + + + + + land.tx3.generated + demo-client + 0.0.0-SNAPSHOT + + + + land.tx3 + tx3-sdk + ${tx3.sdk.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.16.0 + + + +