Skip to content
This repository has been archived by the owner on Mar 28, 2023. It is now read-only.

Commit

Permalink
Merge pull request #534 from keep-network/funding-fraud
Browse files Browse the repository at this point in the history
Funding fraud refactor

Members of the signing group could have decided to call notifyFraudFundingTimeout
in a race to avoid late submissions for provideFraudBTCFundingProof to succeed
in order to contain funds lost due to fraud.

To address this, fraud during now funding ends on provideFundingECDSAFraudProof
with the signers always losing their full bond as long as the funding timeout has not
elapsed. The full bond always goes to the funder, whether or not a funding tx was
shipped.
  • Loading branch information
Shadowfiend committed Mar 27, 2020
2 parents c397657 + c267e20 commit 93697fe
Show file tree
Hide file tree
Showing 8 changed files with 32 additions and 402 deletions.
43 changes: 0 additions & 43 deletions solidity/contracts/deposit/Deposit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -302,49 +302,6 @@ contract Deposit is DepositFactoryAuthority {
return true;
}

/// @notice Anyone may notify the contract no funding proof was submitted during funding fraud.
/// @dev This is not a funder fault. The signers have faulted, so the funder shouldn't fund.
/// @return True if successful, otherwise revert.
function notifyFraudFundingTimeout() public returns (bool) {
self.notifyFraudFundingTimeout();
return true;
}

/// @notice Anyone may notify the deposit of a funding proof during funding fraud.
// We reward the funder the entire bond if this occurs.
/// @dev Takes a pre-parsed transaction and calculates values needed to verify funding.
/// @param _txVersion Transaction version number (4-byte LE).
/// @param _txInputVector All transaction inputs prepended by the number of inputs encoded as a VarInt, max 0xFC(252) inputs.
/// @param _txOutputVector All transaction outputs prepended by the number of outputs encoded as a VarInt, max 0xFC(252) outputs.
/// @param _txLocktime Final 4 bytes of the transaction.
/// @param _fundingOutputIndex Index of funding output in _txOutputVector (0-indexed).
/// @param _merkleProof The merkle proof of transaction inclusion in a block.
/// @param _txIndexInBlock Transaction index in the block (0-indexed).
/// @param _bitcoinHeaders Single bytestring of 80-byte bitcoin headers, lowest height first.
/// @return True if no errors are thrown.
function provideFraudBTCFundingProof(
bytes4 _txVersion,
bytes memory _txInputVector,
bytes memory _txOutputVector,
bytes4 _txLocktime,
uint8 _fundingOutputIndex,
bytes memory _merkleProof,
uint256 _txIndexInBlock,
bytes memory _bitcoinHeaders
) public returns (bool) {
self.provideFraudBTCFundingProof(
_txVersion,
_txInputVector,
_txOutputVector,
_txLocktime,
_fundingOutputIndex,
_merkleProof,
_txIndexInBlock,
_bitcoinHeaders
);
return true;
}

/// @notice Anyone may notify the deposit of a funding proof to activate the deposit.
/// This is the happy-path of the funding flow. It means that we have succeeded.
/// @dev Takes a pre-parsed transaction and calculates values needed to verify funding.
Expand Down
86 changes: 2 additions & 84 deletions solidity/contracts/deposit/DepositFunding.sol
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,6 @@ library DepositFunding {
return true;
}

/// @notice Slashes the signers partially for committing fraud before funding occurs.
/// @dev Called only by notifyFraudFundingTimeout.
function partiallySlashForFraudInFunding(DepositUtils.Deposit storage _d) internal {
uint256 _seized = _d.seizeSignerBonds();
uint256 _slash = _seized.div(TBTCConstants.getFundingFraudPartialSlashDivisor());
_d.pushFundsToKeepGroup(_seized.sub(_slash));
_d.depositOwner().transfer(_slash);
}

