Cant seem to fetch transaction (via Java)

Hi there,

I’m using Java sdk 1.18.1. My aim is to imitate the pingPongReactive behaviour so that my app will open an ongoing stream with the Ledger (not sandbox), detect if a certain contract exists and fetch it.

I created a contract on TPA identity and i’m trying to understand why the following filter doesn’t find it:

Identifier TEMPLATE_ID = new Identifier("32c577df433e26dd7c66dda0808f50dcfc603d83d2814bc8b7a4531aea4a2897", "SE2.Notifications", "PolicyPagesNotification");

Flowable<Transaction> transactions = client.getTransactionsClient().getTransactions(
                LedgerOffset.LedgerBegin.getInstance(),
                filterFor(TEMPLATE_ID, TPA_PARTY_ID), true, TPA_TOKEN);

The template id (first argument in the filter) is a generated object

I verified that the id is the same on the Ledger (see screenshot), the template name is: “SE2.Notifications” and the entityName is: “PolicyPagesNotification”

Thanks in advance

1 Like

What is the value of TPA_PARTY_ID? It should be the ledger party literal (ledger-party-SOME_LONG_UUID—from your example, it looks like ledger-party-79995d65-ab91-4e4d-82a0-e5fb8d72b71d) and not TPA, which is merely the display name.

Hi Davin,

Not sure i follow you. The TPA Party ID from my Ledger and Java app is:

public static final String TPA_PARTY_ID = "ledger-party-79995d65-ab91-4e4d-82a0-e5fb8d72b71d";

and indeed corresponds to the signatory we see in the screenshot. I assume this is ok?

can you share a minimal example to reproduce the issue?

Sure @cocreature Please GitHub - liavezer/daml-reactive: This repo show case how we subscribe to a stream of contracts (transactions) and once a contract exists Java app automatically gets triggered

I can’t fully reproduce given that you didn’t include the DAR and that I don’t know what data you created exactly (via the hub UI or some other way) to get to this state. However, a few suggestions:

  1. Your class accepts the party, the token and the identifier as a constructor argument daml-reactive/PolicyPagesNotificationProcessor.java at a91a2bf9aa2ddc61c1193652d0056df557a91a18 · liavezer/daml-reactive · GitHub. However, then later you hardcode it again instead of referring to those arguments daml-reactive/PolicyPagesNotificationProcessor.java at a91a2bf9aa2ddc61c1193652d0056df557a91a18 · liavezer/daml-reactive · GitHub and some other places. I suggest that you only refer to the class fields and don’t have any direct references to make sure that you don’t end up referring to two different parties or template ids. Search for references to DownloadPolicyPagesFromLedger and PolicyPagesNotification and replace them by class fields. I think at the moment this is actually consistent but this will easily break during refactoring
  2. Your stream processing function just returns null if you have the right transaction daml-reactive/PolicyPagesNotificationProcessor.java at a91a2bf9aa2ddc61c1193652d0056df557a91a18 · liavezer/daml-reactive · GitHub which is going to cause an error (which you may be swallowing silently somewhere)’ I suspect this might be the issue: Your application receives the transaction but then it fails to process it.

Hi @cocreature , 10x for your eyes on the code. Off course you need the daml models and/or the dar. I’ll re-generate it and send it over.

Regarding #1 - totally right…coding in a hurry, no excuses. However, i suspect its still solid and the transaction list returned with content from the Ledger so identities aren’t the issue.

Regarding #2 I just wanted to verify that the transactions i fetch contains the one that i’m interested about and yes - all parties (beside default ones if exists) are manually created. Also contracts.
This is the same Ledger that i ran against the pingPongReactive app - so to be accurate - some of the templates there were creating by Alice and Bob.

My problem is in line number 88 that i call the equals method of Identity object with apparently two different objects. One (‘template’) is a Messages module and the other (‘policyPagesNotificationIdentifier’) is a Notification one.
Can you explain in short the relation between those entities? (in DAML hub context i mean)

        boolean isPolicyPagesNotification = template.equals(policyPagesNotificationIdentifier);

My problem is in line number 88 that i call the equals method of Identity object with apparently two different objects. One (‘template’) is a Messages module and the other (‘policyPagesNotificationIdentifier’) is a Notification one.
Can you explain in short the relation between those entities? (in DAML hub context i mean)

I’m not quite following, you are filtering by template id so you should not receive events for any other template. I also don’t see a Messages module in your code. What is the full template identifier (package id, module name, entity name) you are observing?

contingent-claims-0.0.1.dar (740.5 KB)
(dar uploaded)

@cocreature - Sorry for not being clear. Let me try again.

My end game is to subscribe to a contract stream and get notified whenever a certain contract (the one below in screenshot) exists in the Ledger under a certain party (called “TPA”):
image

I’m trying to compose pieces of code snippets to accomplish this goal.

The first step of finding the package id looks like passed successfully. Meaning that i looped over all listed packages in the Ledger and matched this DottedName

segments: "SE2"
segments: "Notifications"

(see DownloadPolicyPagesFromLedger::containsPolicyPagesNotificationModule method):

By the way, I think i can skip this step since i already have a generated class holding this packageId but still i want to automatically detect it.

Than, after creating a Processor class, i try fetching all transactions given my party (“TPA”) and NoFilter.instance (see runIndefinitely() method)

