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

Commit

Permalink
Add requestFunderAbort function to deposits
Browse files Browse the repository at this point in the history
This acts as a signal to the signing group corresponding to a deposit
that failed funding that the depositor would like their UTXO back. This
signal is completely unenforced by the system, as once it is deliverable
the funding process has already failed completely and the signers have
retrieved their bonds. However, it allows for off-chain coordination to
return the funder's UTXO in unusual cases such as a funding transaction
that was unprovable to the contract.
  • Loading branch information
Shadowfiend committed Apr 19, 2020
1 parent 2e2cbac commit 77a44aa
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 1 deletion.
19 changes: 19 additions & 0 deletions solidity/contracts/DepositLog.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ contract DepositLog {
uint256 _timestamp
);

// This event is fired when a funder requests funder abort after
// FAILED_SETUP has been reached. Funder abort is a voluntary signer action
// to return UTXO(s) that were sent to a signer-controlled wallet despite
// the funding proofs having failed.
event FunderAbortRequested(
address indexed _depositContractAddress,
bytes _abortOutputScript
);

// This event is fired when we detect an ECDSA fraud before seeing a funding proof
event FraudDuringSetup(
address indexed _depositContractAddress,
Expand Down Expand Up @@ -211,6 +220,16 @@ contract DepositLog {
emit SetupFailed(msg.sender, block.timestamp);
}

/// @notice Fires a FunderAbortRequested event.
/// @dev We append the sender, which is the deposit contract that called.
function logFunderRequestedAbort(bytes memory _abortOutputScript) public {
require(
approvedToLog(msg.sender),
"Caller is not approved to log events"
);
emit FunderAbortRequested(msg.sender, _abortOutputScript);
}

/// @notice Fires a FraudDuringSetup event.
/// @dev We append the sender, which is the deposit contract that called.
function logFraudDuringSetup() public {
Expand Down
21 changes: 21 additions & 0 deletions solidity/contracts/deposit/Deposit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,27 @@ contract Deposit is DepositFactoryAuthority {
return true;
}

/// @notice Requests a funder abort for a failed-funding deposit; that is,
/// requests the return of a sent UTXO to _abortOutputScript. It
/// imposes no requirements on the signing group. Signers should
/// send their UTXO to the requested output script, but do so at
/// their discretion and with no penalty for failing to do so. This
/// can be used for example when a UTXO is sent that is the wrong
/// size for the lot.
/// @dev This is a self-admitted funder fault, and is only be callable by
/// the TDT holder. This function emits the FunderAbortRequested event,
/// but stores no additional state.
/// @param _abortOutputScript The output script the funder wishes to request
/// a return of their UTXO to.
function requestFunderAbort(bytes memory _abortOutputScript) public {
require(
self.depositOwner() == msg.sender,
"Only TDT holder can request funder abort"
);

self.requestFunderAbort(_abortOutputScript);
}

/// @notice Anyone can provide a signature that was not requested to prove fraud during funding.
/// @dev Calls out to the keep to verify if there was fraud.
/// @param _v Signature recovery value.
Expand Down
23 changes: 23 additions & 0 deletions solidity/contracts/deposit/DepositFunding.sol
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,29 @@ library DepositFunding {
fundingTeardown(_d);
}

/// @notice Requests a funder abort for a failed-funding deposit; that is,
/// requests return of a sent UTXO to `_abortOutputScript`. This can
/// be used for example when a UTXO is sent that is the wrong size
/// for the lot. Must be called after setup fails for any reason,
/// and imposes no requirement or incentive on the signing group to
/// return the UTXO.
/// @dev This is a self-admitted funder fault, and should only be callable
/// by the TDT holder.
/// @param _d Deposit storage pointer.
/// @param _abortOutputScript The output script the funder wishes to request
/// a return of their UTXO to.
function requestFunderAbort(
DepositUtils.Deposit storage _d,
bytes memory _abortOutputScript
) public {
require(
_d.inFailedSetup(),
"The deposit has not failed funding"
);

_d.logFunderRequestedAbort(_abortOutputScript);
}

/// @notice Anyone can provide a signature that was not requested to prove fraud during funding.
/// @dev Calls out to the keep to verify if there was fraud.
/// @param _d Deposit storage pointer.
Expand Down
10 changes: 10 additions & 0 deletions solidity/contracts/deposit/OutsourceDepositLogging.sol
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@ library OutsourceDepositLogging {
_logger.logSetupFailed();
}

/// @notice Fires a FunderAbortRequested event.
/// @dev The logger is on a system contract, so all logs from all deposits are from the same address.
function logFunderRequestedAbort(
DepositUtils.Deposit storage _d,
bytes memory _abortOutputScript
) public {
DepositLog _logger = DepositLog(address(_d.tbtcSystem));
_logger.logFunderRequestedAbort(_abortOutputScript);
}

/// @notice Fires a FraudDuringSetup event.
/// @dev The logger is on a system contract, so all logs from all deposits are from the same address.
function logFraudDuringSetup(DepositUtils.Deposit storage _d) external {
Expand Down
53 changes: 52 additions & 1 deletion solidity/test/DepositFundingTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ describe("DepositFunding", async function() {
let timer
let owner
let openKeepFee
before(async () => {})

before(async () => {
;({
Expand Down Expand Up @@ -394,6 +393,58 @@ describe("DepositFunding", async function() {
})
})

describe("requestFunderAbort", async () => {
let timer
let owner

before(async () => {
timer = await tbtcConstants.getFundingTimeout.call()
owner = accounts[1]
await tbtcDepositToken.forceMint(
owner,
web3.utils.toBN(testDeposit.address),
)
})

beforeEach(async () => {
const block = await web3.eth.getBlock("latest")
const blockTimestamp = block.timestamp
fundingProofTimerStart = blockTimestamp - timer.toNumber() - 1

await testDeposit.setState(states.AWAITING_BTC_FUNDING_PROOF)
await testDeposit.setFundingProofTimerStart(fundingProofTimerStart)
})

it("fails if the deposit has not failed setup", () => {
expectRevert(
testDeposit.requestFunderAbort("0x1234", {from: owner}),
"The deposit has not failed funding",
)
})

it("emits a FunderAbortRequested event", async () => {
const blockNumber = await web3.eth.getBlockNumber()
await testDeposit.notifyFundingTimeout()

const outputScript = "0x012345"
await testDeposit.requestFunderAbort(outputScript, {from: owner})

const eventList = await tbtcSystemStub.getPastEvents(
"FunderAbortRequested",
{
fromBlock: blockNumber,
toBlock: "latest",
},
)
expect(eventList.length).to.equal(1)
expect(eventList[0].name == "FunderAbortRequested")
expect(eventList[0].returnValues).to.contain({
_depositContractAddress: testDeposit.address,
_abortOutputScript: outputScript,
})
})
})

describe("provideBTCFundingProof", async () => {
beforeEach(async () => {
await mockRelay.setCurrentEpochDifficulty(currentDifficulty)
Expand Down

0 comments on commit 77a44aa

Please sign in to comment.