/// @notice Seizes signer bonds and distributes them to the funder.
/// @dev This is only called as part of funding fraud flow.
function distributeSignerBondsToFunder(DepositUtils.Deposit storage _d) internal {
Expand Down Expand Up @@ -140,6 +131,7 @@ library DepositFunding {
_d.setFailedSetup();
_d.logSetupFailed();

_d.closeKeep();
fundingTeardown(_d);
}

Expand Down Expand Up @@ -168,84 +160,10 @@ library DepositFunding {
bool _isFraud = _d.submitSignatureFraud(_v, _r, _s, _signedDigest, _preimage);
require(_isFraud, "Signature is not fraudulent");
_d.logFraudDuringSetup();

// If the funding timeout has elapsed, punish the funder too!
if (block.timestamp > _d.fundingProofTimerStart.add(TBTCConstants.getFundingTimeout())) {
_d.setFailedSetup();
} else {
/* NB: This is reuse of the variable */
_d.fundingProofTimerStart = block.timestamp;
_d.setFraudAwaitingBTCFundingProof();
}
}

/// @notice Anyone may notify the contract no funding proof was submitted during funding fraud.
/// @dev This is not a funder fault. The signers have faulted, so the funder shouldn't fund.
/// @param _d Deposit storage pointer.
function notifyFraudFundingTimeout(DepositUtils.Deposit storage _d) public {
require(
_d.inFraudAwaitingBTCFundingProof(),
"Not currently awaiting fraud-related funding proof"
);
require(
block.timestamp > _d.fundingProofTimerStart.add(TBTCConstants.getFraudFundingTimeout()),
"Fraud funding proof timeout has not elapsed"
);
_d.setFailedSetup();
_d.logSetupFailed();

partiallySlashForFraudInFunding(_d);
distributeSignerBondsToFunder(_d);
fundingFraudTeardown(_d);
}

/// @notice Anyone may notify the deposit of a funding proof during funding fraud.
// We reward the funder the entire bond if this occurs.
/// @dev Takes a pre-parsed transaction and calculates values needed to verify funding.
/// @param _d Deposit storage pointer.
/// @param _txVersion Transaction version number (4-byte LE).
/// @param _txInputVector All transaction inputs prepended by the number of inputs encoded as a VarInt, max 0xFC(252) inputs.
/// @param _txOutputVector All transaction outputs prepended by the number of outputs encoded as a VarInt, max 0xFC(252) outputs.
/// @param _txLocktime Final 4 bytes of the transaction.
/// @param _fundingOutputIndex Index of funding output in _txOutputVector (0-indexed).
/// @param _merkleProof The merkle proof of transaction inclusion in a block.
/// @param _txIndexInBlock Transaction index in the block (0-indexed).
/// @param _bitcoinHeaders Single bytestring of 80-byte bitcoin headers, lowest height first.
/// @return True if no errors are thrown.
function provideFraudBTCFundingProof(
DepositUtils.Deposit storage _d,
bytes4 _txVersion,
bytes memory _txInputVector,
bytes memory _txOutputVector,
bytes4 _txLocktime,
uint8 _fundingOutputIndex,
bytes memory _merkleProof,
uint256 _txIndexInBlock,
bytes memory _bitcoinHeaders
) public returns (bool) {
require(_d.inFraudAwaitingBTCFundingProof(), "Not awaiting a funding proof during setup fraud");

bytes8 _valueBytes;
bytes memory _utxoOutpoint;

(_valueBytes, _utxoOutpoint) = _d.validateAndParseFundingSPVProof(
_txVersion,
_txInputVector,
_txOutputVector,
_txLocktime,
_fundingOutputIndex,
_merkleProof,
_txIndexInBlock,
_bitcoinHeaders
);

_d.setFailedSetup();
_d.logSetupFailed();

// If the proof is accepted, update to failed, and distribute signer bonds
distributeSignerBondsToFunder(_d);
fundingFraudTeardown(_d);

return true;
}

/// @notice Anyone may notify the deposit of a funding proof to activate the deposit.
Expand Down
2 changes: 1 addition & 1 deletion solidity/contracts/deposit/DepositLiquidation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ library DepositLiquidation {
bytes memory _preimage
) public {
require(
!_d.inFunding() && !_d.inFundingFailure(),
!_d.inFunding(),
"Use provideFundingECDSAFraudProof instead"
);
require(
Expand Down
10 changes: 1 addition & 9 deletions solidity/contracts/deposit/DepositRedemption.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,6 @@ library DepositRedemption {
_keep.distributeERC20Reward(address(_d.tbtcToken), _d.signerFee());
}

/// @notice Closes keep associated with the deposit.
/// @dev Should be called when the keep is no longer needed and the signing
/// group can disband.
function closeKeep(DepositUtils.Deposit storage _d) internal {
IBondedECDSAKeep _keep = IBondedECDSAKeep(_d.keepAddress);
_keep.closeKeep();
}

/// @notice Approves digest for signing by a keep.
/// @dev Calls given keep to sign the digest. Records a current timestamp
/// for given digest.
Expand Down Expand Up @@ -321,7 +313,7 @@ library DepositRedemption {

// Transfer TBTC to signers and close the keep.
distributeSignerFee(_d);
closeKeep(_d);
_d.closeKeep();

_d.distributeFeeRebate();

Expand Down
17 changes: 0 additions & 17 deletions solidity/contracts/deposit/DepositStates.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ library DepositStates {
AWAITING_BTC_FUNDING_PROOF,

// FAILED SETUP
FRAUD_AWAITING_BTC_FUNDING_PROOF,
FAILED_SETUP,

// ACTIVE
Expand Down Expand Up @@ -42,14 +41,6 @@ library DepositStates {
);
}

/// @notice Check if the contract is currently in the funding faud flow.
/// @dev This checks for the flow, not the SETUP_FAILED termination state.
/// @param _d Deposit storage pointer.
/// @return True if contract is currently in the funding fraud flow else False.
function inFundingFailure(DepositUtils.Deposit storage _d) public view returns (bool) {
return (_d.currentState == uint8(States.FRAUD_AWAITING_BTC_FUNDING_PROOF));
}

/// @notice Check if the contract is currently in the signer liquidation flow.
/// @dev This could be caused by fraud, or by an unfilled margin call.
/// @param _d Deposit storage pointer.
Expand Down Expand Up @@ -111,10 +102,6 @@ library DepositStates {
return _d.currentState == uint8(States.AWAITING_BTC_FUNDING_PROOF);
}

function inFraudAwaitingBTCFundingProof(DepositUtils.Deposit storage _d) external view returns (bool) {
return _d.currentState == uint8(States.FRAUD_AWAITING_BTC_FUNDING_PROOF);
}

function inFailedSetup(DepositUtils.Deposit storage _d) external view returns (bool) {
return _d.currentState == uint8(States.FAILED_SETUP);
}
Expand Down Expand Up @@ -159,10 +146,6 @@ library DepositStates {
_d.currentState = uint8(States.AWAITING_BTC_FUNDING_PROOF);
}

function setFraudAwaitingBTCFundingProof(DepositUtils.Deposit storage _d) external {
_d.currentState = uint8(States.FRAUD_AWAITING_BTC_FUNDING_PROOF);
}

function setFailedSetup(DepositUtils.Deposit storage _d) external {
_d.currentState = uint8(States.FAILED_SETUP);
}
Expand Down
8 changes: 8 additions & 0 deletions solidity/contracts/deposit/DepositUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ library DepositUtils {
mapping (bytes32 => uint256) approvedDigests;
}

/// @notice Closes keep associated with the deposit.
/// @dev Should be called when the keep is no longer needed and the signing
/// group can disband.
function closeKeep(DepositUtils.Deposit storage _d) internal {
IBondedECDSAKeep _keep = IBondedECDSAKeep(_d.keepAddress);
_keep.closeKeep();
}

/// @notice Gets the current block difficulty.
/// @dev Calls the light relay and gets the current block difficulty.
/// @return The difficulty.
Expand Down
Loading

0 comments on commit 93697fe

Please sign in to comment.