Note
|
RFC-Number: 007 Status: Draft + Created on: 2019-03-12 |
RFC003 requires ledgers have an identity type specified to negotiate a SWAP.
The identity to be used on Ethereum is the Ethereum address as defined in equation 284 of the Ethereum Yellowpaper: the right most 160-bits (20 bytes) of the Keccak-256 hash of the corresponding ECDSA public key.
An ethereum address MUST be encoded as this 20 byte hex string prefixed by 0x
(as is standard in the Ethereum ecosystem).
Furthermore, implementations MUST also accept EIP50 mixed case addresses and MAY verify the checksum.
This RFC extends the registry with the following entry in the identity table:
Ledger | Identity Name | Description |
---|---|---|
Ethereum |
|
An Ethereum Address |
Note that since contract addresses and user addresses are indistinguishable, a contract address could be used as an identity. RFCs that use the Ethereum identity defined here should explain the impact (if any) this has on the protocol.
This RFC specifies SHA-256 as the only value the hash_function
parameter to comit-rfc-003
may take if Ethereum is used as a ledger.
This may be expanded in subsequent RFCs.
The parameters for the Ether HTLC follow RFC003 and are described concretely in the following table:
Variable | Description |
---|---|
asset |
The quantity of wei |
secret_hash |
The hash of the |
redeem_identity |
The |
refund_identity |
The |
expiry |
The absolute UNIX timestamp in seconds after which the HTLC can be refunded |
This RFC defines a single disposable contract as the HTLC.
It uses the selfdestruct
EVM opcode to release the funds to the intended party.
This approach was chosen over a stateful contract written in solidity so that the contract can be precisely defined, verified and updated.
The redeem_identity
and refund_identity
are hard coded into the contract.
If called with the correct secret
as the calldata it will transfer all the Ether to the redeem_identity
using selfdestruct
.
If called without any calldata after the expiry
it will transfer all the Ether to the refund_identity
using selfdestruct
.
The contract never checks the caller so the redeem and refund transactions can be sent by anyone.
The funds will only ever be transferred to the redeem_identity
or the refund_identity
.
No special considerations need to be made when either identity is a contract address as they simply receive funds.
The contract is compiled from the following EVM assembly:
{
// Load received secret size
calldatasize
// Check if secret is zero length
iszero
// If secret is zero length, jump to branch that checks if expiry time has been reached
check_expiry
jumpi
// Load expected secret size
32
// Load received secret size
calldatasize
// Compare secret size
eq
iszero
// If passed secret is wrong size, jump to exit contract
invalid_secret
jumpi
// Load secret into memory
calldatacopy(0, 0, 32)
// Hash secret with SHA-256 (pre-compiled contract 0x02)
call(72, 0x02, 0, 0, 32, 33, 32)
// Placeholder for correct secret hash
<secret_hash>
// Load hashed secret from memory
mload(33)
// Compare hashed secret with existing one
eq
// Combine `eq` result with `call` result
and
// Jump to redeem if hashes match
redeem
jumpi
// Continue to invalid secret if no match
invalid_secret:
// return "invalidSecret" = 0x696e76616c69645365637265740000000000000000000000000000000000000000
mstore(0, "invalidSecret")
revert(0, 32)
check_expiry:
// Timestamp of the current block in seconds since the epoch
timestamp
// Placeholder for refund timestamp
<expiry>
// Compare refund timestamp with current timestamp
lt
// Jump to refund if time is expired
refund
jumpi
// return "too early" = 0x746f6f4561726c79000000000000000000000000000000000000000000000000
mstore(0, "tooEarly")
revert(0, 32)
redeem:
// log ascii to hex of "redeemed"
// 0x72656465656d6564000000000000000000000000000000000000000000000000
log1(0, 32, "redeemed")
selfdestruct(<redeem_identity>)
refund:
// log ascii to hex of "refunded"
// 0x726566756e646564000000000000000000000000000000000000000000000000
log1(0, 0, "refunded")
selfdestruct(<refund_identity>)
}
The following is the contract deployment code template encoded as hex which will deploy the above contract when the placeholder values are replaced with the parameters.
61012861000f6000396101286000f3361561007957602036141561004f57602060006000376020602160206000600060026048f17f100000000000000000000000000000000000000000000000000000000000000160215114166100ae575b7f696e76616c69645365637265740000000000000000000000000000000000000060005260206000fd5b426320000002106100eb577f746f6f4561726c7900000000000000000000000000000000000000000000000060005260206000fd5b7f72656465656d656400000000000000000000000000000000000000000000000060206000a1733000000000000000000000000000000000000003ff5b7f726566756e64656400000000000000000000000000000000000000000000000060006000a1734000000000000000000000000000000000000004ff
To compile the contract code replace the placeholder values with the HTLC parameters at the following offsets:
Name | Byte Range | Length (bytes) |
---|---|---|
|
53..85 |
32 |
|
139..143 |
4 |
|
229..249 |
20 |
|
290..310 |
20 |
The contract emits two logs:
Topic | keccack256(Topic) | Data | Emitted When |
---|---|---|---|
ascii to hex of |
0x72656465656d6564000000000000000000000000000000000000000000000000 |
|
The contract is redeemed |
ascii to hex of |
0x726566756e646564000000000000000000000000000000000000000000000000 |
- |
The contract is refunded |
The contract returns two different error values for the failure cases:
Description | bytes | Emitted When |
---|---|---|
ascii to hex of |
0x696e76616c69645365637265740000000000000000000000000000000000000000 |
Attempt to redeem with an invalid secret |
ascii to hex of |
0x746f6f4561726c79000000000000000000000000000000000000000000000000 |
Attempt to refund too early |
Implementations SHOULD use the following gas limits on the transactions related to the contract:
Transaction | recommended gas limit |
---|---|
Deployment |
121,800 |
Redeem |
100,000 |
Refund |
100,000 |
The following section describes how both parties should interact with the Ethereum blockchain during the RFC003 execution phase.
At the start of the deployment stage, both parties compile the contract code as described in the previous section.
We will call this value contract_code
.
To deploy the Ether HTLC, the funder must deploy the contract_code
.
They SHOULD do this by sending a contract deployment transaction to the relevant Ethereum blockchain with:
-
contract_code
as thedata
of the transaction -
The quantity of wei specified in the Ether Asset header as the
value
of the transaction
The funder SHOULD NOT do this through another contract, i.e. by executing the CREATE
opcode.
To be notified of the deployment event, both parties MAY watch the blockchain for a transaction with the contract_code
as the data.
Upon observing the deployment transaction, both parties SHOULD record the address the contract was deployed to (referred to as contract_address
from now on).
Before redeeming, the redeemer SHOULD wait until the deployment transaction has enough confirmation such that they consider it permanent.
To redeem the HTLC, the redeemer SHOULD send a transaction to the contract_address
with the data
of the transaction set to the secret
.
To be notified of the redeem event, both parties SHOULD watch the blockchain for contract_address
emitting the Redeemed()
log.
If Ethereum is the beta_ledger
(see RFC003), then the funder MUST watch for such a log, extract the secret
from the transaction receipt and continue the protocol.
In this case, Bob MUST NOT watch for a transaction sent to contract_address
with the secret
as data
.
This would be insufficient, because he would miss learning the secret
if the contract is redeemed by a call from another contract (rather than from a transaction).
To refund the HTLC, the funder MUST send a transaction to contract_address
with empty data in a block with timestamp greater than expiry
.
To be notified of the refund event, both parties SHOULD watch the blockchain for contract_address
emitting the Refunded()
log.
A transaction to the HTLC will fail in two cases:
-
If redeem was attempted with an invalid secret: the contract will return
invalidSecret
in bytes (0x696e76616c69645365637265740000000000000000000000000000000000000000
) -
If refund was attempted too early: the contract will return
tooEarly
in bytes (0x746f6f4561726c79000000000000000000000000000000000000000000000000
)
This RFC extends the registry with an identity definition for the Ethereum ledger:
Ledger | Identity Name | Encoding | Description |
---|---|---|---|
Ethereum |
|
|
An Ethereum Address |
The following shows an RFC003 SWAP REQUEST where the alpha_ledger
is Ethereum, the alpha_asset
is 1 Ether (with …
being used where the value is only relevant for the beta_ledger
).
{
"type": "SWAP",
"headers": {
"alpha_ledger": {
"value": "ethereum",
"parameters": { "network": "mainnet" }
},
"beta_ledger": {...},
"alpha_asset": {
"value": "ether",
"parameters": { "quantity": "1000000000000000000" }
},
"beta_asset": {...},
"protocol": {
"value" : "comit-rfc-003",
"parameters" : { "hash_function" : "SHA-256" }
}
},
"body": {
"aplha_ledger_refund_identity": "0x0f59e9e105be01d5e2206792a267406f255c5ea5",
"alpha_expiry": 1552263040,
"secret_hash" : "ac5a18da6431ed256965b873ef49dc15a70a0a66e2d28d0c226b5db040123727",
"beta_ledger_redeem_identity" : "...",
"beta_expiry" : ...
},
}
Note, the pre-image of secret_hash
is 51a488e06e9c69c555b8ad5e2c4629bb3135b96accd1f23451af75e06d3aee9c
.
A valid RESPONSE
to the above REQUEST
could look like:
{
"status" : "OK00",
"body": {
"alpha_ledger_redeem_identity": "0x53fd2cac865d3aa1ad6fbdebaa00802c94239fba",
"beta_ledger_refund_identity": "..."
}
}
The above REQUEST
and RESPONSE
results in the following parameters to the HTLC:
Parameter | value |
---|---|
redeem_identity |
|
redund_identity |
|
secret_hash |
|
expiry |
1552263040 |
Which should compile to the following contract_code
:
61012861000f6000396101286000f3361561007957602036141561004f57602060006000376020602160206000600060026048f17fac5a18da6431ed256965b873ef49dc15a70a0a66e2d28d0c226b5db04012372760215114166100ae575b7f696e76616c69645365637265740000000000000000000000000000000000000060005260206000fd5b42635c85a780106100eb577f746f6f4561726c7900000000000000000000000000000000000000000000000060005260206000fd5b7f72656465656d656400000000000000000000000000000000000000000000000060206000a17353fd2cac865d3aa1ad6fbdebaa00802c94239fbaff5b7f726566756e64656400000000000000000000000000000000000000000000000060006000a1730f59e9e105be01d5e2206792a267406f255c5ea5ff