mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
WIP: SMC for phase 1
Former-commit-id: d756e5811f4d8803252276d83bc9e189473ed435 [formerly 3f9041a1733c9bb0882b395010008fe2aea5f74e] Former-commit-id: 3911cda4e088c50ba9a5d85e6a6af963c4773a91
This commit is contained in:
@@ -1,35 +1,38 @@
|
||||
pragma solidity ^0.4.19;
|
||||
|
||||
contract SMC {
|
||||
event TxToShard(address indexed to, int indexed shardId, int receiptId);
|
||||
event CollationAdded(int indexed shardId, uint expectedPeriodNumber,
|
||||
bytes32 periodStartPrevHash, bytes32 parentHash,
|
||||
bytes32 transactionRoot, address coinbase,
|
||||
bytes32 stateRoot, bytes32 receiptRoot,
|
||||
int number, bool isNewHead, int score);
|
||||
event HeaderAdded(uint indexed shard_id, bytes32 parent_hash,
|
||||
bytes32 chunk_root, int128 period, int128 height,
|
||||
address proposer_address, uint proposer_bid,
|
||||
bytes proposer_signature);
|
||||
event Deposit(address collator, int index);
|
||||
event Withdraw(int index);
|
||||
|
||||
// Entry in collator_registry
|
||||
struct Collator {
|
||||
// Amount of wei the collator holds
|
||||
uint deposit;
|
||||
// The collator's address
|
||||
address addr;
|
||||
// When collator asked for unregistration
|
||||
uint deregistered;
|
||||
// The collator's pool index
|
||||
int pool_index;
|
||||
}
|
||||
|
||||
// Entry in proposer_registry
|
||||
struct Proposer {
|
||||
// Shard id of the deposit
|
||||
uint shardId;
|
||||
// Deposit in ether in each shard
|
||||
uint[] deposit;
|
||||
}
|
||||
|
||||
struct CollationHeader {
|
||||
bytes32 parentHash;
|
||||
int score;
|
||||
}
|
||||
|
||||
struct Receipt {
|
||||
int shardId;
|
||||
uint txStartgas;
|
||||
uint txGasprice;
|
||||
uint value;
|
||||
bytes32 data;
|
||||
address sender;
|
||||
address to;
|
||||
uint256 shard_id; // pointer to shard
|
||||
bytes32 parent_hash; // pointer to parent header
|
||||
bytes32 chunk_root; // pointer to collation body
|
||||
int128 period; // Period which header should be included
|
||||
int128 height; // Collation's height
|
||||
address proposer_address; // Proposer's address
|
||||
uint256 proposer_bid; // Proposer's bid
|
||||
bytes proposer_signature; // Proposer's signature
|
||||
}
|
||||
|
||||
// Packed variables to be used in addHeader
|
||||
@@ -41,36 +44,55 @@ contract SMC {
|
||||
}
|
||||
|
||||
// collatorId => Collators
|
||||
mapping (int => Collator) public collators;
|
||||
// mapping (int => Collator) public collators;
|
||||
address[] public collator_pool;
|
||||
// proposerId => Proposer
|
||||
// mapping (int => Proposer) public proposers;
|
||||
Proposer[] public proposer_pool;
|
||||
|
||||
// Collator registry (deregistered is 0 for not yet deregistered collators)
|
||||
mapping (address => Collator) public collator_registry;
|
||||
mapping (address => Proposer) public proposer_registry;
|
||||
// shard_id => (header_hash => tree root)
|
||||
mapping (uint => mapping (bytes32 => bytes32)) public collation_trees;
|
||||
// shardId => (headerHash => CollationHeader)
|
||||
mapping (int => mapping (bytes32 => CollationHeader)) public collationHeaders;
|
||||
// receiptId => Receipt
|
||||
mapping (int => Receipt) public receipts;
|
||||
// shardId => headerHash
|
||||
mapping (int => bytes32) shardHead;
|
||||
mapping (int => bytes32) shardead;
|
||||
|
||||
// Number of collators
|
||||
int public numCollators;
|
||||
// Number of receipts
|
||||
int numReceipts;
|
||||
// Indexs of empty slots caused by the function `withdraw`
|
||||
mapping (int => int) emptySlotsStack;
|
||||
int public collator_pool_len;
|
||||
// Indexes of empty slots caused by the function `withdraw`
|
||||
int[] empty_slots_stack;
|
||||
// The top index of the stack in empty_slots_stack
|
||||
int emptySlotsStackTop;
|
||||
int empty_slots_stack_top;
|
||||
// Has the collator deposited before?
|
||||
mapping (address => bool) public isCollatorDeposited;
|
||||
mapping (address => bool) public is_collator_deposited;
|
||||
|
||||
// Constant values
|
||||
uint constant periodLength = 5;
|
||||
int constant public shardCount = 100;
|
||||
// The exact deposit size which you have to deposit to become a collator
|
||||
uint constant depositSize = 100 ether;
|
||||
uint constant PERIOD_LENGTH = 5;
|
||||
// Exact collation body size (1mb)
|
||||
uint constant COLLATION_SIZE = 2 ** 20;
|
||||
// Subsidy in vEth
|
||||
uint constant COLLATOR_SUBSIDY = 0.001 ether;
|
||||
// Number of shards
|
||||
int constant SHARD_COUNT = 100;
|
||||
// The minimum deposit size for a collator
|
||||
uint constant COLLATOR_DEPOSIT = 1000 ether;
|
||||
// The minimum deposit size for a proposer
|
||||
uint constant PROPOSER_DEPOSIT = 1 ether;
|
||||
// The minimum balance of a proposer (for collators)
|
||||
uint constant MIN_PROPOSER_BALANCE = 0.1 ether;
|
||||
// Time the ether is locked by collators
|
||||
uint constant COLLATOR_LOCKUP_LENGTH = 16128;
|
||||
// Time the ether is locked by proposers
|
||||
uint constant PROPOSER_LOCKUP_LENGTH = 48;
|
||||
// Number of periods ahead of current period, which the contract
|
||||
// is able to return the collator of that period
|
||||
uint constant lookAheadPeriods = 4;
|
||||
uint constant LOOKAHEAD_LENGTH = 4;
|
||||
|
||||
// Log the latest period number of the shard
|
||||
mapping (int => int) public periodHead;
|
||||
mapping (int => int) public period_head;
|
||||
|
||||
function SMC() public {
|
||||
}
|
||||
@@ -84,170 +106,162 @@ contract SMC {
|
||||
// Uses a block hash as a seed to pseudorandomly select a signer from the collator pool.
|
||||
// [TODO] Chance of being selected should be proportional to the collator's deposit.
|
||||
// Should be able to return a value for the current period or any future period up to.
|
||||
function getEligibleCollator(int _shardId, uint _period) public view returns(address) {
|
||||
require(_period >= lookAheadPeriods);
|
||||
require((_period - lookAheadPeriods) * periodLength < block.number);
|
||||
require(numCollators > 0);
|
||||
function get_eligible_collator(uint256 shard_id, uint256 period) public view returns(address) {
|
||||
require(period >= LOOKAHEAD_LENGTH);
|
||||
require((period - LOOKAHEAD_LENGTH) * PERIOD_LENGTH < block.number);
|
||||
require(collator_pool_len > 0);
|
||||
// [TODO] Should check further if this safe or not
|
||||
return collators[
|
||||
int(
|
||||
uint(
|
||||
keccak256(
|
||||
uint(block.blockhash((_period - lookAheadPeriods) * periodLength)),
|
||||
_shardId
|
||||
)
|
||||
) %
|
||||
uint(getCollatorsMaxIndex())
|
||||
)
|
||||
].addr;
|
||||
return collator_pool[
|
||||
uint(
|
||||
keccak256(
|
||||
uint(block.blockhash((period - LOOKAHEAD_LENGTH) * PERIOD_LENGTH)),
|
||||
shard_id
|
||||
)
|
||||
) %
|
||||
uint(collator_pool_len)
|
||||
];
|
||||
}
|
||||
|
||||
function deposit() public payable returns(int) {
|
||||
require(!isCollatorDeposited[msg.sender]);
|
||||
require(msg.value == depositSize);
|
||||
// Find the empty slot index in collators pool
|
||||
function compute_header_hash(uint256 shard_id,
|
||||
bytes32 parent_hash,
|
||||
bytes32 chunk_root,
|
||||
uint256 period,
|
||||
address proposer_address,
|
||||
uint256 proposer_bid) public returns(bytes32){
|
||||
|
||||
}
|
||||
|
||||
function register_collator() public payable returns(int) {
|
||||
address collator_address = msg.sender;
|
||||
require(!is_collator_deposited[msg.sender]);
|
||||
require(msg.value == COLLATOR_DEPOSIT);
|
||||
|
||||
is_collator_deposited[msg.sender] = true;
|
||||
int index;
|
||||
if (!isStackEmpty())
|
||||
index = stackPop();
|
||||
else
|
||||
index = int(numCollators);
|
||||
|
||||
collators[index] = Collator({
|
||||
deposit: msg.value,
|
||||
addr: msg.sender
|
||||
});
|
||||
++numCollators;
|
||||
isCollatorDeposited[msg.sender] = true;
|
||||
|
||||
Deposit(msg.sender, index);
|
||||
if (!empty_stack()) {
|
||||
index = stack_pop();
|
||||
collator_pool[uint(index)] = collator_address;
|
||||
}
|
||||
else {
|
||||
index = collator_pool_len;
|
||||
collator_pool.push(collator_address);
|
||||
}
|
||||
++collator_pool_len;
|
||||
Deposit(collator_address, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
// Removes the collator from the collator pool and refunds the deposited ether
|
||||
function withdraw(int _collatorIndex) public {
|
||||
require(msg.sender == collators[_collatorIndex].addr);
|
||||
// [FIXME] Should consider calling the collator's contract, might be useful
|
||||
// when the collator is a contract.
|
||||
collators[_collatorIndex].addr.transfer(collators[_collatorIndex].deposit);
|
||||
isCollatorDeposited[collators[_collatorIndex].addr] = false;
|
||||
delete collators[_collatorIndex];
|
||||
stackPush(_collatorIndex);
|
||||
--numCollators;
|
||||
Withdraw(_collatorIndex);
|
||||
function deregister_collator(int index) public {
|
||||
address collator_address = msg.sender;
|
||||
require(is_collator_deposited[msg.sender]);
|
||||
require(collator_pool[uint(index)] == collator_address);
|
||||
|
||||
collator_registry[collator_address] = Collator({
|
||||
deregistered: block.number,
|
||||
pool_index: index
|
||||
});
|
||||
|
||||
stack_push(index);
|
||||
--collator_pool_len;
|
||||
// Withdraw(_collatorIndex);
|
||||
}
|
||||
|
||||
function release_collator() public {
|
||||
|
||||
}
|
||||
|
||||
function register_proposer() public payable returns(int) {
|
||||
|
||||
}
|
||||
|
||||
function deregister_proposer() public {
|
||||
|
||||
}
|
||||
|
||||
function release_proposer() public {
|
||||
|
||||
}
|
||||
|
||||
function proposer_add_balance(uint shard_id) payable public {
|
||||
|
||||
}
|
||||
|
||||
function proposer_withdraw_balance(uint shard_id) public {
|
||||
|
||||
}
|
||||
|
||||
// Attempts to process a collation header, returns true on success, reverts on failure.
|
||||
function addHeader(int _shardId, uint _expectedPeriodNumber, bytes32 _periodStartPrevHash,
|
||||
bytes32 _parentHash, bytes32 _transactionRoot,
|
||||
address _coinbase, bytes32 _stateRoot, bytes32 _receiptRoot,
|
||||
int _number) public returns(bool) {
|
||||
HeaderVars memory headerVars;
|
||||
function addHeader(uint _shardId, uint period, bytes32 height,
|
||||
bytes32 _parent_hash, bytes32 chunk_root,
|
||||
address proposer_address, uint proposer_bid,
|
||||
bytes proposer_signature) public returns(bool) {
|
||||
// HeaderVars memory headerVars;
|
||||
|
||||
// Check if the header is valid
|
||||
require((_shardId >= 0) && (_shardId < shardCount));
|
||||
require(block.number >= periodLength);
|
||||
require(_expectedPeriodNumber == block.number / periodLength);
|
||||
require(_periodStartPrevHash == block.blockhash(_expectedPeriodNumber * periodLength - 1));
|
||||
// // Check if the header is valid
|
||||
// require((_shardId >= 0) && (_shardId < shardCount));
|
||||
// require(block.number >= periodLength);
|
||||
// require(_expectedPeriodNumber == block.number / periodLength);
|
||||
// require(_periodStartPrevHash == block.blockhash(_expectedPeriodNumber * periodLength - 1));
|
||||
|
||||
// Check if this header already exists
|
||||
headerVars.entireHeaderHash = keccak256(_shardId, _expectedPeriodNumber, _periodStartPrevHash,
|
||||
_parentHash, _transactionRoot, bytes32(_coinbase),
|
||||
_stateRoot, _receiptRoot, _number);
|
||||
assert(collationHeaders[_shardId][headerVars.entireHeaderHash].score == 0);
|
||||
// Check whether the parent exists.
|
||||
// if (parent_collation_hash == 0), i.e., is the genesis,
|
||||
// then there is no need to check.
|
||||
if (_parentHash != 0x0)
|
||||
assert(collationHeaders[_shardId][_parentHash].score > 0);
|
||||
// Check if only one collation in one period
|
||||
assert(periodHead[_shardId] < int(_expectedPeriodNumber));
|
||||
// // Check if this header already exists
|
||||
// headerVars.entireHeaderHash = keccak256(_shardId, _expectedPeriodNumber, _periodStartPrevHash,
|
||||
// _parentHash, _transactionRoot, bytes32(_coinbase),
|
||||
// _stateRoot, _receiptRoot, _number);
|
||||
// assert(collationHeaders[_shardId][headerVars.entireHeaderHash].score == 0);
|
||||
// // Check whether the parent exists.
|
||||
// // if (parent_collation_hash == 0), i.e., is the genesis,
|
||||
// // then there is no need to check.
|
||||
// if (_parentHash != 0x0)
|
||||
// assert(collationHeaders[_shardId][_parentHash].score > 0);
|
||||
// // Check if only one collation in one period
|
||||
// assert(periodHead[_shardId] < int(_expectedPeriodNumber));
|
||||
|
||||
// Check the signature with validation_code_addr
|
||||
headerVars.collatorAddr = getEligibleCollator(_shardId, block.number/periodLength);
|
||||
require(headerVars.collatorAddr != 0x0);
|
||||
require(msg.sender == headerVars.collatorAddr);
|
||||
// // Check the signature with validation_code_addr
|
||||
// headerVars.collatorAddr = getEligibleCollator(_shardId, block.number/periodLength);
|
||||
// require(headerVars.collatorAddr != 0x0);
|
||||
// require(msg.sender == headerVars.collatorAddr);
|
||||
|
||||
// Check score == collationNumber
|
||||
headerVars.score = collationHeaders[_shardId][_parentHash].score + 1;
|
||||
require(_number == headerVars.score);
|
||||
// // Check score == collationNumber
|
||||
// headerVars.score = collationHeaders[_shardId][_parentHash].score + 1;
|
||||
// require(_number == headerVars.score);
|
||||
|
||||
// Add the header
|
||||
collationHeaders[_shardId][headerVars.entireHeaderHash] = CollationHeader({
|
||||
parentHash: _parentHash,
|
||||
score: headerVars.score
|
||||
});
|
||||
// // Add the header
|
||||
// collationHeaders[_shardId][headerVars.entireHeaderHash] = CollationHeader({
|
||||
// parentHash: _parentHash,
|
||||
// score: headerVars.score
|
||||
// });
|
||||
|
||||
// Update the latest period number
|
||||
periodHead[_shardId] = int(_expectedPeriodNumber);
|
||||
// // Update the latest period number
|
||||
// periodHead[_shardId] = int(_expectedPeriodNumber);
|
||||
|
||||
// Determine the head
|
||||
if (headerVars.score > collationHeaders[_shardId][shardHead[_shardId]].score) {
|
||||
shardHead[_shardId] = headerVars.entireHeaderHash;
|
||||
headerVars.isNewHead = true;
|
||||
}
|
||||
// // Determine the head
|
||||
// if (headerVars.score > collationHeaders[_shardId][shardHead[_shardId]].score) {
|
||||
// shardHead[_shardId] = headerVars.entireHeaderHash;
|
||||
// headerVars.isNewHead = true;
|
||||
// }
|
||||
|
||||
CollationAdded(_shardId, _expectedPeriodNumber, _periodStartPrevHash,
|
||||
_parentHash, _transactionRoot, _coinbase, _stateRoot,
|
||||
_receiptRoot, _number, headerVars.isNewHead, headerVars.score);
|
||||
// CollationAdded(_shardId, _expectedPeriodNumber, _periodStartPrevHash,
|
||||
// _parentHash, _transactionRoot, _coinbase, _stateRoot,
|
||||
// _receiptRoot, _number, headerVars.isNewHead, headerVars.score);
|
||||
|
||||
return true;
|
||||
// return true;
|
||||
}
|
||||
|
||||
// Records a request to deposit msg.value ETH to address to in shard shard_id
|
||||
// during a future collation. Saves a `receipt ID` for this request,
|
||||
// also saving `msg.sender`, `msg.value`, `to`, `shard_id`, `startgas`,
|
||||
// `gasprice`, and `data`.
|
||||
function txToShard(address _to, int _shardId, uint _txStartgas, uint _txGasprice,
|
||||
bytes12 _data) public payable returns(int) {
|
||||
receipts[numReceipts] = Receipt({
|
||||
shardId: _shardId,
|
||||
txStartgas: _txStartgas,
|
||||
txGasprice: _txGasprice,
|
||||
value: msg.value,
|
||||
sender: msg.sender,
|
||||
to: _to,
|
||||
data: _data
|
||||
});
|
||||
var receiptId = numReceipts;
|
||||
++numReceipts;
|
||||
|
||||
TxToShard(_to, _shardId, receiptId);
|
||||
return receiptId;
|
||||
function empty_stack() internal view returns(bool) {
|
||||
return empty_slots_stack_top == 0;
|
||||
}
|
||||
|
||||
function updateGasPrice(int _receiptId, uint _txGasprice) public payable returns(bool) {
|
||||
require(receipts[_receiptId].sender == msg.sender);
|
||||
receipts[_receiptId].txGasprice = _txGasprice;
|
||||
return true;
|
||||
function stack_push(int index) internal {
|
||||
empty_slots_stack[uint(empty_slots_stack_top)] = index;
|
||||
++empty_slots_stack_top;
|
||||
}
|
||||
|
||||
function isStackEmpty() internal view returns(bool) {
|
||||
return emptySlotsStackTop == 0;
|
||||
}
|
||||
|
||||
function stackPush(int index) internal {
|
||||
emptySlotsStack[emptySlotsStackTop] = index;
|
||||
++emptySlotsStackTop;
|
||||
}
|
||||
|
||||
function stackPop() internal returns(int) {
|
||||
if (isStackEmpty())
|
||||
function stack_pop() internal returns(int) {
|
||||
if (empty_stack())
|
||||
return -1;
|
||||
--emptySlotsStackTop;
|
||||
return emptySlotsStack[emptySlotsStackTop];
|
||||
}
|
||||
|
||||
function getCollatorsMaxIndex() internal view returns(int) {
|
||||
int activateCollatorNum = 0;
|
||||
int allCollatorSlotsNum = numCollators + emptySlotsStackTop;
|
||||
|
||||
// TODO: any better way to iterate the mapping?
|
||||
for (int i = 0; i < 1024; ++i) {
|
||||
if (i >= allCollatorSlotsNum)
|
||||
break;
|
||||
if (collators[i].addr != 0x0)
|
||||
activateCollatorNum += 1;
|
||||
}
|
||||
return activateCollatorNum + emptySlotsStackTop;
|
||||
--empty_slots_stack_top;
|
||||
return empty_slots_stack[uint(empty_slots_stack_top)];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user