Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MessageHarness #30

Merged
merged 2 commits into from
Jun 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 56 additions & 42 deletions packages/contracts/contracts/libs/TypedMemView.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.10 <0.8.14;

import { SafeMath } from "@openzeppelin/contracts/utils/math/SafeMath.sol";
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.12;

library TypedMemView {
using SafeMath for uint256;

// Why does this exist?
// the solidity `bytes memory` type has a few weaknesses.
// 1. You can't index ranges effectively
Expand Down Expand Up @@ -147,21 +143,27 @@ library TypedMemView {
* @return second - The bottom 16 bytes
*/
function encodeHex(uint256 _b) internal pure returns (uint256 first, uint256 second) {
for (uint8 i = 31; i > 15; i -= 1) {
for (uint8 i = 31; i > 15; ) {
uint8 _byte = uint8(_b >> (i * 8));
first |= byteHex(_byte);
if (i != 16) {
first <<= 16;
}
unchecked {
i -= 1;
}
}

// abusing underflow here =_=
for (uint8 i = 15; i < 255; i -= 1) {
for (uint8 i = 15; i < 255; ) {
uint8 _byte = uint8(_b >> (i * 8));
second |= byteHex(_byte);
if (i != 0) {
second <<= 16;
}
unchecked {
i -= 1;
}
}
}

Expand Down Expand Up @@ -202,7 +204,7 @@ library TypedMemView {
function leftMask(uint8 _len) private pure returns (uint256 mask) {
// ugly. redo without assembly?
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
mask := sar(
sub(_len, 1),
0x8000000000000000000000000000000000000000000000000000000000000000
Expand Down Expand Up @@ -248,7 +250,7 @@ library TypedMemView {
}
uint256 _end = end(memView);
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
ret := not(gt(_end, mload(0x40)))
}
}
Expand Down Expand Up @@ -307,7 +309,7 @@ library TypedMemView {
function castTo(bytes29 memView, uint40 _newType) internal pure returns (bytes29 newView) {
// then | in the new type
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
// shift off the top 5 bytes
newView := or(newView, shr(40, shl(40, memView)))
newView := or(newView, shl(216, _newType))
Expand All @@ -330,7 +332,7 @@ library TypedMemView {
uint256 _len
) private pure returns (bytes29 newView) {
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
newView := shl(96, or(newView, _type)) // insert type
newView := shl(96, or(newView, _loc)) // insert loc
newView := shl(24, or(newView, _len)) // empty bottom 3 bytes
Expand All @@ -352,9 +354,9 @@ library TypedMemView {
uint256 _loc,
uint256 _len
) internal pure returns (bytes29 newView) {
uint256 _end = _loc.add(_len);
uint256 _end = _loc + _len;
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
if gt(_end, mload(0x40)) {
_end := 0
}
Expand All @@ -378,7 +380,7 @@ library TypedMemView {

uint256 _loc;
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
_loc := add(arr, 0x20) // our view is of the data, not the struct
}

Expand All @@ -392,7 +394,7 @@ library TypedMemView {
*/
function typeOf(bytes29 memView) internal pure returns (uint40 _type) {
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
// 216 == 256 - 40
_type := shr(216, memView) // shift out lower 24 bytes
}
Expand All @@ -416,7 +418,7 @@ library TypedMemView {
function loc(bytes29 memView) internal pure returns (uint96 _loc) {
uint256 _mask = LOW_12_MASK; // assembly can't use globals
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
// 120 bits = 12 bytes (the encoded loc) + 3 bytes (empty low space)
_loc := and(shr(120, memView), _mask)
}
Expand All @@ -428,7 +430,7 @@ library TypedMemView {
* @return uint256 - The number of memory words
*/
function words(bytes29 memView) internal pure returns (uint256) {
return uint256(len(memView)).add(32) / 32;
return (uint256(len(memView)) + 32) / 32;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

len(memView) is uint96, so this never overflows, safe to uncheck it

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

footprint will never overflow as well

}

/**
Expand All @@ -448,7 +450,7 @@ library TypedMemView {
function len(bytes29 memView) internal pure returns (uint96 _len) {
uint256 _mask = LOW_12_MASK; // assembly can't use globals
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
_len := and(shr(24, memView), _mask)
}
}
Expand All @@ -459,7 +461,9 @@ library TypedMemView {
* @return uint256 - The endpoint of `memView`
*/
function end(bytes29 memView) internal pure returns (uint256) {
return loc(memView) + len(memView);
unchecked {
return loc(memView) + len(memView);
}
}

/**
Expand All @@ -479,11 +483,11 @@ library TypedMemView {
uint256 _loc = loc(memView);

// Ensure it doesn't overrun the view
if (_loc.add(_index).add(_len) > end(memView)) {
if (_loc + _index + _len > end(memView)) {
return NULL;
}

_loc = _loc.add(_index);
_loc = _loc + _index;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if _loc + _index + _len hasn't overflowed, _loc + _index will never as well. Safe to add unchecked here.

return build(newType, _loc, _len);
}

Expand Down Expand Up @@ -514,7 +518,7 @@ library TypedMemView {
uint256 _len,
uint40 newType
) internal pure returns (bytes29) {
return slice(memView, uint256(len(memView)).sub(_len), _len, newType);
return slice(memView, uint256(len(memView)) - _len, _len, newType);
}

/**
Expand Down Expand Up @@ -568,16 +572,19 @@ library TypedMemView {
if (_bytes == 0) {
return bytes32(0);
}
if (_index.add(_bytes) > len(memView)) {
if (_index + _bytes > len(memView)) {
revert(indexErrOverrun(loc(memView), len(memView), _index, uint256(_bytes)));
}
require(_bytes <= 32, "TypedMemView/index - Attempted to index more than 32 bytes");

uint8 bitLength = _bytes * 8;
uint8 bitLength;
unchecked {
bitLength = _bytes * 8;
}
uint256 _loc = loc(memView);
uint256 _mask = leftMask(bitLength);
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
result := and(mload(add(_loc, _index)), _mask)
}
}
Expand Down Expand Up @@ -633,7 +640,7 @@ library TypedMemView {
uint256 _loc = loc(memView);
uint256 _len = len(memView);
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
digest := keccak256(_loc, _len)
}
}
Expand All @@ -648,7 +655,7 @@ library TypedMemView {
uint256 _loc = loc(memView);
uint256 _len = len(memView);
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
let ptr := mload(0x40)
pop(staticcall(gas(), 2, _loc, _len, ptr, 0x20)) // sha2 #1
digest := mload(ptr)
Expand All @@ -664,7 +671,7 @@ library TypedMemView {
uint256 _loc = loc(memView);
uint256 _len = len(memView);
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
let ptr := mload(0x40)
pop(staticcall(gas(), 2, _loc, _len, ptr, 0x20)) // sha2
pop(staticcall(gas(), 3, ptr, 0x20, ptr, 0x20)) // rmd160
Expand All @@ -681,7 +688,7 @@ library TypedMemView {
uint256 _loc = loc(memView);
uint256 _len = len(memView);
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
let ptr := mload(0x40)
pop(staticcall(gas(), 2, _loc, _len, ptr, 0x20)) // sha2 #1
pop(staticcall(gas(), 2, ptr, 0x20, ptr, 0x20)) // sha2 #2
Expand Down Expand Up @@ -751,7 +758,7 @@ library TypedMemView {

uint256 ptr;
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
ptr := mload(0x40)
// revert if we're writing in occupied memory
if gt(ptr, _newLoc) {
Expand All @@ -777,13 +784,15 @@ library TypedMemView {
uint256 ptr;
uint256 _len = len(memView);
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
ptr := mload(0x40) // load unused memory pointer
ret := ptr
}
unsafeCopyTo(memView, ptr + 0x20);
unchecked {
unsafeCopyTo(memView, ptr + 0x20);
}
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
mstore(0x40, add(add(ptr, _len), 0x20)) // write new unused pointer
mstore(ptr, _len) // write len of new array (in bytes)
}
Expand All @@ -805,7 +814,7 @@ library TypedMemView {
returns (bytes29 unsafeView)
{
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
let ptr := mload(0x40)
// revert if we're writing in occupied memory
if gt(ptr, _location) {
Expand All @@ -816,8 +825,10 @@ library TypedMemView {
uint256 _offset = 0;
for (uint256 i = 0; i < memViews.length; i++) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++i should be moved to the unchecked part of the loop

bytes29 memView = memViews[i];
unsafeCopyTo(memView, _location + _offset);
_offset += len(memView);
unchecked {
unsafeCopyTo(memView, _location + _offset);
_offset += len(memView);
}
}
unsafeView = unsafeBuildUnchecked(0, _location, _offset);
}
Expand All @@ -830,7 +841,7 @@ library TypedMemView {
function joinKeccak(bytes29[] memory memViews) internal view returns (bytes32) {
uint256 ptr;
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
ptr := mload(0x40) // load unused memory pointer
}
return keccak(unsafeJoin(memViews, ptr));
Expand All @@ -844,7 +855,7 @@ library TypedMemView {
function joinSha2(bytes29[] memory memViews) internal view returns (bytes32) {
uint256 ptr;
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
ptr := mload(0x40) // load unused memory pointer
}
return sha2(unsafeJoin(memViews, ptr));
Expand All @@ -858,16 +869,19 @@ library TypedMemView {
function join(bytes29[] memory memViews) internal view returns (bytes memory ret) {
uint256 ptr;
assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
ptr := mload(0x40) // load unused memory pointer
}

bytes29 _newView = unsafeJoin(memViews, ptr + 0x20);
bytes29 _newView;
unchecked {
_newView = unsafeJoin(memViews, ptr + 0x20);
}
uint256 _written = len(_newView);
uint256 _footprint = footprint(_newView);

assembly {
// solium-disable-previous-line security/no-inline-assembly
// solhint-disable-previous-line no-inline-assembly
// store the legnth
mstore(ptr, _written)
// new pointer is old + 0x20 + the footprint of the body
Expand Down
74 changes: 74 additions & 0 deletions packages/contracts/test/Message.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.13;

import "forge-std/Test.sol";
import "forge-std/console2.sol";

import { MessageHarness } from "./harnesses/MessageHarness.sol";
import { TypedMemView } from "../contracts/libs/TypedMemView.sol";

contract MessageTest is Test {
MessageHarness messageHarness;
using TypedMemView for bytes;
using TypedMemView for bytes29;

uint32 originDomain;
bytes32 sender;
uint32 nonce;
uint32 destinationDomain;
bytes32 recipient;
bytes messageBody;

function setUp() public {
messageHarness = new MessageHarness();
originDomain = 1000;
sender = bytes32("AAAA THE SENDOOOOOR");
nonce = 0;
destinationDomain = 2000;
recipient = bytes32("AAAA THE RECEIVOOOR");
messageBody = bytes("Messagoooor");
}

function test_formatMessage() public {
bytes memory message = messageHarness.formatMessage(
originDomain,
sender,
nonce,
destinationDomain,
recipient,
messageBody
);

console2.log(messageHarness.origin(message));
assertEq(messageHarness.origin(message), originDomain);
assertEq(messageHarness.sender(message), sender);
assertEq(messageHarness.nonce(message), nonce);
assertEq(messageHarness.destination(message), destinationDomain);
assertEq(messageHarness.recipient(message), recipient);
assertEq(messageHarness.body(message), (messageBody));
assertEq(messageHarness.leaf(message), keccak256(message));
}

function test_messageHash() public {
bytes memory message = messageHarness.formatMessage(
originDomain,
sender,
nonce,
destinationDomain,
recipient,
messageBody
);

bytes32 messageHash = messageHarness.messageHash(
originDomain,
sender,
nonce,
destinationDomain,
recipient,
messageBody
);

assertEq(messageHash, keccak256(message));
}
}
Loading