Skip to content

Commit

Permalink
Merge pull request #150 from mysteriumnetwork/delete-exit-request
Browse files Browse the repository at this point in the history
Delete exit request
  • Loading branch information
Jaro committed Mar 15, 2022
2 parents ae7a6a4 + 30857d8 commit 8e25be2
Show file tree
Hide file tree
Showing 5 changed files with 2 additions and 173 deletions.
39 changes: 0 additions & 39 deletions contracts/ChannelImplementation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ contract ChannelImplementation is FundsRecovery, Utils {
using ECDSA for bytes32;

string constant EXIT_PREFIX = "Exit request:";
uint256 constant DELAY_SECONDS = 345600; // 4 days

uint256 internal lastNonce;

Expand Down Expand Up @@ -115,44 +114,6 @@ contract ChannelImplementation is FundsRecovery, Utils {
emit PromiseSettled(hermes.contractAddress, _unpaidAmount, hermes.settled, _lock);
}

// Returns timestamp until which exit request should be locked
function getTimelock() internal view virtual returns (uint256) {
return block.timestamp + DELAY_SECONDS;
}

// Start withdrawal of deposited but still not settled funds
// NOTE _validUntil is needed for replay protection
function requestExit(address _beneficiary, uint256 _validUntil, bytes memory _signature) public {
uint256 _timelock = getTimelock();

require(exitRequest.timelock == 0, "Channel: new exit can be requested only when old one was finalised");
require(_validUntil >= block.timestamp, "Channel: valid until have to be greater than or equal to current block timestamp");
require(_timelock > _validUntil, "Channel: request have to be valid shorter than DELAY_SECONDS");
require(_beneficiary != address(0), "Channel: beneficiary can't be zero address");

if (msg.sender != operator) {
address _channelId = address(this);
address _signer = keccak256(abi.encodePacked(EXIT_PREFIX, _channelId, _beneficiary, _validUntil)).recover(_signature);
require(_signer == operator, "Channel: have to be signed by operator");
}

exitRequest = ExitRequest(_timelock, _beneficiary);

emit ExitRequested(_timelock);
}

// Anyone can finalize exit request after timelock block passed
function finalizeExit() public {
require(exitRequest.timelock != 0 && block.timestamp >= exitRequest.timelock, "Channel: exit have to be requested and timelock have to be in past");

// Exit with all not settled funds
uint256 _amount = token.balanceOf(address(this));
token.transfer(exitRequest.beneficiary, _amount);
emit Withdraw(exitRequest.beneficiary, _amount);

exitRequest = ExitRequest(0, address(0)); // deleting request
}

// Fast funds withdrawal is possible when hermes agrees that given amount of funds can be withdrawn
function fastExit(uint256 _amount, uint256 _transactorFee, address _beneficiary, uint256 _validUntil, bytes memory _operatorSignature, bytes memory _hermesSignature) public {
require(_validUntil >= block.timestamp, "Channel: _validUntil have to be greater than or equal to current block timestamp");
Expand Down
39 changes: 0 additions & 39 deletions contracts/flattened/ChannelImplementation.sol.flattened
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,6 @@ contract ChannelImplementation is FundsRecovery, Utils {
using ECDSA for bytes32;

string constant EXIT_PREFIX = "Exit request:";
uint256 constant DELAY_SECONDS = 345600; // 4 days

uint256 internal lastNonce;

Expand Down Expand Up @@ -712,44 +711,6 @@ contract ChannelImplementation is FundsRecovery, Utils {
emit PromiseSettled(hermes.contractAddress, _unpaidAmount, hermes.settled, _lock);
}

// Returns timestamp until which exit request should be locked
function getTimelock() internal view virtual returns (uint256) {
return block.timestamp + DELAY_SECONDS;
}

// Start withdrawal of deposited but still not settled funds
// NOTE _validUntil is needed for replay protection
function requestExit(address _beneficiary, uint256 _validUntil, bytes memory _signature) public {
uint256 _timelock = getTimelock();

require(exitRequest.timelock == 0, "Channel: new exit can be requested only when old one was finalised");
require(_validUntil >= block.timestamp, "Channel: valid until have to be greater than or equal to current block timestamp");
require(_timelock > _validUntil, "Channel: request have to be valid shorter than DELAY_SECONDS");
require(_beneficiary != address(0), "Channel: beneficiary can't be zero address");

if (msg.sender != operator) {
address _channelId = address(this);
address _signer = keccak256(abi.encodePacked(EXIT_PREFIX, _channelId, _beneficiary, _validUntil)).recover(_signature);
require(_signer == operator, "Channel: have to be signed by operator");
}

exitRequest = ExitRequest(_timelock, _beneficiary);

emit ExitRequested(_timelock);
}

// Anyone can finalize exit request after timelock block passed
function finalizeExit() public {
require(exitRequest.timelock != 0 && block.timestamp >= exitRequest.timelock, "Channel: exit have to be requested and timelock have to be in past");

// Exit with all not settled funds
uint256 _amount = token.balanceOf(address(this));
token.transfer(exitRequest.beneficiary, _amount);
emit Withdraw(exitRequest.beneficiary, _amount);

exitRequest = ExitRequest(0, address(0)); // deleting request
}

// Fast funds withdrawal is possible when hermes agrees that given amount of funds can be withdrawn
function fastExit(uint256 _amount, uint256 _transactorFee, address _beneficiary, uint256 _validUntil, bytes memory _operatorSignature, bytes memory _hermesSignature) public {
require(_validUntil >= block.timestamp, "Channel: _validUntil have to be greater than or equal to current block timestamp");
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

89 changes: 0 additions & 89 deletions test/channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,95 +120,6 @@ contract('Channel Contract Implementation tests', ([txMaker, ...otherAccounts])
await wallet.sendTx(channel.address, constructPayload(promise), hermes).should.be.rejected
})

/**
* Testing channel exit scenarios
*/

let firstExitRequest
it("should successfully request exit channel", async () => {
const beneficiary = otherAccounts[1]
const { validUntil, signature } = await signExitRequest(channel, beneficiary, identity)
await channel.requestExit(beneficiary, validUntil, signature)

const exitRequest = await channel.exitRequest()
expect(exitRequest.beneficiary).to.be.equal(beneficiary)

// This will be needed in later requests
firstExitRequest = { beneficiary, validUntil, signature }
})

it("should fail requesting exit channel, when previous request is still active", async () => {
const beneficiary = otherAccounts[2] // different beneficiary
const { validUntil, signature } = await signExitRequest(channel, beneficiary, identity)
await channel.requestExit(beneficiary, validUntil, signature).should.be.rejected

const exitRequest = await channel.exitRequest()
expect(exitRequest.beneficiary).to.be.not.equal(beneficiary)
})

it("finalise exit should fail if requested before timelock", async () => {
const expectedTxBlockNumber = (await web3.eth.getBlock('latest')).number
const timelock = (await channel.exitRequest()).timelock
expect(timelock.toNumber()).to.be.above(expectedTxBlockNumber)

await channel.finalizeExit().should.be.rejected
})

it("during exit waiting period, receiving party should be able to settle latest promise", async () => {
const channelState = Object.assign({}, await channel.hermes(), { channelId: channel.address })
const channelBalanceBefore = await token.balanceOf(channel.address)
const hermesBalanceBefore = await token.balanceOf(channelState.contractAddress)

const promise = generatePromise(OneToken, new BN(0), channelState, identity)
await channel.settlePromise(promise.amount, promise.fee, promise.lock, promise.signature)

const channelBalanceAfter = await token.balanceOf(channel.address)
channelBalanceAfter.should.be.bignumber.equal(channelBalanceBefore.sub(OneToken))

const hermesBalanceAfter = await token.balanceOf(channelState.contractAddress)
hermesBalanceAfter.should.be.bignumber.equal(hermesBalanceBefore.add(OneToken))
})

it("should finalise exit request and send tokens into beneficiary address", async () => {
const beneficiary = otherAccounts[1]
const channelTokensBefore = await token.balanceOf(channel.address)
const delay = 3.5 // seconds

// Transaction's block time should be bigger or equal to timelock block
const expectedTxBlockTime = (await web3.eth.getBlock('latest')).timestamp + delay
const timelock = (await channel.exitRequest()).timelock
expect(expectedTxBlockTime).to.be.at.least(timelock.toNumber())

// Finalise request should be successful
await sleep(delay * 1000) // we have to wait at least `delay` seconds before doing next transaction
await channel.finalizeExit()

// All the left in channel tokens have to be sent into beneficiary address
expect((await token.balanceOf(channel.address)).toNumber()).to.be.equal(0)

const beneficiaryBalance = await token.balanceOf(beneficiary)
beneficiaryBalance.should.be.bignumber.equal(channelTokensBefore)

const exitRequest = await channel.exitRequest()
expect(exitRequest.timelock.toNumber()).to.be.equal(0)
})

it("should fail requesting exit with already used signature", async () => {
const { beneficiary, validUntil, signature } = firstExitRequest
await channel.requestExit(beneficiary, validUntil, signature).should.be.rejected

const exitRequest = await channel.exitRequest()
expect(exitRequest.timelock.toNumber()).to.be.equal(0)
})

it("should be possible to request new exit", async () => {
const { beneficiary, validUntil, signature } = await signExitRequest(channel, otherAccounts[0], identity)
await channel.requestExit(beneficiary, validUntil, signature)

const exitRequest = await channel.exitRequest()
expect(exitRequest.beneficiary).to.be.equal(beneficiary)
})

/**
* Testing topup with ETH via DEX
*/
Expand Down
4 changes: 0 additions & 4 deletions test/contracts/TestChannelImplementation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ contract TestChannelImplementation is ChannelImplementation {
initialize(_token, _dex, _identityHash, _hermesAddress, _fee);
}

function getTimelock() internal view override returns (uint256) {
return block.timestamp + TEST_DELAY_TIME;
}

function getNow() public view returns (uint256) {
return block.timestamp;
}
Expand Down

0 comments on commit 8e25be2

Please sign in to comment.