This list is indeed populated with transactions! Here comes the part that i’m confused and i might be composing irrelevant pieces of code from the pingPongReactive app:
For each transaction i fetch its events of type “CreatedEvent” (“processEvent()” method that temporarily returns null).
I was hoping that from each event instance i would be able to fetch its contract but i only see contractId and contractKey getters:

Am i getting close to the end game stated above :upside_down_face:

getArguments gives you the contract payload/value which sounds like it’s what you’re looking for?

I strongly recommend against trying to detect it via the package service. There can be multiple templates with the same module & template name (but different package ids) so you cannot do this reliably.

Thanks Moritz.

I believe that i can fetch the contract simply and directly from the client like below code demonstrate.
But how do i subscribe to the stream that “notifies” me that such contract exists in the Ledger instead of pro-actively search for it like here:

(the “fromCreatedEvent” method is generated )

private static void processActiveContracts(DamlLedgerClient client) {
    ActiveContractsClient activeContractsClient = client.getActiveContractSetClient();
    TransactionFilter filter = filterFor(TEMPLATE_ID_IDENTIFIER, ADMIN_PARTY_ID);
    Flowable<GetActiveContractsResponse> activeContracts = activeContractsClient.getActiveContracts(filter, true);

    activeContracts.blockingForEach(
        response -> {
          response.getCreatedEvents().stream()
              .map(PolicyPagesNotification.Contract::fromCreatedEvent)
              .forEach(
                  contract -> {
                    // do something with the contract
                  });
        });
  }

Subscribing to the transaction stream should work for this. I don’t understand the issue you are encountering. You said you receive the transaction which has the created event inside is so which part is missing?

Hi @cocreature ,

I deleted everything and started from scratch. Now i successfully perform the following:

  1. Connect to the Ledger
  2. Find the contract i need with the code i posted earlier (processActiveContracts method) given the module generated identifier (above called TEMPLATE_ID_IDENTIFIER) and the party’s ID (ADMIN_PARTY_ID).

How do i change the program to trigger the above method whenever such contract exists on the Ledger?
Meaning - How do i convert the program to subscribe to the contract stream so that given a certain contract created on the Ledger (with specific module id) my app will be notified via gRPC stream.
Off course i’ll convert the app to stay up always (using spring-boot mS)

Thanks

I think I still don’t understand what exactly you are looking for. getTransactions will subscribe to the transaction stream and give you all incoming transactions (provided you don’t specify an end offset) and thereby the create events. That’s what the runIndefinitely method in your example is doing. In the original example, that’s used to see all creates of Ping and Pong contracts as they are created.

It sounds like you are looking for something different but I don’t understand in what way it should be different.

I’m new to DAML so i really have hard time understand the correlation between the those terminologies:

  • Transactions
  • Contracts
  • Events

My aim is to subscribe to a stream that will notify me whenever a certain contract (matching a certain module name) has been created.

Can i do it via the getTransactions and fetch this contract from the Transaction object?

A transaction on the (flat) transaction stream (there is also a transaction tree stream which you don’t need here so I’ll ignore it) is a list of events with each event being either a create or an archive. It sounds like you primarily care about the create events here.
The three main values that you probably want to look at in the create event are the contract id, the template id and lastly the contract arguments. The contract arguments refer to the value you pass to create which I suspect is what you are looking for when you say you want to “fetch this contract from the transaction object”.

Thanks to your explanation @cocreature i realized that previouse code was indeed the correct path to solve it (beside your recommendation to avoid fetching transactions by packageId which i’ll refactor)

Can you refer me to a code example of how to fetch the contract from a given Create event?

This is what i see inside the event object that i retrieved from the transaction stream (processEvent method in my code)

I don’t have a code sample but it looks like you are already calling getArguments which is exactly the right thing to do. Is it missing some information that you are looking for?

OK, i’ve managed to assemble the contract using a helper method from the generated module object (see line above the TODO).

In order to avoid returning null i imitated the pingPongReactive behaviour and returned a stream of command of type ARCHIVE choice.

@cocreature My question is:
Is a real stream really opened under the hood here?
Meaning - If i’ll force the program to stay up and i’ll create such contract (that complies to the one i look for in the code) on the Ledger - will i suddenly get invoked? If yes - which method will be invoked?

I guess i’m asking that since i’m new also to the gRPC concept and i’m trying to compare it to a REST http endpoint (e.g Servlet) that listens to incoming HTTP requests and gets invoked once a client calls it.

    private Stream<Command> processEvent(String workflowId, CreatedEvent event) {
        Identifier template = event.getTemplateId();

        boolean isPolicyPagesNotification = template.equals(policyPagesNotificationIdentifier);

        if (!isPolicyPagesNotification) {
            return Stream.empty();
        }
        else {
            System.out.println("Found module " + policyPagesNotificationIdentifier.toString() + " !!");
            PolicyPagesNotification.Contract contract = PolicyPagesNotification.Contract.fromCreatedEvent(event);

            //TODO send contract to FS and S3

            // assemble the exercise command
            Command cmd = new ExerciseCommand(
                    template,
                    event.getContractId(),
                    DownloadPolicyPagesFromLedger.CHOICE_NAME,
                    new DamlRecord(Collections.emptyList()));

            return Stream.of(cmd);
        }
    }

gRPC streams work more like websockets than listening for gRPC requests. Every time a new transaction comes in the method you pass to transaction.forEach(method) will get invoked.

1 Like