Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Process Exit Bounty] Implement Bounty for Processing Exits consolidated #698

Merged
merged 4 commits into from
Aug 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ contract PaymentInFlightExitRouterMock is FailFastReentrancyGuard, PaymentInFlig
}

/** override and calls processInFlightExit for test */
function processExit(uint168 exitId, uint256, address ercContract) external {
PaymentInFlightExitRouter.processInFlightExit(exitId, ercContract);
function processExit(uint168 exitId, uint256, address ercContract, address payable processExitInitiator) external {
PaymentInFlightExitRouter.processInFlightExit(exitId, ercContract, processExitInitiator);
}

function setInFlightExit(uint168 exitId, PaymentExitDataModel.InFlightExit memory exit) public {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ contract PaymentStandardExitRouterMock is PaymentStandardExitRouter {
}

/** override and calls processStandardExit for test */
function processExit(uint168 exitId, uint256, address ercContract) external {
PaymentStandardExitRouter.processStandardExit(exitId, ercContract);
function processExit(uint168 exitId, uint256, address ercContract, address payable processExitInitiator) external {
PaymentStandardExitRouter.processStandardExit(exitId, ercContract, processExitInitiator);
}

/** helper functions for testing */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ contract DummyExitGame is IExitProcessor {
);

// override ExitProcessor interface
function processExit(uint168 exitId, uint256 vaultId, address ercContract) public {
function processExit(uint168 exitId, uint256 vaultId, address ercContract, address payable) public {
emit ExitFinalizedFromDummyExitGame(exitId, vaultId, ercContract);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ contract ReentrancyExitGame is IExitProcessor {
}

// override ExitProcessor interface
// This would call the processExits back to mimic reentracy attack
function processExit(uint168, uint256, address) public {
exitGameController.processExits(vaultId, testToken, 0, reentryMaxExitToProcess);
// This would call the processExits back to mimic reentrancy attack
function processExit(uint168, uint256, address, address payable) public {
exitGameController.processExits(vaultId, testToken, 0, reentryMaxExitToProcess, keccak256(abi.encodePacked(msg.sender)));
}

function enqueue(uint256 _vaultId, address _token, uint32 _exitableAt, uint256 _txPos, uint168 _exitId, IExitProcessor _exitProcessor)
Expand Down
21 changes: 21 additions & 0 deletions plasma_framework/contracts/mocks/utils/ExitBountyMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
pragma solidity 0.5.11;

import "../../src/exits/utils/ExitBounty.sol";

contract ExitBountyMock {
using ExitBounty for ExitBounty.Params;

ExitBounty.Params public exitBounty;

constructor (uint128 initialExitBountySize, uint16 lowerBoundDivisor, uint16 upperBoundMultiplier) public {
exitBounty = ExitBounty.buildParams(initialExitBountySize, lowerBoundDivisor, upperBoundMultiplier);
}

function exitBountySize() public view returns (uint128) {
return exitBounty.exitBountySize();
}

function updateExitBountySize(uint128 newExitBountySize) public {
exitBounty.updateExitBountySize(newExitBountySize);
}
}
10 changes: 9 additions & 1 deletion plasma_framework/contracts/poc/fast_exits/Liquidity.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,19 @@ import "../../src/utils/PosLib.sol";
import "../../src/framework/models/BlockModel.sol";
import "../../src/utils/Merkle.sol";
import "../../src/exits/payment/routers/PaymentStandardExitRouter.sol";
import "../../src/exits/utils/ExitBounty.sol";
import "openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol";
import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
import "openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";

/**
* @title Liquidity Contract
* Implementation Doc - https://github.com/omisego/research/blob/master/plasma/simple_fast_withdrawals.md
*/
contract Liquidity is ERC721Full {
using SafeERC20 for IERC20;
using SafeMath for uint256;

PaymentExitGame public paymentExitGame;

Expand Down Expand Up @@ -165,7 +168,12 @@ contract Liquidity is ERC721Full {

FungibleTokenOutputModel.Output memory outputFromSecondTransaction
= decodedSecondTx.outputs[0];
exitData[exitId] = ExitData(msg.value, msg.sender, outputFromSecondTransaction.amount, outputFromSecondTransaction.token);
exitData[exitId] = ExitData(
msg.value.sub(paymentExitGame.processStandardExitBountySize()),
msg.sender,
outputFromSecondTransaction.amount,
outputFromSecondTransaction.token
);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ library PaymentExitDataModel {
* @param exitTarget The address to which the exit withdraws funds
* @param amount The amount of funds to withdraw with this exit
* @param bondSize The size of the bond put up for this exit to start, and which is used to cover the cost of challenges
* @param bountySize The size of the bounty put up to cover the cost of processing the exit
*/
struct StandardExit {
bool exitable;
Expand All @@ -23,6 +24,7 @@ library PaymentExitDataModel {
address payable exitTarget;
uint256 amount;
uint256 bondSize;
uint256 bountySize;
}

/**
Expand All @@ -41,6 +43,7 @@ library PaymentExitDataModel {
address token;
uint256 amount;
uint256 piggybackBondSize;
uint256 bountySize;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ contract PaymentExitGame is IExitProcessor, OnlyFromAddress, PaymentStandardExit
* @notice Callback processes exit function for the PlasmaFramework to call
* @param exitId The exit ID
* @param token Token (ERC20 address or address(0) for ETH) of the exiting output
* @param processExitInitiator The processExits() initiator
*/
function processExit(uint168 exitId, uint256, address token) external onlyFrom(address(paymentExitGameArgs.framework)) {
function processExit(uint168 exitId, uint256, address token, address payable processExitInitiator) external onlyFrom(address(paymentExitGameArgs.framework)) {
if (ExitId.isStandardExit(exitId)) {
PaymentStandardExitRouter.processStandardExit(exitId, token);
PaymentStandardExitRouter.processStandardExit(exitId, token, processExitInitiator);
} else {
PaymentInFlightExitRouter.processInFlightExit(exitId, token);
PaymentInFlightExitRouter.processInFlightExit(exitId, token, processExitInitiator);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ library PaymentChallengeIFEInputSpent {
ife.clearInputPiggybacked(args.inFlightTxInputIndex);

uint256 piggybackBondSize = ife.inputs[args.inFlightTxInputIndex].piggybackBondSize;
SafeEthTransfer.transferRevertOnError(msg.sender, piggybackBondSize, self.safeGasStipend);
uint256 bountySize = ife.inputs[args.inFlightTxInputIndex].bountySize;
SafeEthTransfer.transferRevertOnError(msg.sender, piggybackBondSize + bountySize, self.safeGasStipend);

emit InFlightExitInputBlocked(msg.sender, keccak256(args.inFlightTx), args.inFlightTxInputIndex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ library PaymentChallengeIFEOutputSpent {
ife.clearOutputPiggybacked(outputIndex);

uint256 piggybackBondSize = ife.outputs[outputIndex].piggybackBondSize;
SafeEthTransfer.transferRevertOnError(msg.sender, piggybackBondSize, controller.safeGasStipend);
uint256 bountySize = ife.outputs[outputIndex].bountySize;
SafeEthTransfer.transferRevertOnError(msg.sender, piggybackBondSize + bountySize, controller.safeGasStipend);

emit InFlightExitOutputBlocked(msg.sender, keccak256(args.inFlightTx), outputIndex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ library PaymentChallengeStandardExit {

exitMap.exits[args.exitId].exitable = false;

SafeEthTransfer.transferRevertOnError(msg.sender, data.exitData.bondSize, self.safeGasStipend);
SafeEthTransfer.transferRevertOnError(msg.sender, data.exitData.bondSize + data.exitData.bountySize, self.safeGasStipend);

emit ExitChallenged(data.exitData.utxoPos);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ import "../../../framework/interfaces/IExitProcessor.sol";
import "../../../transactions/PaymentTransactionModel.sol";
import "../../../utils/PosLib.sol";

import "openzeppelin-solidity/contracts/math/SafeMath.sol";

library PaymentPiggybackInFlightExit {
using PosLib for PosLib.Position;
using ExitableTimestamp for ExitableTimestamp.Calculator;
using PaymentInFlightExitModelUtils for PaymentExitDataModel.InFlightExit;
using SafeMath for uint256;

struct Controller {
PlasmaFramework framework;
Expand Down Expand Up @@ -71,7 +74,8 @@ library PaymentPiggybackInFlightExit {
function piggybackInput(
Controller memory self,
PaymentExitDataModel.InFlightExitMap storage inFlightExitMap,
PaymentInFlightExitRouterArgs.PiggybackInFlightExitOnInputArgs memory args
PaymentInFlightExitRouterArgs.PiggybackInFlightExitOnInputArgs memory args,
uint128 processInFlightExitBountySize
)
public
{
Expand All @@ -87,7 +91,8 @@ library PaymentPiggybackInFlightExit {
PaymentExitDataModel.WithdrawData storage withdrawData = exit.inputs[args.inputIndex];

require(withdrawData.exitTarget == msg.sender, "Can be called only by the exit target");
withdrawData.piggybackBondSize = msg.value;
withdrawData.bountySize = processInFlightExitBountySize;
withdrawData.piggybackBondSize = msg.value.sub(withdrawData.bountySize);

if (isFirstPiggybackOfTheToken(exit, withdrawData.token)) {
enqueue(self, withdrawData.token, PosLib.decode(exit.position), exitId);
Expand All @@ -108,7 +113,8 @@ library PaymentPiggybackInFlightExit {
function piggybackOutput(
Controller memory self,
PaymentExitDataModel.InFlightExitMap storage inFlightExitMap,
PaymentInFlightExitRouterArgs.PiggybackInFlightExitOnOutputArgs memory args
PaymentInFlightExitRouterArgs.PiggybackInFlightExitOnOutputArgs memory args,
uint128 processInFlightExitBountySize
)
public
{
Expand All @@ -124,7 +130,8 @@ library PaymentPiggybackInFlightExit {
PaymentExitDataModel.WithdrawData storage withdrawData = exit.outputs[args.outputIndex];

require(withdrawData.exitTarget == msg.sender, "Can be called only by the exit target");
withdrawData.piggybackBondSize = msg.value;
withdrawData.bountySize = processInFlightExitBountySize;
withdrawData.piggybackBondSize = msg.value.sub(withdrawData.bountySize);

if (isFirstPiggybackOfTheToken(exit, withdrawData.token)) {
enqueue(self, withdrawData.token, PosLib.decode(exit.position), exitId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ library PaymentProcessInFlightExit {
uint256 amount
);

event InFlightBountyReturnFailed(
address indexed receiver,
uint256 amount
);

/**
* @notice Main logic function to process in-flight exit
* @dev emits InFlightExitOmitted event if the exit is omitted
Expand All @@ -49,12 +54,14 @@ library PaymentProcessInFlightExit {
* @param exitMap The storage of all in-flight exit data
* @param exitId The exitId of the in-flight exit
* @param token The ERC20 token address of the exit; uses address(0) to represent ETH
@ @param processExitInitiator The processExits() initiator
*/
function run(
Controller memory self,
PaymentExitDataModel.InFlightExitMap storage exitMap,
uint168 exitId,
address token
address token,
address payable processExitInitiator
)
public
{
Expand Down Expand Up @@ -108,8 +115,8 @@ library PaymentProcessInFlightExit {
flagOutputsWhenCanonical(self.framework, exit, token, exitId);
}

returnInputPiggybackBonds(self, exit, token);
returnOutputPiggybackBonds(self, exit, token);
returnInputPiggybackBonds(self, exit, token, processExitInitiator);
returnOutputPiggybackBonds(self, exit, token, processExitInitiator);

clearPiggybackInputFlag(exit, token);
clearPiggybackOutputFlag(exit, token);
Expand Down Expand Up @@ -266,7 +273,8 @@ library PaymentProcessInFlightExit {
function returnInputPiggybackBonds(
Controller memory self,
PaymentExitDataModel.InFlightExit storage exit,
address token
address token,
address payable processExitInitiator
)
private
{
Expand All @@ -275,22 +283,32 @@ library PaymentProcessInFlightExit {

// If the input has been challenged, isInputPiggybacked() will return false
if (token == withdrawal.token && exit.isInputPiggybacked(i)) {
bool success = SafeEthTransfer.transferReturnResult(
bool successBondReturn = SafeEthTransfer.transferReturnResult(
withdrawal.exitTarget, withdrawal.piggybackBondSize, self.safeGasStipend
);

// we do not want to block a queue if bond return is unsuccessful
if (!success) {
if (!successBondReturn) {
emit InFlightBondReturnFailed(withdrawal.exitTarget, withdrawal.piggybackBondSize);
}

bool successBountyReturn = SafeEthTransfer.transferReturnResult(
processExitInitiator, withdrawal.bountySize, self.safeGasStipend
);

// we do not want to block a queue if bounty return is unsuccessful
if (!successBountyReturn) {
emit InFlightBountyReturnFailed(processExitInitiator, withdrawal.bountySize);
}
}
}
}

function returnOutputPiggybackBonds(
Controller memory self,
PaymentExitDataModel.InFlightExit storage exit,
address token
address token,
address payable processExitInitiator
)
private
{
Expand All @@ -299,14 +317,23 @@ library PaymentProcessInFlightExit {

// If the output has been challenged, isOutputPiggybacked() will return false
if (token == withdrawal.token && exit.isOutputPiggybacked(i)) {
bool success = SafeEthTransfer.transferReturnResult(
bool successBondReturn = SafeEthTransfer.transferReturnResult(
withdrawal.exitTarget, withdrawal.piggybackBondSize, self.safeGasStipend
);

// we do not want to block a queue if bond return is unsuccessful
if (!success) {
if (!successBondReturn) {
emit InFlightBondReturnFailed(withdrawal.exitTarget, withdrawal.piggybackBondSize);
}

bool successBountyReturn = SafeEthTransfer.transferReturnResult(
processExitInitiator, withdrawal.bountySize, self.safeGasStipend
);

// we do not want to block a queue if bounty return is unsuccessful
if (!successBountyReturn) {
emit InFlightBountyReturnFailed(processExitInitiator, withdrawal.bountySize);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ library PaymentProcessStandardExit {
uint256 amount
);

event BountyReturnFailed(
address indexed receiver,
uint256 amount
);

/**
* @notice Main logic function to process standard exit
* @dev emits ExitOmitted event if the exit is omitted
Expand All @@ -37,12 +42,14 @@ library PaymentProcessStandardExit {
* @param exitMap The storage of all standard exit data
* @param exitId The exitId of the standard exit
* @param token The ERC20 token address of the exit. Uses address(0) to represent ETH.
* @param processExitInitiator The processExits() initiator
*/
function run(
Controller memory self,
PaymentExitDataModel.StandardExitMap storage exitMap,
uint168 exitId,
address token
address token,
address payable processExitInitiator
)
public
{
Expand All @@ -57,11 +64,16 @@ library PaymentProcessStandardExit {
self.framework.flagOutputFinalized(exit.outputId, exitId);

// we do not want to block a queue if bond return is unsuccessful
bool success = SafeEthTransfer.transferReturnResult(exit.exitTarget, exit.bondSize, self.safeGasStipend);
if (!success) {
bool successBondReturn = SafeEthTransfer.transferReturnResult(exit.exitTarget, exit.bondSize, self.safeGasStipend);
if (!successBondReturn) {
emit BondReturnFailed(exit.exitTarget, exit.bondSize);
}

bool successBountyReturn = SafeEthTransfer.transferReturnResult(processExitInitiator, exit.bountySize, self.safeGasStipend);
if (!successBountyReturn) {
emit BountyReturnFailed(processExitInitiator, exit.bountySize);
}

if (token == address(0)) {
self.ethVault.withdraw(exit.exitTarget, exit.amount);
} else {
Expand Down
Loading