diff --git a/sharding/contracts/RLP.sol b/sharding/contracts/RLP.sol deleted file mode 100644 index 7397026432..0000000000 --- a/sharding/contracts/RLP.sol +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/sharding/contracts/validator_manager.sol b/sharding/contracts/validator_manager.sol index 5aa53302af..659a8e5a52 100644 --- a/sharding/contracts/validator_manager.sol +++ b/sharding/contracts/validator_manager.sol @@ -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; }