From 785d99de0220598e551154acd506438476bdaa3a Mon Sep 17 00:00:00 2001 From: Fynn Date: Wed, 17 Jan 2018 17:21:54 +0100 Subject: [PATCH] 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 000000000..739702643 --- /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