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

Destructing staker proxy via ost composer contract #777

Merged
merged 3 commits into from
Jun 25, 2019
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
24 changes: 13 additions & 11 deletions contracts/gateway/OSTComposer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,14 @@ import "./StakerProxy.sol";
import "./EIP20GatewayInterface.sol";
import "../lib/EIP20Interface.sol";
import "../lib/Mutex.sol";
import "./ComposerInterface.sol";

/**
* @title OSTComposer implements Organized contract. Reentrancy is prevented
* by using Mutex contract.
*
* @notice It facilitates the staker to get the OSTPrime on sidechains.
*/
contract OSTComposer is Organized, Mutex, ComposerInterface {
contract OSTComposer is Organized, Mutex {

/* Constants */

Expand Down Expand Up @@ -365,19 +364,22 @@ contract OSTComposer is Organized, Mutex, ComposerInterface {
}

/**
* @notice It can only be called by StakerProxy contract of the staker. It
* deletes the StakerProxy contract of the staker.
*
* @param _owner Owner of the StakerProxy contract.
* @notice It can only be called by owner of the staker proxy. It
* deletes the StakerProxy contract of the staker and calls self
* destruct on StakerProxy contract.
*/
function removeStakerProxy(
address _owner
)
function destructStakerProxy()
external
onlyStakerProxy(_owner)
{

StakerProxy stakerProxy = stakerProxies[msg.sender];
require(
address(stakerProxy) != address(0),
"Staker proxy does not exist for the caller."
);
// Resetting the proxy address of the staker.
delete stakerProxies[_owner];
delete stakerProxies[msg.sender];
stakerProxy.selfDestruct();
}


Expand Down
12 changes: 4 additions & 8 deletions contracts/gateway/StakerProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ pragma solidity ^0.5.0;
//
// ----------------------------------------------------------------------------

import "./ComposerInterface.sol";
import "./EIP20GatewayInterface.sol";
import "../lib/EIP20Interface.sol";
import "../lib/Mutex.sol";
Expand All @@ -38,7 +37,7 @@ contract StakerProxy is Mutex {
/* Storage */

/** The composer that deployed this contract. */
ComposerInterface public composer;
address public composer;
pgev marked this conversation as resolved.
Show resolved Hide resolved

/** The composer deployed the StakerProxy on behalf of the owner. */
address payable public owner;
Expand Down Expand Up @@ -70,13 +69,12 @@ contract StakerProxy is Mutex {
/* Special Functions */

/**
* @notice Must be constructed by a contract that implements the
* `ComposerInterface`.
* @notice Must be constructed by a composer contract.
*
* @param _owner The owner that this proxy is deployed for.
*/
constructor(address payable _owner) public {
composer = ComposerInterface(msg.sender);
composer = msg.sender;
owner = _owner;
}

Expand Down Expand Up @@ -160,9 +158,7 @@ contract StakerProxy is Mutex {
* to transfer all remaining token balance of this contract before
* calling this method.
*/
function selfDestruct() external onlyOwner {
composer.removeStakerProxy(owner);

function selfDestruct() external onlyComposer {
selfdestruct(owner);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,29 @@ pragma solidity ^0.5.0;
//
// ----------------------------------------------------------------------------

/**
* @title MockStakerProxy is a contract used for unit testing of ostComposer
* method `destructStakerProxy`.
*/
contract MockStakerProxy {

interface ComposerInterface {
/* Storage */

/** Flag to assert if self destruct is called. */
bool public selfDestruted;


/* Special Functions */

constructor () public {
selfDestruted = false;
}

/**
* @notice It deletes the StakerProxy contract for the staker.
*
* @param _owner Owner of the StakerProxy contract.
* @notice Mock method called from ost composer during unit testing of
* `destructStakerProxy`.
*/
function removeStakerProxy(address _owner) external;
function selfDestruct() external {
selfDestruted = true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,14 @@

const OSTComposer = artifacts.require('TestOSTComposer');
const MockOrganization = artifacts.require('MockOrganization');
const StakerProxy = artifacts.require('StakerProxy');
const BN = require('bn.js');
const StakerProxy = artifacts.require('MockStakerProxy');
const Utils = require('../../test_lib/utils');

contract('OSTComposer.removeStakerProxy() ', (accounts) => {
contract('OSTComposer.destructStakerProxy() ', (accounts) => {
let organization;
let ostComposer;
const stakeRequest = {};
let stakerProxy;
let expectedActiveGatewayCount;
let owner;
let worker;
beforeEach(async () => {
Expand All @@ -38,15 +36,13 @@ contract('OSTComposer.removeStakerProxy() ', (accounts) => {
organization = await MockOrganization.new(owner, worker);
stakeRequest.staker = accounts[2];
ostComposer = await OSTComposer.new(organization.address);
expectedActiveGatewayCount = new BN(0);
stakerProxy = accounts[10];
await ostComposer.setStakerProxy(stakeRequest.staker, stakerProxy);
stakerProxy = await StakerProxy.new();
await ostComposer.setStakerProxy(stakeRequest.staker, stakerProxy.address);
});

it('should be able to successfully remove staker proxy', async () => {
const response = await ostComposer.removeStakerProxy(
stakeRequest.staker,
{ from: stakerProxy },
const response = await ostComposer.destructStakerProxy(
{ from: stakeRequest.staker },
);

assert.strictEqual(response.receipt.status, true, 'Receipt status is unsuccessful');
Expand All @@ -57,27 +53,22 @@ contract('OSTComposer.removeStakerProxy() ', (accounts) => {
Utils.NULL_ADDRESS,
'Staker\'s proxy address must be reset to null',
);
});

it('should fail when non proxy address request removal of staker proxy', async () => {
const nonProxy = accounts[8];
await Utils.expectRevert(
ostComposer.removeStakerProxy(
stakeRequest.staker,
{ from: nonProxy },
),
'Caller is invalid proxy address.',
const isSelfDestructed = await stakerProxy.selfDestruted.call();
assert.strictEqual(
isSelfDestructed,
true,
'Staker proxy self destruct must be called',
);
});

it('should fail when owner doesn\'t have any deployed staker proxy', async () => {
const nonOwner = accounts[12];
const nonProxy = accounts[8];
await Utils.expectRevert(
ostComposer.removeStakerProxy(
nonOwner,
{ from: stakerProxy },
ostComposer.destructStakerProxy(
{ from: nonProxy },
),
'Caller is invalid proxy address.',
'Staker proxy does not exist for the caller.',
);
});
});
57 changes: 57 additions & 0 deletions test/gateway/staker_proxy/self_destruct.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2019 OpenST Ltd.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would propose to test the followings in addition:

  • That contract was actually selfdestruct -ed. (web3.eth.getCode(address) can be helpful I would assume).
  • That remaining ether was sent to the owner.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can add contract code assertion, to ensure that selfdestruct is called. Asserting ether will be more like testing solidity selfdestruct works fine or not. Also, I don't think this contract can hold ether. There is not payable function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've thought, like this:
we do: selfdestruct(owner) and we want to test that, and not that we do selfdestruct() or selfdestruct(non-owner-address). What do you think?

//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ----------------------------------------------------------------------------
pgev marked this conversation as resolved.
Show resolved Hide resolved
//
// http://www.simpletoken.org/
//
// ----------------------------------------------------------------------------

'use strict';

const StakerProxy = artifacts.require('./StakerProxy.sol');
const Utils = require('../../test_lib/utils');

contract('StakerProxy.selfDestruct()', (accounts) => {
const [composer, owner, anyOtherAddress] = accounts;
let stakerProxy;

beforeEach(async () => {
stakerProxy = await StakerProxy.new(
owner,
{ from: composer },
);
});

it('should allow self destruct by composer ', async () => {
const response = await stakerProxy.selfDestruct({ from: composer });

assert.strictEqual(response.receipt.status, true, 'Receipt status is unsuccessful');

const stakerProxyCode = await web3.eth.getCode(stakerProxy.address);

assert.strictEqual(
stakerProxyCode,
'0x',
'Staker proxy contract code should be 0x after self destructing',
);
});

it('should not allow self destruct by non composer address ', async () => {
await Utils.expectRevert(
stakerProxy.selfDestruct({ from: anyOtherAddress }),
'This function can only be called by the composer.',
);
});
});