-
Notifications
You must be signed in to change notification settings - Fork 41
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
feat(node): support l2 plus value transfer #1240
Merged
Merged
Changes from 9 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
fd027c4
support l2 plus value transfer
ca75ac8
review feedbacks
21cc779
wip
dcd0fcc
initial working version
ddc7610
more tests
d2a1607
remove unnecessary check
566fe88
update xnet msg refund
e4bbf11
update tests
5bb94d2
remove unused comment
0b1ab4b
revert result handling
24f33ea
final review feedbacks
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// SPDX-License-Identifier: MIT OR Apache-2.0 | ||
pragma solidity ^0.8.23; | ||
|
||
import {Asset} from "../structs/Subnet.sol"; | ||
|
||
/// @title Subnet actor interface | ||
interface ISubnetActor { | ||
function supplySource() external view returns (Asset memory); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,14 +13,16 @@ import {CrossMsgHelper} from "../lib/CrossMsgHelper.sol"; | |
import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol"; | ||
import {SubnetIDHelper} from "../lib/SubnetIDHelper.sol"; | ||
import {AssetHelper} from "../lib/AssetHelper.sol"; | ||
import {ISubnetActor} from "../interfaces/ISubnetActor.sol"; | ||
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; | ||
|
||
// Validation outcomes for cross messages | ||
enum CrossMessageValidationOutcome { | ||
Valid, | ||
InvalidDstSubnet, | ||
CannotSendToItself, | ||
CommonParentNotExist | ||
CommonParentNotExist, | ||
IncompatibleSupplySource | ||
} | ||
|
||
library LibGateway { | ||
|
@@ -251,9 +253,7 @@ library LibGateway { | |
|
||
crossMessage.nonce = topDownNonce; | ||
subnet.topDownNonce = topDownNonce + 1; | ||
if (crossMessage.kind != IpcMsgKind.Call) { | ||
subnet.circSupply += crossMessage.value; | ||
} | ||
subnet.circSupply += crossMessage.value; | ||
|
||
emit NewTopDownMessage({subnet: subnet.id.getAddress(), message: crossMessage, id: crossMessage.toDeterministicHash()}); | ||
} | ||
|
@@ -464,7 +464,7 @@ library LibGateway { | |
emit MessageStoredInPostbox({id: crossMsg.toDeterministicHash()}); | ||
return; | ||
} | ||
|
||
// execute the message and get the receipt. | ||
(bool success, bytes memory ret) = executeCrossMsg(crossMsg, supplySource); | ||
if (success) { | ||
|
@@ -567,54 +567,120 @@ library LibGateway { | |
} | ||
} | ||
|
||
/// Checks if the incoming and outgoing subnet supply sources can be mapped. | ||
/// Caller should make sure the incoming/outgoing subnets and current subnet are immediate parent/child subnets. | ||
function checkSubnetsSupplyCompatible( | ||
bool isLCA, | ||
IPCMsgType applyType, | ||
SubnetID memory incoming, | ||
SubnetID memory outgoing, | ||
SubnetID memory current | ||
) internal view returns(bool) { | ||
if (isLCA) { | ||
// now, it's pivoting @ LCA (i.e. upwards => downwards) | ||
// if incoming bottom up subnet and outgoing target subnet have the same | ||
// asset, we will allow it. This is because if they are using the | ||
// same asset, then the asset can be mapped in both subnets. | ||
|
||
(, SubnetID memory incDown) = incoming.down(current); | ||
(, SubnetID memory outDown) = outgoing.down(current); | ||
|
||
Asset memory incAsset = ISubnetActor(incDown.getActor()).supplySource(); | ||
Asset memory outAsset = ISubnetActor(outDown.getActor()).supplySource(); | ||
|
||
return incAsset.equals(outAsset); | ||
} | ||
|
||
if (applyType == IPCMsgType.BottomUp) { | ||
// The child subnet has supply source native, this is the same as | ||
// the current subnet's native source, the mapping makes sense, propagate up. | ||
(, SubnetID memory incDown) = incoming.down(current); | ||
return incDown.getActor().hasSupplyOfKind(AssetKind.Native); | ||
} | ||
|
||
// Topdown handling | ||
|
||
// The incoming subnet's supply source will be mapped to native coin in the | ||
// next child subnet. If the down subnet has native, then the mapping makes | ||
// sense. | ||
(, SubnetID memory down) = outgoing.down(current); | ||
return down.getActor().hasSupplyOfKind(AssetKind.Native); | ||
} | ||
|
||
/// @notice Validates a cross message before committing it. | ||
function validateCrossMessage(IpcEnvelope memory envelope) internal view returns (CrossMessageValidationOutcome) { | ||
GatewayActorStorage storage s = LibGatewayActorStorage.appStorage(); | ||
SubnetID memory toSubnetId = envelope.to.subnetId; | ||
(CrossMessageValidationOutcome outcome, ) = checkCrossMessage(envelope); | ||
return outcome; | ||
} | ||
|
||
/// @notice Validates a cross message and returns the applyType if the message is valid | ||
function checkCrossMessage(IpcEnvelope memory envelope) internal view returns (CrossMessageValidationOutcome, IPCMsgType applyType) { | ||
Comment on lines
611
to
+617
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: I would keep these as a single function returning a tuple. Would call this single method |
||
SubnetID memory toSubnetId = envelope.to.subnetId; | ||
if (toSubnetId.isEmpty()) { | ||
return CrossMessageValidationOutcome.InvalidDstSubnet; | ||
return (CrossMessageValidationOutcome.InvalidDstSubnet, applyType); | ||
} | ||
|
||
GatewayActorStorage storage s = LibGatewayActorStorage.appStorage(); | ||
SubnetID memory currentNetwork = s.networkName; | ||
|
||
// We cannot send a cross message to the same subnet. | ||
if (toSubnetId.equals(s.networkName)) { | ||
return CrossMessageValidationOutcome.CannotSendToItself; | ||
if (toSubnetId.equals(currentNetwork)) { | ||
return (CrossMessageValidationOutcome.CannotSendToItself, applyType); | ||
} | ||
|
||
// Lowest common ancestor subnet | ||
bool isLCA = toSubnetId.commonParent(envelope.from.subnetId).equals(s.networkName); | ||
IPCMsgType applyType = envelope.applyType(s.networkName); | ||
bool isLCA = toSubnetId.commonParent(envelope.from.subnetId).equals(currentNetwork); | ||
applyType = envelope.applyType(currentNetwork); | ||
|
||
// If the directionality is top-down, or if we're inverting the direction | ||
// else we need to check if the common parent exists. | ||
if (applyType == IPCMsgType.TopDown || isLCA) { | ||
(bool foundChildSubnetId, SubnetID memory childSubnetId) = toSubnetId.down(s.networkName); | ||
(bool foundChildSubnetId, SubnetID memory childSubnetId) = toSubnetId.down(currentNetwork); | ||
if (!foundChildSubnetId) { | ||
return CrossMessageValidationOutcome.InvalidDstSubnet; | ||
return (CrossMessageValidationOutcome.InvalidDstSubnet, applyType); | ||
} | ||
|
||
(bool foundSubnet,) = LibGateway.getSubnet(childSubnetId); | ||
if (!foundSubnet) { | ||
return CrossMessageValidationOutcome.InvalidDstSubnet; | ||
return (CrossMessageValidationOutcome.InvalidDstSubnet, applyType); | ||
} | ||
} else { | ||
SubnetID memory commonParent = toSubnetId.commonParent(s.networkName); | ||
SubnetID memory commonParent = toSubnetId.commonParent(currentNetwork); | ||
if (commonParent.isEmpty()) { | ||
return CrossMessageValidationOutcome.CommonParentNotExist; | ||
return (CrossMessageValidationOutcome.CommonParentNotExist, applyType); | ||
} | ||
} | ||
|
||
return CrossMessageValidationOutcome.Valid; | ||
// starting/ending subnet, no need check supply sources | ||
if (envelope.from.subnetId.equals(currentNetwork) || envelope.to.subnetId.equals(currentNetwork)) { | ||
return (CrossMessageValidationOutcome.Valid, applyType); | ||
} | ||
|
||
bool supplySourcesCompatible = checkSubnetsSupplyCompatible({ | ||
isLCA: isLCA, | ||
applyType: applyType, | ||
incoming: envelope.from.subnetId, | ||
outgoing: envelope.to.subnetId, | ||
current: currentNetwork | ||
}); | ||
|
||
if (!supplySourcesCompatible) { | ||
return (CrossMessageValidationOutcome.IncompatibleSupplySource, applyType); | ||
} | ||
|
||
return (CrossMessageValidationOutcome.Valid, applyType); | ||
} | ||
|
||
// Function to map CrossMessageValidationOutcome to InvalidXnetMessageReason | ||
// Function to map CrossMessageValidationOutcome to InvalidXnetMessageReason | ||
function validationOutcomeToInvalidXnetMsgReason(CrossMessageValidationOutcome outcome) internal pure returns (InvalidXnetMessageReason) { | ||
if (outcome == CrossMessageValidationOutcome.InvalidDstSubnet) { | ||
return InvalidXnetMessageReason.DstSubnet; | ||
} else if (outcome == CrossMessageValidationOutcome.CannotSendToItself) { | ||
return InvalidXnetMessageReason.CannotSendToItself; | ||
} else if (outcome == CrossMessageValidationOutcome.CommonParentNotExist) { | ||
return InvalidXnetMessageReason.CommonParentNotExist; | ||
} else if (outcome == CrossMessageValidationOutcome.IncompatibleSupplySource) { | ||
return InvalidXnetMessageReason.IncompatibleSupplySource; | ||
} | ||
|
||
revert("Unhandled validation outcome"); | ||
|
@@ -627,10 +693,11 @@ library LibGateway { | |
GatewayActorStorage storage s = LibGatewayActorStorage.appStorage(); | ||
|
||
uint256 keysLength = s.postboxKeys.length(); | ||
bytes32[] memory ids = new bytes32[](keysLength); | ||
|
||
bytes32[] memory values = s.postboxKeys.values(); | ||
|
||
for (uint256 i = 0; i < keysLength; ) { | ||
bytes32 msgCid = s.postboxKeys.at(i); | ||
ids[i] = msgCid; | ||
bytes32 msgCid = values[i]; | ||
LibGateway.propagatePostboxMessage(msgCid); | ||
|
||
unchecked { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not correct; the version in main is more correct. For a Result message, you will want to perform a call as this returns control back to the caller. Rationale: