-
Notifications
You must be signed in to change notification settings - Fork 112
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #69 from 0xPolygon/sec/update-rlp
Update RLPReader
- Loading branch information
Showing
1 changed file
with
52 additions
and
37 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,8 +4,7 @@ | |
* @author Hamdi Allam [email protected] | ||
* Please reach out with any questions or concerns | ||
*/ | ||
|
||
pragma solidity ^0.8.0; | ||
pragma solidity >=0.5.10 <0.9.0; | ||
|
||
library RLPReader { | ||
uint8 constant STRING_SHORT_START = 0x80; | ||
|
@@ -74,21 +73,33 @@ library RLPReader { | |
} | ||
|
||
/* | ||
* @param item RLP encoded bytes | ||
* @param the RLP item. | ||
*/ | ||
function rlpLen(RLPItem memory item) internal pure returns (uint256) { | ||
return item.len; | ||
} | ||
|
||
/* | ||
* @param item RLP encoded bytes | ||
* @param the RLP item. | ||
* @return (memPtr, len) pair: location of the item's payload in memory. | ||
*/ | ||
function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) { | ||
uint256 offset = _payloadOffset(item.memPtr); | ||
uint256 memPtr = item.memPtr + offset; | ||
uint256 len = item.len - offset; // data length | ||
return (memPtr, len); | ||
} | ||
|
||
/* | ||
* @param the RLP item. | ||
*/ | ||
function payloadLen(RLPItem memory item) internal pure returns (uint256) { | ||
return item.len - _payloadOffset(item.memPtr); | ||
(, uint256 len) = payloadLocation(item); | ||
return len; | ||
} | ||
|
||
/* | ||
* @param item RLP encoded list in bytes | ||
* @param the RLP item containing the encoded list. | ||
*/ | ||
function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) { | ||
require(isList(item)); | ||
|
@@ -135,13 +146,6 @@ library RLPReader { | |
return result; | ||
} | ||
|
||
function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) { | ||
uint256 offset = _payloadOffset(item.memPtr); | ||
uint256 memPtr = item.memPtr + offset; | ||
uint256 len = item.len - offset; // data length | ||
return (memPtr, len); | ||
} | ||
|
||
/* | ||
* @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory. | ||
* @return keccak256 hash of the item payload. | ||
|
@@ -171,7 +175,7 @@ library RLPReader { | |
return result; | ||
} | ||
|
||
// any non-zero byte < 128 is considered true | ||
// any non-zero byte except "0x80" is considered true | ||
function toBoolean(RLPItem memory item) internal pure returns (bool) { | ||
require(item.len == 1); | ||
uint256 result; | ||
|
@@ -180,7 +184,15 @@ library RLPReader { | |
result := byte(0, mload(memPtr)) | ||
} | ||
|
||
return result == 0 ? false : true; | ||
// SEE Github Issue #5. | ||
// Summary: Most commonly used RLP libraries (i.e Geth) will encode | ||
// "0" as "0x80" instead of as "0". We handle this edge case explicitly | ||
// here. | ||
if (result == 0 || result == STRING_SHORT_START) { | ||
return false; | ||
} else { | ||
return true; | ||
} | ||
} | ||
|
||
function toAddress(RLPItem memory item) internal pure returns (address) { | ||
|
@@ -193,11 +205,9 @@ library RLPReader { | |
function toUint(RLPItem memory item) internal pure returns (uint256) { | ||
require(item.len > 0 && item.len <= 33); | ||
|
||
uint256 offset = _payloadOffset(item.memPtr); | ||
uint256 len = item.len - offset; | ||
(uint256 memPtr, uint256 len) = payloadLocation(item); | ||
|
||
uint256 result; | ||
uint256 memPtr = item.memPtr + offset; | ||
assembly { | ||
result := mload(memPtr) | ||
|
||
|
@@ -227,16 +237,15 @@ library RLPReader { | |
function toBytes(RLPItem memory item) internal pure returns (bytes memory) { | ||
require(item.len > 0); | ||
|
||
uint256 offset = _payloadOffset(item.memPtr); | ||
uint256 len = item.len - offset; // data length | ||
(uint256 memPtr, uint256 len) = payloadLocation(item); | ||
bytes memory result = new bytes(len); | ||
|
||
uint256 destPtr; | ||
assembly { | ||
destPtr := add(0x20, result) | ||
} | ||
|
||
copy(item.memPtr + offset, destPtr, len); | ||
copy(memPtr, destPtr, len); | ||
return result; | ||
} | ||
|
||
|
@@ -267,12 +276,15 @@ library RLPReader { | |
byte0 := byte(0, mload(memPtr)) | ||
} | ||
|
||
if (byte0 < STRING_SHORT_START) itemLen = 1; | ||
else if (byte0 < STRING_LONG_START) itemLen = byte0 - STRING_SHORT_START + 1; | ||
else if (byte0 < LIST_SHORT_START) { | ||
if (byte0 < STRING_SHORT_START) { | ||
itemLen = 1; | ||
} else if (byte0 < STRING_LONG_START) { | ||
itemLen = 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 | ||
itemLen := add(dataLen, add(byteLen, 1)) | ||
|
@@ -299,12 +311,16 @@ library RLPReader { | |
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) | ||
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; | ||
} else { | ||
return byte0 - (LIST_LONG_START - 1) + 1; | ||
} | ||
} | ||
|
||
/* | ||
|
@@ -325,15 +341,14 @@ library RLPReader { | |
dest += WORD_SIZE; | ||
} | ||
|
||
if (len == 0) return; | ||
|
||
// 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)) | ||
if (len > 0) { | ||
// 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)) | ||
} | ||
} | ||
} | ||
} |