taggedArguments() {
/**
* Resolves this invocation through the configured TRP client.
*
- * Profile environment and party addresses are applied first. Explicit transaction arguments
- * win over injected party addresses, and every dynamic argument follows its resolved parameter
- * type. Values supplied through {@link #argTagged} are sent without another encoding pass.
+ *
Profile environment and party addresses are applied first as resolver arguments. Explicit
+ * transaction arguments win over injected values, and every dynamic argument follows its resolved
+ * parameter type. Values supplied through {@link #argTagged} are sent without another encoding
+ * pass.
*
* @throws ResolutionException if a required transaction parameter has no explicit or injected
* value
@@ -103,7 +104,7 @@ public CompletableFuture resolve() {
throw new ValidationException("txBuilder", "this transaction builder cannot resolve");
}
- var merged = new LinkedHashMap();
+ var merged = new LinkedHashMap(environment);
parties.forEach(
(name, party) -> merged.put(normalize(name), ArgValue.address(party.address())));
taggedArguments.forEach((name, value) -> merged.put(normalize(name), value));
@@ -115,7 +116,6 @@ public CompletableFuture resolve() {
}
}
- var env = environment.isEmpty() ? null : new LinkedHashMap(environment);
var signers = new java.util.ArrayList();
parties.forEach(
(name, party) ->
@@ -125,7 +125,7 @@ public CompletableFuture resolve() {
signer ->
signers.add(new ResolvedTx.SignerEntry(name, party.address(), signer))));
- var pending = trp.resolve(new ResolveParams(tir, merged, env));
+ var pending = trp.resolve(new ResolveParams(tir, merged));
var result = new CompletableFuture();
pending.whenComplete(
(response, failure) -> {
diff --git a/src/test/java/land/tx3/sdk/FacadeTest.java b/src/test/java/land/tx3/sdk/FacadeTest.java
index cefe0a2..68057f2 100644
--- a/src/test/java/land/tx3/sdk/FacadeTest.java
+++ b/src/test/java/land/tx3/sdk/FacadeTest.java
@@ -4,6 +4,7 @@
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -96,7 +97,8 @@ void dynamicAndPartsClientsProduceEquivalentResolveRequests() throws Exception {
var dynamicParams = dynamicTransport.params();
var generatedParams = generatedTransport.params();
assertEquals(dynamicParams, generatedParams);
- assertEquals(9, dynamicParams.path("env").path("fee").intValue());
+ assertEquals(9, dynamicParams.path("args").path("fee").intValue());
+ assertTrue(dynamicParams.path("env").isMissingNode());
assertEquals(
"addr_argument", dynamicParams.path("args").path("sender").path("address").textValue());
assertEquals(
diff --git a/src/test/java/land/tx3/sdk/ProtocolTest.java b/src/test/java/land/tx3/sdk/ProtocolTest.java
index 4624f6d..a8e1f41 100644
--- a/src/test/java/land/tx3/sdk/ProtocolTest.java
+++ b/src/test/java/land/tx3/sdk/ProtocolTest.java
@@ -116,7 +116,7 @@ void discriminatesReadJsonAndStructureFailures() {
void fixturesMatchTheFounderBoundCanonicalHashes() throws Exception {
var digest = MessageDigest.getInstance("SHA-256");
assertEquals(
- "8d5d715f300e618373b588af96f0cfb9b0a3904b3a34fc0038f72ebb1695da31",
+ "02da9f4a6710dd4821cf219c5f373d14e3c933519953068c1b03e38947568d32",
HexFormat.of().formatHex(digest.digest(java.nio.file.Files.readAllBytes(TRANSFER))));
assertEquals(
"0a7195b22fe3262a87110a89ee83f75d1295257c193d8859165842aeeea2ae83",
diff --git a/src/test/java/land/tx3/sdk/TransactionE2eTest.java b/src/test/java/land/tx3/sdk/TransactionE2eTest.java
new file mode 100644
index 0000000..b679a91
--- /dev/null
+++ b/src/test/java/land/tx3/sdk/TransactionE2eTest.java
@@ -0,0 +1,119 @@
+package land.tx3.sdk;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.net.URI;
+import java.nio.file.Path;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletionException;
+import org.junit.jupiter.api.Assumptions;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+/** Live tests for the canonical transfer protocol against preprod TRP. */
+@Tag("e2e")
+class TransactionE2eTest {
+ private static final List REQUIRED_ENV =
+ List.of(
+ "TRP_ENDPOINT_PREPROD",
+ "TRP_API_KEY_PREPROD",
+ "TEST_PARTY_A_ADDRESS",
+ "TEST_PARTY_A_MNEMONIC",
+ "TEST_PARTY_B_ADDRESS",
+ "TEST_PARTY_B_MNEMONIC");
+
+ @Test
+ void transferCompletesResolveSignSubmitAndConfirm() {
+ var env = environment();
+ var client = liveClient(env);
+
+ var resolved = client.tx("transfer").arg("quantity", 10_000_000).resolve().join();
+ var signed = resolved.sign();
+ assertEquals(resolved.hash(), signed.hash());
+
+ var submitted = signed.submit().join();
+ var confirmed = submitted.waitForConfirmed(PollConfig.defaults()).join();
+ assertTrue(confirmed.stage() == TxStage.CONFIRMED || confirmed.stage() == TxStage.FINALIZED);
+ }
+
+ @Test
+ void missingArgumentIsReportedBeforeTransport() {
+ var env = environment();
+ var failure =
+ assertThrows(ResolutionException.class, () -> liveClient(env).tx("transfer").resolve());
+ assertEquals("quantity", failure.parameter());
+ }
+
+ @Test
+ void unreachableEndpointProducesTypedTransportFailure() {
+ environment();
+ var protocol = Protocol.fromFile(fixture());
+ var client =
+ protocol
+ .client()
+ .trp(
+ new ClientOptions(
+ URI.create("http://127.0.0.1:1"), java.util.Map.of(), Duration.ofSeconds(2)))
+ .withProfile("preprod")
+ .withParty("sender", Party.address(new Address("addr_test1unreachable")))
+ .withParty("receiver", Party.address(new Address("addr_test1unreachable")))
+ .withParty("middleman", Party.address(new Address("addr_test1unreachable")))
+ .build();
+
+ var failure =
+ assertThrows(
+ CompletionException.class,
+ () -> client.tx("transfer").arg("quantity", 10_000_000).resolve().join());
+ assertInstanceOf(TransportException.class, failure.getCause());
+ }
+
+ private static Tx3Client liveClient(E2eEnvironment env) {
+ var protocol = Protocol.fromFile(fixture());
+ var signer = new CardanoSigner(env.partyAMnemonic(), new Address(env.partyAAddress()));
+ return protocol
+ .client()
+ .trpEndpoint(URI.create(env.endpoint()))
+ .withHeader("dmtr-api-key", env.apiKey())
+ .withProfile("preprod")
+ .withParty("sender", Party.signer(signer))
+ .withParty("receiver", Party.address(new Address(env.partyBAddress())))
+ .withParty("middleman", Party.address(new Address(env.partyBAddress())))
+ .build();
+ }
+
+ private static Path fixture() {
+ return Path.of("src/test/resources/fixtures/transfer.tii");
+ }
+
+ private static E2eEnvironment environment() {
+ var missing = new ArrayList();
+ for (var name : REQUIRED_ENV) {
+ if (System.getenv(name) == null || System.getenv(name).isBlank()) missing.add(name);
+ }
+ if (!missing.isEmpty()) {
+ var message = "Missing e2e configuration: " + String.join(", ", missing);
+ if (System.getenv("CI") != null) throw new AssertionError(message);
+ Assumptions.abort(message);
+ }
+ return new E2eEnvironment(
+ System.getenv("TRP_ENDPOINT_PREPROD"),
+ System.getenv("TRP_API_KEY_PREPROD"),
+ System.getenv("TEST_PARTY_A_ADDRESS"),
+ System.getenv("TEST_PARTY_A_MNEMONIC"),
+ System.getenv("TEST_PARTY_B_ADDRESS"),
+ System.getenv("TEST_PARTY_B_MNEMONIC"));
+ }
+
+ private record E2eEnvironment(
+ String endpoint,
+ String apiKey,
+ String partyAAddress,
+ String partyAMnemonic,
+ String partyBAddress,
+ String partyBMnemonic) {}
+}
diff --git a/src/test/java/land/tx3/sdk/TransactionLifecycleTest.java b/src/test/java/land/tx3/sdk/TransactionLifecycleTest.java
index d720cd6..96f57fe 100644
--- a/src/test/java/land/tx3/sdk/TransactionLifecycleTest.java
+++ b/src/test/java/land/tx3/sdk/TransactionLifecycleTest.java
@@ -142,6 +142,25 @@ void finalizedWaitIgnoresConfirmedAndReportsTerminalAndTimeoutKinds() {
assertInstanceOf(PollingException.class, timeout.getCause()).kind());
}
+ @Test
+ void pollingRetriesTransientStatusTransportFailures() {
+ var transport = new LifecycleTransport();
+ transport.failure(
+ new TransportException(
+ TransportFailure.HTTP_STATUS,
+ "TRP returned HTTP status 502",
+ null,
+ 502,
+ null,
+ "bad gateway"));
+ transport.status(HASH, TxStage.CONFIRMED);
+
+ var status = submitted(transport).waitForConfirmed(new PollConfig(2, Duration.ZERO)).join();
+
+ assertEquals(TxStage.CONFIRMED, status.stage());
+ assertEquals(List.of("trp.checkStatus", "trp.checkStatus"), transport.methods);
+ }
+
@Test
void cancellationStopsInFlightStatusRequestAndScheduledDelay() {
var pendingTransport = new LifecycleTransport();
@@ -233,6 +252,10 @@ CompletableFuture pendingStatus() {
return pending;
}
+ void failure(RuntimeException failure) {
+ responses.add(CompletableFuture.failedFuture(failure));
+ }
+
@Override
public CompletableFuture send(TransportRequest request) {
try {
diff --git a/src/test/resources/fixtures/README.md b/src/test/resources/fixtures/README.md
index 8209496..6e5ba1b 100644
--- a/src/test/resources/fixtures/README.md
+++ b/src/test/resources/fixtures/README.md
@@ -1,10 +1,10 @@
# Canonical TII fixtures
These test fixtures are copied without modification from the Tx3 SDK specification at umbrella
-inspection revision `d9bfbaf891f3c67a96087ecaa84ae78e5c2584a3`:
+inspection revision `2bc0b261fa6cc69643ff0fac769171424078de37`:
- `transfer.tii` from `solution/sdks/sdk-spec/test-vectors/transfer/transfer.tii`
- (`sha256:8d5d715f300e618373b588af96f0cfb9b0a3904b3a34fc0038f72ebb1695da31`)
+ (`sha256:02da9f4a6710dd4821cf219c5f373d14e3c933519953068c1b03e38947568d32`)
- `complex.tii` from `solution/sdks/sdk-spec/test-vectors/complex-types/complex.tii`
(`sha256:0a7195b22fe3262a87110a89ee83f75d1295257c193d8859165842aeeea2ae83`)
diff --git a/src/test/resources/fixtures/transfer.tii b/src/test/resources/fixtures/transfer.tii
index 9f00265..15754dd 100644
--- a/src/test/resources/fixtures/transfer.tii
+++ b/src/test/resources/fixtures/transfer.tii
@@ -49,7 +49,7 @@
"type": "object"
},
"tir": {
- "content": "ab6466656573a1694576616c506172616d6a457870656374466565736a7265666572656e6365738066696e7075747381a3646e616d6566736f75726365657574786f73a1694576616c506172616da16b457870656374496e7075748266736f75726365a56761646472657373a1694576616c506172616da16b45787065637456616c7565826673656e64657267416464726573736a6d696e5f616d6f756e74a16b4576616c4275696c74496ea16341646482a16b4576616c4275696c74496ea16341646482a16641737365747381a366706f6c696379644e6f6e656a61737365745f6e616d65644e6f6e6566616d6f756e74a16b4576616c4275696c74496ea16341646482a1694576616c506172616da16b45787065637456616c756582687175616e7469747963496e74a1694576616c506172616da16b45787065637456616c7565826374617863496e74a1694576616c506172616d6a45787065637446656573a16c4576616c436f6d70696c6572a16e436f6d707574654d696e5574786fa1664e756d6265720263726566644e6f6e65646d616e79f46a636f6c6c61746572616cf46872656465656d6572644e6f6e65676f75747075747383a46761646472657373a1694576616c506172616da16b45787065637456616c756582687265636569766572674164647265737365646174756d644e6f6e6566616d6f756e74a16641737365747381a366706f6c696379644e6f6e656a61737365745f6e616d65644e6f6e6566616d6f756e74a1694576616c506172616da16b45787065637456616c756582687175616e7469747963496e74686f7074696f6e616cf4a46761646472657373a1694576616c506172616da16b45787065637456616c756582696d6964646c656d616e674164647265737365646174756d644e6f6e6566616d6f756e74a16641737365747381a366706f6c696379644e6f6e656a61737365745f6e616d65644e6f6e6566616d6f756e74a1694576616c506172616da16b45787065637456616c7565826374617863496e74686f7074696f6e616cf4a46761646472657373a1694576616c506172616da16b45787065637456616c7565826673656e646572674164647265737365646174756d644e6f6e6566616d6f756e74a16b4576616c4275696c74496ea16353756282a16b4576616c4275696c74496ea16353756282a16a4576616c436f65726365a16a496e746f417373657473a1694576616c506172616da16b457870656374496e7075748266736f75726365a56761646472657373a1694576616c506172616da16b45787065637456616c7565826673656e64657267416464726573736a6d696e5f616d6f756e74a16b4576616c4275696c74496ea16341646482a16b4576616c4275696c74496ea16341646482a16641737365747381a366706f6c696379644e6f6e656a61737365745f6e616d65644e6f6e6566616d6f756e74a16b4576616c4275696c74496ea16341646482a1694576616c506172616da16b45787065637456616c756582687175616e7469747963496e74a1694576616c506172616da16b45787065637456616c7565826374617863496e74a1694576616c506172616d6a45787065637446656573a16c4576616c436f6d70696c6572a16e436f6d707574654d696e5574786fa1664e756d6265720263726566644e6f6e65646d616e79f46a636f6c6c61746572616cf4a16641737365747381a366706f6c696379644e6f6e656a61737365745f6e616d65644e6f6e6566616d6f756e74a16b4576616c4275696c74496ea16341646482a1694576616c506172616da16b45787065637456616c756582687175616e7469747963496e74a1694576616c506172616da16b45787065637456616c7565826374617863496e74a1694576616c506172616d6a45787065637446656573686f7074696f6e616cf46876616c6964697479f6656d696e747380656275726e7380656164686f63806a636f6c6c61746572616c80677369676e657273f6686d6574616461746180",
+ "content": "ab6466656573a1694576616c506172616d6a457870656374466565736a7265666572656e6365738066696e7075747381a3646e616d6566736f75726365657574786f73a1694576616c506172616da16b457870656374496e7075748266736f75726365a56761646472657373a1694576616c506172616da16b45787065637456616c7565826673656e64657267416464726573736a6d696e5f616d6f756e74a16b4576616c4275696c74496ea16341646482a16b4576616c4275696c74496ea16341646482a16641737365747381a366706f6c696379644e6f6e656a61737365745f6e616d65644e6f6e6566616d6f756e74a16b4576616c4275696c74496ea16341646482a1694576616c506172616da16b45787065637456616c756582687175616e7469747963496e74a1694576616c506172616da16b45787065637456616c7565826374617863496e74a1694576616c506172616d6a45787065637446656573a16c4576616c436f6d70696c6572a16e436f6d707574654d696e5574786fa1664e756d6265720263726566644e6f6e65646d616e79f56a636f6c6c61746572616cf46872656465656d6572644e6f6e65676f75747075747383a46761646472657373a1694576616c506172616da16b45787065637456616c756582687265636569766572674164647265737365646174756d644e6f6e6566616d6f756e74a16641737365747381a366706f6c696379644e6f6e656a61737365745f6e616d65644e6f6e6566616d6f756e74a1694576616c506172616da16b45787065637456616c756582687175616e7469747963496e74686f7074696f6e616cf4a46761646472657373a1694576616c506172616da16b45787065637456616c756582696d6964646c656d616e674164647265737365646174756d644e6f6e6566616d6f756e74a16641737365747381a366706f6c696379644e6f6e656a61737365745f6e616d65644e6f6e6566616d6f756e74a1694576616c506172616da16b45787065637456616c7565826374617863496e74686f7074696f6e616cf4a46761646472657373a1694576616c506172616da16b45787065637456616c7565826673656e646572674164647265737365646174756d644e6f6e6566616d6f756e74a16b4576616c4275696c74496ea16353756282a16b4576616c4275696c74496ea16353756282a16a4576616c436f65726365a16a496e746f417373657473a1694576616c506172616da16b457870656374496e7075748266736f75726365a56761646472657373a1694576616c506172616da16b45787065637456616c7565826673656e64657267416464726573736a6d696e5f616d6f756e74a16b4576616c4275696c74496ea16341646482a16b4576616c4275696c74496ea16341646482a16641737365747381a366706f6c696379644e6f6e656a61737365745f6e616d65644e6f6e6566616d6f756e74a16b4576616c4275696c74496ea16341646482a1694576616c506172616da16b45787065637456616c756582687175616e7469747963496e74a1694576616c506172616da16b45787065637456616c7565826374617863496e74a1694576616c506172616d6a45787065637446656573a16c4576616c436f6d70696c6572a16e436f6d707574654d696e5574786fa1664e756d6265720263726566644e6f6e65646d616e79f56a636f6c6c61746572616cf4a16641737365747381a366706f6c696379644e6f6e656a61737365745f6e616d65644e6f6e6566616d6f756e74a16b4576616c4275696c74496ea16341646482a1694576616c506172616da16b45787065637456616c756582687175616e7469747963496e74a1694576616c506172616da16b45787065637456616c7565826374617863496e74a1694576616c506172616d6a45787065637446656573686f7074696f6e616cf46876616c6964697479f6656d696e747380656275726e7380656164686f63806a636f6c6c61746572616c80677369676e657273f6686d6574616461746180",
"encoding": "hex",
"version": "v1beta0"
}