Skip to content

Commit

Permalink
test: subscribe to a dapp with a token (#4696)
Browse files Browse the repository at this point in the history
Adds a separate TS package for entrypoints
Adds a contract to act as a canonical fee payment contract
Adds tests for various dapp subscription and fee payment flows
Co-authored-by: Mitchell Tracy <[email protected]>
  • Loading branch information
alexghr authored Feb 24, 2024
1 parent 0f80579 commit 3bbe167
Show file tree
Hide file tree
Showing 56 changed files with 103,966 additions and 709 deletions.
1 change: 1 addition & 0 deletions boxes/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@aztec/accounts": "portal:../yarn-project/accounts",
"@aztec/aztec.js": "portal:../yarn-project/aztec.js",
"@aztec/circuits.js": "portal:../yarn-project/circuits.js",
"@aztec/entrypoints": "portal:../yarn-project/entrypoints",
"@aztec/foundation": "portal:../yarn-project/foundation",
"@aztec/bb.js": "portal:../barretenberg/ts",
"@aztec/circuit-types": "portal:../yarn-project/circuit-types",
Expand Down
55 changes: 34 additions & 21 deletions boxes/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,24 @@ __metadata:
languageName: node
linkType: hard

"@aztec/accounts@portal:../yarn-project/accounts::locator=create-aztec%40workspace%3A.":
"@aztec/accounts@portal:../yarn-project/accounts::locator=create-aztec-app%40workspace%3A.":
version: 0.0.0-use.local
resolution: "@aztec/accounts@portal:../yarn-project/accounts::locator=create-aztec%40workspace%3A."
resolution: "@aztec/accounts@portal:../yarn-project/accounts::locator=create-aztec-app%40workspace%3A."
dependencies:
"@aztec/aztec.js": "workspace:^"
"@aztec/circuit-types": "workspace:^"
"@aztec/circuits.js": "workspace:^"
"@aztec/entrypoints": "workspace:^"
"@aztec/ethereum": "workspace:^"
"@aztec/foundation": "workspace:^"
"@aztec/types": "workspace:^"
tslib: "npm:^2.4.0"
languageName: node
linkType: soft

"@aztec/aztec.js@portal:../yarn-project/aztec.js::locator=create-aztec%40workspace%3A.":
"@aztec/aztec.js@portal:../yarn-project/aztec.js::locator=create-aztec-app%40workspace%3A.":
version: 0.0.0-use.local
resolution: "@aztec/aztec.js@portal:../yarn-project/aztec.js::locator=create-aztec%40workspace%3A."
resolution: "@aztec/aztec.js@portal:../yarn-project/aztec.js::locator=create-aztec-app%40workspace%3A."
dependencies:
"@aztec/circuit-types": "workspace:^"
"@aztec/circuits.js": "workspace:^"
Expand All @@ -57,9 +58,9 @@ __metadata:
languageName: node
linkType: soft

"@aztec/bb.js@portal:../barretenberg/ts::locator=create-aztec%40workspace%3A.":
"@aztec/bb.js@portal:../barretenberg/ts::locator=create-aztec-app%40workspace%3A.":
version: 0.0.0-use.local
resolution: "@aztec/bb.js@portal:../barretenberg/ts::locator=create-aztec%40workspace%3A."
resolution: "@aztec/bb.js@portal:../barretenberg/ts::locator=create-aztec-app%40workspace%3A."
dependencies:
comlink: "npm:^4.4.1"
commander: "npm:^10.0.1"
Expand Down Expand Up @@ -141,9 +142,9 @@ __metadata:
languageName: unknown
linkType: soft

"@aztec/circuit-types@portal:../yarn-project/circuit-types::locator=create-aztec%40workspace%3A.":
"@aztec/circuit-types@portal:../yarn-project/circuit-types::locator=create-aztec-app%40workspace%3A.":
version: 0.0.0-use.local
resolution: "@aztec/circuit-types@portal:../yarn-project/circuit-types::locator=create-aztec%40workspace%3A."
resolution: "@aztec/circuit-types@portal:../yarn-project/circuit-types::locator=create-aztec-app%40workspace%3A."
dependencies:
"@aztec/circuits.js": "workspace:^"
"@aztec/ethereum": "workspace:^"
Expand All @@ -157,9 +158,9 @@ __metadata:
languageName: node
linkType: soft

"@aztec/circuits.js@portal:../yarn-project/circuits.js::locator=create-aztec%40workspace%3A.":
"@aztec/circuits.js@portal:../yarn-project/circuits.js::locator=create-aztec-app%40workspace%3A.":
version: 0.0.0-use.local
resolution: "@aztec/circuits.js@portal:../yarn-project/circuits.js::locator=create-aztec%40workspace%3A."
resolution: "@aztec/circuits.js@portal:../yarn-project/circuits.js::locator=create-aztec-app%40workspace%3A."
dependencies:
"@aztec/bb.js": "portal:../../barretenberg/ts"
"@aztec/foundation": "workspace:^"
Expand All @@ -170,9 +171,21 @@ __metadata:
languageName: node
linkType: soft

"@aztec/ethereum@portal:../yarn-project/ethereum::locator=create-aztec%40workspace%3A.":
"@aztec/entrypoints@portal:../yarn-project/entrypoints::locator=create-aztec-app%40workspace%3A.":
version: 0.0.0-use.local
resolution: "@aztec/ethereum@portal:../yarn-project/ethereum::locator=create-aztec%40workspace%3A."
resolution: "@aztec/entrypoints@portal:../yarn-project/entrypoints::locator=create-aztec-app%40workspace%3A."
dependencies:
"@aztec/aztec.js": "workspace:^"
"@aztec/circuit-types": "workspace:^"
"@aztec/circuits.js": "workspace:^"
"@aztec/foundation": "workspace:^"
tslib: "npm:^2.4.0"
languageName: node
linkType: soft

"@aztec/ethereum@portal:../yarn-project/ethereum::locator=create-aztec-app%40workspace%3A.":
version: 0.0.0-use.local
resolution: "@aztec/ethereum@portal:../yarn-project/ethereum::locator=create-aztec-app%40workspace%3A."
dependencies:
"@aztec/foundation": "workspace:^"
dotenv: "npm:^16.0.3"
Expand All @@ -181,9 +194,9 @@ __metadata:
languageName: node
linkType: soft

"@aztec/foundation@portal:../yarn-project/foundation::locator=create-aztec%40workspace%3A.":
"@aztec/foundation@portal:../yarn-project/foundation::locator=create-aztec-app%40workspace%3A.":
version: 0.0.0-use.local
resolution: "@aztec/foundation@portal:../yarn-project/foundation::locator=create-aztec%40workspace%3A."
resolution: "@aztec/foundation@portal:../yarn-project/foundation::locator=create-aztec-app%40workspace%3A."
dependencies:
"@aztec/bb.js": "portal:../../barretenberg/ts"
"@koa/cors": "npm:^4.0.0"
Expand All @@ -208,9 +221,9 @@ __metadata:
languageName: node
linkType: soft

"@aztec/protocol-contracts@portal:../yarn-project/protocol-contracts::locator=create-aztec%40workspace%3A.":
"@aztec/protocol-contracts@portal:../yarn-project/protocol-contracts::locator=create-aztec-app%40workspace%3A.":
version: 0.0.0-use.local
resolution: "@aztec/protocol-contracts@portal:../yarn-project/protocol-contracts::locator=create-aztec%40workspace%3A."
resolution: "@aztec/protocol-contracts@portal:../yarn-project/protocol-contracts::locator=create-aztec-app%40workspace%3A."
dependencies:
"@aztec/circuits.js": "workspace:^"
"@aztec/foundation": "workspace:^"
Expand All @@ -220,9 +233,9 @@ __metadata:
languageName: node
linkType: soft

"@aztec/types@portal:../yarn-project/types::locator=create-aztec%40workspace%3A.":
"@aztec/types@portal:../yarn-project/types::locator=create-aztec-app%40workspace%3A.":
version: 0.0.0-use.local
resolution: "@aztec/types@portal:../yarn-project/types::locator=create-aztec%40workspace%3A."
resolution: "@aztec/types@portal:../yarn-project/types::locator=create-aztec-app%40workspace%3A."
dependencies:
"@aztec/ethereum": "workspace:^"
"@aztec/foundation": "workspace:^"
Expand Down Expand Up @@ -3538,9 +3551,9 @@ __metadata:
languageName: node
linkType: hard

"create-aztec@workspace:.":
"create-aztec-app@workspace:.":
version: 0.0.0-use.local
resolution: "create-aztec@workspace:."
resolution: "create-aztec-app@workspace:."
dependencies:
"@iarna/toml": "npm:^2.2.5"
"@inquirer/confirm": "npm:^3.0.0"
Expand All @@ -3552,7 +3565,7 @@ __metadata:
node-pty: "npm:^1.0.0"
tiged: "npm:^2.12.6"
bin:
create-aztec: npx.js
create-aztec-app: npx.js
languageName: unknown
linkType: soft

Expand Down
98 changes: 98 additions & 0 deletions l1-contracts/test/portals/GasPortal.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
pragma solidity >=0.8.18;

import {IERC20} from "@oz/token/ERC20/IERC20.sol";
import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol";

// Messaging
import {IRegistry} from "../../src/core/interfaces/messagebridge/IRegistry.sol";
import {IInbox} from "../../src/core/interfaces/messagebridge/IInbox.sol";
import {DataStructures} from "../../src/core/libraries/DataStructures.sol";
// docs:start:content_hash_sol_import
import {Hash} from "../../src/core/libraries/Hash.sol";
// docs:end:content_hash_sol_import

// docs:start:init
contract GasPortal {
using SafeERC20 for IERC20;

IRegistry public registry;
IERC20 public underlying;
bytes32 public l2TokenAddress;

function initialize(address _registry, address _underlying, bytes32 _l2TokenAddress) external {
registry = IRegistry(_registry);
underlying = IERC20(_underlying);
l2TokenAddress = _l2TokenAddress;
}
// docs:end:init

// docs:start:deposit_public
/**
* @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 _canceller - The address that can cancel the L1 to L2 message
* @param _deadline - The timestamp after which the entry can be cancelled
* @param _secretHash - The hash of the secret consumable message. The hash should be 254 bits (so it can fit in a Field element)
* @return - The key of the entry in the Inbox
*/
function depositToAztecPublic(
bytes32 _to,
uint256 _amount,
address _canceller,
uint32 _deadline,
bytes32 _secretHash
) external payable returns (bytes32) {
// Preamble
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_public(bytes32,uint256,address)", _to, _amount, _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, _secretHash);
}

/**
* @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
* @param _deadline - The timestamp after which the entry can be cancelled
* @param _secretHash - The hash of the secret consumable message in the original message
* @param _fee - The fee paid to the sequencer
* @return The key of the entry in the Inbox
*/
function cancelL1ToAztecMessagePublic(
bytes32 _to,
uint256 _amount,
uint32 _deadline,
bytes32 _secretHash,
uint64 _fee
) external returns (bytes32) {
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_public(bytes32,uint256,address)", _to, _amount, msg.sender)
),
secretHash: _secretHash,
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;
}
}
5 changes: 2 additions & 3 deletions noir-projects/aztec-nr/aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use dep::protocol_types::{
},
contrakt::{storage_read::StorageRead, storage_update_request::StorageUpdateRequest},
grumpkin_private_key::GrumpkinPrivateKey, hash::hash_args, header::Header,
messaging::l2_to_l1_message::L2ToL1Message, utils::reader::Reader
messaging::l2_to_l1_message::L2ToL1Message, utils::reader::Reader, traits::is_empty
};
use dep::std::option::Option;

