Skip to content

Commit

Permalink
refactor: token partial notes refactor pt. 2 - bridging (#9600)
Browse files Browse the repository at this point in the history
  • Loading branch information
benesjan authored Nov 12, 2024
1 parent abdd912 commit d513099
Show file tree
Hide file tree
Showing 31 changed files with 151 additions and 243 deletions.
30 changes: 10 additions & 20 deletions l1-contracts/test/portals/TokenPortal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@ contract TokenPortal {
);

event DepositToAztecPrivate(
bytes32 secretHashForRedeemingMintedNotes,
uint256 amount,
bytes32 secretHashForL2MessageConsumption,
bytes32 key,
uint256 index
uint256 amount, bytes32 secretHashForL2MessageConsumption, bytes32 key, uint256 index
);

IRegistry public registry;
Expand Down Expand Up @@ -76,26 +72,22 @@ contract TokenPortal {
// docs:start:deposit_private
/**
* @notice Deposit funds into the portal and adds an L2 message which can only be consumed privately on Aztec
* @param _secretHashForRedeemingMintedNotes - 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 _amount - The amount to deposit
* @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)
* @return The key of the entry in the Inbox and its leaf index
*/
function depositToAztecPrivate(
bytes32 _secretHashForRedeemingMintedNotes,
uint256 _amount,
bytes32 _secretHashForL2MessageConsumption
) external returns (bytes32, uint256) {
function depositToAztecPrivate(uint256 _amount, bytes32 _secretHashForL2MessageConsumption)
external
returns (bytes32, uint256)
{
// Preamble
IInbox inbox = IRollup(registry.getRollup()).INBOX();
DataStructures.L2Actor memory actor = DataStructures.L2Actor(l2Bridge, 1);

// Hash the message content to be reconstructed in the receiving contract
bytes32 contentHash = Hash.sha256ToField(
abi.encodeWithSignature(
"mint_private(bytes32,uint256)", _secretHashForRedeemingMintedNotes, _amount
)
);
// Hash the message content to be reconstructed in the receiving contract - the signature below does not correspond
// to a real function. It's just an identifier of an action.
bytes32 contentHash =
Hash.sha256ToField(abi.encodeWithSignature("mint_private(uint256)", _amount));

// Hold the tokens in the portal
underlying.safeTransferFrom(msg.sender, address(this), _amount);
Expand All @@ -105,9 +97,7 @@ contract TokenPortal {
inbox.sendL2Message(actor, contentHash, _secretHashForL2MessageConsumption);

// Emit event
emit DepositToAztecPrivate(
_secretHashForRedeemingMintedNotes, _amount, _secretHashForL2MessageConsumption, key, index
);
emit DepositToAztecPrivate(_amount, _secretHashForL2MessageConsumption, key, index);

return (key, index);
}
Expand Down
14 changes: 3 additions & 11 deletions l1-contracts/test/portals/TokenPortal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@ contract TokenPortalTest is Test {
// this hash is just a random 32 byte string
bytes32 internal secretHashForL2MessageConsumption =
0x147e4fec49805c924e28150fc4b36824679bc17ecb1d7d9f6a9effb7fde6b6a0;
// this hash is just a random 32 byte string
bytes32 internal secretHashForRedeemingMintedNotes =
0x157e4fec49805c924e28150fc4b36824679bc17ecb1d7d9f6a9effb7fde6b6a0;

// params for withdraw:
address internal recipient = address(0xdead);
Expand Down Expand Up @@ -94,11 +91,7 @@ contract TokenPortalTest is Test {
return DataStructures.L1ToL2Msg({
sender: DataStructures.L1Actor(address(tokenPortal), block.chainid),
recipient: DataStructures.L2Actor(l2TokenAddress, 1),
content: Hash.sha256ToField(
abi.encodeWithSignature(
"mint_private(bytes32,uint256)", secretHashForRedeemingMintedNotes, amount
)
),
content: Hash.sha256ToField(abi.encodeWithSignature("mint_private(uint256)", amount)),
secretHash: secretHashForL2MessageConsumption,
index: _index
});
Expand Down Expand Up @@ -137,9 +130,8 @@ contract TokenPortalTest is Test {
// event we will get

// Perform op
(bytes32 leaf, uint256 index) = tokenPortal.depositToAztecPrivate(
secretHashForRedeemingMintedNotes, amount, secretHashForL2MessageConsumption
);
(bytes32 leaf, uint256 index) =
tokenPortal.depositToAztecPrivate(amount, secretHashForL2MessageConsumption);

assertEq(leaf, expectedLeaf, "returned leaf and calculated leaf should match");
assertEq(index, expectedIndex, "returned index and calculated index should match");
Expand Down
10 changes: 3 additions & 7 deletions l1-contracts/test/portals/UniswapPortal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,6 @@ contract UniswapPortal {
* @param _uniswapFeeTier - The fee tier for the swap on UniswapV3
* @param _outputTokenPortal - The ethereum address of the output token portal
* @param _amountOutMinimum - The minimum amount of output assets to receive from the swap (slippage protection)
* @param _secretHashForRedeemingMintedNotes - 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 _secretHashForL1ToL2Message - The hash of the secret consumable message. The hash should be 254 bits (so it can fit in a Field element)
* @param _withCaller - When true, using `msg.sender` as the caller, otherwise address(0)
* @return A hash of the L1 to L2 message inserted in the Inbox
Expand All @@ -169,7 +168,6 @@ contract UniswapPortal {
uint24 _uniswapFeeTier,
address _outputTokenPortal,
uint256 _amountOutMinimum,
bytes32 _secretHashForRedeemingMintedNotes,
bytes32 _secretHashForL1ToL2Message,
bool _withCaller,
// Avoiding stack too deep
Expand All @@ -195,13 +193,12 @@ contract UniswapPortal {
// prevent stack too deep errors
vars.contentHash = Hash.sha256ToField(
abi.encodeWithSignature(
"swap_private(address,uint256,uint24,address,uint256,bytes32,bytes32,address)",
"swap_private(address,uint256,uint24,address,uint256,bytes32,address)",
_inputTokenPortal,
_inAmount,
_uniswapFeeTier,
_outputTokenPortal,
_amountOutMinimum,
_secretHashForRedeemingMintedNotes,
_secretHashForL1ToL2Message,
_withCaller ? msg.sender : address(0)
)
Expand Down Expand Up @@ -247,9 +244,8 @@ contract UniswapPortal {
vars.outputAsset.approve(address(_outputTokenPortal), amountOut);

// Deposit the output asset to the L2 via its portal
return TokenPortal(_outputTokenPortal).depositToAztecPrivate(
_secretHashForRedeemingMintedNotes, amountOut, _secretHashForL1ToL2Message
);
return
TokenPortal(_outputTokenPortal).depositToAztecPrivate(amountOut, _secretHashForL1ToL2Message);
}
}
// docs:end:solidity_uniswap_swap_private
14 changes: 3 additions & 11 deletions l1-contracts/test/portals/UniswapPortal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ contract UniswapPortalTest is Test {
uint24 internal uniswapFeePool = 3000; // 0.3% fee
uint256 internal amountOutMinimum = 0;
bytes32 internal aztecRecipient = bytes32(uint256(0x3));
bytes32 internal secretHashForRedeemingMintedNotes = bytes32(uint256(0x4));

uint256 internal l2BlockNumber = 69;

Expand Down Expand Up @@ -140,26 +139,21 @@ contract UniswapPortalTest is Test {

/**
* L2 to L1 message to be added to the outbox -
* @param _secretHashForRedeemingMintedNotes - The hash of the secret to redeem minted notes privately on Aztec
* @param _caller - designated caller on L1 that will call the swap function - typically address(this)
* Set to address(0) if anyone can call.
*/
function _createUniswapSwapMessagePrivate(
bytes32 _secretHashForRedeemingMintedNotes,
address _caller
) internal view returns (bytes32) {
function _createUniswapSwapMessagePrivate(address _caller) internal view returns (bytes32) {
DataStructures.L2ToL1Msg memory message = DataStructures.L2ToL1Msg({
sender: DataStructures.L2Actor(l2UniswapAddress, 1),
recipient: DataStructures.L1Actor(address(uniswapPortal), block.chainid),
content: Hash.sha256ToField(
abi.encodeWithSignature(
"swap_private(address,uint256,uint24,address,uint256,bytes32,bytes32,address)",
"swap_private(address,uint256,uint24,address,uint256,bytes32,address)",
address(daiTokenPortal),
amount,
uniswapFeePool,
address(wethTokenPortal),
amountOutMinimum,
_secretHashForRedeemingMintedNotes,
secretHash,
_caller
)
Expand Down Expand Up @@ -572,8 +566,7 @@ contract UniswapPortalTest is Test {
})
];

bytes32 messageHashPortalChecksAgainst =
_createUniswapSwapMessagePrivate(secretHashForRedeemingMintedNotes, address(this));
bytes32 messageHashPortalChecksAgainst = _createUniswapSwapMessagePrivate(address(this));

bytes32 actualRoot;
bytes32 consumedRoot;
Expand Down Expand Up @@ -607,7 +600,6 @@ contract UniswapPortalTest is Test {
uniswapFeePool,
address(wethTokenPortal),
amountOutMinimum,
secretHashForRedeemingMintedNotes,
secretHash,
true,
outboxMessageMetadata
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,15 +386,13 @@ contract Test {

#[private]
fn consume_mint_private_message(
secret_hash_for_redeeming_minted_notes: Field,
amount: Field,
secret_for_L1_to_L2_message_consumption: Field,
portal_address: EthAddress,
message_leaf_index: Field,
) {
// Consume L1 to L2 message and emit nullifier
let content_hash =
get_mint_private_content_hash(secret_hash_for_redeeming_minted_notes, amount);
let content_hash = get_mint_private_content_hash(amount);
context.consume_l1_to_l2_message(
content_hash,
secret_for_L1_to_L2_message_consumption,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use dep::aztec::macros::aztec;

#[aztec]
contract TokenBridge {
use dep::aztec::prelude::{AztecAddress, EthAddress, PublicMutable, SharedImmutable};
use dep::aztec::prelude::{AztecAddress, EthAddress, SharedImmutable};

use dep::token_portal_content_hash_lib::{
get_mint_private_content_hash, get_mint_public_content_hash, get_withdraw_content_hash,
Expand All @@ -27,15 +27,15 @@ contract TokenBridge {
// Storage structure, containing all storage, and specifying what slots they use.
#[storage]
struct Storage<Context> {
token: PublicMutable<AztecAddress, Context>,
token: SharedImmutable<AztecAddress, Context>,
portal_address: SharedImmutable<EthAddress, Context>,
}

// Constructs the contract.
#[public]
#[initializer]
fn constructor(token: AztecAddress, portal_address: EthAddress) {
storage.token.write(token);
storage.token.initialize(token);
storage.portal_address.initialize(portal_address);
}
// docs:end:token_bridge_storage_and_constructor
Expand Down Expand Up @@ -65,7 +65,7 @@ contract TokenBridge {
);

// Mint tokens
Token::at(storage.token.read()).mint_public(to, amount).call(&mut context);
Token::at(storage.token.read_public()).mint_public(to, amount).call(&mut context);
}
// docs:end:claim_public

Expand All @@ -84,39 +84,42 @@ contract TokenBridge {
context.message_portal(storage.portal_address.read_public(), content);

// Burn tokens
Token::at(storage.token.read()).burn_public(context.msg_sender(), amount, nonce).call(
&mut context,
);
Token::at(storage.token.read_public())
.burn_public(context.msg_sender(), amount, nonce)
.call(&mut context);
}
// docs:end:exit_to_l1_public

// docs:start:claim_private
// Consumes a L1->L2 message and calls the token contract to mint the appropriate amount in private assets
// User needs to call token.redeem_shield() to get the private assets
// TODO(#8416): Consider creating a truly private claim flow.
/// Claims the bridged tokens and makes them accessible in private. Note that recipient's address is not revealed
/// but the amount is. Hence it's most likely possible to determine to which L1 deposit this claim corresponds to
/// (unless there are multiple pending deposits of the same amount).
/// TODO(#8416): Consider creating a truly private claim flow.
#[private]
fn claim_private(
secret_hash_for_redeeming_minted_notes: Field, // secret hash used to redeem minted notes at a later time. This enables anyone to call this function and mint tokens to a user on their behalf
recipient: AztecAddress, // recipient of the bridged tokens
amount: Field,
secret_for_L1_to_L2_message_consumption: Field, // secret used to consume the L1 to L2 message
message_leaf_index: Field,
) {
// Consume L1 to L2 message and emit nullifier
let content_hash =
get_mint_private_content_hash(secret_hash_for_redeeming_minted_notes, amount);
let content_hash = get_mint_private_content_hash(amount);
context.consume_l1_to_l2_message(
content_hash,
secret_for_L1_to_L2_message_consumption,
storage.portal_address.read_private(),
message_leaf_index,
);

// Mint tokens on L2
// `mint_private` on token is public. So we call an internal public function
// which then calls the public method on the token contract.
// Since the secret_hash is passed, no secret is leaked.
TokenBridge::at(context.this_address())
._call_mint_on_token(amount, secret_hash_for_redeeming_minted_notes)
.enqueue(&mut context);
// Read the token address from storage
let token_address = storage.token.read_private();

// At last we mint the tokens
// docs:start:call_mint_on_token
Token::at(token_address).mint_to_private(context.msg_sender(), recipient, amount).call(
&mut context,
);
// docs:end:call_mint_on_token
}
// docs:end:claim_private

Expand Down Expand Up @@ -147,26 +150,18 @@ contract TokenBridge {
#[public]
#[view]
fn get_token() -> AztecAddress {
storage.token.read()
storage.token.read_public()
}
// docs:end:get_token

// docs:start:call_mint_on_token
// This is a public call as we need to read from public storage.
// Also, note that user hashes their secret in private and only sends the hash in public
// meaning only user can `redeem_shield` at a later time with their secret.
#[public]
#[internal]
fn _call_mint_on_token(amount: Field, secret_hash: Field) {
Token::at(storage.token.read()).mint_private_old(amount, secret_hash).call(&mut context);
}
// docs:end:call_mint_on_token

// docs:start:assert_token_is_same
#[public]
#[internal]
fn _assert_token_is_same(token: AztecAddress) {
assert(storage.token.read().eq(token), "Token address is not the same as seen in storage");
assert(
storage.token.read_public().eq(token),
"Token address is not the same as seen in storage",
);
}
// docs:end:assert_token_is_same
}
Loading

0 comments on commit d513099

Please sign in to comment.