Skip to content

Commit 967829f

Browse files
karlemcryptoAtwillcryptoAtwillraulk
authored
feat: support l2 plus (#1157)
Co-authored-by: cryptoAtwill <[email protected]> Co-authored-by: cryptoAtwill <[email protected]> Co-authored-by: raulk <[email protected]>
1 parent dafebd9 commit 967829f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+3363
-1170
lines changed

contracts/.storage-layouts/GatewayActorModifiers.json

Lines changed: 284 additions & 194 deletions
Large diffs are not rendered by default.

contracts/.storage-layouts/GatewayDiamond.json

Lines changed: 284 additions & 194 deletions
Large diffs are not rendered by default.

contracts/.storage-layouts/SubnetActorDiamond.json

Lines changed: 234 additions & 152 deletions
Large diffs are not rendered by default.

contracts/.storage-layouts/SubnetActorModifiers.json

Lines changed: 234 additions & 152 deletions
Large diffs are not rendered by default.

contracts/contracts/GatewayDiamond.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ import {BATCH_PERIOD, MAX_MSGS_PER_BATCH} from "./structs/CrossNet.sol";
1616

1717
error FunctionNotFound(bytes4 _functionSelector);
1818

19-
bool constant FEATURE_MULTILEVEL_CROSSMSG = false;
19+
bool constant FEATURE_MULTILEVEL_CROSSMSG = true;
2020
bool constant FEATURE_GENERAL_PUPRPOSE_CROSSMSG = true;
21-
uint8 constant FEATURE_SUBNET_DEPTH = 2;
21+
uint8 constant FEATURE_SUBNET_DEPTH = 10;
2222

2323
contract GatewayDiamond {
2424
GatewayActorStorage internal s;

contracts/contracts/errors/IPCErrors.sol

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ error AlreadyRegisteredSubnet();
66
error AlreadyInSet();
77
error CannotConfirmFutureChanges();
88
error CannotReleaseZero();
9-
error CannotSendCrossMsgToItself();
109
error CheckpointAlreadyExists();
1110
error BatchAlreadyExists();
1211
error MaxMsgsPerBatchExceeded();
@@ -90,7 +89,10 @@ enum InvalidXnetMessageReason {
9089
DstSubnet,
9190
Nonce,
9291
Value,
93-
Kind
92+
Kind,
93+
ReflexiveSend,
94+
NoRoute,
95+
IncompatibleSupplySource
9496
}
9597

9698
string constant ERR_PERMISSIONED_AND_BOOTSTRAPPED = "Method not allowed if permissioned is enabled and subnet bootstrapped";
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
pragma solidity ^0.8.23;
3+
4+
import {FvmAddress} from "../structs/FvmAddress.sol";
5+
import {SubnetID, IPCAddress} from "../structs/Subnet.sol";
6+
import {IpcEnvelope, IpcMsgKind, CallMsg, ResultMsg} from "../structs/CrossNet.sol";
7+
import {IGateway} from "../interfaces/IGateway.sol";
8+
import {SubnetIDHelper} from "../lib/SubnetIDHelper.sol";
9+
import {FvmAddressHelper} from "../lib/FvmAddressHelper.sol";
10+
import {EMPTY_BYTES, METHOD_SEND} from "../constants/Constants.sol";
11+
import {IpcExchange} from "../../sdk/IpcContract.sol";
12+
13+
interface ISubnetGetter {
14+
function ipcGatewayAddr() external view returns (address);
15+
function getParent() external view returns (SubnetID memory);
16+
}
17+
18+
/// This is a simple example contract to invoke cross messages between subnets from different levels
19+
contract CrossMessengerCaller is IpcExchange {
20+
event CallReceived(IPCAddress from, CallMsg msg);
21+
event ResultReceived(IpcEnvelope original, ResultMsg result);
22+
23+
uint256 public callsReceived;
24+
uint256 public resultsReceived;
25+
26+
constructor(address gatewayAddr_) IpcExchange(gatewayAddr_) {
27+
callsReceived = 0;
28+
resultsReceived = 0;
29+
}
30+
31+
function _handleIpcCall(
32+
IpcEnvelope memory envelope,
33+
CallMsg memory callMsg
34+
) internal override returns (bytes memory) {
35+
emit CallReceived(envelope.from, callMsg);
36+
callsReceived += 1;
37+
return EMPTY_BYTES;
38+
}
39+
40+
function _handleIpcResult(
41+
IpcEnvelope storage original,
42+
IpcEnvelope memory,
43+
ResultMsg memory resultMsg
44+
) internal override {
45+
resultsReceived += 1;
46+
emit ResultReceived(original, resultMsg);
47+
}
48+
49+
/// @dev Invoke a cross net send fund message from the current subnet to the target subnet
50+
function invokeSendMessage(SubnetID calldata targetSubnet, address recipient, uint256 value) external {
51+
IPCAddress memory to = IPCAddress({subnetId: targetSubnet, rawAddress: FvmAddressHelper.from(recipient)});
52+
CallMsg memory message = CallMsg({method: abi.encodePacked(METHOD_SEND), params: EMPTY_BYTES});
53+
invokeCrossMessage(to, message, value);
54+
}
55+
56+
function invokeCrossMessage(IPCAddress memory to, CallMsg memory callMsg, uint256 value) internal {
57+
// "sendContractXnetMessage" will handle the `from`
58+
IPCAddress memory from;
59+
60+
IpcEnvelope memory envelope = IpcEnvelope({
61+
kind: IpcMsgKind.Call,
62+
from: from,
63+
to: to,
64+
value: value,
65+
message: abi.encode(callMsg),
66+
originalNonce: 0,
67+
localNonce: 0
68+
});
69+
70+
IGateway(gatewayAddr).sendContractXnetMessage(envelope);
71+
}
72+
}

contracts/contracts/gateway/GatewayGetterFacet.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ contract GatewayGetterFacet {
139139
return (s.postbox[id]);
140140
}
141141

142+
function postboxMsgs() external view returns (bytes32[] memory) {
143+
return (s.postboxKeys.values());
144+
}
145+
142146
/// @notice Returns the majority percentage required for certain consensus or decision-making processes.
143147
function majorityPercentage() external view returns (uint64) {
144148
return s.majorityPercentage;

contracts/contracts/gateway/GatewayManagerFacet.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ contract GatewayManagerFacet is GatewayActorModifiers, ReentrancyGuard {
141141
revert InvalidXnetMessage(InvalidXnetMessageReason.Value);
142142
}
143143
// slither-disable-next-line unused-return
144-
(bool registered, ) = LibGateway.getSubnet(subnetId);
144+
(bool registered, Subnet storage subnet) = LibGateway.getSubnet(subnetId);
145145
if (!registered) {
146146
revert NotRegisteredSubnet();
147147
}
@@ -158,7 +158,7 @@ contract GatewayManagerFacet is GatewayActorModifiers, ReentrancyGuard {
158158
});
159159

160160
// commit top-down message.
161-
LibGateway.commitTopDownMsg(crossMsg);
161+
LibGateway.commitTopDownMsg(subnet, crossMsg);
162162
}
163163

164164
/// @notice Sends funds to a specified subnet receiver using ERC20 tokens.
@@ -174,7 +174,7 @@ contract GatewayManagerFacet is GatewayActorModifiers, ReentrancyGuard {
174174
revert InvalidXnetMessage(InvalidXnetMessageReason.Value);
175175
}
176176
// slither-disable-next-line unused-return
177-
(bool registered, ) = LibGateway.getSubnet(subnetId);
177+
(bool registered, Subnet storage subnet) = LibGateway.getSubnet(subnetId);
178178
if (!registered) {
179179
revert NotRegisteredSubnet();
180180
}
@@ -199,7 +199,7 @@ contract GatewayManagerFacet is GatewayActorModifiers, ReentrancyGuard {
199199
});
200200

