Skip to content

Commit db06a5f

Browse files
authored
Merge pull request #56 from luciditytech/feature/individual-phase-durations
feat(Chain): individual phase durations
2 parents fe89fdb + abb57d4 commit db06a5f

23 files changed

Lines changed: 206 additions & 153 deletions

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
## Unreleased
8+
###Changed
9+
- Each phase has its own duration
10+
711
## [0.5.1] - 2020-01-27
812
### Changed
913
- when updating contract via `ContractRegistry`, old `Chain` contract is not killed

config/development.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"verifiersPerShard": "10"
1111
},
1212
"Chain": {
13-
"blocksPerPhase": "10",
13+
"blocksPerPropose": "100",
14+
"blocksPerReveal": "10",
1415
"minimumStakingTokenPercentage": "70"
1516
},
1617
"ContractRegistry": {

config/production.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
},
1212
"Chain": {
1313
"verifierRegistryAddress": "0x614cb086657c47739068c12eeff43a4018be1190",
14-
"blocksPerPhase": "10",
14+
"blocksPerPropose": "100",
15+
"blocksPerReveal": "10",
1516
"minimumStakingTokenPercentage": "70"
1617
},
1718
"ContractRegistry": {

config/staging.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
},
1212
"Chain": {
1313
"verifierRegistryAddress": "0xd209f28511f37e3e975d4631b7cfa2f52ffabcef",
14-
"blocksPerPhase": "10",
14+
"blocksPerPropose": "100",
15+
"blocksPerReveal": "10",
1516
"minimumStakingTokenPercentage": "70"
1617
},
1718
"ContractRegistry": {

contracts/Chain.sol

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ contract Chain is IChain, RegistrableWithSingleStorage, ReentrancyGuard, Ownable
2020
bytes32 constant NAME = "Chain";
2121

2222
modifier whenProposePhase() {
23-
require(getCurrentElectionCycleBlock() < blocksPerPhase(), "we are not in propose phase");
23+
require(getCurrentElectionCycleBlock() < blocksPerPropose(), "we are not in propose phase");
2424
_;
2525
}
2626
modifier whenRevealPhase() {
27-
require(getCurrentElectionCycleBlock() >= blocksPerPhase(), "we are not in reveal phase");
27+
require(getCurrentElectionCycleBlock() >= blocksPerPropose(), "we are not in reveal phase");
2828
_;
2929
}
3030

@@ -113,7 +113,7 @@ contract Chain is IChain, RegistrableWithSingleStorage, ReentrancyGuard, Ownable
113113
}
114114

115115
function getBlockHeight() public view returns (uint256) {
116-
return block.number.div(uint256(blocksPerPhase()) * 2);
116+
return block.number.div(uint256(blocksPerPropose() + blocksPerReveal()) );
117117
}
118118

119119
/// @dev this function needs to be called each time we successfully reveal a proposal
@@ -235,18 +235,25 @@ contract Chain is IChain, RegistrableWithSingleStorage, ReentrancyGuard, Ownable
235235
return _storage().minimumStakingTokenPercentage();
236236
}
237237

238-
function blocksPerPhase()
238+
function blocksPerPropose()
239239
public
240240
view
241241
returns (uint8) {
242-
return _storage().blocksPerPhase();
242+
return _storage().blocksPerPropose();
243+
}
244+
245+
function blocksPerReveal()
246+
public
247+
view
248+
returns (uint8) {
249+
return _storage().blocksPerReveal();
243250
}
244251

245252
function getCurrentElectionCycleBlock()
246253
public
247254
view
248255
returns (uint256) {
249-
return block.number % (uint256(blocksPerPhase()) * 2);
256+
return block.number % (uint256(blocksPerPropose() + blocksPerReveal()));
250257
}
251258

252259
/// @return first block number (blockchain block) of current cycle
@@ -261,7 +268,7 @@ contract Chain is IChain, RegistrableWithSingleStorage, ReentrancyGuard, Ownable
261268
public
262269
view
263270
returns (bool) {
264-
return getCurrentElectionCycleBlock() < blocksPerPhase();
271+
return getCurrentElectionCycleBlock() < blocksPerPropose();
265272
}
266273

267274
function initialBlockHeights(uint256 _shard)

