Vulnerability: tbtc - State transitions are not always enforced
Severity: high

Description:
A deposit follows a complex state-machine that makes sure it is correctly funded before `TBTC` Tokens are minted. The deposit lifecycle starts with a set of states modeling a funding flow that - if successful - ultimately leads to the deposit being active, meaning that corresponding `TBTC` tokens exist for the deposits. A redemption flow allows to redeem `TBTC` for `BTC` and a liquidation flow handles fraud and abort conditions. Fraud cases in the funding flow are handled separately.\\nState transitions from one deposit state to another require someone calling the corresponding transition method on the deposit and actually spend gas on it. The incentive to call a transition varies and is analyzed in more detail in the security-specification section of this report.\\nThis issue assumes that participants are not always pushing forward through the state machine as soon as a new state becomes available, opening up the possibility of having multiple state transitions being a valid option for a deposit (e.g. pushing a deposit to active state even though a timeout should have been called on it).\\nA TDT holder can choose not to call out `notifySignerSetupFailure` hoping that the signing group still forms after the signer setup timeout passes.\\nthere is no incentive for the TDT holder to terminate its own deposit after a timeout.\\nthe deposit might end up never being in a final error state.\\nthere is no incentive for the signing group to terminate the deposit.\\nThis affects all states that can time out.\\nThe deposit can be pushed to active state even after `notifySignerSetupFailure`, `notifyFundingTimeout` have passed but nobody called it out.\\nThere is no timeout check in `retrieveSignerPubkey`, `provideBTCFundingProof`.\\n```\\n/// @notice we poll the Keep contract to retrieve our pubkey\\n/// @dev We store the pubkey as 2 bytestrings, X and Y.\\n/// @param \\_d deposit storage pointer\\n/// @return True if successful, otherwise revert\\nfunction retrieveSignerPubkey(DepositUtils.Deposit storage \\_d) public {\\n    require(\\_d.inAwaitingSignerSetup(), "Not currently awaiting signer setup");\\n\\n    bytes memory \\_publicKey = IBondedECDSAKeep(\\_d.keepAddress).getPublicKey();\\n    require(\\_publicKey.length == 64, "public key not set or not 64-bytes long");\\n```\\n\\n```\\nfunction provideBTCFundingProof(\\n    DepositUtils.Deposit storage \\_d,\\n    bytes4 \\_txVersion,\\n    bytes memory \\_txInputVector,\\n    bytes memory \\_txOutputVector,\\n    bytes4 \\_txLocktime,\\n    uint8 \\_fundingOutputIndex,\\n    bytes memory \\_merkleProof,\\n    uint256 \\_txIndexInBlock,\\n    bytes memory \\_bitcoinHeaders\\n) public returns (bool) {\\n\\n    require(\\_d.inAwaitingBTCFundingProof(), "Not awaiting funding");\\n\\n    bytes8 \\_valueBytes;\\n    bytes memory  \\_utxoOutpoint;\\n```\\n\\nMembers of the signing group might decide to call `notifyFraudFundingTimeout` in a race to avoid late submissions for `provideFraudBTCFundingProof` to succeed in order to contain funds lost due to fraud.\\nIt should be noted that even after the fraud funding timeout passed the TDT holder could `provideFraudBTCFundingProof` as it does not check for the timeout.\\nA malicious signing group observes BTC funding on the bitcoin chain in an attempt to commit fraud at the time the `provideBTCFundingProof` transition becomes available to front-run `provideFundingECDSAFraudProof` forcing the deposit into active state.\\nThe malicious users of the signing group can then try to report fraud, set themselves as `liquidationInitiator` to be awarded part of the signer bond (in addition to taking control of the BTC collateral).\\nThe TDT holders fraud-proof can be front-run, see issue 5.15\\nIf oracle price slippage occurs for one block (flash-crash type of event) someone could call an undercollateralization transition.\\nFor severe oracle errors deposits might be liquidated by calling `notifyUndercollateralizedLiquidation`. The TDT holder cannot exit liquidation in this case.\\nFor non-severe under collateralization someone could call `notifyCourtesyCall` to impose extra effort on TDT holders to `exitCourtesyCall` deposits.\\nA deposit term expiration courtesy call can be exit in the rare case where `_d.fundedAt + TBTCConstants.getDepositTerm() == block.timestamp`\\n```\\n/// @notice Goes from courtesy call to active\\n/// @dev Only callable if collateral is sufficient and the deposit is not expiring\\n/// @param \\_d deposit storage pointer\\nfunction exitCourtesyCall(DepositUtils.Deposit storage \\_d) public {\\n    require(\\_d.inCourtesyCall(), "Not currently in courtesy call");\\n    require(block.timestamp <= \\_d.fundedAt + TBTCConstants.getDepositTerm(), "Deposit is expiring");\\n    require(getCollateralizationPercentage(\\_d) >= \\_d.undercollateralizedThresholdPercent, "Deposit is still undercollateralized");\\n    \\_d.setActive();\\n    \\_d.logExitedCourtesyCall();\\n}\\n```\\n\\n```\\n/// @notice Notifies the contract that its term limit has been reached\\n/// @dev This initiates a courtesy call\\n/// @param \\_d deposit storage pointer\\nfunction notifyDepositExpiryCourtesyCall(DepositUtils.Deposit storage \\_d) public {\\n    require(\\_d.inActive(), "Deposit is not active");\\n    require(block.timestamp >= \\_d.fundedAt + TBTCConstants.getDepositTerm(), "Deposit term not elapsed");\\n    \\_d.setCourtesyCall();\\n    \\_d.logCourtesyCalled();\\n    \\_d.courtesyCallInitiated = block.timestamp;\\n}\\n```\\n\\nAllow exiting the courtesy call only if the deposit is not expired: `block.timestamp < _d.fundedAt + TBTCConstants.getDepositTerm()`

Impact:

Recommended mitigation:
Ensure that there are no competing interests between participants of the system to favor one transition over the other, causing race conditions, front-running opportunities or stale deposits that are not pushed to end-states.\\nNote: Please find an analysis of incentives to call state transitions in the security section of this document.