Conditional-upon-Transfer-Decryption for DvP
Video
Original
Abstract
The interfaces in this proposal model a functional transaction scheme to establish a secure delivery-versus-payment across two blockchains, where a) no intermediary is required and b) one of the two chains can securely interact with a stateless "decryption oracle". Here, delivery-versus-payment refers to the exchange of, e.g., an asset against a payment; however, the concept is generic to make a transfer of one token on one chain (e.g., the payment) conditional to the successful transfer of another token on another chain (e.g., the asset).
The scheme is realized by two smart contracts, one on each chain.
One smart contract implements the ILockingContract interface on one chain (e.g. the "asset chain"), and another smart contract implements the IDecryptionContract interface on the other chain (e.g., the "payment chain").
The smart contract implementing ILockingContract locks a token (e.g., the asset) on its chain until a key is presented to encrypt to one of two given values.
The smart contract implementing IDecryptionContract, decrypts one of two keys (via the decryption oracle) conditional to the success or failure of the token transfer (e.g., the payment). A stateless decryption oracle is attached to the chain running IDecryptionContract for the decryption.
In addtion there are to interface that standardize the communication with external decryption oracle(s).
The interfaceIDecryptionOracle.sol is implemented by a decryption oracle proxy contract.
The interface IDecryptionOracleCallback.sol has to be implemented by a callback receiving the decrypted key.
Motivation
Within the domain of financial transactions and distributed ledger technology (DLT), the Hash-Linked Contract (HLC) concept has been recognized as valuable and has been thoroughly investigated. The concept may help to solve the challenge of delivery-versus-payment (DvP), especially in cases where the asset chain and payment system (which may be a chain, too) are separated. A prominent application of smart contracts realizing a secure DvP is that of buying an asset, where the asset is managed on one chain (the asset chain), but the payments are executed on another chain (the payment chain). Proposed solutions are based on an API-based interaction mechanism which bridges the communication between a so-called asset chain and a corresponding payment system or requires complex and problematic time locks.1
Here, we propose a protocol that facilitates secure delivery-versus-payment with less overhead, especially with a stateless oracle.2
Specification
Methods
Smart Contract on the chain that performs the locking (e.g. the asset chain)
The following methods specify the functionality of the smart contract implementing
the locking. For further information, please also look at the interface
documentation ILockingContract.sol.
Initiation of Transfer: inceptTransfer
function inceptTransfer(uint256 id, int amount, address from, bytes keyHashedSeller, bytes memory keyEncryptedSeller) external;
Called from the buyer of the token to initiate token transfer. Emits a TransferIncepted event.
The parameter id is an identifier of the trade. The parameter from is the address of the seller (the address of the buyer is msg.sender).
The parameter keyHashedSeller is a hash of the key that can be used by the seller to (re-)claim the token.
The parameter keyEncryptedSeller is an encryption of the key that can be used by the buyer to claim the token.
It is possible to implement the protocol in a way where the hashing method agrees with the encryption method. See below on "encryption".
Initiation of Transfer: confirmTransfer
function confirmTransfer(uint256 id, int amount, address to, bytes keyHashedBuyer, bytes memory keyEncryptedBuyer) external;
Called from the seller of the token to confirm token transfer. Emits a TransferConfirmed event.
The parameter id is an identifier of the trade. The parameter to is the address of the buyer (the address of the seller is msg.sender).
The parameter keyHashedBuyer is a hash of the key that can be used by the buyer to (re-)claim the token.
The parameter keyEncryptedBuyer is an encryption of the key that can be used by the buyer to (re-)claim the token.
It is possibly to implement the protocol in a way where the hashing method agrees with the encryption method. See below on "encryption".
If the trade specification, that is, the quadruple (id, amount, from, to), in a call to confirmTransfer
matches that of a previous call to inceptTransfer, and the balance is sufficient, the corresponding amount
of tokens is locked (transferred from from to the smart contract) and TransferConfirmed is emitted.
Transfer: transferWithKey
function transferWithKey(uint256 id, bytes memory key) external;
Called from either the buyer or the seller of the token
of the trade with id id.
If the method is called from the buyer (to) and the hashing of key matches keyHashedBuyer,
then the locked tokens are transferred to the buyer (to). This emits TokenClaimed.
If the method is called from the seller (from) and the hashing of key matches keyHashedSeller,
then the locked tokens are transferred (back) to the seller (to). This emits TokenReclaimed.
Summary
The interface ILockingContract:
interface ILockingContract { event TransferIncepted(uint256 id, int amount, address from, address to, bytes keyHashedSeller, bytes keyEncryptedSeller); event TransferConfirmed(uint256 id, int amount, address from, address to, bytes keyHashedBuyer, bytes keyEncryptedBuyer); event TokenClaimed(uint256 id, bytes key); event TokenReclaimed(uint256 id, bytes key); function inceptTransfer(uint256 id, int amount, address from, bytes memory keyHashedSeller, bytes memory keyEncryptedSeller) external; function confirmTransfer(uint256 id, int amount, address to, bytes memory keyHashedBuyer, bytes memory keyEncryptedBuyer) external; function transferWithKey(uint256 id, bytes memory key) external; }
Smart Contract on the other chain that performs the conditional decryption (e.g. the payment chain)
The following methods specify the functionality of the smart contract implementing
the conditional decryption. For further information, please also look at the interface
documentation IDecryptionContract.sol.
Initiation of Transfer: inceptTransfer
function inceptTransfer(uint256 id, int amount, address from, bytes memory keyEncryptedSuccess, bytes memory keyEncryptedFailure) external;
Called from the receiver of the amount to initiate payment transfer. Emits a TransferIncepted.
The parameter id is an identifier of the trade. The parameter from is the address of the sender of the payment (the address of the receiver is msg.sender).
The parameter keyEncryptedSuccess is an encryption of a key and will be decrypted if the transfer is successful in a call to transferAndDecrypt.
The parameter keyEncryptedFailure is an encryption of a key and will be decrypted if the transfer fails in a call to transferAndDecrypt or if cancelAndDecrypt is successful.
Transfer: transferAndDecrypt
function transferAndDecrypt(uint256 id, int amount, address to, bytes memory keyEncryptedSuccess, bytes memory keyEncryptedFailure) external;
Called from the sender of the amount to initiate completion of the payment transfer. Emits a TransferKeyRequested with keys depending on completion success.
The parameter id is an identifier of the trade. The parameter to is the address of the receiver of the payment (the sender of the payment (from) is implicitly the msg.sender).
The parameter keyEncryptedSuccess is an encryption of the key and will be decrypted if the transfer is successful.
The parameter keyEncryptedFailure is an encryption of the key and will be decrypted if the transfer fails.
The method will not decrypt any key and not perform a transfer of a payment if the values (id, amount, from to, keyEncryptedSuccess, keyEncryptedFailure)
do not match a previous call to inceptTransfer.
Cancelation of Transfer: cancelAndDecrypt
function cancelAndDecrypt(uint256 id, address from, bytes memory keyEncryptedSuccess, bytes memory keyEncryptedFailure) external;
Called from the receiver of the amount to cancel payment transfer (cancels the incept transfer).
The method must be called from the caller of a previous call to inceptTransfer
with the exact same arguments and cancels this specific transfer.
If these preconditions are met and a valid call to transferAndDecrypt has not been issued before,
i.e. if keyEncryptedSuccess has not been issued in a TransferKeyRequested event,
then this method emits a TransferKeyRequested with the key keyEncryptedFailure.
Release of ILockingContract Access Key: releaseKey
function releaseKey(uint256 id, bytes memory key) external;
Called from the (possibly external) decryption oracle.
Emits the event TransferKeyReleased with the value of key if the call was eligible.
Summary
The interface IDecryptionContract:
interface IDecryptionContract { event TransferIncepted(uint256 id, int amount, address from, address to, bytes keyEncryptedSuccess, bytes keyEncryptedFailure); event TransferKeyRequested(address sender, uint256 id, bytes encryptedKey); event TransferKeyReleased(address sender, uint256 id, bool success, bytes key); function inceptTransfer(uint256 id, int amount, address from, bytes memory keyEncryptedSuccess, bytes memory keyEncryptedFailure) external; function transferAndDecrypt(uint256 id, int amount, address to, bytes memory keyEncryptedSuccess, bytes memory keyEncryptedFailure) external; function cancelAndDecrypt(uint256 id, address from, bytes memory keyEncryptedSuccess, bytes memory keyEncryptedFailure) external; function releaseKey(uint256 id, bytes memory key) external; }
Encryption and Decryption
The linkage of the two smart contracts relies on use of a key, encryptedKey and hashedKey.
The implementation is free to support several encryption methods for
as long as the decryption oracle supports it.
The encryption is performed with the public key of the decryption oracle. Either the encryption oracle offers a method performing encryption, in which case the encryption method isn't even required to be known, or both parties know the public key of the decryption oracle and can perform the generation of the key and its encryption.
It is implicitly assumed that the two parties may check that
the strings keyEncryptedBuyer and keyEncryptedSeller are
in a valid format.
To avoid on-chain encryption in the ILockingContract, it is possible to use a
simpler hashing algorithm on the ILockingContract. In that case, the decryption oracle has
to provide a method that allows to obtain the hash H(K) (keyHashed) for an
encrypted key E(K) (keyEncrypted) without exposing the key K (``key`), cf. 2.
Sequence diagram of delivery versus payment
The interplay of the two smart contracts is summarized in the following sequence diagram:

Rationale
The protocol tries to be parsimonious. The transfer
is associated with a (preferably unique) id possibly
generated by some additional interaction of the trading
parties.
The key and the encryptedKey arguments are strings to
allow the flexible use of different encryption schemes.
The decryption/encryption scheme should be inferable from the contents
of the encryptedKey.
Ensuring Secure Key Decryption - Key Format
It has to be ensured that the decryption oracle decrypts a key only for the eligible contract.
It seems as if this would require us to introduce a concept of eligibility to the description oracle, which would imply a kind of state.
A fully stateless decryption can be realized by introducing a document format for the key and a corresponding eligibility verification protocol. We propose the following elements:
- The (unencrypted) key documents contain the address of the payment contract implementing
IDecryptionContract. - The decryption oracle offers a stateless function
verifythat receives an encrypted key and returns the callback address (that will be used for thereleaseKeycall) that is stored inside the decrypted key without returning the decrypted key. - When an encrypted key is presented to the decryption oracle, the oracle decrypts the document and passes the decrypted key to
releaseKeyof the callback contract address found within the document decrypted key.
We propose the following XML schema for the document of the decrypted key:
<?xml version="1.0" encoding="utf-8"?> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://finnmath.net/erc/ILockingContract" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="releaseKey"> <xs:complexType> <xs:simpleContent> <xs:extension base="xs:string"> <xs:attribute name="contract" type="xs:string" use="required" /> <xs:attribute name="transaction" type="xs:unsignedShort" use="required" /> </xs:extension> </xs:simpleContent> </xs:complexType> </xs:element> </xs:schema>
A corresponding XML sample is shown below.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <releaseKey contract="eip155:1:0x1234567890abcdef1234567890abcdef12345678" transaction="3141" xmlns="http://finnmath.net/erc/ILockingContract"> <!-- random data --> zZsnePj9ZLPkelpSKUUcg93VGNOPC2oBwX1oCcVwa+U= </releaseKey>
Multi-Party Delivery versus Payment
Locking is a Feature
In Delivery-versus-Payment (DvP) protocols like ERC-7573, at least one token must be locked to ensure atomicity, even if only for a short period during the transaction.
While locking may appear as an inconvenient necessity, it is in fact a feature that becomes valuable in the construction of conditional trades or multi-party DvPs.
If n parties wish to perform bilateral transactions atomically, there are at least m := 2 • (n - 1) transactions, of which m-1 require locking. The last one can operate directly, and its success or failure decides whether the other locks are released or reverted.
A multi-party delivery versus payment is a valuable trade feature. Consider, for example, the case where counterparty A wishes to buy a token Y (e.g., a bond) from counterparty C, but in order to fund this transaction, counterparty A wishes to sell a token X (e.g., another bond) to counterparty B. However, A does not want to sell bond X if the purchase of Y fails. A multi-party DvP allows these two transactions to be bound into a single atomic unit.
While for a two-party DvP with two tokens only one token requires locking—and hence a DvP can be constructed without locking on the cash chain—a three-party DvP with three tokens in general requires the ability to lock all three tokens.
This highlights that locking is not just a constraint, but a required feature to enable advanced and economically meaningful protocols.
N-DvP with ERC-7573
A multi-party DvP can be created elegantly by combining multiple (n-1) two-party DvPs, for example based on the ERC-7573 protocol.
The procedure is simple: instead of finalizing the respective two-party DvP by a call to transferAndDecrypt, the two-party DvP is first confirmed with a call to confirm, leaving the finalization open.
At any time, any party can call cancelAndDecrypt to release the failure key and revert all lockings.
Once all parties are linked with their respective two-party DvPs, a single call to transferAndDecrypt performs locking of the token implementing the IDecryptionContract and releases either the success key on success or the failure key on failure.
Initiation and Finalization
The counterparty that initiates the multi-party DvP by making the first call
to the IDecryptionContract is the one that is allowed to finalize it via transferAndDecrypt, the other may cancel via cancelAndDecrypt.
Sequence Diagram
Below we depict the corresponding sequence diagram of a multi-party DvP via ERC-7573.
Note that the individual DvP may come in two different flavors depending on which counterparty is the receiver of the token on the IDecryptionContract.
The diagram depicts a multi-party dvp with n+1 counterparties trading n+1 tokens out of which the DvPs are bound by the contract on token 0.
Note: The more general case of N counterparties trading M tokens is just a special case where we enumerate all combination as new counterparties and new tokens.
Security Considerations
The decryption oracle does not need to be a single trusted entity. Instead, a threshold decryption scheme can be employed, where multiple oracles perform partial decryption, requiring a quorum of them to reconstruct the secret key. This enhances security by mitigating the risk associated with a single point of failure or trust.
In such cases, each participating decryption oracle will observe the decryption request from an emitted TransferKeyRequested event, and subsequently call the releaseKey method with a partial decryption result. The following sequence diagram illustrates this.

See 2 for details.
Copyright
Copyright and related rights waived via CC0.
{ "type": "article", "id": 1, "author": [ { "family": "La Rocca", "given": "Rosario" }, { "family": "Mancini", "given": "Riccardo" }, { "family": "Benedetti", "given": "Marco" }, { "family": "Caruso", "given": "Matteo" }, { "family": "Cossu", "given": "Stefano" }, { "family": "Galano", "given": "Giuseppe" }, { "family": "Mancini", "given": "Simone" }, { "family": "Marcelli", "given": "Gabriele" }, { "family": "Martella", "given": "Piero" }, { "family": "Nardelli", "given": "Matteo" }, { "family": "Oliviero", "given": "Ciro" } ], "DOI": "10.2139/ssrn.4386904", "title": "Integrating DLTs with Market Infrastructures: Analysis and Proof-of-Concept for Secure DvP between TIPS and DLT Platforms", "original-date": { "date-parts": [ [2022, 7, 19] ] }, "URL": "http://dx.doi.org/10.2139/ssrn.4386904" }
{ "type": "article", "id": 2, "author": [ { "family": "Fries", "given": "Christian" }, { "family": "Kohl-Landgraf", "given": "Peter" } ], "DOI": "10.2139/ssrn.4628811", "title": "A Proposal for a Lean and Functional Delivery versus Payment across two Blockchains", "original-date": { "date-parts": [ [2023, 11, 9] ] }, "URL": "http://dx.doi.org/10.2139/ssrn.4628811" }
Footnotes
Adopted by projects
Not miss a beat of EIPs' update?
Subscribe EIPs Fun to receive the latest updates of EIPs Good for Buidlers to follow up.
View all