Canton Transfer - Replay attack
Hi guys,
I’m using the ledger api to do transfers between two parties. I store my private keys and sign the messages, verify the signature and then submit it to my Canton node - all good.
My concern is that I can replay my transfers infinitely until I have emptied the wallet, if someone breaks our security. Is there any recommendation on how to prevent replay attacks in Canton?
@Bacarden I’m not sure I understand the concern (or the scenario you describe). You cannot successfully replay the exact same command. The command you submit to the Ledger API includes the UTXO contracts that you want to transfer. If the command succeeds, these UTXO contracts will be consumed. Then, if you submit the exact same command with the same UTXO contracts again, the command will fail because the UTXO contracts it references will no longer be active (they will have been consumed by the previous command).
Does this address the concern you have?
In Canton, the usual protection is command deduplication plus on-ledger idempotency.
Give each transfer a unique command/change ID, reuse that same ID for retries, and submit via the same participant so duplicates can be rejected.
But the more important part is to design the Daml workflow so the transfer consumes a unique contract/state and therefore can only succeed once.
You’re partially protected already: in Canton/Daml a successful transfer consumes the input contracts, so the exact same command can’t be replayed against the same state.
However, that alone doesn’t fully prevent replay at the intent level (e.g. resubmitting old signed payloads, retries, or slightly modified commands).
To make this robust:
-
Treat each transfer as a single-use state transition (consume inputs → can only succeed once).
-
Use a unique commandId/transferId and rely on deduplication (noting it’s time-bounded).
-
Add a business-level unique ID or nonce per sender on-ledger so the same transfer intent can’t execute twice.
-
Include expiry/TTL in the signed payload to prevent delayed replays.
In short: contract consumption prevents exact replays, but you should also enforce idempotency + intent uniqueness + time bounds to fully protect against replay attacks.
The CommandId was the keyword I needed. I will try it out, thanks all! ![]()
@Bacarden Nice, glad that helped ![]()