Expand Down Expand Up @@ -71,8 +71,7 @@ impl PrivateContext {
pub fn new(inputs: PrivateContextInputs, args_hash: Field) -> PrivateContext {
let side_effect_counter = inputs.call_context.start_side_effect_counter;
let mut min_revertible_side_effect_counter = 0;
// Note. The side effect counter is 2 when this is the initial call
if (side_effect_counter == 2) {
if is_empty(inputs.call_context.msg_sender) {
min_revertible_side_effect_counter = side_effect_counter;
}
PrivateContext {
Expand Down
4 changes: 2 additions & 2 deletions noir-projects/aztec-nr/aztec/src/state_vars/singleton.nr
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ impl<Note> Singleton<Note> {
// When this initialization nullifier is emitted, an observer could do a dictionary or rainbow attack to learn the preimage of this nullifier to deduce the storage slot and contract address.
// For some applications, leaking the details that a particular state variable of a particular contract has been initialized will be unacceptable.
// Under such circumstances, such application developers might wish to _not_ use this state variable type.
// This is especially dangerous for initial assignment to elements of a `Map<AztecAddress, Singleton>` type (for example), because the storage slot often also identifies an actor. e.g.
// This is especially dangerous for initial assignment to elements of a `Map<AztecAddress, Singleton>` type (for example), because the storage slot often also identifies an actor. e.g.
// the initial assignment to `my_map.at(msg.sender)` will leak: `msg.sender`, the fact that an element of `my_map` was assigned-to for the first time, and the contract_address.
// Note: subsequent nullification of this state variable, via the `replace` method will not be leaky, if the `compute_nullifier()` method of the underlying note is designed to ensure privacy.
// Note: subsequent nullification of this state variable, via the `replace` method will not be leaky, if the `compute_nullifier()` method of the underlying note is designed to ensure privacy.
// For example, if the `compute_nullifier()` method injects the secret key of a note owner into the computed nullifier's preimage.
pub fn compute_initialization_nullifier(self) -> Field {
pedersen_hash(
Expand Down
2 changes: 2 additions & 0 deletions noir-projects/noir-contracts/Nargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[workspace]
members = [
"contracts/app_subscription_contract",
"contracts/avm_test_contract",
"contracts/fpc_contract",
"contracts/benchmarking_contract",
"contracts/card_game_contract",
"contracts/child_contract",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "app_subscription_contract"
authors = [""]
compiler_version = ">=0.18.0"
type = "contract"

[dependencies]
aztec = { path = "../../../aztec-nr/aztec" }
authwit = { path = "../../../aztec-nr/authwit" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use dep::aztec::context::PrivateContext;
use dep::aztec::protocol_types::{
constants::GENERATOR_INDEX__SIGNATURE_PAYLOAD, hash::pedersen_hash, traits::{Hash, Serialize},
address::AztecAddress
};

use dep::authwit::entrypoint::function_call::{FunctionCall, FUNCTION_CALL_SIZE_IN_BYTES};

// FUNCTION_CALL_SIZE * DAPP_MAX_CALLS + 1
global DAPP_PAYLOAD_SIZE: Field = 5;
// FUNCTION_CALL_SIZE_IN_BYTES * DAPP_MAX_CALLS + 32
global DAPP_PAYLOAD_SIZE_IN_BYTES: Field = 129;

global DAPP_MAX_CALLS: Field = 1;

// Note: If you change the following struct you have to update default_entrypoint.ts
// docs:start:app-payload-struct
struct DAppPayload {
function_calls: [FunctionCall; DAPP_MAX_CALLS],
nonce: Field,
}
// docs:end:app-payload-struct

impl Serialize<DAPP_PAYLOAD_SIZE> for DAppPayload {
// Serializes the entrypoint struct
fn serialize(self) -> [Field; DAPP_PAYLOAD_SIZE] {
let mut fields: BoundedVec<Field, DAPP_PAYLOAD_SIZE> = BoundedVec::new(0);
for call in self.function_calls {
fields.extend_from_array(call.serialize());
}
fields.push(self.nonce);
fields.storage
}
}

impl Hash for DAppPayload {
fn hash(self) -> Field {
pedersen_hash(
self.serialize(),
GENERATOR_INDEX__SIGNATURE_PAYLOAD
)
}
}

impl DAppPayload {
// Serializes the payload as an array of bytes. Useful for hashing with sha256.
fn to_be_bytes(self) -> [u8; DAPP_PAYLOAD_SIZE_IN_BYTES] {
let mut bytes: BoundedVec<u8, DAPP_PAYLOAD_SIZE_IN_BYTES> = BoundedVec::new(0);

for i in 0..DAPP_MAX_CALLS {
bytes.extend_from_array(self.function_calls[i].to_be_bytes());
}
bytes.extend_from_array(self.nonce.to_be_bytes(32));

bytes.storage
}

// Executes all private and public calls
// docs:start:entrypoint-execute-calls
fn execute_calls(self, context: &mut PrivateContext, target_address: AztecAddress) {
for call in self.function_calls {
// whitelist the calls that the user can do only go to the expected Dapp contract
assert(call.target_address == target_address);
if call.is_public {
context.call_public_function_with_packed_args(
call.target_address,
call.function_selector,
call.args_hash,
false,
false
);
} else {
let _result = context.call_private_function_with_packed_args(
call.target_address,
call.function_selector,
call.args_hash,
false,
false
);
}
}
}
// docs:end:entrypoint-execute-calls
}
Loading

0 comments on commit 3bbe167

Please sign in to comment.