From d58ab11530a46fcaee43ce662a35b46dae2b8449 Mon Sep 17 00:00:00 2001 From: Santiago Date: Sun, 27 Sep 2026 21:07:28 -0300 Subject: [PATCH 1/2] feat: add Java SDK generated-client journey --- .github/workflows/dx-e2e-beta.yml | 20 ++++ .github/workflows/dx-e2e-stable.yml | 20 ++++ journeys/12-java-sdk/JavaSdkJourney.java | 47 ++++++++++ journeys/12-java-sdk/README.md | 27 ++++++ journeys/12-java-sdk/devnet.toml | 4 + journeys/12-java-sdk/journey.sh | 111 +++++++++++++++++++++++ journeys/12-java-sdk/pom.xml | 40 ++++++++ 7 files changed, 269 insertions(+) create mode 100644 journeys/12-java-sdk/JavaSdkJourney.java create mode 100644 journeys/12-java-sdk/README.md create mode 100644 journeys/12-java-sdk/devnet.toml create mode 100755 journeys/12-java-sdk/journey.sh create mode 100644 journeys/12-java-sdk/pom.xml diff --git a/.github/workflows/dx-e2e-beta.yml b/.github/workflows/dx-e2e-beta.yml index 8fa3efa..865eecf 100644 --- a/.github/workflows/dx-e2e-beta.yml +++ b/.github/workflows/dx-e2e-beta.yml @@ -62,3 +62,23 @@ 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 }} diff --git a/.github/workflows/dx-e2e-stable.yml b/.github/workflows/dx-e2e-stable.yml index 20ec743..039a419 100644 --- a/.github/workflows/dx-e2e-stable.yml +++ b/.github/workflows/dx-e2e-stable.yml @@ -65,3 +65,23 @@ 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 }} 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..0240b6d --- /dev/null +++ b/journeys/12-java-sdk/README.md @@ -0,0 +1,27 @@ +# 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. The selected trix channel must also include + the built-in Java plugin registration that invokes `--template java-client`. +- **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. + +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..f42db51 --- /dev/null +++ b/journeys/12-java-sdk/journey.sh @@ -0,0 +1,111 @@ +#!/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" + +run_cmd "trix devnet --background — start a local devnet" "${TRIX}" devnet --background +assert_output_contains "devnet started in background" +DEVNET_PIDS="$(pgrep -f 'dolos.*daemon' | tr '\n' ' ')" +# shellcheck disable=SC2064 +trap "[[ -n \"${DEVNET_PIDS}\" ]] && kill -9 ${DEVNET_PIDS} 2>/dev/null" EXIT +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 + + + + From bf69e16af108224c28a7b9d48a8bba7305a26d03 Mon Sep 17 00:00:00 2001 From: Santiago Date: Mon, 28 Sep 2026 08:38:49 -0300 Subject: [PATCH 2/2] fix: require Java CI coverage and scope devnet cleanup --- .github/workflows/dx-e2e-beta.yml | 10 ++++++++++ .github/workflows/dx-e2e-stable.yml | 10 ++++++++++ journeys/12-java-sdk/README.md | 11 +++++++++-- journeys/12-java-sdk/journey.sh | 9 ++++++--- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dx-e2e-beta.yml b/.github/workflows/dx-e2e-beta.yml index 865eecf..5547ffb 100644 --- a/.github/workflows/dx-e2e-beta.yml +++ b/.github/workflows/dx-e2e-beta.yml @@ -82,3 +82,13 @@ jobs: 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 039a419..361d386 100644 --- a/.github/workflows/dx-e2e-stable.yml +++ b/.github/workflows/dx-e2e-stable.yml @@ -85,3 +85,13 @@ jobs: 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/README.md b/journeys/12-java-sdk/README.md index 0240b6d..1905f52 100644 --- a/journeys/12-java-sdk/README.md +++ b/journeys/12-java-sdk/README.md @@ -11,8 +11,9 @@ disposable local devnet. 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. The selected trix channel must also include - the built-in Java plugin registration that invokes `--template java-client`. + 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. @@ -21,6 +22,12 @@ disposable local devnet. 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 diff --git a/journeys/12-java-sdk/journey.sh b/journeys/12-java-sdk/journey.sh index f42db51..a6feeb0 100755 --- a/journeys/12-java-sdk/journey.sh +++ b/journeys/12-java-sdk/journey.sh @@ -90,11 +90,14 @@ 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" -DEVNET_PIDS="$(pgrep -f 'dolos.*daemon' | tr '\n' ' ')" -# shellcheck disable=SC2064 -trap "[[ -n \"${DEVNET_PIDS}\" ]] && kill -9 ${DEVNET_PIDS} 2>/dev/null" EXIT for _ in $(seq 1 30); do (exec 3<>/dev/tcp/127.0.0.1/8164) 2>/dev/null && { exec 3>&- 3<&-; break; } sleep 1