From b67e080a4f74b8fce71897b61234846834cea22e Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 6 Jan 2023 10:14:42 -0800 Subject: [PATCH 01/39] cleanup TaikoL1 tests with refactored code to utils folders before attempting tokenomics tests, as code will largely need to be re-used. --- .../test/L1/TestTaikoL1EnableTokenomics.sol | 73 ++++ .../test/L1/TaikoL1.integration.test.ts | 226 ++++++++++++ packages/protocol/test/L1/TaikoL1.test.ts | 332 +----------------- packages/protocol/test/L2/TaikoL2.test.ts | 5 +- packages/protocol/test/test_tokenomics.sh | 83 +++++ .../test/tokenomics/Tokenomics.test.ts | 3 + .../protocol/test/utils/block_metadata.ts | 17 + packages/protocol/test/utils/bridge.ts | 0 packages/protocol/test/utils/bytes.ts | 7 + packages/protocol/test/utils/commit.ts | 31 ++ packages/protocol/test/utils/encoding.ts | 13 + packages/protocol/test/utils/propose.ts | 46 +++ packages/protocol/test/utils/tokenomics.ts | 0 13 files changed, 503 insertions(+), 333 deletions(-) create mode 100644 packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol create mode 100644 packages/protocol/test/L1/TaikoL1.integration.test.ts create mode 100644 packages/protocol/test/test_tokenomics.sh create mode 100644 packages/protocol/test/tokenomics/Tokenomics.test.ts create mode 100644 packages/protocol/test/utils/block_metadata.ts create mode 100644 packages/protocol/test/utils/bridge.ts create mode 100644 packages/protocol/test/utils/bytes.ts create mode 100644 packages/protocol/test/utils/commit.ts create mode 100644 packages/protocol/test/utils/encoding.ts create mode 100644 packages/protocol/test/utils/propose.ts create mode 100644 packages/protocol/test/utils/tokenomics.ts diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol new file mode 100644 index 0000000000..3a12d19c13 --- /dev/null +++ b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +// +// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ +// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ +// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ +// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ +// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ +// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ +pragma solidity ^0.8.9; + +import {IProofVerifier} from "../../L1/ProofVerifier.sol"; +import "../../L1/TaikoL1.sol"; + +contract TestTaikoL1EnableTokenomics is TaikoL1, IProofVerifier { + function getConfig() + public + pure + override + returns (TaikoData.Config memory config) + { + config.chainId = 167; + // up to 2048 pending blocks + config.maxNumBlocks = 4; + config.blockHashHistory = 3; + // This number is calculated from maxNumBlocks to make + // the 'the maximum value of the multiplier' close to 20.0 + config.zkProofsPerBlock = 1; + config.maxVerificationsPerTx = 2; + config.commitConfirmations = 1; + config.maxProofsPerForkChoice = 5; + config.blockMaxGasLimit = 30000000; // TODO + config.maxTransactionsPerBlock = 20; // TODO + config.maxBytesPerTxList = 10240; // TODO + config.minTxGasLimit = 21000; // TODO + config.anchorTxGasLimit = 250000; + config.feePremiumLamda = 590; + config.rewardBurnBips = 100; // 100 basis points or 1% + config.proposerDepositPctg = 25; // 25% + + // Moving average factors + config.feeBaseMAF = 1024; + config.blockTimeMAF = 64; + config.proofTimeMAF = 64; + + config.rewardMultiplierPctg = 400; // 400% + config.feeGracePeriodPctg = 125; // 125% + config.feeMaxPeriodPctg = 375; // 375% + config.blockTimeCap = 48 seconds; + config.proofTimeCap = 60 minutes; + config.boostrapDiscountHalvingPeriod = 180 days; + config.initialUncleDelay = 1 minutes; + config.enableTokenomics = true; + } + + function verifyZKP( + bytes memory /*verificationKey*/, + bytes calldata /*zkproof*/, + bytes32 /*blockHash*/, + address /*prover*/, + bytes32 /*txListHash*/ + ) public pure override returns (bool) { + return true; + } + + function verifyMKP( + bytes memory /*key*/, + bytes memory /*value*/, + bytes memory /*proof*/, + bytes32 /*root*/ + ) public pure override returns (bool) { + return true; + } +} diff --git a/packages/protocol/test/L1/TaikoL1.integration.test.ts b/packages/protocol/test/L1/TaikoL1.integration.test.ts new file mode 100644 index 0000000000..a50fbfa936 --- /dev/null +++ b/packages/protocol/test/L1/TaikoL1.integration.test.ts @@ -0,0 +1,226 @@ +import { expect } from "chai"; +import { BigNumber, ethers as ethersLib } from "ethers"; +import { ethers } from "hardhat"; +import { TaikoL1, TaikoL2 } from "../../typechain"; +import { BlockMetadata } from "../utils/block_metadata"; +import { commitBlock, generateCommitHash } from "../utils/commit"; +import { buildProposeBlockInputs, proposeBlock } from "../utils/propose"; + +describe("integration:TaikoL1", function () { + let taikoL1: TaikoL1; + let taikoL2: TaikoL2; + let l2Provider: ethersLib.providers.JsonRpcProvider; + let l2Signer: ethersLib.Signer; + + beforeEach(async function () { + l2Provider = new ethers.providers.JsonRpcProvider( + "http://localhost:28545" + ); + + l2Signer = await l2Provider.getSigner( + ( + await l2Provider.listAccounts() + )[0] + ); + + const addressManager = await ( + await ethers.getContractFactory("AddressManager") + ).deploy(); + await addressManager.init(); + + const libReceiptDecoder = await ( + await ethers.getContractFactory("LibReceiptDecoder") + ).deploy(); + + const libTxDecoder = await ( + await ethers.getContractFactory("LibTxDecoder") + ).deploy(); + + const libProposing = await ( + await ethers.getContractFactory("LibProposing") + ).deploy(); + + const libProving = await ( + await ethers.getContractFactory("LibProving", { + libraries: { + LibReceiptDecoder: libReceiptDecoder.address, + LibTxDecoder: libTxDecoder.address, + }, + }) + ).deploy(); + + const libVerifying = await ( + await ethers.getContractFactory("LibVerifying") + ).deploy(); + + const l2AddressManager = await ( + await ethers.getContractFactory("AddressManager") + ) + .connect(l2Signer) + .deploy(); + await l2AddressManager.init(); + + // Deploying TaikoL2 Contract linked with LibTxDecoder (throws error otherwise) + const l2LibTxDecoder = await ( + await ethers.getContractFactory("LibTxDecoder") + ) + .connect(l2Signer) + .deploy(); + + taikoL2 = await ( + await ethers.getContractFactory("TaikoL2", { + libraries: { + LibTxDecoder: l2LibTxDecoder.address, + }, + }) + ) + .connect(l2Signer) + .deploy(l2AddressManager.address); + + const genesisHash = taikoL2.deployTransaction.blockHash; + + taikoL1 = await ( + await ethers.getContractFactory("TestTaikoL1", { + libraries: { + LibVerifying: libVerifying.address, + LibProposing: libProposing.address, + LibProving: libProving.address, + }, + }) + ).deploy(); + + const feeBase = BigNumber.from(10).pow(18); + + await taikoL1.init( + addressManager.address, + genesisHash as string, + feeBase + ); + }); + + describe("isCommitValid()", async function () { + it("should not be valid", async function () { + const block = await l2Provider.getBlock("latest"); + const commit = generateCommitHash(block); + + const isCommitValid = await taikoL1.isCommitValid( + 1, + 1, + commit.hash + ); + + expect(isCommitValid).to.be.eq(false); + }); + }); + + describe("getProposedBlock()", function () { + it("proposed block does not exist", async function () { + const block = await taikoL1.getProposedBlock(123); + expect(block[0]).to.be.eq(ethers.constants.HashZero); + expect(block[1]).to.be.eq(ethers.constants.AddressZero); + expect(block[2]).to.be.eq(BigNumber.from(0)); + }); + }); + describe("commitBlock() -> proposeBlock() integration", async function () { + it("should revert with invalid meta", async function () { + const block = await l2Provider.getBlock("latest"); + const { tx, commit } = await commitBlock(taikoL1, block); + + await expect( + proposeBlock( + taikoL1, + block, + commit.txListHash, + tx.blockNumber as number, + 1, + block.gasLimit + ) + ).to.be.revertedWith("L1:placeholder"); + }); + + it("should revert with invalid gasLimit", async function () { + const block = await l2Provider.getBlock("latest"); + const { tx, commit } = await commitBlock(taikoL1, block); + + // blockMetadata is inputs[0], txListBytes = inputs[1] + const config = await taikoL1.getConfig(); + const gasLimit = config[7]; + await proposeBlock( + taikoL1, + block, + commit.txListHash, + tx.blockNumber as number, + 0, + block.gasLimit + ); + + await expect( + proposeBlock( + taikoL1, + block, + commit.txListHash, + tx.blockNumber as number, + 0, + gasLimit.add(1) + ) + ).to.be.revertedWith("L1:gasLimit"); + }); + + it("should revert with invalid extraData", async function () { + const block = await l2Provider.getBlock("latest"); + const { tx, commit } = await commitBlock(taikoL1, block); + + const meta: BlockMetadata = { + id: 0, + l1Height: 0, + l1Hash: ethers.constants.HashZero, + beneficiary: block.miner, + txListHash: commit.txListHash, + mixHash: ethers.constants.HashZero, + extraData: ethers.utils.hexlify(ethers.utils.randomBytes(33)), // invalid extradata + gasLimit: block.gasLimit, + timestamp: 0, + commitSlot: 1, + commitHeight: tx.blockNumber as number, + }; + + const inputs = buildProposeBlockInputs(block, meta); + + await expect(taikoL1.proposeBlock(inputs)).to.be.revertedWith( + "L1:extraData" + ); + }); + + it("should commit and be able to propose", async function () { + const block = await l2Provider.getBlock("latest"); + const { tx, commit } = await commitBlock(taikoL1, block); + + await proposeBlock( + taikoL1, + block, + commit.txListHash, + tx.blockNumber as number, + 0, + block.gasLimit + ); + + const stateVariables = await taikoL1.getStateVariables(); + const nextBlockId = stateVariables[4]; + const proposedBlock = await taikoL1.getProposedBlock( + nextBlockId.sub(1) + ); + + expect(proposedBlock[0]).not.to.be.eq(ethers.constants.HashZero); + expect(proposedBlock[2]).not.to.be.eq(ethers.constants.AddressZero); + expect(proposedBlock[3]).not.to.be.eq(BigNumber.from(0)); + + const isCommitValid = await taikoL1.isCommitValid( + 1, + tx.blockNumber as number, + commit.hash + ); + + expect(isCommitValid).to.be.eq(true); + }); + }); +}); diff --git a/packages/protocol/test/L1/TaikoL1.test.ts b/packages/protocol/test/L1/TaikoL1.test.ts index 5b25613a33..11665c4339 100644 --- a/packages/protocol/test/L1/TaikoL1.test.ts +++ b/packages/protocol/test/L1/TaikoL1.test.ts @@ -1,8 +1,8 @@ import { expect } from "chai"; -import { BigNumber, ContractTransaction, ethers as ethersLib } from "ethers"; +import { BigNumber } from "ethers"; import { ethers } from "hardhat"; -import RLP from "rlp"; -import { TaikoL1, TaikoL2 } from "../../typechain"; +import { TaikoL1 } from "../../typechain"; +import { randomBytes32 } from "../utils/bytes"; describe("TaikoL1", function () { let taikoL1: TaikoL1; @@ -156,329 +156,3 @@ describe("TaikoL1", function () { }); }); }); - -describe("integration:TaikoL1", function () { - let taikoL1: TaikoL1; - let taikoL2: TaikoL2; - let l2Provider: ethersLib.providers.JsonRpcProvider; - let l2Signer: ethersLib.Signer; - - beforeEach(async function () { - l2Provider = new ethers.providers.JsonRpcProvider( - "http://localhost:28545" - ); - - l2Signer = await l2Provider.getSigner( - (await l2Provider.listAccounts())[0] - ); - - const addressManager = await ( - await ethers.getContractFactory("AddressManager") - ).deploy(); - await addressManager.init(); - - const libReceiptDecoder = await ( - await ethers.getContractFactory("LibReceiptDecoder") - ).deploy(); - - const libTxDecoder = await ( - await ethers.getContractFactory("LibTxDecoder") - ).deploy(); - - const libProposing = await ( - await ethers.getContractFactory("LibProposing") - ).deploy(); - - const libProving = await ( - await ethers.getContractFactory("LibProving", { - libraries: { - LibReceiptDecoder: libReceiptDecoder.address, - LibTxDecoder: libTxDecoder.address, - }, - }) - ).deploy(); - - const libVerifying = await ( - await ethers.getContractFactory("LibVerifying") - ).deploy(); - - const l2AddressManager = await ( - await ethers.getContractFactory("AddressManager") - ) - .connect(l2Signer) - .deploy(); - await l2AddressManager.init(); - - // Deploying TaikoL2 Contract linked with LibTxDecoder (throws error otherwise) - const l2LibTxDecoder = await ( - await ethers.getContractFactory("LibTxDecoder") - ) - .connect(l2Signer) - .deploy(); - - taikoL2 = await ( - await ethers.getContractFactory("TaikoL2", { - libraries: { - LibTxDecoder: l2LibTxDecoder.address, - }, - }) - ) - .connect(l2Signer) - .deploy(l2AddressManager.address); - - const genesisHash = taikoL2.deployTransaction.blockHash; - - taikoL1 = await ( - await ethers.getContractFactory("TestTaikoL1", { - libraries: { - LibVerifying: libVerifying.address, - LibProposing: libProposing.address, - LibProving: libProving.address, - }, - }) - ).deploy(); - - const feeBase = BigNumber.from(10).pow(18); - - await taikoL1.init( - addressManager.address, - genesisHash as string, - feeBase - ); - }); - - describe("isCommitValid()", async function () { - it("should not be valid", async function () { - const block = await l2Provider.getBlock("latest"); - const txListHash = ethers.utils.keccak256( - RLP.encode(block.transactions) - ); - const hash = ethers.utils.keccak256( - ethers.utils.solidityPack( - ["address", "bytes32"], - [block.miner, txListHash] - ) - ); - - const isCommitValid = await taikoL1.isCommitValid(1, 1, hash); - - expect(isCommitValid).to.be.eq(false); - }); - }); - - describe("getProposedBlock()", function () { - it("proposed block does not exist", async function () { - const block = await taikoL1.getProposedBlock(123); - expect(block[0]).to.be.eq(ethers.constants.HashZero); - expect(block[1]).to.be.eq(ethers.constants.AddressZero); - expect(block[2]).to.be.eq(BigNumber.from(0)); - }); - }); - describe("commitBlock() -> proposeBlock() integration", async function () { - it("should revert with invalid meta", async function () { - const block = await l2Provider.getBlock("latest"); - const txListHash = ethers.utils.keccak256( - RLP.encode(block.transactions) - ); - const hash = ethers.utils.keccak256( - ethers.utils.solidityPack( - ["address", "bytes32"], - [block.miner, txListHash] - ) - ); - let tx: ContractTransaction; - expect((tx = await taikoL1.commitBlock(1, hash))).to.emit( - taikoL1, - "BlockCommitted" - ); - - // blockMetadata is inputs[0], txListBytes = inputs[1] - const inputs = []; - const meta = { - id: 1, // invalid because id should be 0 - l1Height: 0, - l1Hash: ethers.constants.HashZero, - beneficiary: block.miner, - txListHash: txListHash, - mixHash: ethers.constants.HashZero, - extraData: block.extraData, - gasLimit: block.gasLimit, - timestamp: 0, - commitSlot: 1, - commitHeight: tx.blockNumber, - }; - - const blockMetadataBytes = encodeBlockMetadata(meta); - - inputs[0] = blockMetadataBytes; - inputs[1] = RLP.encode(block.transactions); - - await expect(taikoL1.proposeBlock(inputs)).to.be.revertedWith( - "L1:placeholder" - ); - }); - - it("should revert with invalid gasLimit", async function () { - const block = await l2Provider.getBlock("latest"); - const txListHash = ethers.utils.keccak256( - RLP.encode(block.transactions) - ); - const hash = ethers.utils.keccak256( - ethers.utils.solidityPack( - ["address", "bytes32"], - [block.miner, txListHash] - ) - ); - let tx: ContractTransaction; - expect((tx = await taikoL1.commitBlock(1, hash))).to.emit( - taikoL1, - "BlockCommitted" - ); - - // blockMetadata is inputs[0], txListBytes = inputs[1] - const config = await taikoL1.getConfig(); - const gasLimit = config[7]; - const inputs = []; - const meta = { - id: 0, - l1Height: 0, - l1Hash: ethers.constants.HashZero, - beneficiary: block.miner, - txListHash: txListHash, - mixHash: ethers.constants.HashZero, - extraData: block.extraData, - gasLimit: gasLimit.add(1), - timestamp: 0, - commitSlot: 1, - commitHeight: tx.blockNumber, - }; - - const blockMetadataBytes = encodeBlockMetadata(meta); - - inputs[0] = blockMetadataBytes; - inputs[1] = RLP.encode(block.transactions); - - await expect(taikoL1.proposeBlock(inputs)).to.be.revertedWith( - "L1:gasLimit" - ); - }); - - it("should revert with invalid extraData", async function () { - const block = await l2Provider.getBlock("latest"); - const txListHash = ethers.utils.keccak256( - RLP.encode(block.transactions) - ); - const hash = ethers.utils.keccak256( - ethers.utils.solidityPack( - ["address", "bytes32"], - [block.miner, txListHash] - ) - ); - let tx: ContractTransaction; - expect((tx = await taikoL1.commitBlock(1, hash))).to.emit( - taikoL1, - "BlockCommitted" - ); - - // blockMetadata is inputs[0], txListBytes = inputs[1] - const inputs = []; - const meta = { - id: 0, - l1Height: 0, - l1Hash: ethers.constants.HashZero, - beneficiary: block.miner, - txListHash: txListHash, - mixHash: ethers.constants.HashZero, - extraData: ethers.utils.hexlify(ethers.utils.randomBytes(33)), // invalid extradata - gasLimit: block.gasLimit, - timestamp: 0, - commitSlot: 1, - commitHeight: tx.blockNumber, - }; - - const blockMetadataBytes = encodeBlockMetadata(meta); - - inputs[0] = blockMetadataBytes; - inputs[1] = RLP.encode(block.transactions); - - await expect(taikoL1.proposeBlock(inputs)).to.be.revertedWith( - "L1:extraData" - ); - }); - - it("should commit and be able to propose", async function () { - const block = await l2Provider.getBlock("latest"); - const txListHash = ethers.utils.keccak256( - RLP.encode(block.transactions) - ); - const hash = ethers.utils.keccak256( - ethers.utils.solidityPack( - ["address", "bytes32"], - [block.miner, txListHash] - ) - ); - let tx: ContractTransaction; - expect((tx = await taikoL1.commitBlock(1, hash))).to.emit( - taikoL1, - "BlockCommitted" - ); - - // blockMetadata is inputs[0], txListBytes = inputs[1] - const inputs = []; - const meta = { - id: 0, - l1Height: 0, - l1Hash: ethers.constants.HashZero, - beneficiary: block.miner, - txListHash: txListHash, - mixHash: ethers.constants.HashZero, - extraData: block.extraData, - gasLimit: block.gasLimit, - timestamp: 0, - commitSlot: 1, - commitHeight: tx.blockNumber, - }; - - const blockMetadataBytes = encodeBlockMetadata(meta); - - inputs[0] = blockMetadataBytes; - inputs[1] = RLP.encode(block.transactions); - - expect(await taikoL1.proposeBlock(inputs)).to.emit( - taikoL1, - "BlockProposed" - ); - - const stateVariables = await taikoL1.getStateVariables(); - const nextBlockId = stateVariables[4]; - const proposedBlock = await taikoL1.getProposedBlock( - nextBlockId.sub(1) - ); - - expect(proposedBlock[0]).not.to.be.eq(ethers.constants.HashZero); - expect(proposedBlock[2]).not.to.be.eq(ethers.constants.AddressZero); - expect(proposedBlock[3]).not.to.be.eq(BigNumber.from(0)); - - const isCommitValid = await taikoL1.isCommitValid( - 1, - tx.blockNumber as number, - hash - ); - - expect(isCommitValid).to.be.eq(true); - }); - }); -}); - -function randomBytes32() { - return ethers.utils.hexlify(ethers.utils.randomBytes(32)); -} - -function encodeBlockMetadata(meta: unknown) { - return ethers.utils.defaultAbiCoder.encode( - [ - "tuple(uint256 id, uint256 l1Height, bytes32 l1Hash, address beneficiary, bytes32 txListHash, bytes32 mixHash, bytes extraData, uint64 gasLimit, uint64 timestamp, uint64 commitHeight, uint64 commitSlot)", - ], - [meta] - ); -} diff --git a/packages/protocol/test/L2/TaikoL2.test.ts b/packages/protocol/test/L2/TaikoL2.test.ts index ff9f550288..4195f9b5fa 100644 --- a/packages/protocol/test/L2/TaikoL2.test.ts +++ b/packages/protocol/test/L2/TaikoL2.test.ts @@ -1,6 +1,7 @@ import { expect } from "chai"; import { ethers } from "hardhat"; import { TaikoL2 } from "../../typechain"; +import { randomBytes32 } from "../utils/bytes"; describe("TaikoL2", function () { let taikoL2: TaikoL2; @@ -47,7 +48,3 @@ describe("TaikoL2", function () { }); }); }); - -function randomBytes32() { - return ethers.utils.hexlify(ethers.utils.randomBytes(32)); -} diff --git a/packages/protocol/test/test_tokenomics.sh b/packages/protocol/test/test_tokenomics.sh new file mode 100644 index 0000000000..2ccd451863 --- /dev/null +++ b/packages/protocol/test/test_tokenomics.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +set -eou pipefail + +DIR=$(cd $(dirname ${BASH_SOURCE[0]}); pwd) +TEST_NODE_CONTAINER_NAME_L1="test-ethereum-node-l1" +TEST_NODE_CONTAINER_NAME_L2="test-ethereum-node-l2" +TEST_IMPORT_TEST_ACCOUNT_ETH_JOB_NAME="import-test-account-eth" +TEST_ACCOUNT_ADDRESS="0xdf08f82de32b8d460adbe8d72043e3a7e25a3b39" +TEST_ACCOUNT_PRIV_KEY="2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501200" + +if ! command -v docker &> /dev/null 2>&1; then + echo "ERROR: `docker` command not found" + exit 1 +fi + +if ! docker info > /dev/null 2>&1; then + echo "ERROR: docker daemon isn't running" + exit 1 +fi + +docker rm --force $TEST_NODE_CONTAINER_NAME_L1 \ + $TEST_NODE_CONTAINER_NAME_L2 \ + $TEST_IMPORT_TEST_ACCOUNT_ETH_JOB_NAME &> /dev/null + +# Start a test ethereum node +docker run -d \ + --name $TEST_NODE_CONTAINER_NAME_L1 \ + -p 18545:8545 \ + ethereum/client-go:latest \ + --dev --http --http.addr 0.0.0.0 --http.vhosts "*" \ + --http.api debug,eth,net,web3,txpool,miner + +docker run -d \ + --name $TEST_NODE_CONTAINER_NAME_L2 \ + -p 28545:8545 \ + gcr.io/evmchain/hardhat-node:latest \ + hardhat node --hostname "0.0.0.0" + +function waitTestNode { + echo "Waiting for test node: $1" + # Wait till the test node fully started + RETRIES=30 + i=0 + until curl \ + --silent \ + --fail \ + --noproxy localhost \ + -X POST \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","id":0,"method":"eth_chainId","params":[]}' \ + $1 + do + sleep 1 + if [ $i -eq $RETRIES ]; then + echo 'Timed out waiting for test node' + exit 1 + fi + ((i=i+1)) + done +} + +waitTestNode http://localhost:18545 +waitTestNode http://localhost:28545 + +# Import ETHs from the random pre-allocated developer account to the test account +docker run -d \ + --name $TEST_IMPORT_TEST_ACCOUNT_ETH_JOB_NAME \ + --add-host host.docker.internal:host-gateway \ + ethereum/client-go:latest \ + --exec 'eth.sendTransaction({from: eth.coinbase, to: "'0xdf08f82de32b8d460adbe8d72043e3a7e25a3b39'", value: web3.toWei(1024, "'ether'")})' attach http://host.docker.internal:18545 + +function cleanup { + docker rm --force $TEST_NODE_CONTAINER_NAME_L1 \ + $TEST_NODE_CONTAINER_NAME_L2 \ + $TEST_IMPORT_TEST_ACCOUNT_ETH_JOB_NAME &> /dev/null +} + +trap cleanup EXIT INT KILL ERR + +# Run the tests +PRIVATE_KEY=$TEST_ACCOUNT_PRIV_KEY \ + npx hardhat test --network l1_test --grep "^tokenomics" \ No newline at end of file diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts new file mode 100644 index 0000000000..9edfcf57c0 --- /dev/null +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -0,0 +1,3 @@ +describe("tokenomics", function () { + it("runs", async function () {}); +}); diff --git a/packages/protocol/test/utils/block_metadata.ts b/packages/protocol/test/utils/block_metadata.ts new file mode 100644 index 0000000000..74532a6ebd --- /dev/null +++ b/packages/protocol/test/utils/block_metadata.ts @@ -0,0 +1,17 @@ +import { BigNumberish } from "ethers"; + +type BlockMetadata = { + id: number; + l1Height: number; + l1Hash: string; + beneficiary: string; + txListHash: string; + mixHash: string; + extraData: string; + gasLimit: BigNumberish; + timestamp: number; + commitSlot: number; + commitHeight: number; +}; + +export { BlockMetadata }; diff --git a/packages/protocol/test/utils/bridge.ts b/packages/protocol/test/utils/bridge.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/protocol/test/utils/bytes.ts b/packages/protocol/test/utils/bytes.ts new file mode 100644 index 0000000000..50331a1d3a --- /dev/null +++ b/packages/protocol/test/utils/bytes.ts @@ -0,0 +1,7 @@ +import { ethers } from "hardhat"; + +function randomBytes32() { + return ethers.utils.hexlify(ethers.utils.randomBytes(32)); +} + +export { randomBytes32 }; diff --git a/packages/protocol/test/utils/commit.ts b/packages/protocol/test/utils/commit.ts new file mode 100644 index 0000000000..1f99bb2ca4 --- /dev/null +++ b/packages/protocol/test/utils/commit.ts @@ -0,0 +1,31 @@ +import { ethers } from "ethers"; +import RLP from "rlp"; +import { TaikoL1 } from "../../typechain"; + +const generateCommitHash = ( + block: ethers.providers.Block +): { hash: string; txListHash: string } => { + const txListHash = ethers.utils.keccak256(RLP.encode(block.transactions)); + const hash = ethers.utils.keccak256( + ethers.utils.solidityPack( + ["address", "bytes32"], + [block.miner, txListHash] + ) + ); + + return { hash: hash, txListHash: txListHash }; +}; + +const commitBlock = async ( + taikoL1: TaikoL1, + block: ethers.providers.Block +): Promise<{ + tx: ethers.ContractTransaction; + commit: { hash: string; txListHash: string }; +}> => { + const commit = generateCommitHash(block); + const tx = await taikoL1.commitBlock(1, commit.hash); + return { tx, commit }; +}; + +export { generateCommitHash, commitBlock }; diff --git a/packages/protocol/test/utils/encoding.ts b/packages/protocol/test/utils/encoding.ts new file mode 100644 index 0000000000..bf28f44ccb --- /dev/null +++ b/packages/protocol/test/utils/encoding.ts @@ -0,0 +1,13 @@ +import { ethers } from "hardhat"; +import { BlockMetadata } from "./block_metadata"; + +function encodeBlockMetadata(meta: BlockMetadata) { + return ethers.utils.defaultAbiCoder.encode( + [ + "tuple(uint256 id, uint256 l1Height, bytes32 l1Hash, address beneficiary, bytes32 txListHash, bytes32 mixHash, bytes extraData, uint64 gasLimit, uint64 timestamp, uint64 commitHeight, uint64 commitSlot)", + ], + [meta] + ); +} + +export { encodeBlockMetadata }; diff --git a/packages/protocol/test/utils/propose.ts b/packages/protocol/test/utils/propose.ts new file mode 100644 index 0000000000..07fc2b861a --- /dev/null +++ b/packages/protocol/test/utils/propose.ts @@ -0,0 +1,46 @@ +import { BigNumber, ethers } from "ethers"; +import RLP from "rlp"; +import { TaikoL1 } from "../../typechain"; +import { BlockMetadata } from "./block_metadata"; +import { encodeBlockMetadata } from "./encoding"; + +const buildProposeBlockInputs = ( + block: ethers.providers.Block, + meta: BlockMetadata +) => { + const inputs = []; + const blockMetadataBytes = encodeBlockMetadata(meta); + + inputs[0] = blockMetadataBytes; + inputs[1] = RLP.encode(block.transactions); + return inputs; +}; + +const proposeBlock = async ( + taikoL1: TaikoL1, + block: ethers.providers.Block, + txListHash: string, + commitHeight: number, + id: number, + gasLimit: BigNumber +) => { + const meta: BlockMetadata = { + id: id, + l1Height: 0, + l1Hash: ethers.constants.HashZero, + beneficiary: block.miner, + txListHash: txListHash, + mixHash: ethers.constants.HashZero, + extraData: block.extraData, + gasLimit: gasLimit, + timestamp: 0, + commitSlot: 1, + commitHeight: commitHeight, + }; + + const inputs = buildProposeBlockInputs(block, meta); + + const tx = await taikoL1.proposeBlock(inputs); + return tx; +}; +export { buildProposeBlockInputs, proposeBlock }; diff --git a/packages/protocol/test/utils/tokenomics.ts b/packages/protocol/test/utils/tokenomics.ts new file mode 100644 index 0000000000..e69de29bb2 From 777179f97f31598682dad92551a8782b2cfdc1ef Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 6 Jan 2023 10:37:29 -0800 Subject: [PATCH 02/39] separate bridge and bridge integration tests, move deployBridge --- .../test/bridge/Bridge.integration.test.ts | 728 ++++++++++++++++ packages/protocol/test/bridge/Bridge.test.ts | 799 +----------------- packages/protocol/test/utils/bridge.ts | 78 ++ 3 files changed, 810 insertions(+), 795 deletions(-) create mode 100644 packages/protocol/test/bridge/Bridge.integration.test.ts diff --git a/packages/protocol/test/bridge/Bridge.integration.test.ts b/packages/protocol/test/bridge/Bridge.integration.test.ts new file mode 100644 index 0000000000..6a59a1c710 --- /dev/null +++ b/packages/protocol/test/bridge/Bridge.integration.test.ts @@ -0,0 +1,728 @@ +import { expect } from "chai"; +import { BigNumber } from "ethers"; +import hre, { ethers } from "hardhat"; +import { + getLatestBlockHeader, + getSignalProof, + getSignalSlot, +} from "../../tasks/utils"; +import { + AddressManager, + TestBadReceiver, + TestHeaderSync, + TestLibBridgeData, +} from "../../typechain"; +import { deployBridge } from "../utils/bridge"; +import { Message } from "../utils/message"; + +describe("integration:Bridge", function () { + async function deployBridgeFixture() { + const [owner, nonOwner] = await ethers.getSigners(); + + const { chainId } = await ethers.provider.getNetwork(); + + const srcChainId = chainId; + + // seondary node to deploy L2 on + const l2Provider = new ethers.providers.JsonRpcProvider( + "http://localhost:28545" + ); + + const l2Signer = await l2Provider.getSigner( + ( + await l2Provider.listAccounts() + )[0] + ); + + const l2NonOwner = await l2Provider.getSigner(); + + const l2Network = await l2Provider.getNetwork(); + const enabledDestChainId = l2Network.chainId; + + const addressManager: AddressManager = await ( + await ethers.getContractFactory("AddressManager") + ).deploy(); + await addressManager.init(); + + const l2AddressManager: AddressManager = await ( + await ethers.getContractFactory("AddressManager") + ) + .connect(l2Signer) + .deploy(); + await l2AddressManager.init(); + + const { bridge: l1Bridge, etherVault: l1EtherVault } = + await deployBridge( + owner, + addressManager, + enabledDestChainId, + srcChainId + ); + + const { bridge: l2Bridge, etherVault: l2EtherVault } = + await deployBridge( + l2Signer, + l2AddressManager, + srcChainId, + enabledDestChainId + ); + + await addressManager.setAddress( + `${enabledDestChainId}.bridge`, + l2Bridge.address + ); + + await l2AddressManager + .connect(l2Signer) + .setAddress(`${srcChainId}.bridge`, l1Bridge.address); + + const headerSync: TestHeaderSync = await ( + await ethers.getContractFactory("TestHeaderSync") + ) + .connect(l2Signer) + .deploy(); + + await l2AddressManager + .connect(l2Signer) + .setAddress(`${enabledDestChainId}.taiko`, headerSync.address); + + const m: Message = { + id: 1, + sender: owner.address, + srcChainId: srcChainId, + destChainId: enabledDestChainId, + owner: owner.address, + to: owner.address, + refundAddress: owner.address, + depositValue: 1000, + callValue: 1000, + processingFee: 1000, + gasLimit: 10000, + data: ethers.constants.HashZero, + memo: "", + }; + + return { + owner, + l2Signer, + nonOwner, + l2NonOwner, + l1Bridge, + l2Bridge, + addressManager, + enabledDestChainId, + l1EtherVault, + l2EtherVault, + srcChainId, + headerSync, + m, + }; + } + + describe("processMessage()", function () { + it("should throw if message.gasLimit == 0 & msg.sender is not message.owner", async function () { + const { + owner, + l2NonOwner, + srcChainId, + enabledDestChainId, + l2Bridge, + } = await deployBridgeFixture(); + + const m: Message = { + id: 1, + sender: await l2NonOwner.getAddress(), + srcChainId: srcChainId, + destChainId: enabledDestChainId, + owner: owner.address, + to: owner.address, + refundAddress: owner.address, + depositValue: 1000, + callValue: 1000, + processingFee: 1000, + gasLimit: 0, + data: ethers.constants.HashZero, + memo: "", + }; + + await expect( + l2Bridge.processMessage(m, ethers.constants.HashZero) + ).to.be.revertedWith("B:forbidden"); + }); + + it("should throw if message.destChainId is not equal to current block.chainId", async function () { + const { owner, srcChainId, enabledDestChainId, l2Bridge } = + await deployBridgeFixture(); + + const m: Message = { + id: 1, + sender: owner.address, + srcChainId: srcChainId, + destChainId: enabledDestChainId + 1, + owner: owner.address, + to: owner.address, + refundAddress: owner.address, + depositValue: 1000, + callValue: 1000, + processingFee: 1000, + gasLimit: 10000, + data: ethers.constants.HashZero, + memo: "", + }; + + await expect( + l2Bridge.processMessage(m, ethers.constants.HashZero) + ).to.be.revertedWith("B:destChainId"); + }); + + it("should throw if messageStatus of message is != NEW", async function () { + const { l1Bridge, l2Bridge, headerSync, m } = + await deployBridgeFixture(); + + const expectedAmount = + m.depositValue + m.callValue + m.processingFee; + const tx = await l1Bridge.sendMessage(m, { + value: expectedAmount, + }); + + const receipt = await tx.wait(); + + const [messageSentEvent] = receipt.events as any as Event[]; + + const { signal, message } = (messageSentEvent as any).args; + + const sender = l1Bridge.address; + + const key = getSignalSlot(hre, sender, signal); + + const { block, blockHeader } = await getLatestBlockHeader(hre); + + await headerSync.setSyncedHeader(block.hash); + + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.number, + blockHeader + ); + + // upon successful processing, this immediately gets marked as DONE + await l2Bridge.processMessage(message, signalProof); + + // recalling this process should be prevented as it's status is no longer NEW + await expect( + l2Bridge.processMessage(message, signalProof) + ).to.be.revertedWith("B:status"); + }); + + it("should throw if message signalproof is not valid", async function () { + const { l1Bridge, l2Bridge, headerSync, m } = + await deployBridgeFixture(); + + const libData: TestLibBridgeData = await ( + await ethers.getContractFactory("TestLibBridgeData") + ).deploy(); + + const signal = await libData.hashMessage(m); + + const sender = l1Bridge.address; + + const key = getSignalSlot(hre, sender, signal); + const { block, blockHeader } = await getLatestBlockHeader(hre); + + await headerSync.setSyncedHeader(ethers.constants.HashZero); + + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.number, + blockHeader + ); + + await expect( + l2Bridge.processMessage(m, signalProof) + ).to.be.revertedWith("LTP:invalid storage proof"); + }); + + it("should throw if message has not been received", async function () { + const { l1Bridge, l2Bridge, headerSync, m } = + await deployBridgeFixture(); + + const expectedAmount = + m.depositValue + m.callValue + m.processingFee; + const tx = await l1Bridge.sendMessage(m, { + value: expectedAmount, + }); + + const receipt = await tx.wait(); + + const [messageSentEvent] = receipt.events as any as Event[]; + + const { signal, message } = (messageSentEvent as any).args; + + expect(signal).not.to.be.eq(ethers.constants.HashZero); + + const messageStatus = await l1Bridge.getMessageStatus(signal); + + expect(messageStatus).to.be.eq(0); + + const sender = l1Bridge.address; + + const key = getSignalSlot(hre, sender, signal); + + const { block, blockHeader } = await getLatestBlockHeader(hre); + + await headerSync.setSyncedHeader(ethers.constants.HashZero); + + // get storageValue for the key + const storageValue = await ethers.provider.getStorageAt( + l1Bridge.address, + key, + block.number + ); + // make sure it equals 1 so our proof will pass + expect(storageValue).to.be.eq( + "0x0000000000000000000000000000000000000000000000000000000000000001" + ); + + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.number, + blockHeader + ); + + await expect( + l2Bridge.processMessage(message, signalProof) + ).to.be.revertedWith("B:notReceived"); + }); + + it("processes a message when the signal has been verified from the sending chain", async () => { + const { l1Bridge, l2Bridge, headerSync, m } = + await deployBridgeFixture(); + + const expectedAmount = + m.depositValue + m.callValue + m.processingFee; + const tx = await l1Bridge.sendMessage(m, { + value: expectedAmount, + }); + + const receipt = await tx.wait(); + + const [messageSentEvent] = receipt.events as any as Event[]; + + const { signal, message } = (messageSentEvent as any).args; + + expect(signal).not.to.be.eq(ethers.constants.HashZero); + + const messageStatus = await l1Bridge.getMessageStatus(signal); + + expect(messageStatus).to.be.eq(0); + + const sender = l1Bridge.address; + + const key = getSignalSlot(hre, sender, signal); + + const { block, blockHeader } = await getLatestBlockHeader(hre); + + await headerSync.setSyncedHeader(block.hash); + + // get storageValue for the key + const storageValue = await ethers.provider.getStorageAt( + l1Bridge.address, + key, + block.number + ); + // make sure it equals 1 so our proof will pass + expect(storageValue).to.be.eq( + "0x0000000000000000000000000000000000000000000000000000000000000001" + ); + + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.number, + blockHeader + ); + + expect( + await l2Bridge.processMessage(message, signalProof, { + gasLimit: BigNumber.from(2000000), + }) + ).to.emit(l2Bridge, "MessageStatusChanged"); + }); + }); + + describe("isMessageSent()", function () { + it("should return false, since no message was sent", async function () { + const { l1Bridge, m } = await deployBridgeFixture(); + + const libData = await ( + await ethers.getContractFactory("TestLibBridgeData") + ).deploy(); + const signal = await libData.hashMessage(m); + + expect(await l1Bridge.isMessageSent(signal)).to.be.eq(false); + }); + + it("should return true if message was sent properly", async function () { + const { l1Bridge, m } = await deployBridgeFixture(); + + const expectedAmount = + m.depositValue + m.callValue + m.processingFee; + const tx = await l1Bridge.sendMessage(m, { + value: expectedAmount, + }); + + const receipt = await tx.wait(); + + const [messageSentEvent] = receipt.events as any as Event[]; + + const { signal } = (messageSentEvent as any).args; + + expect(signal).not.to.be.eq(ethers.constants.HashZero); + + expect(await l1Bridge.isMessageSent(signal)).to.be.eq(true); + }); + }); + + describe("retryMessage()", function () { + async function retriableMessageSetup() { + const { + owner, + l2Signer, + nonOwner, + l2NonOwner, + l1Bridge, + l2Bridge, + addressManager, + enabledDestChainId, + l1EtherVault, + l2EtherVault, + srcChainId, + headerSync, + } = await deployBridgeFixture(); + + const testBadReceiver: TestBadReceiver = await ( + await ethers.getContractFactory("TestBadReceiver") + ) + .connect(l2Signer) + .deploy(); + + await testBadReceiver.deployed(); + + const m: Message = { + id: 1, + sender: owner.address, + srcChainId: srcChainId, + destChainId: enabledDestChainId, + owner: owner.address, + to: testBadReceiver.address, + refundAddress: owner.address, + depositValue: 1000, + callValue: 1000, + processingFee: 1000, + gasLimit: 1, + data: ethers.constants.HashZero, + memo: "", + }; + + const expectedAmount = + m.depositValue + m.callValue + m.processingFee; + const tx = await l1Bridge.connect(owner).sendMessage(m, { + value: expectedAmount, + }); + + const receipt = await tx.wait(); + + const [messageSentEvent] = receipt.events as any as Event[]; + + const { signal, message } = (messageSentEvent as any).args; + + expect(signal).not.to.be.eq(ethers.constants.HashZero); + + const messageStatus = await l1Bridge.getMessageStatus(signal); + + expect(messageStatus).to.be.eq(0); + + const sender = l1Bridge.address; + + const key = getSignalSlot(hre, sender, signal); + + const { block, blockHeader } = await getLatestBlockHeader(hre); + + await headerSync.setSyncedHeader(block.hash); + + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.number, + blockHeader + ); + + await l2Bridge + .connect(l2NonOwner) + .processMessage(message, signalProof, { + gasLimit: BigNumber.from(2000000), + }); + + const status = await l2Bridge.getMessageStatus(signal); + expect(status).to.be.eq(1); // message is retriable now + // because the LibBridgeInvoke call failed, because + // message.to is a bad receiver and throws upon receipt + + return { + message, + l2Signer, + l2NonOwner, + l1Bridge, + l2Bridge, + addressManager, + headerSync, + owner, + nonOwner, + srcChainId, + enabledDestChainId, + l1EtherVault, + l2EtherVault, + signal, + }; + } + it("setup message to fail first processMessage", async function () { + const { l2Bridge, signal } = await retriableMessageSetup(); + l2Bridge; + signal; + }); + }); + + describe("isMessageReceived()", function () { + it("should throw if signal is not a bridge message; proof is invalid since sender != bridge.", async function () { + const { owner, l1Bridge, l2Bridge, headerSync, srcChainId } = + await deployBridgeFixture(); + + const signal = ethers.utils.hexlify(ethers.utils.randomBytes(32)); + + const tx = await l1Bridge.connect(owner).sendSignal(signal); + + await tx.wait(); + + const sender = owner.address; + + const key = getSignalSlot(hre, sender, signal); + + const { block, blockHeader } = await getLatestBlockHeader(hre); + + await headerSync.setSyncedHeader(block.hash); + + // get storageValue for the key + const storageValue = await ethers.provider.getStorageAt( + l1Bridge.address, + key, + block.number + ); + // // make sure it equals 1 so we know sendSignal worked + expect(storageValue).to.be.eq( + "0x0000000000000000000000000000000000000000000000000000000000000001" + ); + + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.number, + blockHeader + ); + + await expect( + l2Bridge.isMessageReceived(signal, srcChainId, signalProof) + ).to.be.reverted; + }); + + it("should return true", async function () { + const { l1Bridge, srcChainId, l2Bridge, headerSync, m } = + await deployBridgeFixture(); + + const expectedAmount = + m.depositValue + m.callValue + m.processingFee; + const tx = await l1Bridge.sendMessage(m, { + value: expectedAmount, + }); + + const receipt = await tx.wait(); + + const [messageSentEvent] = receipt.events as any as Event[]; + + const { signal } = (messageSentEvent as any).args; + + const sender = l1Bridge.address; + + const key = getSignalSlot(hre, sender, signal); + + const { block, blockHeader } = await getLatestBlockHeader(hre); + + await headerSync.setSyncedHeader(block.hash); + + // get storageValue for the key + const storageValue = await ethers.provider.getStorageAt( + l1Bridge.address, + key, + block.number + ); + // // make sure it equals 1 so we know sendMessage worked + expect(storageValue).to.be.eq( + "0x0000000000000000000000000000000000000000000000000000000000000001" + ); + + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.number, + blockHeader + ); + + expect( + await l2Bridge.isMessageReceived( + signal, + srcChainId, + signalProof + ) + ).to.be.eq(true); + }); + }); + + describe("isSignalReceived()", function () { + it("should throw if sender == address(0)", async function () { + const { l2Bridge, srcChainId } = await deployBridgeFixture(); + + const signal = ethers.utils.randomBytes(32); + const sender = ethers.constants.AddressZero; + const signalProof = ethers.constants.HashZero; + + await expect( + l2Bridge.isSignalReceived( + signal, + srcChainId, + sender, + signalProof + ) + ).to.be.revertedWith("B:sender"); + }); + + it("should throw if signal == HashZero", async function () { + const { owner, l2Bridge, srcChainId } = await deployBridgeFixture(); + + const signal = ethers.constants.HashZero; + const sender = owner.address; + const signalProof = ethers.constants.HashZero; + + await expect( + l2Bridge.isSignalReceived( + signal, + srcChainId, + sender, + signalProof + ) + ).to.be.revertedWith("B:signal"); + }); + + it("should throw if calling from same layer", async function () { + const { owner, l1Bridge, headerSync, srcChainId } = + await deployBridgeFixture(); + const signal = ethers.utils.hexlify(ethers.utils.randomBytes(32)); + + const tx = await l1Bridge.connect(owner).sendSignal(signal); + + await tx.wait(); + + const sender = owner.address; + + const key = getSignalSlot(hre, sender, signal); + + const { block, blockHeader } = await getLatestBlockHeader(hre); + + await headerSync.setSyncedHeader(block.hash); + + // get storageValue for the key + const storageValue = await ethers.provider.getStorageAt( + l1Bridge.address, + key, + block.number + ); + // make sure it equals 1 so our proof is valid + expect(storageValue).to.be.eq( + "0x0000000000000000000000000000000000000000000000000000000000000001" + ); + + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.number, + blockHeader + ); + + await expect( + l1Bridge.isSignalReceived( + signal, + srcChainId, + sender, + signalProof + ) + ).to.be.revertedWith("B:srcBridge"); + }); + + it("should return true and pass", async function () { + const { owner, l1Bridge, l2Bridge, headerSync, srcChainId } = + await deployBridgeFixture(); + + const signal = ethers.utils.hexlify(ethers.utils.randomBytes(32)); + + const tx = await l1Bridge.connect(owner).sendSignal(signal); + + await tx.wait(); + + const sender = owner.address; + + const key = getSignalSlot(hre, sender, signal); + + const { block, blockHeader } = await getLatestBlockHeader(hre); + + await headerSync.setSyncedHeader(block.hash); + + // get storageValue for the key + const storageValue = await ethers.provider.getStorageAt( + l1Bridge.address, + key, + block.number + ); + // make sure it equals 1 so our proof will pass + expect(storageValue).to.be.eq( + "0x0000000000000000000000000000000000000000000000000000000000000001" + ); + + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.number, + blockHeader + ); + // proving functionality; l2Bridge can check if l1Bridge receives a signal + // allowing for dapp cross layer communication + expect( + await l2Bridge.isSignalReceived( + signal, + srcChainId, + sender, + signalProof + ) + ).to.be.eq(true); + }); + }); +}); diff --git a/packages/protocol/test/bridge/Bridge.test.ts b/packages/protocol/test/bridge/Bridge.test.ts index 10a127a643..62b5ac804a 100644 --- a/packages/protocol/test/bridge/Bridge.test.ts +++ b/packages/protocol/test/bridge/Bridge.test.ts @@ -1,89 +1,10 @@ import { expect } from "chai"; -import { BigNumber, Signer } from "ethers"; -import hre, { ethers } from "hardhat"; -import { - getLatestBlockHeader, - getSignalProof, - getSignalSlot, -} from "../../tasks/utils"; -import { - AddressManager, - Bridge, - EtherVault, - LibTrieProof, - TestBadReceiver, - TestHeaderSync, - TestLibBridgeData, -} from "../../typechain"; +import { BigNumber } from "ethers"; +import { ethers } from "hardhat"; +import { AddressManager } from "../../typechain"; +import { deployBridge } from "../utils/bridge"; import { Message } from "../utils/message"; -async function deployBridge( - signer: Signer, - addressManager: AddressManager, - destChain: number, - srcChain: number -): Promise<{ bridge: Bridge; etherVault: EtherVault }> { - const libTrieProof: LibTrieProof = await ( - await ethers.getContractFactory("LibTrieProof") - ) - .connect(signer) - .deploy(); - - const libBridgeProcess = await ( - await ethers.getContractFactory("LibBridgeProcess", { - libraries: { - LibTrieProof: libTrieProof.address, - }, - }) - ) - .connect(signer) - .deploy(); - - const libBridgeRetry = await ( - await ethers.getContractFactory("LibBridgeRetry") - ) - .connect(signer) - .deploy(); - - const BridgeFactory = await ethers.getContractFactory("Bridge", { - libraries: { - LibBridgeProcess: libBridgeProcess.address, - LibBridgeRetry: libBridgeRetry.address, - LibTrieProof: libTrieProof.address, - }, - }); - - const bridge: Bridge = await BridgeFactory.connect(signer).deploy(); - - await bridge.connect(signer).init(addressManager.address); - - await bridge.connect(signer).enableDestChain(destChain, true); - - const etherVault: EtherVault = await ( - await ethers.getContractFactory("EtherVault") - ) - .connect(signer) - .deploy(); - - await etherVault.connect(signer).init(addressManager.address); - - await etherVault.connect(signer).authorize(bridge.address, true); - - await etherVault.connect(signer).authorize(await signer.getAddress(), true); - - await addressManager.setAddress( - `${srcChain}.ether_vault`, - etherVault.address - ); - - await signer.sendTransaction({ - to: etherVault.address, - value: BigNumber.from(100000000), - gasLimit: 1000000, - }); - - return { bridge, etherVault }; -} describe("Bridge", function () { async function deployBridgeFixture() { const [owner, nonOwner] = await ethers.getSigners(); @@ -432,715 +353,3 @@ describe("Bridge", function () { }); }); }); - -describe("integration:Bridge", function () { - async function deployBridgeFixture() { - const [owner, nonOwner] = await ethers.getSigners(); - - const { chainId } = await ethers.provider.getNetwork(); - - const srcChainId = chainId; - - // seondary node to deploy L2 on - const l2Provider = new ethers.providers.JsonRpcProvider( - "http://localhost:28545" - ); - - const l2Signer = await l2Provider.getSigner( - ( - await l2Provider.listAccounts() - )[0] - ); - - const l2NonOwner = await l2Provider.getSigner(); - - const l2Network = await l2Provider.getNetwork(); - const enabledDestChainId = l2Network.chainId; - - const addressManager: AddressManager = await ( - await ethers.getContractFactory("AddressManager") - ).deploy(); - await addressManager.init(); - - const l2AddressManager: AddressManager = await ( - await ethers.getContractFactory("AddressManager") - ) - .connect(l2Signer) - .deploy(); - await l2AddressManager.init(); - - const { bridge: l1Bridge, etherVault: l1EtherVault } = - await deployBridge( - owner, - addressManager, - enabledDestChainId, - srcChainId - ); - - const { bridge: l2Bridge, etherVault: l2EtherVault } = - await deployBridge( - l2Signer, - l2AddressManager, - srcChainId, - enabledDestChainId - ); - - await addressManager.setAddress( - `${enabledDestChainId}.bridge`, - l2Bridge.address - ); - - await l2AddressManager - .connect(l2Signer) - .setAddress(`${srcChainId}.bridge`, l1Bridge.address); - - const headerSync: TestHeaderSync = await ( - await ethers.getContractFactory("TestHeaderSync") - ) - .connect(l2Signer) - .deploy(); - - await l2AddressManager - .connect(l2Signer) - .setAddress(`${enabledDestChainId}.taiko`, headerSync.address); - - const m: Message = { - id: 1, - sender: owner.address, - srcChainId: srcChainId, - destChainId: enabledDestChainId, - owner: owner.address, - to: owner.address, - refundAddress: owner.address, - depositValue: 1000, - callValue: 1000, - processingFee: 1000, - gasLimit: 10000, - data: ethers.constants.HashZero, - memo: "", - }; - - return { - owner, - l2Signer, - nonOwner, - l2NonOwner, - l1Bridge, - l2Bridge, - addressManager, - enabledDestChainId, - l1EtherVault, - l2EtherVault, - srcChainId, - headerSync, - m, - }; - } - - describe("processMessage()", function () { - it("should throw if message.gasLimit == 0 & msg.sender is not message.owner", async function () { - const { - owner, - l2NonOwner, - srcChainId, - enabledDestChainId, - l2Bridge, - } = await deployBridgeFixture(); - - const m: Message = { - id: 1, - sender: await l2NonOwner.getAddress(), - srcChainId: srcChainId, - destChainId: enabledDestChainId, - owner: owner.address, - to: owner.address, - refundAddress: owner.address, - depositValue: 1000, - callValue: 1000, - processingFee: 1000, - gasLimit: 0, - data: ethers.constants.HashZero, - memo: "", - }; - - await expect( - l2Bridge.processMessage(m, ethers.constants.HashZero) - ).to.be.revertedWith("B:forbidden"); - }); - - it("should throw if message.destChainId is not equal to current block.chainId", async function () { - const { owner, srcChainId, enabledDestChainId, l2Bridge } = - await deployBridgeFixture(); - - const m: Message = { - id: 1, - sender: owner.address, - srcChainId: srcChainId, - destChainId: enabledDestChainId + 1, - owner: owner.address, - to: owner.address, - refundAddress: owner.address, - depositValue: 1000, - callValue: 1000, - processingFee: 1000, - gasLimit: 10000, - data: ethers.constants.HashZero, - memo: "", - }; - - await expect( - l2Bridge.processMessage(m, ethers.constants.HashZero) - ).to.be.revertedWith("B:destChainId"); - }); - - it("should throw if messageStatus of message is != NEW", async function () { - const { l1Bridge, l2Bridge, headerSync, m } = - await deployBridgeFixture(); - - const expectedAmount = - m.depositValue + m.callValue + m.processingFee; - const tx = await l1Bridge.sendMessage(m, { - value: expectedAmount, - }); - - const receipt = await tx.wait(); - - const [messageSentEvent] = receipt.events as any as Event[]; - - const { signal, message } = (messageSentEvent as any).args; - - const sender = l1Bridge.address; - - const key = getSignalSlot(hre, sender, signal); - - const { block, blockHeader } = await getLatestBlockHeader(hre); - - await headerSync.setSyncedHeader(block.hash); - - const signalProof = await getSignalProof( - hre, - l1Bridge.address, - key, - block.number, - blockHeader - ); - - // upon successful processing, this immediately gets marked as DONE - await l2Bridge.processMessage(message, signalProof); - - // recalling this process should be prevented as it's status is no longer NEW - await expect( - l2Bridge.processMessage(message, signalProof) - ).to.be.revertedWith("B:status"); - }); - - it("should throw if message signalproof is not valid", async function () { - const { l1Bridge, l2Bridge, headerSync, m } = - await deployBridgeFixture(); - - const libData: TestLibBridgeData = await ( - await ethers.getContractFactory("TestLibBridgeData") - ).deploy(); - - const signal = await libData.hashMessage(m); - - const sender = l1Bridge.address; - - const key = getSignalSlot(hre, sender, signal); - const { block, blockHeader } = await getLatestBlockHeader(hre); - - await headerSync.setSyncedHeader(ethers.constants.HashZero); - - const signalProof = await getSignalProof( - hre, - l1Bridge.address, - key, - block.number, - blockHeader - ); - - await expect( - l2Bridge.processMessage(m, signalProof) - ).to.be.revertedWith("LTP:invalid storage proof"); - }); - - it("should throw if message has not been received", async function () { - const { l1Bridge, l2Bridge, headerSync, m } = - await deployBridgeFixture(); - - const expectedAmount = - m.depositValue + m.callValue + m.processingFee; - const tx = await l1Bridge.sendMessage(m, { - value: expectedAmount, - }); - - const receipt = await tx.wait(); - - const [messageSentEvent] = receipt.events as any as Event[]; - - const { signal, message } = (messageSentEvent as any).args; - - expect(signal).not.to.be.eq(ethers.constants.HashZero); - - const messageStatus = await l1Bridge.getMessageStatus(signal); - - expect(messageStatus).to.be.eq(0); - - const sender = l1Bridge.address; - - const key = getSignalSlot(hre, sender, signal); - - const { block, blockHeader } = await getLatestBlockHeader(hre); - - await headerSync.setSyncedHeader(ethers.constants.HashZero); - - // get storageValue for the key - const storageValue = await ethers.provider.getStorageAt( - l1Bridge.address, - key, - block.number - ); - // make sure it equals 1 so our proof will pass - expect(storageValue).to.be.eq( - "0x0000000000000000000000000000000000000000000000000000000000000001" - ); - - const signalProof = await getSignalProof( - hre, - l1Bridge.address, - key, - block.number, - blockHeader - ); - - await expect( - l2Bridge.processMessage(message, signalProof) - ).to.be.revertedWith("B:notReceived"); - }); - - it("processes a message when the signal has been verified from the sending chain", async () => { - const { l1Bridge, l2Bridge, headerSync, m } = - await deployBridgeFixture(); - - const expectedAmount = - m.depositValue + m.callValue + m.processingFee; - const tx = await l1Bridge.sendMessage(m, { - value: expectedAmount, - }); - - const receipt = await tx.wait(); - - const [messageSentEvent] = receipt.events as any as Event[]; - - const { signal, message } = (messageSentEvent as any).args; - - expect(signal).not.to.be.eq(ethers.constants.HashZero); - - const messageStatus = await l1Bridge.getMessageStatus(signal); - - expect(messageStatus).to.be.eq(0); - - const sender = l1Bridge.address; - - const key = getSignalSlot(hre, sender, signal); - - const { block, blockHeader } = await getLatestBlockHeader(hre); - - await headerSync.setSyncedHeader(block.hash); - - // get storageValue for the key - const storageValue = await ethers.provider.getStorageAt( - l1Bridge.address, - key, - block.number - ); - // make sure it equals 1 so our proof will pass - expect(storageValue).to.be.eq( - "0x0000000000000000000000000000000000000000000000000000000000000001" - ); - - const signalProof = await getSignalProof( - hre, - l1Bridge.address, - key, - block.number, - blockHeader - ); - - expect( - await l2Bridge.processMessage(message, signalProof, { - gasLimit: BigNumber.from(2000000), - }) - ).to.emit(l2Bridge, "MessageStatusChanged"); - }); - }); - - describe("isMessageSent()", function () { - it("should return false, since no message was sent", async function () { - const { l1Bridge, m } = await deployBridgeFixture(); - - const libData = await ( - await ethers.getContractFactory("TestLibBridgeData") - ).deploy(); - const signal = await libData.hashMessage(m); - - expect(await l1Bridge.isMessageSent(signal)).to.be.eq(false); - }); - - it("should return true if message was sent properly", async function () { - const { l1Bridge, m } = await deployBridgeFixture(); - - const expectedAmount = - m.depositValue + m.callValue + m.processingFee; - const tx = await l1Bridge.sendMessage(m, { - value: expectedAmount, - }); - - const receipt = await tx.wait(); - - const [messageSentEvent] = receipt.events as any as Event[]; - - const { signal } = (messageSentEvent as any).args; - - expect(signal).not.to.be.eq(ethers.constants.HashZero); - - expect(await l1Bridge.isMessageSent(signal)).to.be.eq(true); - }); - }); - - describe("retryMessage()", function () { - async function retriableMessageSetup() { - const { - owner, - l2Signer, - nonOwner, - l2NonOwner, - l1Bridge, - l2Bridge, - addressManager, - enabledDestChainId, - l1EtherVault, - l2EtherVault, - srcChainId, - headerSync, - } = await deployBridgeFixture(); - - const testBadReceiver: TestBadReceiver = await ( - await ethers.getContractFactory("TestBadReceiver") - ) - .connect(l2Signer) - .deploy(); - - await testBadReceiver.deployed(); - - const m: Message = { - id: 1, - sender: owner.address, - srcChainId: srcChainId, - destChainId: enabledDestChainId, - owner: owner.address, - to: testBadReceiver.address, - refundAddress: owner.address, - depositValue: 1000, - callValue: 1000, - processingFee: 1000, - gasLimit: 1, - data: ethers.constants.HashZero, - memo: "", - }; - - const expectedAmount = - m.depositValue + m.callValue + m.processingFee; - const tx = await l1Bridge.connect(owner).sendMessage(m, { - value: expectedAmount, - }); - - const receipt = await tx.wait(); - - const [messageSentEvent] = receipt.events as any as Event[]; - - const { signal, message } = (messageSentEvent as any).args; - - expect(signal).not.to.be.eq(ethers.constants.HashZero); - - const messageStatus = await l1Bridge.getMessageStatus(signal); - - expect(messageStatus).to.be.eq(0); - - const sender = l1Bridge.address; - - const key = getSignalSlot(hre, sender, signal); - - const { block, blockHeader } = await getLatestBlockHeader(hre); - - await headerSync.setSyncedHeader(block.hash); - - const signalProof = await getSignalProof( - hre, - l1Bridge.address, - key, - block.number, - blockHeader - ); - - await l2Bridge - .connect(l2NonOwner) - .processMessage(message, signalProof, { - gasLimit: BigNumber.from(2000000), - }); - - const status = await l2Bridge.getMessageStatus(signal); - expect(status).to.be.eq(1); // message is retriable now - // because the LibBridgeInvoke call failed, because - // message.to is a bad receiver and throws upon receipt - - return { - message, - l2Signer, - l2NonOwner, - l1Bridge, - l2Bridge, - addressManager, - headerSync, - owner, - nonOwner, - srcChainId, - enabledDestChainId, - l1EtherVault, - l2EtherVault, - signal, - }; - } - it("setup message to fail first processMessage", async function () { - const { l2Bridge, signal } = await retriableMessageSetup(); - l2Bridge; - signal; - }); - }); - - describe("isMessageReceived()", function () { - it("should throw if signal is not a bridge message; proof is invalid since sender != bridge.", async function () { - const { owner, l1Bridge, l2Bridge, headerSync, srcChainId } = - await deployBridgeFixture(); - - const signal = ethers.utils.hexlify(ethers.utils.randomBytes(32)); - - const tx = await l1Bridge.connect(owner).sendSignal(signal); - - await tx.wait(); - - const sender = owner.address; - - const key = getSignalSlot(hre, sender, signal); - - const { block, blockHeader } = await getLatestBlockHeader(hre); - - await headerSync.setSyncedHeader(block.hash); - - // get storageValue for the key - const storageValue = await ethers.provider.getStorageAt( - l1Bridge.address, - key, - block.number - ); - // // make sure it equals 1 so we know sendSignal worked - expect(storageValue).to.be.eq( - "0x0000000000000000000000000000000000000000000000000000000000000001" - ); - - const signalProof = await getSignalProof( - hre, - l1Bridge.address, - key, - block.number, - blockHeader - ); - - await expect( - l2Bridge.isMessageReceived(signal, srcChainId, signalProof) - ).to.be.reverted; - }); - - it("should return true", async function () { - const { l1Bridge, srcChainId, l2Bridge, headerSync, m } = - await deployBridgeFixture(); - - const expectedAmount = - m.depositValue + m.callValue + m.processingFee; - const tx = await l1Bridge.sendMessage(m, { - value: expectedAmount, - }); - - const receipt = await tx.wait(); - - const [messageSentEvent] = receipt.events as any as Event[]; - - const { signal } = (messageSentEvent as any).args; - - const sender = l1Bridge.address; - - const key = getSignalSlot(hre, sender, signal); - - const { block, blockHeader } = await getLatestBlockHeader(hre); - - await headerSync.setSyncedHeader(block.hash); - - // get storageValue for the key - const storageValue = await ethers.provider.getStorageAt( - l1Bridge.address, - key, - block.number - ); - // // make sure it equals 1 so we know sendMessage worked - expect(storageValue).to.be.eq( - "0x0000000000000000000000000000000000000000000000000000000000000001" - ); - - const signalProof = await getSignalProof( - hre, - l1Bridge.address, - key, - block.number, - blockHeader - ); - - expect( - await l2Bridge.isMessageReceived( - signal, - srcChainId, - signalProof - ) - ).to.be.eq(true); - }); - }); - - describe("isSignalReceived()", function () { - it("should throw if sender == address(0)", async function () { - const { l2Bridge, srcChainId } = await deployBridgeFixture(); - - const signal = ethers.utils.randomBytes(32); - const sender = ethers.constants.AddressZero; - const signalProof = ethers.constants.HashZero; - - await expect( - l2Bridge.isSignalReceived( - signal, - srcChainId, - sender, - signalProof - ) - ).to.be.revertedWith("B:sender"); - }); - - it("should throw if signal == HashZero", async function () { - const { owner, l2Bridge, srcChainId } = await deployBridgeFixture(); - - const signal = ethers.constants.HashZero; - const sender = owner.address; - const signalProof = ethers.constants.HashZero; - - await expect( - l2Bridge.isSignalReceived( - signal, - srcChainId, - sender, - signalProof - ) - ).to.be.revertedWith("B:signal"); - }); - - it("should throw if calling from same layer", async function () { - const { owner, l1Bridge, headerSync, srcChainId } = - await deployBridgeFixture(); - const signal = ethers.utils.hexlify(ethers.utils.randomBytes(32)); - - const tx = await l1Bridge.connect(owner).sendSignal(signal); - - await tx.wait(); - - const sender = owner.address; - - const key = getSignalSlot(hre, sender, signal); - - const { block, blockHeader } = await getLatestBlockHeader(hre); - - await headerSync.setSyncedHeader(block.hash); - - // get storageValue for the key - const storageValue = await ethers.provider.getStorageAt( - l1Bridge.address, - key, - block.number - ); - // make sure it equals 1 so our proof is valid - expect(storageValue).to.be.eq( - "0x0000000000000000000000000000000000000000000000000000000000000001" - ); - - const signalProof = await getSignalProof( - hre, - l1Bridge.address, - key, - block.number, - blockHeader - ); - - await expect( - l1Bridge.isSignalReceived( - signal, - srcChainId, - sender, - signalProof - ) - ).to.be.revertedWith("B:srcBridge"); - }); - - it("should return true and pass", async function () { - const { owner, l1Bridge, l2Bridge, headerSync, srcChainId } = - await deployBridgeFixture(); - - const signal = ethers.utils.hexlify(ethers.utils.randomBytes(32)); - - const tx = await l1Bridge.connect(owner).sendSignal(signal); - - await tx.wait(); - - const sender = owner.address; - - const key = getSignalSlot(hre, sender, signal); - - const { block, blockHeader } = await getLatestBlockHeader(hre); - - await headerSync.setSyncedHeader(block.hash); - - // get storageValue for the key - const storageValue = await ethers.provider.getStorageAt( - l1Bridge.address, - key, - block.number - ); - // make sure it equals 1 so our proof will pass - expect(storageValue).to.be.eq( - "0x0000000000000000000000000000000000000000000000000000000000000001" - ); - - const signalProof = await getSignalProof( - hre, - l1Bridge.address, - key, - block.number, - blockHeader - ); - // proving functionality; l2Bridge can check if l1Bridge receives a signal - // allowing for dapp cross layer communication - expect( - await l2Bridge.isSignalReceived( - signal, - srcChainId, - sender, - signalProof - ) - ).to.be.eq(true); - }); - }); -}); diff --git a/packages/protocol/test/utils/bridge.ts b/packages/protocol/test/utils/bridge.ts index e69de29bb2..fbebacef20 100644 --- a/packages/protocol/test/utils/bridge.ts +++ b/packages/protocol/test/utils/bridge.ts @@ -0,0 +1,78 @@ +import { BigNumber, Signer } from "ethers"; +import { ethers } from "hardhat"; +import { + AddressManager, + Bridge, + EtherVault, + LibTrieProof, +} from "../../typechain"; + +async function deployBridge( + signer: Signer, + addressManager: AddressManager, + destChain: number, + srcChain: number +): Promise<{ bridge: Bridge; etherVault: EtherVault }> { + const libTrieProof: LibTrieProof = await ( + await ethers.getContractFactory("LibTrieProof") + ) + .connect(signer) + .deploy(); + + const libBridgeProcess = await ( + await ethers.getContractFactory("LibBridgeProcess", { + libraries: { + LibTrieProof: libTrieProof.address, + }, + }) + ) + .connect(signer) + .deploy(); + + const libBridgeRetry = await ( + await ethers.getContractFactory("LibBridgeRetry") + ) + .connect(signer) + .deploy(); + + const BridgeFactory = await ethers.getContractFactory("Bridge", { + libraries: { + LibBridgeProcess: libBridgeProcess.address, + LibBridgeRetry: libBridgeRetry.address, + LibTrieProof: libTrieProof.address, + }, + }); + + const bridge: Bridge = await BridgeFactory.connect(signer).deploy(); + + await bridge.connect(signer).init(addressManager.address); + + await bridge.connect(signer).enableDestChain(destChain, true); + + const etherVault: EtherVault = await ( + await ethers.getContractFactory("EtherVault") + ) + .connect(signer) + .deploy(); + + await etherVault.connect(signer).init(addressManager.address); + + await etherVault.connect(signer).authorize(bridge.address, true); + + await etherVault.connect(signer).authorize(await signer.getAddress(), true); + + await addressManager.setAddress( + `${srcChain}.ether_vault`, + etherVault.address + ); + + await signer.sendTransaction({ + to: etherVault.address, + value: BigNumber.from(100000000), + gasLimit: 1000000, + }); + + return { bridge, etherVault }; +} + +export { deployBridge }; From 3beee0898920e22443ed8096869e8baff10d02fa Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 6 Jan 2023 11:18:45 -0800 Subject: [PATCH 03/39] additional bridge test refactoring --- .../test/bridge/Bridge.integration.test.ts | 295 +++--------------- packages/protocol/test/bridge/Bridge.test.ts | 99 ++---- packages/protocol/test/utils/bridge.ts | 18 +- 3 files changed, 79 insertions(+), 333 deletions(-) diff --git a/packages/protocol/test/bridge/Bridge.integration.test.ts b/packages/protocol/test/bridge/Bridge.integration.test.ts index 6a59a1c710..852232efaf 100644 --- a/packages/protocol/test/bridge/Bridge.integration.test.ts +++ b/packages/protocol/test/bridge/Bridge.integration.test.ts @@ -1,5 +1,5 @@ import { expect } from "chai"; -import { BigNumber } from "ethers"; +import { BigNumber, ethers as ethersLib } from "ethers"; import hre, { ethers } from "hardhat"; import { getLatestBlockHeader, @@ -8,36 +8,48 @@ import { } from "../../tasks/utils"; import { AddressManager, - TestBadReceiver, + Bridge, TestHeaderSync, TestLibBridgeData, } from "../../typechain"; -import { deployBridge } from "../utils/bridge"; +import { deployBridge, sendMessage } from "../utils/bridge"; import { Message } from "../utils/message"; describe("integration:Bridge", function () { - async function deployBridgeFixture() { - const [owner, nonOwner] = await ethers.getSigners(); + let owner: any; + let l2Provider: ethersLib.providers.JsonRpcProvider; + let l2Signer: ethersLib.Signer; + let srcChainId: number; + let enabledDestChainId: number; + let l2NonOwner: ethersLib.Signer; + let l1Bridge: Bridge; + let l2Bridge: Bridge; + let m: Message; + let headerSync: TestHeaderSync; + + beforeEach(async () => { + [owner] = await ethers.getSigners(); const { chainId } = await ethers.provider.getNetwork(); - const srcChainId = chainId; + srcChainId = chainId; // seondary node to deploy L2 on - const l2Provider = new ethers.providers.JsonRpcProvider( + l2Provider = new ethers.providers.JsonRpcProvider( "http://localhost:28545" ); - const l2Signer = await l2Provider.getSigner( + l2Signer = await l2Provider.getSigner( ( await l2Provider.listAccounts() )[0] ); - const l2NonOwner = await l2Provider.getSigner(); + l2NonOwner = await l2Provider.getSigner(); const l2Network = await l2Provider.getNetwork(); - const enabledDestChainId = l2Network.chainId; + + enabledDestChainId = l2Network.chainId; const addressManager: AddressManager = await ( await ethers.getContractFactory("AddressManager") @@ -51,21 +63,19 @@ describe("integration:Bridge", function () { .deploy(); await l2AddressManager.init(); - const { bridge: l1Bridge, etherVault: l1EtherVault } = - await deployBridge( - owner, - addressManager, - enabledDestChainId, - srcChainId - ); + ({ bridge: l1Bridge } = await deployBridge( + owner, + addressManager, + enabledDestChainId, + srcChainId + )); - const { bridge: l2Bridge, etherVault: l2EtherVault } = - await deployBridge( - l2Signer, - l2AddressManager, - srcChainId, - enabledDestChainId - ); + ({ bridge: l2Bridge } = await deployBridge( + l2Signer, + l2AddressManager, + srcChainId, + enabledDestChainId + )); await addressManager.setAddress( `${enabledDestChainId}.bridge`, @@ -76,9 +86,7 @@ describe("integration:Bridge", function () { .connect(l2Signer) .setAddress(`${srcChainId}.bridge`, l1Bridge.address); - const headerSync: TestHeaderSync = await ( - await ethers.getContractFactory("TestHeaderSync") - ) + headerSync = await (await ethers.getContractFactory("TestHeaderSync")) .connect(l2Signer) .deploy(); @@ -86,7 +94,7 @@ describe("integration:Bridge", function () { .connect(l2Signer) .setAddress(`${enabledDestChainId}.taiko`, headerSync.address); - const m: Message = { + m = { id: 1, sender: owner.address, srcChainId: srcChainId, @@ -101,34 +109,10 @@ describe("integration:Bridge", function () { data: ethers.constants.HashZero, memo: "", }; - - return { - owner, - l2Signer, - nonOwner, - l2NonOwner, - l1Bridge, - l2Bridge, - addressManager, - enabledDestChainId, - l1EtherVault, - l2EtherVault, - srcChainId, - headerSync, - m, - }; - } + }); describe("processMessage()", function () { it("should throw if message.gasLimit == 0 & msg.sender is not message.owner", async function () { - const { - owner, - l2NonOwner, - srcChainId, - enabledDestChainId, - l2Bridge, - } = await deployBridgeFixture(); - const m: Message = { id: 1, sender: await l2NonOwner.getAddress(), @@ -151,9 +135,6 @@ describe("integration:Bridge", function () { }); it("should throw if message.destChainId is not equal to current block.chainId", async function () { - const { owner, srcChainId, enabledDestChainId, l2Bridge } = - await deployBridgeFixture(); - const m: Message = { id: 1, sender: owner.address, @@ -176,21 +157,7 @@ describe("integration:Bridge", function () { }); it("should throw if messageStatus of message is != NEW", async function () { - const { l1Bridge, l2Bridge, headerSync, m } = - await deployBridgeFixture(); - - const expectedAmount = - m.depositValue + m.callValue + m.processingFee; - const tx = await l1Bridge.sendMessage(m, { - value: expectedAmount, - }); - - const receipt = await tx.wait(); - - const [messageSentEvent] = receipt.events as any as Event[]; - - const { signal, message } = (messageSentEvent as any).args; - + const { signal, message } = await sendMessage(l1Bridge, m); const sender = l1Bridge.address; const key = getSignalSlot(hre, sender, signal); @@ -217,9 +184,6 @@ describe("integration:Bridge", function () { }); it("should throw if message signalproof is not valid", async function () { - const { l1Bridge, l2Bridge, headerSync, m } = - await deployBridgeFixture(); - const libData: TestLibBridgeData = await ( await ethers.getContractFactory("TestLibBridgeData") ).deploy(); @@ -247,20 +211,7 @@ describe("integration:Bridge", function () { }); it("should throw if message has not been received", async function () { - const { l1Bridge, l2Bridge, headerSync, m } = - await deployBridgeFixture(); - - const expectedAmount = - m.depositValue + m.callValue + m.processingFee; - const tx = await l1Bridge.sendMessage(m, { - value: expectedAmount, - }); - - const receipt = await tx.wait(); - - const [messageSentEvent] = receipt.events as any as Event[]; - - const { signal, message } = (messageSentEvent as any).args; + const { signal, message } = await sendMessage(l1Bridge, m); expect(signal).not.to.be.eq(ethers.constants.HashZero); @@ -301,20 +252,7 @@ describe("integration:Bridge", function () { }); it("processes a message when the signal has been verified from the sending chain", async () => { - const { l1Bridge, l2Bridge, headerSync, m } = - await deployBridgeFixture(); - - const expectedAmount = - m.depositValue + m.callValue + m.processingFee; - const tx = await l1Bridge.sendMessage(m, { - value: expectedAmount, - }); - - const receipt = await tx.wait(); - - const [messageSentEvent] = receipt.events as any as Event[]; - - const { signal, message } = (messageSentEvent as any).args; + const { signal, message } = await sendMessage(l1Bridge, m); expect(signal).not.to.be.eq(ethers.constants.HashZero); @@ -359,8 +297,6 @@ describe("integration:Bridge", function () { describe("isMessageSent()", function () { it("should return false, since no message was sent", async function () { - const { l1Bridge, m } = await deployBridgeFixture(); - const libData = await ( await ethers.getContractFactory("TestLibBridgeData") ).deploy(); @@ -370,19 +306,7 @@ describe("integration:Bridge", function () { }); it("should return true if message was sent properly", async function () { - const { l1Bridge, m } = await deployBridgeFixture(); - - const expectedAmount = - m.depositValue + m.callValue + m.processingFee; - const tx = await l1Bridge.sendMessage(m, { - value: expectedAmount, - }); - - const receipt = await tx.wait(); - - const [messageSentEvent] = receipt.events as any as Event[]; - - const { signal } = (messageSentEvent as any).args; + const { signal } = await sendMessage(l1Bridge, m); expect(signal).not.to.be.eq(ethers.constants.HashZero); @@ -390,121 +314,8 @@ describe("integration:Bridge", function () { }); }); - describe("retryMessage()", function () { - async function retriableMessageSetup() { - const { - owner, - l2Signer, - nonOwner, - l2NonOwner, - l1Bridge, - l2Bridge, - addressManager, - enabledDestChainId, - l1EtherVault, - l2EtherVault, - srcChainId, - headerSync, - } = await deployBridgeFixture(); - - const testBadReceiver: TestBadReceiver = await ( - await ethers.getContractFactory("TestBadReceiver") - ) - .connect(l2Signer) - .deploy(); - - await testBadReceiver.deployed(); - - const m: Message = { - id: 1, - sender: owner.address, - srcChainId: srcChainId, - destChainId: enabledDestChainId, - owner: owner.address, - to: testBadReceiver.address, - refundAddress: owner.address, - depositValue: 1000, - callValue: 1000, - processingFee: 1000, - gasLimit: 1, - data: ethers.constants.HashZero, - memo: "", - }; - - const expectedAmount = - m.depositValue + m.callValue + m.processingFee; - const tx = await l1Bridge.connect(owner).sendMessage(m, { - value: expectedAmount, - }); - - const receipt = await tx.wait(); - - const [messageSentEvent] = receipt.events as any as Event[]; - - const { signal, message } = (messageSentEvent as any).args; - - expect(signal).not.to.be.eq(ethers.constants.HashZero); - - const messageStatus = await l1Bridge.getMessageStatus(signal); - - expect(messageStatus).to.be.eq(0); - - const sender = l1Bridge.address; - - const key = getSignalSlot(hre, sender, signal); - - const { block, blockHeader } = await getLatestBlockHeader(hre); - - await headerSync.setSyncedHeader(block.hash); - - const signalProof = await getSignalProof( - hre, - l1Bridge.address, - key, - block.number, - blockHeader - ); - - await l2Bridge - .connect(l2NonOwner) - .processMessage(message, signalProof, { - gasLimit: BigNumber.from(2000000), - }); - - const status = await l2Bridge.getMessageStatus(signal); - expect(status).to.be.eq(1); // message is retriable now - // because the LibBridgeInvoke call failed, because - // message.to is a bad receiver and throws upon receipt - - return { - message, - l2Signer, - l2NonOwner, - l1Bridge, - l2Bridge, - addressManager, - headerSync, - owner, - nonOwner, - srcChainId, - enabledDestChainId, - l1EtherVault, - l2EtherVault, - signal, - }; - } - it("setup message to fail first processMessage", async function () { - const { l2Bridge, signal } = await retriableMessageSetup(); - l2Bridge; - signal; - }); - }); - describe("isMessageReceived()", function () { it("should throw if signal is not a bridge message; proof is invalid since sender != bridge.", async function () { - const { owner, l1Bridge, l2Bridge, headerSync, srcChainId } = - await deployBridgeFixture(); - const signal = ethers.utils.hexlify(ethers.utils.randomBytes(32)); const tx = await l1Bridge.connect(owner).sendSignal(signal); @@ -544,20 +355,7 @@ describe("integration:Bridge", function () { }); it("should return true", async function () { - const { l1Bridge, srcChainId, l2Bridge, headerSync, m } = - await deployBridgeFixture(); - - const expectedAmount = - m.depositValue + m.callValue + m.processingFee; - const tx = await l1Bridge.sendMessage(m, { - value: expectedAmount, - }); - - const receipt = await tx.wait(); - - const [messageSentEvent] = receipt.events as any as Event[]; - - const { signal } = (messageSentEvent as any).args; + const { signal } = await sendMessage(l1Bridge, m); const sender = l1Bridge.address; @@ -598,8 +396,6 @@ describe("integration:Bridge", function () { describe("isSignalReceived()", function () { it("should throw if sender == address(0)", async function () { - const { l2Bridge, srcChainId } = await deployBridgeFixture(); - const signal = ethers.utils.randomBytes(32); const sender = ethers.constants.AddressZero; const signalProof = ethers.constants.HashZero; @@ -615,8 +411,6 @@ describe("integration:Bridge", function () { }); it("should throw if signal == HashZero", async function () { - const { owner, l2Bridge, srcChainId } = await deployBridgeFixture(); - const signal = ethers.constants.HashZero; const sender = owner.address; const signalProof = ethers.constants.HashZero; @@ -632,8 +426,6 @@ describe("integration:Bridge", function () { }); it("should throw if calling from same layer", async function () { - const { owner, l1Bridge, headerSync, srcChainId } = - await deployBridgeFixture(); const signal = ethers.utils.hexlify(ethers.utils.randomBytes(32)); const tx = await l1Bridge.connect(owner).sendSignal(signal); @@ -678,9 +470,6 @@ describe("integration:Bridge", function () { }); it("should return true and pass", async function () { - const { owner, l1Bridge, l2Bridge, headerSync, srcChainId } = - await deployBridgeFixture(); - const signal = ethers.utils.hexlify(ethers.utils.randomBytes(32)); const tx = await l1Bridge.connect(owner).sendSignal(signal); diff --git a/packages/protocol/test/bridge/Bridge.test.ts b/packages/protocol/test/bridge/Bridge.test.ts index 62b5ac804a..3b52e5e5ed 100644 --- a/packages/protocol/test/bridge/Bridge.test.ts +++ b/packages/protocol/test/bridge/Bridge.test.ts @@ -1,49 +1,42 @@ import { expect } from "chai"; import { BigNumber } from "ethers"; import { ethers } from "hardhat"; -import { AddressManager } from "../../typechain"; -import { deployBridge } from "../utils/bridge"; +import { AddressManager, Bridge, EtherVault } from "../../typechain"; +import { deployBridge, sendMessage } from "../utils/bridge"; import { Message } from "../utils/message"; describe("Bridge", function () { - async function deployBridgeFixture() { - const [owner, nonOwner] = await ethers.getSigners(); + let owner: any; + let nonOwner: any; + let srcChainId: number; + let enabledDestChainId: number; + let l1Bridge: Bridge; + let l1EtherVault: EtherVault; + + beforeEach(async () => { + [owner, nonOwner] = await ethers.getSigners(); const { chainId } = await ethers.provider.getNetwork(); - const srcChainId = chainId; + srcChainId = chainId; - const enabledDestChainId = srcChainId + 1; + enabledDestChainId = srcChainId + 1; const addressManager: AddressManager = await ( await ethers.getContractFactory("AddressManager") ).deploy(); await addressManager.init(); - const { bridge: l1Bridge, etherVault: l1EtherVault } = - await deployBridge( - owner, - addressManager, - enabledDestChainId, - srcChainId - ); - - // deploy protocol contract - return { + ({ bridge: l1Bridge, etherVault: l1EtherVault } = await deployBridge( owner, - nonOwner, - l1Bridge, addressManager, enabledDestChainId, - l1EtherVault, - srcChainId, - }; - } + srcChainId + )); + }); describe("sendMessage()", function () { it("throws when owner is the zero address", async () => { - const { owner, nonOwner, l1Bridge } = await deployBridgeFixture(); - const message: Message = { id: 1, sender: owner.address, @@ -66,8 +59,6 @@ describe("Bridge", function () { }); it("throws when dest chain id is same as block.chainid", async () => { - const { owner, nonOwner, l1Bridge } = await deployBridgeFixture(); - const network = await ethers.provider.getNetwork(); const message: Message = { id: 1, @@ -91,8 +82,6 @@ describe("Bridge", function () { }); it("throws when dest chain id is not enabled", async () => { - const { owner, nonOwner, l1Bridge } = await deployBridgeFixture(); - const message: Message = { id: 1, sender: owner.address, @@ -115,9 +104,6 @@ describe("Bridge", function () { }); it("throws when msg.value is not the same as expected amount", async () => { - const { owner, nonOwner, l1Bridge, enabledDestChainId } = - await deployBridgeFixture(); - const message: Message = { id: 1, sender: owner.address, @@ -140,14 +126,6 @@ describe("Bridge", function () { }); it("emits event and is successful when message is valid, ether_vault receives the expectedAmount", async () => { - const { - owner, - nonOwner, - l1EtherVault, - l1Bridge, - enabledDestChainId, - } = await deployBridgeFixture(); - const etherVaultOriginalBalance = await ethers.provider.getBalance( l1EtherVault.address ); @@ -172,11 +150,8 @@ describe("Bridge", function () { message.depositValue + message.callValue + message.processingFee; - await expect( - l1Bridge.sendMessage(message, { - value: expectedAmount, - }) - ).to.emit(l1Bridge, "MessageSent"); + + await sendMessage(l1Bridge, message); const etherVaultUpdatedBalance = await ethers.provider.getBalance( l1EtherVault.address @@ -190,16 +165,12 @@ describe("Bridge", function () { describe("sendSignal()", async function () { it("throws when signal is empty", async function () { - const { owner, l1Bridge } = await deployBridgeFixture(); - await expect( l1Bridge.connect(owner).sendSignal(ethers.constants.HashZero) ).to.be.revertedWith("B:signal"); }); it("sends signal, confirms it was sent", async function () { - const { owner, l1Bridge } = await deployBridgeFixture(); - const hash = "0xf2e08f6b93d8cf4f37a3b38f91a8c37198095dde8697463ca3789e25218a8e9d"; await expect(l1Bridge.connect(owner).sendSignal(hash)) @@ -216,16 +187,11 @@ describe("Bridge", function () { describe("isDestChainEnabled()", function () { it("is disabled for unabled chainIds", async () => { - const { l1Bridge } = await deployBridgeFixture(); - const enabled = await l1Bridge.isDestChainEnabled(68); expect(enabled).to.be.eq(false); }); it("is enabled for enabled chainId", async () => { - const { l1Bridge, enabledDestChainId } = - await deployBridgeFixture(); - const enabled = await l1Bridge.isDestChainEnabled( enabledDestChainId ); @@ -235,8 +201,6 @@ describe("Bridge", function () { describe("context()", function () { it("returns unitialized context", async () => { - const { l1Bridge } = await deployBridgeFixture(); - const ctx = await l1Bridge.context(); expect(ctx[0]).to.be.eq(ethers.constants.HashZero); expect(ctx[1]).to.be.eq(ethers.constants.AddressZero); @@ -246,8 +210,6 @@ describe("Bridge", function () { describe("getMessageStatus()", function () { it("returns new for uninitialized signal", async () => { - const { l1Bridge } = await deployBridgeFixture(); - const messageStatus = await l1Bridge.getMessageStatus( ethers.constants.HashZero ); @@ -256,9 +218,6 @@ describe("Bridge", function () { }); it("returns for initiaized signal", async () => { - const { owner, nonOwner, enabledDestChainId, l1Bridge } = - await deployBridgeFixture(); - const message: Message = { id: 1, sender: owner.address, @@ -275,20 +234,7 @@ describe("Bridge", function () { memo: "", }; - const expectedAmount = - message.depositValue + - message.callValue + - message.processingFee; - - const tx = await l1Bridge.sendMessage(message, { - value: expectedAmount, - }); - - const receipt = await tx.wait(); - - const [messageSentEvent] = receipt.events as any as Event[]; - - const { signal } = (messageSentEvent as any).args; + const { signal } = await sendMessage(l1Bridge, message); expect(signal).not.to.be.eq(ethers.constants.HashZero); @@ -300,9 +246,6 @@ describe("Bridge", function () { describe("processMessage()", async function () { it("throws when message.gasLimit is 0 and msg.sender is not the message.owner", async () => { - const { owner, nonOwner, l1Bridge, enabledDestChainId } = - await deployBridgeFixture(); - const message: Message = { id: 1, sender: owner.address, @@ -327,8 +270,6 @@ describe("Bridge", function () { }); it("throws message.destChainId is not block.chainId", async () => { - const { owner, nonOwner, l1Bridge } = await deployBridgeFixture(); - const message: Message = { id: 1, sender: nonOwner.address, diff --git a/packages/protocol/test/utils/bridge.ts b/packages/protocol/test/utils/bridge.ts index fbebacef20..568712c424 100644 --- a/packages/protocol/test/utils/bridge.ts +++ b/packages/protocol/test/utils/bridge.ts @@ -6,6 +6,7 @@ import { EtherVault, LibTrieProof, } from "../../typechain"; +import { Message } from "./message"; async function deployBridge( signer: Signer, @@ -75,4 +76,19 @@ async function deployBridge( return { bridge, etherVault }; } -export { deployBridge }; +async function sendMessage(bridge: Bridge, m: Message) { + const expectedAmount = m.depositValue + m.callValue + m.processingFee; + + const tx = await bridge.sendMessage(m, { + value: expectedAmount, + }); + + const receipt = await tx.wait(); + + const [messageSentEvent] = receipt.events as any as Event[]; + + const { signal, message } = (messageSentEvent as any).args; + + return { bridge, messageSentEvent, signal, message }; +} +export { deployBridge, sendMessage }; From 931e1efa30453651817f6a0f9a440db26b41270d Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 6 Jan 2023 12:30:22 -0800 Subject: [PATCH 04/39] move ethervault/tokenvautl to own folders, addtl refactors from task/utils to regular utils folders, reusabel deploy functions for taikol1 and taikol2 --- packages/protocol/tasks/utils.ts | 91 ------------------- .../test/L1/TaikoL1.integration.test.ts | 80 ++-------------- packages/protocol/test/L1/TaikoL1.test.ts | 44 +-------- packages/protocol/test/L2/TaikoL2.test.ts | 19 +--- .../test/bridge/Bridge.integration.test.ts | 12 +-- packages/protocol/test/bridge/Bridge.test.ts | 8 +- .../test/bridge/libs/LibBridgeData.test.ts | 3 +- .../test/bridge/libs/LibBridgeProcess.test.ts | 7 +- .../test/bridge/libs/LibBridgeRetry.test.ts | 6 +- .../{bridge => etherVault}/EtherVault.test.ts | 0 .../protocol/test/libs/LibTrieProof.test.ts | 2 +- .../thirdparty/LibBlockHeaderDecoder.test.ts | 5 +- .../{bridge => tokenVault}/TokenVault.test.ts | 0 packages/protocol/test/utils/bridge.ts | 1 + packages/protocol/test/utils/message.ts | 16 +++- packages/protocol/test/utils/rpc.ts | 38 +++++++- packages/protocol/test/utils/signal.ts | 44 +++++++++ packages/protocol/test/utils/taikoL1.ts | 60 ++++++++++++ packages/protocol/test/utils/taikoL2.ts | 37 ++++++++ 19 files changed, 225 insertions(+), 248 deletions(-) rename packages/protocol/test/{bridge => etherVault}/EtherVault.test.ts (100%) rename packages/protocol/test/{bridge => tokenVault}/TokenVault.test.ts (100%) create mode 100644 packages/protocol/test/utils/signal.ts create mode 100644 packages/protocol/test/utils/taikoL1.ts create mode 100644 packages/protocol/test/utils/taikoL2.ts diff --git a/packages/protocol/tasks/utils.ts b/packages/protocol/tasks/utils.ts index fc50bf80bc..e8a82b35c9 100644 --- a/packages/protocol/tasks/utils.ts +++ b/packages/protocol/tasks/utils.ts @@ -1,7 +1,5 @@ import * as fs from "fs"; import * as log from "./log"; -import { Block, BlockHeader, EthGetProofResponse } from "../test/utils/rpc"; -import RLP from "rlp"; async function deployContract( hre: any, @@ -68,94 +66,10 @@ function getDeployments(_fileName: string) { return JSON.parse(`${json}`); } -async function getMessageStatusSlot(hre: any, signal: any) { - return hre.ethers.utils.solidityKeccak256( - ["string", "bytes"], - ["MESSAGE_STATUS", signal] - ); -} - async function decode(hre: any, type: any, data: any) { return hre.ethers.utils.defaultAbiCoder.decode([type], data).toString(); } -function getSignalSlot(hre: any, sender: any, signal: any) { - return hre.ethers.utils.keccak256( - hre.ethers.utils.solidityPack( - ["string", "address", "bytes32"], - ["SIGNAL", sender, signal] - ) - ); -} - -const MessageStatus = { - NEW: 0, - RETRIABLE: 1, - DONE: 2, - FAILED: 3, -}; - -async function getLatestBlockHeader(hre: any) { - const block: Block = await hre.ethers.provider.send( - "eth_getBlockByNumber", - ["latest", false] - ); - - const logsBloom = block.logsBloom.toString().substring(2); - - const blockHeader: BlockHeader = { - parentHash: block.parentHash, - ommersHash: block.sha3Uncles, - beneficiary: block.miner, - stateRoot: block.stateRoot, - transactionsRoot: block.transactionsRoot, - receiptsRoot: block.receiptsRoot, - logsBloom: logsBloom.match(/.{1,64}/g)!.map((s: string) => "0x" + s), - difficulty: block.difficulty, - height: block.number, - gasLimit: block.gasLimit, - gasUsed: block.gasUsed, - timestamp: block.timestamp, - extraData: block.extraData, - mixHash: block.mixHash, - nonce: block.nonce, - baseFeePerGas: block.baseFeePerGas ? parseInt(block.baseFeePerGas) : 0, - }; - - return { block, blockHeader }; -} - -async function getSignalProof( - hre: any, - contractAddress: string, - key: string, - blockNumber: number, - blockHeader: BlockHeader -) { - const proof: EthGetProofResponse = await hre.ethers.provider.send( - "eth_getProof", - [contractAddress, [key], blockNumber] - ); - - // RLP encode the proof together for LibTrieProof to decode - const encodedProof = hre.ethers.utils.defaultAbiCoder.encode( - ["bytes", "bytes"], - [ - RLP.encode(proof.accountProof), - RLP.encode(proof.storageProof[0].proof), - ] - ); - // encode the SignalProof struct from LibBridgeSignal - const signalProof = hre.ethers.utils.defaultAbiCoder.encode( - [ - "tuple(tuple(bytes32 parentHash, bytes32 ommersHash, address beneficiary, bytes32 stateRoot, bytes32 transactionsRoot, bytes32 receiptsRoot, bytes32[8] logsBloom, uint256 difficulty, uint128 height, uint64 gasLimit, uint64 gasUsed, uint64 timestamp, bytes extraData, bytes32 mixHash, uint64 nonce, uint256 baseFeePerGas) header, bytes proof)", - ], - [{ header: blockHeader, proof: encodedProof }] - ); - - return signalProof; -} - export { deployContract, getDeployer, @@ -163,10 +77,5 @@ export { getContract, saveDeployments, getDeployments, - getMessageStatusSlot, - getSignalSlot, decode, - MessageStatus, - getLatestBlockHeader, - getSignalProof, }; diff --git a/packages/protocol/test/L1/TaikoL1.integration.test.ts b/packages/protocol/test/L1/TaikoL1.integration.test.ts index a50fbfa936..0e3dde4bd7 100644 --- a/packages/protocol/test/L1/TaikoL1.integration.test.ts +++ b/packages/protocol/test/L1/TaikoL1.integration.test.ts @@ -5,6 +5,8 @@ import { TaikoL1, TaikoL2 } from "../../typechain"; import { BlockMetadata } from "../utils/block_metadata"; import { commitBlock, generateCommitHash } from "../utils/commit"; import { buildProposeBlockInputs, proposeBlock } from "../utils/propose"; +import { deployTaikoL1 } from "../utils/taikoL1"; +import { deployTaikoL2 } from "../utils/taikoL2"; describe("integration:TaikoL1", function () { let taikoL1: TaikoL1; @@ -23,79 +25,11 @@ describe("integration:TaikoL1", function () { )[0] ); - const addressManager = await ( - await ethers.getContractFactory("AddressManager") - ).deploy(); - await addressManager.init(); - - const libReceiptDecoder = await ( - await ethers.getContractFactory("LibReceiptDecoder") - ).deploy(); - - const libTxDecoder = await ( - await ethers.getContractFactory("LibTxDecoder") - ).deploy(); - - const libProposing = await ( - await ethers.getContractFactory("LibProposing") - ).deploy(); - - const libProving = await ( - await ethers.getContractFactory("LibProving", { - libraries: { - LibReceiptDecoder: libReceiptDecoder.address, - LibTxDecoder: libTxDecoder.address, - }, - }) - ).deploy(); - - const libVerifying = await ( - await ethers.getContractFactory("LibVerifying") - ).deploy(); - - const l2AddressManager = await ( - await ethers.getContractFactory("AddressManager") - ) - .connect(l2Signer) - .deploy(); - await l2AddressManager.init(); - - // Deploying TaikoL2 Contract linked with LibTxDecoder (throws error otherwise) - const l2LibTxDecoder = await ( - await ethers.getContractFactory("LibTxDecoder") - ) - .connect(l2Signer) - .deploy(); - - taikoL2 = await ( - await ethers.getContractFactory("TaikoL2", { - libraries: { - LibTxDecoder: l2LibTxDecoder.address, - }, - }) - ) - .connect(l2Signer) - .deploy(l2AddressManager.address); - - const genesisHash = taikoL2.deployTransaction.blockHash; - - taikoL1 = await ( - await ethers.getContractFactory("TestTaikoL1", { - libraries: { - LibVerifying: libVerifying.address, - LibProposing: libProposing.address, - LibProving: libProving.address, - }, - }) - ).deploy(); - - const feeBase = BigNumber.from(10).pow(18); - - await taikoL1.init( - addressManager.address, - genesisHash as string, - feeBase - ); + taikoL2 = await deployTaikoL2(l2Signer); + + const genesisHash = taikoL2.deployTransaction.blockHash as string; + + taikoL1 = await deployTaikoL1(genesisHash); }); describe("isCommitValid()", async function () { diff --git a/packages/protocol/test/L1/TaikoL1.test.ts b/packages/protocol/test/L1/TaikoL1.test.ts index 11665c4339..145cef39f6 100644 --- a/packages/protocol/test/L1/TaikoL1.test.ts +++ b/packages/protocol/test/L1/TaikoL1.test.ts @@ -1,56 +1,16 @@ import { expect } from "chai"; -import { BigNumber } from "ethers"; import { ethers } from "hardhat"; import { TaikoL1 } from "../../typechain"; import { randomBytes32 } from "../utils/bytes"; +import { deployTaikoL1 } from "../utils/taikoL1"; describe("TaikoL1", function () { let taikoL1: TaikoL1; let genesisHash: string; beforeEach(async function () { - const addressManager = await ( - await ethers.getContractFactory("AddressManager") - ).deploy(); - await addressManager.init(); - - const libReceiptDecoder = await ( - await ethers.getContractFactory("LibReceiptDecoder") - ).deploy(); - - const libTxDecoder = await ( - await ethers.getContractFactory("LibTxDecoder") - ).deploy(); - - const libProposing = await ( - await ethers.getContractFactory("LibProposing") - ).deploy(); - - const libProving = await ( - await ethers.getContractFactory("LibProving", { - libraries: { - LibReceiptDecoder: libReceiptDecoder.address, - LibTxDecoder: libTxDecoder.address, - }, - }) - ).deploy(); - - const libVerifying = await ( - await ethers.getContractFactory("LibVerifying") - ).deploy(); - genesisHash = randomBytes32(); - const feeBase = BigNumber.from(10).pow(18); - taikoL1 = await ( - await ethers.getContractFactory("TestTaikoL1", { - libraries: { - LibVerifying: libVerifying.address, - LibProposing: libProposing.address, - LibProving: libProving.address, - }, - }) - ).deploy(); - await taikoL1.init(addressManager.address, genesisHash, feeBase); + taikoL1 = await deployTaikoL1(genesisHash); }); describe("getLatestSyncedHeader()", async function () { diff --git a/packages/protocol/test/L2/TaikoL2.test.ts b/packages/protocol/test/L2/TaikoL2.test.ts index 4195f9b5fa..c2009bf216 100644 --- a/packages/protocol/test/L2/TaikoL2.test.ts +++ b/packages/protocol/test/L2/TaikoL2.test.ts @@ -2,28 +2,13 @@ import { expect } from "chai"; import { ethers } from "hardhat"; import { TaikoL2 } from "../../typechain"; import { randomBytes32 } from "../utils/bytes"; +import { deployTaikoL2 } from "../utils/taikoL2"; describe("TaikoL2", function () { let taikoL2: TaikoL2; beforeEach(async function () { - const addressManager = await ( - await ethers.getContractFactory("AddressManager") - ).deploy(); - await addressManager.init(); - - // Deploying TaikoL2 Contract linked with LibTxDecoder (throws error otherwise) - const libTxDecoder = await ( - await ethers.getContractFactory("LibTxDecoder") - ).deploy(); - - taikoL2 = await ( - await ethers.getContractFactory("TaikoL2", { - libraries: { - LibTxDecoder: libTxDecoder.address, - }, - }) - ).deploy(addressManager.address); + taikoL2 = await deployTaikoL2((await ethers.getSigners())[0]); }); describe("anchor()", async function () { diff --git a/packages/protocol/test/bridge/Bridge.integration.test.ts b/packages/protocol/test/bridge/Bridge.integration.test.ts index 852232efaf..70552a35b1 100644 --- a/packages/protocol/test/bridge/Bridge.integration.test.ts +++ b/packages/protocol/test/bridge/Bridge.integration.test.ts @@ -1,11 +1,6 @@ import { expect } from "chai"; import { BigNumber, ethers as ethersLib } from "ethers"; import hre, { ethers } from "hardhat"; -import { - getLatestBlockHeader, - getSignalProof, - getSignalSlot, -} from "../../tasks/utils"; import { AddressManager, Bridge, @@ -13,7 +8,10 @@ import { TestLibBridgeData, } from "../../typechain"; import { deployBridge, sendMessage } from "../utils/bridge"; +import { randomBytes32 } from "../utils/bytes"; import { Message } from "../utils/message"; +import { getLatestBlockHeader } from "../utils/rpc"; +import { getSignalProof, getSignalSlot } from "../utils/signal"; describe("integration:Bridge", function () { let owner: any; @@ -396,7 +394,7 @@ describe("integration:Bridge", function () { describe("isSignalReceived()", function () { it("should throw if sender == address(0)", async function () { - const signal = ethers.utils.randomBytes(32); + const signal = randomBytes32(); const sender = ethers.constants.AddressZero; const signalProof = ethers.constants.HashZero; @@ -426,7 +424,7 @@ describe("integration:Bridge", function () { }); it("should throw if calling from same layer", async function () { - const signal = ethers.utils.hexlify(ethers.utils.randomBytes(32)); + const signal = randomBytes32(); const tx = await l1Bridge.connect(owner).sendSignal(signal); diff --git a/packages/protocol/test/bridge/Bridge.test.ts b/packages/protocol/test/bridge/Bridge.test.ts index 3b52e5e5ed..33577568d9 100644 --- a/packages/protocol/test/bridge/Bridge.test.ts +++ b/packages/protocol/test/bridge/Bridge.test.ts @@ -262,10 +262,8 @@ describe("Bridge", function () { memo: "", }; - const proof = ethers.constants.HashZero; - await expect( - l1Bridge.processMessage(message, proof) + l1Bridge.processMessage(message, ethers.constants.HashZero) ).to.be.revertedWith("B:forbidden"); }); @@ -286,10 +284,8 @@ describe("Bridge", function () { memo: "", }; - const proof = ethers.constants.HashZero; - await expect( - l1Bridge.processMessage(message, proof) + l1Bridge.processMessage(message, ethers.constants.HashZero) ).to.be.revertedWith("B:destChainId"); }); }); diff --git a/packages/protocol/test/bridge/libs/LibBridgeData.test.ts b/packages/protocol/test/bridge/libs/LibBridgeData.test.ts index 2d04c6e4d4..c78bbd7634 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeData.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeData.test.ts @@ -2,8 +2,7 @@ import { expect } from "chai"; import { ethers } from "hardhat"; import { TestLibBridgeData } from "../../../typechain"; import { K_BRIDGE_MESSAGE } from "../../constants/messages"; -import { MessageStatus } from "../../../tasks/utils"; -import { Message } from "../../utils/message"; +import { Message, MessageStatus } from "../../utils/message"; describe("LibBridgeData", function () { let owner: any; diff --git a/packages/protocol/test/bridge/libs/LibBridgeProcess.test.ts b/packages/protocol/test/bridge/libs/LibBridgeProcess.test.ts index c542d640ae..4655bdb395 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeProcess.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeProcess.test.ts @@ -1,8 +1,11 @@ import * as helpers from "@nomicfoundation/hardhat-network-helpers"; import { expect } from "chai"; import hre, { ethers } from "hardhat"; -import { getMessageStatusSlot, MessageStatus } from "../../../tasks/utils"; -import { Message } from "../../utils/message"; +import { + getMessageStatusSlot, + Message, + MessageStatus, +} from "../../utils/message"; import { AddressManager, EtherVault, diff --git a/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts b/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts index bd99c8110e..2cc6aa7cce 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts @@ -1,12 +1,12 @@ import * as helpers from "@nomicfoundation/hardhat-network-helpers"; import { expect } from "chai"; import hre, { ethers } from "hardhat"; -import { Message } from "../../utils/message"; import { getMessageStatusSlot, - decode, + Message, MessageStatus, -} from "../../../tasks/utils"; +} from "../../utils/message"; +import { decode } from "../../../tasks/utils"; import { AddressManager, EtherVault, diff --git a/packages/protocol/test/bridge/EtherVault.test.ts b/packages/protocol/test/etherVault/EtherVault.test.ts similarity index 100% rename from packages/protocol/test/bridge/EtherVault.test.ts rename to packages/protocol/test/etherVault/EtherVault.test.ts diff --git a/packages/protocol/test/libs/LibTrieProof.test.ts b/packages/protocol/test/libs/LibTrieProof.test.ts index 89b5e7f1b7..bd8d671c5e 100644 --- a/packages/protocol/test/libs/LibTrieProof.test.ts +++ b/packages/protocol/test/libs/LibTrieProof.test.ts @@ -3,7 +3,7 @@ import hre, { ethers } from "hardhat"; import RLP from "rlp"; import { Message } from "../utils/message"; import { EthGetProofResponse } from "../utils/rpc"; -import { getSignalSlot } from "../../tasks/utils"; +import { getSignalSlot } from "../utils/signal"; describe("integration:LibTrieProof", function () { async function deployLibTrieProofFixture() { diff --git a/packages/protocol/test/thirdparty/LibBlockHeaderDecoder.test.ts b/packages/protocol/test/thirdparty/LibBlockHeaderDecoder.test.ts index e3e2115601..3c2edc9431 100644 --- a/packages/protocol/test/thirdparty/LibBlockHeaderDecoder.test.ts +++ b/packages/protocol/test/thirdparty/LibBlockHeaderDecoder.test.ts @@ -1,14 +1,15 @@ // eslint-disable-next-line no-unused-vars import { expect } from "chai"; import { keccak256 } from "ethers/lib/utils"; +import { LibBlockHeaderDecoder, TestLibBlockHeader } from "../../typechain"; const hre = require("hardhat"); const ethers = hre.ethers; const EBN = ethers.BigNumber; describe("LibBlockHeaderDecoder", async function () { // eslint-disable-next-line no-unused-vars - let blockHeaderDecoder: any; - let hashBlockHeader: any; + let blockHeaderDecoder: LibBlockHeaderDecoder; + let hashBlockHeader: TestLibBlockHeader; before(async function () { // Deploying Lib to Link diff --git a/packages/protocol/test/bridge/TokenVault.test.ts b/packages/protocol/test/tokenVault/TokenVault.test.ts similarity index 100% rename from packages/protocol/test/bridge/TokenVault.test.ts rename to packages/protocol/test/tokenVault/TokenVault.test.ts diff --git a/packages/protocol/test/utils/bridge.ts b/packages/protocol/test/utils/bridge.ts index 568712c424..1b7e8ad180 100644 --- a/packages/protocol/test/utils/bridge.ts +++ b/packages/protocol/test/utils/bridge.ts @@ -91,4 +91,5 @@ async function sendMessage(bridge: Bridge, m: Message) { return { bridge, messageSentEvent, signal, message }; } + export { deployBridge, sendMessage }; diff --git a/packages/protocol/test/utils/message.ts b/packages/protocol/test/utils/message.ts index 717641d1b2..bf74fb9906 100644 --- a/packages/protocol/test/utils/message.ts +++ b/packages/protocol/test/utils/message.ts @@ -14,4 +14,18 @@ type Message = { memo: string; }; -export { Message }; +const MessageStatus = { + NEW: 0, + RETRIABLE: 1, + DONE: 2, + FAILED: 3, +}; + +async function getMessageStatusSlot(hre: any, signal: any) { + return hre.ethers.utils.solidityKeccak256( + ["string", "bytes"], + ["MESSAGE_STATUS", signal] + ); +} + +export { Message, MessageStatus, getMessageStatusSlot }; diff --git a/packages/protocol/test/utils/rpc.ts b/packages/protocol/test/utils/rpc.ts index 250d4ea87a..77473084ae 100644 --- a/packages/protocol/test/utils/rpc.ts +++ b/packages/protocol/test/utils/rpc.ts @@ -56,4 +56,40 @@ type BlockHeader = { baseFeePerGas: number; }; -export { Block, BlockHeader, StorageEntry, EthGetProofResponse }; +async function getLatestBlockHeader(hre: any) { + const block: Block = await hre.ethers.provider.send( + "eth_getBlockByNumber", + ["latest", false] + ); + + const logsBloom = block.logsBloom.toString().substring(2); + + const blockHeader: BlockHeader = { + parentHash: block.parentHash, + ommersHash: block.sha3Uncles, + beneficiary: block.miner, + stateRoot: block.stateRoot, + transactionsRoot: block.transactionsRoot, + receiptsRoot: block.receiptsRoot, + logsBloom: logsBloom.match(/.{1,64}/g)!.map((s: string) => "0x" + s), + difficulty: block.difficulty, + height: block.number, + gasLimit: block.gasLimit, + gasUsed: block.gasUsed, + timestamp: block.timestamp, + extraData: block.extraData, + mixHash: block.mixHash, + nonce: block.nonce, + baseFeePerGas: block.baseFeePerGas ? parseInt(block.baseFeePerGas) : 0, + }; + + return { block, blockHeader }; +} + +export { + Block, + BlockHeader, + StorageEntry, + EthGetProofResponse, + getLatestBlockHeader, +}; diff --git a/packages/protocol/test/utils/signal.ts b/packages/protocol/test/utils/signal.ts new file mode 100644 index 0000000000..32b5b1c54f --- /dev/null +++ b/packages/protocol/test/utils/signal.ts @@ -0,0 +1,44 @@ +import RLP from "rlp"; +import { BlockHeader, EthGetProofResponse } from "./rpc"; + +function getSignalSlot(hre: any, sender: string, signal: string) { + return hre.ethers.utils.keccak256( + hre.ethers.utils.solidityPack( + ["string", "address", "bytes32"], + ["SIGNAL", sender, signal] + ) + ); +} + +async function getSignalProof( + hre: any, + contractAddress: string, + key: string, + blockNumber: number, + blockHeader: BlockHeader +) { + const proof: EthGetProofResponse = await hre.ethers.provider.send( + "eth_getProof", + [contractAddress, [key], blockNumber] + ); + + // RLP encode the proof together for LibTrieProof to decode + const encodedProof = hre.ethers.utils.defaultAbiCoder.encode( + ["bytes", "bytes"], + [ + RLP.encode(proof.accountProof), + RLP.encode(proof.storageProof[0].proof), + ] + ); + // encode the SignalProof struct from LibBridgeSignal + const signalProof = hre.ethers.utils.defaultAbiCoder.encode( + [ + "tuple(tuple(bytes32 parentHash, bytes32 ommersHash, address beneficiary, bytes32 stateRoot, bytes32 transactionsRoot, bytes32 receiptsRoot, bytes32[8] logsBloom, uint256 difficulty, uint128 height, uint64 gasLimit, uint64 gasUsed, uint64 timestamp, bytes extraData, bytes32 mixHash, uint64 nonce, uint256 baseFeePerGas) header, bytes proof)", + ], + [{ header: blockHeader, proof: encodedProof }] + ); + + return signalProof; +} + +export { getSignalSlot, getSignalProof }; diff --git a/packages/protocol/test/utils/taikoL1.ts b/packages/protocol/test/utils/taikoL1.ts new file mode 100644 index 0000000000..185a66b866 --- /dev/null +++ b/packages/protocol/test/utils/taikoL1.ts @@ -0,0 +1,60 @@ +import { BigNumber } from "ethers"; +import { ethers } from "hardhat"; +import { TestTaikoL1 } from "../../typechain"; + +const defaultFeeBase = BigNumber.from(10).pow(18); + +async function deployTaikoL1( + genesisHash: string, + feeBase?: BigNumber +): Promise { + const addressManager = await ( + await ethers.getContractFactory("AddressManager") + ).deploy(); + await addressManager.init(); + + const libReceiptDecoder = await ( + await ethers.getContractFactory("LibReceiptDecoder") + ).deploy(); + + const libTxDecoder = await ( + await ethers.getContractFactory("LibTxDecoder") + ).deploy(); + + const libProposing = await ( + await ethers.getContractFactory("LibProposing") + ).deploy(); + + const libProving = await ( + await ethers.getContractFactory("LibProving", { + libraries: { + LibReceiptDecoder: libReceiptDecoder.address, + LibTxDecoder: libTxDecoder.address, + }, + }) + ).deploy(); + + const libVerifying = await ( + await ethers.getContractFactory("LibVerifying") + ).deploy(); + + const taikoL1 = await ( + await ethers.getContractFactory("TestTaikoL1", { + libraries: { + LibVerifying: libVerifying.address, + LibProposing: libProposing.address, + LibProving: libProving.address, + }, + }) + ).deploy(); + + await taikoL1.init( + addressManager.address, + genesisHash, + feeBase ?? defaultFeeBase + ); + + return taikoL1; +} + +export { deployTaikoL1, defaultFeeBase }; diff --git a/packages/protocol/test/utils/taikoL2.ts b/packages/protocol/test/utils/taikoL2.ts new file mode 100644 index 0000000000..55b5a0efcd --- /dev/null +++ b/packages/protocol/test/utils/taikoL2.ts @@ -0,0 +1,37 @@ +import { ethers } from "hardhat"; +import { TaikoL2 } from "../../typechain"; + +async function deployTaikoL2(signer: any): Promise { + const addressManager = await ( + await ethers.getContractFactory("AddressManager") + ).deploy(); + await addressManager.init(); + + const l2AddressManager = await ( + await ethers.getContractFactory("AddressManager") + ) + .connect(signer) + .deploy(); + await l2AddressManager.init(); + + // Deploying TaikoL2 Contract linked with LibTxDecoder (throws error otherwise) + const l2LibTxDecoder = await ( + await ethers.getContractFactory("LibTxDecoder") + ) + .connect(signer) + .deploy(); + + const taikoL2: TaikoL2 = await ( + await ethers.getContractFactory("TaikoL2", { + libraries: { + LibTxDecoder: l2LibTxDecoder.address, + }, + }) + ) + .connect(signer) + .deploy(l2AddressManager.address); + + return taikoL2; +} + +export { deployTaikoL2 }; From 7fd5c31875d220c07a454d96b5ea4bf84490bb54 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 6 Jan 2023 12:33:33 -0800 Subject: [PATCH 05/39] folder names match contract names --- .../protocol/test/{L1 => taikoL1}/TaikoL1.integration.test.ts | 0 packages/protocol/test/{L1 => taikoL1}/TaikoL1.test.ts | 0 packages/protocol/test/{L2 => taikoL2}/TaikoL2.test.ts | 0 packages/protocol/test/{L1 => tkoToken}/TkoToken.test.ts | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) rename packages/protocol/test/{L1 => taikoL1}/TaikoL1.integration.test.ts (100%) rename packages/protocol/test/{L1 => taikoL1}/TaikoL1.test.ts (100%) rename packages/protocol/test/{L2 => taikoL2}/TaikoL2.test.ts (100%) rename packages/protocol/test/{L1 => tkoToken}/TkoToken.test.ts (99%) diff --git a/packages/protocol/test/L1/TaikoL1.integration.test.ts b/packages/protocol/test/taikoL1/TaikoL1.integration.test.ts similarity index 100% rename from packages/protocol/test/L1/TaikoL1.integration.test.ts rename to packages/protocol/test/taikoL1/TaikoL1.integration.test.ts diff --git a/packages/protocol/test/L1/TaikoL1.test.ts b/packages/protocol/test/taikoL1/TaikoL1.test.ts similarity index 100% rename from packages/protocol/test/L1/TaikoL1.test.ts rename to packages/protocol/test/taikoL1/TaikoL1.test.ts diff --git a/packages/protocol/test/L2/TaikoL2.test.ts b/packages/protocol/test/taikoL2/TaikoL2.test.ts similarity index 100% rename from packages/protocol/test/L2/TaikoL2.test.ts rename to packages/protocol/test/taikoL2/TaikoL2.test.ts diff --git a/packages/protocol/test/L1/TkoToken.test.ts b/packages/protocol/test/tkoToken/TkoToken.test.ts similarity index 99% rename from packages/protocol/test/L1/TkoToken.test.ts rename to packages/protocol/test/tkoToken/TkoToken.test.ts index 34afa79845..815f842f16 100644 --- a/packages/protocol/test/L1/TkoToken.test.ts +++ b/packages/protocol/test/tkoToken/TkoToken.test.ts @@ -8,7 +8,7 @@ import { } from "../constants/errors"; import { BigNumber } from "ethers"; -describe("TokenVault", function () { +describe("TKOToken", function () { let owner: any; let nonOwner: any; let protoBroker: any; From 4c0aab439c0772c325361c15c092738e6d011a8b Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 6 Jan 2023 12:36:38 -0800 Subject: [PATCH 06/39] undo folder structure, should match contracts tree imo --- .../protocol/test/{taikoL1 => L1}/TaikoL1.integration.test.ts | 0 packages/protocol/test/{taikoL1 => L1}/TaikoL1.test.ts | 0 packages/protocol/test/{tkoToken => L1}/TkoToken.test.ts | 0 packages/protocol/test/{taikoL2 => L2}/TaikoL2.test.ts | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename packages/protocol/test/{taikoL1 => L1}/TaikoL1.integration.test.ts (100%) rename packages/protocol/test/{taikoL1 => L1}/TaikoL1.test.ts (100%) rename packages/protocol/test/{tkoToken => L1}/TkoToken.test.ts (100%) rename packages/protocol/test/{taikoL2 => L2}/TaikoL2.test.ts (100%) diff --git a/packages/protocol/test/taikoL1/TaikoL1.integration.test.ts b/packages/protocol/test/L1/TaikoL1.integration.test.ts similarity index 100% rename from packages/protocol/test/taikoL1/TaikoL1.integration.test.ts rename to packages/protocol/test/L1/TaikoL1.integration.test.ts diff --git a/packages/protocol/test/taikoL1/TaikoL1.test.ts b/packages/protocol/test/L1/TaikoL1.test.ts similarity index 100% rename from packages/protocol/test/taikoL1/TaikoL1.test.ts rename to packages/protocol/test/L1/TaikoL1.test.ts diff --git a/packages/protocol/test/tkoToken/TkoToken.test.ts b/packages/protocol/test/L1/TkoToken.test.ts similarity index 100% rename from packages/protocol/test/tkoToken/TkoToken.test.ts rename to packages/protocol/test/L1/TkoToken.test.ts diff --git a/packages/protocol/test/taikoL2/TaikoL2.test.ts b/packages/protocol/test/L2/TaikoL2.test.ts similarity index 100% rename from packages/protocol/test/taikoL2/TaikoL2.test.ts rename to packages/protocol/test/L2/TaikoL2.test.ts From 377324e821e35018ad3ea3bbdd66b380e0f9b7f0 Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Sat, 7 Jan 2023 22:51:47 +0800 Subject: [PATCH 07/39] Update TkoToken.test.ts --- packages/protocol/test/L1/TkoToken.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/test/L1/TkoToken.test.ts b/packages/protocol/test/L1/TkoToken.test.ts index 815f842f16..114ebe4753 100644 --- a/packages/protocol/test/L1/TkoToken.test.ts +++ b/packages/protocol/test/L1/TkoToken.test.ts @@ -8,7 +8,7 @@ import { } from "../constants/errors"; import { BigNumber } from "ethers"; -describe("TKOToken", function () { +describe("TkoToken", function () { let owner: any; let nonOwner: any; let protoBroker: any; From be414084e16b79a45b4cff8f8ff28cd704fd5569 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Mon, 9 Jan 2023 15:52:06 -0800 Subject: [PATCH 08/39] check status/receipt --- packages/protocol/test/L1/TaikoL1.integration.test.ts | 8 +++++--- packages/protocol/test/utils/propose.ts | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/protocol/test/L1/TaikoL1.integration.test.ts b/packages/protocol/test/L1/TaikoL1.integration.test.ts index 0e3dde4bd7..0ebd4711b9 100644 --- a/packages/protocol/test/L1/TaikoL1.integration.test.ts +++ b/packages/protocol/test/L1/TaikoL1.integration.test.ts @@ -48,7 +48,7 @@ describe("integration:TaikoL1", function () { }); describe("getProposedBlock()", function () { - it("proposed block does not exist", async function () { + it("should return zero values for non-existing blocks without exception to be thrown", async function () { const block = await taikoL1.getProposedBlock(123); expect(block[0]).to.be.eq(ethers.constants.HashZero); expect(block[1]).to.be.eq(ethers.constants.AddressZero); @@ -56,7 +56,7 @@ describe("integration:TaikoL1", function () { }); }); describe("commitBlock() -> proposeBlock() integration", async function () { - it("should revert with invalid meta", async function () { + it("should fail if a proposed block's placeholder field values are not default", async function () { const block = await l2Provider.getBlock("latest"); const { tx, commit } = await commitBlock(taikoL1, block); @@ -79,7 +79,7 @@ describe("integration:TaikoL1", function () { // blockMetadata is inputs[0], txListBytes = inputs[1] const config = await taikoL1.getConfig(); const gasLimit = config[7]; - await proposeBlock( + const proposeReceipt = await proposeBlock( taikoL1, block, commit.txListHash, @@ -88,6 +88,8 @@ describe("integration:TaikoL1", function () { block.gasLimit ); + expect(proposeReceipt.status).to.be.eq(1); + await expect( proposeBlock( taikoL1, diff --git a/packages/protocol/test/utils/propose.ts b/packages/protocol/test/utils/propose.ts index 07fc2b861a..b88f617e86 100644 --- a/packages/protocol/test/utils/propose.ts +++ b/packages/protocol/test/utils/propose.ts @@ -41,6 +41,7 @@ const proposeBlock = async ( const inputs = buildProposeBlockInputs(block, meta); const tx = await taikoL1.proposeBlock(inputs); - return tx; + const receipt = await tx.wait(); + return receipt; }; export { buildProposeBlockInputs, proposeBlock }; From 751492c0d967f70b83bd706e73f2f4212aec34a5 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Mon, 9 Jan 2023 17:24:21 -0800 Subject: [PATCH 09/39] wip --- .github/workflows/solidity.yml | 4 ++ packages/protocol/package.json | 1 + .../test/L1/TaikoL1.integration.test.ts | 71 ++++++++++++++++++- 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/.github/workflows/solidity.yml b/.github/workflows/solidity.yml index 5314a30647..938f74c47f 100644 --- a/.github/workflows/solidity.yml +++ b/.github/workflows/solidity.yml @@ -52,6 +52,10 @@ jobs: working-directory: ./packages/protocol run: pnpm test:integration + - name: protocol - Tokenomics Tests + working-directory: ./packages/protocol + run: pnpm test:tokenomics + - name: protocol - Test Coverage working-directory: ./packages/protocol run: pnpm test:coverage diff --git a/packages/protocol/package.json b/packages/protocol/package.json index 8cf7dfcd67..e35f390e78 100644 --- a/packages/protocol/package.json +++ b/packages/protocol/package.json @@ -16,6 +16,7 @@ "generate:genesis": "ts-node ./utils/generate_genesis/main.ts", "test:genesis": "./test/genesis/generate_genesis.test.sh", "test:integration": "./test/test_integration.sh", + "test:tokenomics": "./test/test_tokenomics.sh", "deploy:hardhat": "LOG_LEVEL=debug pnpm hardhat deploy_L1 --network hardhat --dao-vault 0xdf08f82de32b8d460adbe8d72043e3a7e25a3b39 --team-vault 0xdf08f82de32b8d460adbe8d72043e3a7e25a3b39 --l2-genesis-block-hash 0xee1950562d42f0da28bd4550d88886bc90894c77c9c9eaefef775d4c8223f259 --bridge-funder-private-key ddbf12f72c946bb1e6de5eaf580c51db51828ba198d9b0dba9c7d48ec748dc04 --bridge-fund 0xff --confirmations 1", "lint-staged": "lint-staged --allow-empty" }, diff --git a/packages/protocol/test/L1/TaikoL1.integration.test.ts b/packages/protocol/test/L1/TaikoL1.integration.test.ts index 0ebd4711b9..df893db8eb 100644 --- a/packages/protocol/test/L1/TaikoL1.integration.test.ts +++ b/packages/protocol/test/L1/TaikoL1.integration.test.ts @@ -146,9 +146,13 @@ describe("integration:TaikoL1", function () { nextBlockId.sub(1) ); - expect(proposedBlock[0]).not.to.be.eq(ethers.constants.HashZero); - expect(proposedBlock[2]).not.to.be.eq(ethers.constants.AddressZero); - expect(proposedBlock[3]).not.to.be.eq(BigNumber.from(0)); + expect(proposedBlock.metaHash).not.to.be.eq( + ethers.constants.HashZero + ); + expect(proposedBlock.proposer).not.to.be.eq( + ethers.constants.AddressZero + ); + expect(proposedBlock.proposedAt).not.to.be.eq(BigNumber.from(0)); const isCommitValid = await taikoL1.isCommitValid( 1, @@ -158,5 +162,66 @@ describe("integration:TaikoL1", function () { expect(isCommitValid).to.be.eq(true); }); + + it("should commit and be able to propose for all available slots, then revert when all slots are taken", async function () { + const { maxNumBlocks } = await taikoL1.getConfig(); + // propose blocks and fill up maxNumBlocks number of slots, + // expect each one to be successful. + for (let i = 0; i < maxNumBlocks.toNumber() - 1; i++) { + const block = await l2Provider.getBlock("latest"); + const { tx, commit } = await commitBlock(taikoL1, block); + + const receipt = await proposeBlock( + taikoL1, + block, + commit.txListHash, + tx.blockNumber as number, + 0, + block.gasLimit + ); + + expect(receipt.status).to.be.eq(1); + + const stateVariables = await taikoL1.getStateVariables(); + const nextBlockId = stateVariables[4]; + const proposedBlock = await taikoL1.getProposedBlock( + nextBlockId.sub(1) + ); + + expect(proposedBlock.metaHash).not.to.be.eq( + ethers.constants.HashZero + ); + expect(proposedBlock.proposer).not.to.be.eq( + ethers.constants.AddressZero + ); + expect(proposedBlock.proposedAt).not.to.be.eq( + BigNumber.from(0) + ); + + const isCommitValid = await taikoL1.isCommitValid( + 1, + tx.blockNumber as number, + commit.hash + ); + + expect(isCommitValid).to.be.eq(true); + } + + // now expect another proposed block to be invalid since all slots are full and none have + // been proven. + const block = await l2Provider.getBlock("latest"); + const { tx, commit } = await commitBlock(taikoL1, block); + + await expect( + proposeBlock( + taikoL1, + block, + commit.txListHash, + tx.blockNumber as number, + 0, + block.gasLimit + ) + ).to.be.revertedWith("L1:tooMany"); + }); }); }); From 47e88ebc9dc1d85ecce448c3bb48154e576af77a Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Tue, 10 Jan 2023 07:51:26 -0800 Subject: [PATCH 10/39] merge main, tests passing --- packages/protocol/contracts/bridge/README.md | 2 +- packages/protocol/test/L1/TaikoL1.integration.test.ts | 9 ++++----- packages/protocol/test/utils/bridge.ts | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/protocol/contracts/bridge/README.md b/packages/protocol/contracts/bridge/README.md index 4afb604e8b..634460d899 100644 --- a/packages/protocol/contracts/bridge/README.md +++ b/packages/protocol/contracts/bridge/README.md @@ -18,7 +18,7 @@ Let's go deeper into the steps that occur when bridging ETH from srcChain to des User initiates a bridge transaction with `sendMessage` on the source chain which includes: - `depositValue`, `callValue`, and `processingFee` -- these must sum to `msg.value`. -- The destination chain's ID (must be enabled via `Bridge.enableDestChain()`). +- The destination chain's ID (must be enabled via setting `addressResolver` for `${chainID}.bridge`). Inside the `sendMessage` call, the `msg.value` amount of Ether is sent to the srcChain `EtherVault` contract. Next, a `signal` is created from the message, and a `key` is stored on the srcChain bridge contract address. The `key` is a hash of the `signal` and the srcChain bridge contract address. The `key` is stored on the `Bridge` contract with a value of `1`, and a `MessageSent` event is emitted for the relayer to pick up. diff --git a/packages/protocol/test/L1/TaikoL1.integration.test.ts b/packages/protocol/test/L1/TaikoL1.integration.test.ts index df893db8eb..2f9f1dec63 100644 --- a/packages/protocol/test/L1/TaikoL1.integration.test.ts +++ b/packages/protocol/test/L1/TaikoL1.integration.test.ts @@ -48,11 +48,10 @@ describe("integration:TaikoL1", function () { }); describe("getProposedBlock()", function () { - it("should return zero values for non-existing blocks without exception to be thrown", async function () { - const block = await taikoL1.getProposedBlock(123); - expect(block[0]).to.be.eq(ethers.constants.HashZero); - expect(block[1]).to.be.eq(ethers.constants.AddressZero); - expect(block[2]).to.be.eq(BigNumber.from(0)); + it("should revfert if block is out of range and not a valid proposed block", async function () { + await expect(taikoL1.getProposedBlock(123)).to.be.revertedWith( + "L1:id" + ); }); }); describe("commitBlock() -> proposeBlock() integration", async function () { diff --git a/packages/protocol/test/utils/bridge.ts b/packages/protocol/test/utils/bridge.ts index 1b7e8ad180..3d1d4733d9 100644 --- a/packages/protocol/test/utils/bridge.ts +++ b/packages/protocol/test/utils/bridge.ts @@ -48,8 +48,6 @@ async function deployBridge( await bridge.connect(signer).init(addressManager.address); - await bridge.connect(signer).enableDestChain(destChain, true); - const etherVault: EtherVault = await ( await ethers.getContractFactory("EtherVault") ) @@ -73,6 +71,8 @@ async function deployBridge( gasLimit: 1000000, }); + await addressManager.setAddress(`${destChain}.bridge`, bridge.address); + return { bridge, etherVault }; } From 1c8a5324491893d7e690230f99de1a040114cf0c Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Tue, 10 Jan 2023 08:41:45 -0800 Subject: [PATCH 11/39] chmod test tokenomics --- packages/protocol/test/test_tokenomics.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 packages/protocol/test/test_tokenomics.sh diff --git a/packages/protocol/test/test_tokenomics.sh b/packages/protocol/test/test_tokenomics.sh old mode 100644 new mode 100755 From e701ac52d8f0cabc1af21ffb884fafeb77ea9ab7 Mon Sep 17 00:00:00 2001 From: jeff <113397187+cyberhorsey@users.noreply.github.com> Date: Tue, 10 Jan 2023 09:12:34 -0800 Subject: [PATCH 12/39] Update packages/protocol/test/L1/TaikoL1.integration.test.ts Co-authored-by: dave | d1onys1us <13951458+d1onys1us@users.noreply.github.com> --- packages/protocol/test/L1/TaikoL1.integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/test/L1/TaikoL1.integration.test.ts b/packages/protocol/test/L1/TaikoL1.integration.test.ts index 2f9f1dec63..16982c6fe5 100644 --- a/packages/protocol/test/L1/TaikoL1.integration.test.ts +++ b/packages/protocol/test/L1/TaikoL1.integration.test.ts @@ -48,7 +48,7 @@ describe("integration:TaikoL1", function () { }); describe("getProposedBlock()", function () { - it("should revfert if block is out of range and not a valid proposed block", async function () { + it("should revert if block is out of range and not a valid proposed block", async function () { await expect(taikoL1.getProposedBlock(123)).to.be.revertedWith( "L1:id" ); From cbdc1fabc040d053b996d9c402f2a12ae9c4ab89 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 12 Jan 2023 12:56:39 -0800 Subject: [PATCH 13/39] some refactors for reusabel deploy functions, beginning of tokenomcis scaffolding, a testing Proposer class, testing Prover class --- packages/protocol/contracts/L1/TaikoData.sol | 1 + packages/protocol/contracts/L2/TaikoL2.sol | 6 +- .../contracts/libs/LibSharedConfig.sol | 3 +- .../contracts/test/L1/TestTaikoL1.sol | 1 + .../test/L1/TestTaikoL1EnableTokenomics.sol | 7 +- .../test/thirdparty/TestTKOToken.sol | 9 + packages/protocol/package.json | 4 +- .../test/L1/TaikoL1.integration.test.ts | 126 ++++++----- packages/protocol/test/L1/TaikoL1.test.ts | 2 +- packages/protocol/test/L1/TkoToken.test.ts | 23 +- .../test/bridge/Bridge.integration.test.ts | 34 ++- packages/protocol/test/test_integration.sh | 2 +- packages/protocol/test/test_tokenomics.sh | 83 ------- .../test/tokenomics/Tokenomics.test.ts | 207 +++++++++++++++++- packages/protocol/test/utils/commit.ts | 5 +- packages/protocol/test/utils/evidence.ts | 11 + packages/protocol/test/utils/propose.ts | 13 +- packages/protocol/test/utils/proposer.ts | 63 ++++++ packages/protocol/test/utils/prove.ts | 58 +++++ packages/protocol/test/utils/prover.ts | 12 + packages/protocol/test/utils/rpc.ts | 14 +- packages/protocol/test/utils/sleep.ts | 7 + packages/protocol/test/utils/taikoL1.ts | 24 +- packages/protocol/test/utils/tkoToken.ts | 28 +++ packages/protocol/test/utils/transaction.ts | 11 + 25 files changed, 550 insertions(+), 204 deletions(-) create mode 100644 packages/protocol/contracts/test/thirdparty/TestTKOToken.sol delete mode 100755 packages/protocol/test/test_tokenomics.sh create mode 100644 packages/protocol/test/utils/evidence.ts create mode 100644 packages/protocol/test/utils/proposer.ts create mode 100644 packages/protocol/test/utils/prove.ts create mode 100644 packages/protocol/test/utils/prover.ts create mode 100644 packages/protocol/test/utils/sleep.ts create mode 100644 packages/protocol/test/utils/tkoToken.ts create mode 100644 packages/protocol/test/utils/transaction.ts diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 795a3380b9..8385a39f6c 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -41,6 +41,7 @@ library TaikoData { uint64 boostrapDiscountHalvingPeriod; uint64 initialUncleDelay; bool enableTokenomics; + bool enablePublicInputsCheck; } struct BlockMetadata { diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 620697c88b..664af9fc68 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -76,7 +76,10 @@ contract TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { * @param l1Hash The latest L1 block hash when this block was proposed. */ function anchor(uint256 l1Height, bytes32 l1Hash) external { - _checkPublicInputs(); + TaikoData.Config memory config = getConfig(); + if (config.enablePublicInputsCheck) { + _checkPublicInputs(); + } l1Hashes[l1Height] = l1Hash; latestSyncedHeader = l1Hash; @@ -159,6 +162,7 @@ contract TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { uint256 number = block.number; uint256 chainId = block.chainid; + // from 2 to 256, while nnumber is greater than that number for (uint256 i = 2; i <= 256 && number >= i; ++i) { ancestors[(number - i) % 255] = blockhash(number - i); } diff --git a/packages/protocol/contracts/libs/LibSharedConfig.sol b/packages/protocol/contracts/libs/LibSharedConfig.sol index 80e83fcf61..5473327a71 100644 --- a/packages/protocol/contracts/libs/LibSharedConfig.sol +++ b/packages/protocol/contracts/libs/LibSharedConfig.sol @@ -43,7 +43,8 @@ library LibSharedConfig { proofTimeCap: 60 minutes, boostrapDiscountHalvingPeriod: 180 days, initialUncleDelay: 60 minutes, - enableTokenomics: false + enableTokenomics: false, + enablePublicInputsCheck: true }); } } diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1.sol b/packages/protocol/contracts/test/L1/TestTaikoL1.sol index 2a4e431779..90b84e9e20 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1.sol @@ -50,6 +50,7 @@ contract TestTaikoL1 is TaikoL1, IProofVerifier { config.boostrapDiscountHalvingPeriod = 180 days; config.initialUncleDelay = 1 minutes; config.enableTokenomics = false; + config.enablePublicInputsCheck = true; } function verifyZKP( diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol index 3a12d19c13..f4d49ea38c 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol @@ -20,7 +20,7 @@ contract TestTaikoL1EnableTokenomics is TaikoL1, IProofVerifier { { config.chainId = 167; // up to 2048 pending blocks - config.maxNumBlocks = 4; + config.maxNumBlocks = 2048; config.blockHashHistory = 3; // This number is calculated from maxNumBlocks to make // the 'the maximum value of the multiplier' close to 20.0 @@ -46,10 +46,11 @@ contract TestTaikoL1EnableTokenomics is TaikoL1, IProofVerifier { config.feeGracePeriodPctg = 125; // 125% config.feeMaxPeriodPctg = 375; // 375% config.blockTimeCap = 48 seconds; - config.proofTimeCap = 60 minutes; + config.proofTimeCap = 5 seconds; config.boostrapDiscountHalvingPeriod = 180 days; - config.initialUncleDelay = 1 minutes; + config.initialUncleDelay = 5 seconds; config.enableTokenomics = true; + config.enablePublicInputsCheck = false; } function verifyZKP( diff --git a/packages/protocol/contracts/test/thirdparty/TestTKOToken.sol b/packages/protocol/contracts/test/thirdparty/TestTKOToken.sol new file mode 100644 index 0000000000..c7329c20cf --- /dev/null +++ b/packages/protocol/contracts/test/thirdparty/TestTKOToken.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; +import "../../L1/TkoToken.sol"; + +contract TestTkoToken is TkoToken { + function mintAnyone(address account, uint256 amount) public { + _mint(account, amount); + } +} diff --git a/packages/protocol/package.json b/packages/protocol/package.json index e35f390e78..34776163c6 100644 --- a/packages/protocol/package.json +++ b/packages/protocol/package.json @@ -15,8 +15,8 @@ "test:coverage": "pnpm coverage", "generate:genesis": "ts-node ./utils/generate_genesis/main.ts", "test:genesis": "./test/genesis/generate_genesis.test.sh", - "test:integration": "./test/test_integration.sh", - "test:tokenomics": "./test/test_tokenomics.sh", + "test:integration": "TEST_TYPE=integration ./test/test_integration.sh", + "test:tokenomics": "TEST_TYPE=tokenomics ./test/test_integration.sh", "deploy:hardhat": "LOG_LEVEL=debug pnpm hardhat deploy_L1 --network hardhat --dao-vault 0xdf08f82de32b8d460adbe8d72043e3a7e25a3b39 --team-vault 0xdf08f82de32b8d460adbe8d72043e3a7e25a3b39 --l2-genesis-block-hash 0xee1950562d42f0da28bd4550d88886bc90894c77c9c9eaefef775d4c8223f259 --bridge-funder-private-key ddbf12f72c946bb1e6de5eaf580c51db51828ba198d9b0dba9c7d48ec748dc04 --bridge-fund 0xff --confirmations 1", "lint-staged": "lint-staged --allow-empty" }, diff --git a/packages/protocol/test/L1/TaikoL1.integration.test.ts b/packages/protocol/test/L1/TaikoL1.integration.test.ts index 2f9f1dec63..a4fd8f27bb 100644 --- a/packages/protocol/test/L1/TaikoL1.integration.test.ts +++ b/packages/protocol/test/L1/TaikoL1.integration.test.ts @@ -5,7 +5,7 @@ import { TaikoL1, TaikoL2 } from "../../typechain"; import { BlockMetadata } from "../utils/block_metadata"; import { commitBlock, generateCommitHash } from "../utils/commit"; import { buildProposeBlockInputs, proposeBlock } from "../utils/propose"; -import { deployTaikoL1 } from "../utils/taikoL1"; +import { defaultFeeBase, deployTaikoL1 } from "../utils/taikoL1"; import { deployTaikoL2 } from "../utils/taikoL2"; describe("integration:TaikoL1", function () { @@ -29,7 +29,7 @@ describe("integration:TaikoL1", function () { const genesisHash = taikoL2.deployTransaction.blockHash as string; - taikoL1 = await deployTaikoL1(genesisHash); + ({ taikoL1 } = await deployTaikoL1(genesisHash, false, defaultFeeBase)); }); describe("isCommitValid()", async function () { @@ -57,48 +57,64 @@ describe("integration:TaikoL1", function () { describe("commitBlock() -> proposeBlock() integration", async function () { it("should fail if a proposed block's placeholder field values are not default", async function () { const block = await l2Provider.getBlock("latest"); - const { tx, commit } = await commitBlock(taikoL1, block); + const commitSlot = 0; + const { tx, commit } = await commitBlock( + taikoL1, + block, + commitSlot + ); - await expect( - proposeBlock( - taikoL1, - block, - commit.txListHash, - tx.blockNumber as number, - 1, - block.gasLimit - ) - ).to.be.revertedWith("L1:placeholder"); + const receipt = await tx.wait(1); + + const meta: BlockMetadata = { + id: 1, + l1Height: 0, + l1Hash: ethers.constants.HashZero, + beneficiary: block.miner, + txListHash: commit.txListHash, + mixHash: ethers.constants.HashZero, + extraData: block.extraData, + gasLimit: block.gasLimit, + timestamp: 0, + commitSlot: commitSlot, + commitHeight: receipt.blockNumber as number, + }; + + const inputs = buildProposeBlockInputs(block, meta); + + await expect(taikoL1.proposeBlock(inputs)).to.be.revertedWith( + "L1:placeholder" + ); }); it("should revert with invalid gasLimit", async function () { const block = await l2Provider.getBlock("latest"); - const { tx, commit } = await commitBlock(taikoL1, block); - // blockMetadata is inputs[0], txListBytes = inputs[1] const config = await taikoL1.getConfig(); const gasLimit = config[7]; - const proposeReceipt = await proposeBlock( - taikoL1, - block, - commit.txListHash, - tx.blockNumber as number, - 0, - block.gasLimit - ); - expect(proposeReceipt.status).to.be.eq(1); + const { tx, commit } = await commitBlock(taikoL1, block); - await expect( - proposeBlock( - taikoL1, - block, - commit.txListHash, - tx.blockNumber as number, - 0, - gasLimit.add(1) - ) - ).to.be.revertedWith("L1:gasLimit"); + const receipt = await tx.wait(1); + const meta: BlockMetadata = { + id: 0, + l1Height: 0, + l1Hash: ethers.constants.HashZero, + beneficiary: block.miner, + txListHash: commit.txListHash, + mixHash: ethers.constants.HashZero, + extraData: block.extraData, + gasLimit: gasLimit.add(1), + timestamp: 0, + commitSlot: 0, + commitHeight: receipt.blockNumber as number, + }; + + const inputs = buildProposeBlockInputs(block, meta); + + await expect(taikoL1.proposeBlock(inputs)).to.be.revertedWith( + "L1:gasLimit" + ); }); it("should revert with invalid extraData", async function () { @@ -115,7 +131,7 @@ describe("integration:TaikoL1", function () { extraData: ethers.utils.hexlify(ethers.utils.randomBytes(33)), // invalid extradata gasLimit: block.gasLimit, timestamp: 0, - commitSlot: 1, + commitSlot: 0, commitHeight: tx.blockNumber as number, }; @@ -128,16 +144,25 @@ describe("integration:TaikoL1", function () { it("should commit and be able to propose", async function () { const block = await l2Provider.getBlock("latest"); - const { tx, commit } = await commitBlock(taikoL1, block); + const commitSlot = 0; + const { tx, commit } = await commitBlock( + taikoL1, + block, + commitSlot + ); - await proposeBlock( + const { commitConfirmations } = await taikoL1.getConfig(); + + await tx.wait(commitConfirmations.toNumber()); + const receipt = await proposeBlock( taikoL1, block, commit.txListHash, tx.blockNumber as number, - 0, - block.gasLimit + block.gasLimit, + commitSlot ); + expect(receipt.status).to.be.eq(1); const stateVariables = await taikoL1.getStateVariables(); const nextBlockId = stateVariables[4]; @@ -152,14 +177,6 @@ describe("integration:TaikoL1", function () { ethers.constants.AddressZero ); expect(proposedBlock.proposedAt).not.to.be.eq(BigNumber.from(0)); - - const isCommitValid = await taikoL1.isCommitValid( - 1, - tx.blockNumber as number, - commit.hash - ); - - expect(isCommitValid).to.be.eq(true); }); it("should commit and be able to propose for all available slots, then revert when all slots are taken", async function () { @@ -168,15 +185,15 @@ describe("integration:TaikoL1", function () { // expect each one to be successful. for (let i = 0; i < maxNumBlocks.toNumber() - 1; i++) { const block = await l2Provider.getBlock("latest"); - const { tx, commit } = await commitBlock(taikoL1, block); + const { tx, commit } = await commitBlock(taikoL1, block, i); const receipt = await proposeBlock( taikoL1, block, commit.txListHash, tx.blockNumber as number, - 0, - block.gasLimit + block.gasLimit, + i ); expect(receipt.status).to.be.eq(1); @@ -196,14 +213,6 @@ describe("integration:TaikoL1", function () { expect(proposedBlock.proposedAt).not.to.be.eq( BigNumber.from(0) ); - - const isCommitValid = await taikoL1.isCommitValid( - 1, - tx.blockNumber as number, - commit.hash - ); - - expect(isCommitValid).to.be.eq(true); } // now expect another proposed block to be invalid since all slots are full and none have @@ -217,7 +226,6 @@ describe("integration:TaikoL1", function () { block, commit.txListHash, tx.blockNumber as number, - 0, block.gasLimit ) ).to.be.revertedWith("L1:tooMany"); diff --git a/packages/protocol/test/L1/TaikoL1.test.ts b/packages/protocol/test/L1/TaikoL1.test.ts index 145cef39f6..d69ad72b12 100644 --- a/packages/protocol/test/L1/TaikoL1.test.ts +++ b/packages/protocol/test/L1/TaikoL1.test.ts @@ -10,7 +10,7 @@ describe("TaikoL1", function () { beforeEach(async function () { genesisHash = randomBytes32(); - taikoL1 = await deployTaikoL1(genesisHash); + ({ taikoL1 } = await deployTaikoL1(genesisHash, false)); }); describe("getLatestSyncedHeader()", async function () { diff --git a/packages/protocol/test/L1/TkoToken.test.ts b/packages/protocol/test/L1/TkoToken.test.ts index 114ebe4753..40bf1ad52a 100644 --- a/packages/protocol/test/L1/TkoToken.test.ts +++ b/packages/protocol/test/L1/TkoToken.test.ts @@ -1,5 +1,4 @@ import { expect } from "chai"; -import { AddressManager, TkoToken } from "../../typechain"; import { ethers } from "hardhat"; import { ADDRESS_RESOLVER_DENIED, @@ -7,12 +6,14 @@ import { ERC20_TRANSFER_AMOUNT_EXCEEDED, } from "../constants/errors"; import { BigNumber } from "ethers"; +import deployTkoToken from "../utils/tkoToken"; +import { TestTkoToken } from "../../typechain/TestTkoToken"; describe("TkoToken", function () { let owner: any; let nonOwner: any; let protoBroker: any; - let token: TkoToken; + let token: TestTkoToken; let amountMinted: BigNumber; before(async function () { @@ -20,23 +21,7 @@ describe("TkoToken", function () { }); beforeEach(async function () { - const addressManager: AddressManager = await ( - await ethers.getContractFactory("AddressManager") - ).deploy(); - await addressManager.init(); - - token = await (await ethers.getContractFactory("TkoToken")) - .connect(owner) - .deploy(); - await token.init(addressManager.address); - - const { chainId } = await ethers.provider.getNetwork(); - - await addressManager.setAddress( - `${chainId}.proto_broker`, - protoBroker.address - ); - + token = await deployTkoToken(owner, protoBroker.address); amountMinted = ethers.utils.parseEther("100"); await token.connect(protoBroker).mint(owner.address, amountMinted); diff --git a/packages/protocol/test/bridge/Bridge.integration.test.ts b/packages/protocol/test/bridge/Bridge.integration.test.ts index 70552a35b1..8c3a361f2b 100644 --- a/packages/protocol/test/bridge/Bridge.integration.test.ts +++ b/packages/protocol/test/bridge/Bridge.integration.test.ts @@ -10,7 +10,7 @@ import { import { deployBridge, sendMessage } from "../utils/bridge"; import { randomBytes32 } from "../utils/bytes"; import { Message } from "../utils/message"; -import { getLatestBlockHeader } from "../utils/rpc"; +import { getBlockHeader } from "../utils/rpc"; import { getSignalProof, getSignalSlot } from "../utils/signal"; describe("integration:Bridge", function () { @@ -160,7 +160,9 @@ describe("integration:Bridge", function () { const key = getSignalSlot(hre, sender, signal); - const { block, blockHeader } = await getLatestBlockHeader(hre); + const { block, blockHeader } = await getBlockHeader( + hre.ethers.provider + ); await headerSync.setSyncedHeader(block.hash); @@ -191,7 +193,9 @@ describe("integration:Bridge", function () { const sender = l1Bridge.address; const key = getSignalSlot(hre, sender, signal); - const { block, blockHeader } = await getLatestBlockHeader(hre); + const { block, blockHeader } = await getBlockHeader( + hre.ethers.provider + ); await headerSync.setSyncedHeader(ethers.constants.HashZero); @@ -221,7 +225,9 @@ describe("integration:Bridge", function () { const key = getSignalSlot(hre, sender, signal); - const { block, blockHeader } = await getLatestBlockHeader(hre); + const { block, blockHeader } = await getBlockHeader( + hre.ethers.provider + ); await headerSync.setSyncedHeader(ethers.constants.HashZero); @@ -262,7 +268,9 @@ describe("integration:Bridge", function () { const key = getSignalSlot(hre, sender, signal); - const { block, blockHeader } = await getLatestBlockHeader(hre); + const { block, blockHeader } = await getBlockHeader( + hre.ethers.provider + ); await headerSync.setSyncedHeader(block.hash); @@ -324,7 +332,9 @@ describe("integration:Bridge", function () { const key = getSignalSlot(hre, sender, signal); - const { block, blockHeader } = await getLatestBlockHeader(hre); + const { block, blockHeader } = await getBlockHeader( + hre.ethers.provider + ); await headerSync.setSyncedHeader(block.hash); @@ -359,7 +369,9 @@ describe("integration:Bridge", function () { const key = getSignalSlot(hre, sender, signal); - const { block, blockHeader } = await getLatestBlockHeader(hre); + const { block, blockHeader } = await getBlockHeader( + hre.ethers.provider + ); await headerSync.setSyncedHeader(block.hash); @@ -434,7 +446,9 @@ describe("integration:Bridge", function () { const key = getSignalSlot(hre, sender, signal); - const { block, blockHeader } = await getLatestBlockHeader(hre); + const { block, blockHeader } = await getBlockHeader( + hre.ethers.provider + ); await headerSync.setSyncedHeader(block.hash); @@ -478,7 +492,9 @@ describe("integration:Bridge", function () { const key = getSignalSlot(hre, sender, signal); - const { block, blockHeader } = await getLatestBlockHeader(hre); + const { block, blockHeader } = await getBlockHeader( + hre.ethers.provider + ); await headerSync.setSyncedHeader(block.hash); diff --git a/packages/protocol/test/test_integration.sh b/packages/protocol/test/test_integration.sh index 485cc8b4d2..5b0d7c7ad7 100755 --- a/packages/protocol/test/test_integration.sh +++ b/packages/protocol/test/test_integration.sh @@ -80,4 +80,4 @@ trap cleanup EXIT INT KILL ERR # Run the tests PRIVATE_KEY=$TEST_ACCOUNT_PRIV_KEY \ - npx hardhat test --network l1_test --grep "^integration" \ No newline at end of file + npx hardhat test --network l1_test --grep "^$TEST_TYPE" \ No newline at end of file diff --git a/packages/protocol/test/test_tokenomics.sh b/packages/protocol/test/test_tokenomics.sh deleted file mode 100755 index 2ccd451863..0000000000 --- a/packages/protocol/test/test_tokenomics.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/bin/bash - -set -eou pipefail - -DIR=$(cd $(dirname ${BASH_SOURCE[0]}); pwd) -TEST_NODE_CONTAINER_NAME_L1="test-ethereum-node-l1" -TEST_NODE_CONTAINER_NAME_L2="test-ethereum-node-l2" -TEST_IMPORT_TEST_ACCOUNT_ETH_JOB_NAME="import-test-account-eth" -TEST_ACCOUNT_ADDRESS="0xdf08f82de32b8d460adbe8d72043e3a7e25a3b39" -TEST_ACCOUNT_PRIV_KEY="2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501200" - -if ! command -v docker &> /dev/null 2>&1; then - echo "ERROR: `docker` command not found" - exit 1 -fi - -if ! docker info > /dev/null 2>&1; then - echo "ERROR: docker daemon isn't running" - exit 1 -fi - -docker rm --force $TEST_NODE_CONTAINER_NAME_L1 \ - $TEST_NODE_CONTAINER_NAME_L2 \ - $TEST_IMPORT_TEST_ACCOUNT_ETH_JOB_NAME &> /dev/null - -# Start a test ethereum node -docker run -d \ - --name $TEST_NODE_CONTAINER_NAME_L1 \ - -p 18545:8545 \ - ethereum/client-go:latest \ - --dev --http --http.addr 0.0.0.0 --http.vhosts "*" \ - --http.api debug,eth,net,web3,txpool,miner - -docker run -d \ - --name $TEST_NODE_CONTAINER_NAME_L2 \ - -p 28545:8545 \ - gcr.io/evmchain/hardhat-node:latest \ - hardhat node --hostname "0.0.0.0" - -function waitTestNode { - echo "Waiting for test node: $1" - # Wait till the test node fully started - RETRIES=30 - i=0 - until curl \ - --silent \ - --fail \ - --noproxy localhost \ - -X POST \ - -H "Content-Type: application/json" \ - -d '{"jsonrpc":"2.0","id":0,"method":"eth_chainId","params":[]}' \ - $1 - do - sleep 1 - if [ $i -eq $RETRIES ]; then - echo 'Timed out waiting for test node' - exit 1 - fi - ((i=i+1)) - done -} - -waitTestNode http://localhost:18545 -waitTestNode http://localhost:28545 - -# Import ETHs from the random pre-allocated developer account to the test account -docker run -d \ - --name $TEST_IMPORT_TEST_ACCOUNT_ETH_JOB_NAME \ - --add-host host.docker.internal:host-gateway \ - ethereum/client-go:latest \ - --exec 'eth.sendTransaction({from: eth.coinbase, to: "'0xdf08f82de32b8d460adbe8d72043e3a7e25a3b39'", value: web3.toWei(1024, "'ether'")})' attach http://host.docker.internal:18545 - -function cleanup { - docker rm --force $TEST_NODE_CONTAINER_NAME_L1 \ - $TEST_NODE_CONTAINER_NAME_L2 \ - $TEST_IMPORT_TEST_ACCOUNT_ETH_JOB_NAME &> /dev/null -} - -trap cleanup EXIT INT KILL ERR - -# Run the tests -PRIVATE_KEY=$TEST_ACCOUNT_PRIV_KEY \ - npx hardhat test --network l1_test --grep "^tokenomics" \ No newline at end of file diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index 9edfcf57c0..7dec8ab9cb 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -1,3 +1,208 @@ +import { expect } from "chai"; +import { ethers } from "ethers"; +import { ethers as hardhatEthers } from "hardhat"; +import { TaikoL1, TaikoL2 } from "../../typechain"; +import { TestTkoToken } from "../../typechain/TestTkoToken"; +import Proposer from "../utils/proposer"; +import sleep from "../utils/sleep"; +import { defaultFeeBase, deployTaikoL1 } from "../utils/taikoL1"; +import { deployTaikoL2 } from "../utils/taikoL2"; +import deployTkoToken from "../utils/tkoToken"; +import sendTransaction from "../utils/transaction"; + describe("tokenomics", function () { - it("runs", async function () {}); + let taikoL1: TaikoL1; + let taikoL2: TaikoL2; + let l1Provider: ethers.providers.JsonRpcProvider; + let l2Provider: ethers.providers.JsonRpcProvider; + let l1Signer: any; + let l2Signer: any; + let proposerSigner: any; + let proverSigner: any; + let genesisHeight: number; + let genesisHash: string; + let tkoTokenL1: TestTkoToken; + + beforeEach(async () => { + l1Provider = new ethers.providers.JsonRpcProvider( + "http://localhost:18545" + ); + + l1Provider.pollingInterval = 100; + + const signers = await hardhatEthers.getSigners(); + l1Signer = signers[0]; + + l2Provider = new ethers.providers.JsonRpcProvider( + "http://localhost:28545" + ); + + l2Signer = await l2Provider.getSigner( + ( + await l2Provider.listAccounts() + )[0] + ); + + taikoL2 = await deployTaikoL2(l2Signer); + + genesisHash = taikoL2.deployTransaction.blockHash as string; + genesisHeight = taikoL2.deployTransaction.blockNumber as number; + + const { taikoL1: tL1, addressManager } = await deployTaikoL1( + genesisHash, + true, + defaultFeeBase + ); + + taikoL1 = tL1; + + const { chainId } = await l1Provider.getNetwork(); + + proposerSigner = ethers.Wallet.createRandom().connect(l1Provider); + proverSigner = ethers.Wallet.createRandom().connect(l1Provider); + await l1Signer.sendTransaction({ + to: await proposerSigner.getAddress(), + value: ethers.utils.parseEther("1"), + }); + + await l1Signer.sendTransaction({ + to: await proverSigner.getAddress(), + value: ethers.utils.parseEther("1"), + }); + + const balance = await proposerSigner.getBalance(); + expect(balance).to.be.eq(ethers.utils.parseEther("1")); + + tkoTokenL1 = await deployTkoToken(l1Signer, taikoL1.address); + + await addressManager.setAddress( + `${chainId}.tko_token`, + tkoTokenL1.address + ); + + await tkoTokenL1 + .connect(l1Signer) + .mintAnyone( + await proposerSigner.getAddress(), + ethers.utils.parseEther("100") + ); + + expect( + await tkoTokenL1.balanceOf(await proposerSigner.getAddress()) + ).to.be.eq(ethers.utils.parseEther("100")); + + // set up interval mining so we always get new blocks + await l2Provider.send("evm_setIntervalMining", [2000]); + + // send transactions to L1 so we always get new blocks + setInterval(async () => await sendTransaction(l1Signer), 1 * 500); + }); + + it("tests tokenomics, propose blocks, blockFee should increase", async function () { + const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); + const blockIdsToNumber: any = {}; + + const proposer = new Proposer( + taikoL1.connect(proposerSigner), + l2Provider, + commitConfirmations.toNumber(), + maxNumBlocks.toNumber(), + 0 + ); + + // const initialBlockFee = await taikoL1.getBlockFee(); + + // expect(initialBlockFee).not.to.be.eq(0); + + l2Provider.on("block", async (blockNumber) => { + if (blockNumber <= genesisHeight) return; + try { + const block = await l2Provider.getBlock(blockNumber); + const receipt = await proposer.commitThenProposeBlock(block); + expect(receipt.status).to.be.eq(1); + const proposedEvent = (receipt.events as any[]).find( + (e) => e.event === "BlockProposed" + ); + + const { id } = (proposedEvent as any).args; + console.log( + "-----------PROPOSED---------------", + block.number, + id + ); + blockIdsToNumber[id.toString()] = block.number; + } catch (e) { + console.error(e); + expect(true).to.be.eq(false); + } + }); + + await sleep(30 * 1000); + + // const newBlockFee = await taikoL1.getBlockFee(); + // expect(newBlockFee.gt(initialBlockFee)).to.be.eq(true); + }); + + // it("tests tokenomics, propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { + // const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); + // const blockIdsToNumber: any = {}; + + // const proposer = new Proposer( + // taikoL1.connect(proposerSigner), + // l2Provider, + // commitConfirmations.toNumber(), + // maxNumBlocks.toNumber(), + // 0 + // ); + + // const prover: Prover = new Prover(taikoL1.connect(proverSigner)); + + // l2Provider.on("block", async (blockNumber) => { + // if (blockNumber <= genesisHeight) return; + // try { + // console.log("new block", blockNumber); + // const block = await l2Provider.getBlock(blockNumber); + // const receipt = await proposer.commitThenProposeBlock(block); + // expect(receipt.status).to.be.eq(1); + // const proposedEvent = (receipt.events as any[]).find( + // (e) => e.event === "BlockProposed" + // ); + + // const { id } = (proposedEvent as any).args; + // console.log( + // "-----------PROPOSED---------------", + // block.number, + // id + // ); + // blockIdsToNumber[id.toString()] = block.number; + // } catch (e) { + // console.error(e); + // expect(true).to.be.eq(false); + // } + // }); + + // taikoL1.on( + // "BlockProposed", + // async (id: BigNumber, meta: BlockMetadata) => { + // console.log("proving block: id", id.toString()); + // try { + // await proveBlock( + // taikoL1, + // taikoL2, + // l1Provider, + // l2Provider, + // await proverSigner.getAddress(), + // id.toNumber(), + // blockIdsToNumber[id.toString()], + // meta + // ); + // } catch (e) { + // console.error(e); + // throw e; + // } + // } + // ); + + // await sleep(60 * 1000); + // }); }); diff --git a/packages/protocol/test/utils/commit.ts b/packages/protocol/test/utils/commit.ts index 1f99bb2ca4..f22f366c0c 100644 --- a/packages/protocol/test/utils/commit.ts +++ b/packages/protocol/test/utils/commit.ts @@ -18,13 +18,14 @@ const generateCommitHash = ( const commitBlock = async ( taikoL1: TaikoL1, - block: ethers.providers.Block + block: ethers.providers.Block, + commitSlot: number = 0 ): Promise<{ tx: ethers.ContractTransaction; commit: { hash: string; txListHash: string }; }> => { const commit = generateCommitHash(block); - const tx = await taikoL1.commitBlock(1, commit.hash); + const tx = await taikoL1.commitBlock(commitSlot, commit.hash); return { tx, commit }; }; diff --git a/packages/protocol/test/utils/evidence.ts b/packages/protocol/test/utils/evidence.ts new file mode 100644 index 0000000000..58e39c1641 --- /dev/null +++ b/packages/protocol/test/utils/evidence.ts @@ -0,0 +1,11 @@ +import { BlockMetadata } from "./block_metadata"; +import { BlockHeader } from "./rpc"; + +type Evidence = { + meta: BlockMetadata; + header: BlockHeader; + prover: string; + proofs: string[]; +}; + +export default Evidence; diff --git a/packages/protocol/test/utils/propose.ts b/packages/protocol/test/utils/propose.ts index b88f617e86..7d961c0ae5 100644 --- a/packages/protocol/test/utils/propose.ts +++ b/packages/protocol/test/utils/propose.ts @@ -10,7 +10,6 @@ const buildProposeBlockInputs = ( ) => { const inputs = []; const blockMetadataBytes = encodeBlockMetadata(meta); - inputs[0] = blockMetadataBytes; inputs[1] = RLP.encode(block.transactions); return inputs; @@ -21,11 +20,11 @@ const proposeBlock = async ( block: ethers.providers.Block, txListHash: string, commitHeight: number, - id: number, - gasLimit: BigNumber + gasLimit: BigNumber, + commitSlot: number = 0 ) => { const meta: BlockMetadata = { - id: id, + id: 0, l1Height: 0, l1Hash: ethers.constants.HashZero, beneficiary: block.miner, @@ -34,14 +33,16 @@ const proposeBlock = async ( extraData: block.extraData, gasLimit: gasLimit, timestamp: 0, - commitSlot: 1, + commitSlot: commitSlot, commitHeight: commitHeight, }; const inputs = buildProposeBlockInputs(block, meta); const tx = await taikoL1.proposeBlock(inputs); - const receipt = await tx.wait(); + console.log("Proposed block", tx.hash); + const receipt = await tx.wait(1); return receipt; }; + export { buildProposeBlockInputs, proposeBlock }; diff --git a/packages/protocol/test/utils/proposer.ts b/packages/protocol/test/utils/proposer.ts new file mode 100644 index 0000000000..8baca8abc8 --- /dev/null +++ b/packages/protocol/test/utils/proposer.ts @@ -0,0 +1,63 @@ +import { ethers } from "ethers"; +import { TaikoL1 } from "../../typechain"; +import { commitBlock } from "./commit"; +import { proposeBlock } from "./propose"; +import sleep from "./sleep"; + +class Proposer { + private readonly taikoL1: TaikoL1; + private readonly l2Provider: ethers.providers.JsonRpcProvider; + private readonly commitConfirms: number; + private readonly maxNumBlocks: number; + private nextCommitSlot: number; + + private proposingMutex: boolean = false; + + constructor( + taikoL1: TaikoL1, + l2Provider: ethers.providers.JsonRpcProvider, + commitConfirms: number, + maxNumBlocks: number, + initialCommitSlot: number + ) { + this.taikoL1 = taikoL1; + this.l2Provider = l2Provider; + this.commitConfirms = commitConfirms; + this.maxNumBlocks = maxNumBlocks; + this.nextCommitSlot = initialCommitSlot; + } + + async commitThenProposeBlock(block?: ethers.providers.Block) { + while (this.proposingMutex) { + await sleep(100); + } + this.proposingMutex = true; + if (!block) block = await this.l2Provider.getBlock("latest"); + const commitSlot = this.nextCommitSlot; + this.nextCommitSlot++; + console.log("commiting ", block.number, "with commit slot", commitSlot); + const { tx, commit } = await commitBlock( + this.taikoL1, + block, + commitSlot + ); + const commitReceipt = await tx.wait(this.commitConfirms ?? 1); + + console.log("proposing", block.number, "with commit slot", commitSlot); + + const receipt = await proposeBlock( + this.taikoL1, + block, + commit.txListHash, + commitReceipt.blockNumber as number, + block.gasLimit, + commitSlot + ); + + this.proposingMutex = false; + + return receipt; + } +} + +export default Proposer; diff --git a/packages/protocol/test/utils/prove.ts b/packages/protocol/test/utils/prove.ts new file mode 100644 index 0000000000..1aa25be291 --- /dev/null +++ b/packages/protocol/test/utils/prove.ts @@ -0,0 +1,58 @@ +import { ethers } from "ethers"; +import { TaikoL1, TaikoL2 } from "../../typechain"; +import { BlockMetadata } from "./block_metadata"; +import Evidence from "./evidence"; +import { BlockHeader, getBlockHeader } from "./rpc"; + +const buildProveBlockInputs = ( + meta: BlockMetadata, + header: BlockHeader, + prover: string, + anchorTx: any, + anchorReceipt: any +) => { + const inputs = []; + const evidence: Evidence = { + meta: meta, + header: header, + prover: prover, + proofs: [], + }; + + inputs[0] = evidence; + inputs[1] = anchorTx; + inputs[2] = anchorReceipt; + return inputs; +}; + +const proveBlock = async ( + taikoL1: TaikoL1, + taikoL2: TaikoL2, + l1Provider: ethers.providers.JsonRpcProvider, + l2Provider: ethers.providers.JsonRpcProvider, + proverAddress: string, + blockId: number, + blockNumber: number, + meta: BlockMetadata +) => { + const header = await getBlockHeader(l2Provider, blockNumber); + const anchorTx = await taikoL2.anchor(meta.l1Height, meta.l1Hash); + const anchorReceipt = await anchorTx.wait(1); + const [encodedReceipt] = await l2Provider.send("debug_getRawReceipts", [ + anchorReceipt.blockHash, + ]); + + const inputs = buildProveBlockInputs( + meta, + header.blockHeader, + proverAddress, + anchorTx, + encodedReceipt + ); + const tx = await taikoL1.proveBlock(blockId, inputs); + console.log("Proved block", tx.hash); + const receipt = await tx.wait(1); + return receipt; +}; + +export { buildProveBlockInputs, proveBlock }; diff --git a/packages/protocol/test/utils/prover.ts b/packages/protocol/test/utils/prover.ts new file mode 100644 index 0000000000..c5f2a03723 --- /dev/null +++ b/packages/protocol/test/utils/prover.ts @@ -0,0 +1,12 @@ +import { TaikoL1 } from "../../typechain"; + +class Prover { + private readonly taikoL1: TaikoL1; + constructor(taikoL1: TaikoL1) { + this.taikoL1 = taikoL1; + } + + async prove() {} +} + +export default Prover; diff --git a/packages/protocol/test/utils/rpc.ts b/packages/protocol/test/utils/rpc.ts index 77473084ae..1c3481920c 100644 --- a/packages/protocol/test/utils/rpc.ts +++ b/packages/protocol/test/utils/rpc.ts @@ -1,3 +1,5 @@ +import { BigNumber } from "ethers"; + type StorageEntry = { key: string; value: string; @@ -56,11 +58,11 @@ type BlockHeader = { baseFeePerGas: number; }; -async function getLatestBlockHeader(hre: any) { - const block: Block = await hre.ethers.provider.send( - "eth_getBlockByNumber", - ["latest", false] - ); +async function getBlockHeader(provider: any, blockNumber?: number) { + const block: Block = await provider.send("eth_getBlockByNumber", [ + blockNumber ? BigNumber.from(blockNumber).toHexString() : "latest", + false, + ]); const logsBloom = block.logsBloom.toString().substring(2); @@ -91,5 +93,5 @@ export { BlockHeader, StorageEntry, EthGetProofResponse, - getLatestBlockHeader, + getBlockHeader, }; diff --git a/packages/protocol/test/utils/sleep.ts b/packages/protocol/test/utils/sleep.ts new file mode 100644 index 0000000000..268c312630 --- /dev/null +++ b/packages/protocol/test/utils/sleep.ts @@ -0,0 +1,7 @@ +function sleep(ms: number) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} + +export default sleep; diff --git a/packages/protocol/test/utils/taikoL1.ts b/packages/protocol/test/utils/taikoL1.ts index 185a66b866..23e22263ec 100644 --- a/packages/protocol/test/utils/taikoL1.ts +++ b/packages/protocol/test/utils/taikoL1.ts @@ -1,13 +1,14 @@ import { BigNumber } from "ethers"; import { ethers } from "hardhat"; -import { TestTaikoL1 } from "../../typechain"; +import { AddressManager, TaikoL1 } from "../../typechain"; const defaultFeeBase = BigNumber.from(10).pow(18); async function deployTaikoL1( genesisHash: string, + enableTokenomics: boolean, feeBase?: BigNumber -): Promise { +): Promise<{ taikoL1: TaikoL1; addressManager: AddressManager }> { const addressManager = await ( await ethers.getContractFactory("AddressManager") ).deploy(); @@ -39,13 +40,16 @@ async function deployTaikoL1( ).deploy(); const taikoL1 = await ( - await ethers.getContractFactory("TestTaikoL1", { - libraries: { - LibVerifying: libVerifying.address, - LibProposing: libProposing.address, - LibProving: libProving.address, - }, - }) + await ethers.getContractFactory( + enableTokenomics ? "TestTaikoL1EnableTokenomics" : "TestTaikoL1", + { + libraries: { + LibVerifying: libVerifying.address, + LibProposing: libProposing.address, + LibProving: libProving.address, + }, + } + ) ).deploy(); await taikoL1.init( @@ -54,7 +58,7 @@ async function deployTaikoL1( feeBase ?? defaultFeeBase ); - return taikoL1; + return { taikoL1: taikoL1 as TaikoL1, addressManager: addressManager }; } export { deployTaikoL1, defaultFeeBase }; diff --git a/packages/protocol/test/utils/tkoToken.ts b/packages/protocol/test/utils/tkoToken.ts new file mode 100644 index 0000000000..f507c657f1 --- /dev/null +++ b/packages/protocol/test/utils/tkoToken.ts @@ -0,0 +1,28 @@ +import { ethers } from "ethers"; +import { ethers as hardhatEthers } from "hardhat"; +import { AddressManager } from "../../typechain"; + +const deployTkoToken = async (signer: ethers.Signer, protoBroker: string) => { + const addressManager: AddressManager = await ( + await hardhatEthers.getContractFactory("AddressManager") + ) + .connect(signer) + .deploy(); + await addressManager.init(); + + const token = await (await hardhatEthers.getContractFactory("TestTkoToken")) + .connect(signer) + .deploy(); + await token.init(addressManager.address); + + const network = await signer.provider?.getNetwork(); + + await addressManager.setAddress( + `${network?.chainId}.proto_broker`, + protoBroker + ); + + return token; +}; + +export default deployTkoToken; diff --git a/packages/protocol/test/utils/transaction.ts b/packages/protocol/test/utils/transaction.ts new file mode 100644 index 0000000000..ea266f223f --- /dev/null +++ b/packages/protocol/test/utils/transaction.ts @@ -0,0 +1,11 @@ +import { BigNumber } from "ethers"; +import { ethers } from "hardhat"; + +const sendTransaction = async (signer: any) => { + signer.sendTransaction({ + to: ethers.constants.AddressZero, + value: BigNumber.from(1), + }); +}; + +export default sendTransaction; From f9800937d3b86a7aa6ea98b7e9d627381c24971c Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 12 Jan 2023 15:19:44 -0800 Subject: [PATCH 14/39] add test assertions on block prosal for reward and fees --- .../contracts/L1/libs/LibProposing.sol | 2 +- .../test/L1/TestTaikoL1EnableTokenomics.sol | 4 +- .../test/tokenomics/Tokenomics.test.ts | 56 +++++++++++++++---- packages/protocol/test/utils/prove.ts | 20 ++++--- 4 files changed, 61 insertions(+), 21 deletions(-) diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 58879b3d5e..344433d636 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -167,7 +167,7 @@ library LibProposing { isProposal: true, feeBase: newFeeBase }); - fee = LibUtils.getBootstrapDiscountedFee(state, config, fee); + //fee = LibUtils.getBootstrapDiscountedFee(state, config, fee); deposit = (fee * config.proposerDepositPctg) / 100; } diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol index f4d49ea38c..03410099a7 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol @@ -47,8 +47,8 @@ contract TestTaikoL1EnableTokenomics is TaikoL1, IProofVerifier { config.feeMaxPeriodPctg = 375; // 375% config.blockTimeCap = 48 seconds; config.proofTimeCap = 5 seconds; - config.boostrapDiscountHalvingPeriod = 180 days; - config.initialUncleDelay = 5 seconds; + config.boostrapDiscountHalvingPeriod = 1 seconds; + config.initialUncleDelay = 1 seconds; config.enableTokenomics = true; config.enablePublicInputsCheck = false; } diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index 7dec8ab9cb..3454de026c 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -1,5 +1,5 @@ import { expect } from "chai"; -import { ethers } from "ethers"; +import { BigNumber, ethers } from "ethers"; import { ethers as hardhatEthers } from "hardhat"; import { TaikoL1, TaikoL2 } from "../../typechain"; import { TestTkoToken } from "../../typechain/TestTkoToken"; @@ -98,10 +98,11 @@ describe("tokenomics", function () { setInterval(async () => await sendTransaction(l1Signer), 1 * 500); }); - it("tests tokenomics, propose blocks, blockFee should increase", async function () { + it("proposes blocks, blockFee should increase, proposer's balance for TKOToken should decrease as it pays proposer fee, proofReward should increase since slots are growing and no proofs have been submitted", async function () { const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); const blockIdsToNumber: any = {}; + // set up a proposer to continually propose new blocks const proposer = new Proposer( taikoL1.connect(proposerSigner), l2Provider, @@ -110,9 +111,18 @@ describe("tokenomics", function () { 0 ); - // const initialBlockFee = await taikoL1.getBlockFee(); + // get the initiaal tkoBalance, which should decrease every block proposal + let lastProposerTkoBalance = await tkoTokenL1.balanceOf( + await proposerSigner.getAddress() + ); + + // do the same for the blockFee, which should increase every block proposal + // with proofs not being submitted. + let lastBlockFee = await taikoL1.getBlockFee(); + + expect(lastBlockFee).not.to.be.eq(0); - // expect(initialBlockFee).not.to.be.eq(0); + let lastProofReward = BigNumber.from(0); l2Provider.on("block", async (blockNumber) => { if (blockNumber <= genesisHeight) return; @@ -124,23 +134,49 @@ describe("tokenomics", function () { (e) => e.event === "BlockProposed" ); - const { id } = (proposedEvent as any).args; + const { id, meta } = (proposedEvent as any).args; console.log( "-----------PROPOSED---------------", block.number, id ); blockIdsToNumber[id.toString()] = block.number; + + const proofReward = await taikoL1.getProofReward( + new Date().getMilliseconds(), + meta.timestamp + ); + // proofReward should grow every time since slots are increasing + expect(proofReward.gt(lastProofReward)).to.be.eq(true); + // set lastProofReward equal to this once, for further comparison + // on next block proposal. + lastProofReward = proofReward; + + // get the balance of the tkoToken for proposer, and make sure it decreased + // ie: they paid the block proposal fee. + const newProposerTkoBalance = await tkoTokenL1.balanceOf( + await proposerSigner.getAddress() + ); + + expect( + newProposerTkoBalance.lt(lastProposerTkoBalance) + ).to.be.eq(true); + + lastProposerTkoBalance = newProposerTkoBalance; + + // after all proposing the block fee should be greater + // than it originally was. + const newBlockFee = await taikoL1.getBlockFee(); + expect(newBlockFee.gt(lastBlockFee)).to.be.eq(true); + + lastBlockFee = newBlockFee; } catch (e) { console.error(e); expect(true).to.be.eq(false); } }); - await sleep(30 * 1000); - - // const newBlockFee = await taikoL1.getBlockFee(); - // expect(newBlockFee.gt(initialBlockFee)).to.be.eq(true); + await sleep(20 * 1000); }); // it("tests tokenomics, propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { @@ -155,7 +191,7 @@ describe("tokenomics", function () { // 0 // ); - // const prover: Prover = new Prover(taikoL1.connect(proverSigner)); + // // const prover: Prover = new Prover(taikoL1.connect(proverSigner)); // l2Provider.on("block", async (blockNumber) => { // if (blockNumber <= genesisHeight) return; diff --git a/packages/protocol/test/utils/prove.ts b/packages/protocol/test/utils/prove.ts index 1aa25be291..0bdc2294ff 100644 --- a/packages/protocol/test/utils/prove.ts +++ b/packages/protocol/test/utils/prove.ts @@ -1,4 +1,5 @@ import { ethers } from "ethers"; +import RLP from "rlp"; import { TaikoL1, TaikoL2 } from "../../typechain"; import { BlockMetadata } from "./block_metadata"; import Evidence from "./evidence"; @@ -8,7 +9,7 @@ const buildProveBlockInputs = ( meta: BlockMetadata, header: BlockHeader, prover: string, - anchorTx: any, + anchorTx: Uint8Array, anchorReceipt: any ) => { const inputs = []; @@ -16,7 +17,7 @@ const buildProveBlockInputs = ( meta: meta, header: header, prover: prover, - proofs: [], + proofs: [], // TODO }; inputs[0] = evidence; @@ -37,20 +38,23 @@ const proveBlock = async ( ) => { const header = await getBlockHeader(l2Provider, blockNumber); const anchorTx = await taikoL2.anchor(meta.l1Height, meta.l1Hash); + const anchorTxRLPEncoded = await RLP.encode( + ethers.utils.serializeTransaction(anchorTx) + ); const anchorReceipt = await anchorTx.wait(1); - const [encodedReceipt] = await l2Provider.send("debug_getRawReceipts", [ - anchorReceipt.blockHash, - ]); + const anchorReceiptRLPEncoded = RLP.encode( + ethers.utils.serializeTransaction(anchorReceipt) + ); const inputs = buildProveBlockInputs( meta, header.blockHeader, proverAddress, - anchorTx, - encodedReceipt + anchorTxRLPEncoded, + anchorReceiptRLPEncoded ); const tx = await taikoL1.proveBlock(blockId, inputs); - console.log("Proved block", tx.hash); + console.log("Proved block tx", tx.hash); const receipt = await tx.wait(1); return receipt; }; From dbeb9e5e5ce8e5cd8993cb7fb79f5da687b68db1 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 12 Jan 2023 15:25:14 -0800 Subject: [PATCH 15/39] expect doesnt work in the event listener --- packages/protocol/test/tokenomics/Tokenomics.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index 3454de026c..754213880f 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -124,6 +124,7 @@ describe("tokenomics", function () { let lastProofReward = BigNumber.from(0); + let hasFailedAssertions: boolean = false; l2Provider.on("block", async (blockNumber) => { if (blockNumber <= genesisHeight) return; try { @@ -171,12 +172,14 @@ describe("tokenomics", function () { lastBlockFee = newBlockFee; } catch (e) { + hasFailedAssertions = true; console.error(e); - expect(true).to.be.eq(false); + throw e; } }); await sleep(20 * 1000); + expect(hasFailedAssertions).to.be.eq(false); }); // it("tests tokenomics, propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { From c9891903fe40383ca55852680ab02c991dc5655f Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 12 Jan 2023 15:59:57 -0800 Subject: [PATCH 16/39] refactor to an onNewL2Block --- .../test/tokenomics/Tokenomics.test.ts | 171 +++++++++++------- packages/protocol/test/utils/seed.ts | 21 +++ 2 files changed, 122 insertions(+), 70 deletions(-) create mode 100644 packages/protocol/test/utils/seed.ts diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index 754213880f..c81c8449b6 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -1,9 +1,12 @@ import { expect } from "chai"; import { BigNumber, ethers } from "ethers"; import { ethers as hardhatEthers } from "hardhat"; -import { TaikoL1, TaikoL2 } from "../../typechain"; +import { TaikoL1, TaikoL2, TkoToken } from "../../typechain"; import { TestTkoToken } from "../../typechain/TestTkoToken"; +// import { BlockMetadata } from "../utils/block_metadata"; import Proposer from "../utils/proposer"; +// import { proveBlock } from "../utils/prove"; +import createAndSeedWallets from "../utils/seed"; import sleep from "../utils/sleep"; import { defaultFeeBase, deployTaikoL1 } from "../utils/taikoL1"; import { deployTaikoL2 } from "../utils/taikoL2"; @@ -58,20 +61,10 @@ describe("tokenomics", function () { const { chainId } = await l1Provider.getNetwork(); - proposerSigner = ethers.Wallet.createRandom().connect(l1Provider); - proverSigner = ethers.Wallet.createRandom().connect(l1Provider); - await l1Signer.sendTransaction({ - to: await proposerSigner.getAddress(), - value: ethers.utils.parseEther("1"), - }); - - await l1Signer.sendTransaction({ - to: await proverSigner.getAddress(), - value: ethers.utils.parseEther("1"), - }); - - const balance = await proposerSigner.getBalance(); - expect(balance).to.be.eq(ethers.utils.parseEther("1")); + [proposerSigner, proverSigner] = await createAndSeedWallets( + 2, + l1Signer + ); tkoTokenL1 = await deployTkoToken(l1Signer, taikoL1.address); @@ -96,9 +89,11 @@ describe("tokenomics", function () { // send transactions to L1 so we always get new blocks setInterval(async () => await sendTransaction(l1Signer), 1 * 500); + + console.log(proverSigner); // TODO ;remove, just to use variable. }); - it("proposes blocks, blockFee should increase, proposer's balance for TKOToken should decrease as it pays proposer fee, proofReward should increase since slots are growing and no proofs have been submitted", async function () { + it("proposes blocks on interval, blockFee should increase, proposer's balance for TKOToken should decrease as it pays proposer fee, proofReward should increase since slots are growing and no proofs have been submitted", async function () { const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); const blockIdsToNumber: any = {}; @@ -125,53 +120,32 @@ describe("tokenomics", function () { let lastProofReward = BigNumber.from(0); let hasFailedAssertions: boolean = false; + // every time a l2 block is created, we should try to propose it on L1. l2Provider.on("block", async (blockNumber) => { if (blockNumber <= genesisHeight) return; try { - const block = await l2Provider.getBlock(blockNumber); - const receipt = await proposer.commitThenProposeBlock(block); - expect(receipt.status).to.be.eq(1); - const proposedEvent = (receipt.events as any[]).find( - (e) => e.event === "BlockProposed" - ); - - const { id, meta } = (proposedEvent as any).args; - console.log( - "-----------PROPOSED---------------", - block.number, - id - ); - blockIdsToNumber[id.toString()] = block.number; - - const proofReward = await taikoL1.getProofReward( - new Date().getMilliseconds(), - meta.timestamp - ); - // proofReward should grow every time since slots are increasing - expect(proofReward.gt(lastProofReward)).to.be.eq(true); - // set lastProofReward equal to this once, for further comparison - // on next block proposal. - lastProofReward = proofReward; - - // get the balance of the tkoToken for proposer, and make sure it decreased - // ie: they paid the block proposal fee. - const newProposerTkoBalance = await tkoTokenL1.balanceOf( - await proposerSigner.getAddress() - ); + const { newProposerTkoBalance, newBlockFee, newProofReward } = + await onNewL2Block( + l2Provider, + blockNumber, + proposer, + blockIdsToNumber, + taikoL1, + proposerSigner, + tkoTokenL1 + ); expect( newProposerTkoBalance.lt(lastProposerTkoBalance) ).to.be.eq(true); - - lastProposerTkoBalance = newProposerTkoBalance; - - // after all proposing the block fee should be greater - // than it originally was. - const newBlockFee = await taikoL1.getBlockFee(); expect(newBlockFee.gt(lastBlockFee)).to.be.eq(true); + expect(newProofReward.gt(lastProofReward)).to.be.eq(true); lastBlockFee = newBlockFee; + lastProofReward = newProofReward; + lastProposerTkoBalance = newProposerTkoBalance; } catch (e) { + console.log("HAS FAILED ASSERTIONS"); hasFailedAssertions = true; console.error(e); throw e; @@ -182,7 +156,7 @@ describe("tokenomics", function () { expect(hasFailedAssertions).to.be.eq(false); }); - // it("tests tokenomics, propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { + // it("propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { // const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); // const blockIdsToNumber: any = {}; @@ -196,27 +170,25 @@ describe("tokenomics", function () { // // const prover: Prover = new Prover(taikoL1.connect(proverSigner)); + // let hasFailedAssertions: boolean = false; // l2Provider.on("block", async (blockNumber) => { // if (blockNumber <= genesisHeight) return; // try { - // console.log("new block", blockNumber); - // const block = await l2Provider.getBlock(blockNumber); - // const receipt = await proposer.commitThenProposeBlock(block); - // expect(receipt.status).to.be.eq(1); - // const proposedEvent = (receipt.events as any[]).find( - // (e) => e.event === "BlockProposed" - // ); - - // const { id } = (proposedEvent as any).args; - // console.log( - // "-----------PROPOSED---------------", - // block.number, - // id - // ); - // blockIdsToNumber[id.toString()] = block.number; + // await expect( + // onNewL2Block( + // l2Provider, + // blockNumber, + // proposer, + // blockIdsToNumber, + // taikoL1, + // proposerSigner, + // tkoTokenL1 + // ) + // ).not.to.throw; // } catch (e) { + // hasFailedAssertions = true; // console.error(e); - // expect(true).to.be.eq(false); + // throw e; // } // }); @@ -242,6 +214,65 @@ describe("tokenomics", function () { // } // ); - // await sleep(60 * 1000); + // await sleep(30 * 1000); + + // expect(hasFailedAssertions).to.be.eq(false); // }); }); + +async function onNewL2Block( + l2Provider: ethers.providers.JsonRpcProvider, + blockNumber: number, + proposer: Proposer, + blockIdsToNumber: any, + taikoL1: TaikoL1, + proposerSigner: any, + tkoTokenL1: TkoToken +): Promise<{ + newProposerTkoBalance: BigNumber; + newBlockFee: BigNumber; + newProofReward: BigNumber; +}> { + const block = await l2Provider.getBlock(blockNumber); + const receipt = await proposer.commitThenProposeBlock(block); + expect(receipt.status).to.be.eq(1); + const proposedEvent = (receipt.events as any[]).find( + (e) => e.event === "BlockProposed" + ); + + const { id, meta } = (proposedEvent as any).args; + + console.log("-----------PROPOSED---------------", block.number, id); + + blockIdsToNumber[id.toString()] = block.number; + + const newProofReward = await taikoL1.getProofReward( + new Date().getMilliseconds(), + meta.timestamp + ); + + console.log( + "NEW PROOF REWARD", + ethers.utils.formatEther(newProofReward.toString()), + " TKO" + ); + + const newProposerTkoBalance = await tkoTokenL1.balanceOf( + await proposerSigner.getAddress() + ); + + console.log( + "NEW PROPOSER TKO BALANCE", + ethers.utils.formatEther(newProposerTkoBalance.toString()), + " TKO" + ); + + const newBlockFee = await taikoL1.getBlockFee(); + + console.log( + "NEW BLOCK FEE", + ethers.utils.formatEther(newBlockFee.toString()), + " TKO" + ); + return { newProposerTkoBalance, newBlockFee, newProofReward }; +} diff --git a/packages/protocol/test/utils/seed.ts b/packages/protocol/test/utils/seed.ts new file mode 100644 index 0000000000..f9c1b9e91a --- /dev/null +++ b/packages/protocol/test/utils/seed.ts @@ -0,0 +1,21 @@ +import { BigNumber, ethers } from "ethers"; + +const createAndSeedWallets = async ( + len: number, + signer: any, + amount: BigNumber = ethers.utils.parseEther("1") +): Promise => { + const wallets: ethers.Wallet[] = []; + for (let i = 0; i < len; i++) { + const wallet = ethers.Wallet.createRandom().connect(signer.provider); + await signer.sendTransaction({ + to: await wallet.getAddress(), + value: amount, + }); + wallets.push(wallet); + } + + return wallets; +}; + +export default createAndSeedWallets; From a3880958b53b4b211bbda3c0ba30638fae987c2a Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 12 Jan 2023 17:34:14 -0800 Subject: [PATCH 17/39] tests for bootstrap halving --- packages/protocol/contracts/L1/TaikoData.sol | 2 +- .../contracts/L1/libs/LibProposing.sol | 2 +- .../protocol/contracts/L1/libs/LibUtils.sol | 2 +- .../contracts/libs/LibSharedConfig.sol | 2 +- .../contracts/test/L1/TestTaikoL1.sol | 2 +- .../test/L1/TestTaikoL1EnableTokenomics.sol | 2 +- .../test/tokenomics/Tokenomics.test.ts | 30 +++++++++++++++++-- packages/relayer/TaikoL1.json | 2 +- .../status-page/src/constants/abi/TaikoL1.ts | 2 +- .../docs/smart-contracts/L1/TaikoData.md | 2 +- 10 files changed, 37 insertions(+), 11 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 8385a39f6c..d4eb72cf57 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -38,7 +38,7 @@ library TaikoData { uint64 feeMaxPeriodPctg; uint64 blockTimeCap; uint64 proofTimeCap; - uint64 boostrapDiscountHalvingPeriod; + uint64 bootstrapDiscountHalvingPeriod; uint64 initialUncleDelay; bool enableTokenomics; bool enablePublicInputsCheck; diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 344433d636..58879b3d5e 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -167,7 +167,7 @@ library LibProposing { isProposal: true, feeBase: newFeeBase }); - //fee = LibUtils.getBootstrapDiscountedFee(state, config, fee); + fee = LibUtils.getBootstrapDiscountedFee(state, config, fee); deposit = (fee * config.proposerDepositPctg) / 100; } diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index d581a94359..0753caf2e9 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -138,7 +138,7 @@ library LibUtils { uint256 feeBase ) internal view returns (uint256) { uint256 halves = uint256(block.timestamp - state.genesisTimestamp) / - config.boostrapDiscountHalvingPeriod; + config.bootstrapDiscountHalvingPeriod; uint256 gamma = 1024 - (1024 >> halves); return (feeBase * gamma) / 1024; } diff --git a/packages/protocol/contracts/libs/LibSharedConfig.sol b/packages/protocol/contracts/libs/LibSharedConfig.sol index 5473327a71..100b4034a5 100644 --- a/packages/protocol/contracts/libs/LibSharedConfig.sol +++ b/packages/protocol/contracts/libs/LibSharedConfig.sol @@ -41,7 +41,7 @@ library LibSharedConfig { feeMaxPeriodPctg: 375, // 375% blockTimeCap: 48 seconds, proofTimeCap: 60 minutes, - boostrapDiscountHalvingPeriod: 180 days, + bootstrapDiscountHalvingPeriod: 180 days, initialUncleDelay: 60 minutes, enableTokenomics: false, enablePublicInputsCheck: true diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1.sol b/packages/protocol/contracts/test/L1/TestTaikoL1.sol index 90b84e9e20..ec8c6b9a6b 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1.sol @@ -47,7 +47,7 @@ contract TestTaikoL1 is TaikoL1, IProofVerifier { config.feeMaxPeriodPctg = 375; // 375% config.blockTimeCap = 48 seconds; config.proofTimeCap = 60 minutes; - config.boostrapDiscountHalvingPeriod = 180 days; + config.bootstrapDiscountHalvingPeriod = 180 days; config.initialUncleDelay = 1 minutes; config.enableTokenomics = false; config.enablePublicInputsCheck = true; diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol index 03410099a7..abb263791a 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1EnableTokenomics.sol @@ -47,7 +47,7 @@ contract TestTaikoL1EnableTokenomics is TaikoL1, IProofVerifier { config.feeMaxPeriodPctg = 375; // 375% config.blockTimeCap = 48 seconds; config.proofTimeCap = 5 seconds; - config.boostrapDiscountHalvingPeriod = 1 seconds; + config.bootstrapDiscountHalvingPeriod = 1 seconds; config.initialUncleDelay = 1 seconds; config.enableTokenomics = true; config.enablePublicInputsCheck = false; diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index c81c8449b6..36f5ed20e6 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -95,6 +95,7 @@ describe("tokenomics", function () { it("proposes blocks on interval, blockFee should increase, proposer's balance for TKOToken should decrease as it pays proposer fee, proofReward should increase since slots are growing and no proofs have been submitted", async function () { const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); + // wait for one period of halving to occur, so fee is not 0. const blockIdsToNumber: any = {}; // set up a proposer to continually propose new blocks @@ -115,8 +116,6 @@ describe("tokenomics", function () { // with proofs not being submitted. let lastBlockFee = await taikoL1.getBlockFee(); - expect(lastBlockFee).not.to.be.eq(0); - let lastProofReward = BigNumber.from(0); let hasFailedAssertions: boolean = false; @@ -156,6 +155,33 @@ describe("tokenomics", function () { expect(hasFailedAssertions).to.be.eq(false); }); + describe("bootstrapHalvingPeriod", function () { + it("block fee should increase as the halving period passes, while no blocks are proposed", async function () { + const { bootstrapDiscountHalvingPeriod } = + await taikoL1.getConfig(); + + const iterations: number = 5; + const period: number = bootstrapDiscountHalvingPeriod + .mul(1000) + .toNumber(); + + let lastBlockFee: BigNumber = await taikoL1.getBlockFee(); + expect(lastBlockFee.eq(0)).to.be.eq(true); + + for (let i = 0; i < iterations; i++) { + await sleep(period); + const blockFee = await taikoL1.getBlockFee(); + expect(blockFee.gt(lastBlockFee)).to.be.eq(true); + lastBlockFee = blockFee; + } + }); + }); + + it("expects the blockFee to go be 0 when no periods have passed", async function () { + const blockFee = await taikoL1.getBlockFee(); + expect(blockFee.eq(0)).to.be.eq(true); + }); + // it("propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { // const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); // const blockIdsToNumber: any = {}; diff --git a/packages/relayer/TaikoL1.json b/packages/relayer/TaikoL1.json index 9af8ad4746..d27319e81f 100644 --- a/packages/relayer/TaikoL1.json +++ b/packages/relayer/TaikoL1.json @@ -423,7 +423,7 @@ }, { "internalType": "uint64", - "name": "boostrapDiscountHalvingPeriod", + "name": "bootstrapDiscountHalvingPeriod", "type": "uint64" }, { diff --git a/packages/status-page/src/constants/abi/TaikoL1.ts b/packages/status-page/src/constants/abi/TaikoL1.ts index 421cb7fb58..b265ffdb68 100644 --- a/packages/status-page/src/constants/abi/TaikoL1.ts +++ b/packages/status-page/src/constants/abi/TaikoL1.ts @@ -423,7 +423,7 @@ export default [ }, { internalType: "uint64", - name: "boostrapDiscountHalvingPeriod", + name: "bootstrapDiscountHalvingPeriod", type: "uint64", }, { diff --git a/packages/website/docs/smart-contracts/L1/TaikoData.md b/packages/website/docs/smart-contracts/L1/TaikoData.md index 2e9969f0f4..6144b3f3de 100644 --- a/packages/website/docs/smart-contracts/L1/TaikoData.md +++ b/packages/website/docs/smart-contracts/L1/TaikoData.md @@ -27,7 +27,7 @@ struct Config { uint64 feeMaxPeriodPctg; uint64 blockTimeCap; uint64 proofTimeCap; - uint64 boostrapDiscountHalvingPeriod; + uint64 bootstrapDiscountHalvingPeriod; uint64 initialUncleDelay; bool enableTokenomics; } From c6ee1ef8c83d2b156c36ced31f8ec553975129f3 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 12 Jan 2023 18:01:12 -0800 Subject: [PATCH 18/39] bridge test cleanup more --- .../test/bridge/Bridge.integration.test.ts | 98 ++++++-------- .../protocol/test/libs/LibTrieProof.test.ts | 21 +-- .../test/tokenomics/Tokenomics.test.ts | 126 +++++++++--------- packages/protocol/test/utils/bridge.ts | 89 +++++++++++-- packages/protocol/test/utils/signal.ts | 22 +-- 5 files changed, 197 insertions(+), 159 deletions(-) diff --git a/packages/protocol/test/bridge/Bridge.integration.test.ts b/packages/protocol/test/bridge/Bridge.integration.test.ts index 8c3a361f2b..057340aef5 100644 --- a/packages/protocol/test/bridge/Bridge.integration.test.ts +++ b/packages/protocol/test/bridge/Bridge.integration.test.ts @@ -1,5 +1,5 @@ import { expect } from "chai"; -import { BigNumber, ethers as ethersLib } from "ethers"; +import { ethers as ethersLib } from "ethers"; import hre, { ethers } from "hardhat"; import { AddressManager, @@ -7,10 +7,15 @@ import { TestHeaderSync, TestLibBridgeData, } from "../../typechain"; -import { deployBridge, sendMessage } from "../utils/bridge"; +import { + deployBridge, + processMessage, + sendAndProcessMessage, + sendMessage, +} from "../utils/bridge"; import { randomBytes32 } from "../utils/bytes"; import { Message } from "../utils/message"; -import { getBlockHeader } from "../utils/rpc"; +import { Block, getBlockHeader } from "../utils/rpc"; import { getSignalProof, getSignalSlot } from "../utils/signal"; describe("integration:Bridge", function () { @@ -155,28 +160,14 @@ describe("integration:Bridge", function () { }); it("should throw if messageStatus of message is != NEW", async function () { - const { signal, message } = await sendMessage(l1Bridge, m); - const sender = l1Bridge.address; - - const key = getSignalSlot(hre, sender, signal); - - const { block, blockHeader } = await getBlockHeader( - hre.ethers.provider + const { message, signalProof } = await sendAndProcessMessage( + hre.ethers.provider, + headerSync, + m, + l1Bridge, + l2Bridge ); - await headerSync.setSyncedHeader(block.hash); - - const signalProof = await getSignalProof( - hre, - l1Bridge.address, - key, - block.number, - blockHeader - ); - - // upon successful processing, this immediately gets marked as DONE - await l2Bridge.processMessage(message, signalProof); - // recalling this process should be prevented as it's status is no longer NEW await expect( l2Bridge.processMessage(message, signalProof) @@ -192,7 +183,7 @@ describe("integration:Bridge", function () { const sender = l1Bridge.address; - const key = getSignalSlot(hre, sender, signal); + const key = getSignalSlot(sender, signal); const { block, blockHeader } = await getBlockHeader( hre.ethers.provider ); @@ -200,7 +191,7 @@ describe("integration:Bridge", function () { await headerSync.setSyncedHeader(ethers.constants.HashZero); const signalProof = await getSignalProof( - hre, + hre.ethers.provider, l1Bridge.address, key, block.number, @@ -223,7 +214,7 @@ describe("integration:Bridge", function () { const sender = l1Bridge.address; - const key = getSignalSlot(hre, sender, signal); + const key = getSignalSlot(sender, signal); const { block, blockHeader } = await getBlockHeader( hre.ethers.provider @@ -243,7 +234,7 @@ describe("integration:Bridge", function () { ); const signalProof = await getSignalProof( - hre, + hre.ethers.provider, l1Bridge.address, key, block.number, @@ -263,16 +254,19 @@ describe("integration:Bridge", function () { const messageStatus = await l1Bridge.getMessageStatus(signal); expect(messageStatus).to.be.eq(0); + let block: Block; + expect( + ({ block } = await processMessage( + l1Bridge, + l2Bridge, + signal, + hre.ethers.provider, + headerSync, + message + )) + ).to.emit(l2Bridge, "MessageStatusChanged"); - const sender = l1Bridge.address; - - const key = getSignalSlot(hre, sender, signal); - - const { block, blockHeader } = await getBlockHeader( - hre.ethers.provider - ); - - await headerSync.setSyncedHeader(block.hash); + const key = getSignalSlot(l1Bridge.address, signal); // get storageValue for the key const storageValue = await ethers.provider.getStorageAt( @@ -284,20 +278,6 @@ describe("integration:Bridge", function () { expect(storageValue).to.be.eq( "0x0000000000000000000000000000000000000000000000000000000000000001" ); - - const signalProof = await getSignalProof( - hre, - l1Bridge.address, - key, - block.number, - blockHeader - ); - - expect( - await l2Bridge.processMessage(message, signalProof, { - gasLimit: BigNumber.from(2000000), - }) - ).to.emit(l2Bridge, "MessageStatusChanged"); }); }); @@ -330,7 +310,7 @@ describe("integration:Bridge", function () { const sender = owner.address; - const key = getSignalSlot(hre, sender, signal); + const key = getSignalSlot(sender, signal); const { block, blockHeader } = await getBlockHeader( hre.ethers.provider @@ -350,7 +330,7 @@ describe("integration:Bridge", function () { ); const signalProof = await getSignalProof( - hre, + hre.ethers.provider, l1Bridge.address, key, block.number, @@ -362,12 +342,12 @@ describe("integration:Bridge", function () { ).to.be.reverted; }); - it("should return true", async function () { + it("if message is valid and sent by the bridge it should return true", async function () { const { signal } = await sendMessage(l1Bridge, m); const sender = l1Bridge.address; - const key = getSignalSlot(hre, sender, signal); + const key = getSignalSlot(sender, signal); const { block, blockHeader } = await getBlockHeader( hre.ethers.provider @@ -387,7 +367,7 @@ describe("integration:Bridge", function () { ); const signalProof = await getSignalProof( - hre, + hre.ethers.provider, l1Bridge.address, key, block.number, @@ -444,7 +424,7 @@ describe("integration:Bridge", function () { const sender = owner.address; - const key = getSignalSlot(hre, sender, signal); + const key = getSignalSlot(sender, signal); const { block, blockHeader } = await getBlockHeader( hre.ethers.provider @@ -464,7 +444,7 @@ describe("integration:Bridge", function () { ); const signalProof = await getSignalProof( - hre, + hre.ethers.provider, l1Bridge.address, key, block.number, @@ -490,7 +470,7 @@ describe("integration:Bridge", function () { const sender = owner.address; - const key = getSignalSlot(hre, sender, signal); + const key = getSignalSlot(sender, signal); const { block, blockHeader } = await getBlockHeader( hre.ethers.provider @@ -510,7 +490,7 @@ describe("integration:Bridge", function () { ); const signalProof = await getSignalProof( - hre, + hre.ethers.provider, l1Bridge.address, key, block.number, diff --git a/packages/protocol/test/libs/LibTrieProof.test.ts b/packages/protocol/test/libs/LibTrieProof.test.ts index 30fc98f5a4..434b6e3aa6 100644 --- a/packages/protocol/test/libs/LibTrieProof.test.ts +++ b/packages/protocol/test/libs/LibTrieProof.test.ts @@ -1,6 +1,7 @@ import { expect } from "chai"; -import hre, { ethers } from "hardhat"; +import { ethers } from "hardhat"; import RLP from "rlp"; +import { sendMessage } from "../utils/bridge"; import { Message } from "../utils/message"; import { EthGetProofResponse } from "../utils/rpc"; import { getSignalSlot } from "../utils/signal"; @@ -90,19 +91,9 @@ describe("integration:LibTrieProof", function () { memo: "", }; - const expectedAmount = - message.depositValue + - message.callValue + - message.processingFee; - const tx = await bridge.sendMessage(message, { - value: expectedAmount, - }); + const { tx, signal } = await sendMessage(bridge, message); - const receipt = await tx.wait(); - - const [messageSentEvent] = receipt.events as any as Event[]; - - const { signal } = (messageSentEvent as any).args; + await tx.wait(); expect(signal).not.to.be.eq(ethers.constants.HashZero); @@ -110,9 +101,7 @@ describe("integration:LibTrieProof", function () { expect(messageStatus).to.be.eq(0); - const sender = bridge.address; - - const key = getSignalSlot(hre, sender, signal); + const key = getSignalSlot(bridge.address, signal); // use this instead of ethers.provider.getBlock() beccause it doesnt have stateRoot // in the response diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index 36f5ed20e6..db12601a54 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -3,9 +3,9 @@ import { BigNumber, ethers } from "ethers"; import { ethers as hardhatEthers } from "hardhat"; import { TaikoL1, TaikoL2, TkoToken } from "../../typechain"; import { TestTkoToken } from "../../typechain/TestTkoToken"; -// import { BlockMetadata } from "../utils/block_metadata"; +import { BlockMetadata } from "../utils/block_metadata"; import Proposer from "../utils/proposer"; -// import { proveBlock } from "../utils/prove"; +import { proveBlock } from "../utils/prove"; import createAndSeedWallets from "../utils/seed"; import sleep from "../utils/sleep"; import { defaultFeeBase, deployTaikoL1 } from "../utils/taikoL1"; @@ -182,68 +182,66 @@ describe("tokenomics", function () { expect(blockFee.eq(0)).to.be.eq(true); }); - // it("propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { - // const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); - // const blockIdsToNumber: any = {}; - - // const proposer = new Proposer( - // taikoL1.connect(proposerSigner), - // l2Provider, - // commitConfirmations.toNumber(), - // maxNumBlocks.toNumber(), - // 0 - // ); - - // // const prover: Prover = new Prover(taikoL1.connect(proverSigner)); - - // let hasFailedAssertions: boolean = false; - // l2Provider.on("block", async (blockNumber) => { - // if (blockNumber <= genesisHeight) return; - // try { - // await expect( - // onNewL2Block( - // l2Provider, - // blockNumber, - // proposer, - // blockIdsToNumber, - // taikoL1, - // proposerSigner, - // tkoTokenL1 - // ) - // ).not.to.throw; - // } catch (e) { - // hasFailedAssertions = true; - // console.error(e); - // throw e; - // } - // }); - - // taikoL1.on( - // "BlockProposed", - // async (id: BigNumber, meta: BlockMetadata) => { - // console.log("proving block: id", id.toString()); - // try { - // await proveBlock( - // taikoL1, - // taikoL2, - // l1Provider, - // l2Provider, - // await proverSigner.getAddress(), - // id.toNumber(), - // blockIdsToNumber[id.toString()], - // meta - // ); - // } catch (e) { - // console.error(e); - // throw e; - // } - // } - // ); - - // await sleep(30 * 1000); - - // expect(hasFailedAssertions).to.be.eq(false); - // }); + it("propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { + const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); + const blockIdsToNumber: any = {}; + + const proposer = new Proposer( + taikoL1.connect(proposerSigner), + l2Provider, + commitConfirmations.toNumber(), + maxNumBlocks.toNumber(), + 0 + ); + + let hasFailedAssertions: boolean = false; + l2Provider.on("block", async (blockNumber) => { + if (blockNumber <= genesisHeight) return; + try { + await expect( + onNewL2Block( + l2Provider, + blockNumber, + proposer, + blockIdsToNumber, + taikoL1, + proposerSigner, + tkoTokenL1 + ) + ).not.to.throw; + } catch (e) { + hasFailedAssertions = true; + console.error(e); + throw e; + } + }); + + taikoL1.on( + "BlockProposed", + async (id: BigNumber, meta: BlockMetadata) => { + console.log("proving block: id", id.toString()); + try { + await proveBlock( + taikoL1, + taikoL2, + l1Provider, + l2Provider, + await proverSigner.getAddress(), + id.toNumber(), + blockIdsToNumber[id.toString()], + meta + ); + } catch (e) { + console.error(e); + throw e; + } + } + ); + + await sleep(30 * 1000); + + expect(hasFailedAssertions).to.be.eq(false); + }); }); async function onNewL2Block( diff --git a/packages/protocol/test/utils/bridge.ts b/packages/protocol/test/utils/bridge.ts index 3d1d4733d9..0e699b20bd 100644 --- a/packages/protocol/test/utils/bridge.ts +++ b/packages/protocol/test/utils/bridge.ts @@ -1,12 +1,15 @@ -import { BigNumber, Signer } from "ethers"; -import { ethers } from "hardhat"; +import { BigNumber, ethers, Signer } from "ethers"; +import { ethers as hardhatEthers } from "hardhat"; import { AddressManager, Bridge, EtherVault, LibTrieProof, + TestHeaderSync, } from "../../typechain"; import { Message } from "./message"; +import { Block, BlockHeader, getBlockHeader } from "./rpc"; +import { getSignalProof, getSignalSlot } from "./signal"; async function deployBridge( signer: Signer, @@ -15,13 +18,13 @@ async function deployBridge( srcChain: number ): Promise<{ bridge: Bridge; etherVault: EtherVault }> { const libTrieProof: LibTrieProof = await ( - await ethers.getContractFactory("LibTrieProof") + await hardhatEthers.getContractFactory("LibTrieProof") ) .connect(signer) .deploy(); const libBridgeProcess = await ( - await ethers.getContractFactory("LibBridgeProcess", { + await hardhatEthers.getContractFactory("LibBridgeProcess", { libraries: { LibTrieProof: libTrieProof.address, }, @@ -31,12 +34,12 @@ async function deployBridge( .deploy(); const libBridgeRetry = await ( - await ethers.getContractFactory("LibBridgeRetry") + await hardhatEthers.getContractFactory("LibBridgeRetry") ) .connect(signer) .deploy(); - const BridgeFactory = await ethers.getContractFactory("Bridge", { + const BridgeFactory = await hardhatEthers.getContractFactory("Bridge", { libraries: { LibBridgeProcess: libBridgeProcess.address, LibBridgeRetry: libBridgeRetry.address, @@ -49,7 +52,7 @@ async function deployBridge( await bridge.connect(signer).init(addressManager.address); const etherVault: EtherVault = await ( - await ethers.getContractFactory("EtherVault") + await hardhatEthers.getContractFactory("EtherVault") ) .connect(signer) .deploy(); @@ -76,7 +79,16 @@ async function deployBridge( return { bridge, etherVault }; } -async function sendMessage(bridge: Bridge, m: Message) { +async function sendMessage( + bridge: Bridge, + m: Message +): Promise<{ + bridge: Bridge; + signal: any; + messageSentEvent: any; + message: Message; + tx: ethers.ContractTransaction; +}> { const expectedAmount = m.depositValue + m.callValue + m.processingFee; const tx = await bridge.sendMessage(m, { @@ -89,7 +101,64 @@ async function sendMessage(bridge: Bridge, m: Message) { const { signal, message } = (messageSentEvent as any).args; - return { bridge, messageSentEvent, signal, message }; + return { bridge, messageSentEvent, signal, message, tx }; } -export { deployBridge, sendMessage }; +async function processMessage( + l1Bridge: Bridge, + l2Bridge: Bridge, + signal: string, + provider: ethers.providers.JsonRpcProvider, + headerSync: TestHeaderSync, + message: Message +): Promise<{ + tx: ethers.ContractTransaction; + signalProof: string; + block: Block; + blockHeader: BlockHeader; +}> { + const sender = l1Bridge.address; + + const key = getSignalSlot(sender, signal); + + const { block, blockHeader } = await getBlockHeader(provider); + + await headerSync.setSyncedHeader(block.hash); + + const signalProof = await getSignalProof( + provider, + l1Bridge.address, + key, + block.number, + blockHeader + ); + + const tx = await l2Bridge.processMessage(message, signalProof); + return { tx, signalProof, block, blockHeader }; +} + +async function sendAndProcessMessage( + provider: ethers.providers.JsonRpcProvider, + headerSync: TestHeaderSync, + m: Message, + l1Bridge: Bridge, + l2Bridge: Bridge +): Promise<{ + tx: ethers.ContractTransaction; + message: Message; + signal: string; + signalProof: string; +}> { + const { signal, message } = await sendMessage(l1Bridge, m); + const { tx, signalProof } = await processMessage( + l1Bridge, + l2Bridge, + signal, + provider, + headerSync, + message + ); + return { tx, signal, message, signalProof }; +} + +export { deployBridge, sendMessage, processMessage, sendAndProcessMessage }; diff --git a/packages/protocol/test/utils/signal.ts b/packages/protocol/test/utils/signal.ts index 32b5b1c54f..c36a3052a1 100644 --- a/packages/protocol/test/utils/signal.ts +++ b/packages/protocol/test/utils/signal.ts @@ -1,9 +1,10 @@ +import { ethers } from "ethers"; import RLP from "rlp"; import { BlockHeader, EthGetProofResponse } from "./rpc"; -function getSignalSlot(hre: any, sender: string, signal: string) { - return hre.ethers.utils.keccak256( - hre.ethers.utils.solidityPack( +function getSignalSlot(sender: string, signal: any) { + return ethers.utils.keccak256( + ethers.utils.solidityPack( ["string", "address", "bytes32"], ["SIGNAL", sender, signal] ) @@ -11,19 +12,20 @@ function getSignalSlot(hre: any, sender: string, signal: string) { } async function getSignalProof( - hre: any, + provider: ethers.providers.JsonRpcProvider, contractAddress: string, key: string, blockNumber: number, blockHeader: BlockHeader ) { - const proof: EthGetProofResponse = await hre.ethers.provider.send( - "eth_getProof", - [contractAddress, [key], blockNumber] - ); + const proof: EthGetProofResponse = await provider.send("eth_getProof", [ + contractAddress, + [key], + blockNumber, + ]); // RLP encode the proof together for LibTrieProof to decode - const encodedProof = hre.ethers.utils.defaultAbiCoder.encode( + const encodedProof = ethers.utils.defaultAbiCoder.encode( ["bytes", "bytes"], [ RLP.encode(proof.accountProof), @@ -31,7 +33,7 @@ async function getSignalProof( ] ); // encode the SignalProof struct from LibBridgeSignal - const signalProof = hre.ethers.utils.defaultAbiCoder.encode( + const signalProof = ethers.utils.defaultAbiCoder.encode( [ "tuple(tuple(bytes32 parentHash, bytes32 ommersHash, address beneficiary, bytes32 stateRoot, bytes32 transactionsRoot, bytes32 receiptsRoot, bytes32[8] logsBloom, uint256 difficulty, uint128 height, uint64 gasLimit, uint64 gasUsed, uint64 timestamp, bytes extraData, bytes32 mixHash, uint64 nonce, uint256 baseFeePerGas) header, bytes proof)", ], From 44a07053ca22638ad2e30fc4c2982388cd209a7f Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 12 Jan 2023 18:03:17 -0800 Subject: [PATCH 19/39] block max gas limit usage in test --- packages/protocol/test/L1/TaikoL1.integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/test/L1/TaikoL1.integration.test.ts b/packages/protocol/test/L1/TaikoL1.integration.test.ts index 761eec3dcb..eafd83606e 100644 --- a/packages/protocol/test/L1/TaikoL1.integration.test.ts +++ b/packages/protocol/test/L1/TaikoL1.integration.test.ts @@ -91,7 +91,7 @@ describe("integration:TaikoL1", function () { const block = await l2Provider.getBlock("latest"); // blockMetadata is inputs[0], txListBytes = inputs[1] const config = await taikoL1.getConfig(); - const gasLimit = config[7]; + const gasLimit = config.blockMaxGasLimit; const { tx, commit } = await commitBlock(taikoL1, block); From b9ae69043f031529f4b3fa82a78c68eff34f55a4 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 12 Jan 2023 18:04:31 -0800 Subject: [PATCH 20/39] remove prover --- packages/protocol/test/utils/prover.ts | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 packages/protocol/test/utils/prover.ts diff --git a/packages/protocol/test/utils/prover.ts b/packages/protocol/test/utils/prover.ts deleted file mode 100644 index c5f2a03723..0000000000 --- a/packages/protocol/test/utils/prover.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { TaikoL1 } from "../../typechain"; - -class Prover { - private readonly taikoL1: TaikoL1; - constructor(taikoL1: TaikoL1) { - this.taikoL1 = taikoL1; - } - - async prove() {} -} - -export default Prover; From 7010a24cd9439c268163bfefad644f4238c5005f Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 12 Jan 2023 18:15:00 -0800 Subject: [PATCH 21/39] comment out prove test. --- .../test/tokenomics/Tokenomics.test.ts | 123 +++++++++--------- 1 file changed, 60 insertions(+), 63 deletions(-) diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index db12601a54..c2a54fac60 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -3,9 +3,7 @@ import { BigNumber, ethers } from "ethers"; import { ethers as hardhatEthers } from "hardhat"; import { TaikoL1, TaikoL2, TkoToken } from "../../typechain"; import { TestTkoToken } from "../../typechain/TestTkoToken"; -import { BlockMetadata } from "../utils/block_metadata"; import Proposer from "../utils/proposer"; -import { proveBlock } from "../utils/prove"; import createAndSeedWallets from "../utils/seed"; import sleep from "../utils/sleep"; import { defaultFeeBase, deployTaikoL1 } from "../utils/taikoL1"; @@ -144,7 +142,6 @@ describe("tokenomics", function () { lastProofReward = newProofReward; lastProposerTkoBalance = newProposerTkoBalance; } catch (e) { - console.log("HAS FAILED ASSERTIONS"); hasFailedAssertions = true; console.error(e); throw e; @@ -182,66 +179,66 @@ describe("tokenomics", function () { expect(blockFee.eq(0)).to.be.eq(true); }); - it("propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { - const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); - const blockIdsToNumber: any = {}; - - const proposer = new Proposer( - taikoL1.connect(proposerSigner), - l2Provider, - commitConfirmations.toNumber(), - maxNumBlocks.toNumber(), - 0 - ); - - let hasFailedAssertions: boolean = false; - l2Provider.on("block", async (blockNumber) => { - if (blockNumber <= genesisHeight) return; - try { - await expect( - onNewL2Block( - l2Provider, - blockNumber, - proposer, - blockIdsToNumber, - taikoL1, - proposerSigner, - tkoTokenL1 - ) - ).not.to.throw; - } catch (e) { - hasFailedAssertions = true; - console.error(e); - throw e; - } - }); - - taikoL1.on( - "BlockProposed", - async (id: BigNumber, meta: BlockMetadata) => { - console.log("proving block: id", id.toString()); - try { - await proveBlock( - taikoL1, - taikoL2, - l1Provider, - l2Provider, - await proverSigner.getAddress(), - id.toNumber(), - blockIdsToNumber[id.toString()], - meta - ); - } catch (e) { - console.error(e); - throw e; - } - } - ); - - await sleep(30 * 1000); - - expect(hasFailedAssertions).to.be.eq(false); - }); + // it("propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { + // const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); + // const blockIdsToNumber: any = {}; + + // const proposer = new Proposer( + // taikoL1.connect(proposerSigner), + // l2Provider, + // commitConfirmations.toNumber(), + // maxNumBlocks.toNumber(), + // 0 + // ); + + // let hasFailedAssertions: boolean = false; + // l2Provider.on("block", async (blockNumber) => { + // if (blockNumber <= genesisHeight) return; + // try { + // await expect( + // onNewL2Block( + // l2Provider, + // blockNumber, + // proposer, + // blockIdsToNumber, + // taikoL1, + // proposerSigner, + // tkoTokenL1 + // ) + // ).not.to.throw; + // } catch (e) { + // hasFailedAssertions = true; + // console.error(e); + // throw e; + // } + // }); + + // taikoL1.on( + // "BlockProposed", + // async (id: BigNumber, meta: BlockMetadata) => { + // console.log("proving block: id", id.toString()); + // try { + // await proveBlock( + // taikoL1, + // taikoL2, + // l1Provider, + // l2Provider, + // await proverSigner.getAddress(), + // id.toNumber(), + // blockIdsToNumber[id.toString()], + // meta + // ); + // } catch (e) { + // console.error(e); + // throw e; + // } + // } + // ); + + // await sleep(30 * 1000); + + // expect(hasFailedAssertions).to.be.eq(false); + // }); }); async function onNewL2Block( From 074b5bd1401bf6f64ba4013fbec016b5446c2bcf Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 12 Jan 2023 18:22:35 -0800 Subject: [PATCH 22/39] use automine --- packages/protocol/test/tokenomics/Tokenomics.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index c2a54fac60..da4ecc0eaa 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -83,12 +83,12 @@ describe("tokenomics", function () { ).to.be.eq(ethers.utils.parseEther("100")); // set up interval mining so we always get new blocks - await l2Provider.send("evm_setIntervalMining", [2000]); + await l2Provider.send("evm_setAutomine", [true]); // send transactions to L1 so we always get new blocks setInterval(async () => await sendTransaction(l1Signer), 1 * 500); - console.log(proverSigner); // TODO ;remove, just to use variable. + console.log(proverSigner.address); // TODO ;remove, just to use variable. }); it("proposes blocks on interval, blockFee should increase, proposer's balance for TKOToken should decrease as it pays proposer fee, proofReward should increase since slots are growing and no proofs have been submitted", async function () { From 822a725d40d644362b8aeffd6d5a2bf77613e7ea Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 12 Jan 2023 18:23:06 -0800 Subject: [PATCH 23/39] add todo --- packages/protocol/test/utils/prove.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/protocol/test/utils/prove.ts b/packages/protocol/test/utils/prove.ts index 0bdc2294ff..a8daac0549 100644 --- a/packages/protocol/test/utils/prove.ts +++ b/packages/protocol/test/utils/prove.ts @@ -26,6 +26,7 @@ const buildProveBlockInputs = ( return inputs; }; +// TODO const proveBlock = async ( taikoL1: TaikoL1, taikoL2: TaikoL2, From 539c6df0e24eb79dae35fb37b7c96ea9e50c46f9 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 12 Jan 2023 18:44:49 -0800 Subject: [PATCH 24/39] remove 0 check --- packages/protocol/test/tokenomics/Tokenomics.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index da4ecc0eaa..fd50774b9e 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -163,7 +163,6 @@ describe("tokenomics", function () { .toNumber(); let lastBlockFee: BigNumber = await taikoL1.getBlockFee(); - expect(lastBlockFee.eq(0)).to.be.eq(true); for (let i = 0; i < iterations; i++) { await sleep(period); From 7735570d7af8ca124c5eb867d2d63bdb16d1e052 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 12 Jan 2023 19:29:18 -0800 Subject: [PATCH 25/39] resolve test issue? --- packages/protocol/test/tokenomics/Tokenomics.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index fd50774b9e..66b08aa9fc 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -83,7 +83,7 @@ describe("tokenomics", function () { ).to.be.eq(ethers.utils.parseEther("100")); // set up interval mining so we always get new blocks - await l2Provider.send("evm_setAutomine", [true]); + await l2Provider.send("evm_setIntervalMining", [2000]); // send transactions to L1 so we always get new blocks setInterval(async () => await sendTransaction(l1Signer), 1 * 500); From 2cc7b035ecd99e79809a97c6549093d53f6f5dec Mon Sep 17 00:00:00 2001 From: jeff <113397187+cyberhorsey@users.noreply.github.com> Date: Fri, 13 Jan 2023 11:27:18 -0800 Subject: [PATCH 26/39] Update packages/protocol/test/L1/TaikoL1.integration.test.ts Co-authored-by: Daniel Wang <99078276+dantaik@users.noreply.github.com> --- packages/protocol/test/L1/TaikoL1.integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/test/L1/TaikoL1.integration.test.ts b/packages/protocol/test/L1/TaikoL1.integration.test.ts index eafd83606e..58fd213eae 100644 --- a/packages/protocol/test/L1/TaikoL1.integration.test.ts +++ b/packages/protocol/test/L1/TaikoL1.integration.test.ts @@ -33,7 +33,7 @@ describe("integration:TaikoL1", function () { }); describe("isCommitValid()", async function () { - it("should not be valid", async function () { + it("should not be valid if it has not been committed", async function () { const block = await l2Provider.getBlock("latest"); const commit = generateCommitHash(block); From 42988301a57b68bd9ad86518386df845db616232 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 13 Jan 2023 11:52:21 -0800 Subject: [PATCH 27/39] addressManager utils functions, l1/l2 provider, l2signer --- .../test/L1/TaikoL1.integration.test.ts | 26 ++++++--- packages/protocol/test/L1/TaikoL1.test.ts | 5 +- packages/protocol/test/L1/TkoToken.test.ts | 8 ++- packages/protocol/test/L2/TaikoL2.test.ts | 5 +- .../test/bridge/Bridge.integration.test.ts | 28 ++++------ .../protocol/test/bridge/BridgedERC20.test.ts | 12 ++--- .../test/bridge/libs/LibBridgeRetry.test.ts | 15 +++--- .../test/etherVault/EtherVault.test.ts | 8 +-- .../test/thirdparty/AddressManager.test.ts | 6 +-- .../test/tokenomics/Tokenomics.test.ts | 53 +++++++++++-------- .../protocol/test/utils/addressManager.ts | 15 ++++++ packages/protocol/test/utils/proposer.ts | 3 +- packages/protocol/test/utils/provider.ts | 13 +++++ packages/protocol/test/utils/seed.ts | 4 +- packages/protocol/test/utils/taikoL1.ts | 10 ++-- packages/protocol/test/utils/taikoL2.ts | 28 ++++------ packages/protocol/test/utils/tkoToken.ts | 13 ++--- packages/protocol/test/utils/transaction.ts | 11 ---- 18 files changed, 142 insertions(+), 121 deletions(-) create mode 100644 packages/protocol/test/utils/addressManager.ts create mode 100644 packages/protocol/test/utils/provider.ts delete mode 100644 packages/protocol/test/utils/transaction.ts diff --git a/packages/protocol/test/L1/TaikoL1.integration.test.ts b/packages/protocol/test/L1/TaikoL1.integration.test.ts index eafd83606e..c07264c818 100644 --- a/packages/protocol/test/L1/TaikoL1.integration.test.ts +++ b/packages/protocol/test/L1/TaikoL1.integration.test.ts @@ -2,9 +2,11 @@ import { expect } from "chai"; import { BigNumber, ethers as ethersLib } from "ethers"; import { ethers } from "hardhat"; import { TaikoL1, TaikoL2 } from "../../typechain"; +import deployAddressManager from "../utils/addressManager"; import { BlockMetadata } from "../utils/block_metadata"; import { commitBlock, generateCommitHash } from "../utils/commit"; import { buildProposeBlockInputs, proposeBlock } from "../utils/propose"; +import { getDefaultL2Signer, getL1Provider } from "../utils/provider"; import { defaultFeeBase, deployTaikoL1 } from "../utils/taikoL1"; import { deployTaikoL2 } from "../utils/taikoL2"; @@ -19,17 +21,27 @@ describe("integration:TaikoL1", function () { "http://localhost:28545" ); - l2Signer = await l2Provider.getSigner( - ( - await l2Provider.listAccounts() - )[0] - ); + l2Signer = await getDefaultL2Signer(); - taikoL2 = await deployTaikoL2(l2Signer); + const l2AddressManager = await deployAddressManager(l2Signer); + taikoL2 = await deployTaikoL2(l2Signer, l2AddressManager); const genesisHash = taikoL2.deployTransaction.blockHash as string; - ({ taikoL1 } = await deployTaikoL1(genesisHash, false, defaultFeeBase)); + const l1Provider = getL1Provider(); + + l1Provider.pollingInterval = 100; + + const signers = await ethers.getSigners(); + + const l1AddressManager = await deployAddressManager(signers[0]); + + taikoL1 = await deployTaikoL1( + l1AddressManager, + genesisHash, + false, + defaultFeeBase + ); }); describe("isCommitValid()", async function () { diff --git a/packages/protocol/test/L1/TaikoL1.test.ts b/packages/protocol/test/L1/TaikoL1.test.ts index d69ad72b12..96d70461eb 100644 --- a/packages/protocol/test/L1/TaikoL1.test.ts +++ b/packages/protocol/test/L1/TaikoL1.test.ts @@ -1,6 +1,7 @@ import { expect } from "chai"; import { ethers } from "hardhat"; import { TaikoL1 } from "../../typechain"; +import deployAddressManager from "../utils/addressManager"; import { randomBytes32 } from "../utils/bytes"; import { deployTaikoL1 } from "../utils/taikoL1"; @@ -9,8 +10,10 @@ describe("TaikoL1", function () { let genesisHash: string; beforeEach(async function () { + const l1Signer = (await ethers.getSigners())[0]; + const addressManager = await deployAddressManager(l1Signer); genesisHash = randomBytes32(); - ({ taikoL1 } = await deployTaikoL1(genesisHash, false)); + taikoL1 = await deployTaikoL1(addressManager, genesisHash, false); }); describe("getLatestSyncedHeader()", async function () { diff --git a/packages/protocol/test/L1/TkoToken.test.ts b/packages/protocol/test/L1/TkoToken.test.ts index 40bf1ad52a..fa25e217ab 100644 --- a/packages/protocol/test/L1/TkoToken.test.ts +++ b/packages/protocol/test/L1/TkoToken.test.ts @@ -8,6 +8,7 @@ import { import { BigNumber } from "ethers"; import deployTkoToken from "../utils/tkoToken"; import { TestTkoToken } from "../../typechain/TestTkoToken"; +import deployAddressManager from "../utils/addressManager"; describe("TkoToken", function () { let owner: any; @@ -21,7 +22,12 @@ describe("TkoToken", function () { }); beforeEach(async function () { - token = await deployTkoToken(owner, protoBroker.address); + const addressManager = await deployAddressManager(owner); + token = await deployTkoToken( + owner, + addressManager, + protoBroker.address + ); amountMinted = ethers.utils.parseEther("100"); await token.connect(protoBroker).mint(owner.address, amountMinted); diff --git a/packages/protocol/test/L2/TaikoL2.test.ts b/packages/protocol/test/L2/TaikoL2.test.ts index c2009bf216..0c4f2c7e3f 100644 --- a/packages/protocol/test/L2/TaikoL2.test.ts +++ b/packages/protocol/test/L2/TaikoL2.test.ts @@ -1,6 +1,7 @@ import { expect } from "chai"; import { ethers } from "hardhat"; import { TaikoL2 } from "../../typechain"; +import deployAddressManager from "../utils/addressManager"; import { randomBytes32 } from "../utils/bytes"; import { deployTaikoL2 } from "../utils/taikoL2"; @@ -8,7 +9,9 @@ describe("TaikoL2", function () { let taikoL2: TaikoL2; beforeEach(async function () { - taikoL2 = await deployTaikoL2((await ethers.getSigners())[0]); + const signer = (await ethers.getSigners())[0]; + const addressManager = await deployAddressManager(signer); + taikoL2 = await deployTaikoL2(signer, addressManager); }); describe("anchor()", async function () { diff --git a/packages/protocol/test/bridge/Bridge.integration.test.ts b/packages/protocol/test/bridge/Bridge.integration.test.ts index 057340aef5..983f717bcd 100644 --- a/packages/protocol/test/bridge/Bridge.integration.test.ts +++ b/packages/protocol/test/bridge/Bridge.integration.test.ts @@ -7,6 +7,7 @@ import { TestHeaderSync, TestLibBridgeData, } from "../../typechain"; +import deployAddressManager from "../utils/addressManager"; import { deployBridge, processMessage, @@ -15,6 +16,7 @@ import { } from "../utils/bridge"; import { randomBytes32 } from "../utils/bytes"; import { Message } from "../utils/message"; +import { getDefaultL2Signer, getL2Provider } from "../utils/provider"; import { Block, getBlockHeader } from "../utils/rpc"; import { getSignalProof, getSignalSlot } from "../utils/signal"; @@ -38,15 +40,9 @@ describe("integration:Bridge", function () { srcChainId = chainId; // seondary node to deploy L2 on - l2Provider = new ethers.providers.JsonRpcProvider( - "http://localhost:28545" - ); + l2Provider = getL2Provider(); - l2Signer = await l2Provider.getSigner( - ( - await l2Provider.listAccounts() - )[0] - ); + l2Signer = await getDefaultL2Signer(); l2NonOwner = await l2Provider.getSigner(); @@ -54,17 +50,13 @@ describe("integration:Bridge", function () { enabledDestChainId = l2Network.chainId; - const addressManager: AddressManager = await ( - await ethers.getContractFactory("AddressManager") - ).deploy(); - await addressManager.init(); + const addressManager: AddressManager = await deployAddressManager( + owner + ); - const l2AddressManager: AddressManager = await ( - await ethers.getContractFactory("AddressManager") - ) - .connect(l2Signer) - .deploy(); - await l2AddressManager.init(); + const l2AddressManager: AddressManager = await deployAddressManager( + l2Signer + ); ({ bridge: l1Bridge } = await deployBridge( owner, diff --git a/packages/protocol/test/bridge/BridgedERC20.test.ts b/packages/protocol/test/bridge/BridgedERC20.test.ts index 3d24213e06..3ce2a42ed1 100644 --- a/packages/protocol/test/bridge/BridgedERC20.test.ts +++ b/packages/protocol/test/bridge/BridgedERC20.test.ts @@ -7,6 +7,7 @@ import { ERC20_BURN_AMOUNT_EXCEEDED, ERC20_TRANSFER_AMOUNT_EXCEEDED, } from "../constants/errors"; +import deployAddressManager from "../utils/addressManager"; const WETH_GOERLI = "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6"; const CHAIN_ID_GOERLI = 5; @@ -28,19 +29,12 @@ describe("BridgedERC20", function () { }); beforeEach(async function () { - unInitAddressManager = await ( - await ethers.getContractFactory("AddressManager") - ).deploy(); - await unInitAddressManager.init(); - + unInitAddressManager = await deployAddressManager(owner); unInitERC20 = await (await ethers.getContractFactory("BridgedERC20")) .connect(owner) .deploy(); - addressManager = await ( - await ethers.getContractFactory("AddressManager") - ).deploy(); - await addressManager.init(); + addressManager = await deployAddressManager(owner); const network = await ethers.provider.getNetwork(); diff --git a/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts b/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts index 2cc6aa7cce..43a474c227 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts @@ -15,6 +15,7 @@ import { TestLibBridgeRetry, TestReceiver, } from "../../../typechain"; +import deployAddressManager from "../../utils/addressManager"; describe("LibBridgeRetry", function () { let owner: any; @@ -32,15 +33,13 @@ describe("LibBridgeRetry", function () { }); beforeEach(async function () { - const addressManager: AddressManager = await ( - await ethers.getContractFactory("AddressManager") - ).deploy(); - await addressManager.init(); + const addressManager: AddressManager = await deployAddressManager( + owner + ); - const badAddressManager: AddressManager = await ( - await ethers.getContractFactory("AddressManager") - ).deploy(); - await badAddressManager.init(); + const badAddressManager: AddressManager = await deployAddressManager( + owner + ); etherVault = await (await ethers.getContractFactory("EtherVault")) .connect(etherVaultOwner) diff --git a/packages/protocol/test/etherVault/EtherVault.test.ts b/packages/protocol/test/etherVault/EtherVault.test.ts index 0d4b403d21..9d64ec1886 100644 --- a/packages/protocol/test/etherVault/EtherVault.test.ts +++ b/packages/protocol/test/etherVault/EtherVault.test.ts @@ -2,6 +2,7 @@ import { expect } from "chai"; import { AddressManager, EtherVault } from "../../typechain"; import { ethers } from "hardhat"; import { BigNumber } from "ethers"; +import deployAddressManager from "../utils/addressManager"; describe("EtherVault", function () { let owner: any; @@ -16,10 +17,9 @@ describe("EtherVault", function () { }); beforeEach(async function () { - const addressManager: AddressManager = await ( - await ethers.getContractFactory("AddressManager") - ).deploy(); - await addressManager.init(); + const addressManager: AddressManager = await deployAddressManager( + owner + ); etherVault = await (await ethers.getContractFactory("EtherVault")) .connect(owner) diff --git a/packages/protocol/test/thirdparty/AddressManager.test.ts b/packages/protocol/test/thirdparty/AddressManager.test.ts index c4cf33276c..b89759e3d2 100644 --- a/packages/protocol/test/thirdparty/AddressManager.test.ts +++ b/packages/protocol/test/thirdparty/AddressManager.test.ts @@ -1,6 +1,7 @@ import { expect } from "chai"; import { AddressManager } from "../../typechain"; import { ethers } from "hardhat"; +import deployAddressManager from "../utils/addressManager"; describe("AddressManager", function () { let owner: any; @@ -12,10 +13,7 @@ describe("AddressManager", function () { }); beforeEach(async function () { - addressManager = await ( - await ethers.getContractFactory("AddressManager") - ).deploy(); - await addressManager.init(); + addressManager = await deployAddressManager(owner); }); describe("setAddress()", async () => { diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index 66b08aa9fc..d60b029a1b 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -3,13 +3,18 @@ import { BigNumber, ethers } from "ethers"; import { ethers as hardhatEthers } from "hardhat"; import { TaikoL1, TaikoL2, TkoToken } from "../../typechain"; import { TestTkoToken } from "../../typechain/TestTkoToken"; +import deployAddressManager from "../utils/addressManager"; import Proposer from "../utils/proposer"; +import { + getDefaultL2Signer, + getL1Provider, + getL2Provider, +} from "../utils/provider"; import createAndSeedWallets from "../utils/seed"; import sleep from "../utils/sleep"; import { defaultFeeBase, deployTaikoL1 } from "../utils/taikoL1"; import { deployTaikoL2 } from "../utils/taikoL2"; import deployTkoToken from "../utils/tkoToken"; -import sendTransaction from "../utils/transaction"; describe("tokenomics", function () { let taikoL1: TaikoL1; @@ -25,38 +30,30 @@ describe("tokenomics", function () { let tkoTokenL1: TestTkoToken; beforeEach(async () => { - l1Provider = new ethers.providers.JsonRpcProvider( - "http://localhost:18545" - ); + l1Provider = getL1Provider(); l1Provider.pollingInterval = 100; const signers = await hardhatEthers.getSigners(); l1Signer = signers[0]; - l2Provider = new ethers.providers.JsonRpcProvider( - "http://localhost:28545" - ); + l2Provider = getL2Provider(); - l2Signer = await l2Provider.getSigner( - ( - await l2Provider.listAccounts() - )[0] - ); + l2Signer = await getDefaultL2Signer(); - taikoL2 = await deployTaikoL2(l2Signer); + const l2AddressManager = await deployAddressManager(l2Signer); + taikoL2 = await deployTaikoL2(l2Signer, l2AddressManager); genesisHash = taikoL2.deployTransaction.blockHash as string; genesisHeight = taikoL2.deployTransaction.blockNumber as number; - const { taikoL1: tL1, addressManager } = await deployTaikoL1( + const l1AddressManager = await deployAddressManager(l1Signer); + taikoL1 = await deployTaikoL1( + l1AddressManager, genesisHash, true, defaultFeeBase ); - - taikoL1 = tL1; - const { chainId } = await l1Provider.getNetwork(); [proposerSigner, proverSigner] = await createAndSeedWallets( @@ -64,9 +61,13 @@ describe("tokenomics", function () { l1Signer ); - tkoTokenL1 = await deployTkoToken(l1Signer, taikoL1.address); + tkoTokenL1 = await deployTkoToken( + l1Signer, + l1AddressManager, + taikoL1.address + ); - await addressManager.setAddress( + await l1AddressManager.setAddress( `${chainId}.tko_token`, tkoTokenL1.address ); @@ -83,10 +84,13 @@ describe("tokenomics", function () { ).to.be.eq(ethers.utils.parseEther("100")); // set up interval mining so we always get new blocks - await l2Provider.send("evm_setIntervalMining", [2000]); + await l2Provider.send("evm_setAutomine", [true]); // send transactions to L1 so we always get new blocks - setInterval(async () => await sendTransaction(l1Signer), 1 * 500); + setInterval( + async () => await sendTinyEtherToZeroAddress(l1Signer), + 1 * 500 + ); console.log(proverSigner.address); // TODO ;remove, just to use variable. }); @@ -296,3 +300,10 @@ async function onNewL2Block( ); return { newProposerTkoBalance, newBlockFee, newProofReward }; } + +const sendTinyEtherToZeroAddress = async (signer: any) => { + signer.sendTransaction({ + to: ethers.constants.AddressZero, + value: BigNumber.from(1), + }); +}; diff --git a/packages/protocol/test/utils/addressManager.ts b/packages/protocol/test/utils/addressManager.ts new file mode 100644 index 0000000000..47e577c116 --- /dev/null +++ b/packages/protocol/test/utils/addressManager.ts @@ -0,0 +1,15 @@ +import { ethers } from "ethers"; +import { ethers as hardhatEthers } from "hardhat"; +import { AddressManager } from "../../typechain"; + +const deployAddressManager = async (signer: ethers.Signer) => { + const addressManager: AddressManager = await ( + await hardhatEthers.getContractFactory("AddressManager") + ) + .connect(signer) + .deploy(); + await addressManager.init(); + return addressManager; +}; + +export default deployAddressManager; diff --git a/packages/protocol/test/utils/proposer.ts b/packages/protocol/test/utils/proposer.ts index 8baca8abc8..1bb2c98f10 100644 --- a/packages/protocol/test/utils/proposer.ts +++ b/packages/protocol/test/utils/proposer.ts @@ -33,8 +33,7 @@ class Proposer { } this.proposingMutex = true; if (!block) block = await this.l2Provider.getBlock("latest"); - const commitSlot = this.nextCommitSlot; - this.nextCommitSlot++; + const commitSlot = this.nextCommitSlot++; console.log("commiting ", block.number, "with commit slot", commitSlot); const { tx, commit } = await commitBlock( this.taikoL1, diff --git a/packages/protocol/test/utils/provider.ts b/packages/protocol/test/utils/provider.ts new file mode 100644 index 0000000000..66bcdbdd6f --- /dev/null +++ b/packages/protocol/test/utils/provider.ts @@ -0,0 +1,13 @@ +import { ethers } from "ethers"; +// providers for integration tests + +const getL1Provider = () => + new ethers.providers.JsonRpcProvider("http://localhost:18545"); + +const getL2Provider = () => + new ethers.providers.JsonRpcProvider("http://localhost:28545"); + +const getDefaultL2Signer = async () => + await getL2Provider().getSigner((await getL2Provider().listAccounts())[0]); + +export { getL1Provider, getL2Provider, getDefaultL2Signer }; diff --git a/packages/protocol/test/utils/seed.ts b/packages/protocol/test/utils/seed.ts index f9c1b9e91a..83665b3ba1 100644 --- a/packages/protocol/test/utils/seed.ts +++ b/packages/protocol/test/utils/seed.ts @@ -8,10 +8,12 @@ const createAndSeedWallets = async ( const wallets: ethers.Wallet[] = []; for (let i = 0; i < len; i++) { const wallet = ethers.Wallet.createRandom().connect(signer.provider); - await signer.sendTransaction({ + const tx = await signer.sendTransaction({ to: await wallet.getAddress(), value: amount, }); + + await tx.wait(1); wallets.push(wallet); } diff --git a/packages/protocol/test/utils/taikoL1.ts b/packages/protocol/test/utils/taikoL1.ts index 23e22263ec..f6191c61a5 100644 --- a/packages/protocol/test/utils/taikoL1.ts +++ b/packages/protocol/test/utils/taikoL1.ts @@ -5,15 +5,11 @@ import { AddressManager, TaikoL1 } from "../../typechain"; const defaultFeeBase = BigNumber.from(10).pow(18); async function deployTaikoL1( + addressManager: AddressManager, genesisHash: string, enableTokenomics: boolean, feeBase?: BigNumber -): Promise<{ taikoL1: TaikoL1; addressManager: AddressManager }> { - const addressManager = await ( - await ethers.getContractFactory("AddressManager") - ).deploy(); - await addressManager.init(); - +): Promise { const libReceiptDecoder = await ( await ethers.getContractFactory("LibReceiptDecoder") ).deploy(); @@ -58,7 +54,7 @@ async function deployTaikoL1( feeBase ?? defaultFeeBase ); - return { taikoL1: taikoL1 as TaikoL1, addressManager: addressManager }; + return taikoL1 as TaikoL1; } export { deployTaikoL1, defaultFeeBase }; diff --git a/packages/protocol/test/utils/taikoL2.ts b/packages/protocol/test/utils/taikoL2.ts index 55b5a0efcd..8b3dbbc6fe 100644 --- a/packages/protocol/test/utils/taikoL2.ts +++ b/packages/protocol/test/utils/taikoL2.ts @@ -1,35 +1,27 @@ -import { ethers } from "hardhat"; -import { TaikoL2 } from "../../typechain"; - -async function deployTaikoL2(signer: any): Promise { - const addressManager = await ( - await ethers.getContractFactory("AddressManager") - ).deploy(); - await addressManager.init(); - - const l2AddressManager = await ( - await ethers.getContractFactory("AddressManager") - ) - .connect(signer) - .deploy(); - await l2AddressManager.init(); +import { ethers } from "ethers"; +import { ethers as hardhatEthers } from "hardhat"; +import { AddressManager, TaikoL2 } from "../../typechain"; +async function deployTaikoL2( + signer: ethers.Signer, + addressManager: AddressManager +): Promise { // Deploying TaikoL2 Contract linked with LibTxDecoder (throws error otherwise) const l2LibTxDecoder = await ( - await ethers.getContractFactory("LibTxDecoder") + await hardhatEthers.getContractFactory("LibTxDecoder") ) .connect(signer) .deploy(); const taikoL2: TaikoL2 = await ( - await ethers.getContractFactory("TaikoL2", { + await hardhatEthers.getContractFactory("TaikoL2", { libraries: { LibTxDecoder: l2LibTxDecoder.address, }, }) ) .connect(signer) - .deploy(l2AddressManager.address); + .deploy(addressManager.address); return taikoL2; } diff --git a/packages/protocol/test/utils/tkoToken.ts b/packages/protocol/test/utils/tkoToken.ts index f507c657f1..b59c467bc4 100644 --- a/packages/protocol/test/utils/tkoToken.ts +++ b/packages/protocol/test/utils/tkoToken.ts @@ -2,14 +2,11 @@ import { ethers } from "ethers"; import { ethers as hardhatEthers } from "hardhat"; import { AddressManager } from "../../typechain"; -const deployTkoToken = async (signer: ethers.Signer, protoBroker: string) => { - const addressManager: AddressManager = await ( - await hardhatEthers.getContractFactory("AddressManager") - ) - .connect(signer) - .deploy(); - await addressManager.init(); - +const deployTkoToken = async ( + signer: ethers.Signer, + addressManager: AddressManager, + protoBroker: string +) => { const token = await (await hardhatEthers.getContractFactory("TestTkoToken")) .connect(signer) .deploy(); diff --git a/packages/protocol/test/utils/transaction.ts b/packages/protocol/test/utils/transaction.ts deleted file mode 100644 index ea266f223f..0000000000 --- a/packages/protocol/test/utils/transaction.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BigNumber } from "ethers"; -import { ethers } from "hardhat"; - -const sendTransaction = async (signer: any) => { - signer.sendTransaction({ - to: ethers.constants.AddressZero, - value: BigNumber.from(1), - }); -}; - -export default sendTransaction; From afdbe9c64b12e78131c5daaf7637c4f6ab0fe7c2 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 13 Jan 2023 12:21:50 -0800 Subject: [PATCH 28/39] set taikoL2 on taikoL1 --- packages/protocol/test/L1/TaikoL1.integration.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/protocol/test/L1/TaikoL1.integration.test.ts b/packages/protocol/test/L1/TaikoL1.integration.test.ts index 8e068b50a9..4f3cab8242 100644 --- a/packages/protocol/test/L1/TaikoL1.integration.test.ts +++ b/packages/protocol/test/L1/TaikoL1.integration.test.ts @@ -42,6 +42,13 @@ describe("integration:TaikoL1", function () { false, defaultFeeBase ); + + const { chainId: l2ChainId } = await l2Provider.getNetwork(); + + await l1AddressManager.setAddress( + `${l2ChainId}.taiko`, + taikoL2.address + ); }); describe("isCommitValid()", async function () { From 97fbb70c4553b8c9dc59be5cd03bd530120b18ca Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 13 Jan 2023 12:22:43 -0800 Subject: [PATCH 29/39] set taiko --- packages/protocol/test/tokenomics/Tokenomics.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index d60b029a1b..a1f0d7a13a 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -72,6 +72,13 @@ describe("tokenomics", function () { tkoTokenL1.address ); + const { chainId: l2ChainId } = await l2Provider.getNetwork(); + + await l1AddressManager.setAddress( + `${l2ChainId}.taiko`, + taikoL2.address + ); + await tkoTokenL1 .connect(l1Signer) .mintAnyone( From 2627e97f65ccac183bb129839995f12c04870d91 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 13 Jan 2023 15:26:53 -0800 Subject: [PATCH 30/39] put tokenomics utils functions in own file --- .../test/L1/TaikoL1.integration.test.ts | 1 - .../test/tokenomics/Tokenomics.test.ts | 187 ++++++------------ packages/protocol/test/tokenomics/utils.ts | 68 +++++++ 3 files changed, 131 insertions(+), 125 deletions(-) create mode 100644 packages/protocol/test/tokenomics/utils.ts diff --git a/packages/protocol/test/L1/TaikoL1.integration.test.ts b/packages/protocol/test/L1/TaikoL1.integration.test.ts index 4f3cab8242..38dc69c855 100644 --- a/packages/protocol/test/L1/TaikoL1.integration.test.ts +++ b/packages/protocol/test/L1/TaikoL1.integration.test.ts @@ -108,7 +108,6 @@ describe("integration:TaikoL1", function () { it("should revert with invalid gasLimit", async function () { const block = await l2Provider.getBlock("latest"); - // blockMetadata is inputs[0], txListBytes = inputs[1] const config = await taikoL1.getConfig(); const gasLimit = config.blockMaxGasLimit; diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index a1f0d7a13a..53f23470c1 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -1,10 +1,12 @@ import { expect } from "chai"; import { BigNumber, ethers } from "ethers"; import { ethers as hardhatEthers } from "hardhat"; -import { TaikoL1, TaikoL2, TkoToken } from "../../typechain"; +import { TaikoL1, TaikoL2 } from "../../typechain"; import { TestTkoToken } from "../../typechain/TestTkoToken"; import deployAddressManager from "../utils/addressManager"; +import { BlockMetadata } from "../utils/block_metadata"; import Proposer from "../utils/proposer"; +import { proveBlock } from "../utils/prove"; import { getDefaultL2Signer, getL1Provider, @@ -15,6 +17,7 @@ import sleep from "../utils/sleep"; import { defaultFeeBase, deployTaikoL1 } from "../utils/taikoL1"; import { deployTaikoL2 } from "../utils/taikoL2"; import deployTkoToken from "../utils/tkoToken"; +import { onNewL2Block, sendTinyEtherToZeroAddress } from "./utils"; describe("tokenomics", function () { let taikoL1: TaikoL1; @@ -189,128 +192,64 @@ describe("tokenomics", function () { expect(blockFee.eq(0)).to.be.eq(true); }); - // it("propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { - // const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); - // const blockIdsToNumber: any = {}; - - // const proposer = new Proposer( - // taikoL1.connect(proposerSigner), - // l2Provider, - // commitConfirmations.toNumber(), - // maxNumBlocks.toNumber(), - // 0 - // ); - - // let hasFailedAssertions: boolean = false; - // l2Provider.on("block", async (blockNumber) => { - // if (blockNumber <= genesisHeight) return; - // try { - // await expect( - // onNewL2Block( - // l2Provider, - // blockNumber, - // proposer, - // blockIdsToNumber, - // taikoL1, - // proposerSigner, - // tkoTokenL1 - // ) - // ).not.to.throw; - // } catch (e) { - // hasFailedAssertions = true; - // console.error(e); - // throw e; - // } - // }); - - // taikoL1.on( - // "BlockProposed", - // async (id: BigNumber, meta: BlockMetadata) => { - // console.log("proving block: id", id.toString()); - // try { - // await proveBlock( - // taikoL1, - // taikoL2, - // l1Provider, - // l2Provider, - // await proverSigner.getAddress(), - // id.toNumber(), - // blockIdsToNumber[id.toString()], - // meta - // ); - // } catch (e) { - // console.error(e); - // throw e; - // } - // } - // ); - - // await sleep(30 * 1000); - - // expect(hasFailedAssertions).to.be.eq(false); - // }); -}); + it("propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { + const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); + const blockIdsToNumber: any = {}; + + const proposer = new Proposer( + taikoL1.connect(proposerSigner), + l2Provider, + commitConfirmations.toNumber(), + maxNumBlocks.toNumber(), + 0 + ); + + let hasFailedAssertions: boolean = false; + l2Provider.on("block", async (blockNumber) => { + if (blockNumber <= genesisHeight) return; + try { + await expect( + onNewL2Block( + l2Provider, + blockNumber, + proposer, + blockIdsToNumber, + taikoL1, + proposerSigner, + tkoTokenL1 + ) + ).not.to.throw; + } catch (e) { + hasFailedAssertions = true; + console.error(e); + throw e; + } + }); -async function onNewL2Block( - l2Provider: ethers.providers.JsonRpcProvider, - blockNumber: number, - proposer: Proposer, - blockIdsToNumber: any, - taikoL1: TaikoL1, - proposerSigner: any, - tkoTokenL1: TkoToken -): Promise<{ - newProposerTkoBalance: BigNumber; - newBlockFee: BigNumber; - newProofReward: BigNumber; -}> { - const block = await l2Provider.getBlock(blockNumber); - const receipt = await proposer.commitThenProposeBlock(block); - expect(receipt.status).to.be.eq(1); - const proposedEvent = (receipt.events as any[]).find( - (e) => e.event === "BlockProposed" - ); - - const { id, meta } = (proposedEvent as any).args; - - console.log("-----------PROPOSED---------------", block.number, id); - - blockIdsToNumber[id.toString()] = block.number; - - const newProofReward = await taikoL1.getProofReward( - new Date().getMilliseconds(), - meta.timestamp - ); - - console.log( - "NEW PROOF REWARD", - ethers.utils.formatEther(newProofReward.toString()), - " TKO" - ); - - const newProposerTkoBalance = await tkoTokenL1.balanceOf( - await proposerSigner.getAddress() - ); - - console.log( - "NEW PROPOSER TKO BALANCE", - ethers.utils.formatEther(newProposerTkoBalance.toString()), - " TKO" - ); - - const newBlockFee = await taikoL1.getBlockFee(); - - console.log( - "NEW BLOCK FEE", - ethers.utils.formatEther(newBlockFee.toString()), - " TKO" - ); - return { newProposerTkoBalance, newBlockFee, newProofReward }; -} - -const sendTinyEtherToZeroAddress = async (signer: any) => { - signer.sendTransaction({ - to: ethers.constants.AddressZero, - value: BigNumber.from(1), + taikoL1.on( + "BlockProposed", + async (id: BigNumber, meta: BlockMetadata) => { + console.log("proving block: id", id.toString()); + try { + await proveBlock( + taikoL1, + taikoL2, + l1Provider, + l2Provider, + await proverSigner.getAddress(), + id.toNumber(), + blockIdsToNumber[id.toString()], + meta + ); + } catch (e) { + console.error(e); + throw e; + } + } + ); + + await sleep(30 * 1000); + + expect(hasFailedAssertions).to.be.eq(false); }); -}; +}); diff --git a/packages/protocol/test/tokenomics/utils.ts b/packages/protocol/test/tokenomics/utils.ts new file mode 100644 index 0000000000..c738c9c1bb --- /dev/null +++ b/packages/protocol/test/tokenomics/utils.ts @@ -0,0 +1,68 @@ +import { BigNumber, ethers } from "ethers"; +import { TaikoL1, TkoToken } from "../../typechain"; +import Proposer from "../utils/proposer"; + +async function onNewL2Block( + l2Provider: ethers.providers.JsonRpcProvider, + blockNumber: number, + proposer: Proposer, + blockIdsToNumber: any, + taikoL1: TaikoL1, + proposerSigner: any, + tkoTokenL1: TkoToken +): Promise<{ + newProposerTkoBalance: BigNumber; + newBlockFee: BigNumber; + newProofReward: BigNumber; +}> { + const block = await l2Provider.getBlock(blockNumber); + const receipt = await proposer.commitThenProposeBlock(block); + const proposedEvent = (receipt.events as any[]).find( + (e) => e.event === "BlockProposed" + ); + + const { id, meta } = (proposedEvent as any).args; + + console.log("-----------PROPOSED---------------", block.number, id); + + blockIdsToNumber[id.toString()] = block.number; + + const newProofReward = await taikoL1.getProofReward( + new Date().getMilliseconds(), + meta.timestamp + ); + + console.log( + "NEW PROOF REWARD", + ethers.utils.formatEther(newProofReward.toString()), + " TKO" + ); + + const newProposerTkoBalance = await tkoTokenL1.balanceOf( + await proposerSigner.getAddress() + ); + + console.log( + "NEW PROPOSER TKO BALANCE", + ethers.utils.formatEther(newProposerTkoBalance.toString()), + " TKO" + ); + + const newBlockFee = await taikoL1.getBlockFee(); + + console.log( + "NEW BLOCK FEE", + ethers.utils.formatEther(newBlockFee.toString()), + " TKO" + ); + return { newProposerTkoBalance, newBlockFee, newProofReward }; +} + +const sendTinyEtherToZeroAddress = async (signer: any) => { + signer.sendTransaction({ + to: ethers.constants.AddressZero, + value: BigNumber.from(1), + }); +}; + +export { sendTinyEtherToZeroAddress, onNewL2Block }; From 0e716956f0c5ce13311e9c8ddc59c2c062ed71dd Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 13 Jan 2023 15:29:32 -0800 Subject: [PATCH 31/39] dont need prove tests yet --- .../test/tokenomics/Tokenomics.test.ts | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index 53f23470c1..fc7d11906d 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -4,9 +4,9 @@ import { ethers as hardhatEthers } from "hardhat"; import { TaikoL1, TaikoL2 } from "../../typechain"; import { TestTkoToken } from "../../typechain/TestTkoToken"; import deployAddressManager from "../utils/addressManager"; -import { BlockMetadata } from "../utils/block_metadata"; +// import { BlockMetadata } from "../utils/block_metadata"; import Proposer from "../utils/proposer"; -import { proveBlock } from "../utils/prove"; +// import { proveBlock } from "../utils/prove"; import { getDefaultL2Signer, getL1Provider, @@ -192,64 +192,64 @@ describe("tokenomics", function () { expect(blockFee.eq(0)).to.be.eq(true); }); - it("propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { - const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); - const blockIdsToNumber: any = {}; - - const proposer = new Proposer( - taikoL1.connect(proposerSigner), - l2Provider, - commitConfirmations.toNumber(), - maxNumBlocks.toNumber(), - 0 - ); - - let hasFailedAssertions: boolean = false; - l2Provider.on("block", async (blockNumber) => { - if (blockNumber <= genesisHeight) return; - try { - await expect( - onNewL2Block( - l2Provider, - blockNumber, - proposer, - blockIdsToNumber, - taikoL1, - proposerSigner, - tkoTokenL1 - ) - ).not.to.throw; - } catch (e) { - hasFailedAssertions = true; - console.error(e); - throw e; - } - }); - - taikoL1.on( - "BlockProposed", - async (id: BigNumber, meta: BlockMetadata) => { - console.log("proving block: id", id.toString()); - try { - await proveBlock( - taikoL1, - taikoL2, - l1Provider, - l2Provider, - await proverSigner.getAddress(), - id.toNumber(), - blockIdsToNumber[id.toString()], - meta - ); - } catch (e) { - console.error(e); - throw e; - } - } - ); - - await sleep(30 * 1000); - - expect(hasFailedAssertions).to.be.eq(false); - }); + // it("propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { + // const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); + // const blockIdsToNumber: any = {}; + + // const proposer = new Proposer( + // taikoL1.connect(proposerSigner), + // l2Provider, + // commitConfirmations.toNumber(), + // maxNumBlocks.toNumber(), + // 0 + // ); + + // let hasFailedAssertions: boolean = false; + // l2Provider.on("block", async (blockNumber) => { + // if (blockNumber <= genesisHeight) return; + // try { + // await expect( + // onNewL2Block( + // l2Provider, + // blockNumber, + // proposer, + // blockIdsToNumber, + // taikoL1, + // proposerSigner, + // tkoTokenL1 + // ) + // ).not.to.throw; + // } catch (e) { + // hasFailedAssertions = true; + // console.error(e); + // throw e; + // } + // }); + + // taikoL1.on( + // "BlockProposed", + // async (id: BigNumber, meta: BlockMetadata) => { + // console.log("proving block: id", id.toString()); + // try { + // await proveBlock( + // taikoL1, + // taikoL2, + // l1Provider, + // l2Provider, + // await proverSigner.getAddress(), + // id.toNumber(), + // blockIdsToNumber[id.toString()], + // meta + // ); + // } catch (e) { + // console.error(e); + // throw e; + // } + // } + // ); + + // await sleep(30 * 1000); + + // expect(hasFailedAssertions).to.be.eq(false); + // }); }); From c1428734ab4137021385e2bf1de03ac17e5a79b1 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 13 Jan 2023 16:03:58 -0800 Subject: [PATCH 32/39] testtaikoL2 --- packages/protocol/contracts/L2/TaikoL2.sol | 5 +- .../contracts/test/L1/TestTaikoL2.sol | 56 ++++++++ .../test/tokenomics/Tokenomics.test.ts | 125 +++++++++--------- packages/protocol/test/utils/rpc.ts | 15 ++- packages/protocol/test/utils/taikoL2.ts | 4 +- 5 files changed, 135 insertions(+), 70 deletions(-) create mode 100644 packages/protocol/contracts/test/L1/TestTaikoL2.sol diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 664af9fc68..b4e3258c5c 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -105,15 +105,16 @@ contract TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { ); require(tx.gasprice == 0, "L2:gasPrice"); + TaikoData.Config memory config = getConfig(); LibInvalidTxList.Reason reason = LibInvalidTxList.isTxListInvalid({ - config: getConfig(), + config: config, encoded: txList, hint: hint, txIdx: txIdx }); require(reason != LibInvalidTxList.Reason.OK, "L2:reason"); - _checkPublicInputs(); + if (config.enablePublicInputsCheck) _checkPublicInputs(); emit BlockInvalidated(txList.hashTxList()); } diff --git a/packages/protocol/contracts/test/L1/TestTaikoL2.sol b/packages/protocol/contracts/test/L1/TestTaikoL2.sol new file mode 100644 index 0000000000..16037e5d6e --- /dev/null +++ b/packages/protocol/contracts/test/L1/TestTaikoL2.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +// +// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ +// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ +// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ +// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ +// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ +// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ +pragma solidity ^0.8.9; + +import "../../L2/TaikoL2.sol"; + +contract TestTaikoL2 is TaikoL2 { + constructor(address _addressManager) TaikoL2(_addressManager) {} + + function getConfig() + public + pure + override + returns (TaikoData.Config memory config) + { + config.chainId = 167; + // up to 2048 pending blocks + config.maxNumBlocks = 4; + config.blockHashHistory = 3; + // This number is calculated from maxNumBlocks to make + // the 'the maximum value of the multiplier' close to 20.0 + config.zkProofsPerBlock = 1; + config.maxVerificationsPerTx = 2; + config.commitConfirmations = 1; + config.maxProofsPerForkChoice = 5; + config.blockMaxGasLimit = 30000000; // TODO + config.maxTransactionsPerBlock = 20; // TODO + config.maxBytesPerTxList = 10240; // TODO + config.minTxGasLimit = 21000; // TODO + config.anchorTxGasLimit = 250000; + config.feePremiumLamda = 590; + config.rewardBurnBips = 100; // 100 basis points or 1% + config.proposerDepositPctg = 25; // 25% + + // Moving average factors + config.feeBaseMAF = 1024; + config.blockTimeMAF = 64; + config.proofTimeMAF = 64; + + config.rewardMultiplierPctg = 400; // 400% + config.feeGracePeriodPctg = 125; // 125% + config.feeMaxPeriodPctg = 375; // 375% + config.blockTimeCap = 48 seconds; + config.proofTimeCap = 60 minutes; + config.bootstrapDiscountHalvingPeriod = 180 days; + config.initialUncleDelay = 1 minutes; + config.enableTokenomics = true; + config.enablePublicInputsCheck = false; + } +} diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index fc7d11906d..6a4398701b 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -4,9 +4,9 @@ import { ethers as hardhatEthers } from "hardhat"; import { TaikoL1, TaikoL2 } from "../../typechain"; import { TestTkoToken } from "../../typechain/TestTkoToken"; import deployAddressManager from "../utils/addressManager"; -// import { BlockMetadata } from "../utils/block_metadata"; +import { BlockMetadata } from "../utils/block_metadata"; import Proposer from "../utils/proposer"; -// import { proveBlock } from "../utils/prove"; +import { proveBlock } from "../utils/prove"; import { getDefaultL2Signer, getL1Provider, @@ -192,64 +192,65 @@ describe("tokenomics", function () { expect(blockFee.eq(0)).to.be.eq(true); }); - // it("propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { - // const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); - // const blockIdsToNumber: any = {}; - - // const proposer = new Proposer( - // taikoL1.connect(proposerSigner), - // l2Provider, - // commitConfirmations.toNumber(), - // maxNumBlocks.toNumber(), - // 0 - // ); - - // let hasFailedAssertions: boolean = false; - // l2Provider.on("block", async (blockNumber) => { - // if (blockNumber <= genesisHeight) return; - // try { - // await expect( - // onNewL2Block( - // l2Provider, - // blockNumber, - // proposer, - // blockIdsToNumber, - // taikoL1, - // proposerSigner, - // tkoTokenL1 - // ) - // ).not.to.throw; - // } catch (e) { - // hasFailedAssertions = true; - // console.error(e); - // throw e; - // } - // }); - - // taikoL1.on( - // "BlockProposed", - // async (id: BigNumber, meta: BlockMetadata) => { - // console.log("proving block: id", id.toString()); - // try { - // await proveBlock( - // taikoL1, - // taikoL2, - // l1Provider, - // l2Provider, - // await proverSigner.getAddress(), - // id.toNumber(), - // blockIdsToNumber[id.toString()], - // meta - // ); - // } catch (e) { - // console.error(e); - // throw e; - // } - // } - // ); - - // await sleep(30 * 1000); - - // expect(hasFailedAssertions).to.be.eq(false); - // }); + it.only("propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { + const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); + const blockIdsToNumber: any = {}; + + const proposer = new Proposer( + taikoL1.connect(proposerSigner), + l2Provider, + commitConfirmations.toNumber(), + maxNumBlocks.toNumber(), + 0 + ); + + let hasFailedAssertions: boolean = false; + l2Provider.on("block", async (blockNumber) => { + if (blockNumber <= genesisHeight) return; + try { + await expect( + onNewL2Block( + l2Provider, + blockNumber, + proposer, + blockIdsToNumber, + taikoL1, + proposerSigner, + tkoTokenL1 + ) + ).not.to.throw; + } catch (e) { + hasFailedAssertions = true; + console.error(e); + throw e; + } + }); + + taikoL1.on( + "BlockProposed", + async (id: BigNumber, meta: BlockMetadata) => { + console.log("proving block: id", id.toString()); + try { + await proveBlock( + taikoL1, + taikoL2, + l1Provider, + l2Provider, + await proverSigner.getAddress(), + id.toNumber(), + blockIdsToNumber[id.toString()], + meta + ); + } catch (e) { + hasFailedAssertions = true; + console.error(e); + throw e; + } + } + ); + + await sleep(20 * 1000); + + expect(hasFailedAssertions).to.be.eq(false); + }); }); diff --git a/packages/protocol/test/utils/rpc.ts b/packages/protocol/test/utils/rpc.ts index 1c3481920c..a72ee51492 100644 --- a/packages/protocol/test/utils/rpc.ts +++ b/packages/protocol/test/utils/rpc.ts @@ -1,4 +1,4 @@ -import { BigNumber } from "ethers"; +import { BigNumber, ethers } from "ethers"; type StorageEntry = { key: string; @@ -58,9 +58,16 @@ type BlockHeader = { baseFeePerGas: number; }; -async function getBlockHeader(provider: any, blockNumber?: number) { - const block: Block = await provider.send("eth_getBlockByNumber", [ - blockNumber ? BigNumber.from(blockNumber).toHexString() : "latest", +async function getBlockHeader( + provider: ethers.providers.JsonRpcProvider, + blockNumber?: number +) { + const b = await provider.getBlock( + blockNumber ? BigNumber.from(blockNumber).toHexString() : "latest" + ); + + const block: Block = await provider.send("eth_getBlockByHash", [ + b.hash, false, ]); diff --git a/packages/protocol/test/utils/taikoL2.ts b/packages/protocol/test/utils/taikoL2.ts index 8b3dbbc6fe..ebf06a6198 100644 --- a/packages/protocol/test/utils/taikoL2.ts +++ b/packages/protocol/test/utils/taikoL2.ts @@ -13,8 +13,8 @@ async function deployTaikoL2( .connect(signer) .deploy(); - const taikoL2: TaikoL2 = await ( - await hardhatEthers.getContractFactory("TaikoL2", { + const taikoL2 = await ( + await hardhatEthers.getContractFactory("TestTaikoL2", { libraries: { LibTxDecoder: l2LibTxDecoder.address, }, From 81d671b864204b5627df8396251dd9ea721a550c Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 13 Jan 2023 17:27:44 -0800 Subject: [PATCH 33/39] WIP on proving, anchoring --- .../test/tokenomics/Tokenomics.test.ts | 140 ++++++++++-------- packages/protocol/test/utils/encoding.ts | 12 +- packages/protocol/test/utils/prove.ts | 45 +++++- packages/protocol/test/utils/prover.ts | 53 +++++++ 4 files changed, 177 insertions(+), 73 deletions(-) create mode 100644 packages/protocol/test/utils/prover.ts diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index 6a4398701b..0133addaee 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -4,9 +4,8 @@ import { ethers as hardhatEthers } from "hardhat"; import { TaikoL1, TaikoL2 } from "../../typechain"; import { TestTkoToken } from "../../typechain/TestTkoToken"; import deployAddressManager from "../utils/addressManager"; -import { BlockMetadata } from "../utils/block_metadata"; import Proposer from "../utils/proposer"; -import { proveBlock } from "../utils/prove"; +// import Prover from "../utils/prover"; import { getDefaultL2Signer, getL1Provider, @@ -82,6 +81,11 @@ describe("tokenomics", function () { taikoL2.address ); + await l1AddressManager.setAddress( + `${chainId}.proof_verifier`, + taikoL1.address + ); + await tkoTokenL1 .connect(l1Signer) .mintAnyone( @@ -102,7 +106,11 @@ describe("tokenomics", function () { 1 * 500 ); - console.log(proverSigner.address); // TODO ;remove, just to use variable. + const tx = await l2Signer.sendTransaction({ + to: proverSigner.address, + value: ethers.utils.parseUnits("1", "ether"), + }); + await tx.wait(1); }); it("proposes blocks on interval, blockFee should increase, proposer's balance for TKOToken should decrease as it pays proposer fee, proofReward should increase since slots are growing and no proofs have been submitted", async function () { @@ -192,65 +200,69 @@ describe("tokenomics", function () { expect(blockFee.eq(0)).to.be.eq(true); }); - it.only("propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { - const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); - const blockIdsToNumber: any = {}; - - const proposer = new Proposer( - taikoL1.connect(proposerSigner), - l2Provider, - commitConfirmations.toNumber(), - maxNumBlocks.toNumber(), - 0 - ); - - let hasFailedAssertions: boolean = false; - l2Provider.on("block", async (blockNumber) => { - if (blockNumber <= genesisHeight) return; - try { - await expect( - onNewL2Block( - l2Provider, - blockNumber, - proposer, - blockIdsToNumber, - taikoL1, - proposerSigner, - tkoTokenL1 - ) - ).not.to.throw; - } catch (e) { - hasFailedAssertions = true; - console.error(e); - throw e; - } - }); - - taikoL1.on( - "BlockProposed", - async (id: BigNumber, meta: BlockMetadata) => { - console.log("proving block: id", id.toString()); - try { - await proveBlock( - taikoL1, - taikoL2, - l1Provider, - l2Provider, - await proverSigner.getAddress(), - id.toNumber(), - blockIdsToNumber[id.toString()], - meta - ); - } catch (e) { - hasFailedAssertions = true; - console.error(e); - throw e; - } - } - ); - - await sleep(20 * 1000); - - expect(hasFailedAssertions).to.be.eq(false); - }); + // it("propose blocks and prove blocks on interval, proverReward should decline and blockFee should increase", async function () { + // const { maxNumBlocks, commitConfirmations } = await taikoL1.getConfig(); + // const blockIdsToNumber: any = {}; + + // const proposer = new Proposer( + // taikoL1.connect(proposerSigner), + // l2Provider, + // commitConfirmations.toNumber(), + // maxNumBlocks.toNumber(), + // 0 + // ); + + // const prover = new Prover( + // taikoL1, + // taikoL2, + // l1Provider, + // l2Provider, + // proverSigner + // ); + + // let hasFailedAssertions: boolean = false; + // l2Provider.on("block", async (blockNumber) => { + // if (blockNumber <= genesisHeight) return; + // try { + // await expect( + // onNewL2Block( + // l2Provider, + // blockNumber, + // proposer, + // blockIdsToNumber, + // taikoL1, + // proposerSigner, + // tkoTokenL1 + // ) + // ).not.to.throw; + // } catch (e) { + // hasFailedAssertions = true; + // console.error(e); + // throw e; + // } + // }); + + // taikoL1.on( + // "BlockProposed", + // async (id: BigNumber, meta: BlockMetadata) => { + // console.log("proving block: id", id.toString()); + // try { + // await prover.prove( + // await proverSigner.getAddress(), + // id.toNumber(), + // blockIdsToNumber[id.toString()], + // meta + // ); + // } catch (e) { + // hasFailedAssertions = true; + // console.error(e); + // throw e; + // } + // } + // ); + + // await sleep(20 * 1000); + + // expect(hasFailedAssertions).to.be.eq(false); + // }); }); diff --git a/packages/protocol/test/utils/encoding.ts b/packages/protocol/test/utils/encoding.ts index bf28f44ccb..7984bb22e7 100644 --- a/packages/protocol/test/utils/encoding.ts +++ b/packages/protocol/test/utils/encoding.ts @@ -1,5 +1,6 @@ import { ethers } from "hardhat"; import { BlockMetadata } from "./block_metadata"; +import Evidence from "./evidence"; function encodeBlockMetadata(meta: BlockMetadata) { return ethers.utils.defaultAbiCoder.encode( @@ -10,4 +11,13 @@ function encodeBlockMetadata(meta: BlockMetadata) { ); } -export { encodeBlockMetadata }; +function encodeEvidence(evidence: Evidence) { + return ethers.utils.defaultAbiCoder.encode( + [ + "tuple(tuple(uint256 id, uint256 l1Height, bytes32 l1Hash, address beneficiary, bytes32 txListHash, bytes32 mixHash, bytes extraData, uint64 gasLimit, uint64 timestamp, uint64 commitHeight, uint64 commitSlot) meta, tuple(bytes32 parentHash, bytes32 ommersHash, address beneficiary, bytes32 stateRoot, bytes32 transactionsRoot, bytes32 receiptsRoot, bytes32[8] logsBloom, uint256 difficulty, uint128 height, uint64 gasLimit, uint64 gasUsed, uint64 timestamp, bytes extraData, bytes32 mixHash, uint64 nonce, uint256 baseFeePerGas) header, address prover, bytes[] proofs)", + ], + [evidence] + ); +} + +export { encodeBlockMetadata, encodeEvidence }; diff --git a/packages/protocol/test/utils/prove.ts b/packages/protocol/test/utils/prove.ts index a8daac0549..3ee00ec7e8 100644 --- a/packages/protocol/test/utils/prove.ts +++ b/packages/protocol/test/utils/prove.ts @@ -2,6 +2,7 @@ import { ethers } from "ethers"; import RLP from "rlp"; import { TaikoL1, TaikoL2 } from "../../typechain"; import { BlockMetadata } from "./block_metadata"; +import { encodeEvidence } from "./encoding"; import Evidence from "./evidence"; import { BlockHeader, getBlockHeader } from "./rpc"; @@ -9,8 +10,9 @@ const buildProveBlockInputs = ( meta: BlockMetadata, header: BlockHeader, prover: string, - anchorTx: Uint8Array, - anchorReceipt: any + anchorTx: Uint8Array | string, + anchorReceipt: Uint8Array | string, + zkProofsPerBlock: number ) => { const inputs = []; const evidence: Evidence = { @@ -19,8 +21,14 @@ const buildProveBlockInputs = ( prover: prover, proofs: [], // TODO }; + // we have mkp + zkp returnign true in testing, so can just push 0xff + // instead of actually making proofs for anchor tx, anchor receipt, and + // zkp + for (let i = 0; i < zkProofsPerBlock + 2; i++) { + evidence.proofs.push("0xff"); + } - inputs[0] = evidence; + inputs[0] = encodeEvidence(evidence); inputs[1] = anchorTx; inputs[2] = anchorReceipt; return inputs; @@ -30,19 +38,39 @@ const buildProveBlockInputs = ( const proveBlock = async ( taikoL1: TaikoL1, taikoL2: TaikoL2, - l1Provider: ethers.providers.JsonRpcProvider, + l2Signer: ethers.Signer, l2Provider: ethers.providers.JsonRpcProvider, proverAddress: string, blockId: number, blockNumber: number, meta: BlockMetadata ) => { + const config = await taikoL1.getConfig(); const header = await getBlockHeader(l2Provider, blockNumber); - const anchorTx = await taikoL2.anchor(meta.l1Height, meta.l1Hash); - const anchorTxRLPEncoded = await RLP.encode( - ethers.utils.serializeTransaction(anchorTx) + + const anchorTxPopulated = await taikoL2.populateTransaction.anchor( + meta.l1Height, + meta.l1Hash, + { + gasPrice: ethers.utils.parseUnits("5", "gwei"), + gasLimit: config.anchorTxGasLimit, + } ); + + delete anchorTxPopulated.from; + + const anchorTxSigned = await l2Signer.signTransaction(anchorTxPopulated); + + const anchorTx = await l2Provider.sendTransaction(anchorTxSigned); + + await anchorTx.wait(); + const anchorReceipt = await anchorTx.wait(1); + + const anchorTxRLPEncoded = RLP.encode( + ethers.utils.serializeTransaction(anchorTxPopulated) + ); + const anchorReceiptRLPEncoded = RLP.encode( ethers.utils.serializeTransaction(anchorReceipt) ); @@ -52,7 +80,8 @@ const proveBlock = async ( header.blockHeader, proverAddress, anchorTxRLPEncoded, - anchorReceiptRLPEncoded + anchorReceiptRLPEncoded, + config.zkProofsPerBlock.toNumber() ); const tx = await taikoL1.proveBlock(blockId, inputs); console.log("Proved block tx", tx.hash); diff --git a/packages/protocol/test/utils/prover.ts b/packages/protocol/test/utils/prover.ts new file mode 100644 index 0000000000..4ca2f2c713 --- /dev/null +++ b/packages/protocol/test/utils/prover.ts @@ -0,0 +1,53 @@ +import { ethers } from "ethers"; +import { TaikoL1, TaikoL2 } from "../../typechain"; +import { BlockMetadata } from "./block_metadata"; +import { proveBlock } from "./prove"; +import sleep from "./sleep"; + +class Prover { + private readonly taikoL1: TaikoL1; + private readonly taikoL2: TaikoL2; + private readonly l1Provider: ethers.providers.JsonRpcProvider; + private readonly l2Provider: ethers.providers.JsonRpcProvider; + private readonly l2Signer: ethers.Signer; + private provingMutex: boolean = false; + + constructor( + taikoL1: TaikoL1, + taikoL2: TaikoL2, + l1Provider: ethers.providers.JsonRpcProvider, + l2Provider: ethers.providers.JsonRpcProvider, + l2Signer: ethers.Signer + ) { + this.taikoL1 = taikoL1; + this.taikoL2 = taikoL2; + this.l1Provider = l1Provider; + this.l2Provider = l2Provider; + this.l2Signer = l2Signer; + } + + async prove( + proverAddress: string, + blockId: number, + blockNumber: number, + meta: BlockMetadata + ) { + while (this.provingMutex) { + await sleep(100); + } + this.provingMutex = true; + + await proveBlock( + this.taikoL1, + this.taikoL2, + this.l2Signer, + this.l2Provider, + proverAddress, + blockId, + blockNumber, + meta + ); + } +} + +export default Prover; From 9e24c886630712dff6775dd8a52abc01aeba36d7 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 13 Jan 2023 17:36:29 -0800 Subject: [PATCH 34/39] await sendtx --- packages/protocol/test/tokenomics/utils.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/protocol/test/tokenomics/utils.ts b/packages/protocol/test/tokenomics/utils.ts index c738c9c1bb..b2d56776b4 100644 --- a/packages/protocol/test/tokenomics/utils.ts +++ b/packages/protocol/test/tokenomics/utils.ts @@ -59,10 +59,12 @@ async function onNewL2Block( } const sendTinyEtherToZeroAddress = async (signer: any) => { - signer.sendTransaction({ - to: ethers.constants.AddressZero, - value: BigNumber.from(1), - }); + await signer + .sendTransaction({ + to: ethers.constants.AddressZero, + value: BigNumber.from(1), + }) + .wait(1); }; export { sendTinyEtherToZeroAddress, onNewL2Block }; From 18801d6d7aac4508027168df9705a222177818a4 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 13 Jan 2023 17:44:27 -0800 Subject: [PATCH 35/39] taikol2 test contracts --- .../L1/TestTaikoL2EnablePublicInputsCheck.sol | 56 +++++++++++++++++++ packages/protocol/test/utils/taikoL2.ts | 18 ++++-- 2 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 packages/protocol/contracts/test/L1/TestTaikoL2EnablePublicInputsCheck.sol diff --git a/packages/protocol/contracts/test/L1/TestTaikoL2EnablePublicInputsCheck.sol b/packages/protocol/contracts/test/L1/TestTaikoL2EnablePublicInputsCheck.sol new file mode 100644 index 0000000000..8b1ebf7e45 --- /dev/null +++ b/packages/protocol/contracts/test/L1/TestTaikoL2EnablePublicInputsCheck.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +// +// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ +// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ +// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ +// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ +// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ +// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ +pragma solidity ^0.8.9; + +import "../../L2/TaikoL2.sol"; + +contract TestTaikoL2EnablePublicInputsCheck is TaikoL2 { + constructor(address _addressManager) TaikoL2(_addressManager) {} + + function getConfig() + public + pure + override + returns (TaikoData.Config memory config) + { + config.chainId = 167; + // up to 2048 pending blocks + config.maxNumBlocks = 4; + config.blockHashHistory = 3; + // This number is calculated from maxNumBlocks to make + // the 'the maximum value of the multiplier' close to 20.0 + config.zkProofsPerBlock = 1; + config.maxVerificationsPerTx = 2; + config.commitConfirmations = 1; + config.maxProofsPerForkChoice = 5; + config.blockMaxGasLimit = 30000000; // TODO + config.maxTransactionsPerBlock = 20; // TODO + config.maxBytesPerTxList = 10240; // TODO + config.minTxGasLimit = 21000; // TODO + config.anchorTxGasLimit = 250000; + config.feePremiumLamda = 590; + config.rewardBurnBips = 100; // 100 basis points or 1% + config.proposerDepositPctg = 25; // 25% + + // Moving average factors + config.feeBaseMAF = 1024; + config.blockTimeMAF = 64; + config.proofTimeMAF = 64; + + config.rewardMultiplierPctg = 400; // 400% + config.feeGracePeriodPctg = 125; // 125% + config.feeMaxPeriodPctg = 375; // 375% + config.blockTimeCap = 48 seconds; + config.proofTimeCap = 60 minutes; + config.bootstrapDiscountHalvingPeriod = 180 days; + config.initialUncleDelay = 1 minutes; + config.enableTokenomics = true; + config.enablePublicInputsCheck = true; + } +} diff --git a/packages/protocol/test/utils/taikoL2.ts b/packages/protocol/test/utils/taikoL2.ts index ebf06a6198..1ce3880fdb 100644 --- a/packages/protocol/test/utils/taikoL2.ts +++ b/packages/protocol/test/utils/taikoL2.ts @@ -4,7 +4,8 @@ import { AddressManager, TaikoL2 } from "../../typechain"; async function deployTaikoL2( signer: ethers.Signer, - addressManager: AddressManager + addressManager: AddressManager, + enablePublicInputsCheck: boolean = true ): Promise { // Deploying TaikoL2 Contract linked with LibTxDecoder (throws error otherwise) const l2LibTxDecoder = await ( @@ -14,11 +15,16 @@ async function deployTaikoL2( .deploy(); const taikoL2 = await ( - await hardhatEthers.getContractFactory("TestTaikoL2", { - libraries: { - LibTxDecoder: l2LibTxDecoder.address, - }, - }) + await hardhatEthers.getContractFactory( + enablePublicInputsCheck + ? "TestTaikoL2EnablePublicInputsCheck" + : "TestTaikoL2", + { + libraries: { + LibTxDecoder: l2LibTxDecoder.address, + }, + } + ) ) .connect(signer) .deploy(addressManager.address); From 471f8711c5e2bec75a0d4d84251825cfd23e56df Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 13 Jan 2023 17:46:43 -0800 Subject: [PATCH 36/39] contract type --- packages/protocol/test/utils/taikoL2.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/test/utils/taikoL2.ts b/packages/protocol/test/utils/taikoL2.ts index 1ce3880fdb..63fd0a833a 100644 --- a/packages/protocol/test/utils/taikoL2.ts +++ b/packages/protocol/test/utils/taikoL2.ts @@ -29,7 +29,7 @@ async function deployTaikoL2( .connect(signer) .deploy(addressManager.address); - return taikoL2; + return taikoL2 as TaikoL2; } export { deployTaikoL2 }; From 65697ba266d236d39cf93938db35ac0d78022ce1 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 13 Jan 2023 17:52:33 -0800 Subject: [PATCH 37/39] disable public inputs --- packages/protocol/test/tokenomics/Tokenomics.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index 0133addaee..aaf6b0568e 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -44,7 +44,7 @@ describe("tokenomics", function () { l2Signer = await getDefaultL2Signer(); const l2AddressManager = await deployAddressManager(l2Signer); - taikoL2 = await deployTaikoL2(l2Signer, l2AddressManager); + taikoL2 = await deployTaikoL2(l2Signer, l2AddressManager, false); genesisHash = taikoL2.deployTransaction.blockHash as string; genesisHeight = taikoL2.deployTransaction.blockNumber as number; From b1358dd1e634ff72b5cb2b750de71177f200c216 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Fri, 13 Jan 2023 18:04:16 -0800 Subject: [PATCH 38/39] wip cleanup --- packages/protocol/test/tokenomics/Tokenomics.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/protocol/test/tokenomics/Tokenomics.test.ts b/packages/protocol/test/tokenomics/Tokenomics.test.ts index aaf6b0568e..60d066b809 100644 --- a/packages/protocol/test/tokenomics/Tokenomics.test.ts +++ b/packages/protocol/test/tokenomics/Tokenomics.test.ts @@ -103,7 +103,7 @@ describe("tokenomics", function () { // send transactions to L1 so we always get new blocks setInterval( async () => await sendTinyEtherToZeroAddress(l1Signer), - 1 * 500 + 1 * 1000 ); const tx = await l2Signer.sendTransaction({ @@ -135,6 +135,10 @@ describe("tokenomics", function () { // do the same for the blockFee, which should increase every block proposal // with proofs not being submitted. let lastBlockFee = await taikoL1.getBlockFee(); + while (lastBlockFee.eq(0)) { + await sleep(500); + lastBlockFee = await taikoL1.getBlockFee(); + } let lastProofReward = BigNumber.from(0); From 34ff709049f1f33a01a1ae64f41539ddcb3986e8 Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Sat, 14 Jan 2023 10:59:19 +0800 Subject: [PATCH 39/39] Update TaikoL2.sol --- packages/protocol/contracts/L2/TaikoL2.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index b4e3258c5c..9d51e7569a 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -114,7 +114,9 @@ contract TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { }); require(reason != LibInvalidTxList.Reason.OK, "L2:reason"); - if (config.enablePublicInputsCheck) _checkPublicInputs(); + if (config.enablePublicInputsCheck) { + _checkPublicInputs(); + } emit BlockInvalidated(txList.hashTxList()); }