contracts/ChainStorage.sol

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,54 +17,69 @@ contract ChainStorage is StorageBase {
1717
/// @dev structure of block that is created for each election
1818
struct Block {
1919
/// @dev shard => root of merkle tree (the winner)
20-
mapping (uint256 => bytes32) roots;
21-
mapping (bytes32 => bool) uniqueBlindedProposals;
22-
mapping (address => Voter) voters;
20+
mapping(uint256 => bytes32) roots;
21+
mapping(bytes32 => bool) uniqueBlindedProposals;
22+
mapping(address => Voter) voters;
2323

2424
/// @dev shard => max votes
25-
mapping (uint256 => uint256) maxsVotes;
25+
mapping(uint256 => uint256) maxsVotes;
2626

2727
// shard => proposal => counts
2828
// Im using mapping, because its less gas consuming that array,
2929
// and also it is much easier to work with mapping than with array
3030
// unfortunately we can't be able to delete this data to release gas, why?
3131
// because to do this, we need to save all the keys and then run loop for all keys... that may cause OOG
3232
// also storing keys is more gas consuming so... I made decision to stay with mapping and never delete history
33-
mapping (uint256 => mapping(bytes32 => uint256)) counts;
33+
mapping(uint256 => mapping(bytes32 => uint256)) counts;
3434

3535
/// @dev shard => total amount of tokens
36-
mapping (uint256 => uint256) balancesPerShard;
36+
mapping(uint256 => uint256) balancesPerShard;
3737

3838
address[] verifierAddresses;
3939
}
4040

4141
/// @dev blockHeight => Block - results of each elections will be saved here: one block (array element) per election
42-
mapping (uint256 => Block) blocks;
42+
mapping(uint256 => Block) blocks;
4343

4444
/// @dev shard => blockHeight
45-
mapping (uint256 => uint256) public initialBlockHeights;
45+
mapping(uint256 => uint256) public initialBlockHeights;
4646

4747
bool public updateMinimumStakingTokenPercentageEnabled;
4848

49-
uint8 public blocksPerPhase;
49+
uint8 public blocksPerPropose;
50+
uint8 public blocksPerReveal;
5051

5152
uint8 public minimumStakingTokenPercentage;
5253

53-
event LogChainConfig(uint8 blocksPerPhase, uint8 requirePercentOfTokens, bool updateMinimumStakingTokenPercentageEnabled);
54+
event LogChainConfig(
55+
uint8 blocksPerPropose,
56+
uint8 blocksPerReveal,
57+
uint8 requirePercentOfTokens,
58+
bool updateMinimumStakingTokenPercentageEnabled
59+
);
5460

55-
constructor(uint8 _blocksPerPhase,
61+
constructor(
62+
uint8 _blocksPerPropose,
63+
uint8 _blocksPerReveal,
5664
uint8 _minimumStakingTokenPercentage,
5765
bool _updateMinimumStakingTokenPercentageEnabled) public {
58-
require(_blocksPerPhase > 0, "_blocksPerPhase can't be empty");
59-
blocksPerPhase = _blocksPerPhase;
66+
require(_blocksPerPropose > 0, "_blocksPerPropose can't be empty");
67+
require(_blocksPerReveal > 0, "_blocksPerReveal can't be empty");
68+
blocksPerPropose = _blocksPerPropose;
69+
blocksPerReveal = _blocksPerReveal;
6070

6171
require(_minimumStakingTokenPercentage > 0, "_minimumStakingTokenPercentage can't be empty");
6272
require(_minimumStakingTokenPercentage <= 100, "_minimumStakingTokenPercentage can't be over 100%");
6373
minimumStakingTokenPercentage = _minimumStakingTokenPercentage;
6474

6575
updateMinimumStakingTokenPercentageEnabled = _updateMinimumStakingTokenPercentageEnabled;
6676

67-
emit LogChainConfig(_blocksPerPhase, _minimumStakingTokenPercentage, _updateMinimumStakingTokenPercentageEnabled);
77+
emit LogChainConfig(
78+
_blocksPerPropose,
79+
_blocksPerReveal,
80+
_minimumStakingTokenPercentage,
81+
_updateMinimumStakingTokenPercentageEnabled
82+
);
6883
}
6984

7085
function getInitialBlockHeight(uint256 _shard) public view returns (uint256) {
@@ -147,7 +162,7 @@ contract ChainStorage is StorageBase {
147162
}
148163

149164
function updateBlockVoter(uint256 _blockHeight, address _voterAddr, bytes32 _blindedProposal, uint256 _shard, uint256 _balance)
150-
external onlyFromStorageOwner returns (bool) {
165+
external onlyFromStorageOwner returns (bool) {
151166
Voter storage voter = blocks[_blockHeight].voters[_voterAddr];
152167
voter.blindedProposal = _blindedProposal;
153168
voter.shard = _shard;
@@ -170,7 +185,7 @@ contract ChainStorage is StorageBase {
170185
require(_minimumStakingTokenPercentage <= 100, "_minimumStakingTokenPercentage can't be over 100%");
171186
minimumStakingTokenPercentage = _minimumStakingTokenPercentage;
172187

173-
emit LogChainConfig(blocksPerPhase, _minimumStakingTokenPercentage, true);
188+
emit LogChainConfig(blocksPerPropose, blocksPerReveal, _minimumStakingTokenPercentage, true);
174189

175190
return true;
176191
}

migrations/2.0_deploy_chain_storage.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ module.exports = (deployer, network, accounts) => {
77

88
return deployer.deploy(
99
ChainStorageArtifact,
10-
config.Chain.blocksPerPhase,
10+
config.Chain.blocksPerPropose,
11+
config.Chain.blocksPerReveal,
1112
config.Chain.minimumStakingTokenPercentage,
1213
false,
1314
options,

migrations/deployers/ChainStorage.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ module.exports = (
1010

1111
return deployer.deploy(
1212
ChainStorageArtifact,
13-
config.Chain.blocksPerPhase,
13+
config.Chain.blocksPerPropose,
14+
config.Chain.blocksPerReveal,
1415
config.Chain.minimumStakingTokenPercentage,
1516
false,
1617
options,

test/helpers/CycleFunctions.js

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
1-
function blockOfCycle(currentBlock, phaseDuration) {
2-
return currentBlock % (phaseDuration * 2);
1+
function blockOfCycle(currentBlock, cycleDuration) {
2+
return currentBlock % cycleDuration;
33
}
44

5-
function isProposePhase(currentBlock, phaseDuration) {
6-
return blockOfCycle(currentBlock, phaseDuration) < phaseDuration;
5+
function isProposePhase(currentBlock, proposePhaseDuration, revealPhaseDuration) {
6+
const cycleBlock = blockOfCycle(currentBlock, proposePhaseDuration + revealPhaseDuration);
7+
return cycleBlock < proposePhaseDuration;
78
}
89

9-
function isRevealPhase(currentBlock, phaseDuration) {
10-
return !isProposePhase(currentBlock, phaseDuration);
10+
function isRevealPhase(currentBlock, proposePhaseDuration, revealPhaseDuration) {
11+
return !isProposePhase(currentBlock, proposePhaseDuration, revealPhaseDuration);
1112
}
1213

13-
function blocksToWaitForPropose(currentBlock, phaseDuration) {
14-
if (isProposePhase(currentBlock, phaseDuration)) return 0;
14+
function blocksToWaitForPropose(currentBlock, proposePhaseDuration, revealPhaseDuration) {
15+
if (isProposePhase(currentBlock, proposePhaseDuration, revealPhaseDuration)) return 0;
1516

16-
return (phaseDuration * 2) - blockOfCycle(currentBlock, phaseDuration);
17+
// +1 because we want to be in first block in a phase
18+
return (proposePhaseDuration + revealPhaseDuration + 1) -
19+
blockOfCycle(currentBlock, proposePhaseDuration + revealPhaseDuration);
1720
}
1821

19-
function blocksToWaitForReveal(currentBlock, phaseDuration) {
20-
if (isRevealPhase(currentBlock, phaseDuration)) return 0;
22+
function blocksToWaitForReveal(currentBlock, proposePhaseDuration, revealPhaseDuration) {
23+
if (isRevealPhase(currentBlock, proposePhaseDuration, revealPhaseDuration)) return 0;
2124

22-
return phaseDuration - blockOfCycle(currentBlock, phaseDuration);
25+
return proposePhaseDuration -
26+
blockOfCycle(currentBlock, proposePhaseDuration + revealPhaseDuration);
2327
}
2428

2529
export {

test/helpers/SpecHelper.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ const advanceToBlock = async (number) => {
3737
const blockNumber = await web3.eth.getBlockNumber();
3838
if (blockNumber > number) {
3939
throw Error(`block number ${number} is in the past (current is ${blockNumber})`);
40+
} else if (blockNumber === number) {
41+
return Promise.resolve();
4042
}
4143

4244
const awaits = [];
@@ -50,30 +52,32 @@ const advanceToBlock = async (number) => {
5052
return Promise.all(awaits);
5153
};
5254

53-
const mineUntilPropose = async (phaseDuration) => {
55+
const mineUntilPropose = async (proposePhaseDuration, revealPhaseDuration) => {
5456
const blockNumber = await web3.eth.getBlockNumber();
57+
5558
const toMine =
56-
blocksToWaitForPropose(blockNumber, phaseDuration);
59+
blocksToWaitForPropose(blockNumber, proposePhaseDuration, revealPhaseDuration);
5760

5861
const prosalStartBlockNumber = new BigNumber(blockNumber).plus(toMine);
5962

6063
await advanceToBlock(prosalStartBlockNumber.toNumber());
6164
};
6265

63-
const mineUntilReveal = async (phaseDuration) => {
66+
const mineUntilReveal = async (proposePhaseDuration, revealPhaseDuration) => {
6467
const blockNumber = await web3.eth.getBlockNumber();
68+
6569
const toMine =
66-
blocksToWaitForReveal(blockNumber, phaseDuration);
70+
blocksToWaitForReveal(blockNumber, proposePhaseDuration, revealPhaseDuration);
6771

6872
const revealStartBlockNumber = new BigNumber(blockNumber).plus(toMine);
6973
await advanceToBlock(revealStartBlockNumber.toNumber());
7074
};
7175

72-
const getBlockHeight = async (phaseDuration) => {
76+
const getBlockHeight = async (proposePhaseDuration, revealPhaseDuration) => {
7377
const blockNumber = await web3.eth.getBlockNumber();
78+
7479
return BigNumber(blockNumber)
75-
.div(phaseDuration, 10)
76-
.div(2, 10)
80+
.div(parseInt(proposePhaseDuration, 10) + parseInt(revealPhaseDuration, 10), 10)
7781
.toString(10)
7882
.split('.')[0];
7983
};

0 commit comments

Comments
 (0)