Skip to content
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
2 changes: 1 addition & 1 deletion artifacts/erc20_deployment.hex

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion artifacts/erc20_runtime.hex

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion artifacts/native_deployment.hex

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion artifacts/native_runtime.hex

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion out/EscrowERC20.sol/EscrowERC20.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion out/EscrowNative.sol/EscrowNative.json

Large diffs are not rendered by default.

114 changes: 12 additions & 102 deletions src/BlockHeaderParser.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ library BlockHeaderParser {

error InvalidRLPList();
error InvalidRLPEncoding();
error RLPOffsetOutOfBounds();
error ExpectedStringItem();

/**
* @dev Get offset to inner header (skips Tempo wrapper if present)
Expand Down Expand Up @@ -75,15 +73,18 @@ library BlockHeaderParser {
}
}

// Extract block number
(bytes memory numBytes,) = parseItemFromCalldata(blockHeader, offset);

// Decode big-endian number
uint256 blockNumber = 0;
for (uint256 i = 0; i < numBytes.length;) {
blockNumber = (blockNumber << 8) | uint8(numBytes[i]);
unchecked {
++i;
// Extract block number directly from calldata
uint8 prefix = uint8(blockHeader[offset]);
uint256 blockNumber;
if (prefix < 0x80) {
blockNumber = prefix;
} else {
uint256 len = prefix - 0x80;
for (uint256 i = 0; i < len;) {
blockNumber = (blockNumber << 8) | uint8(blockHeader[offset + 1 + i]);
unchecked {
++i;
}
}
}

Expand Down Expand Up @@ -119,97 +120,6 @@ library BlockHeaderParser {
return receiptsRoot;
}

/**
* @dev Parse RLP item from calldata (helper function)
* @param data Calldata containing RLP item
* @param offset Current offset in the data
* @return result Parsed item content
* @return length Total length consumed
*/
function parseItemFromCalldata(bytes calldata data, uint256 offset)
private
pure
returns (bytes memory result, uint256 length)
{
if (offset >= data.length) revert RLPOffsetOutOfBounds();

uint8 prefix = uint8(data[offset]);

if (prefix < 0x80) {
// Single byte
result = new bytes(1);
result[0] = bytes1(prefix);
return (result, 1);
} else if (prefix < 0xb8) {
// Short string
uint256 itemLength = prefix - 0x80;
result = new bytes(itemLength);
for (uint256 i = 0; i < itemLength;) {
result[i] = data[offset + 1 + i];
unchecked {
++i;
}
}
return (result, 1 + itemLength);
} else if (prefix < 0xc0) {
// Long string
uint256 lengthBytes = prefix - 0xb7;
uint256 itemLength = 0;
for (uint256 i = 0; i < lengthBytes;) {
itemLength = (itemLength << 8) | uint8(data[offset + 1 + i]);
unchecked {
++i;
}
}
result = new bytes(itemLength);
for (uint256 i = 0; i < itemLength;) {
result[i] = data[offset + 1 + lengthBytes + i];
unchecked {
++i;
}
}
return (result, 1 + lengthBytes + itemLength);
} else {
revert ExpectedStringItem();
}
}

/**
* @dev Extract state root from block header
* @param blockHeader RLP-encoded block header
* @return State root hash
*/
function extractStateRoot(bytes calldata blockHeader) internal pure returns (bytes32) {
uint256 offset = 0;

// Skip RLP list prefix
if (blockHeader[offset] < 0xc0) revert InvalidRLPList();
if (blockHeader[offset] >= 0xf8) {
offset += 1 + (uint8(blockHeader[offset]) - 0xf7);
} else {
offset += 1;
}

// Skip first 3 fields to get to stateRoot (index 3)
for (uint256 i = 0; i < 3;) {
offset = blockHeader.skipItem(offset);
unchecked {
++i;
}
}

// Extract stateRoot (32 bytes)
if (blockHeader[offset] != 0xa0) revert InvalidRLPEncoding();
offset += 1;

bytes32 stateRoot;
assembly {
stateRoot := calldataload(add(blockHeader.offset, offset))
}

return stateRoot;
}

/**
* @dev Extract transactions root from block header
* @param blockHeader RLP-encoded block header
Expand Down
122 changes: 20 additions & 102 deletions src/ReceiptValidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ library ReceiptValidator {
error WrongEventSignature();
error ToAddressMismatch();
error AmountMismatch();
error ExpectedStringData();
error ReceiptStatusNotSuccess();
error UnsupportedTxType();
error RecipientMismatch();
Expand Down Expand Up @@ -111,52 +110,18 @@ library ReceiptValidator {
}

// Parse emitter address (should be the token contract)
(bytes memory addrBytes, uint256 addrLen) = parseAddressFromRLP(receiptRlp, offset);
if (addrBytes.length != 20) revert InvalidAddress();

// Extract emitter address
if (uint8(receiptRlp[offset]) != 0x94) revert InvalidAddress();
address emitter;
assembly {
emitter := mload(add(addrBytes, 20))
emitter := shr(96, calldataload(add(receiptRlp.offset, add(offset, 1))))
}
if (emitter != tokenContract) revert WrongTokenContract();
offset += addrLen;
offset += 21;

// Parse and validate topics
return validateTransferTopics(receiptRlp, offset, toAddress, expectedAmount);
}

/**
* @dev Parse address from RLP data
* @param data RLP encoded data
* @param offset Current offset
* @return result Parsed address bytes
* @return length Length consumed
*/
function parseAddressFromRLP(bytes calldata data, uint256 offset)
private
pure
returns (bytes memory result, uint256 length)
{
if (offset >= data.length) revert InvalidRLP();

uint8 prefix = uint8(data[offset]);

if (prefix == 0x94) {
// Address is 20 bytes with prefix 0x94
result = new bytes(20);
for (uint256 i = 0; i < 20;) {
result[i] = data[offset + 1 + i];
unchecked {
++i;
}
}
return (result, 21);
} else {
revert InvalidAddress();
}
}

/**
* @dev Validate event topics for Transfer event
* @param receiptRlp The receipt data
Expand Down Expand Up @@ -195,77 +160,30 @@ library ReceiptValidator {

// Parse and validate data payload (amount)
offset = receiptRlp.skipItem(topicsOffset); // Skip entire topics array
(bytes memory dataBytes,) = parseDataFromRLP(receiptRlp, offset);

// Convert data bytes to uint256 (amount)
if (dataBytes.length > 32) revert InvalidRLP();
uint256 logAmount = 0;
for (uint256 i = 0; i < dataBytes.length;) {
logAmount = (logAmount << 8) | uint8(dataBytes[i]);
unchecked {
++i;
uint256 logAmount;
{
uint8 dataPrefix = uint8(receiptRlp[offset]);
if (dataPrefix < 0x80) {
logAmount = dataPrefix;
} else if (dataPrefix == 0x80) {
logAmount = 0;
} else if (dataPrefix <= 0xa0) {
uint256 len = dataPrefix - 0x80;
for (uint256 i = 0; i < len;) {
logAmount = (logAmount << 8) | uint8(receiptRlp[offset + 1 + i]);
unchecked {
++i;
}
}
} else {
revert InvalidRLP();
}
}
if (logAmount != expectedAmount) revert AmountMismatch();

return true;
}

/**
* @dev Parse data field from RLP
* @param data RLP encoded data
* @param offset Current offset
* @return result Parsed data bytes
* @return length Length consumed
*/
function parseDataFromRLP(bytes calldata data, uint256 offset)
private
pure
returns (bytes memory result, uint256 length)
{
if (offset >= data.length) revert InvalidRLP();

uint8 prefix = uint8(data[offset]);

if (prefix < 0x80) {
// Single byte
result = new bytes(1);
result[0] = bytes1(prefix);
return (result, 1);
} else if (prefix < 0xb8) {
// Short string
uint256 dataLength = prefix - 0x80;
result = new bytes(dataLength);
for (uint256 i = 0; i < dataLength;) {
result[i] = data[offset + 1 + i];
unchecked {
++i;
}
}
return (result, 1 + dataLength);
} else if (prefix < 0xc0) {
// Long string
uint256 lengthBytes = prefix - 0xb7;
uint256 dataLength = 0;
for (uint256 i = 0; i < lengthBytes;) {
dataLength = (dataLength << 8) | uint8(data[offset + 1 + i]);
unchecked {
++i;
}
}
result = new bytes(dataLength);
for (uint256 i = 0; i < dataLength;) {
result[i] = data[offset + 1 + lengthBytes + i];
unchecked {
++i;
}
}
return (result, 1 + lengthBytes + dataLength);
} else {
revert ExpectedStringData();
}
}

/**
* @dev Validate receipt status == 1 (successful execution)
* Receipt structure: [status, cumulativeGasUsed, logsBloom, logs]
Expand Down