201201
// Commit top-down message.
202-
LibGateway.commitTopDownMsg(crossMsg);
202+
LibGateway.commitTopDownMsg(subnet, crossMsg);
203203
}
204204

205205
/// @notice release() burns the received value locally in subnet and commits a bottom-up message to release the assets in the parent.

contracts/contracts/gateway/GatewayMessengerFacet.sol

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,41 @@ pragma solidity ^0.8.23;
44
import {GatewayActorModifiers} from "../lib/LibGatewayActorStorage.sol";
55
import {IpcEnvelope, CallMsg, IpcMsgKind} from "../structs/CrossNet.sol";
66
import {IPCMsgType} from "../enums/IPCMsgType.sol";
7-
import {SubnetID, AssetKind, IPCAddress} from "../structs/Subnet.sol";
8-
import {InvalidXnetMessage, InvalidXnetMessageReason, CannotSendCrossMsgToItself, MethodNotAllowed} from "../errors/IPCErrors.sol";
7+
import {Subnet, SubnetID, AssetKind, IPCAddress, Asset} from "../structs/Subnet.sol";
8+
import {InvalidXnetMessage, InvalidXnetMessageReason, MethodNotAllowed} from "../errors/IPCErrors.sol";
99
import {SubnetIDHelper} from "../lib/SubnetIDHelper.sol";
1010
import {LibGateway} from "../lib/LibGateway.sol";
1111
import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol";
1212
import {AssetHelper} from "../lib/AssetHelper.sol";
1313
import {CrossMsgHelper} from "../lib/CrossMsgHelper.sol";
1414
import {FvmAddressHelper} from "../lib/FvmAddressHelper.sol";
15+
import {ISubnetActor} from "../interfaces/ISubnetActor.sol";
16+
17+
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
1518

1619
string constant ERR_GENERAL_CROSS_MSG_DISABLED = "Support for general-purpose cross-net messages is disabled";
1720
string constant ERR_MULTILEVEL_CROSS_MSG_DISABLED = "Support for multi-level cross-net messages is disabled";
1821

