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

Feat/eng 382 unbond function #15

Merged
merged 13 commits into from
Jul 19, 2018
1 change: 0 additions & 1 deletion contracts/ProviderPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ contract ProviderPool is Ownable {
return providerPool.contains(_provider);
}

// TODO: Add default value in the main constructor
function setMaxNumberOfProviders(uint _maxNumber) external onlyOwner {
providerPool.setMaxSize(_maxNumber);
}
Expand Down
18 changes: 14 additions & 4 deletions contracts/RoundManager.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
pragma solidity ^0.4.24;

import "zeppelin-solidity/contracts/math/SafeMath.sol";
import "zeppelin-solidity/contracts/ownership/Ownable.sol";

contract RoundManager {
contract RoundManager is Ownable {
using SafeMath for uint;

// Round number of the last round
Expand All @@ -12,15 +13,24 @@ contract RoundManager {

uint public electionPeriodLength;
uint public rateLockDeadline;
// The time (in number of blocks) that a Delegator has to wait before he can withdraw() his tokens
uint public unbondingPeriod;

modifier onlyBeforeActiveRoundIsLocked() {
require(block.number.sub(startOfCurrentRound) < electionPeriodLength.sub(rateLockDeadline));
_;
}

constructor() public {
electionPeriodLength = 20;
rateLockDeadline = 5;
function setElectionPeriodLength(uint _electionPeriodLength) public onlyOwner {
electionPeriodLength = _electionPeriodLength;
}

function setRateLockDeadline(uint _rateLockDeadline) public onlyOwner {
rateLockDeadline = _rateLockDeadline;
}

function setUnbondingPeriod(uint _unbondingPeriod) public onlyOwner {
unbondingPeriod = _unbondingPeriod;
}

function initializeRound() external {
Expand Down
62 changes: 62 additions & 0 deletions contracts/TransmuteDPOS.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ import "./ProviderPool.sol";

contract TransmuteDPOS is TransmuteToken, RoundManager, ProviderPool {

event DelegatorBonded(
address indexed _delegator,
address indexed _provider,
uint _amount
);

event DelegatorUnbonded(
address indexed _delegator,
address indexed _provider,
uint _amount
);

event ProviderAdded (
address indexed _provider,
uint _pricePerStorageMineral,
Expand All @@ -26,8 +38,11 @@ contract TransmuteDPOS is TransmuteToken, RoundManager, ProviderPool {
address indexed _provider
);

enum DelegatorStatus { Unbonded, UnbondedWithTokensToWithdraw, Bonded }

struct Delegator {
address delegateAddress;
// TODO: rename variable
uint amountBonded;
}

Expand All @@ -48,6 +63,16 @@ contract TransmuteDPOS is TransmuteToken, RoundManager, ProviderPool {
uint public numberOfProviders;
mapping(address => Provider) public providers;

mapping (address => uint) public withdrawBlocks;

// FIXME: Those are temporary values
constructor() public {
// Set constants from RoundManager
electionPeriodLength = 20;
rateLockDeadline = 5;
unbondingPeriod = 10;
}

function provider(uint _pricePerStorageMineral, uint _pricePerComputeMineral, uint _blockRewardCut, uint _feeShare)
external onlyBeforeActiveRoundIsLocked
{
Expand Down Expand Up @@ -92,5 +117,42 @@ contract TransmuteDPOS is TransmuteToken, RoundManager, ProviderPool {
if (p.status == ProviderStatus.Registered) {
updateProvider(_provider, p.totalAmountBonded);
}
emit DelegatorBonded(msg.sender, _provider, _amount);
}

function unbond() external {
// Only Bonded Delegators can call the function
require(delegatorStatus(msg.sender) == DelegatorStatus.Bonded);
// TODO: What if a Provider calls unbond() on himself ?
// Should he resign ?

Choose a reason for hiding this comment

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

That seems reasonable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do that next PR

// What about the tokens of the Delegators that bonded to him ?

Choose a reason for hiding this comment

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

Refunded - but who pays the gas cost for that? Him, I suppose.

Copy link
Contributor Author

@gjgd gjgd Jul 19, 2018

Choose a reason for hiding this comment

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

Yeah, but that could potentially be a huge cost in gas.
An alternative would be to let Delegators unbond() by themselves if they received the event that their Provider resigned.

Copy link
Sponsor Member

Choose a reason for hiding this comment

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

How does Livepeer handle this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They resign the Transcoder without refunding the delegators.

// For now we prevent providers from calling this function
require(providers[msg.sender].status == ProviderStatus.Unregistered);

Delegator storage d = delegators[msg.sender];
Provider storage p = providers[d.delegateAddress];
// Sets the block number from which the Delegator will be able to withdraw() his tokens
withdrawBlocks[msg.sender] = block.number.add(unbondingPeriod);
// Decrease the totalAmountBonded parameter of the provider
p.totalAmountBonded = p.totalAmountBonded.sub(d.amountBonded);
updateProvider(d.delegateAddress, p.totalAmountBonded);
emit DelegatorUnbonded(msg.sender, d.delegateAddress, d.amountBonded);
// Remove delegator from the list. He is no longer in the the Bonded State
delete delegators[msg.sender];
}

// TODO: Create the same function for Providers
// This will remove the need for ProviderStatus inside the Provider Struct
function delegatorStatus(address _delegator) public view returns (DelegatorStatus) {
if (delegators[_delegator].amountBonded != 0) {
// If _delegator is in the mapping, he is Bonded
return DelegatorStatus.Bonded;
} else if (withdrawBlocks[_delegator] != 0) {
// Else if _delegator has a withdrawBlock, he just called unbond() and didn't withdraw() yet
return DelegatorStatus.UnbondedWithTokensToWithdraw;
} else {
// Else he is Unbonded: either he didn't call bond() or he called bond() unbond() and withdraw()
return DelegatorStatus.Unbonded;
}
}
}
9 changes: 7 additions & 2 deletions test/TestRoundManager.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ const { blockMiner, assertFail } = require('./utils.js');

contract('RoundManager', accounts => {

let rm, electionPeriodLength;
let rm;
const electionPeriodLength = 20;
const rateLockDeadline = 5;
const unbondingPeriod = 10;

describe('initializeRound', () => {

before(async () => {
rm = await RoundManager.deployed();
electionPeriodLength = await rm.electionPeriodLength.call();
await rm.setElectionPeriodLength(electionPeriodLength);
await rm.setRateLockDeadline(rateLockDeadline);
await rm.setUnbondingPeriod(unbondingPeriod);
await blockMiner.mineUntilEndOfElectionPeriod(rm);
assert.equal(0, (web3.eth.blockNumber + 1) % electionPeriodLength);
});
Expand Down
Loading