Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ out

# VIM
*.swp
foundry.lock
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Additional FVM support can be found in the [filecoin-solidity library](https://g

| Supported | Name | Address |
| :-------: | :--- | :------ |
| | ResolveAddress | `0xfe00000000000000000000000000000000000001` |
| | ResolveAddress | `0xfe00000000000000000000000000000000000001` |
| ❌ | LookupDelegatedAddress | `0xfe00000000000000000000000000000000000002` |
| ✅ | CallActorByAddress | `0xfe00000000000000000000000000000000000003` |
| ✅ | CallActorById | `0xfe00000000000000000000000000000000000005` |
Expand Down
22 changes: 22 additions & 0 deletions src/FVMAddress.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
pragma solidity ^0.8.30;

import {RESOLVE_ADDRESS} from "./FVMPrecompiles.sol";

library FVMAddress {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Because this overlaps with #3, I think it makes sense for library FVMAddress to be one pull request.

function resolveAddress(bytes memory filAddress) internal view returns (bool success, uint64 actorId) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we should be returning exists rather than success. success is uninteresting because it only fails if the address is invalid. should revert in that case.

bytes memory result;
(success, result) = address(RESOLVE_ADDRESS).staticcall(filAddress);
Copy link
Collaborator

Choose a reason for hiding this comment

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

RESOLVE_ADDRESS is already an address and doesn't need to be cast to address


if (success && result.length == 32) {
actorId = abi.decode(result, (uint64));
}
}

function toActorId(bytes memory filAddress) internal view returns (uint64 actorId) {
(bool success, uint64 id) = resolveAddress(filAddress);
require(success, "FVMAddress: Invalid address format");
require(id != 0, "FVMAddress: Actor not found");
Copy link
Collaborator

Choose a reason for hiding this comment

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

0 is a valid ID. It is the id of the system actor.

return id;
}
}
27 changes: 12 additions & 15 deletions src/FVMPay.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,10 @@ library FVMPay {
mstore(add(160, fmp), 192) // address offset
mstore(add(214, fmp), or(0x040a0000000000000000000000000000000000000000, to)) // address
mstore(add(192, fmp), 22) // address size
success :=
and(
and(gt(returndatasize(), 31), eq(mload(fmp), EXIT_SUCCESS)),
delegatecall(gas(), CALL_ACTOR_BY_ADDRESS, fmp, 256, fmp, 256)
)
success := and(
and(gt(returndatasize(), 31), eq(mload(fmp), EXIT_SUCCESS)),
delegatecall(gas(), CALL_ACTOR_BY_ADDRESS, fmp, 256, fmp, 256)
)
}
}

Expand All @@ -45,11 +44,10 @@ library FVMPay {
mstore(add(96, fmp), EMPTY_CODEC) // codec
mstore(add(128, fmp), 0) // params
mstore(add(160, fmp), actorId) // actor ID
success :=
and(
and(gt(returndatasize(), 31), eq(mload(fmp), EXIT_SUCCESS)),
delegatecall(gas(), CALL_ACTOR_BY_ID, fmp, 192, fmp, 192)
)
success := and(
and(gt(returndatasize(), 31), eq(mload(fmp), EXIT_SUCCESS)),
delegatecall(gas(), CALL_ACTOR_BY_ID, fmp, 192, fmp, 192)
)
}
}

Expand All @@ -65,11 +63,10 @@ library FVMPay {
mstore(add(96, fmp), EMPTY_CODEC) // codec
mstore(add(128, fmp), 0) // params
mstore(add(160, fmp), BURN_ACTOR_ID) // actor ID
success :=
and(
and(gt(returndatasize(), 31), eq(mload(fmp), EXIT_SUCCESS)),
delegatecall(gas(), CALL_ACTOR_BY_ID, fmp, 192, fmp, 192)
)
success := and(
and(gt(returndatasize(), 31), eq(mload(fmp), EXIT_SUCCESS)),
delegatecall(gas(), CALL_ACTOR_BY_ID, fmp, 192, fmp, 192)
)
}
}
}
29 changes: 29 additions & 0 deletions src/mocks/FVMResolveAddress.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
pragma solidity ^0.8.30;

contract FVMResolveAddress {
mapping(bytes32 => uint64) public addressMocks;
Copy link
Collaborator

Choose a reason for hiding this comment

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

mockActorIds

mapping(bytes32 => bool) public addressExists;

function mockResolveAddress(bytes memory filAddress, uint64 actorId) external {
Copy link
Collaborator

Choose a reason for hiding this comment

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

also provide a helper for mocking address that converts the address to f4

bytes32 hash = keccak256(filAddress);
addressMocks[hash] = actorId;
addressExists[hash] = true;
}

fallback() external {
bytes32 addressHash = keccak256(msg.data);

if (addressExists[addressHash]) {
uint64 actorId = addressMocks[addressHash];
assembly ("memory-safe") {
mstore(0, actorId)
return(0, 32)
}
}

assembly ("memory-safe") {
return(0, 0)
}
}
}
6 changes: 4 additions & 2 deletions src/mocks/MockFVMTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@ pragma solidity ^0.8.30;

import {Test} from "forge-std/Test.sol";

import {CALL_ACTOR_BY_ADDRESS, CALL_ACTOR_BY_ID, GET_BEACON_RANDOMNESS} from "../FVMPrecompiles.sol";
import {CALL_ACTOR_BY_ADDRESS, CALL_ACTOR_BY_ID, GET_BEACON_RANDOMNESS, RESOLVE_ADDRESS} from "../FVMPrecompiles.sol";
import {FVMCallActorByAddress} from "./FVMCallActorByAddress.sol";
import {FVMCallActorById} from "./FVMCallActorById.sol";
import {FVMGetBeaconRandomness} from "./FVMGetBeaconRandomness.sol";
import {FVMResolveAddress} from "./FVMResolveAddress.sol";

/// @notice Mocks the FVM precompiles for forge test
Copy link
Collaborator

Choose a reason for hiding this comment

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

I want to keep this. Why do you remove it?

contract MockFVMTest is Test {
FVMGetBeaconRandomness public constant RANDOMNESS_PRECOMPILE = FVMGetBeaconRandomness(GET_BEACON_RANDOMNESS);
FVMResolveAddress public constant RESOLVE_ADDRESS_PRECOMPILE = FVMResolveAddress(RESOLVE_ADDRESS);

function setUp() public virtual {
vm.etch(CALL_ACTOR_BY_ADDRESS, address(new FVMCallActorByAddress()).code);
vm.etch(CALL_ACTOR_BY_ID, address(new FVMCallActorById()).code);
vm.etch(GET_BEACON_RANDOMNESS, address(new FVMGetBeaconRandomness()).code);
vm.etch(RESOLVE_ADDRESS, address(new FVMResolveAddress()).code);
}
}
68 changes: 68 additions & 0 deletions test/Address.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
pragma solidity ^0.8.30;

import {MockFVMTest} from "../src/mocks/MockFVMTest.sol";
import {FVMAddress} from "../src/FVMAddress.sol";

contract AddressTest is MockFVMTest {
using FVMAddress for bytes;

function testResolveAddressNotFound() public view {
bytes memory fakeAddress = hex"0102030405060708090a0b0c0d0e0f10";
(bool success, uint64 actorId) = FVMAddress.resolveAddress(fakeAddress);
assertTrue(success);
assertEq(actorId, 0);
}

function testResolveAddressMocked() public {
bytes memory testAddress = hex"01234567";
uint64 expectedActorId = 12345;

RESOLVE_ADDRESS_PRECOMPILE.mockResolveAddress(testAddress, expectedActorId);

(bool success, uint64 actorId) = FVMAddress.resolveAddress(testAddress);
assertTrue(success);
assertEq(actorId, expectedActorId);
}

function testToActorId() public {
bytes memory testAddress = hex"0123456789abcdef";
uint64 expectedActorId = 99999;

RESOLVE_ADDRESS_PRECOMPILE.mockResolveAddress(testAddress, expectedActorId);

uint64 actorId = testAddress.toActorId();
assertEq(actorId, expectedActorId);
}

function testMultipleAddressResolutions() public {
bytes memory addr1 = hex"aabbccdd";
bytes memory addr2 = hex"11223344";
uint64 actorId1 = 1111;
uint64 actorId2 = 2222;

RESOLVE_ADDRESS_PRECOMPILE.mockResolveAddress(addr1, actorId1);
RESOLVE_ADDRESS_PRECOMPILE.mockResolveAddress(addr2, actorId2);

assertEq(addr1.toActorId(), actorId1);
assertEq(addr2.toActorId(), actorId2);
}

function testDifferentAddressFormats() public {
bytes memory shortAddr = hex"01";
bytes memory longAddr = hex"0123456789abcdef0123456789abcdef";
uint64 shortActorId = 100;
uint64 longActorId = 200;

RESOLVE_ADDRESS_PRECOMPILE.mockResolveAddress(shortAddr, shortActorId);
RESOLVE_ADDRESS_PRECOMPILE.mockResolveAddress(longAddr, longActorId);

(bool success1, uint64 actorId1) = FVMAddress.resolveAddress(shortAddr);
(bool success2, uint64 actorId2) = FVMAddress.resolveAddress(longAddr);

assertTrue(success1);
assertTrue(success2);
assertEq(actorId1, shortActorId);
assertEq(actorId2, longActorId);
}
}