// SPDX-License-Identifier:APACHE-2.0 /* * Taken from https://github.com/hamdiallam/Solidity-RLP */ /* solhint-disable */ pragma solidity ^0.6.12; library RLPReader { uint8 constant STRING_SHORT_START = 0x80; uint8 constant STRING_LONG_START = 0xb8; uint8 constant LIST_SHORT_START = 0xc0; uint8 constant LIST_LONG_START = 0xf8; uint8 constant WORD_SIZE = 32; struct RLPItem { uint256 len; uint256 memPtr; } using RLPReader for bytes; using RLPReader for uint256; using RLPReader for RLPReader.RLPItem; // helper function to decode rlp encoded ethereum transaction /* * @param rawTransaction RLP encoded ethereum transaction * @return tuple (nonce,gasPrice,gasLimit,to,value,data) */ function decodeTransaction( bytes memory rawTransaction ) internal pure returns (uint256, uint256, uint256, address, uint256, bytes memory) { RLPReader.RLPItem[] memory values = rawTransaction.toRlpItem().toList(); // must convert to an rlpItem first! return ( values[0].toUint(), values[1].toUint(), values[2].toUint(), values[3].toAddress(), values[4].toUint(), values[5].toBytes() ); } /* * @param item RLP encoded bytes */ function toRlpItem( bytes memory item ) internal pure returns (RLPItem memory) { if (item.length == 0) return RLPItem(0, 0); uint256 memPtr; assembly { memPtr := add(item, 0x20) } return RLPItem(item.length, memPtr); } /* * @param item RLP encoded list in bytes */ function toList( RLPItem memory item ) internal pure returns (RLPItem[] memory result) { require(isList(item), "isList failed"); uint256 items = numItems(item); result = new RLPItem[](items); uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr); uint256 dataLen; for (uint256 i = 0; i < items; i++) { dataLen = _itemLength(memPtr); result[i] = RLPItem(dataLen, memPtr); memPtr = memPtr + dataLen; } } /* * Helpers */ // @return indicator whether encoded payload is a list. negate this function call for isData. function isList(RLPItem memory item) internal pure returns (bool) { uint8 byte0; uint256 memPtr = item.memPtr; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < LIST_SHORT_START) return false; return true; } // @return number of payload items inside an encoded list. function numItems(RLPItem memory item) internal pure returns (uint256) { uint256 count = 0; uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr); uint256 endPtr = item.memPtr + item.len; while (currPtr < endPtr) { currPtr = currPtr + _itemLength(currPtr); // skip over an item count++; } return count; } // @return entire rlp item byte length function _itemLength(uint256 memPtr) internal pure returns (uint256 len) { uint256 byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) return 1; else if (byte0 < STRING_LONG_START) return byte0 - STRING_SHORT_START + 1; else if (byte0 < LIST_SHORT_START) { assembly { let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is memPtr := add(memPtr, 1) // skip over the first byte /* 32 byte word size */ let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len len := add(dataLen, add(byteLen, 1)) } } else if (byte0 < LIST_LONG_START) { return byte0 - LIST_SHORT_START + 1; } else { assembly { let byteLen := sub(byte0, 0xf7) memPtr := add(memPtr, 1) let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length len := add(dataLen, add(byteLen, 1)) } } } // @return number of bytes until the data function _payloadOffset(uint256 memPtr) internal pure returns (uint256) { uint256 byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) return 0; else if ( byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START) ) return 1; else if (byte0 < LIST_SHORT_START) // being explicit return byte0 - (STRING_LONG_START - 1) + 1; else return byte0 - (LIST_LONG_START - 1) + 1; } /** RLPItem conversions into data types **/ // @returns raw rlp encoding in bytes function toRlpBytes( RLPItem memory item ) internal pure returns (bytes memory) { bytes memory result = new bytes(item.len); uint256 ptr; assembly { ptr := add(0x20, result) } copy(item.memPtr, ptr, item.len); return result; } function toBoolean(RLPItem memory item) internal pure returns (bool) { require( item.len == 1, "Invalid RLPItem. Booleans are encoded in 1 byte" ); uint256 result; uint256 memPtr = item.memPtr; assembly { result := byte(0, mload(memPtr)) } return result == 0 ? false : true; } function toAddress(RLPItem memory item) internal pure returns (address) { // 1 byte for the length prefix according to RLP spec require( item.len <= 21, "Invalid RLPItem. Addresses are encoded in 20 bytes or less" ); return address(toUint(item)); } function toUint(RLPItem memory item) internal pure returns (uint256) { uint256 offset = _payloadOffset(item.memPtr); uint256 len = item.len - offset; uint256 memPtr = item.memPtr + offset; uint256 result; assembly { result := div(mload(memPtr), exp(256, sub(32, len))) // shift to the correct location } return result; } function toBytes(RLPItem memory item) internal pure returns (bytes memory) { uint256 offset = _payloadOffset(item.memPtr); uint256 len = item.len - offset; // data length bytes memory result = new bytes(len); uint256 destPtr; assembly { destPtr := add(0x20, result) } copy(item.memPtr + offset, destPtr, len); return result; } /* * @param src Pointer to source * @param dest Pointer to destination * @param len Amount of memory to copy from the source */ function copy(uint256 src, uint256 dest, uint256 len) internal pure { // copy as many word sizes as possible for (; len >= WORD_SIZE; len -= WORD_SIZE) { assembly { mstore(dest, mload(src)) } src += WORD_SIZE; dest += WORD_SIZE; } // left over bytes. Mask is used to remove unwanted bytes from the word uint256 mask = 256 ** (WORD_SIZE - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) // zero out src let destpart := and(mload(dest), mask) // retrieve the bytes mstore(dest, or(destpart, srcpart)) } } }