Skip to content

Commit

Permalink
feat: public token e2e, refactor test suite
Browse files Browse the repository at this point in the history
  • Loading branch information
Maddiaa0 committed Jun 14, 2023
1 parent ddd2828 commit ba27e19
Show file tree
Hide file tree
Showing 7 changed files with 407 additions and 302 deletions.
165 changes: 165 additions & 0 deletions yarn-project/end-to-end/src/cross_chain/test_harness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import { AztecNodeService } from "@aztec/aztec-node";
import { AztecRPCServer, Contract, TxStatus, computeMessageSecretHash } from "@aztec/aztec.js";
import { AztecAddress, EthAddress, Fr } from "@aztec/circuits.js";
import { DeployL1Contracts } from "@aztec/ethereum";
import { DebugLogger } from "@aztec/foundation/log";
import { PublicClient, HttpTransport, Chain, getContract } from "viem";
import { deployAndInitializeNonNativeL2TokenContracts, pointToPublicKey } from "../utils.js";
import { OutboxAbi } from "@aztec/l1-artifacts";
import { toBigIntBE, toBufferBE } from "@aztec/foundation/bigint-buffer";
import { sha256 } from '@aztec/foundation/crypto';

const sha256ToField = (buf: Buffer): Fr => {
const tempContent = toBigIntBE(sha256(buf));
return Fr.fromBuffer(toBufferBE(tempContent % Fr.MODULUS, 32));
};

export class CrossChainTestHarness {


static async new(
initialBalance: bigint,
aztecNode: AztecNodeService,
aztecRpcServer: AztecRPCServer,
deployL1ContractsValues: DeployL1Contracts,
accounts: AztecAddress[],
logger: DebugLogger,
): Promise<CrossChainTestHarness> {
const walletClient = deployL1ContractsValues.walletClient;
const publicClient = deployL1ContractsValues.publicClient;

const ethAccount = EthAddress.fromString((await walletClient.getAddresses())[0]);
const [ownerAddress, receiver] = accounts;
const ownerPub = pointToPublicKey(await aztecRpcServer.getAccountPublicKey(ownerAddress));

const outbox = getContract({
address: deployL1ContractsValues.outboxAddress.toString(),
abi: OutboxAbi,
publicClient,
});

// Deploy and initialize all required contracts
logger('Deploying Portal, initializing and deploying l2 contract...');
const contracts = await deployAndInitializeNonNativeL2TokenContracts(
aztecRpcServer,
walletClient,
publicClient,
deployL1ContractsValues!.registryAddress,
initialBalance,
ownerPub,
);
const l2Contract = contracts.l2Contract;
const underlyingERC20 = contracts.underlyingERC20;
const tokenPortal = contracts.tokenPortal;
const tokenPortalAddress = contracts.tokenPortalAddress;
// await expectBalance(accounts[0], initialBalance);
logger('Successfully deployed contracts and initialized portal');

return new CrossChainTestHarness(aztecNode, aztecRpcServer, accounts, logger, l2Contract, ethAccount, tokenPortalAddress, tokenPortal, underlyingERC20, outbox, publicClient, walletClient, ownerAddress, receiver, ownerPub);
}
constructor(
public aztecNode: AztecNodeService,
public aztecRpcServer: AztecRPCServer,
public accounts: AztecAddress[],
public logger: DebugLogger,

public l2Contract: Contract,
public ethAccount: EthAddress,

public tokenPortalAddress: EthAddress,
public tokenPortal: any,
public underlyingERC20: any,
public outbox: any,
public publicClient: PublicClient<HttpTransport, Chain>,
public walletClient: any,

public ownerAddress: AztecAddress,
public receiver: AztecAddress,
public ownerPub: { x: bigint; y: bigint },
) {}


async generateClaimSecret(): Promise<[Fr, Fr]> {
this.logger("Generating a claim secret using pedersen's hash function");
const secret = Fr.random();
const secretHash = await computeMessageSecretHash(secret);
this.logger('Generated claim secret: ', secretHash.toString(true));
return [secret, secretHash];
}

async mintTokensOnL1(amount: bigint, bridgeAmount: bigint) {
this.logger('Minting tokens on L1');
await this.underlyingERC20.write.mint([this.ethAccount.toString(), amount], {} as any);
await this.underlyingERC20.write.approve([this.tokenPortalAddress.toString(), bridgeAmount], {} as any);

expect(await this.underlyingERC20.read.balanceOf([this.ethAccount.toString()])).toBe(amount);
}

async sendTokensToPortal(bridgeAmount: bigint, secretHash: Fr) {
// Deposit tokens to the TokenPortal
const deadline = 2 ** 32 - 1; // max uint32 - 1

this.logger('Sending messages to L1 portal to be consumed privately');
const args = [this.ownerAddress.toString(), bridgeAmount, deadline, secretHash.toString(true), this.ethAccount.toString()] as const;
const { result: messageKeyHex } = await this.tokenPortal.simulate.depositToAztec(args, {
account: this.ethAccount.toString(),
} as any);
await this.tokenPortal.write.depositToAztec(args, {} as any);

return Fr.fromString(messageKeyHex);
}

async performL2Transfer(transferAmount: bigint) {
// send a transfer tx to force through rollup with the message included
const transferTx = this.l2Contract.methods
.transfer(
transferAmount,
pointToPublicKey(await this.aztecRpcServer.getAccountPublicKey(this.ownerAddress)),
pointToPublicKey(await this.aztecRpcServer.getAccountPublicKey(this.receiver)),
)
.send({ from: this.accounts[0] });

await transferTx.isMined(0, 0.1);
const transferReceipt = await transferTx.getReceipt();

expect(transferReceipt.status).toBe(TxStatus.MINED);
}


async checkEntryIsNotInOutbox(withdrawAmount: bigint): Promise<Fr> {
this.logger('Ensure that the entry is not in outbox yet');
const contractInfo = await this.aztecNode.getContractInfo(this.l2Contract.address);
// 0x00f714ce, selector for "withdraw(uint256,address)"
const content = sha256ToField(
Buffer.concat([Buffer.from([0x00, 0xf7, 0x14, 0xce]), toBufferBE(withdrawAmount, 32), this.ethAccount.toBuffer32()]),
);
const entryKey = sha256ToField(
Buffer.concat([
this.l2Contract.address.toBuffer(),
new Fr(1).toBuffer(), // aztec version
contractInfo?.portalContractAddress.toBuffer32() ?? Buffer.alloc(32, 0),
new Fr(this.publicClient.chain.id).toBuffer(), // chain id
content.toBuffer(),
]),
);
expect(await this.outbox.read.contains([entryKey.toString(true)])).toBeFalsy();

return entryKey;
}

async withdrawFundsFromBridgeOnL1 (withdrawAmount: bigint, entryKey: Fr){
this.logger('Send L1 tx to consume entry and withdraw funds');
// Call function on L1 contract to consume the message
const { request: withdrawRequest, result: withdrawEntryKey } = await this.tokenPortal.simulate.withdraw([
withdrawAmount,
this.ethAccount.toString(),
]);

expect(withdrawEntryKey).toBe(entryKey.toString(true));

expect(await this.outbox.read.contains([withdrawEntryKey])).toBeTruthy();

await this.walletClient.writeContract(withdrawRequest);
return withdrawEntryKey;
}
}
Loading

0 comments on commit ba27e19

Please sign in to comment.