Skip to content

Commit

Permalink
fix: check data.length and pendingWordLen (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
obatirou authored Nov 25, 2024
1 parent 2aaf540 commit 876daa0
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 21 deletions.
40 changes: 23 additions & 17 deletions src/CairoLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ pragma solidity >=0.8.0 <0.9.0;

library CairoLib {
/// @dev The Cairo precompile contract's address.
address constant CAIRO_MULTICALL_PRECOMPILE= 0x0000000000000000000000000000000000075003;
address constant CAIRO_CALL_PRECOMPILE= 0x0000000000000000000000000000000000075004;
address constant CAIRO_MULTICALL_PRECOMPILE = 0x0000000000000000000000000000000000075003;
address constant CAIRO_CALL_PRECOMPILE = 0x0000000000000000000000000000000000075004;

struct CairoCall {
uint256 contractAddress;
Expand All @@ -18,7 +18,10 @@ library CairoLib {
/// @param functionSelector The function selector of the Cairo contract function to be called.
/// @param data The input data for the Cairo contract function.
/// @return The return data from the Cairo contract function.
function callCairo(uint256 contractAddress, uint256 functionSelector, uint256[] memory data) internal returns (bytes memory) {
function callCairo(uint256 contractAddress, uint256 functionSelector, uint256[] memory data)
internal
returns (bytes memory)
{
bytes memory callData = abi.encode(contractAddress, functionSelector, data);

(bool success, bytes memory result) = CAIRO_CALL_PRECOMPILE.call(callData);
Expand All @@ -27,10 +30,7 @@ library CairoLib {
return result;
}

function callCairo(CairoCall memory call)
internal
returns (bytes memory)
{
function callCairo(CairoCall memory call) internal returns (bytes memory) {
return callCairo(call.contractAddress, call.functionSelector, call.data);
}

Expand All @@ -45,7 +45,10 @@ library CairoLib {
return callCairo(contractAddress, functionSelector, data);
}

function callCairo(uint256 contractAddress, string memory functionName, uint256[] memory data) internal returns (bytes memory) {
function callCairo(uint256 contractAddress, string memory functionName, uint256[] memory data)
internal
returns (bytes memory)
{
uint256 functionSelector = uint256(keccak256(bytes(functionName))) % 2 ** 250;
return callCairo(contractAddress, functionSelector, data);
}
Expand All @@ -56,7 +59,11 @@ library CairoLib {
/// @param functionSelector The function selector of the Cairo contract function to be called.
/// @param data The input data for the Cairo contract function.
/// @return The return data from the Cairo contract function.
function staticcallCairo(uint256 contractAddress, uint256 functionSelector, uint256[] memory data) internal view returns (bytes memory) {
function staticcallCairo(uint256 contractAddress, uint256 functionSelector, uint256[] memory data)
internal
view
returns (bytes memory)
{
bytes memory callData = abi.encode(contractAddress, functionSelector, data);

(bool success, bytes memory result) = CAIRO_CALL_PRECOMPILE.staticcall(callData);
Expand Down Expand Up @@ -84,15 +91,10 @@ library CairoLib {
return staticcallCairo(contractAddress, functionSelector, data);
}

function staticcallCairo(CairoCall memory call)
internal
view
returns (bytes memory)
{
function staticcallCairo(CairoCall memory call) internal view returns (bytes memory) {
return staticcallCairo(call.contractAddress, call.functionSelector, call.data);
}


/// @notice Performs a multicall to Cairo contracts deployed on Starknet.
/// @dev Used with intent to modify the state of the Cairo contract.
/// @param calls The array of CairoCall structs to be executed.
Expand Down Expand Up @@ -136,6 +138,8 @@ library CairoLib {
*/
/// @param data The Cairo representation of the ByteArray serialized to bytes.
function byteArrayToString(bytes memory data) internal pure returns (string memory) {
// It must be at least 96 bytes long (fullWordsLength + pendingWord + pendingWordLen)
// It can be more if fullWordsLength is greater than 0 and fullWords list is not empty
require(data.length >= 96, "Invalid byte array length");

uint256 fullWordsLength;
Expand All @@ -151,8 +155,10 @@ library CairoLib {
pendingWord := mload(pendingWordPtr)
pendingWordLen := mload(add(pendingWordPtr, 32))
}

require(pendingWordLen <= 31, "Invalid pending word length");
// Calculate the expected length of data
uint256 expectedLength = 96 + (fullWordsLength * 32);
require(data.length == expectedLength, "Data length does not match fullWordsLength");
require(pendingWordLen < 31, "Invalid pending word length");

uint256 totalLength = fullWordsLength * 31 + pendingWordLen;
bytes memory result = new bytes(totalLength);
Expand Down
50 changes: 46 additions & 4 deletions test/CairoLib.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,32 @@ contract ByteArrayConverterTest is Test {
assertEq(result, "", "Conversion failed for empty string");
}

function testInvalidPendingWordLength() public {
function testInvalidPendingWordLength(uint256 pendingWordLen) public {
vm.assume(pendingWordLen > 30);
bytes memory input = abi.encodePacked(
uint256(0),
uint256(0),
uint256(32) // Invalid pendingWordLen
uint256(pendingWordLen) // Invalid pendingWordLen
);

vm.expectRevert("Invalid pending word length");
CairoLib.byteArrayToString(input);
}

function testInvalidInputLength() public {
bytes memory input = new bytes(95); // Too short to be valid
function testInvalidPendingWordLength31Bytes() public {
bytes memory input = abi.encodePacked(
uint256(0),
uint256(0x4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e), // 31 byte
uint256(31) // Invalid pendingWordLen, because maximum 30 is allowed
);

vm.expectRevert("Invalid pending word length");
CairoLib.byteArrayToString(input);
}

function testInvalidInputLength(uint256 inputLenght) public {
vm.assume(inputLenght < 96);
bytes memory input = new bytes(inputLenght); // Too short to be valid

vm.expectRevert("Invalid byte array length");
CairoLib.byteArrayToString(input);
Expand All @@ -82,4 +95,33 @@ contract ByteArrayConverterTest is Test {
string memory result = CairoLib.byteArrayToString(input);
assertEq(result, "MyToken");
}

function testInvalidFullWordsLength(uint256 fullWordsLength) public {
fullWordsLength = bound(fullWordsLength, 2, 200);
bytes memory data = abi.encodePacked(
uint256(fullWordsLength), // Incorrect fullWordsLength
uint256(0x48656c6c6f20576f726c642c20746869732069732061206c6f6e6765722073), // "Hello World, this is a longer s"
uint256(0x7472696e672e), // "tring."
uint256(6) // pendingWordLen
);

vm.expectRevert("Data length does not match fullWordsLength");
CairoLib.byteArrayToString(data);
}

function testInvalidMultipleFullWordsLength(uint256 fullWordsLength) public {
fullWordsLength = bound(fullWordsLength, 0, 200);
vm.assume(fullWordsLength != 3);
bytes memory data = abi.encodePacked(
uint256(fullWordsLength),
uint256(0x4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e),
uint256(0x73656374657475722061646970697363696e6720656c69742c207365642064),
uint256(0x6f20656975736d6f642074656d706f7220696e6369646964756e7420757420),
uint256(0x6c61626f726520657420646f6c6f7265206d61676e6120616c697175612e),
uint256(30)
);

vm.expectRevert("Data length does not match fullWordsLength");
CairoLib.byteArrayToString(data);
}
}

0 comments on commit 876daa0

Please sign in to comment.