Skip to content

Commit

Permalink
create separate public, private mint paths, constain ethaddress to be…
Browse files Browse the repository at this point in the history
… 20 bytes, rename fns in bridge for clarity
  • Loading branch information
rahul-kothari committed Sep 19, 2023
1 parent 1868a43 commit ea7beaa
Show file tree
Hide file tree
Showing 14 changed files with 408 additions and 78 deletions.
96 changes: 90 additions & 6 deletions l1-contracts/test/portals/TokenPortal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ contract TokenPortal {
}

/**
* @notice Deposit funds into the portal and adds an L2 message.
* @notice Deposit funds into the portal and adds an L2 message which can only be consumed publicly on Aztec
* @param _to - The aztec address of the recipient
* @param _amount - The amount to deposit
* @param _deadline - The timestamp after which the entry can be cancelled
* @param _secretHash - The hash of the secret consumable message
* @param _canceller - The address that can cancel the L1 to L2 message.
* @return The key of the entry in the Inbox
*/
function depositToAztec(
function depositToAztecPublic(
bytes32 _to,
uint256 _amount,
uint32 _deadline,
Expand All @@ -45,7 +45,7 @@ contract TokenPortal {

// Hash the message content to be reconstructed in the receiving contract
bytes32 contentHash = Hash.sha256ToField(
abi.encodeWithSignature("mint(uint256,bytes32,address)", _amount, _to, _canceller)
abi.encodeWithSignature("mint_public(uint256,bytes32,address)", _amount, _to, _canceller)
);

// Hold the tokens in the portal
Expand All @@ -55,9 +55,49 @@ contract TokenPortal {
return inbox.sendL2Message{value: msg.value}(actor, _deadline, contentHash, _secretHash);
}

/**
* @notice Deposit funds into the portal and adds an L2 message which can only be consumed privately on Aztec
* @param _amount - The amount to deposit
* @param _deadline - The timestamp after which the entry can be cancelled
* @param _secretHashForL2MessageConsumption - The hash of the secret consumable L1 to L2 message. The hash should be 254 bits (so it can fit in a Field element)
* @param _secretHashForL2MessageConsumption - The hash of the secret to redeem minted notes privately on Aztec. The hash should be 254 bits (so it can fit in a Field element)
* @param _canceller - The address that can cancel the L1 to L2 message.
* @return The key of the entry in the Inbox
*/
function depositToAztecPrivate(
uint256 _amount,
uint32 _deadline,
bytes32 _secretHashForL2MessageConsumption,
bytes32 _secretHashForRedeemingMintedNotes,
address _canceller
) external payable returns (bytes32) {
// Preamble
// @todo: (issue #624) handle different versions
IInbox inbox = registry.getInbox();
DataStructures.L2Actor memory actor = DataStructures.L2Actor(l2TokenAddress, 1);

// Hash the message content to be reconstructed in the receiving contract
bytes32 contentHash = Hash.sha256ToField(
abi.encodeWithSignature(
"mint_private(uint256,bytes32,address)",
_amount,
_secretHashForRedeemingMintedNotes,
_canceller
)
);

// Hold the tokens in the portal
underlying.safeTransferFrom(msg.sender, address(this), _amount);

// Send message to rollup
return inbox.sendL2Message{value: msg.value}(
actor, _deadline, contentHash, _secretHashForL2MessageConsumption
);
}

// docs:start:token_portal_cancel
/**
* @notice Cancel the L1 to L2 message
* @notice Cancel a public depositToAztec L1 to L2 message
* @dev only callable by the `canceller` of the message
* @param _to - The aztec address of the recipient in the original message
* @param _amount - The amount to deposit per the original message
Expand All @@ -66,7 +106,7 @@ contract TokenPortal {
* @param _fee - The fee paid to the sequencer
* @return The key of the entry in the Inbox
*/
function cancelL1ToAztecMessage(
function cancelL1ToAztecMessagePublic(
bytes32 _to,
uint256 _amount,
uint32 _deadline,
Expand All @@ -81,7 +121,7 @@ contract TokenPortal {
sender: l1Actor,
recipient: l2Actor,
content: Hash.sha256ToField(
abi.encodeWithSignature("mint(uint256,bytes32,address)", _amount, _to, msg.sender)
abi.encodeWithSignature("mint_public(uint256,bytes32,address)", _amount, _to, msg.sender)
),
secretHash: _secretHash,
deadline: _deadline,
Expand All @@ -93,6 +133,50 @@ contract TokenPortal {
underlying.transfer(msg.sender, _amount);
return entryKey;
}

/**
* @notice Cancel a private depositToAztec L1 to L2 message
* @dev only callable by the `canceller` of the message
* @param _amount - The amount to deposit per the original message
* @param _deadline - The timestamp after which the entry can be cancelled
* @param _secretHashForL2MessageConsumption - The hash of the secret consumable L1 to L2 message
* @param _secretHashForL2MessageConsumption - The hash of the secret to redeem minted notes privately on Aztec
* @param _fee - The fee paid to the sequencer
* @return The key of the entry in the Inbox
*/
function cancelL1ToAztecMessagePrivate(
uint256 _amount,
uint32 _deadline,
bytes32 _secretHashForL2MessageConsumption,
bytes32 _secretHashForRedeemingMintedNotes,
uint64 _fee
) external returns (bytes32) {
// @todo: (issue #624) handle different versions
IInbox inbox = registry.getInbox();
DataStructures.L1Actor memory l1Actor = DataStructures.L1Actor(address(this), block.chainid);
DataStructures.L2Actor memory l2Actor = DataStructures.L2Actor(l2TokenAddress, 1);
DataStructures.L1ToL2Msg memory message = DataStructures.L1ToL2Msg({
sender: l1Actor,
recipient: l2Actor,
content: Hash.sha256ToField(
abi.encodeWithSignature(
"mint_private(uint256,bytes32,address)",
_amount,
_secretHashForRedeemingMintedNotes,
msg.sender
)
),
secretHash: _secretHashForL2MessageConsumption,
deadline: _deadline,
fee: _fee
});
bytes32 entryKey = inbox.cancelL2Message(message, address(this));
// release the funds to msg.sender (since the content hash (& message key) is derived by hashing the caller,
// we confirm that msg.sender is same as `_canceller` supplied when creating the message)
underlying.transfer(msg.sender, _amount);
return entryKey;
}

// docs:end:token_portal_cancel

// docs:start:token_portal_withdraw
Expand Down
157 changes: 144 additions & 13 deletions l1-contracts/test/portals/TokenPortal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ contract TokenPortalTest is Test {
bytes32 internal to = bytes32(0x2d749407d8c364537cdeb799c1574929cb22ff1ece2b96d2a1c6fa287a0e0171);
uint256 internal amount = 100;
uint256 internal mintAmount = 1 ether;
bytes32 internal secretHash = 0x147e4fec49805c924e28150fc4b36824679bc17ecb1d7d9f6a9effb7fde6b6a0;
bytes32 internal secretHashForL2MessageConsumption =
0x147e4fec49805c924e28150fc4b36824679bc17ecb1d7d9f6a9effb7fde6b6a0;
bytes32 internal secretHashForRedeemingMintedNotes =
0x157e4fec49805c924e28150fc4b36824679bc17ecb1d7d9f6a9effb7fde6b6a0;
uint64 internal bid = 1 ether;

// params for withdraw:
Expand All @@ -71,7 +74,7 @@ contract TokenPortalTest is Test {
vm.deal(address(this), 100 ether);
}

function _createExpectedL1ToL2Message(address _canceller)
function _createExpectedMintPrivateL1ToL2Message(address _canceller)
internal
view
returns (DataStructures.L1ToL2Msg memory)
Expand All @@ -80,21 +83,44 @@ contract TokenPortalTest is Test {
sender: DataStructures.L1Actor(address(tokenPortal), block.chainid),
recipient: DataStructures.L2Actor(l2TokenAddress, 1),
content: Hash.sha256ToField(
abi.encodeWithSignature("mint(uint256,bytes32,address)", amount, to, _canceller)
abi.encodeWithSignature(
"mint_private(uint256,bytes32,address)",
amount,
secretHashForRedeemingMintedNotes,
_canceller
)
),
secretHash: secretHash,
secretHash: secretHashForL2MessageConsumption,
deadline: deadline,
fee: bid
});
}

function testDeposit() public returns (bytes32) {
function _createExpectedMintPublicL1ToL2Message(address _canceller)
internal
view
returns (DataStructures.L1ToL2Msg memory)
{
return DataStructures.L1ToL2Msg({
sender: DataStructures.L1Actor(address(tokenPortal), block.chainid),
recipient: DataStructures.L2Actor(l2TokenAddress, 1),
content: Hash.sha256ToField(
abi.encodeWithSignature("mint_public(uint256,bytes32,address)", amount, to, _canceller)
),
secretHash: secretHashForL2MessageConsumption,
deadline: deadline,
fee: bid
});
}

function testDepositPrivate() public returns (bytes32) {
// mint token and approve to the portal
portalERC20.mint(address(this), mintAmount);
portalERC20.approve(address(tokenPortal), mintAmount);

// Check for the expected message
DataStructures.L1ToL2Msg memory expectedMessage = _createExpectedL1ToL2Message(address(this));
DataStructures.L1ToL2Msg memory expectedMessage =
_createExpectedMintPrivateL1ToL2Message(address(this));
bytes32 expectedEntryKey = inbox.computeEntryKey(expectedMessage);

// Check the even was emitted
Expand All @@ -113,8 +139,13 @@ contract TokenPortalTest is Test {
);

// Perform op
bytes32 entryKey =
tokenPortal.depositToAztec{value: bid}(to, amount, deadline, secretHash, address(this));
bytes32 entryKey = tokenPortal.depositToAztecPrivate{value: bid}(
amount,
deadline,
secretHashForL2MessageConsumption,
secretHashForRedeemingMintedNotes,
address(this)
);

assertEq(entryKey, expectedEntryKey, "returned entry key and calculated entryKey should match");

Expand All @@ -125,28 +156,128 @@ contract TokenPortalTest is Test {
return entryKey;
}

function testCancel() public {
bytes32 expectedEntryKey = testDeposit();
function testDepositPublic() public returns (bytes32) {
// mint token and approve to the portal
portalERC20.mint(address(this), mintAmount);
portalERC20.approve(address(tokenPortal), mintAmount);

// Check for the expected message
DataStructures.L1ToL2Msg memory expectedMessage =
_createExpectedMintPublicL1ToL2Message(address(this));
bytes32 expectedEntryKey = inbox.computeEntryKey(expectedMessage);

// Check the even was emitted
vm.expectEmit(true, true, true, true);
// event we expect
emit MessageAdded(
expectedEntryKey,
expectedMessage.sender.actor,
expectedMessage.recipient.actor,
expectedMessage.sender.chainId,
expectedMessage.recipient.version,
expectedMessage.deadline,
expectedMessage.fee,
expectedMessage.content,
expectedMessage.secretHash
);

// Perform op
bytes32 entryKey = tokenPortal.depositToAztecPublic{value: bid}(
to, amount, deadline, secretHashForL2MessageConsumption, address(this)
);

assertEq(entryKey, expectedEntryKey, "returned entry key and calculated entryKey should match");

// Check that the message is in the inbox
DataStructures.Entry memory entry = inbox.get(entryKey);
assertEq(entry.count, 1);

return entryKey;
}

function testCancelPublic() public {
bytes32 expectedEntryKey = testDepositPublic();
// now cancel the message - move time forward (post deadline)
vm.warp(deadline + 1 days);

// ensure no one else can cancel the message:
vm.startPrank(address(0xdead));
bytes32 expectedWrongEntryKey =
inbox.computeEntryKey(_createExpectedL1ToL2Message(address(0xdead)));
inbox.computeEntryKey(_createExpectedMintPublicL1ToL2Message(address(0xdead)));
vm.expectRevert(
abi.encodeWithSelector(Errors.Inbox__NothingToConsume.selector, expectedWrongEntryKey)
);
tokenPortal.cancelL1ToAztecMessage(to, amount, deadline, secretHash, bid);
tokenPortal.cancelL1ToAztecMessagePublic(
to, amount, deadline, secretHashForL2MessageConsumption, bid
);
vm.stopPrank();

// ensure cant cancel with cancelPrivate (since deposit was public)
expectedWrongEntryKey =
inbox.computeEntryKey(_createExpectedMintPrivateL1ToL2Message(address(this)));
vm.expectRevert(
abi.encodeWithSelector(Errors.Inbox__NothingToConsume.selector, expectedWrongEntryKey)
);
tokenPortal.cancelL1ToAztecMessagePrivate(
amount, deadline, secretHashForL2MessageConsumption, secretHashForRedeemingMintedNotes, bid
);

// actually cancel the message
// check event was emitted
vm.expectEmit(true, false, false, false);
// expected event:
emit L1ToL2MessageCancelled(expectedEntryKey);
// perform op
bytes32 entryKey = tokenPortal.cancelL1ToAztecMessage(to, amount, deadline, secretHash, bid);
bytes32 entryKey = tokenPortal.cancelL1ToAztecMessagePublic(
to, amount, deadline, secretHashForL2MessageConsumption, bid
);

assertEq(entryKey, expectedEntryKey, "returned entry key and calculated entryKey should match");
assertFalse(inbox.contains(entryKey), "entry still in inbox");
assertEq(
portalERC20.balanceOf(address(this)),
mintAmount,
"assets should be transferred back to this contract"
);
assertEq(portalERC20.balanceOf(address(tokenPortal)), 0, "portal should have no assets");
}

function testCancelPrivate() public {
bytes32 expectedEntryKey = testDepositPrivate();
// now cancel the message - move time forward (post deadline)
vm.warp(deadline + 1 days);

// ensure no one else can cancel the message:
vm.startPrank(address(0xdead));
bytes32 expectedWrongEntryKey =
inbox.computeEntryKey(_createExpectedMintPrivateL1ToL2Message(address(0xdead)));
vm.expectRevert(
abi.encodeWithSelector(Errors.Inbox__NothingToConsume.selector, expectedWrongEntryKey)
);
tokenPortal.cancelL1ToAztecMessagePrivate(
amount, deadline, secretHashForL2MessageConsumption, secretHashForRedeemingMintedNotes, bid
);
vm.stopPrank();

// ensure cant cancel with cancelPublic (since deposit was private)
expectedWrongEntryKey =
inbox.computeEntryKey(_createExpectedMintPublicL1ToL2Message(address(this)));
vm.expectRevert(
abi.encodeWithSelector(Errors.Inbox__NothingToConsume.selector, expectedWrongEntryKey)
);
tokenPortal.cancelL1ToAztecMessagePublic(
to, amount, deadline, secretHashForL2MessageConsumption, bid
);

// actually cancel the message
// check event was emitted
vm.expectEmit(true, false, false, false);
// expected event:
emit L1ToL2MessageCancelled(expectedEntryKey);
// perform op
bytes32 entryKey = tokenPortal.cancelL1ToAztecMessagePrivate(
amount, deadline, secretHashForL2MessageConsumption, secretHashForRedeemingMintedNotes, bid
);

assertEq(entryKey, expectedEntryKey, "returned entry key and calculated entryKey should match");
assertFalse(inbox.contains(entryKey), "entry still in inbox");
Expand Down
5 changes: 3 additions & 2 deletions l1-contracts/test/portals/UniswapPortal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,9 @@ contract UniswapPortal {
// Note, safeApprove was deprecated from Oz
vars.outputAsset.approve(address(_outputTokenPortal), amountOut);

// Deposit the output asset to the L2 via its portal
return TokenPortal(_outputTokenPortal).depositToAztec{value: msg.value}(
// Deposit the output asset to the L2 via its portal]
// TODO(2167) - Update UniswapPortal properly with new portal standard.
return TokenPortal(_outputTokenPortal).depositToAztecPublic{value: msg.value}(
_aztecRecipient, amountOut, _deadlineForL1ToL2Message, _secretHash, _canceller
);
}
Expand Down
3 changes: 2 additions & 1 deletion l1-contracts/test/portals/UniswapPortal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,8 @@ contract UniswapPortalTest is Test {
// expected event:
emit L1ToL2MessageCancelled(l1ToL2MessageKey);
// perform op
bytes32 entryKey = wethTokenPortal.cancelL1ToAztecMessage(
// TODO(2167) - Update UniswapPortal properly with new portal standard.
bytes32 entryKey = wethTokenPortal.cancelL1ToAztecMessagePublic(
aztecRecipient, wethAmountOut, deadlineForL1ToL2Message, secretHash, 1 ether
);
assertEq(entryKey, l1ToL2MessageKey, "returned entry key and calculated entryKey should match");
Expand Down
Loading

0 comments on commit ea7beaa

Please sign in to comment.