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

Storing previous randomness values #1197

Merged
merged 27 commits into from
Oct 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
bc4ee67
store previous randomness values
mrsmkl Oct 3, 2019
d75a4e6
limited size of history
mrsmkl Oct 4, 2019
5ab9df2
this won't work
mrsmkl Oct 9, 2019
e5b1794
added first tests
mrsmkl Oct 9, 2019
876259c
cleanup
mrsmkl Oct 9, 2019
504de69
Merge branch 'master' of github.com:celo-org/celo-monorepo into rando…
mrsmkl Oct 11, 2019
5ffe716
review changes
mrsmkl Oct 11, 2019
0fb543a
fixing lint
mrsmkl Oct 11, 2019
71324ca
Merge branch 'master' into randomness-history-1181
mrsmkl Oct 11, 2019
faf1ac3
remove _random, checking if something is broken
mrsmkl Oct 15, 2019
f059b9d
Merge branch 'master' of github.com:celo-org/celo-monorepo into rando…
mrsmkl Oct 15, 2019
c370059
Merge branch 'randomness-history-1181' of github.com:mrsmkl/celo-mono…
mrsmkl Oct 15, 2019
4cadc87
added require errors
mrsmkl Oct 15, 2019
dd89be9
fixed lint
mrsmkl Oct 15, 2019
a4045cf
bump
mrsmkl Oct 15, 2019
2862f33
bump
mrsmkl Oct 15, 2019
346a220
oops
mrsmkl Oct 15, 2019
02a3c25
fixed tests
mrsmkl Oct 15, 2019
0ead27d
merge
mrsmkl Oct 18, 2019
eabd053
added comments
mrsmkl Oct 18, 2019
911e8a9
fixing lint
mrsmkl Oct 18, 2019
a4af20b
trying to remove some flakiness
mrsmkl Oct 18, 2019
92eaeb6
reverting changes
mrsmkl Oct 18, 2019
ec33e52
trying to reset
mrsmkl Oct 18, 2019
388ba22
Merge branch 'master' into randomness-history-1181
mrsmkl Oct 18, 2019
1c76973
Merge branch 'master' into randomness-history-1181
nambrot Oct 25, 2019
a488cd5
Merge branch 'master' into randomness-history-1181
Oct 25, 2019
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
122 changes: 113 additions & 9 deletions packages/protocol/contracts/identity/Random.sol
Original file line number Diff line number Diff line change
@@ -1,19 +1,47 @@
pragma solidity ^0.5.3;

import "./interfaces/IRandom.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";

import "../common/Initializable.sol";

