Skip to content

Commit

Permalink
[Process Exit Bounty] Implement Bounty for Processing Exits consolida…
Browse files Browse the repository at this point in the history
…ted (#698)

* [Process Exit Bounty] Add process exit bounty

* feat: add process exit bounty for SE

* fix: pass in msg.sender to exitgame

* style: lint js

* fix: coverage

* revert package.json

* fix: modify LC tests

* style: add newline eof

* fix: modify python tests

* fix: rem gasprice form py tests

* fix: revert plasma_framework.py

* fix: revert plasma_framework.py

* feat: add Bounty lib tests

* style: js lint

* try python tests

* add to python tests

* add w3 python tests

* fix: py tests

* fix: lint py

* fix: lint py

* fix: minor remove unused w3

* style: use gasprice as args in lib

* style: add eof line

* style: remove unused contract

[Process Exit Bounty] Add process exit bounty for IFE (#682)

* feat: add IFE bounty to contracts

* feat: add IFE bounty tests

* feat: add python tests

* fix: lint js

* fix: coverage

* feat: add lib tests

* feat: use safe-math

* style: rename vars

feat: anti-front running

feat: make bounty updatable

fix: py tests

fix: py tests

feat: add updatebounty test

style: update statements

* rename processor to processExitInitiator
  • Loading branch information
souradeep-das authored Aug 31, 2020
1 parent 7e71572 commit 5ed5f6b
Show file tree
Hide file tree
Showing 43 changed files with 1,659 additions and 432 deletions.
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

0 comments on commit 5ed5f6b

Please sign in to comment.