Replace¶
Overview¶
The Replace module allows a vault (OldVault) be replaced by transferring the BTC it is holding locked to another vault (NewVault), which provides the necessary ONE collateral. As a result, the ONE collateral of the OldVault, corresponding to the amount of replaced BTC, is unlocked. The OldVault must thereby provide some amount of collateral to protect against griefing attacks, where the OldVault never finalizes the Replace protocol and the NewVault hence temporarily locked ONE collateral for nothing.
Conceptually, the Replace protocol resembles a SPV atomic cross-chain swap.
Step-by-Step¶
- Precondition: a vault (OldVault) has locked ONE collateral in the Vault Registry and has issued ONEBTC tokens, i.e., holds BTC on Bitcoin.
- OldVault submits a replacement request, indicating how much BTC is to be migrated by calling the requestReplace function.
- OldVault is required to lock some amount of ONE collateral (
ReplaceGriefingCollateral
) as griefing protection, to prevent OldVault from holding NewVault’s ONE collateral locked in the BTC Bridge without ever finalizing the redeem protocol (transfer of BTC).
- OldVault is required to lock some amount of ONE collateral (
- Optional: If an OldVault has changed its mind or can’t find a NewVault to replace it, it can call the withdrawReplaceRequest function to invalidate its request. If the request is already accepted (step 4), then this function cannot be invoked.
- A new candidate vault (NewVault), commits to executing the replacement by locking up the necessary ONE collateral to back the to-be-transferred BTC (according to the
SecureCollateralThreshold
) by calling the acceptReplace function.. - Within a pre-defined delay, OldVault must release the BTC on Bitcoin to NewVault’s BTC address, and submit a valid transaction inclusion proof by calling the executeReplace function (call to
verifyTransactionInclusion
in BTC-Relay). If OldVault releases the BTC to NewVault correctly and submits the transaction inclusion proof to Replace module on time, OldVault’s ONE collateral is released - NewVault has now replaced OldVault.
- Note: to prevent OldVault from trying to re-use old transactions (or other payments to NewVaults on Bitcoin) as fake proofs, we require OldVault to include a
nonce
in an OP_RETURN output of the transfer transaction on Bitcoin.
- Optional: If OldVault fails to provide the correct transaction inclusion proof on time, the NewVault’s
collateral
is unlocked and OldVault’sgriefingCollateral
is sent to the NewVault as reimbursement for the opportunity costs of locking up ONE collateral via the cancelReplace function.
Vault Registry¶
The data access and state changes to the vault registry are documented in Fig. 9 below.
Fee Model¶
Following additions are added if the fee model is integrated.
- If a replace request is canceled, the griefing collateral is transferred to the new_vault.
- If a replace request is executed, the griefing collateral is transferred to the old_vault.
Data Model¶
Scalars¶
ReplaceGriefingCollateral¶
The minimum collateral (ONE) a vault requesting a replacement needs to provide as griefing protection.
Note
Requiring a vault to add ONE collateral for executing replace may be a problem for Vaults which trigger this process due to low collateralization rates. We can potentially slash some of the Vault’s existing collateral instead - this will result in reducing the collateralization rate and move the vault closer to liquidation.
ReplacePeriod¶
The time difference between a replace request is accepted by another vault and the transfer of BTC (and submission of the transaction inclusion proof) by the to-be-replaced Vault. Concretely, this period is the amount by which ActiveBlockCount is allowed to increase before the redeem is considered to be expired. The replace period has an upper limit to prevent griefing of vault collateral.
Maps¶
ReplaceRequests¶
Vaults create replace requests if they want to have (a part of) their ONE collateral to be replaced by other Vaults. This mapping provides access from a unique hash ReplaceId
to a ReplaceRequest
struct. <ReplaceId, Replace>
.
Structs¶
Replace¶
Stores the status and information about a single replace request.
Parameter | Type | Description |
---|---|---|
oldVault |
Account | BTC Bridge account of the vault that is to be replaced. |
opentime |
u256 | Block height of opening the request. |
amount |
ONEBTC | Amount of BTC / ONEBTC to be replaced. |
griefingCollateral |
ONE | Griefing protection collateral locked by oldVault . |
newVault |
Account | Account of the new vault, which accepts the replace request. |
collateral |
ONE | ONE collateral locked by the new Vault. |
acceptTime |
u256 | Block height at which this replace request was accepted by a new Vault. Serves as start for the countdown until when the old vault must transfer the BTC. |
btcAddress |
bytes[20] | Base58 encoded Bitcoin public key of the new Vault. |
Note
The btcAddress
parameter is not to be set the the new vault, but is extracted from the Vaults
mapping in VaultRegistry
for the account of the new Vault.
Functions¶
requestReplace¶
An OldVault (to-be-replaced Vault) submits a request to be (partially) replaced.
Specification¶
Function Signature
requestReplace(oldVault, btcAmount, timeout, griefingCollateral)
Parameters
oldVault
: Account identifier of the vault to be replaced (as tracked inVaults
in Vault Registry).btcAmount
: Integer amount of BTC / ONEBTC to be replaced.timeout
: Time in blocks after which this request expires.griefingCollateral
: collateral locked by theoldVault
as griefing protection
Returns
replaceID
: A unique hash identifying the replace request.
Events
RequestReplace(oldVault, btcAmount, timeout, replaceId)
:
Errors
ERR_VAULT_NOT_FOUND = "There exists no vault with the given account id"
: The specified vault does not exist.ERR_MIN_AMOUNT
: The remaining ONE collateral (converted from the requested BTC replacement value given the current exchange rate) would be below theMinimumCollateralVault
as defined inVaultRegistry
.ERR_UNAUTHORIZED = Unauthorized: Caller must be associated Vault
: The caller of this function is not the associated vault, and hence not authorized to take this action.ERR_VAULT_BANNED = "The selected vault has been temporarily banned."
: Executing replace requests is not possible with temporarily banned Vaults.
Function Sequence¶
- Check that caller of the function is indeed the to-be-replaced Vault. Return
ERR_UNAUTHORIZED
error if this check fails. - Retrieve the
vault
as per theoldVault
account identifier fromVaults
in theVaultRegistry
. ReturnERR_VAULT_NOT_FOUND
if no vault can be found. - Check that the
vault
is currently not banned, i.e.,vault.bannedUntil == None
orvault.bannedUntil < current shard block height
. ReturnERR_VAULT_BANNED
if this check fails. - Check that the requested
btcAmount
is equal to or lower thanvault.issuedTokens
mins thevault.toBeRedeemedTokens
.
- If
btcAmount > vault.issuedTokens
setbtcAmount = vault.issuedTokens
(i.e., the request is for the entire BTC holdings of the Vault).
- If the request is not for the entire BTC holdings, check that the remaining ONE collateral of the vault is higher than
MinimumCollateralVault
as defined inVaultRegistry
. ReturnERR_MIN_AMOUNT
error if this check fails. - Check that the
griefingCollateral
is greater or equalReplaceGriefingCollateral
- Lock the oldVault’s griefing collateral by calling lockCollateral and passing
oldVault
andgriefingCollateral
as parameters. - Call the increaseToBeRedeemedTokens function with the
oldVault
and thebtcAmount
to ensure that the oldVault’s tokens cannot be redeemed when a replace procedure is happening. - Generate a
replaceId
by hashing a random seed, a nonce, and the address of the Requester. - Create new
ReplaceRequest
entry:
Replace.oldVault = vault
,Replace.opentime
= current time on Bridge,Replace.amount = amount
,Replace.griefingCollateral = griefingCollateral
.
- Emit
RequestReplace(vault, btcAmount, timeout, replaceId)
event. - Return the
replaceId
.
withdrawReplaceRequest¶
The OldVault withdraws an existing ReplaceRequest that is made.
Specification¶
Function Signature
withdrawReplaceRequest(oldVault, replaceId)
Parameters
oldVault
: Account identifier of the vault withdrawing it’s replace request (as tracked inVaults
in Vault Registry)replaceId
: The identifier of the replace request inReplaceRequests
.
Events
WithdrawReplaceRequest(oldVault, replaceId)
: emits an event stating that a vault (oldVault
) has withdrawn an existing replace request (requestId
).
Errors
ERR_REPLACE_ID_NOT_FOUND = No ReplaceRequest with given identifier found
: The providedreplaceId
was not found inReplaceRequests
.ERR_UNAUTHORIZED = Unauthorized: Caller must be associated Vault
: The caller of this function is not the associated vault, and hence not authorized to take this action.ERR_CANCEL_ACCEPTED_REQUEST = Cannot cancel the ReplaceRequest as it was already accepted by a Vault
: TheReplaceRequest
was already accepted by another vault and can hence no longer be withdrawn.
Preconditions¶
The ReplaceRequest must have not yet been accepted by another Vault.
Function Sequence¶
- Retrieve the
ReplaceRequest
as per thereplaceId
parameter fromVaults
in theVaultRegistry
. ReturnERR_REPLACE_ID_NOT_FOUND
error if no suchReplaceRequest
was found. - Check that caller of the function is indeed the to-be-replaced vault as specified in the
ReplaceRequest
. ReturnERR_UNAUTHORIZED
error if this check fails. - Check that the
ReplaceRequest
was not yet accepted by another Vault. ReturnERR_CANCEL_ACCEPTED_REQUEST
error if this check fails. - Release the oldVault’s griefing collateral associated with this
ReplaceRequests
by calling releaseCollateral and passing theoldVault
andgriefingCollateral
as parameters. - Call the decreaseToBeRedeemedTokens function in the VaultRegistry to allow the vault to be part of other redeem or replace requests again.
- Remove the
ReplaceRequest
fromReplaceRequests
. - Emit a
WithdrawReplaceRequest(oldVault, replaceId)
event.
acceptReplace¶
A NewVault accepts an existing replace request, locking the necessary ONE collateral.
Note
When issuing tokens, we increase the toBeIssuedTokens
by a vault. Also, when a vault locks collateral via the registerVault
and lockCollateral
function in the VaultRegistry, we would add collateral to the collateral
field of a vault. However, we are not updating the collateral
and toBeIssuedTokens
tokens here. if a vault decides to provide a very high collateral rate, way over the SecureCollateralThreshold
and wants to back the replace with that, we are not interferring with this. If we would lock his collateral in the collateral
field in the VaultRegistry, as user could block part of this collateral with an issue request.
Specification¶
Function Signature
acceptReplace(newVault, replaceId, collateral)
Parameters
newVault
: Account identifier of the vault accepting the replace request (as tracked inVaults
in Vault Registry)replaceId
: The identifier of the replace request inReplaceRequests
.collateral
: ONE collateral provided to match the replace request (i.e., for backing the locked BTC). Can be more than the necessary amount.
Events
AcceptReplace(newVault, replaceId, collateral)
: emits an event stating which vault (newVault
) has accepted theReplaceRequest
request (requestId
), and how much collateral in ONE it provided (collateral
).
Errors
ERR_REPLACE_ID_NOT_FOUND = No ReplaceRequest with given identifier found
: The providedreplaceId
was not found inReplaceRequests
.ERR_INSUFFICIENT_COLLATERAL
: The provided collateral is insufficient to match the replace request.ERR_VAULT_NOT_FOUND
: The caller of the function was not found in the existingVaults
list inVaultRegistry
.ERR_VAULT_BANNED = "The selected vault has been temporarily banned."
: Executing replace requests is not possible with temporarily banned Vaults.
Function Sequence¶
- Retrieve the
ReplaceRequest
as per thereplaceId
parameter fromReplaceRequests
. ReturnERR_REPLACE_ID_NOT_FOUND
error if no suchReplaceRequest
was found. - Retrieve the vault as per the
newVault
parameter fromVaults
in theVaultRegistry
. ReturnERR_VAULT_NOT_FOUND
error if no such vault can be found. - Check that the
newVault
is currently not banned, i.e.,newVault.bannedUntil == None
ornewVault.bannedUntil < current shard block height
. ReturnERR_VAULT_BANNED
if this check fails. - Check that the provided
collateral
exceeds the necessary amount, i.e.,collateral >= SecureCollateralThreshold * Replace.btcAmount
. ReturnERR_INSUFFICIENT_COLLATERAL
error if this check fails. - Lock the newVault’s collateral by calling lockCollateral and providing
newVault
andcollateral
as parameters. - Update the
ReplaceRequest
entry:
Replace.newVault = newVault
,Replace.acceptTime
= current Bridge time,Replace.btcAddress = btcAddress
(new Vault’s BTC address),Replace.collateral = collateral
(ONE collateral locked by new Vault).
- Emit a
AcceptReplace(newVault, replaceId, collateral)
event.
executeReplace¶
The to-be-replaced vault finalizes the replace process by submitting a proof that it transferred the correct amount of BTC to the BTC address of the new vault, as specified in the ReplaceRequest
.
This function calls verifyTransactionInclusion in BTC-Relay, proving a transaction inclusion proof (txId
and merkleProof
) as input, as well as validateTransaction proving the rawTx
, replaceId
and the newVault’s Bitcoin address as parameters.
Specification¶
Function Signature
executeReplace(newVault, replaceId, merkleProof, rawTx)
Parameters
newVault
: Account identifier of the vault accepting the replace request (as tracked inVaults
in Vault Registry)replaceId
: The identifier of the replace request inReplaceRequests
.merkleProof
: Merkle tree path (concatenated LE SHA256 hashes).rawTx
: Raw Bitcoin transaction including the transaction inputs and outputs.
Events
ExecuteReplace(oldVault, newVault, replaceId)
: emits an event stating that the old vault (oldVault
) has executed the BTC transfer to the new vault (newVault
), finalizing theReplaceRequest
request (requestId
).
Errors
ERR_REPLACE_ID_NOT_FOUND = No ReplaceRequest with given identifier found
: The providedreplaceId
was not found inReplaceRequests
.ERR_VAULT_NOT_FOUND = No vault with given Account identifier found
: The caller of the function was not found in the existingVaults
list inVaultRegistry
.ERR_REPLACE_PERIOD_EXPIRED = Replace request expired
:- See errors returned by verifyTransactionInclusion and validateTransaction in BTC-Relay.
Preconditions¶
- The BTC Bridge status in the Security component must be set to
RUNNING:0
. - The to-be-replaced vault transferred the correct amount of BTC to the BTC address of the new vault on Bitcoin, and has generated a transaction inclusion proof.
Function Sequence¶
- Retrieve the
ReplaceRequest
as per thereplaceId
parameter fromVaults
in theVaultRegistry
. ReturnERR_REPLACE_ID_NOT_FOUND
error if no suchReplaceRequest
request was found. - Check if the replace has expired by calling hasExpired in the Security module. Throw
ERR_REPLACE_PERIOD_EXPIRED
if true. - Retrieve the
Vault
as per thenewVault
parameter fromVaults
in theVaultRegistry
. ReturnERR_VAULT_NOT_FOUND
error if no such vault can be found. - Call verifyTransactionInclusion in BTC-Relay, providing
txId
andmerkleProof
as parameters. If this call returns an error, abort and return the received error. - Call validateTransaction in BTC-Relay, providing
rawTx
, the amount of to-be-replaced BTC (Replace.amount
), thenewVault
’s Bitcoin address (Vault.btcAddress
), and thereplaceId
as parameters. If this call returns an error, abort and return the received error. - Call the replaceTokens function in the VaultRegistry with the
newVault
,oldVault
,amount
, and thecollateral
to increase theissuedTokens
amount of thenewVault
as well as itscollateral
. Further, this decreases theissuedTokens
andtoBeRedeemedTokens
of theoldVault
. - Call the releaseCollateral function to release the
oldVaults
griefing collateralgriefingCollateral
. - Emit the
ExecuteReplace(oldVault, newVault, replaceId)
event. - Remove the
ReplaceRequest
fromReplaceRequests
.
Note
It can be the case that the to-be-replaced OldVault controls a significant numbers of Bitcoin UTXOs with user funds, making it impossible to execute the migration of funds to the NewVault within a single Bitcoin transaction. As a result, it may be necessary to “merge” these UTXOs using multiple “merge transactions” on Bitcoin, i.e., transactions which takes as input multiple UTXOs controlled by the OldVault and create a single UTXO controlled (again) by the OldVault. Once the UTXOs produced by “merge transactions” can be merged by a single, final transaction, the OldVault moves the funds to the NewVault. (An alternative is to allow the OldVault to submit multiple transaction inclusion proofs when calling executeReplace
, although this significantly increases the complexity of transaction parsing on the BTC Bridge side).
cancelReplace¶
If a replace request is not executed on time, the replace can be cancelled by the new vault. Since the new vault provided additional collateral in vain, it can claim the old vault’s griefing collateral.
Specification¶
Function Signature
cancelReplace(newVault, replaceId)
Parameters
newVault
: Account identifier of the vault accepting the replace request (as tracked inVaults
in Vault Registry)replaceId
: The identifier of the replace request inReplaceRequests
.
Events
CancelReplace(newVault, oldVault, replaceId)
: emits an event stating that the old vault (oldVault
) has not completed the replace request and the new vault (newVault
) cancelled theReplaceRequest
request (requestId
).
Errors
ERR_REPLACE_ID_NOT_FOUND = No ReplaceRequest with given identifier found
: The providedreplaceId
was not found inReplaceRequests
.ERR_VAULT_NOT_FOUND = No vault with given Account identifier found
: The caller of the function was not found in the existingVaults
list inVaultRegistry
.ERR_PERIOD_NOT_EXPIRED = Replace request not yet expired
: The old vault can still fulfil the replace request.
Function Sequence¶
- Retrieve the
ReplaceRequest
as per thereplaceId
parameter fromVaults
in theVaultRegistry
. ReturnERR_REPLACE_ID_NOT_FOUND
error if no suchReplaceRequest
request was found.
Note
If a replace request has been executed successfully, it has been deleted and this error will be thrown.
- Check if the replace has expired by calling hasExpired in the Security module. Throw
ERR_PERIOD_NOT_EXPIRED
if false. - Transfer the oldVault’s griefing collateral associated with this
ReplaceRequests
to the newVault by calling slashCollateral and passing theoldVault
,newVault
andgriefingCollateral
as parameters. - Call the decreaseToBeRedeemedTokens function in the VaultRegistry for the oldVault.
- Remove the
ReplaceRequest
fromReplaceRequests
. - Emit a
CancelReplace(newVault, oldVault, replaceId)
event.
Events¶
RequestReplace¶
Emit an event when a replace request is made by an oldVault.
Event Signature
ReplaceRequested(oldVault, btcAmount, timeout, replaceId)
Parameters
oldVault
: Account identifier of the vault to be replaced (as tracked inVaults
in Vault Registry).btcAmount
: Integer amount of BTC / ONEBTC to be replaced.timeout
: Time in blocks after which this request expires.replaceId
: The unique identified of a replace request.
Functions
WithdrawReplaceRequest¶
Emits an event stating that a vault (oldVault
) has withdrawn an existing replace request (requestId
).
Event Signature
WithdrawReplaceRequest(oldVault, replaceId)
Parameters
oldVault
: Account identifier of the vault withdrawing it’s replace request (as tracked inVaults
in Vault Registry)replaceId
: The identifier of the replace request inReplaceRequests
.
Functions
- ref:withdrawReplaceRequest
AcceptReplace¶
Emits an event stating which vault (newVault
) has accepted the ReplaceRequest
request (requestId
), and how much collateral in ONE it provided (collateral
).
Event Signature
AcceptReplace(newVault, replaceId, collateral)
Parameters
newVault
: Account identifier of the vault accepting the replace request (as tracked inVaults
in Vault Registry)replaceId
: The identifier of the replace request inReplaceRequests
.collateral
: ONE collateral provided to match the replace request (i.e., for backing the locked BTC). Can be more than the necessary amount.
Functions
- ref:acceptReplace
ExecuteReplace¶
Emits an event stating that the old vault (oldVault
) has executed the BTC transfer to the new vault (newVault
), finalizing the ReplaceRequest
request (requestId
).
Event Signature
ExecuteReplace(oldVault, newVault, replaceId)
Parameters
oldVault
: Account identifier of the vault withdrawing it’s replace request (as tracked inVaults
in Vault Registry)newVault
: Account identifier of the vault accepting the replace request (as tracked inVaults
in Vault Registry)replaceId
: The identifier of the replace request inReplaceRequests
.
Functions
- ref:executeReplace
CancelReplace¶
Emits an event stating that the old vault (oldVault
) has not completed the replace request and the new vault (newVault
) cancelled the ReplaceRequest
request (requestId
).
Event Signature
CancelReplace(newVault, oldVault, replaceId)
Parameters
oldVault
: Account identifier of the vault withdrawing it’s replace request (as tracked inVaults
in Vault Registry)newVault
: Account identifier of the vault accepting the replace request (as tracked inVaults
in Vault Registry)replaceId
: The identifier of the replace request inReplaceRequests
.
Functions
- ref:cancelReplace
Error Codes¶
ERR_MIN_AMOUNT
- Message: “Remaining collateral below
MinimCollateralVaul
limit.” - Function: requestReplace
- Cause: The remaining ONE collateral (converted from the requested BTC replacement value given the current exchange rate) would be below the
MinimumCollateralVault
as defined inVaultRegistry
.
ERR_UNAUTHORIZED
- Message: “Unauthorized: Caller must be associated vault.”
- Function: requestReplace | withdrawReplaceRequest
- Cause: The caller of this function is not the associated vault, and hence not authorized to take this action.
ERR_REPLACE_ID_NOT_FOUND
- Message: “The
replaceId
cannot be found.” - Function: withdrawReplaceRequest | acceptReplace | executeReplace | cancelReplace
- Cause: The
replaceId
in theReplaceRequests
mapping returnedNone
.
ERR_CANCEL_ACCEPTED_REQUEST
- Message: “Cannot cancel the ReplaceRequest as it was already accepted by a Vault.”
- Function: withdrawReplaceRequest
- Cause: The
ReplaceRequest
was already accepted by another vault and can hence no longer be withdrawn.
ERR_INSUFFICIENT_COLLATERAL
- Message: “The provided collateral is too low.”
- Function: acceptReplace
- Cause: The provided collateral is insufficient to match the replace request.
ERR_VAULT_NOT_FOUND
- Message: “The
vault
cannot be found.” - Function: requestReplace | acceptReplace | cancelReplace
- Cause: The vault was not found in the existing
Vaults
list inVaultRegistry
.
ERR_REPLACE_PERIOD_EXPIRED
- Message: “The replace period expired.”
- Function: executeReplace
- Cause: The time limit as defined by the
ReplacePeriod
is not met.
ERR_REPLACE_PERIOD_NOT_EXPIRED
- Message: “The period to complete the replace request is not yet expired.”
- Function: cancelReplace
- Cause: Raises an error if the time limit to call
executeReplace
has not yet passed.