Created
February 23, 2017 10:05
-
-
Save yaronvel/6d7869c9615fa171d1649eb79248c1b4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| pragma solidity ^0.4.9; | |
| import "./Ethash_asm.sol"; | |
| /** | |
| * @title RLPReader | |
| * | |
| * RLPReader is used to read and parse RLP encoded data in memory. | |
| * | |
| * @author Andreas Olofsson ([email protected]) | |
| */ | |
| 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 constant 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 | |
| throw; | |
| } | |
| function next(Iterator memory self, bool strict) internal constant returns (RLPItem memory subItem) { | |
| subItem = next(self); | |
| if(strict && !_validate(subItem)) | |
| throw; | |
| return; | |
| } | |
| function hasNext(Iterator memory self) internal constant 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 constant 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 constant returns (RLPItem memory) { | |
| var item = toRLPItem(self); | |
| if(strict) { | |
| uint len = self.length; | |
| if(_payloadOffset(item) > len) | |
| throw; | |
| if(_itemLength(item._unsafe_memPtr) != len) | |
| throw; | |
| if(!_validate(item)) | |
| throw; | |
| } | |
| 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 constant 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 constant 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 constant 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 constant 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 constant 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 constant returns (Iterator memory it) { | |
| if (!isList(self)) | |
| throw; | |
| 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) { | |
| if(!isData(self)) | |
| throw; | |
| 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 constant returns (RLPItem[] memory list) { | |
| if(!isList(self)) | |
| throw; | |
| 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) { | |
| if(!isData(self)) | |
| throw; | |
| 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 constant returns (uint data) { | |
| if(!isData(self)) | |
| throw; | |
| var (rStartPos, len) = _decode(self); | |
| if (len > 32 || len == 0) | |
| throw; | |
| 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 constant returns (bool data) { | |
| if(!isData(self)) | |
| throw; | |
| var (rStartPos, len) = _decode(self); | |
| if (len != 1) | |
| throw; | |
| uint temp; | |
| assembly { | |
| temp := byte(0, mload(rStartPos)) | |
| } | |
| if (temp > 1) | |
| throw; | |
| 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 constant returns (byte data) { | |
| if(!isData(self)) | |
| throw; | |
| var (rStartPos, len) = _decode(self); | |
| if (len != 1) | |
| throw; | |
| 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 constant 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 constant 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 constant returns (address data) { | |
| if(!isData(self)) | |
| throw; | |
| var (rStartPos, len) = _decode(self); | |
| if (len != 20) | |
| throw; | |
| assembly { | |
| data := div(mload(rStartPos), exp(256, 12)) | |
| } | |
| } | |
| // Get the payload offset. | |
| function _payloadOffset(RLPItem memory self) private constant 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 constant 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 constant returns (uint memPtr, uint len) { | |
| if(!isData(self)) | |
| throw; | |
| 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 constant 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; | |
| } | |
| } | |
| contract RLPReaderTest { | |
| using RLP for RLP.RLPItem; | |
| using RLP for RLP.Iterator; | |
| using RLP for bytes; | |
| function RLPReaderTest() {} | |
| function testItemStrict(bytes rlp) constant returns (bool res) { | |
| res = true; | |
| rlp.toRLPItem(true); | |
| } | |
| function testFirst(bytes rlp) constant returns (uint memPtr, uint len, byte first) { | |
| var item = rlp.toRLPItem(); | |
| memPtr = item._unsafe_memPtr; | |
| len = item._unsafe_length; | |
| uint b0; | |
| assembly { | |
| b0 := byte(0, mload(memPtr)) | |
| } | |
| first = byte(b0); | |
| } | |
| function testIsList(bytes rlp) constant returns (bool ret) { | |
| ret = rlp.toRLPItem().isList(); | |
| } | |
| function testIsData(bytes rlp) constant returns (bool ret) { | |
| ret = rlp.toRLPItem().isData(); | |
| } | |
| function testIsNull(bytes rlp) constant returns (bool ret) { | |
| ret = rlp.toRLPItem().isNull(); | |
| } | |
| function testIsEmpty(bytes rlp) constant returns (bool ret) { | |
| ret = rlp.toRLPItem().isEmpty(); | |
| } | |
| function testItems(bytes rlp) constant returns (uint) { | |
| return rlp.toRLPItem().items(); | |
| } | |
| function testSubItem(bytes rlp, uint index) constant returns (uint memPtr, uint len, bool isList, uint[] list, uint listLen) { | |
| var it = rlp.toRLPItem().iterator(); | |
| uint idx; | |
| while(it.hasNext() && idx < index) { | |
| it.next(); | |
| idx++; | |
| } | |
| var si = it.next(); | |
| return _testItem(si); | |
| } | |
| function testToData(bytes rlp) constant returns (bytes memory bts) { | |
| bts = rlp.toRLPItem().toData(); | |
| } | |
| function testToUint(bytes rlp) constant returns (uint) { | |
| return rlp.toRLPItem().toUint(); | |
| } | |
| function testToInt(bytes rlp) constant returns (int) { | |
| return rlp.toRLPItem().toInt(); | |
| } | |
| function testToBytes32(bytes rlp) constant returns (bytes32) { | |
| return rlp.toRLPItem().toBytes32(); | |
| } | |
| function testToAddress(bytes rlp) constant returns (address) { | |
| return rlp.toRLPItem().toAddress(); | |
| } | |
| function testToByte(bytes rlp) constant returns (byte) { | |
| return rlp.toRLPItem().toByte(); | |
| } | |
| function testToBool(bytes rlp) constant returns (bool) { | |
| return rlp.toRLPItem().toBool(); | |
| } | |
| function _testItem(RLP.RLPItem item) internal constant returns (uint memPtr, uint len, bool isList, uint[] memory list, uint listLen) { | |
| memPtr = item._unsafe_memPtr; | |
| len = item._unsafe_length; | |
| isList = item.isList(); | |
| if (isList) { | |
| uint i; | |
| listLen = item.items(); | |
| list = new uint[](listLen); | |
| var it = item.iterator(); | |
| while(it.hasNext() && i < listLen) { | |
| var si = it.next(); | |
| uint ptr; | |
| assembly { | |
| ptr := mload(si) | |
| } | |
| list[i] = ptr; | |
| i++; | |
| } | |
| } | |
| } | |
| function testItem(bytes rlp) constant returns (uint memPtr, uint len, bool isList, uint[] list, uint listLen) { | |
| var item = rlp.toRLPItem(); | |
| return _testItem(item); | |
| } | |
| function getItem(bytes rlp, uint itemIndex) constant returns(uint) { | |
| var it = rlp.toRLPItem().iterator(); | |
| uint idx; | |
| while(it.hasNext() && idx < itemIndex) { | |
| it.next(); | |
| idx++; | |
| } | |
| return it.next().toUint(); | |
| } | |
| } | |
| contract Agt { | |
| using RLP for RLP.RLPItem; | |
| using RLP for RLP.Iterator; | |
| using RLP for bytes; | |
| struct BlockHeader { | |
| uint prevBlockHash; // 0 | |
| uint coinbase; // 1 | |
| uint blockNumber; // 8 | |
| uint timestamp; // 11 | |
| bytes32 extraData; // 12 | |
| } | |
| function Agt() {} | |
| function parseBlockHeader( bytes rlpHeader ) constant internal returns(BlockHeader) { | |
| BlockHeader memory header; | |
| var it = rlpHeader.toRLPItem().iterator(); | |
| uint idx; | |
| while(it.hasNext()) { | |
| if( idx == 0 ) header.prevBlockHash = it.next().toUint(); | |
| else if ( idx == 2 ) header.coinbase = it.next().toUint(); | |
| else if ( idx == 8 ) header.blockNumber = it.next().toUint(); | |
| else if ( idx == 11 ) header.timestamp = it.next().toUint(); | |
| else if ( idx == 12 ) header.extraData = bytes32(it.next().toUint()); | |
| else it.next(); | |
| idx++; | |
| } | |
| return header; | |
| } | |
| event VerifyAgt( string msg, uint index ); | |
| struct VerifyAgtData { | |
| uint rootHash; | |
| uint rootMin; | |
| uint rootMax; | |
| uint leafHash; | |
| uint leafCounter; | |
| } | |
| function verifyAgt( /*uint[3] root, // [0] = min, [1] = max, [2] = root hash | |
| uint[2] leafData, // [0] = hash, [1] = counter | |
| //uint leaf32BytesHash, | |
| //uint leafCounter,*/ | |
| VerifyAgtData data, | |
| uint branchIndex, | |
| uint[] countersBranch, | |
| uint[] hashesBranch ) constant internal returns(bool) { | |
| uint currentHash = data.leafHash & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; | |
| uint leftCounterMin; | |
| uint leftCounterMax; | |
| uint leftHash; | |
| uint rightCounterMin; | |
| uint rightCounterMax; | |
| uint rightHash; | |
| uint min = data.leafCounter; | |
| uint max = data.leafCounter; | |
| for( uint i = 0 ; i < countersBranch.length ; i++ ) { | |
| if( branchIndex & 0x1 > 0 ) { | |
| leftCounterMin = countersBranch[i] & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; | |
| leftCounterMax = countersBranch[i] >> 128; | |
| leftHash = hashesBranch[i]; | |
| rightCounterMin = min; | |
| rightCounterMax = max; | |
| rightHash = currentHash; | |
| } | |
| else { | |
| leftCounterMin = min; | |
| leftCounterMax = max; | |
| leftHash = currentHash; | |
| rightCounterMin = countersBranch[i] & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; | |
| rightCounterMax = countersBranch[i] >> 128; | |
| rightHash = hashesBranch[i]; | |
| } | |
| currentHash = uint(sha3(leftCounterMin + (leftCounterMax << 128), | |
| leftHash, | |
| rightCounterMin + (rightCounterMax << 128), | |
| rightHash)) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; | |
| if( (leftCounterMin >= leftCounterMax) || (rightCounterMin >= rightCounterMax) ) { | |
| if( i > 0 ) { | |
| VerifyAgt( "counters mismatch",i); | |
| return false; | |
| } | |
| if( leftCounterMin > leftCounterMax ) { | |
| VerifyAgt( "counters mismatch",i); | |
| return false; | |
| } | |
| if( rightCounterMin > rightCounterMax ) { | |
| VerifyAgt( "counters mismatch",i); | |
| return false; | |
| } | |
| } | |
| if( leftCounterMax >= rightCounterMin ) return false; | |
| min = leftCounterMin; | |
| max = rightCounterMax; | |
| branchIndex = branchIndex / 2; | |
| } | |
| if( min != data.rootMin ) { | |
| VerifyAgt( "min does not match root min",min); | |
| return false; | |
| } | |
| if( max != data.rootMax ) { | |
| VerifyAgt( "max does not match root max",max); | |
| return false; | |
| } | |
| if( currentHash != data.rootHash ) { | |
| VerifyAgt( "hash does not match root hash",currentHash); | |
| return false; | |
| } | |
| return true; | |
| } | |
| } | |
| contract TestPool is Ethash, Agt { | |
| address public minerAddress = 0x00B3B47928458109848009ABCDEFFFEA3459012095; | |
| address public contractAddress = 0x00A1A2A3A4A34598ABCDEFFED45902390854389043; | |
| address public owner; | |
| string public version = "0.0.1"; | |
| bool public newVersionReleased = false; | |
| struct SubmissionData { | |
| uint numShares; | |
| uint difficulty; | |
| uint min; | |
| uint max; | |
| uint augMerkle; | |
| uint blockNumber; | |
| } | |
| struct MinerData { | |
| bytes32 minerId; | |
| address paymentAddress; | |
| SubmissionData lastSubmission; | |
| uint lastCounter; | |
| } | |
| mapping(address=>MinerData) minersData; | |
| uint numMiners; | |
| struct EthashCacheData { | |
| uint128 merkleRoot; | |
| uint64 fullSizeIn128Resultion; | |
| uint64 branchDepth; | |
| } | |
| mapping(uint=>EthashCacheData) epochData; | |
| event Debug( string msg ); | |
| function TestPool() { owner = msg.sender; } | |
| function declareNewerVersion() { | |
| if( owner != msg.sender ) throw; | |
| newVersionReleased = true; | |
| } | |
| function to62Encoding( uint id, uint numChars ) constant returns(bytes32) { | |
| if( id >= (26+26+10)**numChars ) throw; | |
| uint result = 0; | |
| for( uint i = 0 ; i < numChars ; i++ ) { | |
| uint b = id % (26+26+10); | |
| uint8 char; | |
| if( b < 10 ) { | |
| char = uint8(b + 0x30); // 0x30 = '0' | |
| } | |
| if( b < 26 + 10 ) { | |
| char = uint8(b + 0x61 - 10); //0x61 = 'a' | |
| } | |
| else { | |
| char = uint8(b + 0x41 - 26 - 10); // 0x41 = 'A' | |
| } | |
| result = (result * 256) + char; | |
| id /= (26+26+10); | |
| } | |
| return bytes32(result); | |
| } | |
| function register( uint timestamp, address paymentAddress ) { | |
| // TODO - replace timestamp with now | |
| Debug("register"); | |
| minersData[minerAddress].lastCounter = timestamp * (2**64); | |
| minersData[minerAddress].paymentAddress = paymentAddress; | |
| // build id | |
| uint id = uint(minerAddress) % (26+26+10)**11; | |
| minersData[minerAddress].minerId = to62Encoding(id,11); | |
| /* | |
| if( id >= (26+26+10)**5 ) throw; // too many miners | |
| byte[5] memory byteId; | |
| for( uint i = 0 ; i < 5 ; i++ ) { | |
| uint b = id % (26+26+10); | |
| if( b < 26 ) { | |
| byteId[i] = byte(b + 0x61); //0x61 = 'a' | |
| } | |
| else if( b < 26 + 26 ) { | |
| byteId[i] = byte(b + 0x41 - 26); // 0x41 = 'A' | |
| } | |
| else { | |
| byteId[i] = byte(b + 0x30 - 52); // 0x30 = '0' | |
| } | |
| id /= (26+26+10); | |
| } | |
| minersData[minerAddress].minerId = byteId; | |
| */ | |
| } | |
| function getMinerId() constant returns(bytes32) { | |
| return minersData[minerAddress].minerId; | |
| } | |
| function getClaimSeed( ) constant returns(uint){ | |
| MinerData memory data = minersData[minerAddress]; | |
| if( block.number > data.lastSubmission.blockNumber + 200 ) return 0; | |
| if( block.number <= data.lastSubmission.blockNumber + 1 ) return 0; | |
| return uint(block.blockhash(data.lastSubmission.blockNumber + 1)); | |
| } | |
| function submitClaim( uint numShares, uint difficulty, uint min, uint max, uint augMerkle ) { | |
| MinerData memory data = minersData[minerAddress]; | |
| if( data.lastCounter >= min ) { | |
| data.lastCounter = max + 1; | |
| Debug("submit claim - miner cheated"); | |
| return; | |
| } | |
| MinerData memory newData; | |
| newData.lastCounter = max + 1; | |
| newData.lastSubmission.numShares = numShares; | |
| newData.lastSubmission.difficulty = difficulty; | |
| newData.lastSubmission.min = min; | |
| newData.lastSubmission.max = max; | |
| newData.lastSubmission.augMerkle = augMerkle; | |
| newData.lastSubmission.blockNumber = block.number; | |
| minersData[minerAddress] = newData; | |
| Debug("submit claim ok"); | |
| } | |
| function setEpochData( uint128 merkleRoot, uint64 fullSizeIn128Resultion, uint64 branchDepth, uint epoch ) { | |
| EthashCacheData memory data; | |
| data.merkleRoot = merkleRoot; | |
| data.fullSizeIn128Resultion = fullSizeIn128Resultion; | |
| data.branchDepth = branchDepth; | |
| epochData[epoch] = data; | |
| Debug("set epoch data"); | |
| } | |
| function verifyExtraData( bytes32 extraData, bytes32 minerId, uint difficulty ) constant returns(bool) { | |
| uint i; | |
| // compare id | |
| for( i = 0 ; i < 11 ; i++ ) { | |
| if( extraData[10+i] != minerId[i] ) { | |
| ErrorLog( "verifyExtraData: miner id not as expected", 0 ); | |
| return false; | |
| } | |
| } | |
| // compare difficulty | |
| bytes32 encodedDiff = to62Encoding(difficulty,11); | |
| for( i = 0 ; i < 11 ; i++ ) { | |
| if(extraData[i+21] != encodedDiff[i]) { | |
| ErrorLog( "verifyExtraData: difficulty is not as expected", uint(encodedDiff) ); | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| event ErrorLog( string msg, uint i ); | |
| event Pay( string msg, uint amount ); | |
| function verifyClaim( bytes rlpHeader, | |
| uint nonce, | |
| uint shareIndex, | |
| uint[] dataSetLookup, | |
| uint[] witnessForLookup, | |
| uint[] augCountersBranch, | |
| uint[] augHashesBranch ) returns(uint) { | |
| BlockHeader memory header = parseBlockHeader(rlpHeader); | |
| SubmissionData submissionData = minersData[ minerAddress ].lastSubmission; | |
| // check extra data | |
| if( ! verifyExtraData( header.extraData, | |
| minersData[ minerAddress ].minerId, | |
| submissionData.difficulty ) ) { | |
| ErrorLog( "extra data not as expected", uint(header.extraData) ); | |
| return 20; | |
| } | |
| // check counter | |
| uint counter = header.timestamp * (2 ** 64) + nonce; | |
| if( counter < submissionData.min ) { | |
| ErrorLog( "counter is smaller than min",counter); | |
| return 4; | |
| } | |
| if( counter > submissionData.max ) { | |
| ErrorLog( "counter is smaller than max",counter); | |
| return 5; | |
| } | |
| // verify agt | |
| uint leafHash = uint(sha3(rlpHeader)); | |
| VerifyAgtData memory agtData; | |
| agtData.rootHash = submissionData.augMerkle; | |
| agtData.rootMin = submissionData.min; | |
| agtData.rootMax = submissionData.max; | |
| agtData.leafHash = leafHash; | |
| agtData.leafCounter = counter; | |
| if( ! verifyAgt( agtData, | |
| shareIndex, | |
| augCountersBranch, | |
| augHashesBranch ) ) { | |
| ErrorLog( "verifyAgt failed",0); | |
| return 6; | |
| } | |
| // get epoch data | |
| EthashCacheData memory eData = epochData[header.blockNumber / 30000]; | |
| // verify ethash | |
| uint ethash = hashimoto( bytes32(leafHash), | |
| bytes8(nonce), | |
| eData.fullSizeIn128Resultion, | |
| dataSetLookup, | |
| witnessForLookup, | |
| eData.branchDepth, | |
| eData.merkleRoot ); | |
| if( ethash > ((2**256-1)/submissionData.difficulty )) { | |
| ErrorLog( "ethash difficulty too low",ethash); | |
| return 7; | |
| } | |
| if( block.difficulty > 0 ) { | |
| Pay( "verification completed you will be paid", | |
| 3 ether * submissionData.difficulty * submissionData.numShares / block.difficulty ); | |
| } | |
| else { | |
| Pay( "verification completed you will be paid", | |
| 3 ether * submissionData.difficulty * submissionData.numShares / 100000 ); | |
| } | |
| return 8; | |
| } | |
| function array( uint[] x ) returns(bytes32){ | |
| return sha3(x); | |
| } | |
| function bytesstream( bytes x ) returns(bytes32) { | |
| return sha3(x); | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment