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] Add process exit bounty for SE #665

Merged
merged 26 commits into from
Aug 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
853c3be
feat: add process exit bounty for SE
souradeep-das Jul 28, 2020
0741796
fix: pass in msg.sender to exitgame
souradeep-das Jul 29, 2020
28c8afe
style: lint js
souradeep-das Jul 29, 2020
89fefef
fix: coverage
souradeep-das Jul 29, 2020
b48c204
revert package.json
souradeep-das Jul 31, 2020
18dc9c9
merge v2.0.0 on branch
souradeep-das Jul 31, 2020
ad65388
fix: modify LC tests
souradeep-das Jul 31, 2020
a4c45bb
style: add newline eof
souradeep-das Jul 31, 2020
904bb4a
Merge branch 'v2.0.0' into souradeep/exit_bounty
souradeep-das Aug 3, 2020
50e4686
fix: modify python tests
souradeep-das Aug 6, 2020
51e3eff
merge v2.0.0 on branch
souradeep-das Aug 6, 2020
e5d620a
fix: rem gasprice form py tests
souradeep-das Aug 6, 2020
bd33161
fix: revert plasma_framework.py
souradeep-das Aug 6, 2020
ba78534
fix: revert plasma_framework.py
souradeep-das Aug 6, 2020
d75f2ff
feat: add Bounty lib tests
souradeep-das Aug 6, 2020
f0e7077
style: js lint
souradeep-das Aug 6, 2020
e3b527a
try python tests
souradeep-das Aug 7, 2020
04af275
add to python tests
souradeep-das Aug 7, 2020
9ea8faa
add w3 python tests
souradeep-das Aug 7, 2020
93ea584
fix: py tests
souradeep-das Aug 7, 2020
2d2b5c6
fix: lint py
souradeep-das Aug 7, 2020
6c78825
fix: lint py
souradeep-das Aug 7, 2020
95ee8d7
fix: minor remove unused w3
souradeep-das Aug 11, 2020
5d63ef8
style: use gasprice as args in lib
souradeep-das Aug 12, 2020
8be513c
style: add eof line
souradeep-das Aug 12, 2020
bb49316
style: remove unused contract
souradeep-das Aug 12, 2020
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,7 +30,7 @@ contract PaymentInFlightExitRouterMock is FailFastReentrancyGuard, PaymentInFlig
}

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

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 processor) external {
PaymentStandardExitRouter.processStandardExit(exitId, ercContract, processor);
}

