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 00000000000..3a12d19c138 --- /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 00000000000..a50fbfa9365 --- /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 5b25613a330..11665c43391 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 ff9f5502882..4195f9b5fa8 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 00000000000..2ccd4518630 --- /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 00000000000..9edfcf57c06 --- /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 00000000000..74532a6ebd7 --- /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 00000000000..e69de29bb2d diff --git a/packages/protocol/test/utils/bytes.ts b/packages/protocol/test/utils/bytes.ts new file mode 100644 index 00000000000..50331a1d3a3 --- /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 00000000000..1f99bb2ca4f --- /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 00000000000..bf28f44ccb8 --- /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 00000000000..07fc2b861aa --- /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 00000000000..e69de29bb2d 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 00000000000..6a59a1c7108 --- /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 10a127a6430..62b5ac804a6 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 e69de29bb2d..fbebacef20f 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 6a59a1c7108..852232efaf3 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 62b5ac804a6..3b52e5e5ed8 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 fbebacef20f..568712c424e 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 fc50bf80bc2..e8a82b35c9c 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 a50fbfa9365..0e3dde4bd72 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 11665c43391..145cef39f6f 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 4195f9b5fa8..c2009bf2169 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 852232efaf3..70552a35b11 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 3b52e5e5ed8..33577568d99 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 2d04c6e4d4b..c78bbd7634e 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 c542d640ae8..4655bdb395c 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 bd99c8110e2..2cc6aa7cce0 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 89b5e7f1b7c..bd8d671c5eb 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 e3e21156016..3c2edc94315 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 568712c424e..1b7e8ad1800 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 717641d1b22..bf74fb99068 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 250d4ea87a9..77473084ae4 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 00000000000..32b5b1c54f9 --- /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 00000000000..185a66b8664 --- /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 00000000000..55b5a0efcd3 --- /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 34afa79845e..815f842f162 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 815f842f162..114ebe4753c 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 0e3dde4bd72..0ebd4711b9a 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 07fc2b861aa..b88f617e861 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 5314a306473..938f74c47f9 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 8cf7dfcd672..e35f390e78d 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 0ebd4711b9a..df893db8ebc 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 4afb604e8bc..634460d8990 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 df893db8ebc..2f9f1dec638 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 1b7e8ad1800..3d1d4733d9e 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 2f9f1dec638..16982c6fe5e 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 795a3380b91..8385a39f6cb 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 620697c88b1..664af9fc68f 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 80e83fcf614..5473327a714 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 2a4e431779f..90b84e9e204 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 3a12d19c138..f4d49ea38cd 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 00000000000..c7329c20cf4 --- /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 e35f390e78d..34776163c67 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 2f9f1dec638..a4fd8f27bbb 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 145cef39f6f..d69ad72b129 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 114ebe4753c..40bf1ad52ab 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 70552a35b11..8c3a361f2bb 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 485cc8b4d23..5b0d7c7ad72 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 2ccd4518630..00000000000 --- 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 9edfcf57c06..7dec8ab9cb2 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 1f99bb2ca4f..f22f366c0cc 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 00000000000..58e39c16414 --- /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 b88f617e861..7d961c0ae50 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 00000000000..8baca8abc82 --- /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 00000000000..1aa25be2919 --- /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 00000000000..c5f2a037233 --- /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 77473084ae4..1c3481920c3 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 00000000000..268c312630f --- /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 185a66b8664..23e22263ec7 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 00000000000..f507c657f19 --- /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 00000000000..ea266f223fd --- /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 58879b3d5e2..344433d6365 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 f4d49ea38cd..03410099a73 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 7dec8ab9cb2..3454de026c8 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 1aa25be2919..0bdc2294ffe 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 3454de026c8..754213880fa 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 754213880fa..c81c8449b6c 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 00000000000..f9c1b9e91a3 --- /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 8385a39f6cb..d4eb72cf57f 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 344433d6365..58879b3d5e2 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 d581a943596..0753caf2e9b 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 5473327a714..100b4034a57 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 90b84e9e204..ec8c6b9a6bd 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 03410099a73..abb263791ae 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 c81c8449b6c..36f5ed20e68 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 9af8ad47467..d27319e81f5 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 421cb7fb58a..b265ffdb688 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 2e9969f0f4d..6144b3f3de8 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 8c3a361f2bb..057340aef5f 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 30fc98f5a43..434b6e3aa67 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 36f5ed20e68..db12601a543 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 3d1d4733d9e..0e699b20bd9 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 32b5b1c54f9..c36a3052a17 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 761eec3dcb0..eafd83606ef 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 c5f2a037233..00000000000 --- 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 db12601a543..c2a54fac60b 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 c2a54fac60b..da4ecc0eaa2 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 0bdc2294ffe..a8daac05494 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 da4ecc0eaa2..fd50774b9e4 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 fd50774b9e4..66b08aa9fc2 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 eafd83606ef..58fd213eae2 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 eafd83606ef..c07264c8185 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 d69ad72b129..96d70461eb4 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 40bf1ad52ab..fa25e217ab6 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 c2009bf2169..0c4f2c7e3f4 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 057340aef5f..983f717bcda 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 3d24213e060..3ce2a42ed13 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 2cc6aa7cce0..43a474c227d 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 0d4b403d21a..9d64ec18860 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 c4cf33276c8..b89759e3d2d 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 66b08aa9fc2..d60b029a1bb 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 00000000000..47e577c1162 --- /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 8baca8abc82..1bb2c98f105 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 00000000000..66bcdbdd6f1 --- /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 f9c1b9e91a3..83665b3ba17 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 23e22263ec7..f6191c61a5b 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 55b5a0efcd3..8b3dbbc6fe4 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 f507c657f19..b59c467bc4e 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 ea266f223fd..00000000000 --- 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 8e068b50a98..4f3cab82426 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 d60b029a1bb..a1f0d7a13a4 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 4f3cab82426..38dc69c855d 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 a1f0d7a13a4..53f23470c14 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 00000000000..c738c9c1bb5 --- /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 53f23470c14..fc7d11906d6 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 664af9fc68f..b4e3258c5c0 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 00000000000..16037e5d6ef --- /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 fc7d11906d6..6a4398701bc 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 1c3481920c3..a72ee514925 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 8b3dbbc6fe4..ebf06a61981 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 6a4398701bc..0133addaee8 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 bf28f44ccb8..7984bb22e77 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 a8daac05494..3ee00ec7e88 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 00000000000..4ca2f2c7137 --- /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 c738c9c1bb5..b2d56776b4f 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 00000000000..8b1ebf7e455 --- /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 ebf06a61981..1ce3880fdbb 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 1ce3880fdbb..63fd0a833a9 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 0133addaee8..aaf6b0568e6 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 aaf6b0568e6..60d066b809c 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 b4e3258c5c0..9d51e7569af 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()); }