mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 23:18:15 -05:00
merged
Former-commit-id: d9e4ef72bd42bf36b98c7e052d9a34838ae996dc [formerly 8e0d5dca4592d67663d91655a6abeca60c37b27d] Former-commit-id: 821fc21775c8e5d973dca6c4702bca43905f777d
This commit is contained in:
@@ -1,398 +0,0 @@
|
||||
pragma solidity ^0.4.19;
|
||||
|
||||
/**
|
||||
* @title RLPReader
|
||||
*
|
||||
* RLPReader is used to read and parse RLP encoded data in memory.
|
||||
*
|
||||
* @author Andreas Olofsson (androlo1980@gmail.com)
|
||||
*/
|
||||
library RLP {
|
||||
|
||||
uint constant DATA_SHORT_START = 0x80;
|
||||
uint constant DATA_LONG_START = 0xB8;
|
||||
uint constant LIST_SHORT_START = 0xC0;
|
||||
uint constant LIST_LONG_START = 0xF8;
|
||||
|
||||
uint constant DATA_LONG_OFFSET = 0xB7;
|
||||
uint constant LIST_LONG_OFFSET = 0xF7;
|
||||
|
||||
|
||||
struct RLPItem {
|
||||
uint _unsafe_memPtr; // Pointer to the RLP-encoded bytes.
|
||||
uint _unsafe_length; // Number of bytes. This is the full length of the string.
|
||||
}
|
||||
|
||||
struct Iterator {
|
||||
RLPItem _unsafe_item; // Item that's being iterated over.
|
||||
uint _unsafe_nextPtr; // Position of the next item in the list.
|
||||
}
|
||||
|
||||
/* Iterator */
|
||||
|
||||
function next(Iterator memory self) internal pure returns (RLPItem memory subItem) {
|
||||
if(hasNext(self)) {
|
||||
var ptr = self._unsafe_nextPtr;
|
||||
var itemLength = _itemLength(ptr);
|
||||
subItem._unsafe_memPtr = ptr;
|
||||
subItem._unsafe_length = itemLength;
|
||||
self._unsafe_nextPtr = ptr + itemLength;
|
||||
}
|
||||
else
|
||||
revert();
|
||||
}
|
||||
|
||||
function next(Iterator memory self, bool strict) internal pure returns (RLPItem memory subItem) {
|
||||
subItem = next(self);
|
||||
require(!strict || _validate(subItem));
|
||||
return;
|
||||
}
|
||||
|
||||
function hasNext(Iterator memory self) internal pure returns (bool) {
|
||||
var item = self._unsafe_item;
|
||||
return self._unsafe_nextPtr < item._unsafe_memPtr + item._unsafe_length;
|
||||
}
|
||||
|
||||
/* RLPItem */
|
||||
|
||||
/// @dev Creates an RLPItem from an array of RLP encoded bytes.
|
||||
/// @param self The RLP encoded bytes.
|
||||
/// @return An RLPItem
|
||||
function toRLPItem(bytes memory self) internal pure returns (RLPItem memory) {
|
||||
uint len = self.length;
|
||||
if (len == 0) {
|
||||
return RLPItem(0, 0);
|
||||
}
|
||||
uint memPtr;
|
||||
assembly {
|
||||
memPtr := add(self, 0x20)
|
||||
}
|
||||
return RLPItem(memPtr, len);
|
||||
}
|
||||
|
||||
/// @dev Creates an RLPItem from an array of RLP encoded bytes.
|
||||
/// @param self The RLP encoded bytes.
|
||||
/// @param strict Will throw if the data is not RLP encoded.
|
||||
/// @return An RLPItem
|
||||
function toRLPItem(bytes memory self, bool strict) internal pure returns (RLPItem memory) {
|
||||
var item = toRLPItem(self);
|
||||
if(strict) {
|
||||
uint len = self.length;
|
||||
assert(_payloadOffset(item) <= len);
|
||||
assert(_itemLength(item._unsafe_memPtr) == len);
|
||||
assert(_validate(item));
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/// @dev Check if the RLP item is null.
|
||||
/// @param self The RLP item.
|
||||
/// @return 'true' if the item is null.
|
||||
function isNull(RLPItem memory self) internal pure returns (bool ret) {
|
||||
return self._unsafe_length == 0;
|
||||
}
|
||||
|
||||
/// @dev Check if the RLP item is a list.
|
||||
/// @param self The RLP item.
|
||||
/// @return 'true' if the item is a list.
|
||||
function isList(RLPItem memory self) internal pure returns (bool ret) {
|
||||
if (self._unsafe_length == 0)
|
||||
return false;
|
||||
uint memPtr = self._unsafe_memPtr;
|
||||
assembly {
|
||||
ret := iszero(lt(byte(0, mload(memPtr)), 0xC0))
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Check if the RLP item is data.
|
||||
/// @param self The RLP item.
|
||||
/// @return 'true' if the item is data.
|
||||
function isData(RLPItem memory self) internal pure returns (bool ret) {
|
||||
if (self._unsafe_length == 0)
|
||||
return false;
|
||||
uint memPtr = self._unsafe_memPtr;
|
||||
assembly {
|
||||
ret := lt(byte(0, mload(memPtr)), 0xC0)
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Check if the RLP item is empty (string or list).
|
||||
/// @param self The RLP item.
|
||||
/// @return 'true' if the item is null.
|
||||
function isEmpty(RLPItem memory self) internal pure returns (bool ret) {
|
||||
if(isNull(self))
|
||||
return false;
|
||||
uint b0;
|
||||
uint memPtr = self._unsafe_memPtr;
|
||||
assembly {
|
||||
b0 := byte(0, mload(memPtr))
|
||||
}
|
||||
return (b0 == DATA_SHORT_START || b0 == LIST_SHORT_START);
|
||||
}
|
||||
|
||||
/// @dev Get the number of items in an RLP encoded list.
|
||||
/// @param self The RLP item.
|
||||
/// @return The number of items.
|
||||
function items(RLPItem memory self) internal pure returns (uint) {
|
||||
if (!isList(self))
|
||||
return 0;
|
||||
uint b0;
|
||||
uint memPtr = self._unsafe_memPtr;
|
||||
assembly {
|
||||
b0 := byte(0, mload(memPtr))
|
||||
}
|
||||
uint pos = memPtr + _payloadOffset(self);
|
||||
uint last = memPtr + self._unsafe_length - 1;
|
||||
uint itms;
|
||||
while(pos <= last) {
|
||||
pos += _itemLength(pos);
|
||||
itms++;
|
||||
}
|
||||
return itms;
|
||||
}
|
||||
|
||||
/// @dev Create an iterator.
|
||||
/// @param self The RLP item.
|
||||
/// @return An 'Iterator' over the item.
|
||||
function iterator(RLPItem memory self) internal pure returns (Iterator memory it) {
|
||||
require(isList(self));
|
||||
uint ptr = self._unsafe_memPtr + _payloadOffset(self);
|
||||
it._unsafe_item = self;
|
||||
it._unsafe_nextPtr = ptr;
|
||||
}
|
||||
|
||||
/// @dev Return the RLP encoded bytes.
|
||||
/// @param self The RLPItem.
|
||||
/// @return The bytes.
|
||||
function toBytes(RLPItem memory self) internal constant returns (bytes memory bts) {
|
||||
var len = self._unsafe_length;
|
||||
if (len == 0)
|
||||
return;
|
||||
bts = new bytes(len);
|
||||
_copyToBytes(self._unsafe_memPtr, bts, len);
|
||||
}
|
||||
|
||||
/// @dev Decode an RLPItem into bytes. This will not work if the
|
||||
/// RLPItem is a list.
|
||||
/// @param self The RLPItem.
|
||||
/// @return The decoded string.
|
||||
function toData(RLPItem memory self) internal constant returns (bytes memory bts) {
|
||||
require(isData(self));
|
||||
var (rStartPos, len) = _decode(self);
|
||||
bts = new bytes(len);
|
||||
_copyToBytes(rStartPos, bts, len);
|
||||
}
|
||||
|
||||
/// @dev Get the list of sub-items from an RLP encoded list.
|
||||
/// Warning: This is inefficient, as it requires that the list is read twice.
|
||||
/// @param self The RLP item.
|
||||
/// @return Array of RLPItems.
|
||||
function toList(RLPItem memory self) internal pure returns (RLPItem[] memory list) {
|
||||
require(isList(self));
|
||||
var numItems = items(self);
|
||||
list = new RLPItem[](numItems);
|
||||
var it = iterator(self);
|
||||
uint idx;
|
||||
while(hasNext(it)) {
|
||||
list[idx] = next(it);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Decode an RLPItem into an ascii string. This will not work if the
|
||||
/// RLPItem is a list.
|
||||
/// @param self The RLPItem.
|
||||
/// @return The decoded string.
|
||||
function toAscii(RLPItem memory self) internal constant returns (string memory str) {
|
||||
require(isData(self));
|
||||
var (rStartPos, len) = _decode(self);
|
||||
bytes memory bts = new bytes(len);
|
||||
_copyToBytes(rStartPos, bts, len);
|
||||
str = string(bts);
|
||||
}
|
||||
|
||||
/// @dev Decode an RLPItem into a uint. This will not work if the
|
||||
/// RLPItem is a list.
|
||||
/// @param self The RLPItem.
|
||||
/// @return The decoded string.
|
||||
function toUint(RLPItem memory self) internal pure returns (uint data) {
|
||||
require(isData(self));
|
||||
var (rStartPos, len) = _decode(self);
|
||||
assert(len <= 32 && len != 0);
|
||||
assembly {
|
||||
data := div(mload(rStartPos), exp(256, sub(32, len)))
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Decode an RLPItem into a boolean. This will not work if the
|
||||
/// RLPItem is a list.
|
||||
/// @param self The RLPItem.
|
||||
/// @return The decoded string.
|
||||
function toBool(RLPItem memory self) internal pure returns (bool data) {
|
||||
require(isData(self));
|
||||
var (rStartPos, len) = _decode(self);
|
||||
assert(len == 1);
|
||||
uint temp;
|
||||
assembly {
|
||||
temp := byte(0, mload(rStartPos))
|
||||
}
|
||||
assert(temp <= 1);
|
||||
return temp == 1 ? true : false;
|
||||
}
|
||||
|
||||
/// @dev Decode an RLPItem into a byte. This will not work if the
|
||||
/// RLPItem is a list.
|
||||
/// @param self The RLPItem.
|
||||
/// @return The decoded string.
|
||||
function toByte(RLPItem memory self) internal pure returns (byte data) {
|
||||
require(isData(self));
|
||||
var (rStartPos, len) = _decode(self);
|
||||
assert(len == 1);
|
||||
uint temp;
|
||||
assembly {
|
||||
temp := byte(0, mload(rStartPos))
|
||||
}
|
||||
return byte(temp);
|
||||
}
|
||||
|
||||
/// @dev Decode an RLPItem into an int. This will not work if the
|
||||
/// RLPItem is a list.
|
||||
/// @param self The RLPItem.
|
||||
/// @return The decoded string.
|
||||
function toInt(RLPItem memory self) internal pure returns (int data) {
|
||||
return int(toUint(self));
|
||||
}
|
||||
|
||||
/// @dev Decode an RLPItem into a bytes32. This will not work if the
|
||||
/// RLPItem is a list.
|
||||
/// @param self The RLPItem.
|
||||
/// @return The decoded string.
|
||||
function toBytes32(RLPItem memory self) internal pure returns (bytes32 data) {
|
||||
return bytes32(toUint(self));
|
||||
}
|
||||
|
||||
/// @dev Decode an RLPItem into an address. This will not work if the
|
||||
/// RLPItem is a list.
|
||||
/// @param self The RLPItem.
|
||||
/// @return The decoded string.
|
||||
function toAddress(RLPItem memory self) internal pure returns (address data) {
|
||||
require(isData(self));
|
||||
var (rStartPos, len) = _decode(self);
|
||||
assert(len == 20);
|
||||
assembly {
|
||||
data := div(mload(rStartPos), exp(256, 12))
|
||||
}
|
||||
}
|
||||
|
||||
// Get the payload offset.
|
||||
function _payloadOffset(RLPItem memory self) private pure returns (uint) {
|
||||
if(self._unsafe_length == 0)
|
||||
return 0;
|
||||
uint b0;
|
||||
uint memPtr = self._unsafe_memPtr;
|
||||
assembly {
|
||||
b0 := byte(0, mload(memPtr))
|
||||
}
|
||||
if(b0 < DATA_SHORT_START)
|
||||
return 0;
|
||||
if(b0 < DATA_LONG_START || (b0 >= LIST_SHORT_START && b0 < LIST_LONG_START))
|
||||
return 1;
|
||||
if(b0 < LIST_SHORT_START)
|
||||
return b0 - DATA_LONG_OFFSET + 1;
|
||||
return b0 - LIST_LONG_OFFSET + 1;
|
||||
}
|
||||
|
||||
// Get the full length of an RLP item.
|
||||
function _itemLength(uint memPtr) private pure returns (uint len) {
|
||||
uint b0;
|
||||
assembly {
|
||||
b0 := byte(0, mload(memPtr))
|
||||
}
|
||||
if (b0 < DATA_SHORT_START)
|
||||
len = 1;
|
||||
else if (b0 < DATA_LONG_START)
|
||||
len = b0 - DATA_SHORT_START + 1;
|
||||
else if (b0 < LIST_SHORT_START) {
|
||||
assembly {
|
||||
let bLen := sub(b0, 0xB7) // bytes length (DATA_LONG_OFFSET)
|
||||
let dLen := div(mload(add(memPtr, 1)), exp(256, sub(32, bLen))) // data length
|
||||
len := add(1, add(bLen, dLen)) // total length
|
||||
}
|
||||
}
|
||||
else if (b0 < LIST_LONG_START)
|
||||
len = b0 - LIST_SHORT_START + 1;
|
||||
else {
|
||||
assembly {
|
||||
let bLen := sub(b0, 0xF7) // bytes length (LIST_LONG_OFFSET)
|
||||
let dLen := div(mload(add(memPtr, 1)), exp(256, sub(32, bLen))) // data length
|
||||
len := add(1, add(bLen, dLen)) // total length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get start position and length of the data.
|
||||
function _decode(RLPItem memory self) private pure returns (uint memPtr, uint len) {
|
||||
require(isData(self));
|
||||
uint b0;
|
||||
uint start = self._unsafe_memPtr;
|
||||
assembly {
|
||||
b0 := byte(0, mload(start))
|
||||
}
|
||||
if (b0 < DATA_SHORT_START) {
|
||||
memPtr = start;
|
||||
len = 1;
|
||||
return;
|
||||
}
|
||||
if (b0 < DATA_LONG_START) {
|
||||
len = self._unsafe_length - 1;
|
||||
memPtr = start + 1;
|
||||
} else {
|
||||
uint bLen;
|
||||
assembly {
|
||||
bLen := sub(b0, 0xB7) // DATA_LONG_OFFSET
|
||||
}
|
||||
len = self._unsafe_length - 1 - bLen;
|
||||
memPtr = start + bLen + 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Assumes that enough memory has been allocated to store in target.
|
||||
function _copyToBytes(uint btsPtr, bytes memory tgt, uint btsLen) private constant {
|
||||
// Exploiting the fact that 'tgt' was the last thing to be allocated,
|
||||
// we can write entire words, and just overwrite any excess.
|
||||
assembly {
|
||||
{
|
||||
let i := 0 // Start at arr + 0x20
|
||||
let words := div(add(btsLen, 31), 32)
|
||||
let rOffset := btsPtr
|
||||
let wOffset := add(tgt, 0x20)
|
||||
tag_loop:
|
||||
jumpi(end, eq(i, words))
|
||||
{
|
||||
let offset := mul(i, 0x20)
|
||||
mstore(add(wOffset, offset), mload(add(rOffset, offset)))
|
||||
i := add(i, 1)
|
||||
}
|
||||
jump(tag_loop)
|
||||
end:
|
||||
mstore(add(tgt, add(0x20, mload(tgt))), 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that an RLP item is valid.
|
||||
function _validate(RLPItem memory self) private pure returns (bool ret) {
|
||||
// Check that RLP is well-formed.
|
||||
uint b0;
|
||||
uint b1;
|
||||
uint memPtr = self._unsafe_memPtr;
|
||||
assembly {
|
||||
b0 := byte(0, mload(memPtr))
|
||||
b1 := byte(1, mload(memPtr))
|
||||
}
|
||||
if(b0 == DATA_SHORT_START + 1 && b1 < DATA_SHORT_START)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,16 @@
|
||||
pragma solidity ^0.4.19;
|
||||
|
||||
import "RLP.sol";
|
||||
|
||||
interface SigHasherContract {
|
||||
// function () returns(bytes32);
|
||||
}
|
||||
|
||||
contract VMC {
|
||||
using RLP for RLP.RLPItem;
|
||||
using RLP for RLP.Iterator;
|
||||
using RLP for bytes;
|
||||
|
||||
event TxToShard(address indexed to, int indexed shardId, int receiptId);
|
||||
event CollationAdded(uint indexed shardId, bytes collationHeader, bool isNewHead, uint score);
|
||||
event Deposit(address validator, int index);
|
||||
event Withdraw(int validatorIndex);
|
||||
|
||||
struct Validator {
|
||||
// Amount of wei the validator holds
|
||||
uint deposit;
|
||||
// The validator's address
|
||||
address addr;
|
||||
// Addess to withdraw to
|
||||
address returnAddr;
|
||||
// The cycle number which the validator would be included after
|
||||
int cycle;
|
||||
}
|
||||
|
||||
struct CollationHeader {
|
||||
@@ -32,9 +23,9 @@ contract VMC {
|
||||
uint txStartgas;
|
||||
uint txGasprice;
|
||||
uint value;
|
||||
bytes32 data;
|
||||
address sender;
|
||||
address to;
|
||||
bytes32 data;
|
||||
}
|
||||
|
||||
mapping (int => Validator) validators;
|
||||
@@ -48,34 +39,22 @@ contract VMC {
|
||||
mapping (int => int) emptySlotsStack;
|
||||
// The top index of the stack in empty_slots_stack
|
||||
int emptySlotsStackTop;
|
||||
// Has the validator deposited before?
|
||||
mapping (address => bool) isValidatorDeposited;
|
||||
|
||||
// Constant values
|
||||
uint constant periodLength = 5;
|
||||
int constant shardCount = 100;
|
||||
// The exact deposit size which you have to deposit to become a validator
|
||||
uint depositSize;
|
||||
// Any given validator randomly gets allocated to some number of shards every SHUFFLING_CYCLE
|
||||
int shufflingCycleLength;
|
||||
// Gas limit of the signature validation code
|
||||
uint sigGasLimit;
|
||||
// Is a valcode addr deposited now?
|
||||
mapping (address => bool) isValcodeDeposited;
|
||||
uint periodLength;
|
||||
int numValidatorsPerCycle;
|
||||
int shardCount;
|
||||
bytes32 addHeaderLogTopic;
|
||||
SigHasherContract sighasher;
|
||||
uint constant depositSize = 100 ether;
|
||||
// Number of periods ahead of current period, which the contract
|
||||
// is able to return the collator of that period
|
||||
uint constant lookAheadPeriods = 4;
|
||||
|
||||
// Log the latest period number of the shard
|
||||
mapping (int => int) periodHead;
|
||||
|
||||
function VMC() public {
|
||||
numValidators = 0;
|
||||
emptySlotsStackTop = 0;
|
||||
depositSize = 100 ether;
|
||||
shufflingCycleLength = 5; // FIXME: just modified temporarily for test;
|
||||
sigGasLimit = 400000;
|
||||
periodLength = 5;
|
||||
numValidatorsPerCycle = 100;
|
||||
shardCount = 100;
|
||||
addHeaderLogTopic = keccak256("add_header()");
|
||||
sighasher = SigHasherContract(0xDFFD41E18F04Ad8810c83B14FD1426a82E625A7D);
|
||||
}
|
||||
|
||||
function isStackEmpty() internal view returns(bool) {
|
||||
@@ -92,230 +71,135 @@ contract VMC {
|
||||
return emptySlotsStack[emptySlotsStackTop];
|
||||
}
|
||||
|
||||
function getValidatorsMaxIndex() public view returns(int) {
|
||||
function getValidatorsMaxIndex() internal view returns(int) {
|
||||
int activateValidatorNum = 0;
|
||||
int currentCycle = int(block.number) / shufflingCycleLength;
|
||||
int allValidatorSlotsNum = numValidators + emptySlotsStackTop;
|
||||
|
||||
// TODO: any better way to iterate the mapping?
|
||||
for (int i = 0; i < 1024; ++i) {
|
||||
if (i >= allValidatorSlotsNum)
|
||||
break;
|
||||
if (validators[i].cycle <= currentCycle)
|
||||
if (validators[i].addr != 0x0)
|
||||
activateValidatorNum += 1;
|
||||
}
|
||||
return activateValidatorNum + emptySlotsStackTop;
|
||||
}
|
||||
|
||||
function deposit(address _returnAddr) public payable returns(int) {
|
||||
require(!isValcodeDeposited[msg.sender]);
|
||||
function deposit() public payable returns(int) {
|
||||
require(!isValidatorDeposited[msg.sender]);
|
||||
require(msg.value == depositSize);
|
||||
// Find the empty slot index in validators set
|
||||
int index;
|
||||
int nextCycle = 0;
|
||||
if (!isStackEmpty())
|
||||
index = stackPop();
|
||||
else {
|
||||
else
|
||||
index = int(numValidators);
|
||||
nextCycle = (int(block.number) / shufflingCycleLength) + 1;
|
||||
validators[index] = Validator({
|
||||
deposit: msg.value,
|
||||
addr: msg.sender,
|
||||
returnAddr: _returnAddr,
|
||||
cycle: nextCycle
|
||||
});
|
||||
}
|
||||
++numValidators;
|
||||
isValcodeDeposited[msg.sender] = true;
|
||||
|
||||
log2(keccak256("deposit()"), bytes32(msg.sender), bytes32(index));
|
||||
return index;
|
||||
|
||||
validators[index] = Validator({
|
||||
deposit: msg.value,
|
||||
addr: msg.sender
|
||||
});
|
||||
++numValidators;
|
||||
isValidatorDeposited[msg.sender] = true;
|
||||
|
||||
Deposit(msg.sender, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
function withdraw(int _validatorIndex) public {
|
||||
var msgHash = keccak256("withdraw");
|
||||
require(msg.sender == validators[_validatorIndex].addr);
|
||||
validators[_validatorIndex].returnAddr.transfer(validators[_validatorIndex].deposit);
|
||||
isValcodeDeposited[validators[_validatorIndex].addr] = false;
|
||||
// [FIXME] Should consider calling the validator's contract, might be useful
|
||||
// when the validator is a contract.
|
||||
validators[_validatorIndex].addr.transfer(validators[_validatorIndex].deposit);
|
||||
isValidatorDeposited[validators[_validatorIndex].addr] = false;
|
||||
delete validators[_validatorIndex];
|
||||
stackPush(_validatorIndex);
|
||||
--numValidators;
|
||||
log1(msgHash, bytes32(_validatorIndex));
|
||||
Withdraw(_validatorIndex);
|
||||
}
|
||||
|
||||
function sample(int _shardId) public constant returns(address) {
|
||||
require(block.number >= periodLength);
|
||||
var cycle = int(block.number) / shufflingCycleLength;
|
||||
int cycleStartBlockNumber = cycle * shufflingCycleLength - 1;
|
||||
if (cycleStartBlockNumber < 0)
|
||||
cycleStartBlockNumber = 0;
|
||||
int cycleSeed = int(block.blockhash(uint(cycleStartBlockNumber)));
|
||||
// originally, error occurs when block.number <= 4 because
|
||||
// `seed_block_number` becomes negative in these cases.
|
||||
int seed = int(block.blockhash(block.number - (block.number % uint(periodLength)) - 1));
|
||||
|
||||
uint indexInSubset = uint(keccak256(seed, bytes32(_shardId))) % uint(numValidatorsPerCycle);
|
||||
uint validatorIndex = uint(keccak256(cycleSeed, bytes32(_shardId), bytes32(indexInSubset))) % uint(getValidatorsMaxIndex());
|
||||
|
||||
if (validators[int(validatorIndex)].cycle > cycle)
|
||||
return 0x0;
|
||||
else
|
||||
return validators[int(validatorIndex)].addr;
|
||||
// Uses a block hash as a seed to pseudorandomly select a signer from the validator set.
|
||||
// [TODO] Chance of being selected should be proportional to the validator's deposit.
|
||||
// Should be able to return a value for the current period or any future period up to.
|
||||
function getEligibleProposer(int _shardId, uint _period) public view returns(address) {
|
||||
require(_period >= lookAheadPeriods);
|
||||
require((_period - lookAheadPeriods) * periodLength < block.number);
|
||||
require(numValidators > 0);
|
||||
// [TODO] Should check further if this safe or not
|
||||
return validators[
|
||||
int(
|
||||
uint(keccak256(uint(block.blockhash(_period - lookAheadPeriods)) * periodLength, _shardId))
|
||||
%
|
||||
uint(getValidatorsMaxIndex())
|
||||
)].addr;
|
||||
}
|
||||
|
||||
// Get all possible shard ids that the given _valcodeAddr
|
||||
// may be sampled in the current cycle
|
||||
function getShardList(address _validatorAddr) public constant returns(bool[100]) {
|
||||
bool[100] memory shardList;
|
||||
int cycle = int(block.number) / shufflingCycleLength;
|
||||
int cycleStartBlockNumber = cycle * shufflingCycleLength - 1;
|
||||
if (cycleStartBlockNumber < 0)
|
||||
cycleStartBlockNumber = 0;
|
||||
|
||||
var cycleSeed = block.blockhash(uint(cycleStartBlockNumber));
|
||||
int validatorsMaxIndex = getValidatorsMaxIndex();
|
||||
if (numValidators != 0) {
|
||||
for (uint8 shardId = 0; shardId < 100; ++shardId) {
|
||||
shardList[shardId] = false;
|
||||
for (uint8 possibleIndexInSubset = 0; possibleIndexInSubset < 100; ++possibleIndexInSubset) {
|
||||
uint validatorIndex = uint(keccak256(cycleSeed, bytes32(shardId), bytes32(possibleIndexInSubset)))
|
||||
% uint(validatorsMaxIndex);
|
||||
if (_validatorAddr == validators[int(validatorIndex)].addr) {
|
||||
shardList[shardId] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return shardList;
|
||||
struct HeaderVars {
|
||||
bytes32 entireHeaderHash;
|
||||
int score;
|
||||
address validatorAddr;
|
||||
bool isNewHead;
|
||||
}
|
||||
|
||||
// function checkHeader(int _shardId, bytes32 _periodStartPrevhash, int _expectedPeriodNumber) internal {
|
||||
// // Check if the header is valid
|
||||
// assert(_shardId >= 0 && _shardId < shardCount);
|
||||
// assert(block.number >= periodLength);
|
||||
// assert(uint(_expectedPeriodNumber) == block.number / periodLength);
|
||||
// assert(_periodStartPrevhash == block.blockhash(uint(_expectedPeriodNumber)*periodLength - 1));
|
||||
|
||||
// // Check if this header already exists
|
||||
// var entireHeaderHash = keccak256(_header);
|
||||
// assert(entireHeaderHash != bytes32(0));
|
||||
// assert(collationHeaders[shardId][entireHeaderHash].score == 0);
|
||||
// }
|
||||
|
||||
struct Header {
|
||||
int shardId;
|
||||
uint expectedPeriodNumber;
|
||||
bytes32 periodStartPrevhash;
|
||||
bytes32 parentCollationHash;
|
||||
bytes32 txListRoot;
|
||||
address collationCoinbase;
|
||||
bytes32 postStateRoot;
|
||||
bytes32 receiptRoot;
|
||||
int collationNumber;
|
||||
bytes sig;
|
||||
}
|
||||
|
||||
function addHeader(bytes _header) public returns(bool) {
|
||||
// require(_header.length <= 4096);
|
||||
// TODO
|
||||
// values = RLPList(header, [num, num, bytes32, bytes32, bytes32, address, bytes32, bytes32, num, bytes])
|
||||
// return True
|
||||
bytes memory mHeader = _header;
|
||||
var RLPList = mHeader.toRLPItem(true).iterator();
|
||||
var header = Header({
|
||||
shardId: RLPList.next().toInt(),
|
||||
expectedPeriodNumber: RLPList.next().toUint(),
|
||||
periodStartPrevhash: RLPList.next().toBytes32(),
|
||||
parentCollationHash: RLPList.next().toBytes32(),
|
||||
txListRoot: RLPList.next().toBytes32(),
|
||||
collationCoinbase: RLPList.next().toAddress(),
|
||||
postStateRoot: RLPList.next().toBytes32(),
|
||||
receiptRoot: RLPList.next().toBytes32(),
|
||||
collationNumber: RLPList.next().toInt(),
|
||||
sig: RLPList.next().toBytes()
|
||||
});
|
||||
function addHeader(int _shardId, uint _expectedPeriodNumber, bytes32 _periodStartPrevHash,
|
||||
bytes32 _parentCollationHash, bytes32 _txListRoot, address _collationCoinbase,
|
||||
bytes32 _postStateRoot, bytes32 _receiptRoot, int _collationNumber) public returns(bool) {
|
||||
HeaderVars memory headerVars;
|
||||
|
||||
// Check if the header is valid
|
||||
require((header.shardId >= 0) && (header.shardId < shardCount));
|
||||
require((_shardId >= 0) && (_shardId < shardCount));
|
||||
require(block.number >= periodLength);
|
||||
require(header.expectedPeriodNumber == (block.number / periodLength));
|
||||
require(header.periodStartPrevhash == block.blockhash(header.expectedPeriodNumber * periodLength - 1));
|
||||
|
||||
require(_expectedPeriodNumber == block.number / periodLength);
|
||||
require(_periodStartPrevHash == block.blockhash(_expectedPeriodNumber * periodLength - 1));
|
||||
|
||||
// Check if this header already exists
|
||||
var entireHeaderHash = keccak256(header);
|
||||
assert(entireHeaderHash != 0x0);
|
||||
assert(collationHeaders[header.shardId][entireHeaderHash].score == 0);
|
||||
headerVars.entireHeaderHash = keccak256(_shardId, _expectedPeriodNumber, _periodStartPrevHash,
|
||||
_parentCollationHash, _txListRoot, bytes32(_collationCoinbase),
|
||||
_postStateRoot, _receiptRoot, _collationNumber);
|
||||
assert(headerVars.entireHeaderHash != 0x0);
|
||||
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 (header.parentCollationHash != 0x0)
|
||||
assert ((header.parentCollationHash == 0x0) || (collationHeaders[header.shardId][header.parentCollationHash].score > 0));
|
||||
// Check if only one colllation in one period
|
||||
assert (periodHead[header.shardId] < int(header.expectedPeriodNumber));
|
||||
if (_parentCollationHash != 0x0)
|
||||
assert((_parentCollationHash == 0x0) || (collationHeaders[_shardId][_parentCollationHash].score > 0));
|
||||
// Check if only one collation in one period
|
||||
assert(periodHead[_shardId] < int(_expectedPeriodNumber));
|
||||
|
||||
// Check the signature with validation_code_addr
|
||||
var collatorValcodeAddr = sample(header.shardId);
|
||||
if (collatorValcodeAddr == 0x0)
|
||||
return false;
|
||||
|
||||
// assembly {
|
||||
// TODO next block
|
||||
// }
|
||||
// sighash = extract32(raw_call(self.sighasher_addr, header, gas=200000, outsize=32), 0)
|
||||
// assert extract32(raw_call(collator_valcode_addr, concat(sighash, sig), gas=self.sig_gas_limit, outsize=32), 0) == as_bytes32(1)
|
||||
|
||||
// Check score == collation_number
|
||||
var _score = collationHeaders[header.shardId][header.parentCollationHash].score + 1;
|
||||
assert(header.collationNumber == _score);
|
||||
headerVars.validatorAddr = getEligibleProposer(_shardId, block.number/periodLength);
|
||||
require(headerVars.validatorAddr != 0x0);
|
||||
require(msg.sender == headerVars.validatorAddr);
|
||||
|
||||
// Check score == collationNumber
|
||||
headerVars.score = collationHeaders[_shardId][_parentCollationHash].score + 1;
|
||||
require(_collationNumber == headerVars.score);
|
||||
|
||||
// Add the header
|
||||
collationHeaders[header.shardId][entireHeaderHash] = CollationHeader({
|
||||
parentCollationHash: header.parentCollationHash,
|
||||
score: _score
|
||||
collationHeaders[_shardId][headerVars.entireHeaderHash] = CollationHeader({
|
||||
parentCollationHash: _parentCollationHash,
|
||||
score: headerVars.score
|
||||
});
|
||||
|
||||
// Update the latest period number
|
||||
periodHead[header.shardId] = int(header.expectedPeriodNumber);
|
||||
periodHead[_shardId] = int(_expectedPeriodNumber);
|
||||
|
||||
// Determine the head
|
||||
if (_score > collationHeaders[header.shardId][shardHead[header.shardId]].score) {
|
||||
var previousHeadHash = shardHead[header.shardId];
|
||||
shardHead[header.shardId] = entireHeaderHash;
|
||||
// Logs only when `change_head` happens due to the fork
|
||||
// TODO LOG
|
||||
// log1([addHeaderLogTopic, keccak256("change_head"), entireHeaderHash], previousHeadHash);
|
||||
if (headerVars.score > collationHeaders[_shardId][shardHead[_shardId]].score) {
|
||||
shardHead[_shardId] = headerVars.entireHeaderHash;
|
||||
headerVars.isNewHead = true;
|
||||
}
|
||||
// Emit log
|
||||
// TODO LOG
|
||||
// log1(addHeaderLogTopic, _header);
|
||||
|
||||
// [TODO] Log
|
||||
//CollationAdded(headerBytes, isNewHead, _score);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getPeriodStartPrevhash(uint _expectedPeriodNumber) public constant returns(bytes32) {
|
||||
uint blockNumber = _expectedPeriodNumber * periodLength - 1;
|
||||
require(block.number > blockNumber);
|
||||
return block.blockhash(blockNumber);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Returns the difference between the block number of this hash and the block
|
||||
// number of the 10000th ancestor of this hash.
|
||||
function getAncestorDistance(bytes32 /*_hash*/) public pure returns(bytes32) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// Returns the gas limit that collations can currently have (by default make
|
||||
// this function always answer 10 million).
|
||||
function getCollationGasLimit() public pure returns(uint) {
|
||||
return 10000000;
|
||||
}
|
||||
|
||||
|
||||
// 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`,
|
||||
@@ -333,7 +217,7 @@ contract VMC {
|
||||
var receiptId = numReceipts;
|
||||
++numReceipts;
|
||||
|
||||
log3(keccak256("tx_to_shard()"), bytes32(_to), bytes32(_shardId), bytes32(receiptId));
|
||||
TxToShard(_to, _shardId, receiptId);
|
||||
return receiptId;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user