/** 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 @@ -19,7 +19,7 @@ contract ReentrancyExitGame is IExitProcessor {

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

Expand Down
11 changes: 11 additions & 0 deletions plasma_framework/contracts/mocks/utils/ExitBountyWrapper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pragma solidity 0.5.11;

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

contract ExitBountyWrapper {

function processStandardExitBountySize(uint256 gasPriceStartExit) public view returns (uint256) {
return ExitBounty.processStandardExitBountySize(gasPriceStartExit);
}

}
8 changes: 7 additions & 1 deletion plasma_framework/contracts/poc/fast_exits/Liquidity.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ 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";
souradeep-das marked this conversation as resolved.
Show resolved Hide resolved
import "openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol";
import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
import "openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol";
Expand Down Expand Up @@ -165,7 +166,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 - ExitBounty.processStandardExitBountySize(tx.gasprice),
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 Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ 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 processor The processExit initiator
*/
function processExit(uint168 exitId, uint256, address token) external onlyFrom(address(paymentExitGameArgs.framework)) {
function processExit(uint168 exitId, uint256, address token, address payable processor) external onlyFrom(address(paymentExitGameArgs.framework)) {
boolafish marked this conversation as resolved.
Show resolved Hide resolved
if (ExitId.isStandardExit(exitId)) {
PaymentStandardExitRouter.processStandardExit(exitId, token);
PaymentStandardExitRouter.processStandardExit(exitId, token, processor);
} else {
PaymentInFlightExitRouter.processInFlightExit(exitId, token);
}
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 @@ -29,6 +29,11 @@ library PaymentProcessStandardExit {
uint256 amount
);

event BountyRewardFailed(
address indexed receiver,
uint256 amount
);

/**
* @notice Main logic function to process standard exit
* @dev emits ExitOmitted event if the exit is omitted
Expand All @@ -42,7 +47,8 @@ library PaymentProcessStandardExit {
Controller memory self,
PaymentExitDataModel.StandardExitMap storage exitMap,
uint168 exitId,
address token
address token,
address payable processor
)
public
{
Expand All @@ -57,11 +63,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(processor, exit.bountySize, self.safeGasStipend);
if (!successBountyReturn) {
emit BountyRewardFailed(processor, exit.bountySize);
}

if (token == address(0)) {
self.ethVault.withdraw(exit.exitTarget, exit.amount);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import "../../utils/ExitableTimestamp.sol";
import "../../utils/ExitId.sol";
import "../../utils/OutputId.sol";
import "../../utils/MoreVpFinalization.sol";
import "../../utils/ExitBounty.sol";
import "../../../transactions/PaymentTransactionModel.sol";
import "../../../utils/PosLib.sol";
import "../../../framework/PlasmaFramework.sol";
import "../../utils/ExitableTimestamp.sol";

library PaymentStartStandardExit {
using ExitableTimestamp for ExitableTimestamp.Calculator;
Expand Down Expand Up @@ -170,7 +170,8 @@ library PaymentStartStandardExit {
outputId: data.outputId,
exitTarget: msg.sender,
amount: data.output.amount,
bondSize: msg.value
bondSize: msg.value - ExitBounty.processStandardExitBountySize(tx.gasprice),
bountySize: ExitBounty.processStandardExitBountySize(tx.gasprice)
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ contract PaymentStandardExitRouter is
emit StandardExitBondUpdated(newBondSize);
}

/**
* @notice Retrieves the process standard exit bounty size
*/
function processStandardExitBountySize(uint256 gasPriceStartExit) public view returns (uint256) {
return ExitBounty.processStandardExitBountySize(gasPriceStartExit);
}

/**
* @notice Starts a standard exit of a given output, using output-age priority
*/
Expand All @@ -138,7 +145,7 @@ contract PaymentStandardExitRouter is
public
payable
nonReentrant(framework)
onlyWithValue(startStandardExitBondSize())
onlyWithValue(startStandardExitBondSize() + processStandardExitBountySize(tx.gasprice))
{
startStandardExitController.run(standardExitMap, args);
}
Expand All @@ -158,8 +165,9 @@ contract PaymentStandardExitRouter is
* @dev This function is designed to be called in the main processExit function, using internal
* @param exitId The standard exit ID
* @param token The token (in erc20 address or address(0) for ETH) of the exiting output
* @param processor The processExit initiator
*/
function processStandardExit(uint168 exitId, address token) internal {
processStandardExitController.run(standardExitMap, exitId, token);
function processStandardExit(uint168 exitId, address token, address payable processor) internal {
processStandardExitController.run(standardExitMap, exitId, token, processor);
}
}
14 changes: 14 additions & 0 deletions plasma_framework/contracts/src/exits/utils/ExitBounty.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pragma solidity 0.5.11;

library ExitBounty {

/**
* @notice Returns the Process Exit Bounty size for standard exits
* @dev See https://github.com/omgnetwork/plasma-contracts/issues/658 for discussion about size
* 107000 is the approx gas usage for calling processExit()
*/
function processStandardExitBountySize(uint256 gasPriceStartExit) internal view returns (uint256) {
return 107000 * gasPriceStartExit;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ contract ExitGameController is ExitGameRegistry {
queue.delMin();
processedNum++;

processor.processExit(exitId, vaultId, token);
processor.processExit(exitId, vaultId, token, msg.sender);
boolafish marked this conversation as resolved.
Show resolved Hide resolved

if (queue.currentSize() == 0) {
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface IExitProcessor {
* @param exitId Unique ID for exit per tx type
* @param vaultId ID of the vault that funds the exit
* @param token Address of the token contract
* @param processor Address of the processExit intitiator
*/
function processExit(uint168 exitId, uint256 vaultId, address token) external;
function processExit(uint168 exitId, uint256 vaultId, address token, address payable processor) external;
}
10 changes: 6 additions & 4 deletions plasma_framework/python_tests/testlang/testlang.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,20 @@ class StandardExit:
exitable (boolean): whether will exit at processing
output_id (str): output exit identifier (not exit id)
bond_size (int): value of paid bond
bounty_size (int): value of process exit bounty
"""

def __init__(self, exitable, utxo_pos, output_id, exit_target, amount, bond_size):
def __init__(self, exitable, utxo_pos, output_id, exit_target, amount, bond_size, bounty_size):
self.owner = exit_target
self.amount = amount
self.position = utxo_pos
self.exitable = exitable
self.output_id = output_id
self.bond_size = bond_size
self.bounty_size = bounty_size

def to_list(self):
return [self.owner, self.amount, self.position, self.exitable, self.output_id, self.bond_size]
return [self.owner, self.amount, self.position, self.exitable, self.output_id, self.bond_size, self.bounty_size]

def __str__(self):
return self.to_list().__str__()
Expand Down Expand Up @@ -224,9 +226,9 @@ def start_standard_exit_with_tx_body(self, output_id, output_tx, account, bond=N
transactions = block.transactions
merkle = FixedMerkle(16, list(map(lambda tx: tx.encoded, transactions)))
proof = merkle.create_membership_proof(output_tx.encoded)
bond = bond if bond is not None else self.root_chain.standardExitBond()
bond = bond if bond is not None else self.root_chain.standardExitBond() + self.root_chain.processStandardExitBounty()
self.root_chain.startStandardExit(output_id, output_tx.encoded, proof,
**{'value': bond, 'from': account.address})
**{'value': bond, 'from': account.address, 'gasPrice': 100})

def challenge_standard_exit(self, output_id, spend_id, input_index=None, signature=None):
spend_tx = self.child_chain.get_transaction(spend_id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ def prepare_exitable_utxo(testlang, owners, amount, outputs, num_outputs=1):


@pytest.mark.parametrize("num_outputs", [1, 2, 3, 4])
def test_process_exits_standard_exit_should_succeed(testlang, num_outputs, plasma_framework):
def test_process_exits_standard_exit_should_succeed(testlang, w3, num_outputs, plasma_framework):
amount = 100
utxo_pos, output_owner = prepare_exitable_utxo(testlang, [], amount, [], num_outputs)

pre_balance = testlang.get_balance(output_owner)
testlang.flush_events()

testlang.start_standard_exit(utxo_pos, output_owner)
gasCost = w3.eth.last_gas_used * 100
_, _, exit_id = plasma_framework.getNextExit(plasma_framework.eth_vault_id, NULL_ADDRESS_HEX)
start_exit_events = testlang.flush_events()

Expand All @@ -45,7 +46,7 @@ def test_process_exits_standard_exit_should_succeed(testlang, num_outputs, plasm
('ExitFinalized', {"exitId": exit_id}),
('ProcessedExitsNum', {'processedNum': 1, 'token': NULL_ADDRESS_HEX})])

assert testlang.get_balance(output_owner) == pre_balance + amount
assert testlang.get_balance(output_owner) == pre_balance + amount - gasCost - testlang.root_chain.processStandardExitBounty()


def test_successful_process_exit_should_clear_exit_fields_and_set_output_as_spent(testlang):
Expand Down Expand Up @@ -424,7 +425,7 @@ def test_finalize_exits_priority_for_in_flight_exits_corresponds_to_the_age_of_y
balance = testlang.get_balance(owner)

testlang.process_exits(NULL_ADDRESS, testlang.get_standard_exit_id(spend_00_id), 1)
assert testlang.get_balance(owner) == balance + 30 + testlang.root_chain.standardExitBond()
assert testlang.get_balance(owner) == balance + 30 + testlang.root_chain.standardExitBond() + testlang.root_chain.processStandardExitBounty()

balance = testlang.get_balance(owner)
testlang.process_exits(NULL_ADDRESS, testlang.get_in_flight_exit_id(spend_1_id), 1)
Expand All @@ -433,7 +434,7 @@ def test_finalize_exits_priority_for_in_flight_exits_corresponds_to_the_age_of_y

balance = testlang.get_balance(owner)
testlang.process_exits(NULL_ADDRESS, testlang.get_standard_exit_id(spend_2_id), 1)
assert testlang.get_balance(owner) == balance + 100 + testlang.root_chain.standardExitBond()
assert testlang.get_balance(owner) == balance + 100 + testlang.root_chain.standardExitBond() + testlang.root_chain.processStandardExitBounty()


def test_finalize_in_flight_exit_with_erc20_token_should_succeed(testlang, token, plasma_framework):
Expand Down Expand Up @@ -912,7 +913,7 @@ def test_should_not_allow_to_withdraw_outputs_from_two_ifes_marked_as_canonical_
# assert alice_token_balance == alice_token_balance_before


def test_not_challenged_standard_exit_blocks_ife_output_exit(testlang, plasma_framework, token):
def test_not_challenged_standard_exit_blocks_ife_output_exit(testlang, w3, plasma_framework, token):
alice, amount_token = testlang.accounts[1], 200
caroline, amount_eth_small, amount_eth_big = testlang.accounts[2], 1, 100

Expand All @@ -931,6 +932,7 @@ def test_not_challenged_standard_exit_blocks_ife_output_exit(testlang, plasma_fr
caroline_eth_balance_before = testlang.get_balance(caroline)

testlang.start_standard_exit(deposit_id_eth_small, caroline)
gasCost = w3.eth.last_gas_used * 100
testlang.start_in_flight_exit(swap_tx_id)

testlang.piggyback_in_flight_exit_output(swap_tx_id, 0, alice)
Expand All @@ -951,7 +953,7 @@ def test_not_challenged_standard_exit_blocks_ife_output_exit(testlang, plasma_fr
assert caroline_token_balance == caroline_token_balance_before
# but gets her Eth back
caroline_eth_balance = testlang.get_balance(caroline)
assert caroline_eth_balance == caroline_eth_balance_before + amount_eth_big + amount_eth_small
assert caroline_eth_balance == caroline_eth_balance_before + amount_eth_big + amount_eth_small - gasCost - testlang.root_chain.processStandardExitBounty()

# alice gets tokens
alice_token_balance = token.balanceOf(alice.address)
Expand All @@ -961,7 +963,7 @@ def test_not_challenged_standard_exit_blocks_ife_output_exit(testlang, plasma_fr
assert alice_eth_balance == alice_eth_balance_before


def test_challenged_standard_exit_does_not_block_ife_output_exit(testlang, plasma_framework, token):
def test_challenged_standard_exit_does_not_block_ife_output_exit(testlang, w3, plasma_framework, token):
alice, amount_token = testlang.accounts[1], 200
caroline, amount_eth_small, amount_eth_big = testlang.accounts[2], 1, 100

Expand All @@ -980,6 +982,7 @@ def test_challenged_standard_exit_does_not_block_ife_output_exit(testlang, plasm
caroline_eth_balance_before = testlang.get_balance(caroline)

testlang.start_standard_exit(deposit_id_eth_small, caroline)
gasCost = w3.eth.last_gas_used * 100
testlang.challenge_standard_exit(deposit_id_eth_small, swap_tx_id)
testlang.start_in_flight_exit(swap_tx_id)

Expand All @@ -1001,7 +1004,7 @@ def test_challenged_standard_exit_does_not_block_ife_output_exit(testlang, plasm
assert caroline_token_balance == caroline_token_balance_before + amount_token
# and does not get the Eth back
caroline_eth_balance = testlang.get_balance(caroline)
assert caroline_eth_balance == caroline_eth_balance_before - testlang.root_chain.standardExitBond()
assert caroline_eth_balance == caroline_eth_balance_before - testlang.root_chain.standardExitBond() - gasCost - testlang.root_chain.processStandardExitBounty()

# alice exits with her Eth output
alice_eth_balance = testlang.get_balance(alice)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,10 @@ def childBlockInterval(self):
def standardExitBond(self):
return self.payment_exit_game.startStandardExitBondSize()

def processStandardExitBounty(self):
dummy_gasprice = 100
return self.payment_exit_game.processStandardExitBountySize(dummy_gasprice)

def inFlightExitBond(self):
return self.payment_exit_game.startIFEBondSize()

Expand Down
Loading