From 4c2180be1d87303a4d88519bbb440a3465a580a4 Mon Sep 17 00:00:00 2001 From: Fynn Date: Mon, 15 Jan 2018 23:01:24 +0100 Subject: [PATCH 01/17] WIP: Validator manager contract in solidity Former-commit-id: 98483e7425feed46ae2a946e7d86461cbcf28d21 [formerly 334390368a91c34c1d66b0e262fcaef293cd6409] Former-commit-id: 7f5f7dc5385c8cef6ecea39040f915554472a736 --- sharding/contracts/validator_manager.sol | 230 +++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 sharding/contracts/validator_manager.sol diff --git a/sharding/contracts/validator_manager.sol b/sharding/contracts/validator_manager.sol new file mode 100644 index 0000000000..50a1e56947 --- /dev/null +++ b/sharding/contracts/validator_manager.sol @@ -0,0 +1,230 @@ +pragma solidity ^0.4.19; + +contract VMC { + struct Validator { + // Amount of wei the validator holds + uint deposit; + // The address which the validator's signatures must verify to (to be later replaced with validation code) + address validationCodeAddr; + // Addess to withdraw to + address returnAddr; + // The cycle number which the validator would be included after + int cycle; + } + + struct CollationHeader { + bytes32 parentCollationHash; + int score; + } + + struct Receipt { + int shardId; + uint txStartgas; + uint txGasprice; + uint value; + address sender; + address to; + bytes12 data; + } + + mapping (int => Validator) validators; + mapping (int => mapping (bytes32 => CollationHeader)) collationHeaders; + mapping (int => Receipt) receipts; + + mapping (int => bytes32) shardHead; + int numValidators; + int numReceipts; + // Indexs of empty slots caused by the function `withdraw` + mapping (int => int) emptySlotsStack; + // The top index of the stack in empty_slots_stack + int emptySlotsStackTop; + + // 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; + address sighasherAddr; + // 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()"); + sighasherAddr = 0xDFFD41E18F04Ad8810c83B14FD1426a82E625A7D; + } + + 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()) + return -1; + --emptySlotsStackTop; + return emptySlotsStack[emptySlotsStackTop]; + } + + function getValidatorsMaxIndex() public view returns(int) { + address zeroAddr = 0x0; + 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].validationCodeAddr != zeroAddr) && + (validators[i].cycle <= currentCycle)) + activateValidatorNum += 1; + } + return activateValidatorNum + emptySlotsStackTop; + } + + function deposit(address _validationCodeAddr, address _returnAddr) public payable returns(int) { + require(!isValcodeDeposited[_validationCodeAddr]); + require(msg.value == depositSize); + // Find the empty slot index in validators set + int index; + int nextCycle = 0; + if (!isStackEmpty()) + index = stackPop(); + else { + index = int(numValidators); + nextCycle = (int(block.number) / shufflingCycleLength) + 1; + validators[index] = Validator({ + deposit: msg.value, + validationCodeAddr: _validationCodeAddr, + returnAddr: _returnAddr, + cycle: nextCycle + }); + } + ++numValidators; + isValcodeDeposited[_validationCodeAddr] = true; + + log2(keccak256("deposit()"), bytes32(_validationCodeAddr), bytes32(index)); + return index; + } + + function withdraw(uint _validatorIndex, bytes10 _sig) public returns(bool) { + // TODO + } + + 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)].validationCodeAddr; + } + + // Get all possible shard ids that the given valcode_addr + // may be sampled in the current cycle + function getShardList(address _valcodeAddr) 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 (int possibleIndexInSubset = 0; possibleIndexInSubset < 100; ++possibleIndexInSubset) { + uint validatorIndex = uint(keccak256(cycleSeed, bytes32(shardId), bytes32(possibleIndexInSubset))) + % uint(validatorsMaxIndex); + if (_valcodeAddr == validators[int(validatorIndex)].validationCodeAddr) { + shardList[shardId] = true; + break; + } + } + } + } + return shardList; + } + + + function addHeader(bytes12 header) public returns(bool) { + // TODO + } + + 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`, + // `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; + + log3(keccak256("tx_to_shard()"), bytes32(_to), bytes32(_shardId), bytes32(receiptId)); + return receiptId; + } + + function updataGasPrice(int _receiptId, uint _txGasprice) public payable returns(bool) { + require(receipts[_receiptId].sender == msg.sender); + receipts[_receiptId].txGasprice = _txGasprice; + return true; + } +} From 785d99de0220598e551154acd506438476bdaa3a Mon Sep 17 00:00:00 2001 From: Fynn Date: Wed, 17 Jan 2018 17:21:54 +0100 Subject: [PATCH 02/17] RLP implementation Former-commit-id: 1a6144cb6f46b16281b79cb999acd838a01cd110 [formerly 42f939e8c110dd9982dbb7db25ef77d64219f2c2] Former-commit-id: a0ebe69fd061ef85e26f87c02f7e1d4029d79133 --- sharding/contracts/RLP.sol | 398 +++++++++++++++++++++++++++++++++++++ 1 file changed, 398 insertions(+) create mode 100644 sharding/contracts/RLP.sol diff --git a/sharding/contracts/RLP.sol b/sharding/contracts/RLP.sol new file mode 100644 index 0000000000..7397026432 --- /dev/null +++ b/sharding/contracts/RLP.sol @@ -0,0 +1,398 @@ +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 From a3eb8fb155b2a6355b0d02c4314049e6c32fb9cf Mon Sep 17 00:00:00 2001 From: Fynn Date: Wed, 17 Jan 2018 17:22:15 +0100 Subject: [PATCH 03/17] withdraw implemented Former-commit-id: f4ec0e7067ecc9406e7b3ef56659d5924b547c87 [formerly 6720442b533ed1bc406087452e5328363f1f01b9] Former-commit-id: e2108b302ee39b6882c3de60b3ef19449c91e32b --- sharding/contracts/validator_manager.sol | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/sharding/contracts/validator_manager.sol b/sharding/contracts/validator_manager.sol index 50a1e56947..b7517c63df 100644 --- a/sharding/contracts/validator_manager.sol +++ b/sharding/contracts/validator_manager.sol @@ -1,6 +1,12 @@ pragma solidity ^0.4.19; +import "RLP.sol"; + contract VMC { + using RLP for RLP.RLPItem; + using RLP for RLP.Iterator; + using RLP for bytes; + struct Validator { // Amount of wei the validator holds uint deposit; @@ -124,8 +130,18 @@ contract VMC { return index; } - function withdraw(uint _validatorIndex, bytes10 _sig) public returns(bool) { - // TODO + function withdraw(int _validatorIndex, bytes10 _sig) public returns(bool) { + var msgHash = keccak256("withdraw"); + var result = validators[_validatorIndex].validationCodeAddr.call.gas(sigGasLimit)(msgHash, _sig) == true; + if (result) { + validators[_validatorIndex].returnAddr.transfer(validators[_validatorIndex].deposit); + isValcodeDeposited[validators[_validatorIndex].validationCodeAddr] = false; + delete validators[_validatorIndex]; + stackPush(_validatorIndex); + --numValidators; + log1(msgHash, bytes32(_validatorIndex)); + return result; + } } function sample(int _shardId) public constant returns(address) { @@ -175,8 +191,8 @@ contract VMC { return shardList; } - - function addHeader(bytes12 header) public returns(bool) { + + function addHeader(bytes _header) public returns(bool) { // TODO } From 435901aa74d87a376d769f2168d86ec37f7d3ccd Mon Sep 17 00:00:00 2001 From: Fynn Date: Sun, 21 Jan 2018 11:10:27 +0100 Subject: [PATCH 04/17] Better abstraction to Validator Address Former-commit-id: e49c7dfc3769f960337c4a5085b34da5c0b29cea [formerly 32bf6a45f7a21be714c4c44d9d954a878b0e22f5] Former-commit-id: 57a34ee1f3269dd764fbff18b4af3cba5e85faf7 --- sharding/contracts/validator_manager.sol | 75 ++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/sharding/contracts/validator_manager.sol b/sharding/contracts/validator_manager.sol index b7517c63df..83225b6746 100644 --- a/sharding/contracts/validator_manager.sol +++ b/sharding/contracts/validator_manager.sol @@ -2,6 +2,10 @@ pragma solidity ^0.4.19; import "RLP.sol"; +interface ValidatorContract { + function withdraw(bytes10 _sig) returns(bool); +} + contract VMC { using RLP for RLP.RLPItem; using RLP for RLP.Iterator; @@ -11,7 +15,7 @@ contract VMC { // Amount of wei the validator holds uint deposit; // The address which the validator's signatures must verify to (to be later replaced with validation code) - address validationCodeAddr; + ValidatorContract validationCodeAddr; // Addess to withdraw to address returnAddr; // The cycle number which the validator would be included after @@ -105,7 +109,7 @@ contract VMC { return activateValidatorNum + emptySlotsStackTop; } - function deposit(address _validationCodeAddr, address _returnAddr) public payable returns(int) { + function deposit(ValidatorContract _validationCodeAddr, address _returnAddr) public payable returns(int) { require(!isValcodeDeposited[_validationCodeAddr]); require(msg.value == depositSize); // Find the empty slot index in validators set @@ -126,13 +130,14 @@ contract VMC { ++numValidators; isValcodeDeposited[_validationCodeAddr] = true; - log2(keccak256("deposit()"), bytes32(_validationCodeAddr), bytes32(index)); + log2(keccak256("deposit()"), bytes32(address(_validationCodeAddr)), bytes32(index)); return index; } function withdraw(int _validatorIndex, bytes10 _sig) public returns(bool) { var msgHash = keccak256("withdraw"); - var result = validators[_validatorIndex].validationCodeAddr.call.gas(sigGasLimit)(msgHash, _sig) == true; + // TODO: Check this function + var result = validators[_validatorIndex].validationCodeAddr.withdraw.gas(sigGasLimit)(_sig) == true; if (result) { validators[_validatorIndex].returnAddr.transfer(validators[_validatorIndex].deposit); isValcodeDeposited[validators[_validatorIndex].validationCodeAddr] = false; @@ -166,7 +171,7 @@ contract VMC { // Get all possible shard ids that the given valcode_addr // may be sampled in the current cycle - function getShardList(address _valcodeAddr) public constant returns(bool[100]) { + function getShardList(ValidatorContract _valcodeAddr) public constant returns(bool[100]) { bool[100] memory shardList; int cycle = int(block.number) / shufflingCycleLength; int cycleStartBlockNumber = cycle * shufflingCycleLength - 1; @@ -191,9 +196,67 @@ contract VMC { return shardList; } - + // 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); + // } + function addHeader(bytes _header) public returns(bool) { + address zeroAddr = 0x0; + // 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(); + int shardId = RLPList.next().toInt(); + int expectedPeriodNumber = RLPList.next().toInt(); + bytes32 periodStartPrevhash = RLPList.next().toBytes32(); + bytes32 parentCollationHash = RLPList.next().toBytes32(); + bytes32 txListRoot = RLPList.next().toBytes32(); + address collationCoinbase = RLPList.next().toAddress(); + bytes32 postStateRoot = RLPList.next().toBytes32(); + bytes32 receiptRoot = RLPList.next().toBytes32(); + int collationNumber = RLPList.next().toInt(); + bytes memory sig = RLPList.next().toBytes(); + + // Check whether the parent exists. + // if (parent_collation_hash == 0), i.e., is the genesis, + // then there is no need to check. + if (parentCollationHash != bytes32(0)) + assert(parentCollationHash == bytes32(0) || collationHeaders[shardId][parentCollationHash].score > 0); + // Check if only one colllation in one period + assert(periodHead[shardId] < expectedPeriodNumber); + + // Check the signature with validation_code_addr + var collatorValcodeAddr = sample(shardId); + if (collatorValcodeAddr == zeroAddr) { + return false; + } + + + // var sighash = sighasherAddr.call.gas(20000)(_header); + // var collatorCall = collatorValcodeAddr.call.gas(sigGasLimit)(); + + // assert(collatorCall == ) + // if collator_valcode_addr == zero_addr: + // return False + // 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[shardId][parentCollationHash].score + 1; + // assert(collationNumber == score); + } function getPeriodStartPrevhash(uint _expectedPeriodNumber) public constant returns(bytes32) { From 5e77438b149202323b4f24146614a6e4895b8bab Mon Sep 17 00:00:00 2001 From: Fynn Date: Thu, 25 Jan 2018 16:55:46 +0100 Subject: [PATCH 05/17] addHeader almost done Former-commit-id: a2e45cb91aec036dd9967db19eb887104371b8ad [formerly 6010ac83b5383d86861ff660ddb2dfae03a50816] Former-commit-id: 737c461344fc027f6b147e923d0757fc636caad6 --- sharding/contracts/validator_manager.sol | 119 ++++++++++++++++------- 1 file changed, 83 insertions(+), 36 deletions(-) diff --git a/sharding/contracts/validator_manager.sol b/sharding/contracts/validator_manager.sol index 83225b6746..7fc17672e5 100644 --- a/sharding/contracts/validator_manager.sol +++ b/sharding/contracts/validator_manager.sol @@ -3,7 +3,11 @@ pragma solidity ^0.4.19; import "RLP.sol"; interface ValidatorContract { - function withdraw(bytes10 _sig) returns(bool); + function withdraw(bytes32 _sig) returns(bool); +} + +interface SigHasherContract { + // function () returns(bytes32); } contract VMC { @@ -34,7 +38,7 @@ contract VMC { uint value; address sender; address to; - bytes12 data; + bytes32 data; } mapping (int => Validator) validators; @@ -61,7 +65,7 @@ contract VMC { int numValidatorsPerCycle; int shardCount; bytes32 addHeaderLogTopic; - address sighasherAddr; + SigHasherContract sighasher; // Log the latest period number of the shard mapping (int => int) periodHead; @@ -75,7 +79,7 @@ contract VMC { numValidatorsPerCycle = 100; shardCount = 100; addHeaderLogTopic = keccak256("add_header()"); - sighasherAddr = 0xDFFD41E18F04Ad8810c83B14FD1426a82E625A7D; + sighasher = SigHasherContract(0xDFFD41E18F04Ad8810c83B14FD1426a82E625A7D); } function isStackEmpty() internal view returns(bool) { @@ -134,10 +138,10 @@ contract VMC { return index; } - function withdraw(int _validatorIndex, bytes10 _sig) public returns(bool) { + function withdraw(int _validatorIndex, bytes32 _sig) public returns(bool) { var msgHash = keccak256("withdraw"); // TODO: Check this function - var result = validators[_validatorIndex].validationCodeAddr.withdraw.gas(sigGasLimit)(_sig) == true; + var result = validators[_validatorIndex].validationCodeAddr.withdraw.gas(sigGasLimit)(_sig); if (result) { validators[_validatorIndex].returnAddr.transfer(validators[_validatorIndex].deposit); isValcodeDeposited[validators[_validatorIndex].validationCodeAddr] = false; @@ -169,7 +173,7 @@ contract VMC { return validators[int(validatorIndex)].validationCodeAddr; } - // Get all possible shard ids that the given valcode_addr + // Get all possible shard ids that the given _valcodeAddr // may be sampled in the current cycle function getShardList(ValidatorContract _valcodeAddr) public constant returns(bool[100]) { bool[100] memory shardList; @@ -183,7 +187,7 @@ contract VMC { if (numValidators != 0) { for (uint8 shardId = 0; shardId < 100; ++shardId) { shardList[shardId] = false; - for (int possibleIndexInSubset = 0; possibleIndexInSubset < 100; ++possibleIndexInSubset) { + for (uint8 possibleIndexInSubset = 0; possibleIndexInSubset < 100; ++possibleIndexInSubset) { uint validatorIndex = uint(keccak256(cycleSeed, bytes32(shardId), bytes32(possibleIndexInSubset))) % uint(validatorsMaxIndex); if (_valcodeAddr == validators[int(validatorIndex)].validationCodeAddr) { @@ -209,6 +213,19 @@ contract VMC { // 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) { address zeroAddr = 0x0; // require(_header.length <= 4096); @@ -217,46 +234,76 @@ contract VMC { // return True bytes memory mHeader = _header; var RLPList = mHeader.toRLPItem(true).iterator(); - int shardId = RLPList.next().toInt(); - int expectedPeriodNumber = RLPList.next().toInt(); - bytes32 periodStartPrevhash = RLPList.next().toBytes32(); - bytes32 parentCollationHash = RLPList.next().toBytes32(); - bytes32 txListRoot = RLPList.next().toBytes32(); - address collationCoinbase = RLPList.next().toAddress(); - bytes32 postStateRoot = RLPList.next().toBytes32(); - bytes32 receiptRoot = RLPList.next().toBytes32(); - int collationNumber = RLPList.next().toInt(); - bytes memory sig = RLPList.next().toBytes(); - + 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() + }); + + // Check if the header is valid + require((header.shardId >= 0) && (header.shardId < shardCount)); + require(block.number >= periodLength); + require(header.expectedPeriodNumber == (block.number / periodLength)); + require(header.periodStartPrevhash == block.blockhash(header.expectedPeriodNumber * periodLength - 1)); + + + // Check if this header already exists + var entireHeaderHash = keccak256(header); + assert(entireHeaderHash != 0x0); + assert(collationHeaders[header.shardId][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 (parentCollationHash != bytes32(0)) - assert(parentCollationHash == bytes32(0) || collationHeaders[shardId][parentCollationHash].score > 0); + 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[shardId] < expectedPeriodNumber); + assert (periodHead[header.shardId] < int(header.expectedPeriodNumber)); // Check the signature with validation_code_addr - var collatorValcodeAddr = sample(shardId); - if (collatorValcodeAddr == zeroAddr) { - return false; - } - + var collatorValcodeAddr = sample(header.shardId); + if (collatorValcodeAddr == 0x0) + return false; - // var sighash = sighasherAddr.call.gas(20000)(_header); - // var collatorCall = collatorValcodeAddr.call.gas(sigGasLimit)(); - - // assert(collatorCall == ) - // if collator_valcode_addr == zero_addr: - // 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[shardId][parentCollationHash].score + 1; - // assert(collationNumber == score); + var _score = collationHeaders[header.shardId][header.parentCollationHash].score + 1; + assert(header.collationNumber == _score); + + // Add the header + collationHeaders[header.shardId][entireHeaderHash] = CollationHeader({ + parentCollationHash: header.parentCollationHash, + score: _score + }); + + // Update the latest period number + periodHead[header.shardId] = int(header.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); + } + // Emit log + // TODO LOG + // log1(addHeaderLogTopic, _header); + + return true; } function getPeriodStartPrevhash(uint _expectedPeriodNumber) public constant returns(bytes32) { From b68db42854bbb778e37c7322bf9b31db81014ffb Mon Sep 17 00:00:00 2001 From: Fynn Date: Mon, 29 Jan 2018 17:43:45 +0100 Subject: [PATCH 06/17] Simplify validator's logic Remove validator's code and use the transaction's signature to verify the validator's identity Former-commit-id: 4c30667db429a46986dd82a0ac80dbe0d64bd8be [formerly fae9ab5be5e78493a497543512a42368f52f8de1] Former-commit-id: a8d78246d61b62d936257d2f48a8ea77c4c06792 --- sharding/contracts/validator_manager.sol | 49 +++++++++--------------- 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/sharding/contracts/validator_manager.sol b/sharding/contracts/validator_manager.sol index 7fc17672e5..5aa53302af 100644 --- a/sharding/contracts/validator_manager.sol +++ b/sharding/contracts/validator_manager.sol @@ -2,10 +2,6 @@ pragma solidity ^0.4.19; import "RLP.sol"; -interface ValidatorContract { - function withdraw(bytes32 _sig) returns(bool); -} - interface SigHasherContract { // function () returns(bytes32); } @@ -18,8 +14,8 @@ contract VMC { struct Validator { // Amount of wei the validator holds uint deposit; - // The address which the validator's signatures must verify to (to be later replaced with validation code) - ValidatorContract validationCodeAddr; + // The validator's address + address addr; // Addess to withdraw to address returnAddr; // The cycle number which the validator would be included after @@ -97,7 +93,6 @@ contract VMC { } function getValidatorsMaxIndex() public view returns(int) { - address zeroAddr = 0x0; int activateValidatorNum = 0; int currentCycle = int(block.number) / shufflingCycleLength; int allValidatorSlotsNum = numValidators + emptySlotsStackTop; @@ -106,15 +101,14 @@ contract VMC { for (int i = 0; i < 1024; ++i) { if (i >= allValidatorSlotsNum) break; - if ((validators[i].validationCodeAddr != zeroAddr) && - (validators[i].cycle <= currentCycle)) + if (validators[i].cycle <= currentCycle) activateValidatorNum += 1; } return activateValidatorNum + emptySlotsStackTop; } - function deposit(ValidatorContract _validationCodeAddr, address _returnAddr) public payable returns(int) { - require(!isValcodeDeposited[_validationCodeAddr]); + function deposit(address _returnAddr) public payable returns(int) { + require(!isValcodeDeposited[msg.sender]); require(msg.value == depositSize); // Find the empty slot index in validators set int index; @@ -126,31 +120,27 @@ contract VMC { nextCycle = (int(block.number) / shufflingCycleLength) + 1; validators[index] = Validator({ deposit: msg.value, - validationCodeAddr: _validationCodeAddr, + addr: msg.sender, returnAddr: _returnAddr, cycle: nextCycle }); } ++numValidators; - isValcodeDeposited[_validationCodeAddr] = true; + isValcodeDeposited[msg.sender] = true; - log2(keccak256("deposit()"), bytes32(address(_validationCodeAddr)), bytes32(index)); + log2(keccak256("deposit()"), bytes32(msg.sender), bytes32(index)); return index; } - function withdraw(int _validatorIndex, bytes32 _sig) public returns(bool) { + function withdraw(int _validatorIndex) public { var msgHash = keccak256("withdraw"); - // TODO: Check this function - var result = validators[_validatorIndex].validationCodeAddr.withdraw.gas(sigGasLimit)(_sig); - if (result) { - validators[_validatorIndex].returnAddr.transfer(validators[_validatorIndex].deposit); - isValcodeDeposited[validators[_validatorIndex].validationCodeAddr] = false; - delete validators[_validatorIndex]; - stackPush(_validatorIndex); - --numValidators; - log1(msgHash, bytes32(_validatorIndex)); - return result; - } + require(msg.sender == validators[_validatorIndex].addr); + validators[_validatorIndex].returnAddr.transfer(validators[_validatorIndex].deposit); + isValcodeDeposited[validators[_validatorIndex].addr] = false; + delete validators[_validatorIndex]; + stackPush(_validatorIndex); + --numValidators; + log1(msgHash, bytes32(_validatorIndex)); } function sample(int _shardId) public constant returns(address) { @@ -170,12 +160,12 @@ contract VMC { if (validators[int(validatorIndex)].cycle > cycle) return 0x0; else - return validators[int(validatorIndex)].validationCodeAddr; + return validators[int(validatorIndex)].addr; } // Get all possible shard ids that the given _valcodeAddr // may be sampled in the current cycle - function getShardList(ValidatorContract _valcodeAddr) public constant returns(bool[100]) { + function getShardList(address _validatorAddr) public constant returns(bool[100]) { bool[100] memory shardList; int cycle = int(block.number) / shufflingCycleLength; int cycleStartBlockNumber = cycle * shufflingCycleLength - 1; @@ -190,7 +180,7 @@ contract VMC { for (uint8 possibleIndexInSubset = 0; possibleIndexInSubset < 100; ++possibleIndexInSubset) { uint validatorIndex = uint(keccak256(cycleSeed, bytes32(shardId), bytes32(possibleIndexInSubset))) % uint(validatorsMaxIndex); - if (_valcodeAddr == validators[int(validatorIndex)].validationCodeAddr) { + if (_validatorAddr == validators[int(validatorIndex)].addr) { shardList[shardId] = true; break; } @@ -227,7 +217,6 @@ contract VMC { } function addHeader(bytes _header) public returns(bool) { - address zeroAddr = 0x0; // require(_header.length <= 4096); // TODO // values = RLPList(header, [num, num, bytes32, bytes32, bytes32, address, bytes32, bytes32, num, bytes]) From 621e7696929e5226d2d87417faa396e4e5b2224d Mon Sep 17 00:00:00 2001 From: Fynn Date: Tue, 30 Jan 2018 10:24:32 +0100 Subject: [PATCH 07/17] constant variables & no need for sigGasLimit Former-commit-id: c36b63e78bc0bb6d05763605f443d99fc6370eb4 [formerly 5ed2b7fa9a7640ea93fc05b7351da95f10a5b6b9] Former-commit-id: ccd55ddcc878e4d1bcd3e71102f88de23982a427 --- sharding/contracts/validator_manager.sol | 46 ++++++++---------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/sharding/contracts/validator_manager.sol b/sharding/contracts/validator_manager.sol index 5aa53302af..d1dc73a564 100644 --- a/sharding/contracts/validator_manager.sol +++ b/sharding/contracts/validator_manager.sol @@ -49,17 +49,18 @@ contract VMC { // The top index of the stack in empty_slots_stack int emptySlotsStackTop; - // 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; + + // Constant values + uint constant periodLength = 5; + int constant numValidatorsPerCycle = 100; + int constant shardCount = 100; + // The exact deposit size which you have to deposit to become a validator + uint constant depositSize = 100 ether; + // Any given validator randomly gets allocated to some number of shards every SHUFFLING_CYCLE + int constant shufflingCycleLength = 5; // FIXME: just modified temporarily for test; + bytes32 addHeaderLogTopic; SigHasherContract sighasher; // Log the latest period number of the shard @@ -68,12 +69,6 @@ contract VMC { 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); } @@ -165,8 +160,8 @@ contract VMC { // 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; + function getShardList(address _validatorAddr) public constant returns(bool[shardCount]) { + bool[shardCount] memory shardList; int cycle = int(block.number) / shufflingCycleLength; int cycleStartBlockNumber = cycle * shufflingCycleLength - 1; if (cycleStartBlockNumber < 0) @@ -175,9 +170,9 @@ contract VMC { var cycleSeed = block.blockhash(uint(cycleStartBlockNumber)); int validatorsMaxIndex = getValidatorsMaxIndex(); if (numValidators != 0) { - for (uint8 shardId = 0; shardId < 100; ++shardId) { + for (uint8 shardId = 0; shardId < shardCount; ++shardId) { shardList[shardId] = false; - for (uint8 possibleIndexInSubset = 0; possibleIndexInSubset < 100; ++possibleIndexInSubset) { + for (uint8 possibleIndexInSubset = 0; possibleIndexInSubset < shardCount; ++possibleIndexInSubset) { uint validatorIndex = uint(keccak256(cycleSeed, bytes32(shardId), bytes32(possibleIndexInSubset))) % uint(validatorsMaxIndex); if (_validatorAddr == validators[int(validatorIndex)].addr) { @@ -190,19 +185,6 @@ contract VMC { return shardList; } - // 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; From d9a7106a0c94a87393bb99d762bc10b560335ca2 Mon Sep 17 00:00:00 2001 From: Fynn Date: Mon, 5 Feb 2018 09:47:27 +0100 Subject: [PATCH 08/17] new event Former-commit-id: 7e553b8f467e46b53b331d70bee5c89277dc4067 [formerly 0b4a95c2b2e1403493db125359fec9d89dad4d23] Former-commit-id: 8bad3a5e1b1a5dcab3dbc717c872dd39b29d207e --- sharding/contracts/validator_manager.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sharding/contracts/validator_manager.sol b/sharding/contracts/validator_manager.sol index d1dc73a564..fad3b155d5 100644 --- a/sharding/contracts/validator_manager.sol +++ b/sharding/contracts/validator_manager.sol @@ -11,6 +11,8 @@ contract VMC { using RLP for RLP.Iterator; using RLP for bytes; + event CollationAdded(uint256 indexed shardId, bytes collationHeader, bool isNewHead, uint256 score); + struct Validator { // Amount of wei the validator holds uint deposit; @@ -67,8 +69,6 @@ contract VMC { mapping (int => int) periodHead; function VMC() public { - numValidators = 0; - emptySlotsStackTop = 0; addHeaderLogTopic = keccak256("add_header()"); sighasher = SigHasherContract(0xDFFD41E18F04Ad8810c83B14FD1426a82E625A7D); } From 7819ec66843c925e97e6c2722bd0cc26fabea8af Mon Sep 17 00:00:00 2001 From: Fynn Date: Mon, 5 Feb 2018 14:04:21 +0100 Subject: [PATCH 09/17] no need for RLP lib :) Former-commit-id: 7864362ff2b5ef8a242a0069de2ea9542c1880d6 [formerly 58bb466edee5553bd6650f61a33744c7983bdcb1] Former-commit-id: c7f09619fb3e51875d271bac61d10df75a5ac4fc --- sharding/contracts/RLP.sol | 398 ------------------------------------- 1 file changed, 398 deletions(-) delete mode 100644 sharding/contracts/RLP.sol 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 From dd4a391f8b42d20578d7dee0f525b8f1e94d55ce Mon Sep 17 00:00:00 2001 From: Fynn Date: Mon, 5 Feb 2018 14:04:38 +0100 Subject: [PATCH 10/17] VMC contract adjustments Missing CollationAdded log Former-commit-id: 2853d6719f0db7b09edc46a382db73f3a4cd0389 [formerly 213dd18a8a5edcda88a5405ef38a37d6053dfbcc] Former-commit-id: a2e31148e881d02c1e0ff83349529cd12d12aeaf --- sharding/contracts/validator_manager.sol | 233 +++++++---------------- 1 file changed, 71 insertions(+), 162 deletions(-) diff --git a/sharding/contracts/validator_manager.sol b/sharding/contracts/validator_manager.sol index fad3b155d5..89a2014912 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(uint256 indexed shardId, bytes collationHeader, bool isNewHead, uint256 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; } @@ -50,27 +41,22 @@ contract VMC { mapping (int => int) emptySlotsStack; // The top index of the stack in empty_slots_stack int emptySlotsStackTop; - - // Is a valcode addr deposited now? - mapping (address => bool) isValcodeDeposited; + // Has the validator deposited before? + mapping (address => bool) isValidatorDeposited; // Constant values uint constant periodLength = 5; - int constant numValidatorsPerCycle = 100; int constant shardCount = 100; // The exact deposit size which you have to deposit to become a validator uint constant depositSize = 100 ether; - // Any given validator randomly gets allocated to some number of shards every SHUFFLING_CYCLE - int constant shufflingCycleLength = 5; // FIXME: just modified temporarily for test; + // Number of periods ahead of current period, which the contract + // is able to return the collator of that period + int constant lookAheadPeriods = 4; - bytes32 addHeaderLogTopic; - SigHasherContract sighasher; // Log the latest period number of the shard mapping (int => int) periodHead; function VMC() public { - addHeaderLogTopic = keccak256("add_header()"); - sighasher = SigHasherContract(0xDFFD41E18F04Ad8810c83B14FD1426a82E625A7D); } function isStackEmpty() internal view returns(bool) { @@ -87,102 +73,65 @@ 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].cycle != 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; - } - - // Get all possible shard ids that the given _valcodeAddr - // may be sampled in the current cycle - function getShardList(address _validatorAddr) public constant returns(bool[shardCount]) { - bool[shardCount] 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 < shardCount; ++shardId) { - shardList[shardId] = false; - for (uint8 possibleIndexInSubset = 0; possibleIndexInSubset < shardCount; ++possibleIndexInSubset) { - uint validatorIndex = uint(keccak256(cycleSeed, bytes32(shardId), bytes32(possibleIndexInSubset))) - % uint(validatorsMaxIndex); - if (_validatorAddr == validators[int(validatorIndex)].addr) { - shardList[shardId] = true; - break; - } - } - } - } - return shardList; + // 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, int period) public { + require(period >= lookAheadPeriods); + require((period - lookAheadPeriods) * periodLength < block.number); + require(numValidators > 0); + return validators[ + uint(keccak256(block.blockhash(period - lookAheadPeriods) * periodLength, shardId)) + % + uint(getValidatorsMaxIndex()) + ].addr; } struct Header { @@ -198,106 +147,66 @@ contract VMC { 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, int expectedPeriodNumber, bytes32 periodStartPrevHash, + bytes32 parentCollationHash, bytes32 txListRoot, address collationCoinbase, + bytes32 postStateRoot, bytes32 receiptRoot, int collationNumber) public returns(bool) { // 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); + var entireHeaderHash = keccak256(shardId, expectedPeriodNumber, periodStartPrevHash, + parentCollationHash, txListRoot, bytes32(collationCoinbase), + postStateRoot, receiptRoot, collationNumber); assert(entireHeaderHash != 0x0); - assert(collationHeaders[header.shardId][entireHeaderHash].score == 0); + assert(collationHeaders[shardId][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); + var validatorAddr = getEligibleProposer(shardId, block.number/periodLength); + require(validatorAddr != 0x0); + require(msg.sender == validatorAddr); + // Check score == collationNumber + var _score = collationHeaders[shardId][parentCollationHash].score + 1; + require(collationNumber == _score); // Add the header - collationHeaders[header.shardId][entireHeaderHash] = CollationHeader({ - parentCollationHash: header.parentCollationHash, + collationHeaders[shardId][entireHeaderHash] = CollationHeader({ + parentCollationHash: parentCollationHash, score: _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); + var isNewHead = false; + if (_score > collationHeaders[shardId[shardHead[shardId]]].score) { + shardHead[shardId] = entireHeaderHash; + 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`, @@ -315,7 +224,7 @@ contract VMC { var receiptId = numReceipts; ++numReceipts; - log3(keccak256("tx_to_shard()"), bytes32(_to), bytes32(_shardId), bytes32(receiptId)); + TxToShard(_to, _shardId, receiptId); return receiptId; } From 50ce4c735cef39286d7218a580c6c0eabce8df25 Mon Sep 17 00:00:00 2001 From: Fynn Date: Mon, 5 Feb 2018 14:10:28 +0100 Subject: [PATCH 11/17] removed validator's cycle var Former-commit-id: 2d019e818145aaf8510d38da46b3a0748d68cb93 [formerly db9083c59f38349e7dcba65ec4fb2843b709818a] Former-commit-id: 8ef26b218aec854a987697e3c73e104f85cb3c65 --- sharding/contracts/validator_manager.sol | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sharding/contracts/validator_manager.sol b/sharding/contracts/validator_manager.sol index 89a2014912..877d4f52d4 100644 --- a/sharding/contracts/validator_manager.sol +++ b/sharding/contracts/validator_manager.sol @@ -11,8 +11,6 @@ contract VMC { uint deposit; // The validator's address address addr; - // The cycle number which the validator would be included after - int cycle; } struct CollationHeader { @@ -81,7 +79,7 @@ contract VMC { for (int i = 0; i < 1024; ++i) { if (i >= allValidatorSlotsNum) break; - if (validators[i].cycle != 0x0) + if (validators[i].addr != 0x0) activateValidatorNum += 1; } return activateValidatorNum + emptySlotsStackTop; @@ -123,7 +121,7 @@ contract VMC { // 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, int period) public { + function getEligibleProposer(int shardId, uint period) public { require(period >= lookAheadPeriods); require((period - lookAheadPeriods) * periodLength < block.number); require(numValidators > 0); From 68e6135d028eb9478dd744aef8f13c2d41e731a2 Mon Sep 17 00:00:00 2001 From: Fynn Date: Mon, 5 Feb 2018 14:37:01 +0100 Subject: [PATCH 12/17] Fix type errors Former-commit-id: d18b56f51a6b87c7b976de24778789fa62644495 [formerly 02d0739b8bbadbfa8e2e499e1a14d5fef772c83e] Former-commit-id: 7eed34e7d43f6255d2310bb86310fc0e56eb4d2b --- sharding/contracts/validator_manager.sol | 63 +++++++++++------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/sharding/contracts/validator_manager.sol b/sharding/contracts/validator_manager.sol index 877d4f52d4..41809a22b5 100644 --- a/sharding/contracts/validator_manager.sol +++ b/sharding/contracts/validator_manager.sol @@ -2,7 +2,7 @@ pragma solidity ^0.4.19; contract VMC { event TxToShard(address indexed to, int indexed shardId, int receiptId); - event CollationAdded(uint256 indexed shardId, bytes collationHeader, bool isNewHead, uint256 score); + event CollationAdded(uint indexed shardId, bytes collationHeader, bool isNewHead, uint score); event Deposit(address validator, int index); event Withdraw(int validatorIndex); @@ -49,7 +49,7 @@ contract VMC { uint constant depositSize = 100 ether; // Number of periods ahead of current period, which the contract // is able to return the collator of that period - int constant lookAheadPeriods = 4; + uint constant lookAheadPeriods = 4; // Log the latest period number of the shard mapping (int => int) periodHead; @@ -121,46 +121,42 @@ contract VMC { // 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 { + 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[ - uint(keccak256(block.blockhash(period - lookAheadPeriods) * periodLength, shardId)) + int( + uint(keccak256(uint(block.blockhash(period - lookAheadPeriods)) * periodLength, shardId)) % uint(getValidatorsMaxIndex()) - ].addr; + )].addr; } - struct Header { - int shardId; - uint expectedPeriodNumber; - bytes32 periodStartPrevhash; - bytes32 parentCollationHash; - bytes32 txListRoot; - address collationCoinbase; - bytes32 postStateRoot; - bytes32 receiptRoot; - int collationNumber; - bytes sig; - } - - function addHeader(int shardId, int expectedPeriodNumber, bytes32 periodStartPrevHash, + struct HeaderVars { + bytes32 entireHeaderHash; + int score; + address validatorAddr; + bool isNewHead; + } + 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((shardId >= 0) && (shardId < shardCount)); require(block.number >= periodLength); - require(expectedPeriodNumber == (block.number / periodLength)); + require(expectedPeriodNumber == block.number / periodLength); require(periodStartPrevHash == block.blockhash(expectedPeriodNumber * periodLength - 1)); // Check if this header already exists - var entireHeaderHash = keccak256(shardId, expectedPeriodNumber, periodStartPrevHash, + headerVars.entireHeaderHash = keccak256(shardId, expectedPeriodNumber, periodStartPrevHash, parentCollationHash, txListRoot, bytes32(collationCoinbase), postStateRoot, receiptRoot, collationNumber); - assert(entireHeaderHash != 0x0); - assert(collationHeaders[shardId][entireHeaderHash].score == 0); + 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. @@ -170,28 +166,27 @@ contract VMC { assert(periodHead[shardId] < int(expectedPeriodNumber)); // Check the signature with validation_code_addr - var validatorAddr = getEligibleProposer(shardId, block.number/periodLength); - require(validatorAddr != 0x0); - require(msg.sender == validatorAddr); + headerVars.validatorAddr = getEligibleProposer(shardId, block.number/periodLength); + require(headerVars.validatorAddr != 0x0); + require(msg.sender == headerVars.validatorAddr); // Check score == collationNumber - var _score = collationHeaders[shardId][parentCollationHash].score + 1; - require(collationNumber == _score); + headerVars.score = collationHeaders[shardId][parentCollationHash].score + 1; + require(collationNumber == headerVars.score); // Add the header - collationHeaders[shardId][entireHeaderHash] = CollationHeader({ + collationHeaders[shardId][headerVars.entireHeaderHash] = CollationHeader({ parentCollationHash: parentCollationHash, - score: _score + score: headerVars.score }); // Update the latest period number periodHead[shardId] = int(expectedPeriodNumber); // Determine the head - var isNewHead = false; - if (_score > collationHeaders[shardId[shardHead[shardId]]].score) { - shardHead[shardId] = entireHeaderHash; - isNewHead = true; + if (headerVars.score > collationHeaders[shardId][shardHead[shardId]].score) { + shardHead[shardId] = headerVars.entireHeaderHash; + headerVars.isNewHead = true; } // [TODO] Log //CollationAdded(headerBytes, isNewHead, _score); From a6a1a4f79071d680092b1b44cdd9eb2322b51daa Mon Sep 17 00:00:00 2001 From: Fynn Date: Mon, 5 Feb 2018 14:38:59 +0100 Subject: [PATCH 13/17] Trying to outsmart the compiler Former-commit-id: cee4cb63e4738372fa5f240e9dd63f9f43a316c9 [formerly f2691c26c2ae080e6996bf97dd4dc16a1ae77bf7] Former-commit-id: bc82a7508a1914700d48b9a505f7a21472f31bc0 --- sharding/contracts/validator_manager.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sharding/contracts/validator_manager.sol b/sharding/contracts/validator_manager.sol index 41809a22b5..eef76ae021 100644 --- a/sharding/contracts/validator_manager.sol +++ b/sharding/contracts/validator_manager.sol @@ -23,9 +23,9 @@ contract VMC { uint txStartgas; uint txGasprice; uint value; + bytes32 data; address sender; address to; - bytes32 data; } mapping (int => Validator) validators; From fb1cce66a3ad26a29174e44e96f6122983564528 Mon Sep 17 00:00:00 2001 From: Fynn Date: Mon, 5 Feb 2018 14:47:48 +0100 Subject: [PATCH 14/17] styling Former-commit-id: 883fb24df46347c049867fcc47cac930b0123b8f [formerly 1e3bfdd66dc35b33e3c298bf5c2e700fb4743d3b] Former-commit-id: 4bc93ce29921fc0b2af09948b6a8bdd1af6ddbc3 --- sharding/contracts/validator_manager.sol | 50 ++++++++++++------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/sharding/contracts/validator_manager.sol b/sharding/contracts/validator_manager.sol index eef76ae021..659a8e5a52 100644 --- a/sharding/contracts/validator_manager.sol +++ b/sharding/contracts/validator_manager.sol @@ -121,14 +121,14 @@ contract VMC { // 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); + 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(keccak256(uint(block.blockhash(_period - lookAheadPeriods)) * periodLength, _shardId)) % uint(getValidatorsMaxIndex()) )].addr; @@ -140,52 +140,52 @@ contract VMC { address validatorAddr; bool isNewHead; } - function addHeader(int shardId, uint expectedPeriodNumber, bytes32 periodStartPrevHash, - bytes32 parentCollationHash, bytes32 txListRoot, address collationCoinbase, - bytes32 postStateRoot, bytes32 receiptRoot, int collationNumber) public returns(bool) { + 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((shardId >= 0) && (shardId < shardCount)); + require((_shardId >= 0) && (_shardId < shardCount)); require(block.number >= periodLength); - require(expectedPeriodNumber == block.number / periodLength); - require(periodStartPrevHash == block.blockhash(expectedPeriodNumber * periodLength - 1)); + require(_expectedPeriodNumber == block.number / periodLength); + require(_periodStartPrevHash == block.blockhash(_expectedPeriodNumber * periodLength - 1)); // Check if this header already exists - headerVars.entireHeaderHash = keccak256(shardId, expectedPeriodNumber, periodStartPrevHash, - parentCollationHash, txListRoot, bytes32(collationCoinbase), - postStateRoot, receiptRoot, collationNumber); + headerVars.entireHeaderHash = keccak256(_shardId, _expectedPeriodNumber, _periodStartPrevHash, + _parentCollationHash, _txListRoot, bytes32(_collationCoinbase), + _postStateRoot, _receiptRoot, _collationNumber); assert(headerVars.entireHeaderHash != 0x0); - assert(collationHeaders[shardId][headerVars.entireHeaderHash].score == 0); + 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 (parentCollationHash != 0x0) - assert((parentCollationHash == 0x0) || (collationHeaders[shardId][parentCollationHash].score > 0)); + if (_parentCollationHash != 0x0) + assert((_parentCollationHash == 0x0) || (collationHeaders[_shardId][_parentCollationHash].score > 0)); // Check if only one collation in one period - assert(periodHead[shardId] < int(expectedPeriodNumber)); + assert(periodHead[_shardId] < int(_expectedPeriodNumber)); // Check the signature with validation_code_addr - headerVars.validatorAddr = getEligibleProposer(shardId, block.number/periodLength); + 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); + headerVars.score = collationHeaders[_shardId][_parentCollationHash].score + 1; + require(_collationNumber == headerVars.score); // Add the header - collationHeaders[shardId][headerVars.entireHeaderHash] = CollationHeader({ - parentCollationHash: parentCollationHash, + collationHeaders[_shardId][headerVars.entireHeaderHash] = CollationHeader({ + parentCollationHash: _parentCollationHash, score: headerVars.score }); // Update the latest period number - periodHead[shardId] = int(expectedPeriodNumber); + periodHead[_shardId] = int(_expectedPeriodNumber); // Determine the head - if (headerVars.score > collationHeaders[shardId][shardHead[shardId]].score) { - shardHead[shardId] = headerVars.entireHeaderHash; + if (headerVars.score > collationHeaders[_shardId][shardHead[_shardId]].score) { + shardHead[_shardId] = headerVars.entireHeaderHash; headerVars.isNewHead = true; } // [TODO] Log From 89b3f36f2c34c5eb73fb2980d8381b87c68d6914 Mon Sep 17 00:00:00 2001 From: Fynn Date: Mon, 5 Feb 2018 19:06:00 +0100 Subject: [PATCH 15/17] adjusting code to review Former-commit-id: 3c47ffd37a1dfe26da31c12c2005b079ce6910bd [formerly b5fca337e8c32e9a4c6f8505722c97e08464cfff] Former-commit-id: 5b8433833e53f8e9411afbf1ea36752b5395be18 --- sharding/contracts/validator_manager.sol | 147 +++++++++++++---------- 1 file changed, 83 insertions(+), 64 deletions(-) diff --git a/sharding/contracts/validator_manager.sol b/sharding/contracts/validator_manager.sol index 659a8e5a52..576c044467 100644 --- a/sharding/contracts/validator_manager.sol +++ b/sharding/contracts/validator_manager.sol @@ -28,11 +28,23 @@ contract VMC { address to; } - mapping (int => Validator) validators; - mapping (int => mapping (bytes32 => CollationHeader)) collationHeaders; - mapping (int => Receipt) receipts; + // Packed variables to be used in addHeader + struct HeaderVars { + bytes32 entireHeaderHash; + int score; + address validatorAddr; + bool isNewHead; + } + // validatorId => Validators + mapping (int => Validator) validators; + // shardId => (headerHash => CollationHeader) + mapping (int => mapping (bytes32 => CollationHeader)) collationHeaders; + // receiptId => Receipt + mapping (int => Receipt) receipts; + // shardId => headerHash mapping (int => bytes32) shardHead; + int numValidators; int numReceipts; // Indexs of empty slots caused by the function `withdraw` @@ -57,32 +69,33 @@ contract VMC { function VMC() public { } - 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()) - return -1; - --emptySlotsStackTop; - return emptySlotsStack[emptySlotsStackTop]; + // 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; } - function getValidatorsMaxIndex() internal view returns(int) { - int activateValidatorNum = 0; - 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].addr != 0x0) - activateValidatorNum += 1; - } - return activateValidatorNum + emptySlotsStackTop; + // 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; } function deposit() public payable returns(int) { @@ -99,13 +112,14 @@ contract VMC { deposit: msg.value, addr: msg.sender }); - ++numValidators; - isValidatorDeposited[msg.sender] = true; - - Deposit(msg.sender, index); - return index; + ++numValidators; + isValidatorDeposited[msg.sender] = true; + + Deposit(msg.sender, index); + return index; } + // Removes the validator from the validator set and refunds the deposited ether function withdraw(int _validatorIndex) public { require(msg.sender == validators[_validatorIndex].addr); // [FIXME] Should consider calling the validator's contract, might be useful @@ -118,31 +132,11 @@ contract VMC { Withdraw(_validatorIndex); } - // 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; - } - - struct HeaderVars { - bytes32 entireHeaderHash; - int score; - address validatorAddr; - bool isNewHead; - } + // Attempts to process a collation header, returns true on success, reverts on failure. function addHeader(int _shardId, uint _expectedPeriodNumber, bytes32 _periodStartPrevHash, - bytes32 _parentCollationHash, bytes32 _txListRoot, address _collationCoinbase, - bytes32 _postStateRoot, bytes32 _receiptRoot, int _collationNumber) public returns(bool) { + bytes32 _parentCollationHash, bytes32 _txListRoot, + address _collationCoinbase, bytes32 _postStateRoot, bytes32 _receiptRoot, + int _collationNumber) public returns(bool) { HeaderVars memory headerVars; // Check if the header is valid @@ -194,17 +188,12 @@ contract VMC { return true; } - // 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`, // `gasprice`, and `data`. - function txToShard(address _to, int _shardId, uint _txStartgas, uint _txGasprice, bytes12 _data) public payable returns(int) { + function txToShard(address _to, int _shardId, uint _txStartgas, uint _txGasprice, + bytes12 _data) public payable returns(int) { receipts[numReceipts] = Receipt({ shardId: _shardId, txStartgas: _txStartgas, @@ -221,9 +210,39 @@ contract VMC { return receiptId; } - function updataGasPrice(int _receiptId, uint _txGasprice) public payable returns(bool) { + function updateGasPrice(int _receiptId, uint _txGasprice) public payable returns(bool) { require(receipts[_receiptId].sender == msg.sender); receipts[_receiptId].txGasprice = _txGasprice; return true; } + + 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()) + return -1; + --emptySlotsStackTop; + return emptySlotsStack[emptySlotsStackTop]; + } + + function getValidatorsMaxIndex() internal view returns(int) { + int activateValidatorNum = 0; + 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].addr != 0x0) + activateValidatorNum += 1; + } + return activateValidatorNum + emptySlotsStackTop; + } } From 74a744f3381b061160128ba5adfaad63dbe8e757 Mon Sep 17 00:00:00 2001 From: Fynn Date: Mon, 5 Feb 2018 19:13:11 +0100 Subject: [PATCH 16/17] make shardCount public Former-commit-id: 7546ce0eb3a7c0c6e6ca53588bfa0d7866d3734d [formerly 1ba1e42c6d4645b69916ef09f90a96fce98c6d0d] Former-commit-id: 963c0eb9e89d82b2015c52558fae69379f3b31bf --- sharding/contracts/validator_manager.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sharding/contracts/validator_manager.sol b/sharding/contracts/validator_manager.sol index 576c044467..f6311727c9 100644 --- a/sharding/contracts/validator_manager.sol +++ b/sharding/contracts/validator_manager.sol @@ -56,7 +56,7 @@ contract VMC { // Constant values uint constant periodLength = 5; - int constant shardCount = 100; + int constant public shardCount = 100; // The exact deposit size which you have to deposit to become a validator uint constant depositSize = 100 ether; // Number of periods ahead of current period, which the contract From 7ef5c029b95916781265603e22814d3211f2b53d Mon Sep 17 00:00:00 2001 From: Fynn Date: Mon, 5 Feb 2018 20:32:45 +0100 Subject: [PATCH 17/17] Renaming variables, removing unnecessary requires Former-commit-id: e7ab3f1756067a9feb9c4b010e8089e5fb04566f [formerly aa39862c0e3edd2baec6d90bdb01ded21b19fff0] Former-commit-id: 514e3db78ad253d7768bdec5cb6ac0c6428ab8ef --- sharding/contracts/validator_manager.sol | 25 ++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/sharding/contracts/validator_manager.sol b/sharding/contracts/validator_manager.sol index f6311727c9..d582dc477e 100644 --- a/sharding/contracts/validator_manager.sol +++ b/sharding/contracts/validator_manager.sol @@ -14,7 +14,7 @@ contract VMC { } struct CollationHeader { - bytes32 parentCollationHash; + bytes32 parentHash; int score; } @@ -45,7 +45,9 @@ contract VMC { // shardId => headerHash mapping (int => bytes32) shardHead; + // Number of validators int numValidators; + // Number of receipts int numReceipts; // Indexs of empty slots caused by the function `withdraw` mapping (int => int) emptySlotsStack; @@ -134,9 +136,9 @@ contract VMC { // Attempts to process a collation header, returns true on success, reverts on failure. function addHeader(int _shardId, uint _expectedPeriodNumber, bytes32 _periodStartPrevHash, - bytes32 _parentCollationHash, bytes32 _txListRoot, - address _collationCoinbase, bytes32 _postStateRoot, bytes32 _receiptRoot, - int _collationNumber) public returns(bool) { + bytes32 _parentHash, bytes32 _transactionRoot, + address _coinbase, bytes32 _stateRoot, bytes32 _receiptRoot, + int _number) public returns(bool) { HeaderVars memory headerVars; // Check if the header is valid @@ -147,15 +149,14 @@ contract VMC { // Check if this header already exists headerVars.entireHeaderHash = keccak256(_shardId, _expectedPeriodNumber, _periodStartPrevHash, - _parentCollationHash, _txListRoot, bytes32(_collationCoinbase), - _postStateRoot, _receiptRoot, _collationNumber); - assert(headerVars.entireHeaderHash != 0x0); + _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 (_parentCollationHash != 0x0) - assert((_parentCollationHash == 0x0) || (collationHeaders[_shardId][_parentCollationHash].score > 0)); + if (_parentHash != 0x0) + assert(collationHeaders[_shardId][_parentHash].score > 0); // Check if only one collation in one period assert(periodHead[_shardId] < int(_expectedPeriodNumber)); @@ -165,12 +166,12 @@ contract VMC { require(msg.sender == headerVars.validatorAddr); // Check score == collationNumber - headerVars.score = collationHeaders[_shardId][_parentCollationHash].score + 1; - require(_collationNumber == headerVars.score); + headerVars.score = collationHeaders[_shardId][_parentHash].score + 1; + require(_number == headerVars.score); // Add the header collationHeaders[_shardId][headerVars.entireHeaderHash] = CollationHeader({ - parentCollationHash: _parentCollationHash, + parentHash: _parentHash, score: headerVars.score });