/**
* @title Provides randomness for verifier selection
*/
contract Random is IRandom {
contract Random is IRandom, Ownable, Initializable {

using SafeMath for uint256;

/* Stores most recent commitment per address */
mapping(address => bytes32) public commitments;

bytes32 public _random;
uint256 public randomnessBlockRetentionWindow = 256;

mapping (uint256 => bytes32) private history;
uint256 private historyFirst;
uint256 private historySize;

event RandomnessBlockRetentionWindowSet(uint256 value);

function initialize() external {
/**
* @notice Initializes the contract with initial parameters.
* @param _randomnessBlockRetentionWindow Number of old random blocks whose randomness
* values can be queried.
*/
function initialize(uint256 _randomnessBlockRetentionWindow) external initializer {
_transferOwnership(msg.sender);
setRandomnessBlockRetentionWindow(_randomnessBlockRetentionWindow);
}

/**
* @notice Sets the number of old random blocks whose randomness values can be queried.
* @param value Number of old random blocks whose randomness values can be queried.
*/
function setRandomnessBlockRetentionWindow(uint256 value) public onlyOwner {
require(value > 0, "randomnessBlockRetetionWindow cannot be zero");
randomnessBlockRetentionWindow = value;
mrsmkl marked this conversation as resolved.
Show resolved Hide resolved
emit RandomnessBlockRetentionWindowSet(value);
}

/**
Expand All @@ -31,28 +59,104 @@ contract Random is IRandom {
bytes32 newCommitment,
address proposer
) external {
require(msg.sender == address(0));
require(msg.sender == address(0), "only VM can call");
_revealAndCommit(randomness, newCommitment, proposer);
}

/**
* @notice Implements step of the randomness protocol.
* @param randomness Bytes that will be added to the entropy pool.
* @param newCommitment The hash of randomness that will be revealed in the future.
* @param proposer Address of the block proposer.
*/
function _revealAndCommit(
asaj marked this conversation as resolved.
Show resolved Hide resolved
bytes32 randomness,
bytes32 newCommitment,
address proposer
) internal {
// ensure revealed randomness matches previous commitment
if (commitments[proposer] != 0) {
require(randomness != 0);
require(randomness != 0, "randomness cannot be zero if there is a previous commitment");
bytes32 expectedCommitment = computeCommitment(randomness);
require(expectedCommitment == commitments[proposer]);
require(
expectedCommitment == commitments[proposer],
"commitment didn't match the posted randomness"
);
} else {
require(randomness == 0);
require(randomness == 0, "randomness should be zero if there is no previous commitment");
}

// add entropy
_random = keccak256(abi.encodePacked(_random, randomness));
uint256 blockNumber = block.number == 0 ? 0 : block.number.sub(1);
addRandomness(block.number, keccak256(abi.encodePacked(history[blockNumber], randomness)));

commitments[proposer] = newCommitment;
}

/**
* @notice Add a value to the randomness history.
* @param blockNumber Current block number.
* @param randomness The new randomness added to history.
* @dev The calls to this function should be made so that on the next call, blockNumber will
* be the previous one, incremented by one.
*/
function addRandomness(uint256 blockNumber, bytes32 randomness) internal {
history[blockNumber] = randomness;
if (historySize == 0) {
historyFirst = block.number;
historySize = 1;
} else if (historySize > randomnessBlockRetentionWindow) {
delete history[historyFirst];
delete history[historyFirst+1];
historyFirst += 2;
historySize--;
} else if (historySize == randomnessBlockRetentionWindow) {
delete history[historyFirst];
historyFirst++;
} else /* historySize < randomnessBlockRetentionWindow */ {
historySize++;
}
}

/**
* @notice Compute the commitment hash for a given randomness value.
* @param randomness The value for which the commitment hash is computed.
* @return Commitment parameter.
*/
function computeCommitment(bytes32 randomness) public pure returns (bytes32) {
return keccak256(abi.encodePacked(randomness));
}

/**
* @notice Querying the current randomness value.
* @return Returns the current randomness value.
*/
function random() external view returns (bytes32) {
return _random;
return _getBlockRandomness(block.number, block.number);
}

/**
* @notice Get randomness values of previous blocks.
* @param blockNumber The number of block whose randomness value we want to know.
* @return The associated randomness value.
*/
function getBlockRandomness(uint256 blockNumber) external view returns (bytes32) {
return _getBlockRandomness(blockNumber, block.number);
}

/**
* @notice Get randomness values of previous blocks.
* @param blockNumber The number of block whose randomness value we want to know.
* @param cur Number of the current block.
* @return The associated randomness value.
*/
function _getBlockRandomness(uint256 blockNumber, uint256 cur) internal view returns (bytes32) {
require(blockNumber <= cur, "Cannot query randomness of future blocks");
require(
blockNumber > cur.sub(historySize) &&
(randomnessBlockRetentionWindow >= cur ||
blockNumber > cur.sub(randomnessBlockRetentionWindow)),
"Cannot query randomness older than the stored history");
return history[blockNumber];
}
}
13 changes: 13 additions & 0 deletions packages/protocol/contracts/identity/test/TestRandom.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
pragma solidity ^0.5.3;

import "../Random.sol";

contract TestRandom is Random {
function addTestRandomness(uint256 blockNumber, bytes32 randomness) external {
addRandomness(blockNumber, randomness);
}
function getTestRandomness(uint256 blockNumber, uint256 cur) external view returns (bytes32) {
return _getBlockRandomness(blockNumber, cur);
}
}

12 changes: 11 additions & 1 deletion packages/protocol/migrations/13_random.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { CeloContractName } from '@celo/protocol/lib/registry-utils'
import { deploymentForCoreContract } from '@celo/protocol/lib/web3-utils'
import { config } from '@celo/protocol/migrationsConfig'
import { RandomInstance } from 'types'

module.exports = deploymentForCoreContract<RandomInstance>(web3, artifacts, CeloContractName.Random)
const initializeArgs = async (_: string): Promise<any[]> => {
return [config.random.randomnessBlockRetentionWindow]
}

module.exports = deploymentForCoreContract<RandomInstance>(
web3,
artifacts,
CeloContractName.Random,
initializeArgs
)
3 changes: 2 additions & 1 deletion packages/protocol/migrations/16_governance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ module.exports = deploymentForCoreContract<GovernanceInstance>(
)
await reserve.addSpender(governance.address)

const proxyOwnedByGovernance = ['GoldToken', 'Random']
const proxyOwnedByGovernance = ['GoldToken']
await Promise.all(
proxyOwnedByGovernance.map((contractName) =>
transferOwnershipOfProxy(contractName, governance.address, artifacts)
Expand All @@ -61,6 +61,7 @@ module.exports = deploymentForCoreContract<GovernanceInstance>(
'GasPriceMinimum',
'Governance',
'LockedGold',
'Random',
'Registry',
'Reserve',
'SortedOracles',
Expand Down
37 changes: 20 additions & 17 deletions packages/protocol/migrationsConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ const DefaultConfig = {
attestationExpirySeconds: 60 * 60, // 1 hour,
attestationRequestFeeInDollars: 0.05,
},
lockedGold: {
unlockingPeriod: 60 * 60 * 24 * 3, // 3 days
},
oracles: {
reportExpiry: 60 * 60, // 1 hour
blockchainParameters: {
minimumClientVersion: {
major: 1,
minor: 8,
patch: 23,
},
},
election: {
minElectableValidators: '22',
Expand All @@ -29,6 +30,12 @@ const DefaultConfig = {
updateFrequency: 3600,
minimumReports: 1,
},
gasPriceMinimum: {
initialMinimum: 10000,
targetDensity: 1 / 2,
adjustmentSpeed: 1 / 2,
proposerFraction: 1 / 2,
},
governance: {
approvalStageDuration: 15 * 60, // 15 minutes
concurrentProposals: 10,
Expand All @@ -42,11 +49,14 @@ const DefaultConfig = {
participationBaselineUpdateFactor: 1 / 5,
participationBaselineQuorumFactor: 1,
},
gasPriceMinimum: {
initialMinimum: 10000,
targetDensity: 1 / 2,
adjustmentSpeed: 1 / 2,
proposerFraction: 1 / 2,
lockedGold: {
asaj marked this conversation as resolved.
Show resolved Hide resolved
unlockingPeriod: 60 * 60 * 24 * 3, // 3 days
},
oracles: {
reportExpiry: 60 * 60, // 1 hour
},
random: {
randomnessBlockRetentionWindow: 256,
},
registry: {
predeployedProxyAddress: '0x000000000000000000000000000000000000ce10',
Expand Down Expand Up @@ -91,13 +101,6 @@ const DefaultConfig = {
groupName: 'C-Labs',
commission: 0.1,
},
blockchainParameters: {
minimumClientVersion: {
major: 1,
minor: 8,
patch: 23,
},
},
}

const linkedLibraries = {
Expand Down
11 changes: 6 additions & 5 deletions packages/protocol/test/identity/attestations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ import {
MockLockedGoldInstance,
MockStableTokenContract,
MockStableTokenInstance,
MockElectionContract,
MockElectionInstance,
RandomContract,
RandomInstance,
TestRandomContract,
TestRandomInstance,
MockElectionContract,
RegistryContract,
RegistryInstance,
} from 'types'
Expand All @@ -37,7 +37,7 @@ const Attestations: TestAttestationsContract = artifacts.require('TestAttestatio
const MockStableToken: MockStableTokenContract = artifacts.require('MockStableToken')
const MockElection: MockElectionContract = artifacts.require('MockElection')
const MockLockedGold: MockLockedGoldContract = artifacts.require('MockLockedGold')
const Random: RandomContract = artifacts.require('Random')
const Random: TestRandomContract = artifacts.require('TestRandom')
const Registry: RegistryContract = artifacts.require('Registry')

const dataEncryptionKey = '0x02f2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e01611111111'
Expand All @@ -49,7 +49,7 @@ contract('Attestations', (accounts: string[]) => {
let attestations: TestAttestationsInstance
let mockStableToken: MockStableTokenInstance
let otherMockStableToken: MockStableTokenInstance
let random: RandomInstance
let random: TestRandomInstance
let mockElection: MockElectionInstance
let mockLockedGold: MockLockedGoldInstance
let registry: RegistryInstance
Expand Down Expand Up @@ -142,6 +142,7 @@ contract('Attestations', (accounts: string[]) => {
otherMockStableToken = await MockStableToken.new()
attestations = await Attestations.new()
random = await Random.new()
random.addTestRandomness(0, '0x00')
mockLockedGold = await MockLockedGold.new()
await Promise.all(
accounts.map((account) =>
Expand Down
Loading