mirror of
https://github.com/status-im/snt-voting.git
synced 2025-02-24 16:18:40 +00:00
217 lines
6.3 KiB
Solidity
217 lines
6.3 KiB
Solidity
|
/*
|
||
|
* @author Hamdi Allam hamdi.allam97@gmail.com
|
||
|
* Please reach our for any questions/concerns
|
||
|
*/
|
||
|
pragma solidity ^0.4.24;
|
||
|
|
||
|
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 {
|
||
|
uint len;
|
||
|
uint memPtr;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* @param item RLP encoded bytes
|
||
|
*/
|
||
|
function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) {
|
||
|
if (item.length == 0)
|
||
|
return RLPItem(0, 0);
|
||
|
|
||
|
uint 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));
|
||
|
|
||
|
uint items = numItems(item);
|
||
|
result = new RLPItem[](items);
|
||
|
|
||
|
uint memPtr = item.memPtr + _payloadOffset(item.memPtr);
|
||
|
uint dataLen;
|
||
|
for (uint 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;
|
||
|
uint 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 (uint) {
|
||
|
uint count = 0;
|
||
|
uint currPtr = item.memPtr + _payloadOffset(item.memPtr);
|
||
|
uint 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(uint memPtr) internal pure returns (uint len) {
|
||
|
uint 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(uint memPtr) internal pure returns (uint) {
|
||
|
uint 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 **/
|
||
|
|
||
|
function toBoolean(RLPItem memory item) internal pure returns (bool) {
|
||
|
require(item.len == 1, "Invalid RLPItem. Booleans are encoded in 1 byte");
|
||
|
uint result;
|
||
|
uint 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");
|
||
|
|
||
|
uint memPtr = item.memPtr + 1; // skip the length prefix
|
||
|
uint addr;
|
||
|
assembly {
|
||
|
addr := div(mload(memPtr), exp(256, 12)) // right shift 12 bytes. we want the most significant 20 bytes
|
||
|
}
|
||
|
|
||
|
return address(addr);
|
||
|
}
|
||
|
|
||
|
function toUint(RLPItem memory item) internal pure returns (uint) {
|
||
|
uint offset = _payloadOffset(item.memPtr);
|
||
|
uint len = item.len - offset;
|
||
|
uint memPtr = item.memPtr + offset;
|
||
|
|
||
|
uint 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) {
|
||
|
uint offset = _payloadOffset(item.memPtr);
|
||
|
uint len = item.len - offset; // data length
|
||
|
bytes memory result = new bytes(len);
|
||
|
|
||
|
uint 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(uint src, uint dest, uint 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
|
||
|
uint 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))
|
||
|
}
|
||
|
}
|
||
|
}
|