Submit Externally Signed Transactions - Part 2

Complete Part 1 before proceeding.

The tutorial illustrates the external signing process using two external parties, Alice and Bob, leveraging the same Ping Daml Template used in Part 1 of the tutorial.

  • In Part 1 Alice created a Ping contract.

  • In Part 2 Bob exercises the Respond choice on the contract and archives it.

The majority of the work involved in external transaction signing was completed in Part 1. The key addition in Part 2 is utilizing the Ping contract created earlier through explicit disclosure and executing the Respond choice on that contract. The overall process remains similar to Part 1.

Important

This tutorial is for demo purposes. The code snippets should not be used directly in a production environment.

Setup

To proceed, gather the following information:

  • Bob’s Party ID, protocol signing private key, and protocol signing public key fingerprint

  • Synchronizer ID of the synchronizer to which the participant is connected

  • gRPC Ledger API endpoint

  • ping_created_event: Event retrieved in the last step of Part 1.

  • contract_id: ID of the contract created in Part 1.

This information should already be known from the onboarding tutorial and the first part of the external signing tutorial.

Python

If you are following this tutorial in Python, generate gRPC Python classes by following the setup instructions in the README in the example folder.

Exercise Respond Choice

This tutorial does not repeat the material covered in Part 1 regarding transaction preparation, validation, signing, and execution, as these steps remain largely the same. Instead, it highlights the key differences from Part 1.

Prepare the transaction

Create the exercise command
 ping_exercise_command = commands_pb2.Command(
     exercise=commands_pb2.ExerciseCommand(
         template_id=ping_template_id,
         contract_id=contract_id,
         choice="Respond",
         choice_argument=value_pb2.Value(
             record=value_pb2.Record(record_id=None, fields=[])
         ),
     )
 )
Prepare the exercise command
 prepare_exercise_request = interactive_submission_service_pb2.PrepareSubmissionRequest(
     user_id=user_id,
     command_id=str(uuid.uuid4()),
     act_as=[responder],
     read_as=[responder],
     synchronizer_id=synchronizer_id,
     commands=[ping_exercise_command],
     # We need to explicitly disclosed the ping contract we created earlier
     disclosed_contracts=[
         commands_pb2.DisclosedContract(
             template_id=template_id,
             contract_id=contract_id,
             created_event_blob=created_event_blob,
             synchronizer_id=synchronizer_id,
         )
     ],
 )

 prepare_exercise_response = iss_client.PrepareSubmission(prepare_exercise_request)

The Prepare request is very similar to the one from Part 1, with the following differences:

  • act_as: Now the responder, Bob, instead of the initiator, Alice. This makes sense because Bob is the one exercising the choice on the contract.

  • commands: The command is now an Exercise``command instead of a ``Create command. Notably it requires the contract_id from Part 1.

  • disclosed_contracts: The serialized representation of contracts required to process the transaction.

Metadata

The only significant difference with Part 1 in the metadata is: disclosed_events. This field now contains the input Ping contract. It is also included in the hash of the transaction.

Important

Like in Part 1, the transaction must be validated, hashed and signed. The hash computation and signature is performed by the execute_and_get_contract_id function provided at the end of Part 1, as shown in the next section.

Submit and observe archived contract

Submit the exercise transaction and observe the contract being archived
 execute_and_get_contract_id(
     prepared_exercise_transaction,
     responder,
     responder_private_key,
     responder_fingerprint,
 )

 # The contract was archived by exercising the choice, we get an archived event this time
 contract_events = get_events(responder, contract_id)
 if contract_events.HasField("archived"):
     print(
         f"Ping contract with ID {contract_events.archived.archived_event.contract_id} has been archived"
     )
 else:
     raise Exception("Expected an archive event")

By querying the event service and filtering for the contract ID, an archived event is observed, confirming that the contract has been successfully archived.

This concludes the external signing tutorial. The code used in this tutorial is available in the examples/08-interactive-submission folder and can be run with

python interactive_submission.py run-demo

Tooling

The scripts mentioned in this tutorial can be used as tools for testing and development purposes

Decode base64 encoded prepared transaction to JSON

./setup.sh
python daml_transaction_util.py --decode --base64 <base64_encoded_transaction>

Compute hash of base64 encoded prepared transaction

./setup.sh
python daml_transaction_util.py --hash --base64 <base64_encoded_transaction>