Skip to content
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 11 commits into from
Dec 31, 2024

Conversation

cryptoAtwill
Copy link
Contributor

@cryptoAtwill cryptoAtwill commented Dec 20, 2024

This PR builds on previous PR but instead enables the value transfer in cross network messages.

Update 27/12/24:

@cryptoAtwill cryptoAtwill requested a review from a team as a code owner December 20, 2024 08:38
Copy link
Contributor

@raulk raulk left a comment

Choose a reason for hiding this comment

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

LGTM. For posterity purposes, this is our guiding design sketch:

image

@@ -37,6 +37,10 @@ library AssetHelper {
require(asset.kind == kind, "Unexpected asset");
}

function equals(Asset memory asset, Asset memory asset2) internal pure returns (bool) {
return asset.tokenAddress == asset2.tokenAddress;
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's compare kinds too to make this code more defensive against future changes, e.g. we may introduce a third asset class identified only by kind, or by attributes other than tokenAddress.

import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

// Validation outcomes for cross messages
enum CrossMessageValidationOutcome {
Valid,
InvalidDstSubnet,
CannotSendToItself,
CommonParentNotExist
CommonParentNotExist,
InvalidSupplySource
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
InvalidSupplySource
IncompatibleSupplySource

@@ -567,28 +569,69 @@ 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 checkSubnetsAssets(
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
function checkSubnetsAssets(
function checkSubnetsSupplyCompatible(

if (commonParent.isEmpty()) {
return CrossMessageValidationOutcome.CommonParentNotExist;
}
}

bool validSupplySources = checkSubnetsAssets({
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
bool validSupplySources = checkSubnetsAssets({
bool supplySourcesCompatible = checkSubnetsAssets({

@@ -219,25 +219,6 @@ contract L2PlusSubnetTest is Test, IntegrationTestBase {
sendCrossMessageFromChildToParentWithResult(params);
}

function testL2PlusSubnet_TokenMixed_SendCrossMessageFromChildToParentWithOkResult() public {
Copy link
Contributor Author

@cryptoAtwill cryptoAtwill Dec 27, 2024

Choose a reason for hiding this comment

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

these tests are removed because tokenL2 to token L3 xnet value transfer does not work. Updated in: https://github.com/consensus-shipyard/ipc/pull/1240/files#diff-53695b3660cd8c2d5418a61e61a1abda1cd2ba05dcc0f607ddd3251b9cc8f57dR436

Copy link
Contributor

Choose a reason for hiding this comment

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

Removed temporarily, since we cover this error scenario in the cited PR.

if (crossMsg.kind == IpcMsgKind.Transfer) {
// If the cross msg kind is Result, create result message should have handled the value correctly.
// If the execution is ok, value should be 0, else one should perform refund.
if (crossMsg.kind == IpcMsgKind.Transfer || crossMsg.kind == IpcMsgKind.Result) {
Copy link
Contributor

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:

  • if it's an account, there will be no code to invoke, so this will be have like a bare transfer
  • but if the original caller was a contract, you will want to give it control so it can handle the result

Copy link
Contributor

@raulk raulk left a comment

Choose a reason for hiding this comment

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

LGTM, feel free to merge, just a few nits and comments inline.

@@ -219,25 +219,6 @@ contract L2PlusSubnetTest is Test, IntegrationTestBase {
sendCrossMessageFromChildToParentWithResult(params);
}

function testL2PlusSubnet_TokenMixed_SendCrossMessageFromChildToParentWithOkResult() public {
Copy link
Contributor

Choose a reason for hiding this comment

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

Removed temporarily, since we cover this error scenario in the cited PR.

Comment on lines 611 to +617
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) {
Copy link
Contributor

Choose a reason for hiding this comment

The 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 inspect().

// Call flow tests.
//---------------------

// testing Native L1 => ERC20 L2 => ERC20 L3, this supply source is not allowed
Copy link
Contributor

Choose a reason for hiding this comment

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

Can't we add assertions on the returned result here? (i.e. that it's a failure and it carries the correct reason)

Comment on lines 131 to 139
interface IGateway {
function sendContractXnetMessage(
IpcEnvelope memory envelope
) external payable returns (IpcEnvelope memory committed);

function fund(SubnetID calldata subnetId, FvmAddress calldata to) external payable;

function fundWithToken(SubnetID calldata subnetId, FvmAddress calldata to, uint256 amount) external;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need this local interface definition here? Can't we import the canonical one from interfaces/?

// ======= internal util methods ========

function executeTopdownMessages(IpcEnvelope[] memory msgs, GatewayDiamond gw) internal {
uint256 minted_tokens;
Copy link
Contributor

Choose a reason for hiding this comment

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

Convention is lower snake case.

Comment on lines 179 to 184
for (uint256 i; i < msgs.length; ) {
minted_tokens += msgs[i].value;
unchecked {
++i;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Since tests do not run in a gas restricted environment, we can unclutter this by removing the unchecked increment.

Comment on lines +199 to +232
function createBottomUpCheckpoint(
SubnetID memory subnet,
GatewayDiamond gw
) internal returns (BottomUpCheckpoint memory checkpoint) {
uint256 e = getNextEpoch(block.number, DEFAULT_CHECKPOINT_PERIOD);

GatewayGetterFacet getter = gw.getter();
CheckpointingFacet checkpointer = gw.checkpointer();

BottomUpMsgBatch memory batch = getter.bottomUpMsgBatch(e);

(, address[] memory addrs, uint256[] memory weights) = TestUtils.getFourValidators(vm);

(bytes32 membershipRoot, ) = MerkleTreeHelper.createMerkleProofsForValidators(addrs, weights);

checkpoint = BottomUpCheckpoint({
subnetID: subnet,
blockHeight: batch.blockHeight,
blockHash: keccak256("block1"),
nextConfigurationNumber: 0,
msgs: batch.msgs,
activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0)))
});

vm.prank(FilAddress.SYSTEM_ACTOR);
checkpointer.createBottomUpCheckpoint(
checkpoint,
membershipRoot,
weights[0] + weights[1] + weights[2],
ActivityHelper.dummyActivityRollup()
);

return checkpoint;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we really not have utils for this kind of common stuff in a test library?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not really, I created an issue to track this good to have: #1244

@cryptoAtwill cryptoAtwill merged commit 8780344 into support-l2-plus Dec 31, 2024
15 checks passed
@cryptoAtwill cryptoAtwill deleted the support-l2-value branch December 31, 2024 05:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

2 participants