1922
contract GatewayMessengerFacet is GatewayActorModifiers {
2023
using FilAddress for address payable;
2124
using SubnetIDHelper for SubnetID;
25+
using EnumerableSet for EnumerableSet.Bytes32Set;
26+
using CrossMsgHelper for IpcEnvelope;
27+
using AssetHelper for Asset;
2228

2329
/**
2430
* @dev Sends a general-purpose cross-message from the local subnet to the destination subnet.
25-
* Any value in msg.value will be forwarded in the call.
31+
* IMPORTANT: Native tokens via msg.value are treated as a contribution toward gas costs associated with message propagation.
32+
* There is no strict enforcement of the exact gas cost, and any msg.value provided will be accepted.
2633
*
2734
* IMPORTANT: Only smart contracts are allowed to trigger these cross-net messages. User wallets can send funds
2835
* from their address to the destination subnet and then run the transaction in the destination normally.
2936
*
30-
* @param envelope - the original envelope, which will be validated, stamped and committed during the send.
37+
* @param envelope - the original envelope, which will be validated, stamped, and committed during the send.
3138
* @return committed envelope.
3239
*/
3340
function sendContractXnetMessage(
34-
IpcEnvelope calldata envelope
41+
IpcEnvelope memory envelope
3542
) external payable returns (IpcEnvelope memory committed) {
3643
if (!s.generalPurposeCrossMsg) {
3744
revert MethodNotAllowed(ERR_GENERAL_CROSS_MSG_DISABLED);
@@ -42,28 +49,33 @@ contract GatewayMessengerFacet is GatewayActorModifiers {
4249
revert InvalidXnetMessage(InvalidXnetMessageReason.Sender);
4350
}
4451

45-
if (envelope.value != msg.value) {
46-
revert InvalidXnetMessage(InvalidXnetMessageReason.Value);
47-
}
48-
49-
if (envelope.kind != IpcMsgKind.Call) {
50-
revert InvalidXnetMessage(InvalidXnetMessageReason.Kind);
51-
}
52-
5352
// Will revert if the message won't deserialize into a CallMsg.
5453
abi.decode(envelope.message, (CallMsg));
5554

5655
committed = IpcEnvelope({
5756
kind: IpcMsgKind.Call,
5857
from: IPCAddress({subnetId: s.networkName, rawAddress: FvmAddressHelper.from(msg.sender)}),
5958
to: envelope.to,
60-
value: msg.value,
59+
value: envelope.value,
6160
message: envelope.message,
62-
nonce: 0 // nonce will be updated by LibGateway.commitCrossMessage
61+
// nonce and originalNonce will be updated by LibGateway.commitValidatedCrossMessage
62+
originalNonce: 0,
63+
localNonce: 0
6364
});
6465

66+
(bool valid, InvalidXnetMessageReason reason, IPCMsgType applyType) = committed.validateCrossMessage();
67+
if (!valid) {
68+
revert InvalidXnetMessage(reason);
69+
}
70+
71+
if (applyType == IPCMsgType.TopDown) {
72+
(, SubnetID memory nextHop) = committed.to.subnetId.down(s.networkName);
73+
// lock funds on the current subnet gateway for the next hop
74+
ISubnetActor(nextHop.getActor()).supplySource().lock(envelope.value);
75+
}
76+
6577
// Commit xnet message for dispatch.
66-
bool shouldBurn = LibGateway.commitCrossMessage(committed);
78+
bool shouldBurn = LibGateway.commitValidatedCrossMessage(committed);
6779

6880
// Apply side effects, such as burning funds.
6981
LibGateway.crossMsgSideEffects({v: committed.value, shouldBurn: shouldBurn});
@@ -75,23 +87,9 @@ contract GatewayMessengerFacet is GatewayActorModifiers {
7587
}
7688

7789
/**
78-
* @dev propagates the populated cross net message for the given cid
79-
* @param msgCid - the cid of the cross-net message
90+
* @dev Propagates all the populated cross-net messages from the postbox.
8091
*/
81-
function propagate(bytes32 msgCid) external payable {
82-
if (!s.multiLevelCrossMsg) {
83-
revert MethodNotAllowed(ERR_MULTILEVEL_CROSS_MSG_DISABLED);
84-
}
85-
86-
IpcEnvelope storage crossMsg = s.postbox[msgCid];
87-
88-
bool shouldBurn = LibGateway.commitCrossMessage(crossMsg);
89-
// We must delete the message first to prevent potential re-entrancies,
90-
// and as the message is deleted and we don't have a reference to the object
91-
// anymore, we need to pull the data from the message to trigger the side-effects.
92-
uint256 v = crossMsg.value;
93-
delete s.postbox[msgCid];
94-
95-
LibGateway.crossMsgSideEffects({v: v, shouldBurn: shouldBurn});
92+
function propagateAll() external payable {
93+
LibGateway.propagateAllPostboxMessages();
9694
}
9795
}

0 commit comments

Comments
 (0)