Submit Externally Signed Transactions - Part 1
This tutorial demonstrates how to submit Daml commands to a Canton ledger using an external private key for transaction authorization. Before proceeding, it is recommended to review the external signing overview to understand the concept of external signing. The tutorial illustrates the external signing process using two external parties,Alice and Bob, leveraging the Ping Daml Template which is included by default in all participant nodes.
- In Part 1
Alicecreates aPingcontract. - In Part 2
Bobexercises theRespondchoice on the contract and archives it.
Prerequisites
For simplicity, this tutorial assumes a minimal Canton setup consisting of one participant node connected to one synchronizer (which includes both a sequencer node and a mediator node).Start Canton
To obtain a Canton artifact refer to the getting started section. First, navigate to the interactive submission example folder located atexamples/08-interactive-submission in the Canton release artifact.
All commands in this tutorial are expected to be run from that folder.
Setup
Navigate to the interactive submission example folder located atexamples/08-interactive-submission in the Canton release artifact.
This tutorial demonstrates external signing with two external parties: Alice and Bob. If you haven’t onboarded an external party yet, refer to the onboarding tutorial.
To proceed, gather the following information:
Alice’s Party Id, protocol signing private key, and protocol signing public key fingerprintBob’s Party Id- Synchronizer Id to which the participant is connected
- gRPC Ledger API endpoint
AliceParty Id:alice::1220d466a5d96a3509736c821e25fe81fc8a73f226d92e57e94a65170e58b07fc08eBobParty Id:bob::1220254d06095b407f8c6a378b6fc443a67d3356ab8edfbf1378cb3e44218de32c8a- Synchronizer Id:
da::12203c0ecb446b35b0efa78e0bda9fd91716855866150a5eb7611a2ed5d418129de3 - gRPC Ledger API endpoint:
localhost:4001
API
This tutorial interacts with theInteractiveSubmissionService, a service available on the gRPC Ledger API of the participant node.
Python
It is recommended to use a dedicated python environment to avoid conflicting dependencies. Considering using venv.Shell
For a terminal-based approach, install the following tools:1. Prepare the transaction
TransformLedger Command into a Daml Transaction.
Bash
Request:
Record the response in
create_ping_prepare_response.json to make it easier to submit the transaction afterwards. Now inspect the response with
Request
user_id: Identifier for the application interacting with the ledger.command_id: Unique, random string identifying this specific command. Each command submission must have a new and uniquecommand_id.act_as: ID of the party issuing the command.synchronizer_id: ID of the synchronizer that processes the transaction upon submission.commands: Ledger commands for submission. In this case, it shows the creation of a Ping contract withAliceas the initiator,Bobas the responder, and aping_idvalue. See the Ledger API command reference for details.
Response
Transaction
Represents the explicit ledger changes upon successful commitment.version: Version of the transaction. This is also calledLF Version.roots: List of root node ids. A Daml transaction is a list of trees. The nodes are flattened in a single list (see below). The root node ids design the root node of each individual tree in the transaction.
A current limitation of externally signed transactions is that they can only contain a single root node, and therefore a single transaction tree.
nodes: List of all nodes in the transaction. There are 4 types of nodes:Create,Exercise,FetchandRollback. The number, type and content of each node depends on the Daml model and the state of the ledger.node_seeds: List of seeds used by the Canton protocol to generate cryptographically secure salts. They can be ignored.
Metadata
Additional information required for transaction processing.-
ledger_effective_time: Time picked during the interpretation of the command into a transaction. Set if and only if the daml model makes use of time. -
submitter_info: Contains theact_asparty andcommand_id -
synchronizer_id: Synchronizer that will be used for transaction processing -
transaction_uuid: Unique value generated by the prepare endpoint to uniquely identify this transaction- The transaction UUID is randomly selected during the
preparestep and is fixed from that point forward. This allows the mediator node to de-duplicate transactions and prevent replays.
- The transaction UUID is randomly selected during the
-
mediator_group: Group of mediators that will gather confirmation responses for the transaction. Can be ignored. -
submission_time: The timestamp that the Canton protocol will use as a submission time to perform validations (e.g for de-duplication) -
disclosed_events: Existing input contracts used in the transaction -
global_key_mapping: Unused in the current version, can be ignored.
Hash
prepared_transaction_hash: Pre-computed transaction hash. For security reasons the hash should be re-computed client-side as mentioned in the Compute transaction hash section.
The
prepare API can return additional details on how the Canton node is hashing the transaction to help troubleshoot hash-related errors (for example: pre-computed and re-computed hash mismatch).
To enable it:- Enable verbose hashing on the participant config
ledger-api.interactive-submission-service.enable-verbose-hashing = true. - Set the
verbose_hashingfield in thePrepareSubmissionRequesttotrue.
Hashing scheme version
Version of the hashing scheme:If the gRPC Ledger API authorization is enabled, the user must have the
readAs claim on behalf of Alice to call the prepare endpoint.Traffic cost estimation
Estimates the traffic cost for the Participant Node that submits the transaction to the synchronizer. See the API documentation for details on the estimation response fields. The precision of the estimation is influenced by several factors that cannot be known when the transaction is being prepared. Additionally, the traffic cost will be incurred by the node submitting the transaction, not the one preparing it. When they are the same, the cost estimation will be more accurate. The following factors contribute to the uncertainty of the cost estimation:In most cases the impact of those factors is low and the variance they cause can generally be expected to be 10% or less. Hints can be specified in the prepare request to help improve the precision of the estimation. It’s worth noting that request amplification can significantly increase traffic cost when triggered. The estimation provided is valid for a single confirmation request submission. Subsequent amplified requests sent will cost additional traffic cost as they correspond to a new confirmation request being sent. For details on traffic management, read the related explanation page.
- Hosting relationship of the submitting party with the executing node
- Number and type of external signatures provided upon submission of the transaction
- Topology state of the network when the transaction will be submitted
- Request amplification during submission
- Part of the transaction that will be confirmed (root view or sub views)
- Whether nodes approve or reject the transaction
- IDs of contract created within the transaction are not suffixed in the cost estimation, whereas they are suffixed in the actual submission
- Whether session signing keys are enabled on the submitting or confirming nodes
2. Validate the transaction
Deserialize and inspect the transaction to verify its correctness before proceeding. The initiator of the transaction must be able to inspect and validate it to ensure it matches their intent before proceeding. See the Trust Model for guidance.3. Compute the transaction hash
It is strongly recommended that the transaction hash be recomputed from the transaction and metadata to verify correctness. The pre-computed hash provided in thePrepare step is for debugging purposes.
- The hashing algorithm specification is available here as well as in the release artifact under
protobuf/ledger-api/com/daml/ledger/api/v2/interactive/README.md - An example implementation in python is available in the release articact under
examples/08-interactive-submission/daml_transaction_hashing_v2.py
4. Sign the transaction hash
UsingAlice’s protocol signing private key, sign the hash.
Technically what is needed is the ability to sign with
Alice’s key, not the key itself. The management of the key can be delegated to a wallet, HSM or crypto custody provider. In this tutorial the key is managed locally and explicitly to demonstrate the signing process. Refer to the onboarding tutorial for details on how to generate a key for this tutorial.Alice's private key is stored in a file called alice::1220d466a5d96a3509736c821e25fe81fc8a73f226d92e57e94a65170e58b07fc08e-private-key.der, he hash can be signed using openssl.
In this tutorial the hash retrieved from the response of step 1 will be signed, without re-computing it as suggested in step 3. For an example of how to re-compute the hash, see the
Python example.party_private_key is assumed to be an EllipticCurvePrivateKey Python object containing Alice’s private key. If the onboarding tutorial was followed, this key should already be available.
5. Execute the transaction
Submit the transaction and its signature to the ledger. Bash-
submission_id: Random string uniquely generated for this submission. This differs from thecommand_idin that a retry of this same prepared transaction would necessitate a newsubmission_id. Thesubmission_idis used to correlate several submissions of the same command with completion events (See next step for more on completion events).- Because
submission_idis not part of the signature, a command can be re-submitted with a differentsubmission_idwithout requiring a new signature.
- Because
-
signatures: Object containing the signature of the transaction hash, along with metadata. In particular:signing_algorithm_spec: Will vary depending on the key used during onboarding.signed_by: Fingerprint of the protocol signing public key ofAlice. This tutorial assumes the same key was used to createAlice’s namespace and her protocol signing key. This is why the fingerprint of the signing key matches the second part of her Party Id (after::). For more details check out the onboarding tutorial and the Daml parties guide.
If the gRPC Ledger API authorization is enabled, the user must have the
actAs claim on behalf of Alice to call the execute endpoint.6. Observe the transaction outcome
Monitor the completion stream for transaction confirmation, then retrieve the contract ID and binary blob representation. Bashstatus.code: A value of0indicates the command completed successfullyoffset: Ledger offset for the eventupdate_id: Unique Id for this completion eventsubmission_id: The submission Id chosen in the submission step
offset and update_id for the next steps.
You may need to interrupt the command with
Ctrl-C as the completion stream is a gRPC server streaming RPC which waits for updates from the server until interrupted.events list includes a created object, representing the newly created contract. Extract the corresponding contract_id for reference. To finalize this step, retrieve the binary blob representation of the created contract. This serialized form will be required when executing a choice on the contract in Part 2.
contract_id. The created_event_blob contains a serialized version of the contract, which can be used in subsequent transactions to exercise choices on it.
Python
contract_id is available.
To complete this part, the next step is to retrieve the binary blob of the creation event for the Ping contract. This serialized representation will be required in Part 2 when executing a choice on the contract.
Bob exercises the Respond choice to archive the contract.
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
Alicecreated aPingcontract. - In Part 2
Bobexercises theRespondchoice on the contract and archives it.
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.
Python
If you are following this tutorial in Python, generate gRPC Python classes by following the setup instructions in theREADME 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
Prepare request is very similar to the one from Part 1, with the following differences:
act_as: Now theresponder,Bob, instead of theinitiator,Alice. This makes sense becauseBobis the one exercising the choice on the contract.commands: The command is now anExercisecommand instead of aCreatecommand. Notably it requires thecontract_idfrom 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.
Submit and observe archived contract
examples/08-interactive-submission folder and can be run with