From 791f33bd03800611f97e5f2f590ed01c9ef4f2cd Mon Sep 17 00:00:00 2001 From: RogerLamTd Date: Tue, 22 Nov 2022 13:21:54 -0500 Subject: [PATCH 01/57] making fixtures type-safe --- packages/protocol/test/bridge/Bridge.test.ts | 19 ++- .../test/bridge/libs/LibBridgeData.test.ts | 23 ++-- .../test/bridge/libs/LibBridgeInvoke.test.ts | 12 +- .../test/bridge/libs/LibBridgeProcess.test.ts | 129 ++---------------- .../test/bridge/libs/LibBridgeRetry.test.ts | 70 ++++------ .../test/bridge/libs/LibBridgeSend.test.ts | 26 +++- .../test/bridge/libs/LibBridgeSignal.test.ts | 8 +- 7 files changed, 101 insertions(+), 186 deletions(-) diff --git a/packages/protocol/test/bridge/Bridge.test.ts b/packages/protocol/test/bridge/Bridge.test.ts index 6a137d3cde4..5461e4dd192 100644 --- a/packages/protocol/test/bridge/Bridge.test.ts +++ b/packages/protocol/test/bridge/Bridge.test.ts @@ -1,12 +1,17 @@ import { expect } from "chai" -import { AddressManager, Bridge, EtherVault } from "../../typechain" +import { + AddressManager, + Bridge, + EtherVault, + LibTrieProof, + TestHeaderSync, + TestLibBridgeData, +} from "../../typechain" import { ethers } from "hardhat" import { BigNumber, Signer } from "ethers" import { Message } from "../utils/message" import { Block, BlockHeader, EthGetProofResponse } from "../utils/rpc" -// import { getSlot, MessageStatus } from "../../tasks/utils" import RLP from "rlp" -// const helpers = require("@nomicfoundation/hardhat-network-helpers") async function deployBridge( signer: Signer, @@ -14,7 +19,9 @@ async function deployBridge( destChain: number, srcChain: number ): Promise<{ bridge: Bridge; etherVault: EtherVault }> { - const libTrieProof = await (await ethers.getContractFactory("LibTrieProof")) + const libTrieProof: LibTrieProof = await ( + await ethers.getContractFactory("LibTrieProof") + ) .connect(signer) .deploy() @@ -476,7 +483,7 @@ describe("integration:Bridge", function () { .connect(l2Signer) .setAddress(`${srcChainId}.bridge`, l1Bridge.address) - const headerSync = await ( + const headerSync: TestHeaderSync = await ( await ethers.getContractFactory("TestHeaderSync") ) .connect(l2Signer) @@ -701,7 +708,7 @@ describe("integration:Bridge", function () { ["latest", false] ) - const libData = await ( + const libData: TestLibBridgeData = await ( await ethers.getContractFactory("TestLibBridgeData") ).deploy() diff --git a/packages/protocol/test/bridge/libs/LibBridgeData.test.ts b/packages/protocol/test/bridge/libs/LibBridgeData.test.ts index 4409a8dcaed..591ed845a51 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeData.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeData.test.ts @@ -1,16 +1,19 @@ import { expect } from "chai" import { ethers } from "hardhat" +import { TestLibBridgeData } from "../../../typechain" +import { MessageStatus } from "../../../tasks/utils" +import { Message } from "../../utils/message" import { TAIKO_BRIDGE_MESSAGE } from "../../constants/messages" describe("LibBridgeData", function () { async function deployLibBridgeDataFixture() { const [owner, nonOwner] = await ethers.getSigners() - const libData = await ( + const libData: TestLibBridgeData = await ( await ethers.getContractFactory("TestLibBridgeData") ).deploy() - const testMessage = { + const testMessage: Message = { id: 1, sender: owner.address, srcChainId: 1, @@ -33,12 +36,6 @@ describe("LibBridgeData", function () { const testVar = [TAIKO_BRIDGE_MESSAGE, testMessage] - const MessageStatus = { - NEW: 0, - RETRIABLE: 1, - DONE: 2, - } - return { owner, nonOwner, @@ -46,7 +43,6 @@ describe("LibBridgeData", function () { testMessage, testTypes, testVar, - MessageStatus, } } @@ -54,7 +50,6 @@ describe("LibBridgeData", function () { it("should return properly hashed message", async function () { const { libData, testMessage, testTypes } = await deployLibBridgeDataFixture() - // dummy struct to test with const testVar = [TAIKO_BRIDGE_MESSAGE, testMessage] const hashed = await libData.hashMessage(testMessage) @@ -72,7 +67,7 @@ describe("LibBridgeData", function () { const { libData } = await deployLibBridgeDataFixture() // dummy struct to test with - const testMessage = { + const testMessage: Message = { id: 0, sender: "0xDA1Ea1362475997419D2055dD43390AEE34c6c37", srcChainId: 31336, @@ -98,8 +93,7 @@ describe("LibBridgeData", function () { describe("updateMessageStatus()", async function () { it("should emit upon successful change, and value should be changed correctly", async function () { - const { libData, testMessage, MessageStatus } = - await deployLibBridgeDataFixture() + const { libData, testMessage } = await deployLibBridgeDataFixture() const signal = await libData.hashMessage(testMessage) @@ -113,8 +107,7 @@ describe("LibBridgeData", function () { }) it("unchanged MessageStatus should not emit event", async function () { - const { libData, testMessage, MessageStatus } = - await deployLibBridgeDataFixture() + const { libData, testMessage } = await deployLibBridgeDataFixture() const signal = await libData.hashMessage(testMessage) diff --git a/packages/protocol/test/bridge/libs/LibBridgeInvoke.test.ts b/packages/protocol/test/bridge/libs/LibBridgeInvoke.test.ts index 7a9c444adbe..eba7340ca9e 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeInvoke.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeInvoke.test.ts @@ -1,11 +1,15 @@ -// import { expect } from "chai" import { expect } from "chai" import { ethers } from "hardhat" import { Message } from "../../utils/message" +import { + TestLibBridgeData, + TestLibBridgeInvoke, + TestReceiver, +} from "../../../typechain" describe("LibBridgeInvoke", function () { async function deployLibBridgeDataFixture() { - const libData = await ( + const libData: TestLibBridgeData = await ( await ethers.getContractFactory("TestLibBridgeData") ).deploy() return { libData } @@ -14,7 +18,7 @@ describe("LibBridgeInvoke", function () { async function deployLibBridgeInvokeFixture() { const [owner, nonOwner] = await ethers.getSigners() - const libInvoke = await ( + const libInvoke: TestLibBridgeInvoke = await ( await ethers.getContractFactory("TestLibBridgeInvoke") ) .connect(owner) @@ -89,7 +93,7 @@ describe("LibBridgeInvoke", function () { const { libData } = await deployLibBridgeDataFixture() - const testReceiver = await ( + const testReceiver: TestReceiver = await ( await ethers.getContractFactory("TestReceiver") ).deploy() diff --git a/packages/protocol/test/bridge/libs/LibBridgeProcess.test.ts b/packages/protocol/test/bridge/libs/LibBridgeProcess.test.ts index 6d176e0f6d8..c593331b539 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeProcess.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeProcess.test.ts @@ -1,8 +1,14 @@ import { expect } from "chai" import hre, { ethers } from "hardhat" import { Message } from "../../utils/message" -import { AddressManager, Bridge } from "../../../typechain" -import { getSlot } from "../../../tasks/utils" +import { + AddressManager, + EtherVault, + LibTrieProof, + TestLibBridgeData, + TestLibBridgeProcess, +} from "../../../typechain" +import { getSlot, MessageStatus } from "../../../tasks/utils" import * as fs from "fs" import * as path from "path" const helpers = require("@nomicfoundation/hardhat-network-helpers") @@ -51,7 +57,9 @@ describe("LibBridgeProcess", function () { ).deploy() await addressManager.init() - const etherVault = await (await ethers.getContractFactory("EtherVault")) + const etherVault: EtherVault = await ( + await ethers.getContractFactory("EtherVault") + ) .connect(etherVaultOwner) .deploy() @@ -71,7 +79,7 @@ describe("LibBridgeProcess", function () { value: ethers.utils.parseEther("10.0"), }) - const libTrieLink = await ( + const libTrieLink: LibTrieProof = await ( await ethers.getContractFactory("LibTrieProof") ) .connect(owner) @@ -89,7 +97,7 @@ describe("LibBridgeProcess", function () { .deploy() await libProcessLink.deployed() - const libProcess = await ( + const libProcess: TestLibBridgeProcess = await ( await ethers.getContractFactory("TestLibBridgeProcess", { libraries: { LibBridgeProcess: libProcessLink.address, @@ -101,7 +109,7 @@ describe("LibBridgeProcess", function () { await libProcess.init(addressManager.address) - const testLibData = await ( + const testLibData: TestLibBridgeData = await ( await ethers.getContractFactory("TestLibBridgeData") ).deploy() @@ -109,18 +117,11 @@ describe("LibBridgeProcess", function () { .connect(etherVaultOwner) .authorize(libProcess.address, true) - const MessageStatus = { - NEW: 0, - RETRIABLE: 1, - DONE: 2, - } - return { owner, srcChainId, messageOwner, libProcess, - MessageStatus, stateSlot, blockChainId, nonOwner, @@ -128,60 +129,6 @@ describe("LibBridgeProcess", function () { addressManager, } } - async function deployBridgeFixture() { - const [owner, etherVault] = await ethers.getSigners() - - const addressManager: AddressManager = await ( - await ethers.getContractFactory("AddressManager") - ).deploy() - await addressManager.init() - - const chainId = hre.network.config.chainId ?? 0 - - await addressManager.setAddress( - `${chainId}.ether_vault`, - etherVault.address - ) - - const libTrieProof = await ( - await ethers.getContractFactory("LibTrieProof") - ).deploy() - - const libBridgeProcess = await ( - await ethers.getContractFactory("LibBridgeProcess", { - libraries: { - LibTrieProof: libTrieProof.address, - }, - }) - ).deploy() - - const libBridgeRetry = await ( - await ethers.getContractFactory("LibBridgeRetry") - ).deploy() - - const BridgeFactory = await ethers.getContractFactory("Bridge", { - libraries: { - LibBridgeProcess: libBridgeProcess.address, - LibBridgeRetry: libBridgeRetry.address, - LibTrieProof: libTrieProof.address, - }, - }) - - const bridge: Bridge = await BridgeFactory.connect(owner).deploy() - - await bridge.init(addressManager.address) - - const headerSync = await ( - await ethers.getContractFactory("TestHeaderSync") - ).deploy() - - await addressManager.setAddress(`${chainId}.taiko`, headerSync.address) - - return { - bridge, - headerSync, - } - } describe("processMessage()", async function () { it("should throw if gaslimit == 0 & msg.sender != message.owner", async function () { @@ -239,7 +186,6 @@ describe("LibBridgeProcess", function () { libProcess, testLibData, blockChainId, - MessageStatus, stateSlot, } = await deployLibBridgeProcessFixture() @@ -271,51 +217,6 @@ describe("LibBridgeProcess", function () { libProcess.processMessage(message, ethers.constants.HashZero) ).to.be.revertedWith("B:status") }) - - it("should throw if signal has not been received", async function () { - const { - owner, - srcChainId, - nonOwner, - libProcess, - blockChainId, - addressManager, - } = await deployLibBridgeProcessFixture() - const { bridge } = await deployBridgeFixture() - - await addressManager.setAddress( - `${srcChainId}.bridge`, - bridge.address - ) - - const message: Message = { - id: 1, - sender: owner.address, - srcChainId: srcChainId, - destChainId: blockChainId, - owner: owner.address, - to: nonOwner.address, - refundAddress: owner.address, - depositValue: 1, - callValue: 1, - processingFee: 1, - gasLimit: 100000000, - data: ethers.constants.HashZero, - memo: "", - } - await expect( - libProcess.processMessage(message, ethers.constants.HashZero) - ).to.be.reverted - - // Reverts because there is no HeaderSync, can't test without - }) - - it("if message fails, refund should go to the intended account and amount", async function () { - await deployLibBridgeProcessFixture() - }) - - it("should pass properly, message should be processed and marked DONE", async function () { - await deployLibBridgeProcessFixture() - }) + // Remaining test cases require integration, will be covered in Bridge.test.ts }) }) diff --git a/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts b/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts index e71335c825b..10fbf2ac93a 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts @@ -1,7 +1,15 @@ import { expect } from "chai" import hre, { ethers } from "hardhat" import { Message } from "../../utils/message" -import { getSlot, decode } from "../../../tasks/utils" +import { getSlot, decode, MessageStatus } from "../../../tasks/utils" +import { + AddressManager, + EtherVault, + TestBadReceiver, + TestLibBridgeData, + TestLibBridgeRetry, + TestReceiver, +} from "../../../typechain" const helpers = require("@nomicfoundation/hardhat-network-helpers") describe("LibBridgeRetry", function () { @@ -9,17 +17,19 @@ describe("LibBridgeRetry", function () { const [owner, refundAddress, nonOwner, etherVaultOwner] = await ethers.getSigners() - const addressManager = await ( + const addressManager: AddressManager = await ( await ethers.getContractFactory("AddressManager") ).deploy() await addressManager.init() - const badAddressManager = await ( + const badAddressManager: AddressManager = await ( await ethers.getContractFactory("AddressManager") ).deploy() await badAddressManager.init() - const etherVault = await (await ethers.getContractFactory("EtherVault")) + const etherVault: EtherVault = await ( + await ethers.getContractFactory("EtherVault") + ) .connect(etherVaultOwner) .deploy() @@ -57,11 +67,11 @@ describe("LibBridgeRetry", function () { }) ).connect(owner) - const libRetry = await libRetryFactory.deploy() + const libRetry: TestLibBridgeRetry = await libRetryFactory.deploy() await libRetry.init(addressManager.address) await libRetry.deployed() - const badLibRetry = await libRetryFactory.deploy() + const badLibRetry: TestLibBridgeRetry = await libRetryFactory.deploy() await badLibRetry.init(badAddressManager.address) await badLibRetry.deployed() @@ -69,23 +79,16 @@ describe("LibBridgeRetry", function () { .connect(etherVaultOwner) .authorize(libRetry.address, true) - const testLibData = await ( + const testLibData: TestLibBridgeData = await ( await ethers.getContractFactory("TestLibBridgeData") ).deploy() - const MessageStatus = { - NEW: 0, - RETRIABLE: 1, - DONE: 2, - } - return { owner, refundAddress, nonOwner, libRetry, testLibData, - MessageStatus, badLibRetry, } } @@ -167,15 +170,10 @@ describe("LibBridgeRetry", function () { }) it("if etherVault resolves to address(0), retry should fail and messageStatus should not change if not lastAttempt since no ether received", async function () { - const { - owner, - refundAddress, - testLibData, - MessageStatus, - badLibRetry, - } = await deployLibBridgeRetryFixture() - - const testReceiver = await ( + const { owner, refundAddress, testLibData, badLibRetry } = + await deployLibBridgeRetryFixture() + + const testReceiver: TestReceiver = await ( await ethers.getContractFactory("TestReceiver") ).deploy() @@ -224,15 +222,10 @@ describe("LibBridgeRetry", function () { }) it("should fail, but since lastAttempt == true messageStatus should be set to DONE", async function () { - const { - owner, - libRetry, - refundAddress, - testLibData, - MessageStatus, - } = await deployLibBridgeRetryFixture() - - const testBadReceiver = await ( + const { owner, libRetry, refundAddress, testLibData } = + await deployLibBridgeRetryFixture() + + const testBadReceiver: TestBadReceiver = await ( await ethers.getContractFactory("TestBadReceiver") ).deploy() @@ -284,15 +277,10 @@ describe("LibBridgeRetry", function () { }) it("should succeed, set message status to done, invoke message succesfsully", async function () { - const { - owner, - libRetry, - refundAddress, - testLibData, - MessageStatus, - } = await deployLibBridgeRetryFixture() - - const testReceiver = await ( + const { owner, libRetry, refundAddress, testLibData } = + await deployLibBridgeRetryFixture() + + const testReceiver: TestReceiver = await ( await ethers.getContractFactory("TestReceiver") ).deploy() diff --git a/packages/protocol/test/bridge/libs/LibBridgeSend.test.ts b/packages/protocol/test/bridge/libs/LibBridgeSend.test.ts index 12b26ad3e86..9f831b41fb6 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeSend.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeSend.test.ts @@ -1,22 +1,42 @@ import { expect } from "chai" import hre, { ethers } from "hardhat" +import { + AddressManager, + TestLibBridgeSend, + EtherVault, +} from "../../../typechain" import { Message } from "../../utils/message" describe("LibBridgeSend", function () { async function deployLibBridgeSendFixture() { - const [owner, nonOwner, etherVault] = await ethers.getSigners() + const [owner, nonOwner, etherVaultOwner] = await ethers.getSigners() - const addressManager = await ( + const addressManager: AddressManager = await ( await ethers.getContractFactory("AddressManager") ).deploy() await addressManager.init() + + const etherVault: EtherVault = await ( + await ethers.getContractFactory("EtherVault") + ) + .connect(etherVaultOwner) + .deploy() + + await etherVault.deployed() + await etherVault.init(addressManager.address) + + await owner.sendTransaction({ + to: etherVault.address, + value: ethers.utils.parseEther("10.0"), + }) + const blockChainId = hre.network.config.chainId ?? 0 await addressManager.setAddress( `${blockChainId}.ether_vault`, etherVault.address ) - const libSend = await ( + const libSend: TestLibBridgeSend = await ( await ethers.getContractFactory("TestLibBridgeSend") ) .connect(owner) diff --git a/packages/protocol/test/bridge/libs/LibBridgeSignal.test.ts b/packages/protocol/test/bridge/libs/LibBridgeSignal.test.ts index 12e2aef883d..7ea337d1f1b 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeSignal.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeSignal.test.ts @@ -1,6 +1,7 @@ import { expect } from "chai" import { ethers } from "hardhat" -import { TestLibBridgeSignal } from "../../../typechain" +import { TestLibBridgeData, TestLibBridgeSignal } from "../../../typechain" +import { Message } from "../../utils/message" describe("integration:LibBridgeSignal", function () { async function deployLibBridgeSignalFixture() { @@ -10,7 +11,7 @@ describe("integration:LibBridgeSignal", function () { await ethers.getContractFactory("TestLibBridgeSignal") ).deploy() - const testMessage = { + const testMessage: Message = { id: 1, sender: owner.address, srcChainId: 1, @@ -29,7 +30,7 @@ describe("integration:LibBridgeSignal", function () { return { owner, nonOwner, libSignal, testMessage } } async function deployLibBridgeDataFixture() { - const libData = await ( + const libData: TestLibBridgeData = await ( await ethers.getContractFactory("TestLibBridgeData") ).deploy() return { libData } @@ -57,6 +58,7 @@ describe("integration:LibBridgeSignal", function () { ).to.be.revertedWith("B:signal") }) }) + describe("isSignalSent()", async function () { it("properly sent message should be received", async function () { const { owner, libSignal, testMessage } = From 854deec5d1c22862183291ffed71c734dbff990b Mon Sep 17 00:00:00 2001 From: RogerLamTd Date: Tue, 22 Nov 2022 13:35:09 -0500 Subject: [PATCH 02/57] linting --- packages/protocol/test/bridge/Bridge.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/protocol/test/bridge/Bridge.test.ts b/packages/protocol/test/bridge/Bridge.test.ts index 450ec0c3a5d..f5b1edeed5c 100644 --- a/packages/protocol/test/bridge/Bridge.test.ts +++ b/packages/protocol/test/bridge/Bridge.test.ts @@ -1,7 +1,6 @@ import { expect } from "chai" import { ethers } from "hardhat" import { BigNumber, Signer } from "ethers" -import { ethers } from "hardhat" import { Message } from "../utils/message" import { AddressManager, @@ -14,7 +13,6 @@ import { import { Block, BlockHeader, EthGetProofResponse } from "../utils/rpc" import RLP from "rlp" - async function deployBridge( signer: Signer, addressManager: AddressManager, From f44f86c70b60bc44dae7c527b393242fab115c98 Mon Sep 17 00:00:00 2001 From: RogerLamTd Date: Tue, 22 Nov 2022 13:50:46 -0500 Subject: [PATCH 03/57] fix libsend tests --- packages/protocol/test/bridge/libs/LibBridgeSend.test.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/protocol/test/bridge/libs/LibBridgeSend.test.ts b/packages/protocol/test/bridge/libs/LibBridgeSend.test.ts index 9f831b41fb6..23416ecd0a3 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeSend.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeSend.test.ts @@ -25,11 +25,6 @@ describe("LibBridgeSend", function () { await etherVault.deployed() await etherVault.init(addressManager.address) - await owner.sendTransaction({ - to: etherVault.address, - value: ethers.utils.parseEther("10.0"), - }) - const blockChainId = hre.network.config.chainId ?? 0 await addressManager.setAddress( `${blockChainId}.ether_vault`, @@ -43,6 +38,9 @@ describe("LibBridgeSend", function () { .deploy() await libSend.init(addressManager.address) + await etherVault + .connect(etherVaultOwner) + .authorize(libSend.address, true) const srcChainId = 1 From 2552093b83124b7dfe699763880e40614dd7a5a5 Mon Sep 17 00:00:00 2001 From: RogerLamTd Date: Wed, 23 Nov 2022 23:03:08 -0500 Subject: [PATCH 04/57] added missing test case LibBridgeRetry --- .../test/bridge/libs/LibBridgeRetry.test.ts | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts b/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts index 2e90baa2ab5..2ded2ca7894 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts @@ -90,6 +90,7 @@ describe("LibBridgeRetry", function () { libRetry, testLibData, badLibRetry, + etherVault, } } @@ -276,6 +277,63 @@ describe("LibBridgeRetry", function () { ) }) + it("should fail, messageStatus is still RETRIABLE and balance is returned to etherVault", async function () { + const { owner, libRetry, testLibData, etherVault } = + await deployLibBridgeRetryFixture() + + const testBadReceiver: TestBadReceiver = await ( + await ethers.getContractFactory("TestBadReceiver") + ).deploy() + + await testBadReceiver.deployed() + + const destChainId = 5 + const message: Message = { + id: 1, + sender: owner.address, + srcChainId: 1, + destChainId: destChainId, + owner: owner.address, + to: testBadReceiver.address, + refundAddress: ethers.constants.AddressZero, + depositValue: 0, + callValue: 1, + processingFee: 1, + gasLimit: 300000, + data: ethers.constants.HashZero, + memo: "", + } + + const signal = await testLibData.hashMessage(message) + + await helpers.setStorageAt( + libRetry.address, + await getSlot(hre, signal, 202), + MessageStatus.RETRIABLE + ) + + const originalBalance = await ethers.provider.getBalance( + etherVault.address + ) + await libRetry.retryMessage(message, false) + const balancePlusRefund = await ethers.provider.getBalance( + etherVault.address + ) + + expect( + await decode( + hre, + "uint256", + await ethers.provider.getStorageAt( + libRetry.address, + getSlot(hre, signal, 202) + ) + ) + ).to.equal(MessageStatus.RETRIABLE.toString()) + + expect(balancePlusRefund).to.be.equal(originalBalance) + }) + it("should succeed, set message status to done, invoke message succesfsully", async function () { const { owner, libRetry, refundAddress, testLibData } = await deployLibBridgeRetryFixture() From 149680893ce14089ff18d3347ce98fd0a567403d Mon Sep 17 00:00:00 2001 From: RogerLamTd Date: Sat, 26 Nov 2022 13:26:25 -0500 Subject: [PATCH 05/57] retryMessage tests attempt --- packages/protocol/test/bridge/Bridge.test.ts | 236 ++++++++++++++++++- 1 file changed, 233 insertions(+), 3 deletions(-) diff --git a/packages/protocol/test/bridge/Bridge.test.ts b/packages/protocol/test/bridge/Bridge.test.ts index f5b1edeed5c..9516f50a77c 100644 --- a/packages/protocol/test/bridge/Bridge.test.ts +++ b/packages/protocol/test/bridge/Bridge.test.ts @@ -1,5 +1,5 @@ import { expect } from "chai" -import { ethers } from "hardhat" +import hre, { ethers } from "hardhat" import { BigNumber, Signer } from "ethers" import { Message } from "../utils/message" import { @@ -10,6 +10,7 @@ import { TestHeaderSync, TestLibBridgeData, } from "../../typechain" +import { getSlot } from "../../tasks/utils" import { Block, BlockHeader, EthGetProofResponse } from "../utils/rpc" import RLP from "rlp" @@ -1019,11 +1020,240 @@ describe("integration:Bridge", function () { [{ header: blockHeader, proof: encodedProof }] ) - // ROGER: this is where we at, we need now to deploy a custom TestHeaderSync that implements - // IHeaderSync where we can manually save synced headers. await l2Bridge.processMessage(message, signalProof, { gasLimit: BigNumber.from(2000000), }) }) }) + + describe("isMessageSent()", function () { + it("should return false, since no message was sent", async function () { + const { owner, l1Bridge, srcChainId, enabledDestChainId } = + await deployBridgeFixture() + + 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: "", + } + + 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 { owner, l1Bridge, srcChainId, enabledDestChainId } = + await deployBridgeFixture() + + 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: "", + } + + 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 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: 1, + data: ethers.constants.HashZero, + memo: "", + } + + 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 = ethers.utils.keccak256( + ethers.utils.solidityPack( + ["address", "bytes32"], + [sender, signal] + ) + ) + + // use this instead of ethers.provider.getBlock() beccause it doesnt have stateRoot + // in the response + const block: Block = await ethers.provider.send( + "eth_getBlockByNumber", + ["latest", false] + ) + + await headerSync.setSyncedHeader(block.hash) + + 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, + } + + // rpc call to get the merkle proof what value is at key on the bridge contract + const proof: EthGetProofResponse = await ethers.provider.send( + "eth_getProof", + [l1Bridge.address, [key], block.hash] + ) + + // RLP encode the proof together for LibTrieProof to decode + const encodedProof = ethers.utils.defaultAbiCoder.encode( + ["bytes", "bytes"], + [ + RLP.encode(proof.accountProof), + RLP.encode(proof.storageProof[0].proof), + ] + ) + // encode the SignalProof struct from LibBridgeSignal + 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)", + ], + [{ header: blockHeader, proof: encodedProof }] + ) + + // @Jeff I'm attempting to make it process a message in such a way + // that it fails intentionally and is marked retriable. It doesn't + // seem to be working, any clues on how to go about this? + + // I set message.gasLimit to 1 and try to have a nonOwner call the + // processMessage() so it should use the message.gasLimit + // but it just processes normally outside the processMessage + // block + + // await expect( + // await l2Bridge + // .connect(l2NonOwner) + // .processMessage(message, signalProof, { + // gasLimit: BigNumber.from(2000000), + // }) + // ).to.be.reverted + + console.log("message processed") + + return { + message, + l2Signer, + l2NonOwner, + l1Bridge, + l2Bridge, + addressManager, + headerSync, + owner, + nonOwner, + srcChainId, + enabledDestChainId, + l1EtherVault, + l2EtherVault, + signal, + } + } + it.only("setup message to fail first processMessage", async function () { + const { l2Bridge, signal } = await retriableMessageSetup() + const storageSlot = await getSlot(hre, signal, 202) + const storageAt = await ethers.provider.send("eth_getStorageAt", [ + l2Bridge.address, + storageSlot, + "latest", + ]) + console.log(storageAt) + }) + }) }) From 0bf5fddb47b3088890520acaed10082b4c38dbef Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Mon, 28 Nov 2022 12:56:37 -0800 Subject: [PATCH 06/57] retriable message --- .../bridge/libs/LibBridgeProcess.sol | 1 + packages/protocol/test/bridge/Bridge.test.ts | 41 ++++++++++--------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol b/packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol index ad3f4d59345..d13520d606d 100644 --- a/packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol +++ b/packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol @@ -94,6 +94,7 @@ library LibBridgeProcess { uint256 gasLimit = msg.sender == message.owner ? gasleft() : message.gasLimit; + bool success = LibBridgeInvoke.invokeMessageCall( state, message, diff --git a/packages/protocol/test/bridge/Bridge.test.ts b/packages/protocol/test/bridge/Bridge.test.ts index 9516f50a77c..0aa87e09903 100644 --- a/packages/protocol/test/bridge/Bridge.test.ts +++ b/packages/protocol/test/bridge/Bridge.test.ts @@ -7,6 +7,7 @@ import { Bridge, EtherVault, LibTrieProof, + TestBadReceiver, TestHeaderSync, TestLibBridgeData, } from "../../typechain" @@ -1110,13 +1111,21 @@ describe("integration:Bridge", function () { 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: owner.address, + to: testBadReceiver.address, refundAddress: owner.address, depositValue: 1000, callValue: 1000, @@ -1128,7 +1137,7 @@ describe("integration:Bridge", function () { const expectedAmount = m.depositValue + m.callValue + m.processingFee - const tx = await l1Bridge.sendMessage(m, { + const tx = await l1Bridge.connect(owner).sendMessage(m, { value: expectedAmount, }) @@ -1201,6 +1210,7 @@ describe("integration:Bridge", function () { RLP.encode(proof.storageProof[0].proof), ] ) + // encode the SignalProof struct from LibBridgeSignal const signalProof = ethers.utils.defaultAbiCoder.encode( [ @@ -1209,24 +1219,16 @@ describe("integration:Bridge", function () { [{ header: blockHeader, proof: encodedProof }] ) - // @Jeff I'm attempting to make it process a message in such a way - // that it fails intentionally and is marked retriable. It doesn't - // seem to be working, any clues on how to go about this? - - // I set message.gasLimit to 1 and try to have a nonOwner call the - // processMessage() so it should use the message.gasLimit - // but it just processes normally outside the processMessage - // block - - // await expect( - // await l2Bridge - // .connect(l2NonOwner) - // .processMessage(message, signalProof, { - // gasLimit: BigNumber.from(2000000), - // }) - // ).to.be.reverted + await l2Bridge + .connect(l2NonOwner) + .processMessage(message, signalProof, { + gasLimit: BigNumber.from(2000000), + }) - console.log("message processed") + 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, @@ -1253,6 +1255,7 @@ describe("integration:Bridge", function () { storageSlot, "latest", ]) + console.log(storageAt) }) }) From d6a1ff8852dfb268aabba554e99f43cc7b023e70 Mon Sep 17 00:00:00 2001 From: RogerLamTd Date: Tue, 29 Nov 2022 18:21:55 -0500 Subject: [PATCH 07/57] added setup for testing isSignalReceived() --- .../test/bridge/TestSignalSender.sol | 17 ++ packages/protocol/test/bridge/Bridge.test.ts | 227 +++++++++++++++++- 2 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 packages/protocol/contracts/test/bridge/TestSignalSender.sol diff --git a/packages/protocol/contracts/test/bridge/TestSignalSender.sol b/packages/protocol/contracts/test/bridge/TestSignalSender.sol new file mode 100644 index 00000000000..9008f65fc9a --- /dev/null +++ b/packages/protocol/contracts/test/bridge/TestSignalSender.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +// +// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ +// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ +// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ +// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ +// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ +// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ +pragma solidity ^0.8.9; + +import "../../bridge/IBridge.sol"; + +contract TestSignalSender { + function sendSignal(IBridge bridge, bytes32 signal) public { + bridge.sendSignal((signal)); + } +} \ No newline at end of file diff --git a/packages/protocol/test/bridge/Bridge.test.ts b/packages/protocol/test/bridge/Bridge.test.ts index 0aa87e09903..4faabb1ccf9 100644 --- a/packages/protocol/test/bridge/Bridge.test.ts +++ b/packages/protocol/test/bridge/Bridge.test.ts @@ -1247,7 +1247,7 @@ describe("integration:Bridge", function () { signal, } } - it.only("setup message to fail first processMessage", async function () { + it("setup message to fail first processMessage", async function () { const { l2Bridge, signal } = await retriableMessageSetup() const storageSlot = await getSlot(hre, signal, 202) const storageAt = await ethers.provider.send("eth_getStorageAt", [ @@ -1259,4 +1259,229 @@ describe("integration:Bridge", function () { console.log(storageAt) }) }) + + describe("isMessageReceived()", function () { + it("should return true", async function () { + const { + owner, + l1Bridge, + srcChainId, + enabledDestChainId, + l2Bridge, + headerSync, + } = await deployBridgeFixture() + + 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: "", + } + + 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 = ethers.utils.keccak256( + ethers.utils.solidityPack( + ["address", "bytes32"], + [sender, signal] + ) + ) + + // use this instead of ethers.provider.getBlock() beccause it doesnt have stateRoot + // in the response + const block: Block = await ethers.provider.send( + "eth_getBlockByNumber", + ["latest", false] + ) + + await headerSync.setSyncedHeader(block.hash) + + 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, + } + + // rpc call to get the merkle proof what value is at key on the bridge contract + const proof: EthGetProofResponse = await ethers.provider.send( + "eth_getProof", + [l1Bridge.address, [key], block.hash] + ) + + // RLP encode the proof together for LibTrieProof to decode + const encodedProof = ethers.utils.defaultAbiCoder.encode( + ["bytes", "bytes"], + [ + RLP.encode(proof.accountProof), + RLP.encode(proof.storageProof[0].proof), + ] + ) + // encode the SignalProof struct from LibBridgeSignal + 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)", + ], + [{ header: blockHeader, proof: encodedProof }] + ) + + expect( + await l2Bridge.isMessageReceived( + signal, + srcChainId, + signalProof + ) + ).to.be.eq(true) + }) + }) + + describe("isSignalReceived()", function () { + it.only("should return true", async function () { + const { nonOwner, l1Bridge, srcChainId, l2Bridge, headerSync } = + await deployBridgeFixture() + + const testSignalSender = await ( + await ethers.getContractFactory("TestSignalSender") + ).deploy() + + const tx = await testSignalSender.sendSignal( + l2Bridge.address, + ethers.utils.keccak256(ethers.utils.hexlify(1000)), + { gasLimit: BigNumber.from(2000000) } + ) + + // const tx = await l1Bridge + // .connect(nonOwner) + // .sendSignal(ethers.utils.keccak256(ethers.utils.hexlify(1000))) + + const receipt = await tx.wait() + + const [messageSentEvent] = receipt.events as any as Event[] + + const { signal } = (messageSentEvent as any).args + + const sender = await nonOwner.getAddress() + + const key = ethers.utils.keccak256( + ethers.utils.solidityPack( + ["address", "bytes32"], + [sender, signal] + ) + ) + + // // use this instead of ethers.provider.getBlock() beccause it doesnt have stateRoot + // // in the response + // const block: Block = await ethers.provider.send( + // "eth_getBlockByNumber", + // ["latest", false] + // ) + + // await headerSync.setSyncedHeader(block.hash) + + // 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, + // } + + // // get storageValue for the key + // const storageValue = await ethers.provider.getStorageAt( + // l2Bridge.address, + // key, + // block.number + // ) + // // // make sure it equals 1 so our proof will pass + // expect(storageValue).to.be.eq( + // "0x0000000000000000000000000000000000000000000000000000000000000001" + // ) + // // rpc call to get the merkle proof what value is at key on the bridge contract + // const proof: EthGetProofResponse = await ethers.provider.send( + // "eth_getProof", + // [l2Bridge.address, [key], block.hash] + // ) + + // // RLP encode the proof together for LibTrieProof to decode + // const encodedProof = ethers.utils.defaultAbiCoder.encode( + // ["bytes", "bytes"], + // [ + // RLP.encode(proof.accountProof), + // RLP.encode(proof.storageProof[0].proof), + // ] + // ) + // // encode the SignalProof struct from LibBridgeSignal + // 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)", + // ], + // [{ header: blockHeader, proof: encodedProof }] + // ) + + // expect( + // await l2Bridge.isSignalReceived( + // signal, + // srcChainId, + // sender, + // signalProof + // ) + // ).to.be.eq(true) + }) + }) }) From 8e5c5940d4f4da5e41dd2616fc64069882a10ef3 Mon Sep 17 00:00:00 2001 From: RogerLamTd Date: Tue, 29 Nov 2022 19:27:30 -0500 Subject: [PATCH 08/57] fix --- packages/protocol/test/bridge/Bridge.test.ts | 113 ++++++++++--------- 1 file changed, 62 insertions(+), 51 deletions(-) diff --git a/packages/protocol/test/bridge/Bridge.test.ts b/packages/protocol/test/bridge/Bridge.test.ts index 4faabb1ccf9..a911c592225 100644 --- a/packages/protocol/test/bridge/Bridge.test.ts +++ b/packages/protocol/test/bridge/Bridge.test.ts @@ -1376,16 +1376,26 @@ describe("integration:Bridge", function () { describe("isSignalReceived()", function () { it.only("should return true", async function () { - const { nonOwner, l1Bridge, srcChainId, l2Bridge, headerSync } = - await deployBridgeFixture() + const { + nonOwner, + l1Bridge, + srcChainId, + l2Bridge, + headerSync, + l2Signer, + } = await deployBridgeFixture() const testSignalSender = await ( await ethers.getContractFactory("TestSignalSender") - ).deploy() + ) + .connect(l2Signer) + .deploy() + + const signal = ethers.utils.keccak256(ethers.utils.hexlify(1000)) const tx = await testSignalSender.sendSignal( l2Bridge.address, - ethers.utils.keccak256(ethers.utils.hexlify(1000)), + signal, { gasLimit: BigNumber.from(2000000) } ) @@ -1393,13 +1403,14 @@ describe("integration:Bridge", function () { // .connect(nonOwner) // .sendSignal(ethers.utils.keccak256(ethers.utils.hexlify(1000))) - const receipt = await tx.wait() + // const receipt = await tx.wait() + await tx.wait() - const [messageSentEvent] = receipt.events as any as Event[] + // const [messageSentEvent] = receipt.events as any as Event[] - const { signal } = (messageSentEvent as any).args + // const { signal } = (messageSentEvent as any).args - const sender = await nonOwner.getAddress() + const sender = await l2Signer.getAddress() const key = ethers.utils.keccak256( ethers.utils.solidityPack( @@ -1408,50 +1419,50 @@ describe("integration:Bridge", function () { ) ) - // // use this instead of ethers.provider.getBlock() beccause it doesnt have stateRoot - // // in the response - // const block: Block = await ethers.provider.send( - // "eth_getBlockByNumber", - // ["latest", false] - // ) + // use this instead of ethers.provider.getBlock() beccause it doesnt have stateRoot + // in the response + const block: Block = await ethers.provider.send( + "eth_getBlockByNumber", + ["latest", false] + ) - // await headerSync.setSyncedHeader(block.hash) - - // 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, - // } - - // // get storageValue for the key - // const storageValue = await ethers.provider.getStorageAt( - // l2Bridge.address, - // key, - // block.number - // ) - // // // make sure it equals 1 so our proof will pass - // expect(storageValue).to.be.eq( - // "0x0000000000000000000000000000000000000000000000000000000000000001" - // ) + await headerSync.setSyncedHeader(block.hash) + + 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, + } + + // get storageValue for the key + const storageValue = await ethers.provider.getStorageAt( + l2Bridge.address, + key, + block.number + ) + // // make sure it equals 1 so our proof will pass + expect(storageValue).to.be.eq( + "0x0000000000000000000000000000000000000000000000000000000000000001" + ) // // rpc call to get the merkle proof what value is at key on the bridge contract // const proof: EthGetProofResponse = await ethers.provider.send( // "eth_getProof", From d3b96788ff1d096ce70a5c4adcf0ba5c4255f304 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 1 Dec 2022 16:34:26 -0800 Subject: [PATCH 09/57] lint --- packages/protocol/test/bridge/Bridge.test.ts | 86 +++++++------------ .../test/bridge/libs/LibBridgeData.test.ts | 1 - 2 files changed, 31 insertions(+), 56 deletions(-) diff --git a/packages/protocol/test/bridge/Bridge.test.ts b/packages/protocol/test/bridge/Bridge.test.ts index a911c592225..7bffcf862a9 100644 --- a/packages/protocol/test/bridge/Bridge.test.ts +++ b/packages/protocol/test/bridge/Bridge.test.ts @@ -1376,40 +1376,15 @@ describe("integration:Bridge", function () { describe("isSignalReceived()", function () { it.only("should return true", async function () { - const { - nonOwner, - l1Bridge, - srcChainId, - l2Bridge, - headerSync, - l2Signer, - } = await deployBridgeFixture() - - const testSignalSender = await ( - await ethers.getContractFactory("TestSignalSender") - ) - .connect(l2Signer) - .deploy() - - const signal = ethers.utils.keccak256(ethers.utils.hexlify(1000)) + const { l2Bridge, headerSync, l2Signer } = + await deployBridgeFixture() - const tx = await testSignalSender.sendSignal( - l2Bridge.address, - signal, - { gasLimit: BigNumber.from(2000000) } - ) + const signal = ethers.utils.hexlify(ethers.utils.randomBytes(32)) - // const tx = await l1Bridge - // .connect(nonOwner) - // .sendSignal(ethers.utils.keccak256(ethers.utils.hexlify(1000))) + const tx = await l2Bridge.connect(l2Signer).sendSignal(signal) - // const receipt = await tx.wait() await tx.wait() - // const [messageSentEvent] = receipt.events as any as Event[] - - // const { signal } = (messageSentEvent as any).args - const sender = await l2Signer.getAddress() const key = ethers.utils.keccak256( @@ -1421,40 +1396,40 @@ describe("integration:Bridge", function () { // use this instead of ethers.provider.getBlock() beccause it doesnt have stateRoot // in the response - const block: Block = await ethers.provider.send( + const block: Block = await l2Signer.provider.send( "eth_getBlockByNumber", ["latest", false] ) await headerSync.setSyncedHeader(block.hash) - 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, - } + // 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, + // } // get storageValue for the key - const storageValue = await ethers.provider.getStorageAt( + const storageValue = await l2Signer.provider.getStorageAt( l2Bridge.address, key, block.number @@ -1463,6 +1438,7 @@ describe("integration:Bridge", function () { expect(storageValue).to.be.eq( "0x0000000000000000000000000000000000000000000000000000000000000001" ) + // // rpc call to get the merkle proof what value is at key on the bridge contract // const proof: EthGetProofResponse = await ethers.provider.send( // "eth_getProof", diff --git a/packages/protocol/test/bridge/libs/LibBridgeData.test.ts b/packages/protocol/test/bridge/libs/LibBridgeData.test.ts index 401f55b1f91..591ed845a51 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeData.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeData.test.ts @@ -4,7 +4,6 @@ import { TestLibBridgeData } from "../../../typechain" import { MessageStatus } from "../../../tasks/utils" import { Message } from "../../utils/message" import { TAIKO_BRIDGE_MESSAGE } from "../../constants/messages" -import { MessageStatus } from "../../../tasks/utils" describe("LibBridgeData", function () { async function deployLibBridgeDataFixture() { From 89e9d781225bfd75dc7afe46ca60152addd8e6d3 Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Fri, 2 Dec 2022 11:14:39 +0800 Subject: [PATCH 10/57] Copy impl tokenomics (#373) --- .gitignore | 6 +- packages/protocol/.solhintignore | 2 + packages/protocol/contracts/L1/LibData.sol | 59 +++-- packages/protocol/contracts/L1/TaikoL1.sol | 216 ++++++++++++------ packages/protocol/contracts/L1/TkoToken.sol | 8 +- .../protocol/contracts/L1/v1/V1Events.sol | 4 + .../protocol/contracts/L1/v1/V1Finalizing.sol | 87 ------- .../protocol/contracts/L1/v1/V1Proposing.sol | 189 ++++++++++----- .../protocol/contracts/L1/v1/V1Proving.sol | 206 ++++++++--------- packages/protocol/contracts/L1/v1/V1Utils.sol | 167 +++++++++++++- .../protocol/contracts/L1/v1/V1Verifying.sol | 200 ++++++++++++++++ packages/protocol/contracts/L2/V1TaikoL2.sol | 93 ++++---- packages/protocol/contracts/bridge/Bridge.sol | 65 +++--- .../contracts/bridge/BridgedERC20.sol | 12 +- .../protocol/contracts/bridge/EtherVault.sol | 6 +- .../protocol/contracts/bridge/TokenVault.sol | 42 ++-- .../bridge/libs/LibBridgeProcess.sol | 27 +-- .../contracts/bridge/libs/LibBridgeRetry.sol | 7 +- .../contracts/bridge/libs/LibBridgeSignal.sol | 14 +- .../contracts/common/AddressResolver.sol | 3 +- .../contracts/common/EssentialContract.sol | 1 + .../contracts/libs/LibAnchorSignature.sol | 8 +- .../contracts/libs/LibBlockHeader.sol | 2 +- .../protocol/contracts/libs/LibConstants.sol | 47 ++-- .../contracts/libs/LibInvalidTxList.sol | 19 +- .../protocol/contracts/libs/LibTxDecoder.sol | 5 +- .../protocol/contracts/libs/LibTxUtils.sol | 4 +- .../test/libs/TestLibAnchorSignature.sol | 4 +- packages/protocol/docs/L1/LibData.md | 13 +- packages/protocol/docs/L1/TaikoL1.md | 36 ++- packages/protocol/docs/L1/v1/V1Finalizing.md | 8 +- packages/protocol/docs/L1/v1/V1Proposing.md | 14 +- packages/protocol/docs/L1/v1/V1Proving.md | 4 +- packages/protocol/docs/L1/v1/V1Utils.md | 19 ++ packages/protocol/docs/L2/V1TaikoL2.md | 2 +- .../protocol/docs/libs/LibAnchorSignature.md | 8 +- packages/protocol/docs/libs/LibConstants.md | 82 ++++--- .../protocol/docs/libs/LibInvalidTxList.md | 8 +- packages/protocol/hardhat.config.ts | 4 +- packages/protocol/tasks/config.ts | 4 +- packages/protocol/tasks/deploy_L1.ts | 26 +-- packages/protocol/test/L1/TaikoL1.test.ts | 26 +-- .../test/bridge/libs/LibBridgeData.test.ts | 6 +- packages/protocol/test/constants/messages.ts | 4 +- .../test/genesis/generate_genesis.test.ts | 2 +- .../protocol/test/libs/LibTxUtils.test.ts | 2 +- 46 files changed, 1162 insertions(+), 609 deletions(-) delete mode 100644 packages/protocol/contracts/L1/v1/V1Finalizing.sol create mode 100644 packages/protocol/contracts/L1/v1/V1Verifying.sol create mode 100644 packages/protocol/docs/L1/v1/V1Utils.md diff --git a/.gitignore b/.gitignore index 6c624e2e1f7..a718c289141 100644 --- a/.gitignore +++ b/.gitignore @@ -109,4 +109,8 @@ dist # vscode .vscode/ -.pdf \ No newline at end of file +# python +__pycache__/ + +# whitepaper +.pdf diff --git a/packages/protocol/.solhintignore b/packages/protocol/.solhintignore index f64817aa3b1..b15d9ed051c 100644 --- a/packages/protocol/.solhintignore +++ b/packages/protocol/.solhintignore @@ -3,4 +3,6 @@ contracts/aux/tokens/ERC20Upgradeable.sol contracts/test/TestLibRLPReader.sol contracts/test/TestLibRLPWriter.sol contracts/libs/Lib1559Math.sol +contracts/libs/LibAddress.sol +contracts/libs/LibMath.sol **/contracts/thirdparty/**/*.sol \ No newline at end of file diff --git a/packages/protocol/contracts/L1/LibData.sol b/packages/protocol/contracts/L1/LibData.sol index cdfb3134016..6c6094b649c 100644 --- a/packages/protocol/contracts/L1/LibData.sol +++ b/packages/protocol/contracts/L1/LibData.sol @@ -26,17 +26,22 @@ library LibData { uint64 commitSlot; } + // 3 slots struct ProposedBlock { bytes32 metaHash; + uint256 deposit; + address proposer; + uint64 proposedAt; } + // 3 + n slots struct ForkChoice { bytes32 blockHash; - uint64 proposedAt; uint64 provenAt; address[] provers; } + // This struct takes 9 slots. struct State { // block id => block hash mapping(uint256 => bytes32) l2Hashes; @@ -46,39 +51,61 @@ library LibData { mapping(uint256 => mapping(bytes32 => ForkChoice)) forkChoices; // proposer => commitSlot => hash(commitHash, commitHeight) mapping(address => mapping(uint256 => bytes32)) commits; - mapping(address => bool) provers; // Whitelisted provers - uint64 statusBits; + // Never or rarely changed uint64 genesisHeight; + uint64 genesisTimestamp; + uint64 __reservedA1; + uint64 statusBits; // rarely change + // Changed when a block is proposed or proven/finalized + uint256 feeBase; + // Changed when a block is proposed + uint64 nextBlockId; + uint64 lastProposedAt; // Timestamp when the last block is proposed. + uint64 avgBlockTime; // The block time moving average + uint64 __avgGasLimit; // the block gaslimit moving average, not updated. + // Changed when a block is proven/finalized uint64 latestVerifiedHeight; uint64 latestVerifiedId; - uint64 nextBlockId; + uint64 avgProofTime; // the proof time moving average + uint64 __reservedC1; + // Reserved + uint256[42] __gap; + } + + struct TentativeState { + mapping(address => bool) proposers; // Whitelisted proposers + mapping(address => bool) provers; // Whitelisted provers + bool whitelistProposers; + bool whitelistProvers; + // // Reserved + uint256[46] __gap; } function saveProposedBlock( - LibData.State storage s, + LibData.State storage state, uint256 id, ProposedBlock memory blk ) internal { - s.proposedBlocks[id % LibConstants.TAIKO_MAX_PROPOSED_BLOCKS] = blk; + state.proposedBlocks[id % LibConstants.K_MAX_NUM_BLOCKS] = blk; } function getProposedBlock( - State storage s, + State storage state, uint256 id ) internal view returns (ProposedBlock storage) { - return s.proposedBlocks[id % LibConstants.TAIKO_MAX_PROPOSED_BLOCKS]; + return state.proposedBlocks[id % LibConstants.K_MAX_NUM_BLOCKS]; } function getL2BlockHash( - State storage s, + State storage state, uint256 number ) internal view returns (bytes32) { - require(number <= s.latestVerifiedHeight, "L1:id"); - return s.l2Hashes[number]; + require(number <= state.latestVerifiedHeight, "L1:id"); + return state.l2Hashes[number]; } function getStateVariables( - State storage s + State storage state ) internal view @@ -89,10 +116,10 @@ library LibData { uint64 nextBlockId ) { - genesisHeight = s.genesisHeight; - latestVerifiedHeight = s.latestVerifiedHeight; - latestVerifiedId = s.latestVerifiedId; - nextBlockId = s.nextBlockId; + genesisHeight = state.genesisHeight; + latestVerifiedHeight = state.latestVerifiedHeight; + latestVerifiedId = state.latestVerifiedId; + nextBlockId = state.nextBlockId; } function hashMetadata( diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 07c149d785d..e776cd083dd 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -8,36 +8,40 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; -import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; - -import "../common/ConfigManager.sol"; import "../common/EssentialContract.sol"; import "../common/IHeaderSync.sol"; import "../libs/LibAnchorSignature.sol"; import "./LibData.sol"; import "./v1/V1Events.sol"; -import "./v1/V1Finalizing.sol"; import "./v1/V1Proposing.sol"; import "./v1/V1Proving.sol"; import "./v1/V1Utils.sol"; +import "./v1/V1Verifying.sol"; /** * @author dantaik */ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { using LibData for LibData.State; - using LibTxDecoder for bytes; - using SafeCastUpgradeable for uint256; LibData.State public state; - uint256[43] private __gap; + LibData.TentativeState public tentative; + uint256[50] private __gap; function init( address _addressManager, - bytes32 _genesisBlockHash + bytes32 _genesisBlockHash, + uint256 _feeBase ) external initializer { EssentialContract._init(_addressManager); - V1Finalizing.init(state, _genesisBlockHash); + V1Verifying.init({ + state: state, + genesisBlockHash: _genesisBlockHash, + feeBase: _feeBase + }); + + tentative.whitelistProposers = false; + tentative.whitelistProvers = true; } /** @@ -74,19 +78,25 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { * transactions in the L2 block. */ function proposeBlock(bytes[] calldata inputs) external nonReentrant { - V1Proposing.proposeBlock(state, inputs); - V1Finalizing.verifyBlocks( - state, - LibConstants.TAIKO_MAX_VERIFICATIONS_PER_TX, - false - ); + V1Proposing.proposeBlock({ + state: state, + tentative: tentative, + resolver: AddressResolver(this), + inputs: inputs + }); + V1Verifying.verifyBlocks({ + state: state, + resolver: AddressResolver(this), + maxBlocks: LibConstants.K_MAX_VERIFICATIONS_PER_TX, + checkHalt: false + }); } /** * Prove a block is valid with a zero-knowledge proof, a transaction * merkel proof, and a receipt merkel proof. * - * @param blockIndex The index of the block to prove. This is also used + * @param blockId The index of the block to prove. This is also used * to select the right implementation version. * @param inputs A list of data input: * - inputs[0] is an abi-encoded object with various information @@ -98,22 +108,29 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { */ function proveBlock( - uint256 blockIndex, + uint256 blockId, bytes[] calldata inputs ) external nonReentrant { - V1Proving.proveBlock(state, AddressResolver(this), blockIndex, inputs); - V1Finalizing.verifyBlocks( - state, - LibConstants.TAIKO_MAX_VERIFICATIONS_PER_TX, - false - ); + V1Proving.proveBlock({ + state: state, + tentative: tentative, + resolver: AddressResolver(this), + blockId: blockId, + inputs: inputs + }); + V1Verifying.verifyBlocks({ + state: state, + resolver: AddressResolver(this), + maxBlocks: LibConstants.K_MAX_VERIFICATIONS_PER_TX, + checkHalt: false + }); } /** * Prove a block is invalid with a zero-knowledge proof and a receipt * merkel proof. * - * @param blockIndex The index of the block to prove. This is also used to + * @param blockId The index of the block to prove. This is also used to * select the right implementation version. * @param inputs A list of data input: * - inputs[0] An Evidence object with various information regarding @@ -124,20 +141,22 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { * be the only transaction in the L2 block. */ function proveBlockInvalid( - uint256 blockIndex, + uint256 blockId, bytes[] calldata inputs ) external nonReentrant { - V1Proving.proveBlockInvalid( - state, - AddressResolver(this), - blockIndex, - inputs - ); - V1Finalizing.verifyBlocks( - state, - LibConstants.TAIKO_MAX_VERIFICATIONS_PER_TX, - false - ); + V1Proving.proveBlockInvalid({ + state: state, + tentative: tentative, + resolver: AddressResolver(this), + blockId: blockId, + inputs: inputs + }); + V1Verifying.verifyBlocks({ + state: state, + resolver: AddressResolver(this), + maxBlocks: LibConstants.K_MAX_VERIFICATIONS_PER_TX, + checkHalt: false + }); } /** @@ -146,10 +165,49 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { */ function verifyBlocks(uint256 maxBlocks) external nonReentrant { require(maxBlocks > 0, "L1:maxBlocks"); - V1Finalizing.verifyBlocks(state, maxBlocks, true); + V1Verifying.verifyBlocks({ + state: state, + resolver: AddressResolver(this), + maxBlocks: maxBlocks, + checkHalt: true + }); + } + + /** + * Enable or disable proposer and prover whitelisting + * @param whitelistProposers True to enable proposer whitelisting. + * @param whitelistProvers True to enable prover whitelisting. + */ + function enableWhitelisting( + bool whitelistProposers, + bool whitelistProvers + ) public onlyOwner { + V1Utils.enableWhitelisting({ + tentative: tentative, + whitelistProposers: whitelistProposers, + whitelistProvers: whitelistProvers + }); + } + + /** + * Add or remove a proposer from the whitelist. + * + * @param proposer The proposer to be added or removed. + * @param whitelisted True to add; remove otherwise. + */ + function whitelistProposer( + address proposer, + bool whitelisted + ) public onlyOwner { + V1Utils.whitelistProposer({ + tentative: tentative, + proposer: proposer, + whitelisted: whitelisted + }); } - /* Add or remove a prover from the whitelist. + /** + * Add or remove a prover from the whitelist. * * @param prover The prover to be added or removed. * @param whitelisted True to add; remove otherwise. @@ -158,7 +216,11 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { address prover, bool whitelisted ) public onlyOwner { - V1Proving.whitelistProver(state, prover, whitelisted); + V1Utils.whitelistProver({ + tentative: tentative, + prover: prover, + whitelisted: whitelisted + }); } /** @@ -169,6 +231,18 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { V1Utils.halt(state, toHalt); } + /** + * Check whether a proposer is whitelisted. + * + * @param proposer The proposer. + * @return True if the proposer is whitelisted, false otherwise. + */ + function isProposerWhitelisted( + address proposer + ) public view returns (bool) { + return V1Utils.isProposerWhitelisted(tentative, proposer); + } + /** * Check whether a prover is whitelisted. * @@ -176,7 +250,23 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { * @return True if the prover is whitelisted, false otherwise. */ function isProverWhitelisted(address prover) public view returns (bool) { - return V1Proving.isProverWhitelisted(state, prover); + return V1Utils.isProverWhitelisted(tentative, prover); + } + + function getBlockFee() public view returns (uint256) { + (, uint fee, uint deposit) = V1Proposing.getBlockFee(state); + return fee + deposit; + } + + function getProofReward( + uint64 provenAt, + uint64 proposedAt + ) public view returns (uint256 reward) { + (, reward, ) = V1Verifying.getProofReward({ + state: state, + provenAt: provenAt, + proposedAt: proposedAt + }); } /** @@ -249,36 +339,30 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { pure returns ( uint256, // K_ZKPROOFS_PER_BLOCK - uint256, // TAIKO_CHAIN_ID - uint256, // TAIKO_MAX_PROPOSED_BLOCKS - uint256, // TAIKO_MAX_VERIFICATIONS_PER_TX - uint256, // K_COMMIT_DELAY_CONFIRMATIONS - uint256, // TAIKO_MAX_PROOFS_PER_FORK_CHOICE - uint256, // TAIKO_BLOCK_MAX_GAS_LIMIT - uint256, // TAIKO_BLOCK_MAX_TXS - bytes32, // TAIKO_BLOCK_DEADEND_HASH - uint256, // TAIKO_TXLIST_MAX_BYTES - uint256, // TAIKO_TX_MIN_GAS_LIMIT - uint256, // V1_ANCHOR_TX_GAS_LIMIT - bytes4, // V1_ANCHOR_TX_SELECTOR - bytes32 // V1_INVALIDATE_BLOCK_LOG_TOPIC + uint256, // K_CHAIN_ID + uint256, // K_MAX_NUM_BLOCKS + uint256, // K_MAX_VERIFICATIONS_PER_TX + uint256, // K_COMMIT_DELAY_CONFIRMS + uint256, // K_MAX_PROOFS_PER_FORK_CHOICE + uint256, // K_BLOCK_MAX_GAS_LIMIT + uint256, // K_BLOCK_MAX_TXS + uint256, // K_TXLIST_MAX_BYTES + uint256, // K_TX_MIN_GAS_LIMIT + uint256 // K_ANCHOR_TX_GAS_LIMIT ) { return ( LibConstants.K_ZKPROOFS_PER_BLOCK, - LibConstants.TAIKO_CHAIN_ID, - LibConstants.TAIKO_MAX_PROPOSED_BLOCKS, - LibConstants.TAIKO_MAX_VERIFICATIONS_PER_TX, - LibConstants.K_COMMIT_DELAY_CONFIRMATIONS, - LibConstants.TAIKO_MAX_PROOFS_PER_FORK_CHOICE, - LibConstants.TAIKO_BLOCK_MAX_GAS_LIMIT, - LibConstants.TAIKO_BLOCK_MAX_TXS, - LibConstants.TAIKO_BLOCK_DEADEND_HASH, - LibConstants.TAIKO_TXLIST_MAX_BYTES, - LibConstants.TAIKO_TX_MIN_GAS_LIMIT, - LibConstants.V1_ANCHOR_TX_GAS_LIMIT, - LibConstants.V1_ANCHOR_TX_SELECTOR, - LibConstants.V1_INVALIDATE_BLOCK_LOG_TOPIC + LibConstants.K_CHAIN_ID, + LibConstants.K_MAX_NUM_BLOCKS, + LibConstants.K_MAX_VERIFICATIONS_PER_TX, + LibConstants.K_COMMIT_DELAY_CONFIRMS, + LibConstants.K_MAX_PROOFS_PER_FORK_CHOICE, + LibConstants.K_BLOCK_MAX_GAS_LIMIT, + LibConstants.K_BLOCK_MAX_TXS, + LibConstants.K_TXLIST_MAX_BYTES, + LibConstants.K_TX_MIN_GAS_LIMIT, + LibConstants.K_ANCHOR_TX_GAS_LIMIT ); } } diff --git a/packages/protocol/contracts/L1/TkoToken.sol b/packages/protocol/contracts/L1/TkoToken.sol index a6a44c70972..58e8979f339 100644 --- a/packages/protocol/contracts/L1/TkoToken.sol +++ b/packages/protocol/contracts/L1/TkoToken.sol @@ -16,7 +16,7 @@ import "../libs/LibMath.sol"; import "../thirdparty/ERC20Upgradeable.sol"; /// @author dantaik -/// @dev This is Taiko's governance token. +/// @dev This is Taiko's governance and fee token. contract TkoToken is EssentialContract, ERC20Upgradeable, IMintableERC20 { using LibMath for uint256; using SafeCastUpgradeable for uint256; @@ -42,7 +42,11 @@ contract TkoToken is EssentialContract, ERC20Upgradeable, IMintableERC20 { /// amountMintToDAO and amountMintToDev shall be set to ~150,000,000. function init(address _addressManager) external initializer { EssentialContract._init(_addressManager); - ERC20Upgradeable.__ERC20_init("Taiko Token", "TKO", 18); + ERC20Upgradeable.__ERC20_init({ + name_: "Taiko Token", + symbol_: "TKO", + decimals_: 18 + }); } /********************* diff --git a/packages/protocol/contracts/L1/v1/V1Events.sol b/packages/protocol/contracts/L1/v1/V1Events.sol index 70ecde99b58..e9dd9d3a933 100644 --- a/packages/protocol/contracts/L1/v1/V1Events.sol +++ b/packages/protocol/contracts/L1/v1/V1Events.sol @@ -32,6 +32,10 @@ abstract contract V1Events { address prover ); + event WhitelistingEnabled(bool whitelistProposers, bool whitelistProvers); + + event ProposerWhitelisted(address indexed prover, bool whitelisted); + event ProverWhitelisted(address indexed prover, bool whitelisted); event Halted(bool halted); diff --git a/packages/protocol/contracts/L1/v1/V1Finalizing.sol b/packages/protocol/contracts/L1/v1/V1Finalizing.sol deleted file mode 100644 index f5f83c3f65b..00000000000 --- a/packages/protocol/contracts/L1/v1/V1Finalizing.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ -// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ -// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ -// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ -// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ -// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ -pragma solidity ^0.8.9; - -import "./V1Utils.sol"; - -/// @author dantaik -library V1Finalizing { - event BlockVerified(uint256 indexed id, bytes32 blockHash); - - event HeaderSynced( - uint256 indexed height, - uint256 indexed srcHeight, - bytes32 srcHash - ); - - function init(LibData.State storage s, bytes32 _genesisBlockHash) public { - s.l2Hashes[0] = _genesisBlockHash; - s.nextBlockId = 1; - s.genesisHeight = uint64(block.number); - - emit BlockVerified(0, _genesisBlockHash); - emit HeaderSynced(block.number, 0, _genesisBlockHash); - } - - function verifyBlocks( - LibData.State storage s, - uint256 maxBlocks, - bool checkHalt - ) public { - bool halted = V1Utils.isHalted(s); - if (checkHalt) { - assert(!halted); - } else if (halted) { - // skip finalizing blocks - return; - } - - uint64 latestL2Height = s.latestVerifiedHeight; - bytes32 latestL2Hash = s.l2Hashes[latestL2Height]; - uint64 processed = 0; - - for ( - uint256 i = s.latestVerifiedId + 1; - i < s.nextBlockId && processed <= maxBlocks; - i++ - ) { - LibData.ForkChoice storage fc = s.forkChoices[i][latestL2Hash]; - - // TODO(daniel): use the average proof-time. - if ( - block.timestamp <= - fc.provenAt + LibConstants.K_VERIFICATION_DELAY - ) { - // This block is proven but still needs to wait for verificaiton. - break; - } - - if (fc.blockHash == LibConstants.TAIKO_BLOCK_DEADEND_HASH) { - emit BlockVerified(i, 0); - } else if (fc.blockHash != 0) { - latestL2Height += 1; - latestL2Hash = fc.blockHash; - emit BlockVerified(i, latestL2Hash); - } else { - break; - } - processed += 1; - } - - if (processed > 0) { - s.latestVerifiedId += processed; - - if (latestL2Height > s.latestVerifiedHeight) { - s.latestVerifiedHeight = latestL2Height; - s.l2Hashes[latestL2Height] = latestL2Hash; - emit HeaderSynced(block.number, latestL2Height, latestL2Hash); - } - } - } -} diff --git a/packages/protocol/contracts/L1/v1/V1Proposing.sol b/packages/protocol/contracts/L1/v1/V1Proposing.sol index 8f0af0acea9..91544f57ca2 100644 --- a/packages/protocol/contracts/L1/v1/V1Proposing.sol +++ b/packages/protocol/contracts/L1/v1/V1Proposing.sol @@ -11,6 +11,7 @@ pragma solidity ^0.8.9; import "../../common/ConfigManager.sol"; import "../../libs/LibConstants.sol"; import "../../libs/LibTxDecoder.sol"; +import "../TkoToken.sol"; import "./V1Utils.sol"; /// @author dantaik @@ -26,104 +27,178 @@ library V1Proposing { ); event BlockProposed(uint256 indexed id, LibData.BlockMetadata meta); + modifier onlyWhitelistedProposer(LibData.TentativeState storage tentative) { + if (tentative.whitelistProposers) { + require(tentative.proposers[msg.sender], "L1:whitelist"); + } + _; + } + function commitBlock( - LibData.State storage s, + LibData.State storage state, uint64 commitSlot, bytes32 commitHash ) public { - assert(LibConstants.K_COMMIT_DELAY_CONFIRMATIONS > 0); + assert(LibConstants.K_COMMIT_DELAY_CONFIRMS > 0); // It's OK to allow committing block when the system is halt. // By not checking the halt status, this method will be cheaper. // - // assert(!V1Utils.isHalted(s)); + // assert(!V1Utils.isHalted(state)); bytes32 hash = _aggregateCommitHash(block.number, commitHash); - require(s.commits[msg.sender][commitSlot] != hash, "L1:committed"); - s.commits[msg.sender][commitSlot] = hash; + require(state.commits[msg.sender][commitSlot] != hash, "L1:committed"); + state.commits[msg.sender][commitSlot] = hash; - emit BlockCommitted(commitSlot, uint64(block.number), commitHash); + emit BlockCommitted({ + commitSlot: commitSlot, + commitHeight: uint64(block.number), + commitHash: commitHash + }); } function proposeBlock( - LibData.State storage s, + LibData.State storage state, + LibData.TentativeState storage tentative, + AddressResolver resolver, bytes[] calldata inputs - ) public { - assert(!V1Utils.isHalted(s)); + ) public onlyWhitelistedProposer(tentative) { + assert(!V1Utils.isHalted(state)); require(inputs.length == 2, "L1:inputs:size"); LibData.BlockMetadata memory meta = abi.decode( inputs[0], (LibData.BlockMetadata) ); - bytes calldata txList = inputs[1]; - + _verifyBlockCommit(state, meta); _validateMetadata(meta); - if (LibConstants.K_COMMIT_DELAY_CONFIRMATIONS > 0) { - bytes32 commitHash = _calculateCommitHash( - meta.beneficiary, - meta.txListHash + { + bytes calldata txList = inputs[1]; + // perform validation and populate some fields + require( + txList.length > 0 && + txList.length <= LibConstants.K_TXLIST_MAX_BYTES && + meta.txListHash == txList.hashTxList(), + "L1:txList" ); - require( - isCommitValid( - s, - meta.commitSlot, - meta.commitHeight, - commitHash - ), - "L1:notCommitted" + state.nextBlockId < + state.latestVerifiedId + LibConstants.K_MAX_NUM_BLOCKS, + "L1:tooMany" ); - if (meta.commitSlot == 0) { - // Special handling of slot 0 for refund; non-zero slots - // are supposed to managed by node software for reuse. - delete s.commits[msg.sender][meta.commitSlot]; + meta.id = state.nextBlockId; + meta.l1Height = block.number - 1; + meta.l1Hash = blockhash(block.number - 1); + meta.timestamp = uint64(block.timestamp); + + // if multiple L2 blocks included in the same L1 block, + // their block.mixHash fields for randomness will be the same. + meta.mixHash = bytes32(block.difficulty); + } + + uint256 deposit; + if (LibConstants.K_TOKENOMICS_ENABLED) { + uint256 newFeeBase; + { + uint256 fee; + (newFeeBase, fee, deposit) = getBlockFee(state); + TkoToken(resolver.resolve("tko_token")).burn( + msg.sender, + fee + deposit + ); } + // Update feeBase and avgBlockTime + state.feeBase = V1Utils.movingAverage({ + maValue: state.feeBase, + newValue: newFeeBase, + maf: LibConstants.K_FEE_BASE_MAF + }); + + state.avgBlockTime = V1Utils + .movingAverage({ + maValue: state.avgBlockTime, + newValue: meta.timestamp - state.lastProposedAt, + maf: LibConstants.K_BLOCK_TIME_MAF + }) + .toUint64(); } - require( - txList.length > 0 && - txList.length <= LibConstants.TAIKO_TXLIST_MAX_BYTES && - meta.txListHash == txList.hashTxList(), - "L1:txList" - ); - require( - s.nextBlockId <= - s.latestVerifiedId + LibConstants.TAIKO_MAX_PROPOSED_BLOCKS, - "L1:tooMany" + state.saveProposedBlock( + state.nextBlockId, + LibData.ProposedBlock({ + metaHash: LibData.hashMetadata(meta), + deposit: deposit, + proposer: msg.sender, + proposedAt: meta.timestamp + }) ); - meta.id = s.nextBlockId; - meta.l1Height = block.number - 1; - meta.l1Hash = blockhash(block.number - 1); - meta.timestamp = uint64(block.timestamp); - - // if multiple L2 blocks included in the same L1 block, - // their block.mixHash fields for randomness will be the same. - meta.mixHash = bytes32(block.difficulty); - - s.saveProposedBlock( - s.nextBlockId, - LibData.ProposedBlock({metaHash: LibData.hashMetadata(meta)}) - ); + state.lastProposedAt = meta.timestamp; + emit BlockProposed(state.nextBlockId++, meta); + } - emit BlockProposed(s.nextBlockId++, meta); + function getBlockFee( + LibData.State storage state + ) public view returns (uint256 newFeeBase, uint256 fee, uint256 deposit) { + (newFeeBase, ) = V1Utils.getTimeAdjustedFee({ + state: state, + isProposal: true, + tNow: uint64(block.timestamp), + tLast: state.lastProposedAt, + tAvg: state.avgBlockTime, + tCap: LibConstants.K_BLOCK_TIME_CAP + }); + fee = V1Utils.getSlotsAdjustedFee({ + state: state, + isProposal: true, + feeBase: newFeeBase + }); + fee = V1Utils.getBootstrapDiscountedFee(state, fee); + deposit = (fee * LibConstants.K_PROPOSER_DEPOSIT_PCTG) / 100; } function isCommitValid( - LibData.State storage s, + LibData.State storage state, uint256 commitSlot, uint256 commitHeight, bytes32 commitHash ) public view returns (bool) { - assert(LibConstants.K_COMMIT_DELAY_CONFIRMATIONS > 0); + assert(LibConstants.K_COMMIT_DELAY_CONFIRMS > 0); bytes32 hash = _aggregateCommitHash(commitHeight, commitHash); return - s.commits[msg.sender][commitSlot] == hash && - block.number >= - commitHeight + LibConstants.K_COMMIT_DELAY_CONFIRMATIONS; + state.commits[msg.sender][commitSlot] == hash && + block.number >= commitHeight + LibConstants.K_COMMIT_DELAY_CONFIRMS; + } + + function _verifyBlockCommit( + LibData.State storage state, + LibData.BlockMetadata memory meta + ) private { + if (LibConstants.K_COMMIT_DELAY_CONFIRMS == 0) { + return; + } + bytes32 commitHash = _calculateCommitHash( + meta.beneficiary, + meta.txListHash + ); + + require( + isCommitValid({ + state: state, + commitSlot: meta.commitSlot, + commitHeight: meta.commitHeight, + commitHash: commitHash + }), + "L1:notCommitted" + ); + + if (meta.commitSlot == 0) { + // Special handling of slot 0 for refund; non-zero slots + // are supposed to managed by node software for reuse. + delete state.commits[msg.sender][meta.commitSlot]; + } } function _validateMetadata(LibData.BlockMetadata memory meta) private pure { @@ -139,7 +214,7 @@ library V1Proposing { ); require( - meta.gasLimit <= LibConstants.TAIKO_BLOCK_MAX_GAS_LIMIT, + meta.gasLimit <= LibConstants.K_BLOCK_MAX_GAS_LIMIT, "L1:gasLimit" ); require(meta.extraData.length <= 32, "L1:extraData"); diff --git a/packages/protocol/contracts/L1/v1/V1Proving.sol b/packages/protocol/contracts/L1/v1/V1Proving.sol index d023fd7cce4..4182c022a51 100644 --- a/packages/protocol/contracts/L1/v1/V1Proving.sol +++ b/packages/protocol/contracts/L1/v1/V1Proving.sol @@ -44,22 +44,21 @@ library V1Proving { address prover ); - event ProverWhitelisted(address indexed prover, bool whitelisted); - - modifier onlyWhitelistedProver(LibData.State storage s) { - if (LibConstants.K_WHITELIST_PROVERS) { - require(s.provers[msg.sender], "L1:whitelist"); + modifier onlyWhitelistedProver(LibData.TentativeState storage tentative) { + if (tentative.whitelistProvers) { + require(tentative.provers[msg.sender], "L1:whitelist"); } _; } function proveBlock( - LibData.State storage s, + LibData.State storage state, + LibData.TentativeState storage tentative, AddressResolver resolver, - uint256 blockIndex, + uint256 blockId, bytes[] calldata inputs - ) public onlyWhitelistedProver(s) { - assert(!V1Utils.isHalted(s)); + ) public onlyWhitelistedProver(tentative) { + assert(!V1Utils.isHalted(state)); // Check and decode inputs require(inputs.length == 3, "L1:inputs:size"); @@ -68,7 +67,7 @@ library V1Proving { bytes calldata anchorReceipt = inputs[2]; // Check evidence - require(evidence.meta.id == blockIndex, "L1:id"); + require(evidence.meta.id == blockId, "L1:id"); require( evidence.proofs.length == 2 + LibConstants.K_ZKPROOFS_PER_BLOCK, "L1:proof:size" @@ -79,11 +78,11 @@ library V1Proving { require(_tx.txType == 0, "L1:anchor:type"); require( _tx.destination == - resolver.resolve(LibConstants.TAIKO_CHAIN_ID, "taiko"), + resolver.resolve(LibConstants.K_CHAIN_ID, "taiko"), "L1:anchor:dest" ); require( - _tx.gasLimit == LibConstants.V1_ANCHOR_TX_GAS_LIMIT, + _tx.gasLimit == LibConstants.K_ANCHOR_TX_GAS_LIMIT, "L1:anchor:gasLimit" ); @@ -95,7 +94,7 @@ library V1Proving { LibBytesUtils.equal( _tx.data, bytes.concat( - LibConstants.V1_ANCHOR_TX_SELECTOR, + LibConstants.K_ANCHOR_TX_SELECTOR, bytes32(evidence.meta.l1Height), evidence.meta.l1Hash ) @@ -105,12 +104,12 @@ library V1Proving { // Check anchor tx is the 1st tx in the block require( - LibMerkleTrie.verifyInclusionProof( - LibRLPWriter.writeUint(0), - anchorTx, - evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK], - evidence.header.transactionsRoot - ), + LibMerkleTrie.verifyInclusionProof({ + _key: LibRLPWriter.writeUint(0), + _value: anchorTx, + _proof: evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK], + _root: evidence.header.transactionsRoot + }), "L1:tx:proof" ); @@ -120,26 +119,33 @@ library V1Proving { require(receipt.status == 1, "L1:receipt:status"); require( - LibMerkleTrie.verifyInclusionProof( - LibRLPWriter.writeUint(0), - anchorReceipt, - evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK + 1], - evidence.header.receiptsRoot - ), + LibMerkleTrie.verifyInclusionProof({ + _key: LibRLPWriter.writeUint(0), + _value: anchorReceipt, + _proof: evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK + 1], + _root: evidence.header.receiptsRoot + }), "L1:receipt:proof" ); // ZK-prove block and mark block proven to be valid. - _proveBlock(s, resolver, evidence, evidence.meta, 0); + _proveBlock({ + state: state, + resolver: resolver, + evidence: evidence, + target: evidence.meta, + blockHashOverride: 0 + }); } function proveBlockInvalid( - LibData.State storage s, + LibData.State storage state, + LibData.TentativeState storage tentative, AddressResolver resolver, - uint256 blockIndex, + uint256 blockId, bytes[] calldata inputs - ) public onlyWhitelistedProver(s) { - assert(!V1Utils.isHalted(s)); + ) public onlyWhitelistedProver(tentative) { + assert(!V1Utils.isHalted(state)); // Check and decode inputs require(inputs.length == 3, "L1:inputs:size"); @@ -151,7 +157,7 @@ library V1Proving { bytes calldata invalidateBlockReceipt = inputs[2]; // Check evidence - require(evidence.meta.id == blockIndex, "L1:id"); + require(evidence.meta.id == blockId, "L1:id"); require( evidence.proofs.length == 1 + LibConstants.K_ZKPROOFS_PER_BLOCK, "L1:proof:size" @@ -167,63 +173,40 @@ library V1Proving { LibReceiptDecoder.Log memory log = receipt.logs[0]; require( log.contractAddress == - resolver.resolve(LibConstants.TAIKO_CHAIN_ID, "taiko"), + resolver.resolve(LibConstants.K_CHAIN_ID, "taiko"), "L1:receipt:addr" ); require(log.data.length == 0, "L1:receipt:data"); require( log.topics.length == 2 && - log.topics[0] == LibConstants.V1_INVALIDATE_BLOCK_LOG_TOPIC && + log.topics[0] == LibConstants.K_INVALIDATE_BLOCK_LOG_TOPIC && log.topics[1] == target.txListHash, "L1:receipt:topics" ); // Check the event is the first one in the throw-away block require( - LibMerkleTrie.verifyInclusionProof( - LibRLPWriter.writeUint(0), - invalidateBlockReceipt, - evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK], - evidence.header.receiptsRoot - ), + LibMerkleTrie.verifyInclusionProof({ + _key: LibRLPWriter.writeUint(0), + _value: invalidateBlockReceipt, + _proof: evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK], + _root: evidence.header.receiptsRoot + }), "L1:receipt:proof" ); // ZK-prove block and mark block proven as invalid. - _proveBlock( - s, - resolver, - evidence, - target, - LibConstants.TAIKO_BLOCK_DEADEND_HASH - ); - } - - function whitelistProver( - LibData.State storage s, - address prover, - bool enabled - ) public { - assert(LibConstants.K_WHITELIST_PROVERS); - require( - prover != address(0) && s.provers[prover] != enabled, - "L1:precondition" - ); - - s.provers[prover] = enabled; - emit ProverWhitelisted(prover, enabled); - } - - function isProverWhitelisted( - LibData.State storage s, - address prover - ) public view returns (bool) { - assert(LibConstants.K_WHITELIST_PROVERS); - return s.provers[prover]; + _proveBlock({ + state: state, + resolver: resolver, + evidence: evidence, + target: target, + blockHashOverride: LibConstants.K_BLOCK_DEADEND_HASH + }); } function _proveBlock( - LibData.State storage s, + LibData.State storage state, AddressResolver resolver, Evidence memory evidence, LibData.BlockMetadata memory target, @@ -232,68 +215,63 @@ library V1Proving { require(evidence.meta.id == target.id, "L1:height"); require(evidence.prover != address(0), "L1:prover"); - _checkMetadata(s, target); + _checkMetadata(state, target); _validateHeaderForMetadata(evidence.header, evidence.meta); bytes32 blockHash = evidence.header.hashBlockHeader(); for (uint256 i = 0; i < LibConstants.K_ZKPROOFS_PER_BLOCK; i++) { - LibZKP.verify( - ConfigManager(resolver.resolve("config_manager")).getValue( - string(abi.encodePacked("zk_vkey_", i)) - ), - evidence.proofs[i], - blockHash, - evidence.prover, - evidence.meta.txListHash - ); + LibZKP.verify({ + verificationKey: ConfigManager( + resolver.resolve("config_manager") + ).getValue(string(abi.encodePacked("zk_vkey_", i))), + zkproof: evidence.proofs[i], + blockHash: blockHash, + prover: evidence.prover, + txListHash: evidence.meta.txListHash + }); } - _markBlockProven( - s, - evidence.prover, - target, - evidence.header.parentHash, - blockHashOverride == 0 ? blockHash : blockHashOverride - ); + _markBlockProven({ + state: state, + prover: evidence.prover, + target: target, + parentHash: evidence.header.parentHash, + blockHash: blockHashOverride == 0 ? blockHash : blockHashOverride + }); } function _markBlockProven( - LibData.State storage s, + LibData.State storage state, address prover, LibData.BlockMetadata memory target, bytes32 parentHash, bytes32 blockHash ) private { - LibData.ForkChoice storage fc = s.forkChoices[target.id][parentHash]; + LibData.ForkChoice storage fc = state.forkChoices[target.id][ + parentHash + ]; if (fc.blockHash == 0) { fc.blockHash = blockHash; - fc.proposedAt = target.timestamp; fc.provenAt = uint64(block.timestamp); } else { - require( - fc.proposedAt == target.timestamp, - "L1:proposedAt:conflict" - ); - if (fc.blockHash != blockHash) { // We have a problem here: two proofs are both valid but claims // the new block has different hashes. - V1Utils.halt(s, true); + V1Utils.halt(state, true); return; } require( - fc.provers.length < - LibConstants.TAIKO_MAX_PROOFS_PER_FORK_CHOICE, + fc.provers.length < LibConstants.K_MAX_PROOFS_PER_FORK_CHOICE, "L1:proof:tooMany" ); - // No uncle proof can take more than 1.5x time the first proof did. - uint256 delay = fc.provenAt - fc.proposedAt; - uint256 deadline = fc.provenAt + delay / 2; - require(block.timestamp <= deadline, "L1:tooLate"); + require( + block.timestamp < V1Utils.uncleProofDeadline(state, fc), + "L1:tooLate" + ); for (uint256 i = 0; i < fc.provers.length; i++) { require(fc.provers[i] != prover, "L1:prover:dup"); @@ -302,14 +280,14 @@ library V1Proving { fc.provers.push(prover); - emit BlockProven( - target.id, - parentHash, - blockHash, - fc.proposedAt, - fc.provenAt, - prover - ); + emit BlockProven({ + id: target.id, + parentHash: parentHash, + blockHash: blockHash, + timestamp: target.timestamp, + provenAt: fc.provenAt, + prover: prover + }); } function _validateAnchorTxSignature( @@ -330,15 +308,15 @@ library V1Proving { } function _checkMetadata( - LibData.State storage s, + LibData.State storage state, LibData.BlockMetadata memory meta ) private view { require( - meta.id > s.latestVerifiedId && meta.id < s.nextBlockId, + meta.id > state.latestVerifiedId && meta.id < state.nextBlockId, "L1:meta:id" ); require( - LibData.getProposedBlock(s, meta.id).metaHash == + LibData.getProposedBlock(state, meta.id).metaHash == LibData.hashMetadata(meta), "L1:metaHash" ); @@ -353,7 +331,7 @@ library V1Proving { header.beneficiary == meta.beneficiary && header.difficulty == 0 && header.gasLimit == - meta.gasLimit + LibConstants.V1_ANCHOR_TX_GAS_LIMIT && + meta.gasLimit + LibConstants.K_ANCHOR_TX_GAS_LIMIT && header.gasUsed > 0 && header.timestamp == meta.timestamp && header.extraData.length == meta.extraData.length && diff --git a/packages/protocol/contracts/L1/v1/V1Utils.sol b/packages/protocol/contracts/L1/v1/V1Utils.sol index caab538f413..168254a4f25 100644 --- a/packages/protocol/contracts/L1/v1/V1Utils.sol +++ b/packages/protocol/contracts/L1/v1/V1Utils.sol @@ -15,28 +15,177 @@ import "../LibData.sol"; /// @author dantaik library V1Utils { + using LibMath for uint256; + uint64 public constant MASK_HALT = 1 << 0; + event WhitelistingEnabled(bool whitelistProposers, bool whitelistProvers); + event ProposerWhitelisted(address indexed proposer, bool whitelisted); + event ProverWhitelisted(address indexed prover, bool whitelisted); event Halted(bool halted); - function halt(LibData.State storage s, bool toHalt) public { - require(isHalted(s) != toHalt, "L1:precondition"); - setBit(s, MASK_HALT, toHalt); + function enableWhitelisting( + LibData.TentativeState storage tentative, + bool whitelistProposers, + bool whitelistProvers + ) internal { + tentative.whitelistProposers = whitelistProvers; + tentative.whitelistProvers = whitelistProvers; + emit WhitelistingEnabled(whitelistProposers, whitelistProvers); + } + + function whitelistProposer( + LibData.TentativeState storage tentative, + address proposer, + bool whitelisted + ) internal { + assert(tentative.whitelistProposers); + require( + proposer != address(0) && + tentative.proposers[proposer] != whitelisted, + "L1:precondition" + ); + + tentative.proposers[proposer] = whitelisted; + emit ProposerWhitelisted(proposer, whitelisted); + } + + function whitelistProver( + LibData.TentativeState storage tentative, + address prover, + bool whitelisted + ) internal { + assert(tentative.whitelistProvers); + require( + prover != address(0) && tentative.provers[prover] != whitelisted, + "L1:precondition" + ); + + tentative.provers[prover] = whitelisted; + emit ProverWhitelisted(prover, whitelisted); + } + + function halt(LibData.State storage state, bool toHalt) internal { + require(isHalted(state) != toHalt, "L1:precondition"); + setBit(state, MASK_HALT, toHalt); emit Halted(toHalt); } - function isHalted(LibData.State storage s) public view returns (bool) { - return isBitOne(s, MASK_HALT); + function isHalted( + LibData.State storage state + ) internal view returns (bool) { + return isBitOne(state, MASK_HALT); + } + + function isProposerWhitelisted( + LibData.TentativeState storage tentative, + address proposer + ) internal view returns (bool) { + assert(tentative.whitelistProposers); + return tentative.proposers[proposer]; + } + + function isProverWhitelisted( + LibData.TentativeState storage tentative, + address prover + ) internal view returns (bool) { + assert(tentative.whitelistProvers); + return tentative.provers[prover]; + } + + // Implement "Incentive Multipliers", see the whitepaper. + function getTimeAdjustedFee( + LibData.State storage state, + bool isProposal, + uint64 tNow, + uint64 tLast, + uint64 tAvg, + uint64 tCap + ) internal view returns (uint256 newFeeBase, uint256 tRelBp) { + if (tAvg == 0) { + newFeeBase = state.feeBase; + tRelBp = 0; + } else { + uint256 _tAvg = tAvg > tCap ? tCap : tAvg; + uint256 tGrace = (LibConstants.K_FEE_GRACE_PERIOD_PCTG * _tAvg) / + 100; + uint256 tMax = (LibConstants.K_FEE_MAX_PERIOD_PCTG * _tAvg) / 100; + uint256 a = tLast + tGrace; + uint256 b = tNow > a ? tNow - a : 0; + tRelBp = (b.min(tMax) * 10000) / tMax; // [0 - 10000] + uint256 alpha = 10000 + + ((LibConstants.K_REWARD_MULTIPLIER_PCTG - 100) * tRelBp) / + 100; + if (isProposal) { + newFeeBase = (state.feeBase * 10000) / alpha; // fee + } else { + newFeeBase = (state.feeBase * alpha) / 10000; // reward + } + } + } + + // Implement "Slot-availability Multipliers", see the whitepaper. + function getSlotsAdjustedFee( + LibData.State storage state, + bool isProposal, + uint256 feeBase + ) internal view returns (uint256) { + // m is the `n'` in the whitepaper + uint256 m = LibConstants.K_MAX_NUM_BLOCKS - + 1 + + LibConstants.K_FEE_PREMIUM_LAMDA; + // n is the number of unverified blocks + uint256 n = state.nextBlockId - state.latestVerifiedId - 1; + // k is `m − n + 1` or `m − n - 1`in the whitepaper + uint256 k = isProposal ? m - n - 1 : m - n + 1; + return (feeBase * (m - 1) * m) / (m - n) / k; + } + + // Implement "Bootstrap Discount Multipliers", see the whitepaper. + function getBootstrapDiscountedFee( + LibData.State storage state, + uint256 feeBase + ) internal view returns (uint256) { + uint256 halves = uint256(block.timestamp - state.genesisTimestamp) / + LibConstants.K_HALVING; + uint256 gamma = 1024 - (1024 >> halves); + return (feeBase * gamma) / 1024; + } + + // Returns a deterministic deadline for uncle proof submission. + function uncleProofDeadline( + LibData.State storage state, + LibData.ForkChoice storage fc + ) internal view returns (uint64) { + return fc.provenAt + state.avgProofTime; + } + + function movingAverage( + uint256 maValue, + uint256 newValue, + uint256 maf + ) internal pure returns (uint256) { + if (maValue == 0) { + return newValue; + } + uint256 _ma = (maValue * (maf - 1) + newValue) / maf; + return _ma > 0 ? _ma : maValue; } - function setBit(LibData.State storage s, uint64 mask, bool one) private { - s.statusBits = one ? s.statusBits | mask : s.statusBits & ~mask; + function setBit( + LibData.State storage state, + uint64 mask, + bool one + ) private { + state.statusBits = one + ? state.statusBits | mask + : state.statusBits & ~mask; } function isBitOne( - LibData.State storage s, + LibData.State storage state, uint64 mask ) private view returns (bool) { - return s.statusBits & mask != 0; + return state.statusBits & mask != 0; } } diff --git a/packages/protocol/contracts/L1/v1/V1Verifying.sol b/packages/protocol/contracts/L1/v1/V1Verifying.sol new file mode 100644 index 00000000000..7c0988ab661 --- /dev/null +++ b/packages/protocol/contracts/L1/v1/V1Verifying.sol @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: MIT +// +// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ +// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ +// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ +// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ +// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ +// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ +pragma solidity ^0.8.9; + +import "../../common/AddressResolver.sol"; +import "../TkoToken.sol"; +import "./V1Utils.sol"; + +/// @author dantaik +library V1Verifying { + using SafeCastUpgradeable for uint256; + event BlockVerified(uint256 indexed id, bytes32 blockHash); + + event HeaderSynced( + uint256 indexed height, + uint256 indexed srcHeight, + bytes32 srcHash + ); + + function init( + LibData.State storage state, + bytes32 genesisBlockHash, + uint256 feeBase + ) public { + require(feeBase > 0, "L1:feeBase"); + + state.genesisHeight = uint64(block.number); + state.genesisTimestamp = uint64(block.timestamp); + state.feeBase = feeBase; + state.nextBlockId = 1; + state.lastProposedAt = uint64(block.timestamp); + state.l2Hashes[0] = genesisBlockHash; + + emit BlockVerified(0, genesisBlockHash); + emit HeaderSynced(block.number, 0, genesisBlockHash); + } + + function verifyBlocks( + LibData.State storage state, + AddressResolver resolver, + uint256 maxBlocks, + bool checkHalt + ) public { + bool halted = V1Utils.isHalted(state); + if (checkHalt) { + require(!halted, "L1:halted"); + } else if (halted) { + // skip finalizing blocks + return; + } + + uint64 latestL2Height = state.latestVerifiedHeight; + bytes32 latestL2Hash = state.l2Hashes[latestL2Height]; + uint64 processed = 0; + TkoToken tkoToken; + + for ( + uint256 i = state.latestVerifiedId + 1; + i < state.nextBlockId && processed <= maxBlocks; + i++ + ) { + LibData.ForkChoice storage fc = state.forkChoices[i][latestL2Hash]; + LibData.ProposedBlock storage target = LibData.getProposedBlock( + state, + i + ); + + // Uncle proof can not take more than 2x time the first proof did. + if (!_isVerifiable(state, fc)) { + break; + } else { + if (LibConstants.K_TOKENOMICS_ENABLED) { + uint256 newFeeBase; + { + uint256 reward; + uint256 tRelBp; // [0-10000], see the whitepaper + (newFeeBase, reward, tRelBp) = getProofReward({ + state: state, + provenAt: fc.provenAt, + proposedAt: target.proposedAt + }); + + if (address(tkoToken) == address(0)) { + tkoToken = TkoToken(resolver.resolve("tko_token")); + } + + _rewardProvers(fc, reward, tkoToken); + _refundProposerDeposit(target, tRelBp, tkoToken); + } + // Update feeBase and avgProofTime + state.feeBase = V1Utils.movingAverage({ + maValue: state.feeBase, + newValue: newFeeBase, + maf: LibConstants.K_FEE_BASE_MAF + }); + + state.avgProofTime = V1Utils + .movingAverage({ + maValue: state.avgProofTime, + newValue: fc.provenAt - target.proposedAt, + maf: LibConstants.K_PROOF_TIME_MAF + }) + .toUint64(); + } + + if (fc.blockHash != LibConstants.K_BLOCK_DEADEND_HASH) { + latestL2Height += 1; + latestL2Hash = fc.blockHash; + } + processed += 1; + _cleanUp(fc); + emit BlockVerified(i, fc.blockHash); + } + } + + if (processed > 0) { + state.latestVerifiedId += processed; + + if (latestL2Height > state.latestVerifiedHeight) { + state.latestVerifiedHeight = latestL2Height; + state.l2Hashes[latestL2Height] = latestL2Hash; + emit HeaderSynced(block.number, latestL2Height, latestL2Hash); + } + } + } + + function getProofReward( + LibData.State storage state, + uint64 provenAt, + uint64 proposedAt + ) public view returns (uint256 newFeeBase, uint256 reward, uint256 tRelBp) { + (newFeeBase, tRelBp) = V1Utils.getTimeAdjustedFee({ + state: state, + isProposal: false, + tNow: provenAt, + tLast: proposedAt, + tAvg: state.avgProofTime, + tCap: LibConstants.K_PROOF_TIME_CAP + }); + reward = V1Utils.getSlotsAdjustedFee({ + state: state, + isProposal: false, + feeBase: newFeeBase + }); + reward = (reward * (10000 - LibConstants.K_REWARD_BURN_BP)) / 10000; + } + + function _refundProposerDeposit( + LibData.ProposedBlock storage target, + uint256 tRelBp, + TkoToken tkoToken + ) private { + uint refund = (target.deposit * (10000 - tRelBp)) / 10000; + if (refund > 0) { + tkoToken.mint(target.proposer, refund); + } + } + + function _rewardProvers( + LibData.ForkChoice storage fc, + uint256 reward, + TkoToken tkoToken + ) private { + uint sum = 2 ** fc.provers.length - 1; + for (uint i = 0; i < fc.provers.length; i++) { + uint weight = (1 << (fc.provers.length - i - 1)); + uint proverReward = (reward * weight) / sum; + + if (tkoToken.balanceOf(fc.provers[i]) == 0) { + // reduce reward if the prover has 0 TKO balance. + proverReward /= 2; + } + tkoToken.mint(fc.provers[i], proverReward); + } + } + + function _cleanUp(LibData.ForkChoice storage fc) private { + fc.blockHash = 0; + fc.provenAt = 0; + for (uint i = 0; i < fc.provers.length; i++) { + fc.provers[i] = address(0); + } + delete fc.provers; + } + + function _isVerifiable( + LibData.State storage state, + LibData.ForkChoice storage fc + ) private view returns (bool) { + return + fc.blockHash != 0 && + block.timestamp > V1Utils.uncleProofDeadline(state, fc); + } +} diff --git a/packages/protocol/contracts/L2/V1TaikoL2.sol b/packages/protocol/contracts/L2/V1TaikoL2.sol index 1674cbea0f2..f562b428bdf 100644 --- a/packages/protocol/contracts/L2/V1TaikoL2.sol +++ b/packages/protocol/contracts/L2/V1TaikoL2.sol @@ -52,12 +52,13 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { for (uint256 i = 0; i < 255 && number >= i + 2; i++) { ancestors[i] = blockhash(number - i - 2); } - publicInputHash = _hashPublicInputs( - block.chainid, - number, - 0, - ancestors - ); + + publicInputHash = _hashPublicInputs({ + chainId: block.chainid, + number: number, + feeBase: 0, + ancestors: ancestors + }); } /********************** @@ -68,7 +69,8 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { * bridging. This function will also check certain block-level global * variables because they are not part of the Trie structure. * - * Note that this transaction shall be the first transaction in every L2 block. + * Note that this transaction shall be the first transaction in every + * L2 block. * * @param l1Height The latest L1 block height when this block was proposed. * @param l1Hash The latest L1 block hash when this block was proposed. @@ -94,11 +96,11 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { LibInvalidTxList.Reason hint, uint256 txIdx ) external { - LibInvalidTxList.Reason reason = LibInvalidTxList.isTxListInvalid( - txList, - hint, - txIdx - ); + LibInvalidTxList.Reason reason = LibInvalidTxList.isTxListInvalid({ + encoded: txList, + hint: hint, + txIdx: txIdx + }); require(reason != LibInvalidTxList.Reason.OK, "L2:reason"); _checkPublicInputs(); @@ -134,41 +136,38 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { * Private Functions * **********************/ + // NOTE: If the order of the return values of this function changes, then + // some test cases that using this function in generate_genesis.test.ts + // may also needs to be modified accordingly. function getConstants() public pure returns ( uint256, // K_ZKPROOFS_PER_BLOCK - uint256, // TAIKO_CHAIN_ID - uint256, // TAIKO_MAX_PROPOSED_BLOCKS - uint256, // TAIKO_MAX_VERIFICATIONS_PER_TX - uint256, // K_COMMIT_DELAY_CONFIRMATIONS - uint256, // TAIKO_MAX_PROOFS_PER_FORK_CHOICE - uint256, // TAIKO_BLOCK_MAX_GAS_LIMIT - uint256, // TAIKO_BLOCK_MAX_TXS - bytes32, // TAIKO_BLOCK_DEADEND_HASH - uint256, // TAIKO_TXLIST_MAX_BYTES - uint256, // TAIKO_TX_MIN_GAS_LIMIT - uint256, // V1_ANCHOR_TX_GAS_LIMIT - bytes4, // V1_ANCHOR_TX_SELECTOR - bytes32 // V1_INVALIDATE_BLOCK_LOG_TOPIC + uint256, // K_CHAIN_ID + uint256, // K_MAX_NUM_BLOCKS + uint256, // K_MAX_VERIFICATIONS_PER_TX + uint256, // K_COMMIT_DELAY_CONFIRMS + uint256, // K_MAX_PROOFS_PER_FORK_CHOICE + uint256, // K_BLOCK_MAX_GAS_LIMIT + uint256, // K_BLOCK_MAX_TXS + uint256, // K_TXLIST_MAX_BYTES + uint256, // K_TX_MIN_GAS_LIMIT + uint256 // K_ANCHOR_TX_GAS_LIMIT ) { return ( LibConstants.K_ZKPROOFS_PER_BLOCK, - LibConstants.TAIKO_CHAIN_ID, - LibConstants.TAIKO_MAX_PROPOSED_BLOCKS, - LibConstants.TAIKO_MAX_VERIFICATIONS_PER_TX, - LibConstants.K_COMMIT_DELAY_CONFIRMATIONS, - LibConstants.TAIKO_MAX_PROOFS_PER_FORK_CHOICE, - LibConstants.TAIKO_BLOCK_MAX_GAS_LIMIT, - LibConstants.TAIKO_BLOCK_MAX_TXS, - LibConstants.TAIKO_BLOCK_DEADEND_HASH, - LibConstants.TAIKO_TXLIST_MAX_BYTES, - LibConstants.TAIKO_TX_MIN_GAS_LIMIT, - LibConstants.V1_ANCHOR_TX_GAS_LIMIT, - LibConstants.V1_ANCHOR_TX_SELECTOR, - LibConstants.V1_INVALIDATE_BLOCK_LOG_TOPIC + LibConstants.K_CHAIN_ID, + LibConstants.K_MAX_NUM_BLOCKS, + LibConstants.K_MAX_VERIFICATIONS_PER_TX, + LibConstants.K_COMMIT_DELAY_CONFIRMS, + LibConstants.K_MAX_PROOFS_PER_FORK_CHOICE, + LibConstants.K_BLOCK_MAX_GAS_LIMIT, + LibConstants.K_BLOCK_MAX_TXS, + LibConstants.K_TXLIST_MAX_BYTES, + LibConstants.K_TX_MIN_GAS_LIMIT, + LibConstants.K_ANCHOR_TX_GAS_LIMIT ); } @@ -187,12 +186,22 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { require( publicInputHash == - _hashPublicInputs(chainId, parentHeight, 0, ancestors), + _hashPublicInputs({ + chainId: chainId, + number: parentHeight, + feeBase: 0, + ancestors: ancestors + }), "L2:publicInputHash" ); ancestors[parentHeight % 255] = parentHash; - publicInputHash = _hashPublicInputs(chainId, number, 0, ancestors); + publicInputHash = _hashPublicInputs({ + chainId: chainId, + number: number, + feeBase: 0, + ancestors: ancestors + }); l2Hashes[parentHeight] = parentHash; } @@ -200,9 +209,9 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { function _hashPublicInputs( uint256 chainId, uint256 number, - uint256 baseFee, + uint256 feeBase, bytes32[255] memory ancestors ) private pure returns (bytes32) { - return keccak256(abi.encodePacked(chainId, number, baseFee, ancestors)); + return keccak256(abi.encodePacked(chainId, number, feeBase, ancestors)); } } diff --git a/packages/protocol/contracts/bridge/Bridge.sol b/packages/protocol/contracts/bridge/Bridge.sol index a1108360d83..4bd39766495 100644 --- a/packages/protocol/contracts/bridge/Bridge.sol +++ b/packages/protocol/contracts/bridge/Bridge.sol @@ -59,7 +59,12 @@ contract Bridge is EssentialContract, IBridge { function sendMessage( Message calldata message ) external payable nonReentrant returns (bytes32 signal) { - return LibBridgeSend.sendMessage(state, AddressResolver(this), message); + return + LibBridgeSend.sendMessage({ + state: state, + resolver: AddressResolver(this), + message: message + }); } function sendSignal(bytes32 signal) external override { @@ -72,12 +77,12 @@ contract Bridge is EssentialContract, IBridge { bytes calldata proof ) external nonReentrant { return - LibBridgeProcess.processMessage( - state, - AddressResolver(this), - message, - proof - ); + LibBridgeProcess.processMessage({ + state: state, + resolver: AddressResolver(this), + message: message, + proof: proof + }); } function retryMessage( @@ -85,19 +90,23 @@ contract Bridge is EssentialContract, IBridge { bool isLastAttempt ) external nonReentrant { return - LibBridgeRetry.retryMessage( - state, - AddressResolver(this), - message, - isLastAttempt - ); + LibBridgeRetry.retryMessage({ + state: state, + resolver: AddressResolver(this), + message: message, + isLastAttempt: isLastAttempt + }); } function enableDestChain( uint256 _chainId, bool enabled ) external nonReentrant { - LibBridgeSend.enableDestChain(state, _chainId, enabled); + LibBridgeSend.enableDestChain({ + state: state, + chainId: _chainId, + enabled: enabled + }); } /********************* @@ -115,13 +124,13 @@ contract Bridge is EssentialContract, IBridge { ) public view virtual override returns (bool) { address srcBridge = resolve(srcChainId, "bridge"); return - LibBridgeSignal.isSignalReceived( - AddressResolver(this), - srcBridge, - srcBridge, - signal, - proof - ); + LibBridgeSignal.isSignalReceived({ + resolver: AddressResolver(this), + srcBridge: srcBridge, + sender: srcBridge, + signal: signal, + proof: proof + }); } function isSignalSent( @@ -139,13 +148,13 @@ contract Bridge is EssentialContract, IBridge { ) public view virtual override returns (bool) { address srcBridge = resolve(srcChainId, "bridge"); return - LibBridgeSignal.isSignalReceived( - AddressResolver(this), - srcBridge, - sender, - signal, - proof - ); + LibBridgeSignal.isSignalReceived({ + resolver: AddressResolver(this), + srcBridge: srcBridge, + sender: sender, + signal: signal, + proof: proof + }); } function getMessageStatus( diff --git a/packages/protocol/contracts/bridge/BridgedERC20.sol b/packages/protocol/contracts/bridge/BridgedERC20.sol index bb1ef4268e9..e851894b6c3 100644 --- a/packages/protocol/contracts/bridge/BridgedERC20.sol +++ b/packages/protocol/contracts/bridge/BridgedERC20.sol @@ -8,11 +8,13 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; -import "../common/EssentialContract.sol"; -import "../thirdparty/ERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +// solhint-disable-next-line max-line-length import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; +import "../common/EssentialContract.sol"; +import "../thirdparty/ERC20Upgradeable.sol"; + /** * @author dantaik */ @@ -50,7 +52,11 @@ contract BridgedERC20 is "BE:params" ); EssentialContract._init(_addressManager); - ERC20Upgradeable.__ERC20_init(_name, _symbol, _decimals); + ERC20Upgradeable.__ERC20_init({ + name_: _name, + symbol_: _symbol, + decimals_: _decimals + }); srcToken = _srcToken; srcChainId = _srcChainId; } diff --git a/packages/protocol/contracts/bridge/EtherVault.sol b/packages/protocol/contracts/bridge/EtherVault.sol index cc874dbb9f9..310b2360b2c 100644 --- a/packages/protocol/contracts/bridge/EtherVault.sol +++ b/packages/protocol/contracts/bridge/EtherVault.sol @@ -8,11 +8,13 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; -import "../common/EssentialContract.sol"; -import "../libs/LibAddress.sol"; +// solhint-disable-next-line max-line-length import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/Create2Upgradeable.sol"; +import "../common/EssentialContract.sol"; +import "../libs/LibAddress.sol"; + /** * Vault that holds Ether. * @author dantaik diff --git a/packages/protocol/contracts/bridge/TokenVault.sol b/packages/protocol/contracts/bridge/TokenVault.sol index 94b4f3e7145..ede396b2181 100644 --- a/packages/protocol/contracts/bridge/TokenVault.sol +++ b/packages/protocol/contracts/bridge/TokenVault.sol @@ -8,6 +8,7 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; +// solhint-disable-next-line max-line-length import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/Create2Upgradeable.sol"; @@ -141,7 +142,12 @@ contract TokenVault is EssentialContract { value: msg.value }(message); - emit EtherSent(to, destChainId, message.depositValue, signal); + emit EtherSent({ + to: to, + destChainId: destChainId, + amount: message.depositValue, + signal: signal + }); } /** @@ -286,13 +292,13 @@ contract TokenVault is EssentialContract { type(BridgedERC20).creationCode ); - BridgedERC20(payable(bridgedToken)).init( - address(_addressManager), - canonicalToken.addr, - canonicalToken.chainId, - canonicalToken.decimals, - canonicalToken.symbol, - string( + BridgedERC20(payable(bridgedToken)).init({ + _addressManager: address(_addressManager), + _srcToken: canonicalToken.addr, + _srcChainId: canonicalToken.chainId, + _decimals: canonicalToken.decimals, + _symbol: canonicalToken.symbol, + _name: string( abi.encodePacked( canonicalToken.name, "(bridged", @@ -301,23 +307,21 @@ contract TokenVault is EssentialContract { ")" ) ) - ); + }); isBridgedToken[bridgedToken] = true; - bridgedToCanonical[bridgedToken] = canonicalToken; - canonicalToBridged[canonicalToken.chainId][ canonicalToken.addr ] = bridgedToken; - emit BridgedERC20Deployed( - canonicalToken.chainId, - canonicalToken.addr, - bridgedToken, - canonicalToken.symbol, - canonicalToken.name, - canonicalToken.decimals - ); + emit BridgedERC20Deployed({ + srcChainId: canonicalToken.chainId, + canonicalToken: canonicalToken.addr, + bridgedToken: bridgedToken, + canonicalTokenSymbol: canonicalToken.symbol, + canonicalTokenName: canonicalToken.name, + canonicalTokenDecimal: canonicalToken.decimals + }); } } diff --git a/packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol b/packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol index ad3f4d59345..9696e841cf1 100644 --- a/packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol +++ b/packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol @@ -60,14 +60,15 @@ library LibBridgeProcess { ); // Message must have been "received" on the destChain (current chain) address srcBridge = resolver.resolve(message.srcChainId, "bridge"); + require( - LibBridgeSignal.isSignalReceived( - resolver, - srcBridge, - srcBridge, - signal, - proof - ), + LibBridgeSignal.isSignalReceived({ + resolver: resolver, + srcBridge: srcBridge, + sender: srcBridge, + signal: signal, + proof: proof + }), "B:notReceived" ); @@ -94,12 +95,12 @@ library LibBridgeProcess { uint256 gasLimit = msg.sender == message.owner ? gasleft() : message.gasLimit; - bool success = LibBridgeInvoke.invokeMessageCall( - state, - message, - signal, - gasLimit - ); + bool success = LibBridgeInvoke.invokeMessageCall({ + state: state, + message: message, + signal: signal, + gasLimit: gasLimit + }); if (success) { status = LibBridgeData.MessageStatus.DONE; diff --git a/packages/protocol/contracts/bridge/libs/LibBridgeRetry.sol b/packages/protocol/contracts/bridge/libs/LibBridgeRetry.sol index c15aea619f8..2a85925c7db 100644 --- a/packages/protocol/contracts/bridge/libs/LibBridgeRetry.sol +++ b/packages/protocol/contracts/bridge/libs/LibBridgeRetry.sol @@ -62,7 +62,12 @@ library LibBridgeRetry { // successful invocation if ( - LibBridgeInvoke.invokeMessageCall(state, message, signal, gasleft()) + LibBridgeInvoke.invokeMessageCall({ + state: state, + message: message, + signal: signal, + gasLimit: gasleft() + }) ) { state.updateMessageStatus(signal, LibBridgeData.MessageStatus.DONE); } else if (isLastAttempt) { diff --git a/packages/protocol/contracts/bridge/libs/LibBridgeSignal.sol b/packages/protocol/contracts/bridge/libs/LibBridgeSignal.sol index 47436c5148a..a0fc61c48ff 100644 --- a/packages/protocol/contracts/bridge/libs/LibBridgeSignal.sol +++ b/packages/protocol/contracts/bridge/libs/LibBridgeSignal.sol @@ -88,13 +88,13 @@ library LibBridgeSignal { require(srcBridge != address(0), "B:srcBridge"); SignalProof memory mkp = abi.decode(proof, (SignalProof)); - LibTrieProof.verify( - mkp.header.stateRoot, - srcBridge, - _key(sender, signal), - bytes32(uint256(1)), - mkp.proof - ); + LibTrieProof.verify({ + stateRoot: mkp.header.stateRoot, + addr: srcBridge, + key: _key(sender, signal), + value: bytes32(uint256(1)), + mkproof: mkp.proof + }); // get synced header hash of the header height specified in the proof bytes32 syncedHeaderHash = IHeaderSync(resolver.resolve("taiko")) .getSyncedHeader(mkp.header.height); diff --git a/packages/protocol/contracts/common/AddressResolver.sol b/packages/protocol/contracts/common/AddressResolver.sol index 0d4050ac6f2..24a6d01e926 100644 --- a/packages/protocol/contracts/common/AddressResolver.sol +++ b/packages/protocol/contracts/common/AddressResolver.sol @@ -8,9 +8,10 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; -import "./IAddressManager.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; +import "./IAddressManager.sol"; + /** * This abstract contract provides a name-to-address lookup. Under the hood, * it uses an AddressManager to manage the name-to-address mapping. diff --git a/packages/protocol/contracts/common/EssentialContract.sol b/packages/protocol/contracts/common/EssentialContract.sol index 15efe953bdd..3c6b086d38d 100644 --- a/packages/protocol/contracts/common/EssentialContract.sol +++ b/packages/protocol/contracts/common/EssentialContract.sol @@ -9,6 +9,7 @@ pragma solidity ^0.8.9; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +// solhint-disable-next-line max-line-length import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "./AddressResolver.sol"; diff --git a/packages/protocol/contracts/libs/LibAnchorSignature.sol b/packages/protocol/contracts/libs/LibAnchorSignature.sol index 7de1e9b93b1..fd1e90718e2 100644 --- a/packages/protocol/contracts/libs/LibAnchorSignature.sol +++ b/packages/protocol/contracts/libs/LibAnchorSignature.sol @@ -11,9 +11,9 @@ import "./LibUint512Math.sol"; /// @author david library LibAnchorSignature { - address public constant TAIKO_GOLDEN_TOUCH_ADDRESS = + address public constant K_GOLDEN_TOUCH_ADDRESS = 0x0000777735367b36bC9B61C50022d9D0700dB4Ec; - uint256 public constant TAIKO_GOLDEN_TOUCH_PRIVATEKEY = + uint256 public constant K_GOLDEN_TOUCH_PRIVATEKEY = 0x92954368afd3caa1f3ce3ead0069c1af414054aefe1ef9aeacc1bf426222ce38; uint256 public constant GX = @@ -32,7 +32,7 @@ library LibAnchorSignature { // ( // uint256 GX_MUL_GOLDEN_TOUCH_PRIVATEKEY_LOW, // uint256 GX_MUL_GOLDEN_TOUCH_PRIVATEKEY_HIGH - // ) = LibUint512Math.mul(GX, TAIKO_GOLDEN_TOUCH_PRIVATEKEY); + // ) = LibUint512Math.mul(GX, K_GOLDEN_TOUCH_PRIVATEKEY); uint256 public constant GX_MUL_GOLDEN_TOUCH_PRIVATEKEY_LOW = 0xb4a95509ce05fe8d45987859a067780d16a367c0e2cacf79cd301b93fb717940; uint256 public constant GX_MUL_GOLDEN_TOUCH_PRIVATEKEY_HIGH = @@ -41,7 +41,7 @@ library LibAnchorSignature { // ( // uint256 GX2_MUL_GOLDEN_TOUCH_PRIVATEKEY_LOW, // uint256 GX2_MUL_GOLDEN_TOUCH_PRIVATEKEY_HIGH - // ) = LibUint512Math.mul(GX2, TAIKO_GOLDEN_TOUCH_PRIVATEKEY); + // ) = LibUint512Math.mul(GX2, K_GOLDEN_TOUCH_PRIVATEKEY); uint256 public constant GX2_MUL_GOLDEN_TOUCH_PRIVATEKEY_LOW = 0xad77eceea844778cb4376153fc8f06f12f1695df4585bf75bfb17ec19ce90818; uint256 public constant GX2_MUL_GOLDEN_TOUCH_PRIVATEKEY_HIGH = diff --git a/packages/protocol/contracts/libs/LibBlockHeader.sol b/packages/protocol/contracts/libs/LibBlockHeader.sol index 37f78087538..a312d314848 100644 --- a/packages/protocol/contracts/libs/LibBlockHeader.sol +++ b/packages/protocol/contracts/libs/LibBlockHeader.sol @@ -78,7 +78,7 @@ library LibBlockHeader { return header.parentHash != 0 && header.ommersHash == EMPTY_OMMERS_HASH && - header.gasLimit <= LibConstants.TAIKO_BLOCK_MAX_GAS_LIMIT && + header.gasLimit <= LibConstants.K_BLOCK_MAX_GAS_LIMIT && header.extraData.length <= 32 && header.difficulty == 0 && header.nonce == 0; diff --git a/packages/protocol/contracts/libs/LibConstants.sol b/packages/protocol/contracts/libs/LibConstants.sol index b43ff248fb0..1b6bf7e2a55 100644 --- a/packages/protocol/contracts/libs/LibConstants.sol +++ b/packages/protocol/contracts/libs/LibConstants.sol @@ -10,29 +10,44 @@ pragma solidity ^0.8.9; /// @author dantaik library LibConstants { - uint256 public constant K_ZKPROOFS_PER_BLOCK = 1; // https://github.com/ethereum-lists/chains/pull/1611 - uint256 public constant TAIKO_CHAIN_ID = 167; + uint256 public constant K_CHAIN_ID = 167; + // up to 2048 pending blocks + uint256 public constant K_MAX_NUM_BLOCKS = 2049; + // This number is calculated from K_MAX_NUM_BLOCKS to make + // the 'the maximum value of the multiplier' close to 20.0 + uint256 public constant K_FEE_PREMIUM_LAMDA = 590; + uint256 public constant K_ZKPROOFS_PER_BLOCK = 1; uint256 public constant K_VERIFICATION_DELAY = 60 minutes; - uint256 public constant TAIKO_MAX_PROPOSED_BLOCKS = 2048; - uint256 public constant TAIKO_MAX_VERIFICATIONS_PER_TX = 20; - uint256 public constant K_COMMIT_DELAY_CONFIRMATIONS = 4; - uint256 public constant TAIKO_MAX_PROOFS_PER_FORK_CHOICE = 5; - uint256 public constant TAIKO_BLOCK_MAX_GAS_LIMIT = 5000000; // TODO - uint256 public constant TAIKO_BLOCK_MAX_TXS = 20; // TODO - bytes32 public constant TAIKO_BLOCK_DEADEND_HASH = bytes32(uint256(1)); + uint256 public constant K_MAX_VERIFICATIONS_PER_TX = 20; + uint256 public constant K_COMMIT_DELAY_CONFIRMS = 4; + uint256 public constant K_MAX_PROOFS_PER_FORK_CHOICE = 5; + uint256 public constant K_BLOCK_MAX_GAS_LIMIT = 5000000; // TODO + uint256 public constant K_BLOCK_MAX_TXS = 20; // TODO + uint256 public constant K_TXLIST_MAX_BYTES = 10240; // TODO + uint256 public constant K_TX_MIN_GAS_LIMIT = 21000; // TODO + uint256 public constant K_REWARD_BURN_BP = 100; // 100 basis points or 1% + uint256 public constant K_ANCHOR_TX_GAS_LIMIT = 250000; + uint256 public constant K_PROPOSER_DEPOSIT_PCTG = 25; // 25% - uint256 public constant TAIKO_TXLIST_MAX_BYTES = 10240; // TODO - uint256 public constant TAIKO_TX_MIN_GAS_LIMIT = 21000; // TODO + // Moving average factors + uint256 public constant K_FEE_BASE_MAF = 1024; + uint256 public constant K_BLOCK_TIME_MAF = 1024; + uint256 public constant K_PROOF_TIME_MAF = 1024; - // Taiko L2 releated constants - uint256 public constant V1_ANCHOR_TX_GAS_LIMIT = 250000; + uint64 public constant K_REWARD_MULTIPLIER_PCTG = 400; // 400% + uint64 public constant K_FEE_GRACE_PERIOD_PCTG = 125; // 125% + uint64 public constant K_FEE_MAX_PERIOD_PCTG = 375; // 375% + uint64 public constant K_BLOCK_TIME_CAP = 48 seconds; + uint64 public constant K_PROOF_TIME_CAP = 60 minutes; + uint64 public constant K_HALVING = 180 days; - bytes4 public constant V1_ANCHOR_TX_SELECTOR = + bytes4 public constant K_ANCHOR_TX_SELECTOR = bytes4(keccak256("anchor(uint256,bytes32)")); - bytes32 public constant V1_INVALIDATE_BLOCK_LOG_TOPIC = + bytes32 public constant K_BLOCK_DEADEND_HASH = bytes32(uint256(1)); + bytes32 public constant K_INVALIDATE_BLOCK_LOG_TOPIC = keccak256("BlockInvalidated(bytes32)"); - bool public constant K_WHITELIST_PROVERS = false; + bool public constant K_TOKENOMICS_ENABLED = true; } diff --git a/packages/protocol/contracts/libs/LibInvalidTxList.sol b/packages/protocol/contracts/libs/LibInvalidTxList.sol index 5231a8167f2..40d31051297 100644 --- a/packages/protocol/contracts/libs/LibInvalidTxList.sol +++ b/packages/protocol/contracts/libs/LibInvalidTxList.sol @@ -18,23 +18,26 @@ import "../thirdparty/LibRLPWriter.sol"; * A library to invalidate a txList using the following rules: * * A txList is valid if and only if: - * 1. The txList's length is no more than `TAIKO_TXLIST_MAX_BYTES`. + * 1. The txList's length is no more than `K_TXLIST_MAX_BYTES`. * 2. The txList is well-formed RLP, with no additional trailing bytes. - * 3. The total number of transactions is no more than `TAIKO_BLOCK_MAX_TXS`. + * 3. The total number of transactions is no more than `K_BLOCK_MAX_TXS`. * 4. The sum of all transaction gas limit is no more than - * `TAIKO_BLOCK_MAX_GAS_LIMIT`. + * `K_BLOCK_MAX_GAS_LIMIT`. * * A transaction is valid if and only if: * 1. The transaction is well-formed RLP, with no additional trailing bytes * (rule #1 in Ethereum yellow paper). * 2. The transaction's signature is valid (rule #2 in Ethereum yellow paper). * 3. The transaction's the gas limit is no smaller than the intrinsic gas - * `TAIKO_TX_MIN_GAS_LIMIT` (rule #5 in Ethereum yellow paper). + * `K_TX_MIN_GAS_LIMIT` (rule #5 in Ethereum yellow paper). * * @title LibInvalidTxList * @author david */ library LibInvalidTxList { + // NOTE: If the order of this enum changes, then some test cases that using + // this enum in generate_genesis.test.ts may also needs to be + // modified accordingly. enum Reason { OK, BINARY_TOO_LARGE, @@ -50,20 +53,20 @@ library LibInvalidTxList { Reason hint, uint256 txIdx ) internal pure returns (Reason) { - if (encoded.length > LibConstants.TAIKO_TXLIST_MAX_BYTES) { + if (encoded.length > LibConstants.K_TXLIST_MAX_BYTES) { return Reason.BINARY_TOO_LARGE; } try LibTxDecoder.decodeTxList(encoded) returns ( LibTxDecoder.TxList memory txList ) { - if (txList.items.length > LibConstants.TAIKO_BLOCK_MAX_TXS) { + if (txList.items.length > LibConstants.K_BLOCK_MAX_TXS) { return Reason.BLOCK_TOO_MANY_TXS; } if ( LibTxDecoder.sumGasLimit(txList) > - LibConstants.TAIKO_BLOCK_MAX_GAS_LIMIT + LibConstants.K_BLOCK_MAX_GAS_LIMIT ) { return Reason.BLOCK_GAS_LIMIT_TOO_LARGE; } @@ -81,7 +84,7 @@ library LibInvalidTxList { if (hint == Reason.TX_GAS_LIMIT_TOO_SMALL) { require( - _tx.gasLimit >= LibConstants.TAIKO_TX_MIN_GAS_LIMIT, + _tx.gasLimit >= LibConstants.K_TX_MIN_GAS_LIMIT, "bad hint" ); return Reason.TX_GAS_LIMIT_TOO_SMALL; diff --git a/packages/protocol/contracts/libs/LibTxDecoder.sol b/packages/protocol/contracts/libs/LibTxDecoder.sol index c7aa4c2895b..9c10316f7d9 100644 --- a/packages/protocol/contracts/libs/LibTxDecoder.sol +++ b/packages/protocol/contracts/libs/LibTxDecoder.sol @@ -162,10 +162,7 @@ library LibTxDecoder { txLegacy.data = LibRLPReader.readBytes(body[5]); // EIP-155 is enabled on L2 txLegacy.v = uint8( - LibRLPReader.readUint256(body[6]) - - LibConstants.TAIKO_CHAIN_ID * - 2 + - 35 + LibRLPReader.readUint256(body[6]) - LibConstants.K_CHAIN_ID * 2 + 35 ); txLegacy.r = LibRLPReader.readUint256(body[7]); txLegacy.s = LibRLPReader.readUint256(body[8]); diff --git a/packages/protocol/contracts/libs/LibTxUtils.sol b/packages/protocol/contracts/libs/LibTxUtils.sol index 2faa63eb515..d68edfbbdc3 100644 --- a/packages/protocol/contracts/libs/LibTxUtils.sol +++ b/packages/protocol/contracts/libs/LibTxUtils.sol @@ -72,9 +72,7 @@ library LibTxUtils { // For legacy transactions, there are three more RLP items to // encode defined in EIP-155. if (transaction.txType == 0 && i == list.length - 4) { - list[i + 1] = LibRLPWriter.writeUint( - LibConstants.TAIKO_CHAIN_ID - ); + list[i + 1] = LibRLPWriter.writeUint(LibConstants.K_CHAIN_ID); list[i + 2] = LibRLPWriter.writeUint64(0); list[i + 3] = LibRLPWriter.writeUint64(0); break; diff --git a/packages/protocol/contracts/test/libs/TestLibAnchorSignature.sol b/packages/protocol/contracts/test/libs/TestLibAnchorSignature.sol index 2f1d0bcdc8b..3a80e2d2d19 100644 --- a/packages/protocol/contracts/test/libs/TestLibAnchorSignature.sol +++ b/packages/protocol/contracts/test/libs/TestLibAnchorSignature.sol @@ -29,8 +29,8 @@ library TestLibAnchorSignature { function goldenTouchAddress() public pure returns (address, uint256) { return ( - LibAnchorSignature.TAIKO_GOLDEN_TOUCH_ADDRESS, - LibAnchorSignature.TAIKO_GOLDEN_TOUCH_PRIVATEKEY + LibAnchorSignature.K_GOLDEN_TOUCH_ADDRESS, + LibAnchorSignature.K_GOLDEN_TOUCH_PRIVATEKEY ); } } diff --git a/packages/protocol/docs/L1/LibData.md b/packages/protocol/docs/L1/LibData.md index 63f11bb57df..21c79e40357 100644 --- a/packages/protocol/docs/L1/LibData.md +++ b/packages/protocol/docs/L1/LibData.md @@ -22,6 +22,8 @@ struct BlockMetadata { ```solidity struct ProposedBlock { bytes32 metaHash; + address proposer; + uint64 gasLimit; } ``` @@ -47,9 +49,18 @@ struct State { mapping(uint256 => mapping(bytes32 => struct LibData.ForkChoice)) forkChoices; mapping(bytes32 => uint256) commits; uint64 genesisHeight; + uint64 genesisTimestamp; + uint64 reservedA1; + uint64 reservedA2; + uint256 feeBase; + uint64 nextBlockId; + uint64 lastProposedAt; + uint64 avgBlockTime; + uint64 avgGasLimit; uint64 latestVerifiedHeight; uint64 latestVerifiedId; - uint64 nextBlockId; + uint64 avgProofTime; + uint64 reservedC1; } ``` diff --git a/packages/protocol/docs/L1/TaikoL1.md b/packages/protocol/docs/L1/TaikoL1.md index 7e18387f854..2d113249cb5 100644 --- a/packages/protocol/docs/L1/TaikoL1.md +++ b/packages/protocol/docs/L1/TaikoL1.md @@ -9,13 +9,13 @@ struct LibData.State state ### \_\_gap ```solidity -uint256[45] __gap +uint256[42] __gap ``` ### init ```solidity -function init(address _addressManager, bytes32 _genesisBlockHash) external +function init(address _addressManager, bytes32 _genesisBlockHash, uint256 _feeBase) external ``` ### commitBlock @@ -51,7 +51,7 @@ Propose a Taiko L2 block. ### proveBlock ```solidity -function proveBlock(uint256 blockIndex, bytes[] inputs) external +function proveBlock(uint256 blockId, bytes[] inputs) external ``` Prove a block is valid with a zero-knowledge proof, a transaction @@ -59,15 +59,15 @@ merkel proof, and a receipt merkel proof. #### Parameters -| Name | Type | Description | -| ---------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| blockIndex | uint256 | The index of the block to prove. This is also used to select the right implementation version. | -| inputs | bytes[] | A list of data input: - inputs[0] is an abi-encoded object with various information regarding the block to be proven and the actual proofs. - inputs[1] is the actual anchor transaction in this L2 block. Note that the anchor transaction is always the first transaction in the block. - inputs[2] is the receipt of the anchor transaction. | +| Name | Type | Description | +| ------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| blockId | uint256 | The index of the block to prove. This is also used to select the right implementation version. | +| inputs | bytes[] | A list of data input: - inputs[0] is an abi-encoded object with various information regarding the block to be proven and the actual proofs. - inputs[1] is the actual anchor transaction in this L2 block. Note that the anchor transaction is always the first transaction in the block. - inputs[2] is the receipt of the anchor transaction. | ### proveBlockInvalid ```solidity -function proveBlockInvalid(uint256 blockIndex, bytes[] inputs) external +function proveBlockInvalid(uint256 blockId, bytes[] inputs) external ``` Prove a block is invalid with a zero-knowledge proof and a receipt @@ -75,10 +75,10 @@ merkel proof. #### Parameters -| Name | Type | Description | -| ---------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| blockIndex | uint256 | The index of the block to prove. This is also used to select the right implementation version. | -| inputs | bytes[] | A list of data input: - inputs[0] An Evidence object with various information regarding the block to be proven and the actual proofs. - inputs[1] The target block to be proven invalid. - inputs[2] The receipt for the `invalidBlock` transaction on L2. Note that the `invalidBlock` transaction is supposed to be the only transaction in the L2 block. | +| Name | Type | Description | +| ------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| blockId | uint256 | The index of the block to prove. This is also used to select the right implementation version. | +| inputs | bytes[] | A list of data input: - inputs[0] An Evidence object with various information regarding the block to be proven and the actual proofs. - inputs[1] The target block to be proven invalid. - inputs[2] The receipt for the `invalidBlock` transaction on L2. Note that the `invalidBlock` transaction is supposed to be the only transaction in the L2 block. | ### verifyBlocks @@ -94,6 +94,18 @@ Verify up to N blocks. | --------- | ------- | ------------------------------- | | maxBlocks | uint256 | Max number of blocks to verify. | +### getBlockFee + +```solidity +function getBlockFee() public view returns (uint256 premiumFee) +``` + +### getProofReward + +```solidity +function getProofReward(uint64 provenAt, uint64 proposedAt) public view returns (uint256 premiumReward) +``` + ### isCommitValid ```solidity diff --git a/packages/protocol/docs/L1/v1/V1Finalizing.md b/packages/protocol/docs/L1/v1/V1Finalizing.md index 1dcf04bbe02..23129248ef0 100644 --- a/packages/protocol/docs/L1/v1/V1Finalizing.md +++ b/packages/protocol/docs/L1/v1/V1Finalizing.md @@ -1,4 +1,4 @@ -## V1Finalizing +## V1Verifying ### BlockVerified @@ -15,11 +15,11 @@ event HeaderSynced(uint256 height, uint256 srcHeight, bytes32 srcHash) ### init ```solidity -function init(struct LibData.State s, bytes32 _genesisBlockHash) public +function init(struct LibData.State s, bytes32 _genesisBlockHash, uint256 _feeBase) public ``` ### verifyBlocks -```solidity -function verifyBlocks(struct LibData.State s, uint256 maxBlocks) public +``` + ``` diff --git a/packages/protocol/docs/L1/v1/V1Proposing.md b/packages/protocol/docs/L1/v1/V1Proposing.md index 2f2ce24c409..01dce60bd9c 100644 --- a/packages/protocol/docs/L1/v1/V1Proposing.md +++ b/packages/protocol/docs/L1/v1/V1Proposing.md @@ -21,7 +21,13 @@ function commitBlock(struct LibData.State s, bytes32 commitHash) public ### proposeBlock ```solidity -function proposeBlock(struct LibData.State s, bytes[] inputs) public +function proposeBlock(struct LibData.State s, contract AddressResolver resolver, bytes[] inputs) public +``` + +### getBlockFee + +```solidity +function getBlockFee(struct LibData.State s) public view returns (uint256 fee, uint256 premiumFee) ``` ### isCommitValid @@ -30,6 +36,12 @@ function proposeBlock(struct LibData.State s, bytes[] inputs) public function isCommitValid(struct LibData.State s, bytes32 hash) public view returns (bool) ``` +### \_calcProposerBootstrapReward + +```solidity +function _calcProposerBootstrapReward(struct LibData.State s) private view returns (uint256 proposerReward) +``` + ### \_validateMetadata ```solidity diff --git a/packages/protocol/docs/L1/v1/V1Proving.md b/packages/protocol/docs/L1/v1/V1Proving.md index 7f56bcc27e5..bbdee912ffd 100644 --- a/packages/protocol/docs/L1/v1/V1Proving.md +++ b/packages/protocol/docs/L1/v1/V1Proving.md @@ -20,13 +20,13 @@ event BlockProven(uint256 id, bytes32 parentHash, bytes32 blockHash, uint64 time ### proveBlock ```solidity -function proveBlock(struct LibData.State s, contract AddressResolver resolver, uint256 blockIndex, bytes[] inputs) public +function proveBlock(struct LibData.State s, contract AddressResolver resolver, uint256 blockId, bytes[] inputs) public ``` ### proveBlockInvalid ```solidity -function proveBlockInvalid(struct LibData.State s, contract AddressResolver resolver, uint256 blockIndex, bytes[] inputs) public +function proveBlockInvalid(struct LibData.State s, contract AddressResolver resolver, uint256 blockId, bytes[] inputs) public ``` ### \_proveBlock diff --git a/packages/protocol/docs/L1/v1/V1Utils.md b/packages/protocol/docs/L1/v1/V1Utils.md new file mode 100644 index 00000000000..b6aad8d1393 --- /dev/null +++ b/packages/protocol/docs/L1/v1/V1Utils.md @@ -0,0 +1,19 @@ +## V1Utils + +### feeScaleBeta + +```solidity +function feeScaleBeta(struct LibData.State s, uint256 fee, bool releaseOneSlot) public view returns (uint256) +``` + +### movingAverage + +```solidity +function movingAverage(uint256 ma, uint256 v, uint256 factor) internal pure returns (uint256) +``` + +### feeScaleAlpha + +```solidity +function feeScaleAlpha(uint64 tNow, uint64 tLast, uint64 tAvg) internal pure returns (uint256) +``` diff --git a/packages/protocol/docs/L2/V1TaikoL2.md b/packages/protocol/docs/L2/V1TaikoL2.md index 643da921471..7cd8b1594fc 100644 --- a/packages/protocol/docs/L2/V1TaikoL2.md +++ b/packages/protocol/docs/L2/V1TaikoL2.md @@ -109,5 +109,5 @@ function _checkPublicInputs() private ### \_hashPublicInputs ```solidity -function _hashPublicInputs(uint256 chainId, uint256 number, uint256 baseFee, bytes32[255] ancestors) private pure returns (bytes32) +function _hashPublicInputs(uint256 chainId, uint256 number, uint256 feeBase, bytes32[255] ancestors) private pure returns (bytes32) ``` diff --git a/packages/protocol/docs/libs/LibAnchorSignature.md b/packages/protocol/docs/libs/LibAnchorSignature.md index ac0531d1349..3d7fe857a4a 100644 --- a/packages/protocol/docs/libs/LibAnchorSignature.md +++ b/packages/protocol/docs/libs/LibAnchorSignature.md @@ -1,15 +1,15 @@ ## LibAnchorSignature -### TAIKO_GOLDEN_TOUCH_ADDRESS +### K_GOLDEN_TOUCH_ADDRESS ```solidity -address TAIKO_GOLDEN_TOUCH_ADDRESS +address K_GOLDEN_TOUCH_ADDRESS ``` -### TAIKO_GOLDEN_TOUCH_PRIVATEKEY +### K_GOLDEN_TOUCH_PRIVATEKEY ```solidity -uint256 TAIKO_GOLDEN_TOUCH_PRIVATEKEY +uint256 K_GOLDEN_TOUCH_PRIVATEKEY ``` ### GX diff --git a/packages/protocol/docs/libs/LibConstants.md b/packages/protocol/docs/libs/LibConstants.md index 812205af2df..cb224c99a17 100644 --- a/packages/protocol/docs/libs/LibConstants.md +++ b/packages/protocol/docs/libs/LibConstants.md @@ -1,79 +1,109 @@ ## LibConstants -### TAIKO_CHAIN_ID +### K_CHAIN_ID ```solidity -uint256 TAIKO_CHAIN_ID +uint256 K_CHAIN_ID ``` -### TAIKO_MAX_PROPOSED_BLOCKS +### K_MAX_NUM_BLOCKS ```solidity -uint256 TAIKO_MAX_PROPOSED_BLOCKS +uint256 K_MAX_NUM_BLOCKS ``` -### TAIKO_MAX_VERIFICATIONS_PER_TX +### K_FEE_PREMIUM_PHI ```solidity -uint256 TAIKO_MAX_VERIFICATIONS_PER_TX +uint256 K_FEE_PREMIUM_PHI ``` -### TAIKO_COMMIT_DELAY_CONFIRMATIONS +### K_REWARD_MULTIPLIER_PCTG ```solidity -uint256 TAIKO_COMMIT_DELAY_CONFIRMATIONS +uint64 K_REWARD_MULTIPLIER_PCTG ``` -### TAIKO_MAX_PROOFS_PER_FORK_CHOICE +### K_FEE_GRACE_PERIOD_PCTG ```solidity -uint256 TAIKO_MAX_PROOFS_PER_FORK_CHOICE +uint64 K_FEE_GRACE_PERIOD_PCTG ``` -### TAIKO_BLOCK_MAX_GAS_LIMIT +### K_FEE_MAX_PERIOD_PCTG ```solidity -uint256 TAIKO_BLOCK_MAX_GAS_LIMIT +uint64 K_FEE_MAX_PERIOD_PCTG ``` -### TAIKO_BLOCK_MAX_TXS +### K_MAX_FINALIZATIONS_PER_TX ```solidity -uint256 TAIKO_BLOCK_MAX_TXS +uint256 K_MAX_FINALIZATIONS_PER_TX ``` -### TAIKO_BLOCK_DEADEND_HASH +### K_COMMIT_DELAY_CONFIRMS ```solidity -bytes32 TAIKO_BLOCK_DEADEND_HASH +uint256 K_COMMIT_DELAY_CONFIRMS ``` -### TAIKO_TXLIST_MAX_BYTES +### K_MAX_PROOFS_PER_FORK_CHOICE ```solidity -uint256 TAIKO_TXLIST_MAX_BYTES +uint256 K_MAX_PROOFS_PER_FORK_CHOICE ``` -### TAIKO_TX_MIN_GAS_LIMIT +### K_BLOCK_MAX_GAS_LIMIT ```solidity -uint256 TAIKO_TX_MIN_GAS_LIMIT +uint256 K_BLOCK_MAX_GAS_LIMIT ``` -### V1_ANCHOR_TX_GAS_LIMIT +### K_BLOCK_MAX_TXS ```solidity -uint256 V1_ANCHOR_TX_GAS_LIMIT +uint256 K_BLOCK_MAX_TXS ``` -### V1_ANCHOR_TX_SELECTOR +### K_BLOCK_DEADEND_HASH ```solidity -bytes4 V1_ANCHOR_TX_SELECTOR +bytes32 K_BLOCK_DEADEND_HASH ``` -### V1_INVALIDATE_BLOCK_LOG_TOPIC +### K_TXLIST_MAX_BYTES ```solidity -bytes32 V1_INVALIDATE_BLOCK_LOG_TOPIC +uint256 K_TXLIST_MAX_BYTES +``` + +### K_TX_MIN_GAS_LIMIT + +```solidity +uint256 K_TX_MIN_GAS_LIMIT +``` + +### K_REWARD_BURN_BP + +```solidity +uint256 K_REWARD_BURN_BP +``` + +### K_ANCHOR_TX_GAS_LIMIT + +```solidity +uint256 K_ANCHOR_TX_GAS_LIMIT +``` + +### K_ANCHOR_TX_SELECTOR + +```solidity +bytes4 K_ANCHOR_TX_SELECTOR +``` + +### K_INVALIDATE_BLOCK_LOG_TOPIC + +```solidity +bytes32 K_INVALIDATE_BLOCK_LOG_TOPIC ``` diff --git a/packages/protocol/docs/libs/LibInvalidTxList.md b/packages/protocol/docs/libs/LibInvalidTxList.md index 2a2c134cc94..15ff494ca54 100644 --- a/packages/protocol/docs/libs/LibInvalidTxList.md +++ b/packages/protocol/docs/libs/LibInvalidTxList.md @@ -4,11 +4,11 @@ A library to invalidate a txList using the following rules: A txList is valid if and only if: -1. The txList's length is no more than `TAIKO_TXLIST_MAX_BYTES`. +1. The txList's length is no more than `K_TXLIST_MAX_BYTES`. 2. The txList is well-formed RLP, with no additional trailing bytes. -3. The total number of transactions is no more than `TAIKO_BLOCK_MAX_TXS`. +3. The total number of transactions is no more than `K_BLOCK_MAX_TXS`. 4. The sum of all transaction gas limit is no more than - `TAIKO_BLOCK_MAX_GAS_LIMIT`. + `K_BLOCK_MAX_GAS_LIMIT`. A transaction is valid if and only if: @@ -16,7 +16,7 @@ A transaction is valid if and only if: (rule #1 in Ethereum yellow paper). 2. The transaction's signature is valid (rule #2 in Ethereum yellow paper). 3. The transaction's the gas limit is no smaller than the intrinsic gas - `TAIKO_TX_MIN_GAS_LIMIT` (rule #5 in Ethereum yellow paper). + `K_TX_MIN_GAS_LIMIT` (rule #5 in Ethereum yellow paper). ### Reason diff --git a/packages/protocol/hardhat.config.ts b/packages/protocol/hardhat.config.ts index e5b50213f5d..d5f0ec648b2 100644 --- a/packages/protocol/hardhat.config.ts +++ b/packages/protocol/hardhat.config.ts @@ -92,8 +92,8 @@ const config: HardhatUserConfig = { eachLine: () => ({ transform: (line) => { for (const constantName of [ - "TAIKO_CHAIN_ID", - "K_COMMIT_DELAY_CONFIRMATIONS", + "K_CHAIN_ID", + "K_COMMIT_DELAY_CONFIRMS", "TAIKO_BLOCK_MAX_TXS", "TAIKO_TXLIST_MAX_BYTES", "TAIKO_BLOCK_MAX_GAS_LIMIT", diff --git a/packages/protocol/tasks/config.ts b/packages/protocol/tasks/config.ts index 22ae3d79c92..de7f7afc3b2 100644 --- a/packages/protocol/tasks/config.ts +++ b/packages/protocol/tasks/config.ts @@ -1,3 +1,3 @@ // https://github.com/ethereum-lists/chains/pull/1611 -export const TAIKO_CHAINID = 167 -export const DEFAULT_DEPLOY_CONFIRMATIONS = 12 +export const K_CHAIN_ID = 167 +export const K_DEPLOY_CONFIRMATIONS = 12 diff --git a/packages/protocol/tasks/deploy_L1.ts b/packages/protocol/tasks/deploy_L1.ts index 07fcd7a7f42..103b8121ff7 100644 --- a/packages/protocol/tasks/deploy_L1.ts +++ b/packages/protocol/tasks/deploy_L1.ts @@ -18,16 +18,11 @@ task("deploy_L1") "L2 genesis block hash", ethers.constants.HashZero ) - .addOptionalParam( - "l2ChainId", - "L2 chain id", - config.TAIKO_CHAINID, - types.int - ) + .addOptionalParam("l2ChainId", "L2 chain id", config.K_CHAIN_ID, types.int) .addOptionalParam( "confirmations", "Number of confirmations to wait for deploy transaction.", - config.DEFAULT_DEPLOY_CONFIRMATIONS, + config.K_DEPLOY_CONFIRMATIONS, types.int ) .setAction(async (args, hre: any) => { @@ -112,9 +107,11 @@ export async function deployContracts(hre: any) { "TaikoL1", await deployBaseLibs(hre) ) + const feeBase = hre.ethers.BigNumber.from(10).pow(18) + await utils.waitTx( hre, - await TaikoL1.init(AddressManager.address, l2GenesisBlockHash) + await TaikoL1.init(AddressManager.address, l2GenesisBlockHash, feeBase) ) // Used by LibBridgeRead @@ -163,26 +160,19 @@ async function deployBaseLibs(hre: any) { ) const libTxDecoder = await utils.deployContract(hre, "LibTxDecoder") - const v1Utils = await utils.deployContract(hre, "V1Utils") - const v1Finalizing = await utils.deployContract(hre, "V1Finalizing", { - V1Utils: v1Utils.address, - }) - const v1Proposing = await utils.deployContract(hre, "V1Proposing", { - V1Utils: v1Utils.address, - }) + const v1Verifying = await utils.deployContract(hre, "V1Verifying", {}) + const v1Proposing = await utils.deployContract(hre, "V1Proposing", {}) const v1Proving = await utils.deployContract(hre, "V1Proving", { LibZKP: libZKP.address, LibReceiptDecoder: libReceiptDecoder.address, LibTxDecoder: libTxDecoder.address, - V1Utils: v1Utils.address, }) return { - V1Finalizing: v1Finalizing.address, + V1Verifying: v1Verifying.address, V1Proposing: v1Proposing.address, V1Proving: v1Proving.address, - V1Utils: v1Utils.address, } } diff --git a/packages/protocol/test/L1/TaikoL1.test.ts b/packages/protocol/test/L1/TaikoL1.test.ts index 03ee88752c3..fa8a92d9428 100644 --- a/packages/protocol/test/L1/TaikoL1.test.ts +++ b/packages/protocol/test/L1/TaikoL1.test.ts @@ -1,5 +1,6 @@ import { expect } from "chai" import { ethers } from "hardhat" +import { BigNumber } from "ethers" describe("TaikoL1", function () { async function deployTaikoL1Fixture() { @@ -21,16 +22,8 @@ describe("TaikoL1", function () { await ethers.getContractFactory("LibZKP") ).deploy() - const v1Utils = await ( - await ethers.getContractFactory("V1Utils") - ).deploy() - const v1Proposing = await ( - await ethers.getContractFactory("V1Proposing", { - libraries: { - V1Utils: v1Utils.address, - }, - }) + await ethers.getContractFactory("V1Proposing") ).deploy() const v1Proving = await ( @@ -39,31 +32,26 @@ describe("TaikoL1", function () { LibReceiptDecoder: libReceiptDecoder.address, LibTxDecoder: libTxDecoder.address, LibZKP: libZKP.address, - V1Utils: v1Utils.address, }, }) ).deploy() - const v1Finalizing = await ( - await ethers.getContractFactory("V1Finalizing", { - libraries: { - V1Utils: v1Utils.address, - }, - }) + const v1Verifying = await ( + await ethers.getContractFactory("V1Verifying") ).deploy() const TaikoL1Factory = await ethers.getContractFactory("TaikoL1", { libraries: { - V1Finalizing: v1Finalizing.address, + V1Verifying: v1Verifying.address, V1Proposing: v1Proposing.address, V1Proving: v1Proving.address, - V1Utils: v1Utils.address, }, }) const genesisHash = randomBytes32() const taikoL1 = await TaikoL1Factory.deploy() - await taikoL1.init(addressManager.address, genesisHash) + const feeBase = BigNumber.from(10).pow(18) + await taikoL1.init(addressManager.address, genesisHash, feeBase) return { taikoL1, genesisHash } } diff --git a/packages/protocol/test/bridge/libs/LibBridgeData.test.ts b/packages/protocol/test/bridge/libs/LibBridgeData.test.ts index 84a633d1503..9729cfc3c67 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeData.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeData.test.ts @@ -1,6 +1,6 @@ import { expect } from "chai" import { ethers } from "hardhat" -import { TAIKO_BRIDGE_MESSAGE } from "../../constants/messages" +import { K_BRIDGE_MESSAGE } from "../../constants/messages" import { MessageStatus } from "../../../tasks/utils" describe("LibBridgeData", function () { @@ -32,7 +32,7 @@ describe("LibBridgeData", function () { "tuple(uint256 id, address sender, uint256 srcChainId, uint256 destChainId, address owner, address to, address refundAddress, uint256 depositValue, uint256 callValue, uint256 processingFee, uint256 gasLimit, bytes data, string memo)", ] - const testVar = [TAIKO_BRIDGE_MESSAGE, testMessage] + const testVar = [K_BRIDGE_MESSAGE, testMessage] return { owner, @@ -51,7 +51,7 @@ describe("LibBridgeData", function () { await deployLibBridgeDataFixture() // dummy struct to test with - const testVar = [TAIKO_BRIDGE_MESSAGE, testMessage] + const testVar = [K_BRIDGE_MESSAGE, testMessage] const hashed = await libData.hashMessage(testMessage) const expectedEncoded = ethers.utils.defaultAbiCoder.encode( testTypes, diff --git a/packages/protocol/test/constants/messages.ts b/packages/protocol/test/constants/messages.ts index 212301c9f81..c6880c6fd00 100644 --- a/packages/protocol/test/constants/messages.ts +++ b/packages/protocol/test/constants/messages.ts @@ -1,3 +1,3 @@ -const TAIKO_BRIDGE_MESSAGE = "TAIKO_BRIDGE_MESSAGE" +const K_BRIDGE_MESSAGE = "TAIKO_BRIDGE_MESSAGE" -export { TAIKO_BRIDGE_MESSAGE } +export { K_BRIDGE_MESSAGE } diff --git a/packages/protocol/test/genesis/generate_genesis.test.ts b/packages/protocol/test/genesis/generate_genesis.test.ts index f92955c6bfd..3ffafc66242 100644 --- a/packages/protocol/test/genesis/generate_genesis.test.ts +++ b/packages/protocol/test/genesis/generate_genesis.test.ts @@ -183,7 +183,7 @@ action("Generate Genesis", function () { const tx = await V1TaikoL2.invalidateBlock( bytes, - 5, // hint: TX_INVALID_SIG + 6, // hint: TX_INVALID_SIG 0 ) diff --git a/packages/protocol/test/libs/LibTxUtils.test.ts b/packages/protocol/test/libs/LibTxUtils.test.ts index 4dc9a82a84b..ae92f04db92 100644 --- a/packages/protocol/test/libs/LibTxUtils.test.ts +++ b/packages/protocol/test/libs/LibTxUtils.test.ts @@ -30,7 +30,7 @@ describe("LibTxUtils", function () { await ethers.getContractFactory("TestLibRLPWriter") ).deploy() - chainId = (await libConstants.TAIKO_CHAIN_ID()).toNumber() + chainId = (await libConstants.K_CHAIN_ID()).toNumber() const unsignedLegacyTx: UnsignedTransaction = { type: 0, From d51fe1f5f491c947c06ef4dfc4629f9641117fe6 Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Fri, 2 Dec 2022 11:32:10 +0800 Subject: [PATCH 11/57] undo tokenomics --- packages/protocol/contracts/L1/LibData.sol | 3 - packages/protocol/contracts/L1/TaikoL1.sol | 25 +---- .../protocol/contracts/L1/v1/V1Proposing.sol | 48 ---------- packages/protocol/contracts/L1/v1/V1Utils.sol | 71 -------------- .../protocol/contracts/L1/v1/V1Verifying.sol | 95 +------------------ packages/protocol/contracts/L2/V1TaikoL2.sol | 10 +- .../protocol/contracts/libs/LibConstants.sol | 17 ---- packages/protocol/test/L1/TaikoL1.test.ts | 4 +- 8 files changed, 9 insertions(+), 264 deletions(-) diff --git a/packages/protocol/contracts/L1/LibData.sol b/packages/protocol/contracts/L1/LibData.sol index 6c6094b649c..d753a798a83 100644 --- a/packages/protocol/contracts/L1/LibData.sol +++ b/packages/protocol/contracts/L1/LibData.sol @@ -29,7 +29,6 @@ library LibData { // 3 slots struct ProposedBlock { bytes32 metaHash; - uint256 deposit; address proposer; uint64 proposedAt; } @@ -56,8 +55,6 @@ library LibData { uint64 genesisTimestamp; uint64 __reservedA1; uint64 statusBits; // rarely change - // Changed when a block is proposed or proven/finalized - uint256 feeBase; // Changed when a block is proposed uint64 nextBlockId; uint64 lastProposedAt; // Timestamp when the last block is proposed. diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index e776cd083dd..245b49c0e4a 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -30,15 +30,10 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { function init( address _addressManager, - bytes32 _genesisBlockHash, - uint256 _feeBase + bytes32 _genesisBlockHash ) external initializer { EssentialContract._init(_addressManager); - V1Verifying.init({ - state: state, - genesisBlockHash: _genesisBlockHash, - feeBase: _feeBase - }); + V1Verifying.init({state: state, genesisBlockHash: _genesisBlockHash}); tentative.whitelistProposers = false; tentative.whitelistProvers = true; @@ -253,22 +248,6 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { return V1Utils.isProverWhitelisted(tentative, prover); } - function getBlockFee() public view returns (uint256) { - (, uint fee, uint deposit) = V1Proposing.getBlockFee(state); - return fee + deposit; - } - - function getProofReward( - uint64 provenAt, - uint64 proposedAt - ) public view returns (uint256 reward) { - (, reward, ) = V1Verifying.getProofReward({ - state: state, - provenAt: provenAt, - proposedAt: proposedAt - }); - } - /** * Check if the L1 is halted. * @return True if halted, false otherwise. diff --git a/packages/protocol/contracts/L1/v1/V1Proposing.sol b/packages/protocol/contracts/L1/v1/V1Proposing.sol index 91544f57ca2..bdd7ed9b1f5 100644 --- a/packages/protocol/contracts/L1/v1/V1Proposing.sol +++ b/packages/protocol/contracts/L1/v1/V1Proposing.sol @@ -98,38 +98,10 @@ library V1Proposing { meta.mixHash = bytes32(block.difficulty); } - uint256 deposit; - if (LibConstants.K_TOKENOMICS_ENABLED) { - uint256 newFeeBase; - { - uint256 fee; - (newFeeBase, fee, deposit) = getBlockFee(state); - TkoToken(resolver.resolve("tko_token")).burn( - msg.sender, - fee + deposit - ); - } - // Update feeBase and avgBlockTime - state.feeBase = V1Utils.movingAverage({ - maValue: state.feeBase, - newValue: newFeeBase, - maf: LibConstants.K_FEE_BASE_MAF - }); - - state.avgBlockTime = V1Utils - .movingAverage({ - maValue: state.avgBlockTime, - newValue: meta.timestamp - state.lastProposedAt, - maf: LibConstants.K_BLOCK_TIME_MAF - }) - .toUint64(); - } - state.saveProposedBlock( state.nextBlockId, LibData.ProposedBlock({ metaHash: LibData.hashMetadata(meta), - deposit: deposit, proposer: msg.sender, proposedAt: meta.timestamp }) @@ -139,26 +111,6 @@ library V1Proposing { emit BlockProposed(state.nextBlockId++, meta); } - function getBlockFee( - LibData.State storage state - ) public view returns (uint256 newFeeBase, uint256 fee, uint256 deposit) { - (newFeeBase, ) = V1Utils.getTimeAdjustedFee({ - state: state, - isProposal: true, - tNow: uint64(block.timestamp), - tLast: state.lastProposedAt, - tAvg: state.avgBlockTime, - tCap: LibConstants.K_BLOCK_TIME_CAP - }); - fee = V1Utils.getSlotsAdjustedFee({ - state: state, - isProposal: true, - feeBase: newFeeBase - }); - fee = V1Utils.getBootstrapDiscountedFee(state, fee); - deposit = (fee * LibConstants.K_PROPOSER_DEPOSIT_PCTG) / 100; - } - function isCommitValid( LibData.State storage state, uint256 commitSlot, diff --git a/packages/protocol/contracts/L1/v1/V1Utils.sol b/packages/protocol/contracts/L1/v1/V1Utils.sol index 168254a4f25..b339b911899 100644 --- a/packages/protocol/contracts/L1/v1/V1Utils.sol +++ b/packages/protocol/contracts/L1/v1/V1Utils.sol @@ -93,65 +93,6 @@ library V1Utils { return tentative.provers[prover]; } - // Implement "Incentive Multipliers", see the whitepaper. - function getTimeAdjustedFee( - LibData.State storage state, - bool isProposal, - uint64 tNow, - uint64 tLast, - uint64 tAvg, - uint64 tCap - ) internal view returns (uint256 newFeeBase, uint256 tRelBp) { - if (tAvg == 0) { - newFeeBase = state.feeBase; - tRelBp = 0; - } else { - uint256 _tAvg = tAvg > tCap ? tCap : tAvg; - uint256 tGrace = (LibConstants.K_FEE_GRACE_PERIOD_PCTG * _tAvg) / - 100; - uint256 tMax = (LibConstants.K_FEE_MAX_PERIOD_PCTG * _tAvg) / 100; - uint256 a = tLast + tGrace; - uint256 b = tNow > a ? tNow - a : 0; - tRelBp = (b.min(tMax) * 10000) / tMax; // [0 - 10000] - uint256 alpha = 10000 + - ((LibConstants.K_REWARD_MULTIPLIER_PCTG - 100) * tRelBp) / - 100; - if (isProposal) { - newFeeBase = (state.feeBase * 10000) / alpha; // fee - } else { - newFeeBase = (state.feeBase * alpha) / 10000; // reward - } - } - } - - // Implement "Slot-availability Multipliers", see the whitepaper. - function getSlotsAdjustedFee( - LibData.State storage state, - bool isProposal, - uint256 feeBase - ) internal view returns (uint256) { - // m is the `n'` in the whitepaper - uint256 m = LibConstants.K_MAX_NUM_BLOCKS - - 1 + - LibConstants.K_FEE_PREMIUM_LAMDA; - // n is the number of unverified blocks - uint256 n = state.nextBlockId - state.latestVerifiedId - 1; - // k is `m − n + 1` or `m − n - 1`in the whitepaper - uint256 k = isProposal ? m - n - 1 : m - n + 1; - return (feeBase * (m - 1) * m) / (m - n) / k; - } - - // Implement "Bootstrap Discount Multipliers", see the whitepaper. - function getBootstrapDiscountedFee( - LibData.State storage state, - uint256 feeBase - ) internal view returns (uint256) { - uint256 halves = uint256(block.timestamp - state.genesisTimestamp) / - LibConstants.K_HALVING; - uint256 gamma = 1024 - (1024 >> halves); - return (feeBase * gamma) / 1024; - } - // Returns a deterministic deadline for uncle proof submission. function uncleProofDeadline( LibData.State storage state, @@ -160,18 +101,6 @@ library V1Utils { return fc.provenAt + state.avgProofTime; } - function movingAverage( - uint256 maValue, - uint256 newValue, - uint256 maf - ) internal pure returns (uint256) { - if (maValue == 0) { - return newValue; - } - uint256 _ma = (maValue * (maf - 1) + newValue) / maf; - return _ma > 0 ? _ma : maValue; - } - function setBit( LibData.State storage state, uint64 mask, diff --git a/packages/protocol/contracts/L1/v1/V1Verifying.sol b/packages/protocol/contracts/L1/v1/V1Verifying.sol index 7c0988ab661..03ea7876d90 100644 --- a/packages/protocol/contracts/L1/v1/V1Verifying.sol +++ b/packages/protocol/contracts/L1/v1/V1Verifying.sol @@ -25,14 +25,10 @@ library V1Verifying { function init( LibData.State storage state, - bytes32 genesisBlockHash, - uint256 feeBase + bytes32 genesisBlockHash ) public { - require(feeBase > 0, "L1:feeBase"); - state.genesisHeight = uint64(block.number); state.genesisTimestamp = uint64(block.timestamp); - state.feeBase = feeBase; state.nextBlockId = 1; state.lastProposedAt = uint64(block.timestamp); state.l2Hashes[0] = genesisBlockHash; @@ -58,7 +54,6 @@ library V1Verifying { uint64 latestL2Height = state.latestVerifiedHeight; bytes32 latestL2Hash = state.l2Hashes[latestL2Height]; uint64 processed = 0; - TkoToken tkoToken; for ( uint256 i = state.latestVerifiedId + 1; @@ -66,49 +61,11 @@ library V1Verifying { i++ ) { LibData.ForkChoice storage fc = state.forkChoices[i][latestL2Hash]; - LibData.ProposedBlock storage target = LibData.getProposedBlock( - state, - i - ); // Uncle proof can not take more than 2x time the first proof did. if (!_isVerifiable(state, fc)) { break; } else { - if (LibConstants.K_TOKENOMICS_ENABLED) { - uint256 newFeeBase; - { - uint256 reward; - uint256 tRelBp; // [0-10000], see the whitepaper - (newFeeBase, reward, tRelBp) = getProofReward({ - state: state, - provenAt: fc.provenAt, - proposedAt: target.proposedAt - }); - - if (address(tkoToken) == address(0)) { - tkoToken = TkoToken(resolver.resolve("tko_token")); - } - - _rewardProvers(fc, reward, tkoToken); - _refundProposerDeposit(target, tRelBp, tkoToken); - } - // Update feeBase and avgProofTime - state.feeBase = V1Utils.movingAverage({ - maValue: state.feeBase, - newValue: newFeeBase, - maf: LibConstants.K_FEE_BASE_MAF - }); - - state.avgProofTime = V1Utils - .movingAverage({ - maValue: state.avgProofTime, - newValue: fc.provenAt - target.proposedAt, - maf: LibConstants.K_PROOF_TIME_MAF - }) - .toUint64(); - } - if (fc.blockHash != LibConstants.K_BLOCK_DEADEND_HASH) { latestL2Height += 1; latestL2Hash = fc.blockHash; @@ -130,56 +87,6 @@ library V1Verifying { } } - function getProofReward( - LibData.State storage state, - uint64 provenAt, - uint64 proposedAt - ) public view returns (uint256 newFeeBase, uint256 reward, uint256 tRelBp) { - (newFeeBase, tRelBp) = V1Utils.getTimeAdjustedFee({ - state: state, - isProposal: false, - tNow: provenAt, - tLast: proposedAt, - tAvg: state.avgProofTime, - tCap: LibConstants.K_PROOF_TIME_CAP - }); - reward = V1Utils.getSlotsAdjustedFee({ - state: state, - isProposal: false, - feeBase: newFeeBase - }); - reward = (reward * (10000 - LibConstants.K_REWARD_BURN_BP)) / 10000; - } - - function _refundProposerDeposit( - LibData.ProposedBlock storage target, - uint256 tRelBp, - TkoToken tkoToken - ) private { - uint refund = (target.deposit * (10000 - tRelBp)) / 10000; - if (refund > 0) { - tkoToken.mint(target.proposer, refund); - } - } - - function _rewardProvers( - LibData.ForkChoice storage fc, - uint256 reward, - TkoToken tkoToken - ) private { - uint sum = 2 ** fc.provers.length - 1; - for (uint i = 0; i < fc.provers.length; i++) { - uint weight = (1 << (fc.provers.length - i - 1)); - uint proverReward = (reward * weight) / sum; - - if (tkoToken.balanceOf(fc.provers[i]) == 0) { - // reduce reward if the prover has 0 TKO balance. - proverReward /= 2; - } - tkoToken.mint(fc.provers[i], proverReward); - } - } - function _cleanUp(LibData.ForkChoice storage fc) private { fc.blockHash = 0; fc.provenAt = 0; diff --git a/packages/protocol/contracts/L2/V1TaikoL2.sol b/packages/protocol/contracts/L2/V1TaikoL2.sol index f562b428bdf..a313c70bdfc 100644 --- a/packages/protocol/contracts/L2/V1TaikoL2.sol +++ b/packages/protocol/contracts/L2/V1TaikoL2.sol @@ -56,7 +56,7 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { publicInputHash = _hashPublicInputs({ chainId: block.chainid, number: number, - feeBase: 0, + baseFee: 0, ancestors: ancestors }); } @@ -189,7 +189,7 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { _hashPublicInputs({ chainId: chainId, number: parentHeight, - feeBase: 0, + baseFee: 0, ancestors: ancestors }), "L2:publicInputHash" @@ -199,7 +199,7 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { publicInputHash = _hashPublicInputs({ chainId: chainId, number: number, - feeBase: 0, + baseFee: 0, ancestors: ancestors }); @@ -209,9 +209,9 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { function _hashPublicInputs( uint256 chainId, uint256 number, - uint256 feeBase, + uint256 baseFee, bytes32[255] memory ancestors ) private pure returns (bytes32) { - return keccak256(abi.encodePacked(chainId, number, feeBase, ancestors)); + return keccak256(abi.encodePacked(chainId, number, baseFee, ancestors)); } } diff --git a/packages/protocol/contracts/libs/LibConstants.sol b/packages/protocol/contracts/libs/LibConstants.sol index 1b6bf7e2a55..804497d523f 100644 --- a/packages/protocol/contracts/libs/LibConstants.sol +++ b/packages/protocol/contracts/libs/LibConstants.sol @@ -16,7 +16,6 @@ library LibConstants { uint256 public constant K_MAX_NUM_BLOCKS = 2049; // This number is calculated from K_MAX_NUM_BLOCKS to make // the 'the maximum value of the multiplier' close to 20.0 - uint256 public constant K_FEE_PREMIUM_LAMDA = 590; uint256 public constant K_ZKPROOFS_PER_BLOCK = 1; uint256 public constant K_VERIFICATION_DELAY = 60 minutes; uint256 public constant K_MAX_VERIFICATIONS_PER_TX = 20; @@ -26,21 +25,7 @@ library LibConstants { uint256 public constant K_BLOCK_MAX_TXS = 20; // TODO uint256 public constant K_TXLIST_MAX_BYTES = 10240; // TODO uint256 public constant K_TX_MIN_GAS_LIMIT = 21000; // TODO - uint256 public constant K_REWARD_BURN_BP = 100; // 100 basis points or 1% uint256 public constant K_ANCHOR_TX_GAS_LIMIT = 250000; - uint256 public constant K_PROPOSER_DEPOSIT_PCTG = 25; // 25% - - // Moving average factors - uint256 public constant K_FEE_BASE_MAF = 1024; - uint256 public constant K_BLOCK_TIME_MAF = 1024; - uint256 public constant K_PROOF_TIME_MAF = 1024; - - uint64 public constant K_REWARD_MULTIPLIER_PCTG = 400; // 400% - uint64 public constant K_FEE_GRACE_PERIOD_PCTG = 125; // 125% - uint64 public constant K_FEE_MAX_PERIOD_PCTG = 375; // 375% - uint64 public constant K_BLOCK_TIME_CAP = 48 seconds; - uint64 public constant K_PROOF_TIME_CAP = 60 minutes; - uint64 public constant K_HALVING = 180 days; bytes4 public constant K_ANCHOR_TX_SELECTOR = bytes4(keccak256("anchor(uint256,bytes32)")); @@ -48,6 +33,4 @@ library LibConstants { bytes32 public constant K_BLOCK_DEADEND_HASH = bytes32(uint256(1)); bytes32 public constant K_INVALIDATE_BLOCK_LOG_TOPIC = keccak256("BlockInvalidated(bytes32)"); - - bool public constant K_TOKENOMICS_ENABLED = true; } diff --git a/packages/protocol/test/L1/TaikoL1.test.ts b/packages/protocol/test/L1/TaikoL1.test.ts index fa8a92d9428..598dd4568ef 100644 --- a/packages/protocol/test/L1/TaikoL1.test.ts +++ b/packages/protocol/test/L1/TaikoL1.test.ts @@ -1,6 +1,5 @@ import { expect } from "chai" import { ethers } from "hardhat" -import { BigNumber } from "ethers" describe("TaikoL1", function () { async function deployTaikoL1Fixture() { @@ -50,8 +49,7 @@ describe("TaikoL1", function () { const genesisHash = randomBytes32() const taikoL1 = await TaikoL1Factory.deploy() - const feeBase = BigNumber.from(10).pow(18) - await taikoL1.init(addressManager.address, genesisHash, feeBase) + await taikoL1.init(addressManager.address, genesisHash) return { taikoL1, genesisHash } } From 855099e18e9940f31403f1405a803f6d77a60357 Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Fri, 2 Dec 2022 11:32:54 +0800 Subject: [PATCH 12/57] Revert "undo tokenomics" This reverts commit d51fe1f5f491c947c06ef4dfc4629f9641117fe6. --- packages/protocol/contracts/L1/LibData.sol | 3 + packages/protocol/contracts/L1/TaikoL1.sol | 25 ++++- .../protocol/contracts/L1/v1/V1Proposing.sol | 48 ++++++++++ packages/protocol/contracts/L1/v1/V1Utils.sol | 71 ++++++++++++++ .../protocol/contracts/L1/v1/V1Verifying.sol | 95 ++++++++++++++++++- packages/protocol/contracts/L2/V1TaikoL2.sol | 10 +- .../protocol/contracts/libs/LibConstants.sol | 17 ++++ packages/protocol/test/L1/TaikoL1.test.ts | 4 +- 8 files changed, 264 insertions(+), 9 deletions(-) diff --git a/packages/protocol/contracts/L1/LibData.sol b/packages/protocol/contracts/L1/LibData.sol index d753a798a83..6c6094b649c 100644 --- a/packages/protocol/contracts/L1/LibData.sol +++ b/packages/protocol/contracts/L1/LibData.sol @@ -29,6 +29,7 @@ library LibData { // 3 slots struct ProposedBlock { bytes32 metaHash; + uint256 deposit; address proposer; uint64 proposedAt; } @@ -55,6 +56,8 @@ library LibData { uint64 genesisTimestamp; uint64 __reservedA1; uint64 statusBits; // rarely change + // Changed when a block is proposed or proven/finalized + uint256 feeBase; // Changed when a block is proposed uint64 nextBlockId; uint64 lastProposedAt; // Timestamp when the last block is proposed. diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 245b49c0e4a..e776cd083dd 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -30,10 +30,15 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { function init( address _addressManager, - bytes32 _genesisBlockHash + bytes32 _genesisBlockHash, + uint256 _feeBase ) external initializer { EssentialContract._init(_addressManager); - V1Verifying.init({state: state, genesisBlockHash: _genesisBlockHash}); + V1Verifying.init({ + state: state, + genesisBlockHash: _genesisBlockHash, + feeBase: _feeBase + }); tentative.whitelistProposers = false; tentative.whitelistProvers = true; @@ -248,6 +253,22 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { return V1Utils.isProverWhitelisted(tentative, prover); } + function getBlockFee() public view returns (uint256) { + (, uint fee, uint deposit) = V1Proposing.getBlockFee(state); + return fee + deposit; + } + + function getProofReward( + uint64 provenAt, + uint64 proposedAt + ) public view returns (uint256 reward) { + (, reward, ) = V1Verifying.getProofReward({ + state: state, + provenAt: provenAt, + proposedAt: proposedAt + }); + } + /** * Check if the L1 is halted. * @return True if halted, false otherwise. diff --git a/packages/protocol/contracts/L1/v1/V1Proposing.sol b/packages/protocol/contracts/L1/v1/V1Proposing.sol index bdd7ed9b1f5..91544f57ca2 100644 --- a/packages/protocol/contracts/L1/v1/V1Proposing.sol +++ b/packages/protocol/contracts/L1/v1/V1Proposing.sol @@ -98,10 +98,38 @@ library V1Proposing { meta.mixHash = bytes32(block.difficulty); } + uint256 deposit; + if (LibConstants.K_TOKENOMICS_ENABLED) { + uint256 newFeeBase; + { + uint256 fee; + (newFeeBase, fee, deposit) = getBlockFee(state); + TkoToken(resolver.resolve("tko_token")).burn( + msg.sender, + fee + deposit + ); + } + // Update feeBase and avgBlockTime + state.feeBase = V1Utils.movingAverage({ + maValue: state.feeBase, + newValue: newFeeBase, + maf: LibConstants.K_FEE_BASE_MAF + }); + + state.avgBlockTime = V1Utils + .movingAverage({ + maValue: state.avgBlockTime, + newValue: meta.timestamp - state.lastProposedAt, + maf: LibConstants.K_BLOCK_TIME_MAF + }) + .toUint64(); + } + state.saveProposedBlock( state.nextBlockId, LibData.ProposedBlock({ metaHash: LibData.hashMetadata(meta), + deposit: deposit, proposer: msg.sender, proposedAt: meta.timestamp }) @@ -111,6 +139,26 @@ library V1Proposing { emit BlockProposed(state.nextBlockId++, meta); } + function getBlockFee( + LibData.State storage state + ) public view returns (uint256 newFeeBase, uint256 fee, uint256 deposit) { + (newFeeBase, ) = V1Utils.getTimeAdjustedFee({ + state: state, + isProposal: true, + tNow: uint64(block.timestamp), + tLast: state.lastProposedAt, + tAvg: state.avgBlockTime, + tCap: LibConstants.K_BLOCK_TIME_CAP + }); + fee = V1Utils.getSlotsAdjustedFee({ + state: state, + isProposal: true, + feeBase: newFeeBase + }); + fee = V1Utils.getBootstrapDiscountedFee(state, fee); + deposit = (fee * LibConstants.K_PROPOSER_DEPOSIT_PCTG) / 100; + } + function isCommitValid( LibData.State storage state, uint256 commitSlot, diff --git a/packages/protocol/contracts/L1/v1/V1Utils.sol b/packages/protocol/contracts/L1/v1/V1Utils.sol index b339b911899..168254a4f25 100644 --- a/packages/protocol/contracts/L1/v1/V1Utils.sol +++ b/packages/protocol/contracts/L1/v1/V1Utils.sol @@ -93,6 +93,65 @@ library V1Utils { return tentative.provers[prover]; } + // Implement "Incentive Multipliers", see the whitepaper. + function getTimeAdjustedFee( + LibData.State storage state, + bool isProposal, + uint64 tNow, + uint64 tLast, + uint64 tAvg, + uint64 tCap + ) internal view returns (uint256 newFeeBase, uint256 tRelBp) { + if (tAvg == 0) { + newFeeBase = state.feeBase; + tRelBp = 0; + } else { + uint256 _tAvg = tAvg > tCap ? tCap : tAvg; + uint256 tGrace = (LibConstants.K_FEE_GRACE_PERIOD_PCTG * _tAvg) / + 100; + uint256 tMax = (LibConstants.K_FEE_MAX_PERIOD_PCTG * _tAvg) / 100; + uint256 a = tLast + tGrace; + uint256 b = tNow > a ? tNow - a : 0; + tRelBp = (b.min(tMax) * 10000) / tMax; // [0 - 10000] + uint256 alpha = 10000 + + ((LibConstants.K_REWARD_MULTIPLIER_PCTG - 100) * tRelBp) / + 100; + if (isProposal) { + newFeeBase = (state.feeBase * 10000) / alpha; // fee + } else { + newFeeBase = (state.feeBase * alpha) / 10000; // reward + } + } + } + + // Implement "Slot-availability Multipliers", see the whitepaper. + function getSlotsAdjustedFee( + LibData.State storage state, + bool isProposal, + uint256 feeBase + ) internal view returns (uint256) { + // m is the `n'` in the whitepaper + uint256 m = LibConstants.K_MAX_NUM_BLOCKS - + 1 + + LibConstants.K_FEE_PREMIUM_LAMDA; + // n is the number of unverified blocks + uint256 n = state.nextBlockId - state.latestVerifiedId - 1; + // k is `m − n + 1` or `m − n - 1`in the whitepaper + uint256 k = isProposal ? m - n - 1 : m - n + 1; + return (feeBase * (m - 1) * m) / (m - n) / k; + } + + // Implement "Bootstrap Discount Multipliers", see the whitepaper. + function getBootstrapDiscountedFee( + LibData.State storage state, + uint256 feeBase + ) internal view returns (uint256) { + uint256 halves = uint256(block.timestamp - state.genesisTimestamp) / + LibConstants.K_HALVING; + uint256 gamma = 1024 - (1024 >> halves); + return (feeBase * gamma) / 1024; + } + // Returns a deterministic deadline for uncle proof submission. function uncleProofDeadline( LibData.State storage state, @@ -101,6 +160,18 @@ library V1Utils { return fc.provenAt + state.avgProofTime; } + function movingAverage( + uint256 maValue, + uint256 newValue, + uint256 maf + ) internal pure returns (uint256) { + if (maValue == 0) { + return newValue; + } + uint256 _ma = (maValue * (maf - 1) + newValue) / maf; + return _ma > 0 ? _ma : maValue; + } + function setBit( LibData.State storage state, uint64 mask, diff --git a/packages/protocol/contracts/L1/v1/V1Verifying.sol b/packages/protocol/contracts/L1/v1/V1Verifying.sol index 03ea7876d90..7c0988ab661 100644 --- a/packages/protocol/contracts/L1/v1/V1Verifying.sol +++ b/packages/protocol/contracts/L1/v1/V1Verifying.sol @@ -25,10 +25,14 @@ library V1Verifying { function init( LibData.State storage state, - bytes32 genesisBlockHash + bytes32 genesisBlockHash, + uint256 feeBase ) public { + require(feeBase > 0, "L1:feeBase"); + state.genesisHeight = uint64(block.number); state.genesisTimestamp = uint64(block.timestamp); + state.feeBase = feeBase; state.nextBlockId = 1; state.lastProposedAt = uint64(block.timestamp); state.l2Hashes[0] = genesisBlockHash; @@ -54,6 +58,7 @@ library V1Verifying { uint64 latestL2Height = state.latestVerifiedHeight; bytes32 latestL2Hash = state.l2Hashes[latestL2Height]; uint64 processed = 0; + TkoToken tkoToken; for ( uint256 i = state.latestVerifiedId + 1; @@ -61,11 +66,49 @@ library V1Verifying { i++ ) { LibData.ForkChoice storage fc = state.forkChoices[i][latestL2Hash]; + LibData.ProposedBlock storage target = LibData.getProposedBlock( + state, + i + ); // Uncle proof can not take more than 2x time the first proof did. if (!_isVerifiable(state, fc)) { break; } else { + if (LibConstants.K_TOKENOMICS_ENABLED) { + uint256 newFeeBase; + { + uint256 reward; + uint256 tRelBp; // [0-10000], see the whitepaper + (newFeeBase, reward, tRelBp) = getProofReward({ + state: state, + provenAt: fc.provenAt, + proposedAt: target.proposedAt + }); + + if (address(tkoToken) == address(0)) { + tkoToken = TkoToken(resolver.resolve("tko_token")); + } + + _rewardProvers(fc, reward, tkoToken); + _refundProposerDeposit(target, tRelBp, tkoToken); + } + // Update feeBase and avgProofTime + state.feeBase = V1Utils.movingAverage({ + maValue: state.feeBase, + newValue: newFeeBase, + maf: LibConstants.K_FEE_BASE_MAF + }); + + state.avgProofTime = V1Utils + .movingAverage({ + maValue: state.avgProofTime, + newValue: fc.provenAt - target.proposedAt, + maf: LibConstants.K_PROOF_TIME_MAF + }) + .toUint64(); + } + if (fc.blockHash != LibConstants.K_BLOCK_DEADEND_HASH) { latestL2Height += 1; latestL2Hash = fc.blockHash; @@ -87,6 +130,56 @@ library V1Verifying { } } + function getProofReward( + LibData.State storage state, + uint64 provenAt, + uint64 proposedAt + ) public view returns (uint256 newFeeBase, uint256 reward, uint256 tRelBp) { + (newFeeBase, tRelBp) = V1Utils.getTimeAdjustedFee({ + state: state, + isProposal: false, + tNow: provenAt, + tLast: proposedAt, + tAvg: state.avgProofTime, + tCap: LibConstants.K_PROOF_TIME_CAP + }); + reward = V1Utils.getSlotsAdjustedFee({ + state: state, + isProposal: false, + feeBase: newFeeBase + }); + reward = (reward * (10000 - LibConstants.K_REWARD_BURN_BP)) / 10000; + } + + function _refundProposerDeposit( + LibData.ProposedBlock storage target, + uint256 tRelBp, + TkoToken tkoToken + ) private { + uint refund = (target.deposit * (10000 - tRelBp)) / 10000; + if (refund > 0) { + tkoToken.mint(target.proposer, refund); + } + } + + function _rewardProvers( + LibData.ForkChoice storage fc, + uint256 reward, + TkoToken tkoToken + ) private { + uint sum = 2 ** fc.provers.length - 1; + for (uint i = 0; i < fc.provers.length; i++) { + uint weight = (1 << (fc.provers.length - i - 1)); + uint proverReward = (reward * weight) / sum; + + if (tkoToken.balanceOf(fc.provers[i]) == 0) { + // reduce reward if the prover has 0 TKO balance. + proverReward /= 2; + } + tkoToken.mint(fc.provers[i], proverReward); + } + } + function _cleanUp(LibData.ForkChoice storage fc) private { fc.blockHash = 0; fc.provenAt = 0; diff --git a/packages/protocol/contracts/L2/V1TaikoL2.sol b/packages/protocol/contracts/L2/V1TaikoL2.sol index a313c70bdfc..f562b428bdf 100644 --- a/packages/protocol/contracts/L2/V1TaikoL2.sol +++ b/packages/protocol/contracts/L2/V1TaikoL2.sol @@ -56,7 +56,7 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { publicInputHash = _hashPublicInputs({ chainId: block.chainid, number: number, - baseFee: 0, + feeBase: 0, ancestors: ancestors }); } @@ -189,7 +189,7 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { _hashPublicInputs({ chainId: chainId, number: parentHeight, - baseFee: 0, + feeBase: 0, ancestors: ancestors }), "L2:publicInputHash" @@ -199,7 +199,7 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { publicInputHash = _hashPublicInputs({ chainId: chainId, number: number, - baseFee: 0, + feeBase: 0, ancestors: ancestors }); @@ -209,9 +209,9 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { function _hashPublicInputs( uint256 chainId, uint256 number, - uint256 baseFee, + uint256 feeBase, bytes32[255] memory ancestors ) private pure returns (bytes32) { - return keccak256(abi.encodePacked(chainId, number, baseFee, ancestors)); + return keccak256(abi.encodePacked(chainId, number, feeBase, ancestors)); } } diff --git a/packages/protocol/contracts/libs/LibConstants.sol b/packages/protocol/contracts/libs/LibConstants.sol index 804497d523f..1b6bf7e2a55 100644 --- a/packages/protocol/contracts/libs/LibConstants.sol +++ b/packages/protocol/contracts/libs/LibConstants.sol @@ -16,6 +16,7 @@ library LibConstants { uint256 public constant K_MAX_NUM_BLOCKS = 2049; // This number is calculated from K_MAX_NUM_BLOCKS to make // the 'the maximum value of the multiplier' close to 20.0 + uint256 public constant K_FEE_PREMIUM_LAMDA = 590; uint256 public constant K_ZKPROOFS_PER_BLOCK = 1; uint256 public constant K_VERIFICATION_DELAY = 60 minutes; uint256 public constant K_MAX_VERIFICATIONS_PER_TX = 20; @@ -25,7 +26,21 @@ library LibConstants { uint256 public constant K_BLOCK_MAX_TXS = 20; // TODO uint256 public constant K_TXLIST_MAX_BYTES = 10240; // TODO uint256 public constant K_TX_MIN_GAS_LIMIT = 21000; // TODO + uint256 public constant K_REWARD_BURN_BP = 100; // 100 basis points or 1% uint256 public constant K_ANCHOR_TX_GAS_LIMIT = 250000; + uint256 public constant K_PROPOSER_DEPOSIT_PCTG = 25; // 25% + + // Moving average factors + uint256 public constant K_FEE_BASE_MAF = 1024; + uint256 public constant K_BLOCK_TIME_MAF = 1024; + uint256 public constant K_PROOF_TIME_MAF = 1024; + + uint64 public constant K_REWARD_MULTIPLIER_PCTG = 400; // 400% + uint64 public constant K_FEE_GRACE_PERIOD_PCTG = 125; // 125% + uint64 public constant K_FEE_MAX_PERIOD_PCTG = 375; // 375% + uint64 public constant K_BLOCK_TIME_CAP = 48 seconds; + uint64 public constant K_PROOF_TIME_CAP = 60 minutes; + uint64 public constant K_HALVING = 180 days; bytes4 public constant K_ANCHOR_TX_SELECTOR = bytes4(keccak256("anchor(uint256,bytes32)")); @@ -33,4 +48,6 @@ library LibConstants { bytes32 public constant K_BLOCK_DEADEND_HASH = bytes32(uint256(1)); bytes32 public constant K_INVALIDATE_BLOCK_LOG_TOPIC = keccak256("BlockInvalidated(bytes32)"); + + bool public constant K_TOKENOMICS_ENABLED = true; } diff --git a/packages/protocol/test/L1/TaikoL1.test.ts b/packages/protocol/test/L1/TaikoL1.test.ts index 598dd4568ef..fa8a92d9428 100644 --- a/packages/protocol/test/L1/TaikoL1.test.ts +++ b/packages/protocol/test/L1/TaikoL1.test.ts @@ -1,5 +1,6 @@ import { expect } from "chai" import { ethers } from "hardhat" +import { BigNumber } from "ethers" describe("TaikoL1", function () { async function deployTaikoL1Fixture() { @@ -49,7 +50,8 @@ describe("TaikoL1", function () { const genesisHash = randomBytes32() const taikoL1 = await TaikoL1Factory.deploy() - await taikoL1.init(addressManager.address, genesisHash) + const feeBase = BigNumber.from(10).pow(18) + await taikoL1.init(addressManager.address, genesisHash, feeBase) return { taikoL1, genesisHash } } From de14fc5e7070504169ac5fd9e8ea7f38683cd99a Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Fri, 2 Dec 2022 11:33:52 +0800 Subject: [PATCH 13/57] refactor (#374) * Copy impl tokenomics (#373) * undo tokenomics --- .gitignore | 6 +- packages/protocol/.solhintignore | 2 + packages/protocol/contracts/L1/LibData.sol | 56 +++-- packages/protocol/contracts/L1/TaikoL1.sol | 193 ++++++++++------ packages/protocol/contracts/L1/TkoToken.sol | 8 +- .../protocol/contracts/L1/v1/V1Events.sol | 4 + .../protocol/contracts/L1/v1/V1Finalizing.sol | 87 -------- .../protocol/contracts/L1/v1/V1Proposing.sol | 145 +++++++----- .../protocol/contracts/L1/v1/V1Proving.sol | 206 ++++++++---------- packages/protocol/contracts/L1/v1/V1Utils.sol | 96 +++++++- .../protocol/contracts/L1/v1/V1Verifying.sol | 107 +++++++++ packages/protocol/contracts/L2/V1TaikoL2.sol | 89 ++++---- packages/protocol/contracts/bridge/Bridge.sol | 65 +++--- .../contracts/bridge/BridgedERC20.sol | 12 +- .../protocol/contracts/bridge/EtherVault.sol | 6 +- .../protocol/contracts/bridge/TokenVault.sol | 42 ++-- .../bridge/libs/LibBridgeProcess.sol | 27 +-- .../contracts/bridge/libs/LibBridgeRetry.sol | 7 +- .../contracts/bridge/libs/LibBridgeSignal.sol | 14 +- .../contracts/common/AddressResolver.sol | 3 +- .../contracts/common/EssentialContract.sol | 1 + .../contracts/libs/LibAnchorSignature.sol | 8 +- .../contracts/libs/LibBlockHeader.sol | 2 +- .../protocol/contracts/libs/LibConstants.sol | 36 ++- .../contracts/libs/LibInvalidTxList.sol | 19 +- .../protocol/contracts/libs/LibTxDecoder.sol | 5 +- .../protocol/contracts/libs/LibTxUtils.sol | 4 +- .../test/libs/TestLibAnchorSignature.sol | 4 +- packages/protocol/docs/L1/LibData.md | 13 +- packages/protocol/docs/L1/TaikoL1.md | 36 ++- packages/protocol/docs/L1/v1/V1Finalizing.md | 8 +- packages/protocol/docs/L1/v1/V1Proposing.md | 14 +- packages/protocol/docs/L1/v1/V1Proving.md | 4 +- packages/protocol/docs/L1/v1/V1Utils.md | 19 ++ packages/protocol/docs/L2/V1TaikoL2.md | 2 +- .../protocol/docs/libs/LibAnchorSignature.md | 8 +- packages/protocol/docs/libs/LibConstants.md | 82 ++++--- .../protocol/docs/libs/LibInvalidTxList.md | 8 +- packages/protocol/hardhat.config.ts | 4 +- packages/protocol/tasks/config.ts | 4 +- packages/protocol/tasks/deploy_L1.ts | 26 +-- packages/protocol/test/L1/TaikoL1.test.ts | 22 +- .../test/bridge/libs/LibBridgeData.test.ts | 6 +- packages/protocol/test/constants/messages.ts | 4 +- .../test/genesis/generate_genesis.test.ts | 2 +- .../protocol/test/libs/LibTxUtils.test.ts | 2 +- 46 files changed, 908 insertions(+), 610 deletions(-) delete mode 100644 packages/protocol/contracts/L1/v1/V1Finalizing.sol create mode 100644 packages/protocol/contracts/L1/v1/V1Verifying.sol create mode 100644 packages/protocol/docs/L1/v1/V1Utils.md diff --git a/.gitignore b/.gitignore index 6c624e2e1f7..a718c289141 100644 --- a/.gitignore +++ b/.gitignore @@ -109,4 +109,8 @@ dist # vscode .vscode/ -.pdf \ No newline at end of file +# python +__pycache__/ + +# whitepaper +.pdf diff --git a/packages/protocol/.solhintignore b/packages/protocol/.solhintignore index f64817aa3b1..b15d9ed051c 100644 --- a/packages/protocol/.solhintignore +++ b/packages/protocol/.solhintignore @@ -3,4 +3,6 @@ contracts/aux/tokens/ERC20Upgradeable.sol contracts/test/TestLibRLPReader.sol contracts/test/TestLibRLPWriter.sol contracts/libs/Lib1559Math.sol +contracts/libs/LibAddress.sol +contracts/libs/LibMath.sol **/contracts/thirdparty/**/*.sol \ No newline at end of file diff --git a/packages/protocol/contracts/L1/LibData.sol b/packages/protocol/contracts/L1/LibData.sol index cdfb3134016..d753a798a83 100644 --- a/packages/protocol/contracts/L1/LibData.sol +++ b/packages/protocol/contracts/L1/LibData.sol @@ -26,17 +26,21 @@ library LibData { uint64 commitSlot; } + // 3 slots struct ProposedBlock { bytes32 metaHash; + address proposer; + uint64 proposedAt; } + // 3 + n slots struct ForkChoice { bytes32 blockHash; - uint64 proposedAt; uint64 provenAt; address[] provers; } + // This struct takes 9 slots. struct State { // block id => block hash mapping(uint256 => bytes32) l2Hashes; @@ -46,39 +50,59 @@ library LibData { mapping(uint256 => mapping(bytes32 => ForkChoice)) forkChoices; // proposer => commitSlot => hash(commitHash, commitHeight) mapping(address => mapping(uint256 => bytes32)) commits; - mapping(address => bool) provers; // Whitelisted provers - uint64 statusBits; + // Never or rarely changed uint64 genesisHeight; + uint64 genesisTimestamp; + uint64 __reservedA1; + uint64 statusBits; // rarely change + // Changed when a block is proposed + uint64 nextBlockId; + uint64 lastProposedAt; // Timestamp when the last block is proposed. + uint64 avgBlockTime; // The block time moving average + uint64 __avgGasLimit; // the block gaslimit moving average, not updated. + // Changed when a block is proven/finalized uint64 latestVerifiedHeight; uint64 latestVerifiedId; - uint64 nextBlockId; + uint64 avgProofTime; // the proof time moving average + uint64 __reservedC1; + // Reserved + uint256[42] __gap; + } + + struct TentativeState { + mapping(address => bool) proposers; // Whitelisted proposers + mapping(address => bool) provers; // Whitelisted provers + bool whitelistProposers; + bool whitelistProvers; + // // Reserved + uint256[46] __gap; } function saveProposedBlock( - LibData.State storage s, + LibData.State storage state, uint256 id, ProposedBlock memory blk ) internal { - s.proposedBlocks[id % LibConstants.TAIKO_MAX_PROPOSED_BLOCKS] = blk; + state.proposedBlocks[id % LibConstants.K_MAX_NUM_BLOCKS] = blk; } function getProposedBlock( - State storage s, + State storage state, uint256 id ) internal view returns (ProposedBlock storage) { - return s.proposedBlocks[id % LibConstants.TAIKO_MAX_PROPOSED_BLOCKS]; + return state.proposedBlocks[id % LibConstants.K_MAX_NUM_BLOCKS]; } function getL2BlockHash( - State storage s, + State storage state, uint256 number ) internal view returns (bytes32) { - require(number <= s.latestVerifiedHeight, "L1:id"); - return s.l2Hashes[number]; + require(number <= state.latestVerifiedHeight, "L1:id"); + return state.l2Hashes[number]; } function getStateVariables( - State storage s + State storage state ) internal view @@ -89,10 +113,10 @@ library LibData { uint64 nextBlockId ) { - genesisHeight = s.genesisHeight; - latestVerifiedHeight = s.latestVerifiedHeight; - latestVerifiedId = s.latestVerifiedId; - nextBlockId = s.nextBlockId; + genesisHeight = state.genesisHeight; + latestVerifiedHeight = state.latestVerifiedHeight; + latestVerifiedId = state.latestVerifiedId; + nextBlockId = state.nextBlockId; } function hashMetadata( diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 07c149d785d..245b49c0e4a 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -8,36 +8,35 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; -import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; - -import "../common/ConfigManager.sol"; import "../common/EssentialContract.sol"; import "../common/IHeaderSync.sol"; import "../libs/LibAnchorSignature.sol"; import "./LibData.sol"; import "./v1/V1Events.sol"; -import "./v1/V1Finalizing.sol"; import "./v1/V1Proposing.sol"; import "./v1/V1Proving.sol"; import "./v1/V1Utils.sol"; +import "./v1/V1Verifying.sol"; /** * @author dantaik */ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { using LibData for LibData.State; - using LibTxDecoder for bytes; - using SafeCastUpgradeable for uint256; LibData.State public state; - uint256[43] private __gap; + LibData.TentativeState public tentative; + uint256[50] private __gap; function init( address _addressManager, bytes32 _genesisBlockHash ) external initializer { EssentialContract._init(_addressManager); - V1Finalizing.init(state, _genesisBlockHash); + V1Verifying.init({state: state, genesisBlockHash: _genesisBlockHash}); + + tentative.whitelistProposers = false; + tentative.whitelistProvers = true; } /** @@ -74,19 +73,25 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { * transactions in the L2 block. */ function proposeBlock(bytes[] calldata inputs) external nonReentrant { - V1Proposing.proposeBlock(state, inputs); - V1Finalizing.verifyBlocks( - state, - LibConstants.TAIKO_MAX_VERIFICATIONS_PER_TX, - false - ); + V1Proposing.proposeBlock({ + state: state, + tentative: tentative, + resolver: AddressResolver(this), + inputs: inputs + }); + V1Verifying.verifyBlocks({ + state: state, + resolver: AddressResolver(this), + maxBlocks: LibConstants.K_MAX_VERIFICATIONS_PER_TX, + checkHalt: false + }); } /** * Prove a block is valid with a zero-knowledge proof, a transaction * merkel proof, and a receipt merkel proof. * - * @param blockIndex The index of the block to prove. This is also used + * @param blockId The index of the block to prove. This is also used * to select the right implementation version. * @param inputs A list of data input: * - inputs[0] is an abi-encoded object with various information @@ -98,22 +103,29 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { */ function proveBlock( - uint256 blockIndex, + uint256 blockId, bytes[] calldata inputs ) external nonReentrant { - V1Proving.proveBlock(state, AddressResolver(this), blockIndex, inputs); - V1Finalizing.verifyBlocks( - state, - LibConstants.TAIKO_MAX_VERIFICATIONS_PER_TX, - false - ); + V1Proving.proveBlock({ + state: state, + tentative: tentative, + resolver: AddressResolver(this), + blockId: blockId, + inputs: inputs + }); + V1Verifying.verifyBlocks({ + state: state, + resolver: AddressResolver(this), + maxBlocks: LibConstants.K_MAX_VERIFICATIONS_PER_TX, + checkHalt: false + }); } /** * Prove a block is invalid with a zero-knowledge proof and a receipt * merkel proof. * - * @param blockIndex The index of the block to prove. This is also used to + * @param blockId The index of the block to prove. This is also used to * select the right implementation version. * @param inputs A list of data input: * - inputs[0] An Evidence object with various information regarding @@ -124,20 +136,22 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { * be the only transaction in the L2 block. */ function proveBlockInvalid( - uint256 blockIndex, + uint256 blockId, bytes[] calldata inputs ) external nonReentrant { - V1Proving.proveBlockInvalid( - state, - AddressResolver(this), - blockIndex, - inputs - ); - V1Finalizing.verifyBlocks( - state, - LibConstants.TAIKO_MAX_VERIFICATIONS_PER_TX, - false - ); + V1Proving.proveBlockInvalid({ + state: state, + tentative: tentative, + resolver: AddressResolver(this), + blockId: blockId, + inputs: inputs + }); + V1Verifying.verifyBlocks({ + state: state, + resolver: AddressResolver(this), + maxBlocks: LibConstants.K_MAX_VERIFICATIONS_PER_TX, + checkHalt: false + }); } /** @@ -146,10 +160,49 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { */ function verifyBlocks(uint256 maxBlocks) external nonReentrant { require(maxBlocks > 0, "L1:maxBlocks"); - V1Finalizing.verifyBlocks(state, maxBlocks, true); + V1Verifying.verifyBlocks({ + state: state, + resolver: AddressResolver(this), + maxBlocks: maxBlocks, + checkHalt: true + }); + } + + /** + * Enable or disable proposer and prover whitelisting + * @param whitelistProposers True to enable proposer whitelisting. + * @param whitelistProvers True to enable prover whitelisting. + */ + function enableWhitelisting( + bool whitelistProposers, + bool whitelistProvers + ) public onlyOwner { + V1Utils.enableWhitelisting({ + tentative: tentative, + whitelistProposers: whitelistProposers, + whitelistProvers: whitelistProvers + }); + } + + /** + * Add or remove a proposer from the whitelist. + * + * @param proposer The proposer to be added or removed. + * @param whitelisted True to add; remove otherwise. + */ + function whitelistProposer( + address proposer, + bool whitelisted + ) public onlyOwner { + V1Utils.whitelistProposer({ + tentative: tentative, + proposer: proposer, + whitelisted: whitelisted + }); } - /* Add or remove a prover from the whitelist. + /** + * Add or remove a prover from the whitelist. * * @param prover The prover to be added or removed. * @param whitelisted True to add; remove otherwise. @@ -158,7 +211,11 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { address prover, bool whitelisted ) public onlyOwner { - V1Proving.whitelistProver(state, prover, whitelisted); + V1Utils.whitelistProver({ + tentative: tentative, + prover: prover, + whitelisted: whitelisted + }); } /** @@ -169,6 +226,18 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { V1Utils.halt(state, toHalt); } + /** + * Check whether a proposer is whitelisted. + * + * @param proposer The proposer. + * @return True if the proposer is whitelisted, false otherwise. + */ + function isProposerWhitelisted( + address proposer + ) public view returns (bool) { + return V1Utils.isProposerWhitelisted(tentative, proposer); + } + /** * Check whether a prover is whitelisted. * @@ -176,7 +245,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { * @return True if the prover is whitelisted, false otherwise. */ function isProverWhitelisted(address prover) public view returns (bool) { - return V1Proving.isProverWhitelisted(state, prover); + return V1Utils.isProverWhitelisted(tentative, prover); } /** @@ -249,36 +318,30 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { pure returns ( uint256, // K_ZKPROOFS_PER_BLOCK - uint256, // TAIKO_CHAIN_ID - uint256, // TAIKO_MAX_PROPOSED_BLOCKS - uint256, // TAIKO_MAX_VERIFICATIONS_PER_TX - uint256, // K_COMMIT_DELAY_CONFIRMATIONS - uint256, // TAIKO_MAX_PROOFS_PER_FORK_CHOICE - uint256, // TAIKO_BLOCK_MAX_GAS_LIMIT - uint256, // TAIKO_BLOCK_MAX_TXS - bytes32, // TAIKO_BLOCK_DEADEND_HASH - uint256, // TAIKO_TXLIST_MAX_BYTES - uint256, // TAIKO_TX_MIN_GAS_LIMIT - uint256, // V1_ANCHOR_TX_GAS_LIMIT - bytes4, // V1_ANCHOR_TX_SELECTOR - bytes32 // V1_INVALIDATE_BLOCK_LOG_TOPIC + uint256, // K_CHAIN_ID + uint256, // K_MAX_NUM_BLOCKS + uint256, // K_MAX_VERIFICATIONS_PER_TX + uint256, // K_COMMIT_DELAY_CONFIRMS + uint256, // K_MAX_PROOFS_PER_FORK_CHOICE + uint256, // K_BLOCK_MAX_GAS_LIMIT + uint256, // K_BLOCK_MAX_TXS + uint256, // K_TXLIST_MAX_BYTES + uint256, // K_TX_MIN_GAS_LIMIT + uint256 // K_ANCHOR_TX_GAS_LIMIT ) { return ( LibConstants.K_ZKPROOFS_PER_BLOCK, - LibConstants.TAIKO_CHAIN_ID, - LibConstants.TAIKO_MAX_PROPOSED_BLOCKS, - LibConstants.TAIKO_MAX_VERIFICATIONS_PER_TX, - LibConstants.K_COMMIT_DELAY_CONFIRMATIONS, - LibConstants.TAIKO_MAX_PROOFS_PER_FORK_CHOICE, - LibConstants.TAIKO_BLOCK_MAX_GAS_LIMIT, - LibConstants.TAIKO_BLOCK_MAX_TXS, - LibConstants.TAIKO_BLOCK_DEADEND_HASH, - LibConstants.TAIKO_TXLIST_MAX_BYTES, - LibConstants.TAIKO_TX_MIN_GAS_LIMIT, - LibConstants.V1_ANCHOR_TX_GAS_LIMIT, - LibConstants.V1_ANCHOR_TX_SELECTOR, - LibConstants.V1_INVALIDATE_BLOCK_LOG_TOPIC + LibConstants.K_CHAIN_ID, + LibConstants.K_MAX_NUM_BLOCKS, + LibConstants.K_MAX_VERIFICATIONS_PER_TX, + LibConstants.K_COMMIT_DELAY_CONFIRMS, + LibConstants.K_MAX_PROOFS_PER_FORK_CHOICE, + LibConstants.K_BLOCK_MAX_GAS_LIMIT, + LibConstants.K_BLOCK_MAX_TXS, + LibConstants.K_TXLIST_MAX_BYTES, + LibConstants.K_TX_MIN_GAS_LIMIT, + LibConstants.K_ANCHOR_TX_GAS_LIMIT ); } } diff --git a/packages/protocol/contracts/L1/TkoToken.sol b/packages/protocol/contracts/L1/TkoToken.sol index a6a44c70972..58e8979f339 100644 --- a/packages/protocol/contracts/L1/TkoToken.sol +++ b/packages/protocol/contracts/L1/TkoToken.sol @@ -16,7 +16,7 @@ import "../libs/LibMath.sol"; import "../thirdparty/ERC20Upgradeable.sol"; /// @author dantaik -/// @dev This is Taiko's governance token. +/// @dev This is Taiko's governance and fee token. contract TkoToken is EssentialContract, ERC20Upgradeable, IMintableERC20 { using LibMath for uint256; using SafeCastUpgradeable for uint256; @@ -42,7 +42,11 @@ contract TkoToken is EssentialContract, ERC20Upgradeable, IMintableERC20 { /// amountMintToDAO and amountMintToDev shall be set to ~150,000,000. function init(address _addressManager) external initializer { EssentialContract._init(_addressManager); - ERC20Upgradeable.__ERC20_init("Taiko Token", "TKO", 18); + ERC20Upgradeable.__ERC20_init({ + name_: "Taiko Token", + symbol_: "TKO", + decimals_: 18 + }); } /********************* diff --git a/packages/protocol/contracts/L1/v1/V1Events.sol b/packages/protocol/contracts/L1/v1/V1Events.sol index 70ecde99b58..e9dd9d3a933 100644 --- a/packages/protocol/contracts/L1/v1/V1Events.sol +++ b/packages/protocol/contracts/L1/v1/V1Events.sol @@ -32,6 +32,10 @@ abstract contract V1Events { address prover ); + event WhitelistingEnabled(bool whitelistProposers, bool whitelistProvers); + + event ProposerWhitelisted(address indexed prover, bool whitelisted); + event ProverWhitelisted(address indexed prover, bool whitelisted); event Halted(bool halted); diff --git a/packages/protocol/contracts/L1/v1/V1Finalizing.sol b/packages/protocol/contracts/L1/v1/V1Finalizing.sol deleted file mode 100644 index f5f83c3f65b..00000000000 --- a/packages/protocol/contracts/L1/v1/V1Finalizing.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ -// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ -// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ -// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ -// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ -// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ -pragma solidity ^0.8.9; - -import "./V1Utils.sol"; - -/// @author dantaik -library V1Finalizing { - event BlockVerified(uint256 indexed id, bytes32 blockHash); - - event HeaderSynced( - uint256 indexed height, - uint256 indexed srcHeight, - bytes32 srcHash - ); - - function init(LibData.State storage s, bytes32 _genesisBlockHash) public { - s.l2Hashes[0] = _genesisBlockHash; - s.nextBlockId = 1; - s.genesisHeight = uint64(block.number); - - emit BlockVerified(0, _genesisBlockHash); - emit HeaderSynced(block.number, 0, _genesisBlockHash); - } - - function verifyBlocks( - LibData.State storage s, - uint256 maxBlocks, - bool checkHalt - ) public { - bool halted = V1Utils.isHalted(s); - if (checkHalt) { - assert(!halted); - } else if (halted) { - // skip finalizing blocks - return; - } - - uint64 latestL2Height = s.latestVerifiedHeight; - bytes32 latestL2Hash = s.l2Hashes[latestL2Height]; - uint64 processed = 0; - - for ( - uint256 i = s.latestVerifiedId + 1; - i < s.nextBlockId && processed <= maxBlocks; - i++ - ) { - LibData.ForkChoice storage fc = s.forkChoices[i][latestL2Hash]; - - // TODO(daniel): use the average proof-time. - if ( - block.timestamp <= - fc.provenAt + LibConstants.K_VERIFICATION_DELAY - ) { - // This block is proven but still needs to wait for verificaiton. - break; - } - - if (fc.blockHash == LibConstants.TAIKO_BLOCK_DEADEND_HASH) { - emit BlockVerified(i, 0); - } else if (fc.blockHash != 0) { - latestL2Height += 1; - latestL2Hash = fc.blockHash; - emit BlockVerified(i, latestL2Hash); - } else { - break; - } - processed += 1; - } - - if (processed > 0) { - s.latestVerifiedId += processed; - - if (latestL2Height > s.latestVerifiedHeight) { - s.latestVerifiedHeight = latestL2Height; - s.l2Hashes[latestL2Height] = latestL2Hash; - emit HeaderSynced(block.number, latestL2Height, latestL2Hash); - } - } - } -} diff --git a/packages/protocol/contracts/L1/v1/V1Proposing.sol b/packages/protocol/contracts/L1/v1/V1Proposing.sol index 8f0af0acea9..bdd7ed9b1f5 100644 --- a/packages/protocol/contracts/L1/v1/V1Proposing.sol +++ b/packages/protocol/contracts/L1/v1/V1Proposing.sol @@ -11,6 +11,7 @@ pragma solidity ^0.8.9; import "../../common/ConfigManager.sol"; import "../../libs/LibConstants.sol"; import "../../libs/LibTxDecoder.sol"; +import "../TkoToken.sol"; import "./V1Utils.sol"; /// @author dantaik @@ -26,104 +27,130 @@ library V1Proposing { ); event BlockProposed(uint256 indexed id, LibData.BlockMetadata meta); + modifier onlyWhitelistedProposer(LibData.TentativeState storage tentative) { + if (tentative.whitelistProposers) { + require(tentative.proposers[msg.sender], "L1:whitelist"); + } + _; + } + function commitBlock( - LibData.State storage s, + LibData.State storage state, uint64 commitSlot, bytes32 commitHash ) public { - assert(LibConstants.K_COMMIT_DELAY_CONFIRMATIONS > 0); + assert(LibConstants.K_COMMIT_DELAY_CONFIRMS > 0); // It's OK to allow committing block when the system is halt. // By not checking the halt status, this method will be cheaper. // - // assert(!V1Utils.isHalted(s)); + // assert(!V1Utils.isHalted(state)); bytes32 hash = _aggregateCommitHash(block.number, commitHash); - require(s.commits[msg.sender][commitSlot] != hash, "L1:committed"); - s.commits[msg.sender][commitSlot] = hash; + require(state.commits[msg.sender][commitSlot] != hash, "L1:committed"); + state.commits[msg.sender][commitSlot] = hash; - emit BlockCommitted(commitSlot, uint64(block.number), commitHash); + emit BlockCommitted({ + commitSlot: commitSlot, + commitHeight: uint64(block.number), + commitHash: commitHash + }); } function proposeBlock( - LibData.State storage s, + LibData.State storage state, + LibData.TentativeState storage tentative, + AddressResolver resolver, bytes[] calldata inputs - ) public { - assert(!V1Utils.isHalted(s)); + ) public onlyWhitelistedProposer(tentative) { + assert(!V1Utils.isHalted(state)); require(inputs.length == 2, "L1:inputs:size"); LibData.BlockMetadata memory meta = abi.decode( inputs[0], (LibData.BlockMetadata) ); - bytes calldata txList = inputs[1]; - + _verifyBlockCommit(state, meta); _validateMetadata(meta); - if (LibConstants.K_COMMIT_DELAY_CONFIRMATIONS > 0) { - bytes32 commitHash = _calculateCommitHash( - meta.beneficiary, - meta.txListHash + { + bytes calldata txList = inputs[1]; + // perform validation and populate some fields + require( + txList.length > 0 && + txList.length <= LibConstants.K_TXLIST_MAX_BYTES && + meta.txListHash == txList.hashTxList(), + "L1:txList" ); - require( - isCommitValid( - s, - meta.commitSlot, - meta.commitHeight, - commitHash - ), - "L1:notCommitted" + state.nextBlockId < + state.latestVerifiedId + LibConstants.K_MAX_NUM_BLOCKS, + "L1:tooMany" ); - if (meta.commitSlot == 0) { - // Special handling of slot 0 for refund; non-zero slots - // are supposed to managed by node software for reuse. - delete s.commits[msg.sender][meta.commitSlot]; - } - } - - require( - txList.length > 0 && - txList.length <= LibConstants.TAIKO_TXLIST_MAX_BYTES && - meta.txListHash == txList.hashTxList(), - "L1:txList" - ); - require( - s.nextBlockId <= - s.latestVerifiedId + LibConstants.TAIKO_MAX_PROPOSED_BLOCKS, - "L1:tooMany" - ); - - meta.id = s.nextBlockId; - meta.l1Height = block.number - 1; - meta.l1Hash = blockhash(block.number - 1); - meta.timestamp = uint64(block.timestamp); + meta.id = state.nextBlockId; + meta.l1Height = block.number - 1; + meta.l1Hash = blockhash(block.number - 1); + meta.timestamp = uint64(block.timestamp); - // if multiple L2 blocks included in the same L1 block, - // their block.mixHash fields for randomness will be the same. - meta.mixHash = bytes32(block.difficulty); + // if multiple L2 blocks included in the same L1 block, + // their block.mixHash fields for randomness will be the same. + meta.mixHash = bytes32(block.difficulty); + } - s.saveProposedBlock( - s.nextBlockId, - LibData.ProposedBlock({metaHash: LibData.hashMetadata(meta)}) + state.saveProposedBlock( + state.nextBlockId, + LibData.ProposedBlock({ + metaHash: LibData.hashMetadata(meta), + proposer: msg.sender, + proposedAt: meta.timestamp + }) ); - emit BlockProposed(s.nextBlockId++, meta); + state.lastProposedAt = meta.timestamp; + emit BlockProposed(state.nextBlockId++, meta); } function isCommitValid( - LibData.State storage s, + LibData.State storage state, uint256 commitSlot, uint256 commitHeight, bytes32 commitHash ) public view returns (bool) { - assert(LibConstants.K_COMMIT_DELAY_CONFIRMATIONS > 0); + assert(LibConstants.K_COMMIT_DELAY_CONFIRMS > 0); bytes32 hash = _aggregateCommitHash(commitHeight, commitHash); return - s.commits[msg.sender][commitSlot] == hash && - block.number >= - commitHeight + LibConstants.K_COMMIT_DELAY_CONFIRMATIONS; + state.commits[msg.sender][commitSlot] == hash && + block.number >= commitHeight + LibConstants.K_COMMIT_DELAY_CONFIRMS; + } + + function _verifyBlockCommit( + LibData.State storage state, + LibData.BlockMetadata memory meta + ) private { + if (LibConstants.K_COMMIT_DELAY_CONFIRMS == 0) { + return; + } + bytes32 commitHash = _calculateCommitHash( + meta.beneficiary, + meta.txListHash + ); + + require( + isCommitValid({ + state: state, + commitSlot: meta.commitSlot, + commitHeight: meta.commitHeight, + commitHash: commitHash + }), + "L1:notCommitted" + ); + + if (meta.commitSlot == 0) { + // Special handling of slot 0 for refund; non-zero slots + // are supposed to managed by node software for reuse. + delete state.commits[msg.sender][meta.commitSlot]; + } } function _validateMetadata(LibData.BlockMetadata memory meta) private pure { @@ -139,7 +166,7 @@ library V1Proposing { ); require( - meta.gasLimit <= LibConstants.TAIKO_BLOCK_MAX_GAS_LIMIT, + meta.gasLimit <= LibConstants.K_BLOCK_MAX_GAS_LIMIT, "L1:gasLimit" ); require(meta.extraData.length <= 32, "L1:extraData"); diff --git a/packages/protocol/contracts/L1/v1/V1Proving.sol b/packages/protocol/contracts/L1/v1/V1Proving.sol index d023fd7cce4..4182c022a51 100644 --- a/packages/protocol/contracts/L1/v1/V1Proving.sol +++ b/packages/protocol/contracts/L1/v1/V1Proving.sol @@ -44,22 +44,21 @@ library V1Proving { address prover ); - event ProverWhitelisted(address indexed prover, bool whitelisted); - - modifier onlyWhitelistedProver(LibData.State storage s) { - if (LibConstants.K_WHITELIST_PROVERS) { - require(s.provers[msg.sender], "L1:whitelist"); + modifier onlyWhitelistedProver(LibData.TentativeState storage tentative) { + if (tentative.whitelistProvers) { + require(tentative.provers[msg.sender], "L1:whitelist"); } _; } function proveBlock( - LibData.State storage s, + LibData.State storage state, + LibData.TentativeState storage tentative, AddressResolver resolver, - uint256 blockIndex, + uint256 blockId, bytes[] calldata inputs - ) public onlyWhitelistedProver(s) { - assert(!V1Utils.isHalted(s)); + ) public onlyWhitelistedProver(tentative) { + assert(!V1Utils.isHalted(state)); // Check and decode inputs require(inputs.length == 3, "L1:inputs:size"); @@ -68,7 +67,7 @@ library V1Proving { bytes calldata anchorReceipt = inputs[2]; // Check evidence - require(evidence.meta.id == blockIndex, "L1:id"); + require(evidence.meta.id == blockId, "L1:id"); require( evidence.proofs.length == 2 + LibConstants.K_ZKPROOFS_PER_BLOCK, "L1:proof:size" @@ -79,11 +78,11 @@ library V1Proving { require(_tx.txType == 0, "L1:anchor:type"); require( _tx.destination == - resolver.resolve(LibConstants.TAIKO_CHAIN_ID, "taiko"), + resolver.resolve(LibConstants.K_CHAIN_ID, "taiko"), "L1:anchor:dest" ); require( - _tx.gasLimit == LibConstants.V1_ANCHOR_TX_GAS_LIMIT, + _tx.gasLimit == LibConstants.K_ANCHOR_TX_GAS_LIMIT, "L1:anchor:gasLimit" ); @@ -95,7 +94,7 @@ library V1Proving { LibBytesUtils.equal( _tx.data, bytes.concat( - LibConstants.V1_ANCHOR_TX_SELECTOR, + LibConstants.K_ANCHOR_TX_SELECTOR, bytes32(evidence.meta.l1Height), evidence.meta.l1Hash ) @@ -105,12 +104,12 @@ library V1Proving { // Check anchor tx is the 1st tx in the block require( - LibMerkleTrie.verifyInclusionProof( - LibRLPWriter.writeUint(0), - anchorTx, - evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK], - evidence.header.transactionsRoot - ), + LibMerkleTrie.verifyInclusionProof({ + _key: LibRLPWriter.writeUint(0), + _value: anchorTx, + _proof: evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK], + _root: evidence.header.transactionsRoot + }), "L1:tx:proof" ); @@ -120,26 +119,33 @@ library V1Proving { require(receipt.status == 1, "L1:receipt:status"); require( - LibMerkleTrie.verifyInclusionProof( - LibRLPWriter.writeUint(0), - anchorReceipt, - evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK + 1], - evidence.header.receiptsRoot - ), + LibMerkleTrie.verifyInclusionProof({ + _key: LibRLPWriter.writeUint(0), + _value: anchorReceipt, + _proof: evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK + 1], + _root: evidence.header.receiptsRoot + }), "L1:receipt:proof" ); // ZK-prove block and mark block proven to be valid. - _proveBlock(s, resolver, evidence, evidence.meta, 0); + _proveBlock({ + state: state, + resolver: resolver, + evidence: evidence, + target: evidence.meta, + blockHashOverride: 0 + }); } function proveBlockInvalid( - LibData.State storage s, + LibData.State storage state, + LibData.TentativeState storage tentative, AddressResolver resolver, - uint256 blockIndex, + uint256 blockId, bytes[] calldata inputs - ) public onlyWhitelistedProver(s) { - assert(!V1Utils.isHalted(s)); + ) public onlyWhitelistedProver(tentative) { + assert(!V1Utils.isHalted(state)); // Check and decode inputs require(inputs.length == 3, "L1:inputs:size"); @@ -151,7 +157,7 @@ library V1Proving { bytes calldata invalidateBlockReceipt = inputs[2]; // Check evidence - require(evidence.meta.id == blockIndex, "L1:id"); + require(evidence.meta.id == blockId, "L1:id"); require( evidence.proofs.length == 1 + LibConstants.K_ZKPROOFS_PER_BLOCK, "L1:proof:size" @@ -167,63 +173,40 @@ library V1Proving { LibReceiptDecoder.Log memory log = receipt.logs[0]; require( log.contractAddress == - resolver.resolve(LibConstants.TAIKO_CHAIN_ID, "taiko"), + resolver.resolve(LibConstants.K_CHAIN_ID, "taiko"), "L1:receipt:addr" ); require(log.data.length == 0, "L1:receipt:data"); require( log.topics.length == 2 && - log.topics[0] == LibConstants.V1_INVALIDATE_BLOCK_LOG_TOPIC && + log.topics[0] == LibConstants.K_INVALIDATE_BLOCK_LOG_TOPIC && log.topics[1] == target.txListHash, "L1:receipt:topics" ); // Check the event is the first one in the throw-away block require( - LibMerkleTrie.verifyInclusionProof( - LibRLPWriter.writeUint(0), - invalidateBlockReceipt, - evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK], - evidence.header.receiptsRoot - ), + LibMerkleTrie.verifyInclusionProof({ + _key: LibRLPWriter.writeUint(0), + _value: invalidateBlockReceipt, + _proof: evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK], + _root: evidence.header.receiptsRoot + }), "L1:receipt:proof" ); // ZK-prove block and mark block proven as invalid. - _proveBlock( - s, - resolver, - evidence, - target, - LibConstants.TAIKO_BLOCK_DEADEND_HASH - ); - } - - function whitelistProver( - LibData.State storage s, - address prover, - bool enabled - ) public { - assert(LibConstants.K_WHITELIST_PROVERS); - require( - prover != address(0) && s.provers[prover] != enabled, - "L1:precondition" - ); - - s.provers[prover] = enabled; - emit ProverWhitelisted(prover, enabled); - } - - function isProverWhitelisted( - LibData.State storage s, - address prover - ) public view returns (bool) { - assert(LibConstants.K_WHITELIST_PROVERS); - return s.provers[prover]; + _proveBlock({ + state: state, + resolver: resolver, + evidence: evidence, + target: target, + blockHashOverride: LibConstants.K_BLOCK_DEADEND_HASH + }); } function _proveBlock( - LibData.State storage s, + LibData.State storage state, AddressResolver resolver, Evidence memory evidence, LibData.BlockMetadata memory target, @@ -232,68 +215,63 @@ library V1Proving { require(evidence.meta.id == target.id, "L1:height"); require(evidence.prover != address(0), "L1:prover"); - _checkMetadata(s, target); + _checkMetadata(state, target); _validateHeaderForMetadata(evidence.header, evidence.meta); bytes32 blockHash = evidence.header.hashBlockHeader(); for (uint256 i = 0; i < LibConstants.K_ZKPROOFS_PER_BLOCK; i++) { - LibZKP.verify( - ConfigManager(resolver.resolve("config_manager")).getValue( - string(abi.encodePacked("zk_vkey_", i)) - ), - evidence.proofs[i], - blockHash, - evidence.prover, - evidence.meta.txListHash - ); + LibZKP.verify({ + verificationKey: ConfigManager( + resolver.resolve("config_manager") + ).getValue(string(abi.encodePacked("zk_vkey_", i))), + zkproof: evidence.proofs[i], + blockHash: blockHash, + prover: evidence.prover, + txListHash: evidence.meta.txListHash + }); } - _markBlockProven( - s, - evidence.prover, - target, - evidence.header.parentHash, - blockHashOverride == 0 ? blockHash : blockHashOverride - ); + _markBlockProven({ + state: state, + prover: evidence.prover, + target: target, + parentHash: evidence.header.parentHash, + blockHash: blockHashOverride == 0 ? blockHash : blockHashOverride + }); } function _markBlockProven( - LibData.State storage s, + LibData.State storage state, address prover, LibData.BlockMetadata memory target, bytes32 parentHash, bytes32 blockHash ) private { - LibData.ForkChoice storage fc = s.forkChoices[target.id][parentHash]; + LibData.ForkChoice storage fc = state.forkChoices[target.id][ + parentHash + ]; if (fc.blockHash == 0) { fc.blockHash = blockHash; - fc.proposedAt = target.timestamp; fc.provenAt = uint64(block.timestamp); } else { - require( - fc.proposedAt == target.timestamp, - "L1:proposedAt:conflict" - ); - if (fc.blockHash != blockHash) { // We have a problem here: two proofs are both valid but claims // the new block has different hashes. - V1Utils.halt(s, true); + V1Utils.halt(state, true); return; } require( - fc.provers.length < - LibConstants.TAIKO_MAX_PROOFS_PER_FORK_CHOICE, + fc.provers.length < LibConstants.K_MAX_PROOFS_PER_FORK_CHOICE, "L1:proof:tooMany" ); - // No uncle proof can take more than 1.5x time the first proof did. - uint256 delay = fc.provenAt - fc.proposedAt; - uint256 deadline = fc.provenAt + delay / 2; - require(block.timestamp <= deadline, "L1:tooLate"); + require( + block.timestamp < V1Utils.uncleProofDeadline(state, fc), + "L1:tooLate" + ); for (uint256 i = 0; i < fc.provers.length; i++) { require(fc.provers[i] != prover, "L1:prover:dup"); @@ -302,14 +280,14 @@ library V1Proving { fc.provers.push(prover); - emit BlockProven( - target.id, - parentHash, - blockHash, - fc.proposedAt, - fc.provenAt, - prover - ); + emit BlockProven({ + id: target.id, + parentHash: parentHash, + blockHash: blockHash, + timestamp: target.timestamp, + provenAt: fc.provenAt, + prover: prover + }); } function _validateAnchorTxSignature( @@ -330,15 +308,15 @@ library V1Proving { } function _checkMetadata( - LibData.State storage s, + LibData.State storage state, LibData.BlockMetadata memory meta ) private view { require( - meta.id > s.latestVerifiedId && meta.id < s.nextBlockId, + meta.id > state.latestVerifiedId && meta.id < state.nextBlockId, "L1:meta:id" ); require( - LibData.getProposedBlock(s, meta.id).metaHash == + LibData.getProposedBlock(state, meta.id).metaHash == LibData.hashMetadata(meta), "L1:metaHash" ); @@ -353,7 +331,7 @@ library V1Proving { header.beneficiary == meta.beneficiary && header.difficulty == 0 && header.gasLimit == - meta.gasLimit + LibConstants.V1_ANCHOR_TX_GAS_LIMIT && + meta.gasLimit + LibConstants.K_ANCHOR_TX_GAS_LIMIT && header.gasUsed > 0 && header.timestamp == meta.timestamp && header.extraData.length == meta.extraData.length && diff --git a/packages/protocol/contracts/L1/v1/V1Utils.sol b/packages/protocol/contracts/L1/v1/V1Utils.sol index caab538f413..b339b911899 100644 --- a/packages/protocol/contracts/L1/v1/V1Utils.sol +++ b/packages/protocol/contracts/L1/v1/V1Utils.sol @@ -15,28 +15,106 @@ import "../LibData.sol"; /// @author dantaik library V1Utils { + using LibMath for uint256; + uint64 public constant MASK_HALT = 1 << 0; + event WhitelistingEnabled(bool whitelistProposers, bool whitelistProvers); + event ProposerWhitelisted(address indexed proposer, bool whitelisted); + event ProverWhitelisted(address indexed prover, bool whitelisted); event Halted(bool halted); - function halt(LibData.State storage s, bool toHalt) public { - require(isHalted(s) != toHalt, "L1:precondition"); - setBit(s, MASK_HALT, toHalt); + function enableWhitelisting( + LibData.TentativeState storage tentative, + bool whitelistProposers, + bool whitelistProvers + ) internal { + tentative.whitelistProposers = whitelistProvers; + tentative.whitelistProvers = whitelistProvers; + emit WhitelistingEnabled(whitelistProposers, whitelistProvers); + } + + function whitelistProposer( + LibData.TentativeState storage tentative, + address proposer, + bool whitelisted + ) internal { + assert(tentative.whitelistProposers); + require( + proposer != address(0) && + tentative.proposers[proposer] != whitelisted, + "L1:precondition" + ); + + tentative.proposers[proposer] = whitelisted; + emit ProposerWhitelisted(proposer, whitelisted); + } + + function whitelistProver( + LibData.TentativeState storage tentative, + address prover, + bool whitelisted + ) internal { + assert(tentative.whitelistProvers); + require( + prover != address(0) && tentative.provers[prover] != whitelisted, + "L1:precondition" + ); + + tentative.provers[prover] = whitelisted; + emit ProverWhitelisted(prover, whitelisted); + } + + function halt(LibData.State storage state, bool toHalt) internal { + require(isHalted(state) != toHalt, "L1:precondition"); + setBit(state, MASK_HALT, toHalt); emit Halted(toHalt); } - function isHalted(LibData.State storage s) public view returns (bool) { - return isBitOne(s, MASK_HALT); + function isHalted( + LibData.State storage state + ) internal view returns (bool) { + return isBitOne(state, MASK_HALT); + } + + function isProposerWhitelisted( + LibData.TentativeState storage tentative, + address proposer + ) internal view returns (bool) { + assert(tentative.whitelistProposers); + return tentative.proposers[proposer]; + } + + function isProverWhitelisted( + LibData.TentativeState storage tentative, + address prover + ) internal view returns (bool) { + assert(tentative.whitelistProvers); + return tentative.provers[prover]; + } + + // Returns a deterministic deadline for uncle proof submission. + function uncleProofDeadline( + LibData.State storage state, + LibData.ForkChoice storage fc + ) internal view returns (uint64) { + return fc.provenAt + state.avgProofTime; } - function setBit(LibData.State storage s, uint64 mask, bool one) private { - s.statusBits = one ? s.statusBits | mask : s.statusBits & ~mask; + function setBit( + LibData.State storage state, + uint64 mask, + bool one + ) private { + state.statusBits = one + ? state.statusBits | mask + : state.statusBits & ~mask; } function isBitOne( - LibData.State storage s, + LibData.State storage state, uint64 mask ) private view returns (bool) { - return s.statusBits & mask != 0; + return state.statusBits & mask != 0; } } diff --git a/packages/protocol/contracts/L1/v1/V1Verifying.sol b/packages/protocol/contracts/L1/v1/V1Verifying.sol new file mode 100644 index 00000000000..03ea7876d90 --- /dev/null +++ b/packages/protocol/contracts/L1/v1/V1Verifying.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: MIT +// +// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ +// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ +// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ +// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ +// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ +// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ +pragma solidity ^0.8.9; + +import "../../common/AddressResolver.sol"; +import "../TkoToken.sol"; +import "./V1Utils.sol"; + +/// @author dantaik +library V1Verifying { + using SafeCastUpgradeable for uint256; + event BlockVerified(uint256 indexed id, bytes32 blockHash); + + event HeaderSynced( + uint256 indexed height, + uint256 indexed srcHeight, + bytes32 srcHash + ); + + function init( + LibData.State storage state, + bytes32 genesisBlockHash + ) public { + state.genesisHeight = uint64(block.number); + state.genesisTimestamp = uint64(block.timestamp); + state.nextBlockId = 1; + state.lastProposedAt = uint64(block.timestamp); + state.l2Hashes[0] = genesisBlockHash; + + emit BlockVerified(0, genesisBlockHash); + emit HeaderSynced(block.number, 0, genesisBlockHash); + } + + function verifyBlocks( + LibData.State storage state, + AddressResolver resolver, + uint256 maxBlocks, + bool checkHalt + ) public { + bool halted = V1Utils.isHalted(state); + if (checkHalt) { + require(!halted, "L1:halted"); + } else if (halted) { + // skip finalizing blocks + return; + } + + uint64 latestL2Height = state.latestVerifiedHeight; + bytes32 latestL2Hash = state.l2Hashes[latestL2Height]; + uint64 processed = 0; + + for ( + uint256 i = state.latestVerifiedId + 1; + i < state.nextBlockId && processed <= maxBlocks; + i++ + ) { + LibData.ForkChoice storage fc = state.forkChoices[i][latestL2Hash]; + + // Uncle proof can not take more than 2x time the first proof did. + if (!_isVerifiable(state, fc)) { + break; + } else { + if (fc.blockHash != LibConstants.K_BLOCK_DEADEND_HASH) { + latestL2Height += 1; + latestL2Hash = fc.blockHash; + } + processed += 1; + _cleanUp(fc); + emit BlockVerified(i, fc.blockHash); + } + } + + if (processed > 0) { + state.latestVerifiedId += processed; + + if (latestL2Height > state.latestVerifiedHeight) { + state.latestVerifiedHeight = latestL2Height; + state.l2Hashes[latestL2Height] = latestL2Hash; + emit HeaderSynced(block.number, latestL2Height, latestL2Hash); + } + } + } + + function _cleanUp(LibData.ForkChoice storage fc) private { + fc.blockHash = 0; + fc.provenAt = 0; + for (uint i = 0; i < fc.provers.length; i++) { + fc.provers[i] = address(0); + } + delete fc.provers; + } + + function _isVerifiable( + LibData.State storage state, + LibData.ForkChoice storage fc + ) private view returns (bool) { + return + fc.blockHash != 0 && + block.timestamp > V1Utils.uncleProofDeadline(state, fc); + } +} diff --git a/packages/protocol/contracts/L2/V1TaikoL2.sol b/packages/protocol/contracts/L2/V1TaikoL2.sol index 1674cbea0f2..a313c70bdfc 100644 --- a/packages/protocol/contracts/L2/V1TaikoL2.sol +++ b/packages/protocol/contracts/L2/V1TaikoL2.sol @@ -52,12 +52,13 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { for (uint256 i = 0; i < 255 && number >= i + 2; i++) { ancestors[i] = blockhash(number - i - 2); } - publicInputHash = _hashPublicInputs( - block.chainid, - number, - 0, - ancestors - ); + + publicInputHash = _hashPublicInputs({ + chainId: block.chainid, + number: number, + baseFee: 0, + ancestors: ancestors + }); } /********************** @@ -68,7 +69,8 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { * bridging. This function will also check certain block-level global * variables because they are not part of the Trie structure. * - * Note that this transaction shall be the first transaction in every L2 block. + * Note that this transaction shall be the first transaction in every + * L2 block. * * @param l1Height The latest L1 block height when this block was proposed. * @param l1Hash The latest L1 block hash when this block was proposed. @@ -94,11 +96,11 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { LibInvalidTxList.Reason hint, uint256 txIdx ) external { - LibInvalidTxList.Reason reason = LibInvalidTxList.isTxListInvalid( - txList, - hint, - txIdx - ); + LibInvalidTxList.Reason reason = LibInvalidTxList.isTxListInvalid({ + encoded: txList, + hint: hint, + txIdx: txIdx + }); require(reason != LibInvalidTxList.Reason.OK, "L2:reason"); _checkPublicInputs(); @@ -134,41 +136,38 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { * Private Functions * **********************/ + // NOTE: If the order of the return values of this function changes, then + // some test cases that using this function in generate_genesis.test.ts + // may also needs to be modified accordingly. function getConstants() public pure returns ( uint256, // K_ZKPROOFS_PER_BLOCK - uint256, // TAIKO_CHAIN_ID - uint256, // TAIKO_MAX_PROPOSED_BLOCKS - uint256, // TAIKO_MAX_VERIFICATIONS_PER_TX - uint256, // K_COMMIT_DELAY_CONFIRMATIONS - uint256, // TAIKO_MAX_PROOFS_PER_FORK_CHOICE - uint256, // TAIKO_BLOCK_MAX_GAS_LIMIT - uint256, // TAIKO_BLOCK_MAX_TXS - bytes32, // TAIKO_BLOCK_DEADEND_HASH - uint256, // TAIKO_TXLIST_MAX_BYTES - uint256, // TAIKO_TX_MIN_GAS_LIMIT - uint256, // V1_ANCHOR_TX_GAS_LIMIT - bytes4, // V1_ANCHOR_TX_SELECTOR - bytes32 // V1_INVALIDATE_BLOCK_LOG_TOPIC + uint256, // K_CHAIN_ID + uint256, // K_MAX_NUM_BLOCKS + uint256, // K_MAX_VERIFICATIONS_PER_TX + uint256, // K_COMMIT_DELAY_CONFIRMS + uint256, // K_MAX_PROOFS_PER_FORK_CHOICE + uint256, // K_BLOCK_MAX_GAS_LIMIT + uint256, // K_BLOCK_MAX_TXS + uint256, // K_TXLIST_MAX_BYTES + uint256, // K_TX_MIN_GAS_LIMIT + uint256 // K_ANCHOR_TX_GAS_LIMIT ) { return ( LibConstants.K_ZKPROOFS_PER_BLOCK, - LibConstants.TAIKO_CHAIN_ID, - LibConstants.TAIKO_MAX_PROPOSED_BLOCKS, - LibConstants.TAIKO_MAX_VERIFICATIONS_PER_TX, - LibConstants.K_COMMIT_DELAY_CONFIRMATIONS, - LibConstants.TAIKO_MAX_PROOFS_PER_FORK_CHOICE, - LibConstants.TAIKO_BLOCK_MAX_GAS_LIMIT, - LibConstants.TAIKO_BLOCK_MAX_TXS, - LibConstants.TAIKO_BLOCK_DEADEND_HASH, - LibConstants.TAIKO_TXLIST_MAX_BYTES, - LibConstants.TAIKO_TX_MIN_GAS_LIMIT, - LibConstants.V1_ANCHOR_TX_GAS_LIMIT, - LibConstants.V1_ANCHOR_TX_SELECTOR, - LibConstants.V1_INVALIDATE_BLOCK_LOG_TOPIC + LibConstants.K_CHAIN_ID, + LibConstants.K_MAX_NUM_BLOCKS, + LibConstants.K_MAX_VERIFICATIONS_PER_TX, + LibConstants.K_COMMIT_DELAY_CONFIRMS, + LibConstants.K_MAX_PROOFS_PER_FORK_CHOICE, + LibConstants.K_BLOCK_MAX_GAS_LIMIT, + LibConstants.K_BLOCK_MAX_TXS, + LibConstants.K_TXLIST_MAX_BYTES, + LibConstants.K_TX_MIN_GAS_LIMIT, + LibConstants.K_ANCHOR_TX_GAS_LIMIT ); } @@ -187,12 +186,22 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { require( publicInputHash == - _hashPublicInputs(chainId, parentHeight, 0, ancestors), + _hashPublicInputs({ + chainId: chainId, + number: parentHeight, + baseFee: 0, + ancestors: ancestors + }), "L2:publicInputHash" ); ancestors[parentHeight % 255] = parentHash; - publicInputHash = _hashPublicInputs(chainId, number, 0, ancestors); + publicInputHash = _hashPublicInputs({ + chainId: chainId, + number: number, + baseFee: 0, + ancestors: ancestors + }); l2Hashes[parentHeight] = parentHash; } diff --git a/packages/protocol/contracts/bridge/Bridge.sol b/packages/protocol/contracts/bridge/Bridge.sol index a1108360d83..4bd39766495 100644 --- a/packages/protocol/contracts/bridge/Bridge.sol +++ b/packages/protocol/contracts/bridge/Bridge.sol @@ -59,7 +59,12 @@ contract Bridge is EssentialContract, IBridge { function sendMessage( Message calldata message ) external payable nonReentrant returns (bytes32 signal) { - return LibBridgeSend.sendMessage(state, AddressResolver(this), message); + return + LibBridgeSend.sendMessage({ + state: state, + resolver: AddressResolver(this), + message: message + }); } function sendSignal(bytes32 signal) external override { @@ -72,12 +77,12 @@ contract Bridge is EssentialContract, IBridge { bytes calldata proof ) external nonReentrant { return - LibBridgeProcess.processMessage( - state, - AddressResolver(this), - message, - proof - ); + LibBridgeProcess.processMessage({ + state: state, + resolver: AddressResolver(this), + message: message, + proof: proof + }); } function retryMessage( @@ -85,19 +90,23 @@ contract Bridge is EssentialContract, IBridge { bool isLastAttempt ) external nonReentrant { return - LibBridgeRetry.retryMessage( - state, - AddressResolver(this), - message, - isLastAttempt - ); + LibBridgeRetry.retryMessage({ + state: state, + resolver: AddressResolver(this), + message: message, + isLastAttempt: isLastAttempt + }); } function enableDestChain( uint256 _chainId, bool enabled ) external nonReentrant { - LibBridgeSend.enableDestChain(state, _chainId, enabled); + LibBridgeSend.enableDestChain({ + state: state, + chainId: _chainId, + enabled: enabled + }); } /********************* @@ -115,13 +124,13 @@ contract Bridge is EssentialContract, IBridge { ) public view virtual override returns (bool) { address srcBridge = resolve(srcChainId, "bridge"); return - LibBridgeSignal.isSignalReceived( - AddressResolver(this), - srcBridge, - srcBridge, - signal, - proof - ); + LibBridgeSignal.isSignalReceived({ + resolver: AddressResolver(this), + srcBridge: srcBridge, + sender: srcBridge, + signal: signal, + proof: proof + }); } function isSignalSent( @@ -139,13 +148,13 @@ contract Bridge is EssentialContract, IBridge { ) public view virtual override returns (bool) { address srcBridge = resolve(srcChainId, "bridge"); return - LibBridgeSignal.isSignalReceived( - AddressResolver(this), - srcBridge, - sender, - signal, - proof - ); + LibBridgeSignal.isSignalReceived({ + resolver: AddressResolver(this), + srcBridge: srcBridge, + sender: sender, + signal: signal, + proof: proof + }); } function getMessageStatus( diff --git a/packages/protocol/contracts/bridge/BridgedERC20.sol b/packages/protocol/contracts/bridge/BridgedERC20.sol index bb1ef4268e9..e851894b6c3 100644 --- a/packages/protocol/contracts/bridge/BridgedERC20.sol +++ b/packages/protocol/contracts/bridge/BridgedERC20.sol @@ -8,11 +8,13 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; -import "../common/EssentialContract.sol"; -import "../thirdparty/ERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +// solhint-disable-next-line max-line-length import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; +import "../common/EssentialContract.sol"; +import "../thirdparty/ERC20Upgradeable.sol"; + /** * @author dantaik */ @@ -50,7 +52,11 @@ contract BridgedERC20 is "BE:params" ); EssentialContract._init(_addressManager); - ERC20Upgradeable.__ERC20_init(_name, _symbol, _decimals); + ERC20Upgradeable.__ERC20_init({ + name_: _name, + symbol_: _symbol, + decimals_: _decimals + }); srcToken = _srcToken; srcChainId = _srcChainId; } diff --git a/packages/protocol/contracts/bridge/EtherVault.sol b/packages/protocol/contracts/bridge/EtherVault.sol index cc874dbb9f9..310b2360b2c 100644 --- a/packages/protocol/contracts/bridge/EtherVault.sol +++ b/packages/protocol/contracts/bridge/EtherVault.sol @@ -8,11 +8,13 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; -import "../common/EssentialContract.sol"; -import "../libs/LibAddress.sol"; +// solhint-disable-next-line max-line-length import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/Create2Upgradeable.sol"; +import "../common/EssentialContract.sol"; +import "../libs/LibAddress.sol"; + /** * Vault that holds Ether. * @author dantaik diff --git a/packages/protocol/contracts/bridge/TokenVault.sol b/packages/protocol/contracts/bridge/TokenVault.sol index 94b4f3e7145..ede396b2181 100644 --- a/packages/protocol/contracts/bridge/TokenVault.sol +++ b/packages/protocol/contracts/bridge/TokenVault.sol @@ -8,6 +8,7 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; +// solhint-disable-next-line max-line-length import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/Create2Upgradeable.sol"; @@ -141,7 +142,12 @@ contract TokenVault is EssentialContract { value: msg.value }(message); - emit EtherSent(to, destChainId, message.depositValue, signal); + emit EtherSent({ + to: to, + destChainId: destChainId, + amount: message.depositValue, + signal: signal + }); } /** @@ -286,13 +292,13 @@ contract TokenVault is EssentialContract { type(BridgedERC20).creationCode ); - BridgedERC20(payable(bridgedToken)).init( - address(_addressManager), - canonicalToken.addr, - canonicalToken.chainId, - canonicalToken.decimals, - canonicalToken.symbol, - string( + BridgedERC20(payable(bridgedToken)).init({ + _addressManager: address(_addressManager), + _srcToken: canonicalToken.addr, + _srcChainId: canonicalToken.chainId, + _decimals: canonicalToken.decimals, + _symbol: canonicalToken.symbol, + _name: string( abi.encodePacked( canonicalToken.name, "(bridged", @@ -301,23 +307,21 @@ contract TokenVault is EssentialContract { ")" ) ) - ); + }); isBridgedToken[bridgedToken] = true; - bridgedToCanonical[bridgedToken] = canonicalToken; - canonicalToBridged[canonicalToken.chainId][ canonicalToken.addr ] = bridgedToken; - emit BridgedERC20Deployed( - canonicalToken.chainId, - canonicalToken.addr, - bridgedToken, - canonicalToken.symbol, - canonicalToken.name, - canonicalToken.decimals - ); + emit BridgedERC20Deployed({ + srcChainId: canonicalToken.chainId, + canonicalToken: canonicalToken.addr, + bridgedToken: bridgedToken, + canonicalTokenSymbol: canonicalToken.symbol, + canonicalTokenName: canonicalToken.name, + canonicalTokenDecimal: canonicalToken.decimals + }); } } diff --git a/packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol b/packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol index ad3f4d59345..9696e841cf1 100644 --- a/packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol +++ b/packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol @@ -60,14 +60,15 @@ library LibBridgeProcess { ); // Message must have been "received" on the destChain (current chain) address srcBridge = resolver.resolve(message.srcChainId, "bridge"); + require( - LibBridgeSignal.isSignalReceived( - resolver, - srcBridge, - srcBridge, - signal, - proof - ), + LibBridgeSignal.isSignalReceived({ + resolver: resolver, + srcBridge: srcBridge, + sender: srcBridge, + signal: signal, + proof: proof + }), "B:notReceived" ); @@ -94,12 +95,12 @@ library LibBridgeProcess { uint256 gasLimit = msg.sender == message.owner ? gasleft() : message.gasLimit; - bool success = LibBridgeInvoke.invokeMessageCall( - state, - message, - signal, - gasLimit - ); + bool success = LibBridgeInvoke.invokeMessageCall({ + state: state, + message: message, + signal: signal, + gasLimit: gasLimit + }); if (success) { status = LibBridgeData.MessageStatus.DONE; diff --git a/packages/protocol/contracts/bridge/libs/LibBridgeRetry.sol b/packages/protocol/contracts/bridge/libs/LibBridgeRetry.sol index c15aea619f8..2a85925c7db 100644 --- a/packages/protocol/contracts/bridge/libs/LibBridgeRetry.sol +++ b/packages/protocol/contracts/bridge/libs/LibBridgeRetry.sol @@ -62,7 +62,12 @@ library LibBridgeRetry { // successful invocation if ( - LibBridgeInvoke.invokeMessageCall(state, message, signal, gasleft()) + LibBridgeInvoke.invokeMessageCall({ + state: state, + message: message, + signal: signal, + gasLimit: gasleft() + }) ) { state.updateMessageStatus(signal, LibBridgeData.MessageStatus.DONE); } else if (isLastAttempt) { diff --git a/packages/protocol/contracts/bridge/libs/LibBridgeSignal.sol b/packages/protocol/contracts/bridge/libs/LibBridgeSignal.sol index 47436c5148a..a0fc61c48ff 100644 --- a/packages/protocol/contracts/bridge/libs/LibBridgeSignal.sol +++ b/packages/protocol/contracts/bridge/libs/LibBridgeSignal.sol @@ -88,13 +88,13 @@ library LibBridgeSignal { require(srcBridge != address(0), "B:srcBridge"); SignalProof memory mkp = abi.decode(proof, (SignalProof)); - LibTrieProof.verify( - mkp.header.stateRoot, - srcBridge, - _key(sender, signal), - bytes32(uint256(1)), - mkp.proof - ); + LibTrieProof.verify({ + stateRoot: mkp.header.stateRoot, + addr: srcBridge, + key: _key(sender, signal), + value: bytes32(uint256(1)), + mkproof: mkp.proof + }); // get synced header hash of the header height specified in the proof bytes32 syncedHeaderHash = IHeaderSync(resolver.resolve("taiko")) .getSyncedHeader(mkp.header.height); diff --git a/packages/protocol/contracts/common/AddressResolver.sol b/packages/protocol/contracts/common/AddressResolver.sol index 0d4050ac6f2..24a6d01e926 100644 --- a/packages/protocol/contracts/common/AddressResolver.sol +++ b/packages/protocol/contracts/common/AddressResolver.sol @@ -8,9 +8,10 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; -import "./IAddressManager.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; +import "./IAddressManager.sol"; + /** * This abstract contract provides a name-to-address lookup. Under the hood, * it uses an AddressManager to manage the name-to-address mapping. diff --git a/packages/protocol/contracts/common/EssentialContract.sol b/packages/protocol/contracts/common/EssentialContract.sol index 15efe953bdd..3c6b086d38d 100644 --- a/packages/protocol/contracts/common/EssentialContract.sol +++ b/packages/protocol/contracts/common/EssentialContract.sol @@ -9,6 +9,7 @@ pragma solidity ^0.8.9; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +// solhint-disable-next-line max-line-length import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "./AddressResolver.sol"; diff --git a/packages/protocol/contracts/libs/LibAnchorSignature.sol b/packages/protocol/contracts/libs/LibAnchorSignature.sol index 7de1e9b93b1..fd1e90718e2 100644 --- a/packages/protocol/contracts/libs/LibAnchorSignature.sol +++ b/packages/protocol/contracts/libs/LibAnchorSignature.sol @@ -11,9 +11,9 @@ import "./LibUint512Math.sol"; /// @author david library LibAnchorSignature { - address public constant TAIKO_GOLDEN_TOUCH_ADDRESS = + address public constant K_GOLDEN_TOUCH_ADDRESS = 0x0000777735367b36bC9B61C50022d9D0700dB4Ec; - uint256 public constant TAIKO_GOLDEN_TOUCH_PRIVATEKEY = + uint256 public constant K_GOLDEN_TOUCH_PRIVATEKEY = 0x92954368afd3caa1f3ce3ead0069c1af414054aefe1ef9aeacc1bf426222ce38; uint256 public constant GX = @@ -32,7 +32,7 @@ library LibAnchorSignature { // ( // uint256 GX_MUL_GOLDEN_TOUCH_PRIVATEKEY_LOW, // uint256 GX_MUL_GOLDEN_TOUCH_PRIVATEKEY_HIGH - // ) = LibUint512Math.mul(GX, TAIKO_GOLDEN_TOUCH_PRIVATEKEY); + // ) = LibUint512Math.mul(GX, K_GOLDEN_TOUCH_PRIVATEKEY); uint256 public constant GX_MUL_GOLDEN_TOUCH_PRIVATEKEY_LOW = 0xb4a95509ce05fe8d45987859a067780d16a367c0e2cacf79cd301b93fb717940; uint256 public constant GX_MUL_GOLDEN_TOUCH_PRIVATEKEY_HIGH = @@ -41,7 +41,7 @@ library LibAnchorSignature { // ( // uint256 GX2_MUL_GOLDEN_TOUCH_PRIVATEKEY_LOW, // uint256 GX2_MUL_GOLDEN_TOUCH_PRIVATEKEY_HIGH - // ) = LibUint512Math.mul(GX2, TAIKO_GOLDEN_TOUCH_PRIVATEKEY); + // ) = LibUint512Math.mul(GX2, K_GOLDEN_TOUCH_PRIVATEKEY); uint256 public constant GX2_MUL_GOLDEN_TOUCH_PRIVATEKEY_LOW = 0xad77eceea844778cb4376153fc8f06f12f1695df4585bf75bfb17ec19ce90818; uint256 public constant GX2_MUL_GOLDEN_TOUCH_PRIVATEKEY_HIGH = diff --git a/packages/protocol/contracts/libs/LibBlockHeader.sol b/packages/protocol/contracts/libs/LibBlockHeader.sol index 37f78087538..a312d314848 100644 --- a/packages/protocol/contracts/libs/LibBlockHeader.sol +++ b/packages/protocol/contracts/libs/LibBlockHeader.sol @@ -78,7 +78,7 @@ library LibBlockHeader { return header.parentHash != 0 && header.ommersHash == EMPTY_OMMERS_HASH && - header.gasLimit <= LibConstants.TAIKO_BLOCK_MAX_GAS_LIMIT && + header.gasLimit <= LibConstants.K_BLOCK_MAX_GAS_LIMIT && header.extraData.length <= 32 && header.difficulty == 0 && header.nonce == 0; diff --git a/packages/protocol/contracts/libs/LibConstants.sol b/packages/protocol/contracts/libs/LibConstants.sol index b43ff248fb0..804497d523f 100644 --- a/packages/protocol/contracts/libs/LibConstants.sol +++ b/packages/protocol/contracts/libs/LibConstants.sol @@ -10,29 +10,27 @@ pragma solidity ^0.8.9; /// @author dantaik library LibConstants { - uint256 public constant K_ZKPROOFS_PER_BLOCK = 1; // https://github.com/ethereum-lists/chains/pull/1611 - uint256 public constant TAIKO_CHAIN_ID = 167; + uint256 public constant K_CHAIN_ID = 167; + // up to 2048 pending blocks + uint256 public constant K_MAX_NUM_BLOCKS = 2049; + // This number is calculated from K_MAX_NUM_BLOCKS to make + // the 'the maximum value of the multiplier' close to 20.0 + uint256 public constant K_ZKPROOFS_PER_BLOCK = 1; uint256 public constant K_VERIFICATION_DELAY = 60 minutes; - uint256 public constant TAIKO_MAX_PROPOSED_BLOCKS = 2048; - uint256 public constant TAIKO_MAX_VERIFICATIONS_PER_TX = 20; - uint256 public constant K_COMMIT_DELAY_CONFIRMATIONS = 4; - uint256 public constant TAIKO_MAX_PROOFS_PER_FORK_CHOICE = 5; - uint256 public constant TAIKO_BLOCK_MAX_GAS_LIMIT = 5000000; // TODO - uint256 public constant TAIKO_BLOCK_MAX_TXS = 20; // TODO - bytes32 public constant TAIKO_BLOCK_DEADEND_HASH = bytes32(uint256(1)); - - uint256 public constant TAIKO_TXLIST_MAX_BYTES = 10240; // TODO - uint256 public constant TAIKO_TX_MIN_GAS_LIMIT = 21000; // TODO + uint256 public constant K_MAX_VERIFICATIONS_PER_TX = 20; + uint256 public constant K_COMMIT_DELAY_CONFIRMS = 4; + uint256 public constant K_MAX_PROOFS_PER_FORK_CHOICE = 5; + uint256 public constant K_BLOCK_MAX_GAS_LIMIT = 5000000; // TODO + uint256 public constant K_BLOCK_MAX_TXS = 20; // TODO + uint256 public constant K_TXLIST_MAX_BYTES = 10240; // TODO + uint256 public constant K_TX_MIN_GAS_LIMIT = 21000; // TODO + uint256 public constant K_ANCHOR_TX_GAS_LIMIT = 250000; - // Taiko L2 releated constants - uint256 public constant V1_ANCHOR_TX_GAS_LIMIT = 250000; - - bytes4 public constant V1_ANCHOR_TX_SELECTOR = + bytes4 public constant K_ANCHOR_TX_SELECTOR = bytes4(keccak256("anchor(uint256,bytes32)")); - bytes32 public constant V1_INVALIDATE_BLOCK_LOG_TOPIC = + bytes32 public constant K_BLOCK_DEADEND_HASH = bytes32(uint256(1)); + bytes32 public constant K_INVALIDATE_BLOCK_LOG_TOPIC = keccak256("BlockInvalidated(bytes32)"); - - bool public constant K_WHITELIST_PROVERS = false; } diff --git a/packages/protocol/contracts/libs/LibInvalidTxList.sol b/packages/protocol/contracts/libs/LibInvalidTxList.sol index 5231a8167f2..40d31051297 100644 --- a/packages/protocol/contracts/libs/LibInvalidTxList.sol +++ b/packages/protocol/contracts/libs/LibInvalidTxList.sol @@ -18,23 +18,26 @@ import "../thirdparty/LibRLPWriter.sol"; * A library to invalidate a txList using the following rules: * * A txList is valid if and only if: - * 1. The txList's length is no more than `TAIKO_TXLIST_MAX_BYTES`. + * 1. The txList's length is no more than `K_TXLIST_MAX_BYTES`. * 2. The txList is well-formed RLP, with no additional trailing bytes. - * 3. The total number of transactions is no more than `TAIKO_BLOCK_MAX_TXS`. + * 3. The total number of transactions is no more than `K_BLOCK_MAX_TXS`. * 4. The sum of all transaction gas limit is no more than - * `TAIKO_BLOCK_MAX_GAS_LIMIT`. + * `K_BLOCK_MAX_GAS_LIMIT`. * * A transaction is valid if and only if: * 1. The transaction is well-formed RLP, with no additional trailing bytes * (rule #1 in Ethereum yellow paper). * 2. The transaction's signature is valid (rule #2 in Ethereum yellow paper). * 3. The transaction's the gas limit is no smaller than the intrinsic gas - * `TAIKO_TX_MIN_GAS_LIMIT` (rule #5 in Ethereum yellow paper). + * `K_TX_MIN_GAS_LIMIT` (rule #5 in Ethereum yellow paper). * * @title LibInvalidTxList * @author david */ library LibInvalidTxList { + // NOTE: If the order of this enum changes, then some test cases that using + // this enum in generate_genesis.test.ts may also needs to be + // modified accordingly. enum Reason { OK, BINARY_TOO_LARGE, @@ -50,20 +53,20 @@ library LibInvalidTxList { Reason hint, uint256 txIdx ) internal pure returns (Reason) { - if (encoded.length > LibConstants.TAIKO_TXLIST_MAX_BYTES) { + if (encoded.length > LibConstants.K_TXLIST_MAX_BYTES) { return Reason.BINARY_TOO_LARGE; } try LibTxDecoder.decodeTxList(encoded) returns ( LibTxDecoder.TxList memory txList ) { - if (txList.items.length > LibConstants.TAIKO_BLOCK_MAX_TXS) { + if (txList.items.length > LibConstants.K_BLOCK_MAX_TXS) { return Reason.BLOCK_TOO_MANY_TXS; } if ( LibTxDecoder.sumGasLimit(txList) > - LibConstants.TAIKO_BLOCK_MAX_GAS_LIMIT + LibConstants.K_BLOCK_MAX_GAS_LIMIT ) { return Reason.BLOCK_GAS_LIMIT_TOO_LARGE; } @@ -81,7 +84,7 @@ library LibInvalidTxList { if (hint == Reason.TX_GAS_LIMIT_TOO_SMALL) { require( - _tx.gasLimit >= LibConstants.TAIKO_TX_MIN_GAS_LIMIT, + _tx.gasLimit >= LibConstants.K_TX_MIN_GAS_LIMIT, "bad hint" ); return Reason.TX_GAS_LIMIT_TOO_SMALL; diff --git a/packages/protocol/contracts/libs/LibTxDecoder.sol b/packages/protocol/contracts/libs/LibTxDecoder.sol index c7aa4c2895b..9c10316f7d9 100644 --- a/packages/protocol/contracts/libs/LibTxDecoder.sol +++ b/packages/protocol/contracts/libs/LibTxDecoder.sol @@ -162,10 +162,7 @@ library LibTxDecoder { txLegacy.data = LibRLPReader.readBytes(body[5]); // EIP-155 is enabled on L2 txLegacy.v = uint8( - LibRLPReader.readUint256(body[6]) - - LibConstants.TAIKO_CHAIN_ID * - 2 + - 35 + LibRLPReader.readUint256(body[6]) - LibConstants.K_CHAIN_ID * 2 + 35 ); txLegacy.r = LibRLPReader.readUint256(body[7]); txLegacy.s = LibRLPReader.readUint256(body[8]); diff --git a/packages/protocol/contracts/libs/LibTxUtils.sol b/packages/protocol/contracts/libs/LibTxUtils.sol index 2faa63eb515..d68edfbbdc3 100644 --- a/packages/protocol/contracts/libs/LibTxUtils.sol +++ b/packages/protocol/contracts/libs/LibTxUtils.sol @@ -72,9 +72,7 @@ library LibTxUtils { // For legacy transactions, there are three more RLP items to // encode defined in EIP-155. if (transaction.txType == 0 && i == list.length - 4) { - list[i + 1] = LibRLPWriter.writeUint( - LibConstants.TAIKO_CHAIN_ID - ); + list[i + 1] = LibRLPWriter.writeUint(LibConstants.K_CHAIN_ID); list[i + 2] = LibRLPWriter.writeUint64(0); list[i + 3] = LibRLPWriter.writeUint64(0); break; diff --git a/packages/protocol/contracts/test/libs/TestLibAnchorSignature.sol b/packages/protocol/contracts/test/libs/TestLibAnchorSignature.sol index 2f1d0bcdc8b..3a80e2d2d19 100644 --- a/packages/protocol/contracts/test/libs/TestLibAnchorSignature.sol +++ b/packages/protocol/contracts/test/libs/TestLibAnchorSignature.sol @@ -29,8 +29,8 @@ library TestLibAnchorSignature { function goldenTouchAddress() public pure returns (address, uint256) { return ( - LibAnchorSignature.TAIKO_GOLDEN_TOUCH_ADDRESS, - LibAnchorSignature.TAIKO_GOLDEN_TOUCH_PRIVATEKEY + LibAnchorSignature.K_GOLDEN_TOUCH_ADDRESS, + LibAnchorSignature.K_GOLDEN_TOUCH_PRIVATEKEY ); } } diff --git a/packages/protocol/docs/L1/LibData.md b/packages/protocol/docs/L1/LibData.md index 63f11bb57df..21c79e40357 100644 --- a/packages/protocol/docs/L1/LibData.md +++ b/packages/protocol/docs/L1/LibData.md @@ -22,6 +22,8 @@ struct BlockMetadata { ```solidity struct ProposedBlock { bytes32 metaHash; + address proposer; + uint64 gasLimit; } ``` @@ -47,9 +49,18 @@ struct State { mapping(uint256 => mapping(bytes32 => struct LibData.ForkChoice)) forkChoices; mapping(bytes32 => uint256) commits; uint64 genesisHeight; + uint64 genesisTimestamp; + uint64 reservedA1; + uint64 reservedA2; + uint256 feeBase; + uint64 nextBlockId; + uint64 lastProposedAt; + uint64 avgBlockTime; + uint64 avgGasLimit; uint64 latestVerifiedHeight; uint64 latestVerifiedId; - uint64 nextBlockId; + uint64 avgProofTime; + uint64 reservedC1; } ``` diff --git a/packages/protocol/docs/L1/TaikoL1.md b/packages/protocol/docs/L1/TaikoL1.md index 7e18387f854..2d113249cb5 100644 --- a/packages/protocol/docs/L1/TaikoL1.md +++ b/packages/protocol/docs/L1/TaikoL1.md @@ -9,13 +9,13 @@ struct LibData.State state ### \_\_gap ```solidity -uint256[45] __gap +uint256[42] __gap ``` ### init ```solidity -function init(address _addressManager, bytes32 _genesisBlockHash) external +function init(address _addressManager, bytes32 _genesisBlockHash, uint256 _feeBase) external ``` ### commitBlock @@ -51,7 +51,7 @@ Propose a Taiko L2 block. ### proveBlock ```solidity -function proveBlock(uint256 blockIndex, bytes[] inputs) external +function proveBlock(uint256 blockId, bytes[] inputs) external ``` Prove a block is valid with a zero-knowledge proof, a transaction @@ -59,15 +59,15 @@ merkel proof, and a receipt merkel proof. #### Parameters -| Name | Type | Description | -| ---------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| blockIndex | uint256 | The index of the block to prove. This is also used to select the right implementation version. | -| inputs | bytes[] | A list of data input: - inputs[0] is an abi-encoded object with various information regarding the block to be proven and the actual proofs. - inputs[1] is the actual anchor transaction in this L2 block. Note that the anchor transaction is always the first transaction in the block. - inputs[2] is the receipt of the anchor transaction. | +| Name | Type | Description | +| ------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| blockId | uint256 | The index of the block to prove. This is also used to select the right implementation version. | +| inputs | bytes[] | A list of data input: - inputs[0] is an abi-encoded object with various information regarding the block to be proven and the actual proofs. - inputs[1] is the actual anchor transaction in this L2 block. Note that the anchor transaction is always the first transaction in the block. - inputs[2] is the receipt of the anchor transaction. | ### proveBlockInvalid ```solidity -function proveBlockInvalid(uint256 blockIndex, bytes[] inputs) external +function proveBlockInvalid(uint256 blockId, bytes[] inputs) external ``` Prove a block is invalid with a zero-knowledge proof and a receipt @@ -75,10 +75,10 @@ merkel proof. #### Parameters -| Name | Type | Description | -| ---------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| blockIndex | uint256 | The index of the block to prove. This is also used to select the right implementation version. | -| inputs | bytes[] | A list of data input: - inputs[0] An Evidence object with various information regarding the block to be proven and the actual proofs. - inputs[1] The target block to be proven invalid. - inputs[2] The receipt for the `invalidBlock` transaction on L2. Note that the `invalidBlock` transaction is supposed to be the only transaction in the L2 block. | +| Name | Type | Description | +| ------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| blockId | uint256 | The index of the block to prove. This is also used to select the right implementation version. | +| inputs | bytes[] | A list of data input: - inputs[0] An Evidence object with various information regarding the block to be proven and the actual proofs. - inputs[1] The target block to be proven invalid. - inputs[2] The receipt for the `invalidBlock` transaction on L2. Note that the `invalidBlock` transaction is supposed to be the only transaction in the L2 block. | ### verifyBlocks @@ -94,6 +94,18 @@ Verify up to N blocks. | --------- | ------- | ------------------------------- | | maxBlocks | uint256 | Max number of blocks to verify. | +### getBlockFee + +```solidity +function getBlockFee() public view returns (uint256 premiumFee) +``` + +### getProofReward + +```solidity +function getProofReward(uint64 provenAt, uint64 proposedAt) public view returns (uint256 premiumReward) +``` + ### isCommitValid ```solidity diff --git a/packages/protocol/docs/L1/v1/V1Finalizing.md b/packages/protocol/docs/L1/v1/V1Finalizing.md index 1dcf04bbe02..23129248ef0 100644 --- a/packages/protocol/docs/L1/v1/V1Finalizing.md +++ b/packages/protocol/docs/L1/v1/V1Finalizing.md @@ -1,4 +1,4 @@ -## V1Finalizing +## V1Verifying ### BlockVerified @@ -15,11 +15,11 @@ event HeaderSynced(uint256 height, uint256 srcHeight, bytes32 srcHash) ### init ```solidity -function init(struct LibData.State s, bytes32 _genesisBlockHash) public +function init(struct LibData.State s, bytes32 _genesisBlockHash, uint256 _feeBase) public ``` ### verifyBlocks -```solidity -function verifyBlocks(struct LibData.State s, uint256 maxBlocks) public +``` + ``` diff --git a/packages/protocol/docs/L1/v1/V1Proposing.md b/packages/protocol/docs/L1/v1/V1Proposing.md index 2f2ce24c409..01dce60bd9c 100644 --- a/packages/protocol/docs/L1/v1/V1Proposing.md +++ b/packages/protocol/docs/L1/v1/V1Proposing.md @@ -21,7 +21,13 @@ function commitBlock(struct LibData.State s, bytes32 commitHash) public ### proposeBlock ```solidity -function proposeBlock(struct LibData.State s, bytes[] inputs) public +function proposeBlock(struct LibData.State s, contract AddressResolver resolver, bytes[] inputs) public +``` + +### getBlockFee + +```solidity +function getBlockFee(struct LibData.State s) public view returns (uint256 fee, uint256 premiumFee) ``` ### isCommitValid @@ -30,6 +36,12 @@ function proposeBlock(struct LibData.State s, bytes[] inputs) public function isCommitValid(struct LibData.State s, bytes32 hash) public view returns (bool) ``` +### \_calcProposerBootstrapReward + +```solidity +function _calcProposerBootstrapReward(struct LibData.State s) private view returns (uint256 proposerReward) +``` + ### \_validateMetadata ```solidity diff --git a/packages/protocol/docs/L1/v1/V1Proving.md b/packages/protocol/docs/L1/v1/V1Proving.md index 7f56bcc27e5..bbdee912ffd 100644 --- a/packages/protocol/docs/L1/v1/V1Proving.md +++ b/packages/protocol/docs/L1/v1/V1Proving.md @@ -20,13 +20,13 @@ event BlockProven(uint256 id, bytes32 parentHash, bytes32 blockHash, uint64 time ### proveBlock ```solidity -function proveBlock(struct LibData.State s, contract AddressResolver resolver, uint256 blockIndex, bytes[] inputs) public +function proveBlock(struct LibData.State s, contract AddressResolver resolver, uint256 blockId, bytes[] inputs) public ``` ### proveBlockInvalid ```solidity -function proveBlockInvalid(struct LibData.State s, contract AddressResolver resolver, uint256 blockIndex, bytes[] inputs) public +function proveBlockInvalid(struct LibData.State s, contract AddressResolver resolver, uint256 blockId, bytes[] inputs) public ``` ### \_proveBlock diff --git a/packages/protocol/docs/L1/v1/V1Utils.md b/packages/protocol/docs/L1/v1/V1Utils.md new file mode 100644 index 00000000000..b6aad8d1393 --- /dev/null +++ b/packages/protocol/docs/L1/v1/V1Utils.md @@ -0,0 +1,19 @@ +## V1Utils + +### feeScaleBeta + +```solidity +function feeScaleBeta(struct LibData.State s, uint256 fee, bool releaseOneSlot) public view returns (uint256) +``` + +### movingAverage + +```solidity +function movingAverage(uint256 ma, uint256 v, uint256 factor) internal pure returns (uint256) +``` + +### feeScaleAlpha + +```solidity +function feeScaleAlpha(uint64 tNow, uint64 tLast, uint64 tAvg) internal pure returns (uint256) +``` diff --git a/packages/protocol/docs/L2/V1TaikoL2.md b/packages/protocol/docs/L2/V1TaikoL2.md index 643da921471..7cd8b1594fc 100644 --- a/packages/protocol/docs/L2/V1TaikoL2.md +++ b/packages/protocol/docs/L2/V1TaikoL2.md @@ -109,5 +109,5 @@ function _checkPublicInputs() private ### \_hashPublicInputs ```solidity -function _hashPublicInputs(uint256 chainId, uint256 number, uint256 baseFee, bytes32[255] ancestors) private pure returns (bytes32) +function _hashPublicInputs(uint256 chainId, uint256 number, uint256 feeBase, bytes32[255] ancestors) private pure returns (bytes32) ``` diff --git a/packages/protocol/docs/libs/LibAnchorSignature.md b/packages/protocol/docs/libs/LibAnchorSignature.md index ac0531d1349..3d7fe857a4a 100644 --- a/packages/protocol/docs/libs/LibAnchorSignature.md +++ b/packages/protocol/docs/libs/LibAnchorSignature.md @@ -1,15 +1,15 @@ ## LibAnchorSignature -### TAIKO_GOLDEN_TOUCH_ADDRESS +### K_GOLDEN_TOUCH_ADDRESS ```solidity -address TAIKO_GOLDEN_TOUCH_ADDRESS +address K_GOLDEN_TOUCH_ADDRESS ``` -### TAIKO_GOLDEN_TOUCH_PRIVATEKEY +### K_GOLDEN_TOUCH_PRIVATEKEY ```solidity -uint256 TAIKO_GOLDEN_TOUCH_PRIVATEKEY +uint256 K_GOLDEN_TOUCH_PRIVATEKEY ``` ### GX diff --git a/packages/protocol/docs/libs/LibConstants.md b/packages/protocol/docs/libs/LibConstants.md index 812205af2df..cb224c99a17 100644 --- a/packages/protocol/docs/libs/LibConstants.md +++ b/packages/protocol/docs/libs/LibConstants.md @@ -1,79 +1,109 @@ ## LibConstants -### TAIKO_CHAIN_ID +### K_CHAIN_ID ```solidity -uint256 TAIKO_CHAIN_ID +uint256 K_CHAIN_ID ``` -### TAIKO_MAX_PROPOSED_BLOCKS +### K_MAX_NUM_BLOCKS ```solidity -uint256 TAIKO_MAX_PROPOSED_BLOCKS +uint256 K_MAX_NUM_BLOCKS ``` -### TAIKO_MAX_VERIFICATIONS_PER_TX +### K_FEE_PREMIUM_PHI ```solidity -uint256 TAIKO_MAX_VERIFICATIONS_PER_TX +uint256 K_FEE_PREMIUM_PHI ``` -### TAIKO_COMMIT_DELAY_CONFIRMATIONS +### K_REWARD_MULTIPLIER_PCTG ```solidity -uint256 TAIKO_COMMIT_DELAY_CONFIRMATIONS +uint64 K_REWARD_MULTIPLIER_PCTG ``` -### TAIKO_MAX_PROOFS_PER_FORK_CHOICE +### K_FEE_GRACE_PERIOD_PCTG ```solidity -uint256 TAIKO_MAX_PROOFS_PER_FORK_CHOICE +uint64 K_FEE_GRACE_PERIOD_PCTG ``` -### TAIKO_BLOCK_MAX_GAS_LIMIT +### K_FEE_MAX_PERIOD_PCTG ```solidity -uint256 TAIKO_BLOCK_MAX_GAS_LIMIT +uint64 K_FEE_MAX_PERIOD_PCTG ``` -### TAIKO_BLOCK_MAX_TXS +### K_MAX_FINALIZATIONS_PER_TX ```solidity -uint256 TAIKO_BLOCK_MAX_TXS +uint256 K_MAX_FINALIZATIONS_PER_TX ``` -### TAIKO_BLOCK_DEADEND_HASH +### K_COMMIT_DELAY_CONFIRMS ```solidity -bytes32 TAIKO_BLOCK_DEADEND_HASH +uint256 K_COMMIT_DELAY_CONFIRMS ``` -### TAIKO_TXLIST_MAX_BYTES +### K_MAX_PROOFS_PER_FORK_CHOICE ```solidity -uint256 TAIKO_TXLIST_MAX_BYTES +uint256 K_MAX_PROOFS_PER_FORK_CHOICE ``` -### TAIKO_TX_MIN_GAS_LIMIT +### K_BLOCK_MAX_GAS_LIMIT ```solidity -uint256 TAIKO_TX_MIN_GAS_LIMIT +uint256 K_BLOCK_MAX_GAS_LIMIT ``` -### V1_ANCHOR_TX_GAS_LIMIT +### K_BLOCK_MAX_TXS ```solidity -uint256 V1_ANCHOR_TX_GAS_LIMIT +uint256 K_BLOCK_MAX_TXS ``` -### V1_ANCHOR_TX_SELECTOR +### K_BLOCK_DEADEND_HASH ```solidity -bytes4 V1_ANCHOR_TX_SELECTOR +bytes32 K_BLOCK_DEADEND_HASH ``` -### V1_INVALIDATE_BLOCK_LOG_TOPIC +### K_TXLIST_MAX_BYTES ```solidity -bytes32 V1_INVALIDATE_BLOCK_LOG_TOPIC +uint256 K_TXLIST_MAX_BYTES +``` + +### K_TX_MIN_GAS_LIMIT + +```solidity +uint256 K_TX_MIN_GAS_LIMIT +``` + +### K_REWARD_BURN_BP + +```solidity +uint256 K_REWARD_BURN_BP +``` + +### K_ANCHOR_TX_GAS_LIMIT + +```solidity +uint256 K_ANCHOR_TX_GAS_LIMIT +``` + +### K_ANCHOR_TX_SELECTOR + +```solidity +bytes4 K_ANCHOR_TX_SELECTOR +``` + +### K_INVALIDATE_BLOCK_LOG_TOPIC + +```solidity +bytes32 K_INVALIDATE_BLOCK_LOG_TOPIC ``` diff --git a/packages/protocol/docs/libs/LibInvalidTxList.md b/packages/protocol/docs/libs/LibInvalidTxList.md index 2a2c134cc94..15ff494ca54 100644 --- a/packages/protocol/docs/libs/LibInvalidTxList.md +++ b/packages/protocol/docs/libs/LibInvalidTxList.md @@ -4,11 +4,11 @@ A library to invalidate a txList using the following rules: A txList is valid if and only if: -1. The txList's length is no more than `TAIKO_TXLIST_MAX_BYTES`. +1. The txList's length is no more than `K_TXLIST_MAX_BYTES`. 2. The txList is well-formed RLP, with no additional trailing bytes. -3. The total number of transactions is no more than `TAIKO_BLOCK_MAX_TXS`. +3. The total number of transactions is no more than `K_BLOCK_MAX_TXS`. 4. The sum of all transaction gas limit is no more than - `TAIKO_BLOCK_MAX_GAS_LIMIT`. + `K_BLOCK_MAX_GAS_LIMIT`. A transaction is valid if and only if: @@ -16,7 +16,7 @@ A transaction is valid if and only if: (rule #1 in Ethereum yellow paper). 2. The transaction's signature is valid (rule #2 in Ethereum yellow paper). 3. The transaction's the gas limit is no smaller than the intrinsic gas - `TAIKO_TX_MIN_GAS_LIMIT` (rule #5 in Ethereum yellow paper). + `K_TX_MIN_GAS_LIMIT` (rule #5 in Ethereum yellow paper). ### Reason diff --git a/packages/protocol/hardhat.config.ts b/packages/protocol/hardhat.config.ts index e5b50213f5d..d5f0ec648b2 100644 --- a/packages/protocol/hardhat.config.ts +++ b/packages/protocol/hardhat.config.ts @@ -92,8 +92,8 @@ const config: HardhatUserConfig = { eachLine: () => ({ transform: (line) => { for (const constantName of [ - "TAIKO_CHAIN_ID", - "K_COMMIT_DELAY_CONFIRMATIONS", + "K_CHAIN_ID", + "K_COMMIT_DELAY_CONFIRMS", "TAIKO_BLOCK_MAX_TXS", "TAIKO_TXLIST_MAX_BYTES", "TAIKO_BLOCK_MAX_GAS_LIMIT", diff --git a/packages/protocol/tasks/config.ts b/packages/protocol/tasks/config.ts index 22ae3d79c92..de7f7afc3b2 100644 --- a/packages/protocol/tasks/config.ts +++ b/packages/protocol/tasks/config.ts @@ -1,3 +1,3 @@ // https://github.com/ethereum-lists/chains/pull/1611 -export const TAIKO_CHAINID = 167 -export const DEFAULT_DEPLOY_CONFIRMATIONS = 12 +export const K_CHAIN_ID = 167 +export const K_DEPLOY_CONFIRMATIONS = 12 diff --git a/packages/protocol/tasks/deploy_L1.ts b/packages/protocol/tasks/deploy_L1.ts index 07fcd7a7f42..103b8121ff7 100644 --- a/packages/protocol/tasks/deploy_L1.ts +++ b/packages/protocol/tasks/deploy_L1.ts @@ -18,16 +18,11 @@ task("deploy_L1") "L2 genesis block hash", ethers.constants.HashZero ) - .addOptionalParam( - "l2ChainId", - "L2 chain id", - config.TAIKO_CHAINID, - types.int - ) + .addOptionalParam("l2ChainId", "L2 chain id", config.K_CHAIN_ID, types.int) .addOptionalParam( "confirmations", "Number of confirmations to wait for deploy transaction.", - config.DEFAULT_DEPLOY_CONFIRMATIONS, + config.K_DEPLOY_CONFIRMATIONS, types.int ) .setAction(async (args, hre: any) => { @@ -112,9 +107,11 @@ export async function deployContracts(hre: any) { "TaikoL1", await deployBaseLibs(hre) ) + const feeBase = hre.ethers.BigNumber.from(10).pow(18) + await utils.waitTx( hre, - await TaikoL1.init(AddressManager.address, l2GenesisBlockHash) + await TaikoL1.init(AddressManager.address, l2GenesisBlockHash, feeBase) ) // Used by LibBridgeRead @@ -163,26 +160,19 @@ async function deployBaseLibs(hre: any) { ) const libTxDecoder = await utils.deployContract(hre, "LibTxDecoder") - const v1Utils = await utils.deployContract(hre, "V1Utils") - const v1Finalizing = await utils.deployContract(hre, "V1Finalizing", { - V1Utils: v1Utils.address, - }) - const v1Proposing = await utils.deployContract(hre, "V1Proposing", { - V1Utils: v1Utils.address, - }) + const v1Verifying = await utils.deployContract(hre, "V1Verifying", {}) + const v1Proposing = await utils.deployContract(hre, "V1Proposing", {}) const v1Proving = await utils.deployContract(hre, "V1Proving", { LibZKP: libZKP.address, LibReceiptDecoder: libReceiptDecoder.address, LibTxDecoder: libTxDecoder.address, - V1Utils: v1Utils.address, }) return { - V1Finalizing: v1Finalizing.address, + V1Verifying: v1Verifying.address, V1Proposing: v1Proposing.address, V1Proving: v1Proving.address, - V1Utils: v1Utils.address, } } diff --git a/packages/protocol/test/L1/TaikoL1.test.ts b/packages/protocol/test/L1/TaikoL1.test.ts index 03ee88752c3..598dd4568ef 100644 --- a/packages/protocol/test/L1/TaikoL1.test.ts +++ b/packages/protocol/test/L1/TaikoL1.test.ts @@ -21,16 +21,8 @@ describe("TaikoL1", function () { await ethers.getContractFactory("LibZKP") ).deploy() - const v1Utils = await ( - await ethers.getContractFactory("V1Utils") - ).deploy() - const v1Proposing = await ( - await ethers.getContractFactory("V1Proposing", { - libraries: { - V1Utils: v1Utils.address, - }, - }) + await ethers.getContractFactory("V1Proposing") ).deploy() const v1Proving = await ( @@ -39,25 +31,19 @@ describe("TaikoL1", function () { LibReceiptDecoder: libReceiptDecoder.address, LibTxDecoder: libTxDecoder.address, LibZKP: libZKP.address, - V1Utils: v1Utils.address, }, }) ).deploy() - const v1Finalizing = await ( - await ethers.getContractFactory("V1Finalizing", { - libraries: { - V1Utils: v1Utils.address, - }, - }) + const v1Verifying = await ( + await ethers.getContractFactory("V1Verifying") ).deploy() const TaikoL1Factory = await ethers.getContractFactory("TaikoL1", { libraries: { - V1Finalizing: v1Finalizing.address, + V1Verifying: v1Verifying.address, V1Proposing: v1Proposing.address, V1Proving: v1Proving.address, - V1Utils: v1Utils.address, }, }) diff --git a/packages/protocol/test/bridge/libs/LibBridgeData.test.ts b/packages/protocol/test/bridge/libs/LibBridgeData.test.ts index 84a633d1503..9729cfc3c67 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeData.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeData.test.ts @@ -1,6 +1,6 @@ import { expect } from "chai" import { ethers } from "hardhat" -import { TAIKO_BRIDGE_MESSAGE } from "../../constants/messages" +import { K_BRIDGE_MESSAGE } from "../../constants/messages" import { MessageStatus } from "../../../tasks/utils" describe("LibBridgeData", function () { @@ -32,7 +32,7 @@ describe("LibBridgeData", function () { "tuple(uint256 id, address sender, uint256 srcChainId, uint256 destChainId, address owner, address to, address refundAddress, uint256 depositValue, uint256 callValue, uint256 processingFee, uint256 gasLimit, bytes data, string memo)", ] - const testVar = [TAIKO_BRIDGE_MESSAGE, testMessage] + const testVar = [K_BRIDGE_MESSAGE, testMessage] return { owner, @@ -51,7 +51,7 @@ describe("LibBridgeData", function () { await deployLibBridgeDataFixture() // dummy struct to test with - const testVar = [TAIKO_BRIDGE_MESSAGE, testMessage] + const testVar = [K_BRIDGE_MESSAGE, testMessage] const hashed = await libData.hashMessage(testMessage) const expectedEncoded = ethers.utils.defaultAbiCoder.encode( testTypes, diff --git a/packages/protocol/test/constants/messages.ts b/packages/protocol/test/constants/messages.ts index 212301c9f81..c6880c6fd00 100644 --- a/packages/protocol/test/constants/messages.ts +++ b/packages/protocol/test/constants/messages.ts @@ -1,3 +1,3 @@ -const TAIKO_BRIDGE_MESSAGE = "TAIKO_BRIDGE_MESSAGE" +const K_BRIDGE_MESSAGE = "TAIKO_BRIDGE_MESSAGE" -export { TAIKO_BRIDGE_MESSAGE } +export { K_BRIDGE_MESSAGE } diff --git a/packages/protocol/test/genesis/generate_genesis.test.ts b/packages/protocol/test/genesis/generate_genesis.test.ts index f92955c6bfd..3ffafc66242 100644 --- a/packages/protocol/test/genesis/generate_genesis.test.ts +++ b/packages/protocol/test/genesis/generate_genesis.test.ts @@ -183,7 +183,7 @@ action("Generate Genesis", function () { const tx = await V1TaikoL2.invalidateBlock( bytes, - 5, // hint: TX_INVALID_SIG + 6, // hint: TX_INVALID_SIG 0 ) diff --git a/packages/protocol/test/libs/LibTxUtils.test.ts b/packages/protocol/test/libs/LibTxUtils.test.ts index 4dc9a82a84b..ae92f04db92 100644 --- a/packages/protocol/test/libs/LibTxUtils.test.ts +++ b/packages/protocol/test/libs/LibTxUtils.test.ts @@ -30,7 +30,7 @@ describe("LibTxUtils", function () { await ethers.getContractFactory("TestLibRLPWriter") ).deploy() - chainId = (await libConstants.TAIKO_CHAIN_ID()).toNumber() + chainId = (await libConstants.K_CHAIN_ID()).toNumber() const unsignedLegacyTx: UnsignedTransaction = { type: 0, From 87bbb2b0bef2bfe5668ac0cf81c16263b4f5ccef Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Fri, 2 Dec 2022 11:41:12 +0800 Subject: [PATCH 14/57] fix --- packages/protocol/contracts/L2/V1TaikoL2.sol | 4 ++-- packages/protocol/contracts/libs/LibConstants.sol | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/protocol/contracts/L2/V1TaikoL2.sol b/packages/protocol/contracts/L2/V1TaikoL2.sol index ed70f39e01d..a313c70bdfc 100644 --- a/packages/protocol/contracts/L2/V1TaikoL2.sol +++ b/packages/protocol/contracts/L2/V1TaikoL2.sol @@ -209,9 +209,9 @@ contract V1TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { function _hashPublicInputs( uint256 chainId, uint256 number, - uint256 feeBase, + uint256 baseFee, bytes32[255] memory ancestors ) private pure returns (bytes32) { - return keccak256(abi.encodePacked(chainId, number, feeBase, ancestors)); + return keccak256(abi.encodePacked(chainId, number, baseFee, ancestors)); } } diff --git a/packages/protocol/contracts/libs/LibConstants.sol b/packages/protocol/contracts/libs/LibConstants.sol index b2cbc6845c5..a9809255ec8 100644 --- a/packages/protocol/contracts/libs/LibConstants.sol +++ b/packages/protocol/contracts/libs/LibConstants.sol @@ -28,7 +28,6 @@ library LibConstants { uint256 public constant K_ANCHOR_TX_GAS_LIMIT = 250000; uint256 public constant K_FEE_PREMIUM_LAMDA = 590; uint256 public constant K_REWARD_BURN_BP = 100; // 100 basis points or 1% - uint256 public constant K_ANCHOR_TX_GAS_LIMIT = 250000; uint256 public constant K_PROPOSER_DEPOSIT_PCTG = 25; // 25% // Moving average factors From 76c095d5ee0172f9df1e3c44d939bd6806d66aaa Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Fri, 2 Dec 2022 11:45:31 +0800 Subject: [PATCH 15/57] Update deploy_L1.ts --- packages/protocol/tasks/deploy_L1.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/protocol/tasks/deploy_L1.ts b/packages/protocol/tasks/deploy_L1.ts index 103b8121ff7..78fba005cc9 100644 --- a/packages/protocol/tasks/deploy_L1.ts +++ b/packages/protocol/tasks/deploy_L1.ts @@ -107,11 +107,10 @@ export async function deployContracts(hre: any) { "TaikoL1", await deployBaseLibs(hre) ) - const feeBase = hre.ethers.BigNumber.from(10).pow(18) await utils.waitTx( hre, - await TaikoL1.init(AddressManager.address, l2GenesisBlockHash, feeBase) + await TaikoL1.init(AddressManager.address, l2GenesisBlockHash) ) // Used by LibBridgeRead From 1e722babeb6121f6d0358d17eadcbb849b608394 Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Fri, 2 Dec 2022 11:46:19 +0800 Subject: [PATCH 16/57] Revert "Merge branch 'various_refactor' into tokenomics_implementation" This reverts commit b624ae7004b2e59dd0fb3267f55a7e53faa3cace, reversing changes made to 87bbb2b0bef2bfe5668ac0cf81c16263b4f5ccef. --- packages/protocol/tasks/deploy_L1.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/protocol/tasks/deploy_L1.ts b/packages/protocol/tasks/deploy_L1.ts index 78fba005cc9..103b8121ff7 100644 --- a/packages/protocol/tasks/deploy_L1.ts +++ b/packages/protocol/tasks/deploy_L1.ts @@ -107,10 +107,11 @@ export async function deployContracts(hre: any) { "TaikoL1", await deployBaseLibs(hre) ) + const feeBase = hre.ethers.BigNumber.from(10).pow(18) await utils.waitTx( hre, - await TaikoL1.init(AddressManager.address, l2GenesisBlockHash) + await TaikoL1.init(AddressManager.address, l2GenesisBlockHash, feeBase) ) // Used by LibBridgeRead From 231757258a849a23a4cdcaa321584e39e5c2030b Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Fri, 2 Dec 2022 11:47:46 +0800 Subject: [PATCH 17/57] add simulation code --- packages/tokenomics/.gitignore | 2 + packages/tokenomics/README.md | 15 + packages/tokenomics/main.py | 395 +++++++++++++++++++++++++ packages/tokenomics/plots.py | 22 ++ packages/tokenomics/present.py | 25 ++ packages/tokenomics/presents/cbvp1.py | 66 +++++ packages/tokenomics/presents/cbvp2.py | 66 +++++ packages/tokenomics/presents/p0.py | 34 +++ packages/tokenomics/presents/vbcp1.py | 66 +++++ packages/tokenomics/presents/vbcp2.py | 66 +++++ packages/tokenomics/presents/vbvps1.py | 57 ++++ packages/tokenomics/presents/vbvps2.py | 56 ++++ 12 files changed, 870 insertions(+) create mode 100644 packages/tokenomics/.gitignore create mode 100644 packages/tokenomics/README.md create mode 100644 packages/tokenomics/main.py create mode 100644 packages/tokenomics/plots.py create mode 100644 packages/tokenomics/present.py create mode 100644 packages/tokenomics/presents/cbvp1.py create mode 100644 packages/tokenomics/presents/cbvp2.py create mode 100644 packages/tokenomics/presents/p0.py create mode 100644 packages/tokenomics/presents/vbcp1.py create mode 100644 packages/tokenomics/presents/vbcp2.py create mode 100644 packages/tokenomics/presents/vbvps1.py create mode 100644 packages/tokenomics/presents/vbvps2.py diff --git a/packages/tokenomics/.gitignore b/packages/tokenomics/.gitignore new file mode 100644 index 00000000000..932765aeba6 --- /dev/null +++ b/packages/tokenomics/.gitignore @@ -0,0 +1,2 @@ +__pycache__/ +venv diff --git a/packages/tokenomics/README.md b/packages/tokenomics/README.md new file mode 100644 index 00000000000..fc129660dc5 --- /dev/null +++ b/packages/tokenomics/README.md @@ -0,0 +1,15 @@ +# README + +## Installation + +You need to install python, **salabim**, **streamlit**, and **matplotlib**. + +```sh +python3 -m venv venv +pip install salabim streamlit matplotlib +``` + +## Usage + +Then in this directory, simply run `streamlit run main.py`, +then visit [http://localhost:8501](http://localhost:8501/) to see simulation results. diff --git a/packages/tokenomics/main.py b/packages/tokenomics/main.py new file mode 100644 index 00000000000..f637c36bf84 --- /dev/null +++ b/packages/tokenomics/main.py @@ -0,0 +1,395 @@ +import salabim as sim +import matplotlib.pyplot as plt +import streamlit as st +from enum import Enum +from typing import NamedTuple +from plots import plot +from present import Config, Present +from presents.p0 import present as p0 +from presents.cbvp1 import present as cbvp1 +from presents.cbvp2 import present as cbvp2 +from presents.vbcp1 import present as vbcp1 +from presents.vbcp2 import present as vbcp2 +from presents.vbvps1 import present as vbvps1 +from presents.vbvps2 import present as vbvps2 + +# from presents.p7 import present as p7 +# from presents.p8 import present as p8 +# from presents.p9 import present as p9 +# from presents.p10 import present as p10 +# from presents.p11 import present as p11 + +DAY = 24 * 3600 +K_FEE_GRACE_PERIOD = 125 +K_FEE_MAX_PERIOD = 375 +K_BLOCK_TIME_CAP = 48 # 48 seconds +K_PROOF_TIME_CAP = 3600 * 1.5 # 1.5 hour + + +class Status(Enum): + PENDING = 1 + PROVEN = 2 + VERIFIED = 3 + + +class Block(NamedTuple): + status: Status + fee: int + proposed_at: int + proven_at: int + + +def get_day(config): + day = int(env.now() / DAY) + if day >= len(config.timing): + day = len(config.timing) - 1 + return day + + +def get_block_time_avg_second(config): + return config.timing[get_day(config)].block_time_avg_second + + +def get_proof_time_avg_second(config): + return config.timing[get_day(config)].proof_time_avg_minute * 60 + + +def moving_average(ma, v, maf): + if ma == 0: + return v + else: + _ma = (ma * (maf - 1) + v) * 1.0 / maf + if _ma > 0: + return _ma + else: + return ma + + +class Protocol(sim.Component): + def setup(self, config): + self.config = config + self.fee_base = config.fee_base + self.last_proposed_at = env.now() + self.last_VERIFIED_id = 0 + self.tko_supply = 0 + self.avg_block_time = 0 + self.avg_proof_time = 0 + + genesis = Block( + status=Status.VERIFIED, + fee=0, + proposed_at=env.now(), + proven_at=env.now(), + ) + self.blocks = [genesis] + + # monitors + self.m_pending_count = sim.Monitor("m_pending_count", level=True) + self.m_fee_base = sim.Monitor( + "m_fee_base", level=True, initial_tally=self.fee_base + ) + self.m_block_fee = sim.Monitor("m_block_fee", level=True) + self.m_proof_reward = sim.Monitor("m_proof_reward", level=True) + self.m_tko_supply = sim.Monitor("m_tko_supply", level=True) + self.m_block_time = sim.Monitor("m_block_time", level=True) + self.m_proof_time = sim.Monitor("m_proof_time", level=True) + + def get_time_adjusted_fee(self, is_proposal, t_now, t_last, t_avg, t_cap): + # if (tAvg == 0) { + # return s.feeBase; + # } + # uint256 _tAvg = tAvg > tCap ? tCap : tAvg; + # uint256 tGrace = (LibConstants.K_FEE_GRACE_PERIOD * _tAvg) / 100; + # uint256 tMax = (LibConstants.K_FEE_MAX_PERIOD * _tAvg) / 100; + # uint256 a = tLast + tGrace; + # uint256 b = tNow > a ? tNow - a : 0; + # uint256 tRel = (b.min(tMax) * 10000) / tMax; // [0 - 10000] + # uint256 alpha = 10000 + + # ((LibConstants.K_REWARD_MULTIPLIER - 100) * tRel) / + # 100; + # if (isProposal) { + # return (s.feeBase * 10000) / alpha; // fee + # } else { + # return (s.feeBase * alpha) / 10000; // reward + # } + + if t_avg == 0: + return self.fee_base + + if t_avg > t_cap: + _avg = t_cap + else: + _avg = t_avg + + t_grace = K_FEE_GRACE_PERIOD * _avg / 100.0 + t_max = K_FEE_MAX_PERIOD * _avg / 100.0 + a = t_last + t_grace + + if t_now > a: + b = t_now - a + else: + b = 0 + + if b > t_max: + b = t_max + + t_rel = 10000 * b / t_max + + alpha = 10000 + (self.config.reward_multiplier - 1) * t_rel + + if is_proposal: + return self.fee_base * 10000 / alpha + else: + return self.fee_base * alpha / 10000 + + def get_slots_adjusted_fee(self, is_proposal, fee): + # uint256 m = LibConstants.K_MAX_NUM_BLOCKS - + # 1 + + # LibConstants.K_FEE_PREMIUM_LAMDA; + # uint256 n = s.nextBlockId - s.latestVERIFIEDId - 1; + # uint256 k = isProposal ? m - n - 1 : m - n + 1; + # return (fee * (m - 1) * m) / (m - n) / k; + + m = self.config.max_blocks - 1 + self.config.lamda + n = self.num_pending() + if is_proposal: # fee + k = m - n - 1 + else: # reward + k = m - n + 1 + return fee * (m - 1) * m / (m - n) / k + + def get_block_fee(self): + fee = self.get_time_adjusted_fee( + True, + env.now(), + self.last_proposed_at, + self.avg_block_time, + K_BLOCK_TIME_CAP, + ) + + premium_fee = self.get_slots_adjusted_fee(True, fee) + # bootstrap discount not simulated + return (fee, premium_fee) + + def get_proof_reward(self, proven_at, proposed_at): + reward = self.get_time_adjusted_fee( + False, proven_at, proposed_at, self.avg_proof_time, K_PROOF_TIME_CAP + ) + premium_reward = self.get_slots_adjusted_fee(False, reward) + return (reward, premium_reward) + + def print_me(self, st): + st.markdown("-----") + st.markdown("##### Protocol state") + st.write("last_VERIFIED_id = {}".format(self.last_VERIFIED_id)) + st.write("num_blocks = {}".format(self.num_pending())) + st.write("fee_base = {}".format(self.fee_base)) + st.write("tko_supply = {}".format(self.tko_supply)) + + def num_pending(self): + return len(self.blocks) - self.last_VERIFIED_id - 1 + + def can_propose(self): + return self.num_pending() < self.config.max_blocks + + def propose_block(self): + if env.now() == 0 or not self.can_propose(): + return + + block_time = env.now() - self.last_proposed_at + + (fee, premium_fee) = self.get_block_fee() + + self.fee_base = moving_average(self.fee_base, fee, self.config.fee_maf) + self.avg_block_time = moving_average( + self.avg_block_time, + block_time, + self.config.time_avg_maf, + ) + self.last_proposed_at = env.now() + self.tko_supply -= premium_fee + + block = Block( + status=Status.PENDING, + fee=premium_fee, + proposed_at=env.now(), + proven_at=0, + ) + self.blocks.append(block) + + Prover(protocol=self, config=self.config, blockId=len(self.blocks) - 1) + self.verify_block() + + self.m_fee_base.tally(self.fee_base) + self.m_block_fee.tally(premium_fee) + self.m_tko_supply.tally(self.tko_supply) + self.m_block_time.tally(block_time) + self.m_pending_count.tally(self.num_pending()) + + def can_prove(self, id): + return ( + id > self.last_VERIFIED_id + and len(self.blocks) > id + and self.blocks[id].status == Status.PENDING + ) + + def prove_block(self, id): + if self.can_prove(id): + self.blocks[id] = self.blocks[id]._replace( + status=Status.PROVEN, proven_at=env.now() + ) + self.verify_block() + + def can_verify(self): + return ( + len(self.blocks) > self.last_VERIFIED_id + 1 + and self.blocks[self.last_VERIFIED_id + 1].status == Status.PROVEN + and env.now() + > self.blocks[self.last_VERIFIED_id + 1].proven_at + self.avg_proof_time + ) + + def verify_block(self): + for i in range(0, 5): + if self.can_verify(): + + k = self.last_VERIFIED_id + 1 + + self.blocks[k] = self.blocks[k]._replace(status=Status.VERIFIED) + + proof_time = self.blocks[k].proven_at - self.blocks[k].proposed_at + + (reward, premium_reward) = self.get_proof_reward( + self.blocks[k].proven_at, self.blocks[k].proposed_at + ) + + self.fee_base = moving_average( + self.fee_base, + reward, + self.config.fee_maf, + ) + + self.avg_proof_time = moving_average( + self.avg_proof_time, + proof_time, + self.config.time_avg_maf, + ) + + self.tko_supply += premium_reward + self.m_fee_base.tally(self.fee_base) + self.m_proof_reward.tally(premium_reward) + self.m_tko_supply.tally(self.tko_supply) + self.m_proof_time.tally(proof_time) + + self.last_VERIFIED_id = k + else: + break + + self.m_pending_count.tally(self.num_pending()) + + +class Prover(sim.Component): + def setup(self, protocol, config, blockId): + self.protocol = protocol + self.config = config + self.blockId = blockId + + def process(self): + _proof_time_avg_second = get_proof_time_avg_second(self.config) + yield self.hold( + sim.Bounded( + sim.Normal( + _proof_time_avg_second, + _proof_time_avg_second * self.config.proof_time_sd_pctg / 100, + ), + lowerbound=1, + ).sample() + ) + self.protocol.prove_block(self.blockId) + + +class Proposer(sim.Component): + def setup(self, protocol): + self.protocol = protocol + self.config = protocol.config + + def process(self): + while True: + if not self.protocol.can_propose(): + yield self.hold(1) + else: + self.protocol.propose_block() + _block_time_avg_second = get_block_time_avg_second(self.config) + yield self.hold( + sim.Bounded( + sim.Normal( + _block_time_avg_second, + _block_time_avg_second + * self.config.block_time_sd_pctg + / 100, + ), + lowerbound=1, + ).sample() + ) + + +def simulate(config, days): + st.markdown("-----") + st.markdown("##### Block & proof time and deviation settings") + st.caption("[block_time (seconds), proof_time (minutes)]") + time_str = "" + for t in config.timing: + time_str += str(t._asdict().values()) + st.write(time_str.replace("dict_values", " ☀️").replace("(", "").replace(")", "")) + + st.markdown("-----") + st.markdown("##### You can change these settings") + cols = st.columns([1, 1, 1, 1]) + inputs = {} + i = 0 + for (k, v) in config._asdict().items(): + if k != "timing": + inputs[k] = cols[i % 4].number_input(k, value=v) + i += 1 + + st.markdown("-----") + if st.button("Simulate {} days".format(days), key="run"): + actual_config = Config(timing=config.timing, **inputs) + + protocol = Protocol(config=actual_config) + proposer = Proposer(protocol=protocol) + + env.run(till=days * DAY) + + st.markdown("-----") + st.markdown("##### Block/Proof Time") + plot(days, [(protocol.m_block_time, "block time")], color="tab:blue") + plot(days, [(protocol.m_proof_time, "proof time")], color="tab:blue") + + st.markdown("-----") + st.markdown("##### Result") + plot(days, [(protocol.m_pending_count, "num pending blocks")]) + plot(days, [(protocol.m_fee_base, "fee_base")]) + plot(days, [(protocol.m_block_fee, "block fee")], color="tab:green") + plot(days, [(protocol.m_proof_reward, "proof reward")]) + + plot(days, [(protocol.m_tko_supply, "tko supply")], color="tab:red") + + protocol.print_me(st) + + +if __name__ == "__main__": + env = sim.Environment(trace=False) + st.title("Taiko Block Fee/Reward Simulation") + + presents = [p0, cbvp1, cbvp2, vbcp1, vbcp2, vbvps1, vbvps2] + st.markdown("## Configs") + selected = st.radio( + "Please choose one of the following predefined configs:", + range(0, len(presents)), + format_func=lambda x: presents[x].title, + ) + present = presents[selected] + st.markdown("-----") + st.markdown("##### Description") + st.markdown(present.desc) + simulate(present.config, present.days) diff --git a/packages/tokenomics/plots.py b/packages/tokenomics/plots.py new file mode 100644 index 00000000000..926848d139b --- /dev/null +++ b/packages/tokenomics/plots.py @@ -0,0 +1,22 @@ +import matplotlib.pyplot as plt +import matplotlib.ticker as ticker +import streamlit as st +import numpy as np + + +@ticker.FuncFormatter +def major_formatter(x, pos): + return "d%d" % (x / 24 / 3600) + + +def plot(days, sources, color="#E28BFD"): + fig, ax = plt.subplots(figsize=(15, 5), nrows=1, ncols=1) + for s in sources: + data = s[0].xt() + ax.plot(data[1], data[0], color, label=s[1]) + ax.legend(loc="lower center", fontsize=18.0) + ax.xaxis.set_ticks(np.arange(0, 24 * 3600 * (days + 1), 24 * 3600)) + ax.xaxis.set_tick_params(labelrotation=45) + ax.xaxis.set_major_formatter(major_formatter) + + st.write(fig) diff --git a/packages/tokenomics/present.py b/packages/tokenomics/present.py new file mode 100644 index 00000000000..02b2e819762 --- /dev/null +++ b/packages/tokenomics/present.py @@ -0,0 +1,25 @@ +from typing import NamedTuple + + +class Timing(NamedTuple): + block_time_avg_second: int + proof_time_avg_minute: int + + +class Config(NamedTuple): + max_blocks: int + lamda: float + fee_base: int + fee_maf: int + reward_multiplier: float + block_time_sd_pctg: int + proof_time_sd_pctg: int + time_avg_maf: int + timing: list[Timing] + + +class Present(NamedTuple): + title: str + desc: str + days: int + config: Config diff --git a/packages/tokenomics/presents/cbvp1.py b/packages/tokenomics/presents/cbvp1.py new file mode 100644 index 00000000000..f94cb1b9027 --- /dev/null +++ b/packages/tokenomics/presents/cbvp1.py @@ -0,0 +1,66 @@ +from present import Config, Timing, Present + +present = Present( + title="cbvp1: constant block time, proof time goes down, up, then restores", + desc=""" + +**About this config** + +- the block time average set to a constant. +- the proof time average varies but eventually changes back to the initial value. + +**What to verify** +- fee_base will become smaller if proof time becomes larger. +- fee_base remains the same if proof time becomes smaller. + +""", + days=21, + config=Config( + max_blocks=2048, + lamda=590, + fee_base=100.0, + fee_maf=1024, + reward_multiplier=4.0, + time_avg_maf=1024, + block_time_sd_pctg=0, + proof_time_sd_pctg=0, + timing=[ + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45/1.3, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45/1.3/1.3, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45/1.3, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45*1.3, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45*1.3*1.3, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45*1.3, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + ], + ), +) diff --git a/packages/tokenomics/presents/cbvp2.py b/packages/tokenomics/presents/cbvp2.py new file mode 100644 index 00000000000..efa29142887 --- /dev/null +++ b/packages/tokenomics/presents/cbvp2.py @@ -0,0 +1,66 @@ +from present import Config, Timing, Present + +present = Present( + title="cbvp2: constant block time, proof time goes up, down, then restores", + desc=""" + +**About this config** + +- the block time average set to a constant. +- the proof time average varies but eventually changes back to the initial value. + +**What to verify** +- fee_base will become smaller if proof time becomes larger. +- fee_base remains the same if proof time becomes smaller. + +""", + days=21, + config=Config( + max_blocks=2048, + lamda=590, + fee_base=100.0, + fee_maf=1024, + reward_multiplier=4.0, + time_avg_maf=1024, + block_time_sd_pctg=0, + proof_time_sd_pctg=0, + timing=[ + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45*1.3, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45*1.3*1.3, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45*1.3, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45/1.3, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45/1.3/1.3, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45/1.3, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + ], + ), +) diff --git a/packages/tokenomics/presents/p0.py b/packages/tokenomics/presents/p0.py new file mode 100644 index 00000000000..52da6756a95 --- /dev/null +++ b/packages/tokenomics/presents/p0.py @@ -0,0 +1,34 @@ +from present import Config, Timing, Present + +present = Present( + title="p0: block time and proof time both constant", + desc=""" + +**What to simulate?** + +The most basic model where the block time average and proof time average are both constant. + +**About this config** + +- TKO supply changes initially but stablizes. +- fee_base remains constant + +""", + days=7, + config=Config( + max_blocks=2048, + lamda=590, + fee_base=100.0, + fee_maf=1024, + reward_multiplier=4.0, + time_avg_maf=1024, + block_time_sd_pctg=0, + proof_time_sd_pctg=0, + timing=[ + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + ], + ), +) diff --git a/packages/tokenomics/presents/vbcp1.py b/packages/tokenomics/presents/vbcp1.py new file mode 100644 index 00000000000..476c4d05397 --- /dev/null +++ b/packages/tokenomics/presents/vbcp1.py @@ -0,0 +1,66 @@ +from present import Config, Timing, Present + +present = Present( + title="vbcp1: constant proof time, block time goes down, up, then restores", + desc=""" + +**About this config** + +- the proof time average set to a constant. +- the block time average varies but eventually changes back to the initial value. + +**What to verify** +- fee_base will become smaller if block time becomes larger. +- fee_base remains the same if block time becomes smaller. + +""", + days=21, + config=Config( + max_blocks=2048, + lamda=590, + fee_base=100.0, + fee_maf=1024, + reward_multiplier=4.0, + time_avg_maf=1024, + block_time_sd_pctg=0, + proof_time_sd_pctg=0, + timing=[ + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15/1.3, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15/1.3/1.3, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15/1.3, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15*1.3, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15*1.3*1.3, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15*1.3, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + ], + ), +) diff --git a/packages/tokenomics/presents/vbcp2.py b/packages/tokenomics/presents/vbcp2.py new file mode 100644 index 00000000000..c581fdb0c25 --- /dev/null +++ b/packages/tokenomics/presents/vbcp2.py @@ -0,0 +1,66 @@ +from present import Config, Timing, Present + +present = Present( + title="vbcp2: constant proof time, block time goes up, down, then restores", + desc=""" + +**About this config** + +- the proof time average set to a constant. +- the block time average varies but eventually changes back to the initial value. + +**What to verify** +- fee_base will become smaller if block time becomes larger. +- fee_base remains the same if block time becomes smaller. + +""", + days=21, + config=Config( + max_blocks=2048, + lamda=590, + fee_base=100.0, + fee_maf=1024, + reward_multiplier=4.0, + time_avg_maf=1024, + block_time_sd_pctg=0, + proof_time_sd_pctg=0, + timing=[ + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15*1.3, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15*1.3*1.3, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15*1.3, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15/1.3, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15/1.3/1.3, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15/1.3, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + ], + ), +) diff --git a/packages/tokenomics/presents/vbvps1.py b/packages/tokenomics/presents/vbvps1.py new file mode 100644 index 00000000000..1808d62b917 --- /dev/null +++ b/packages/tokenomics/presents/vbvps1.py @@ -0,0 +1,57 @@ +from present import Config, Timing, Present + +present = Present( + title="vbvps1: block time & proof time both go down, up to the SAME direction, then restores", + desc=""" + +""", + days=21, + config=Config( + max_blocks=2048, + lamda=590, + fee_base=100.0, + fee_maf=1024, + reward_multiplier=4.0, + time_avg_maf=1024, + block_time_sd_pctg=0, + proof_time_sd_pctg=0, + timing=[ + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15/1.3, + proof_time_avg_minute=45/1.3, + ), + Timing( + block_time_avg_second=15/1.3/1.3, + proof_time_avg_minute=45/1.3/1.3, + ), + Timing( + block_time_avg_second=15/1.3, + proof_time_avg_minute=45/1.3, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15*1.3, + proof_time_avg_minute=45*1.3, + ), + Timing( + block_time_avg_second=15*1.3*1.3, + proof_time_avg_minute=45*1.3*1.3, + ), + Timing( + block_time_avg_second=15*1.3, + proof_time_avg_minute=45*1.3, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + ], + ), +) diff --git a/packages/tokenomics/presents/vbvps2.py b/packages/tokenomics/presents/vbvps2.py new file mode 100644 index 00000000000..f93e04f0df8 --- /dev/null +++ b/packages/tokenomics/presents/vbvps2.py @@ -0,0 +1,56 @@ +from present import Config, Timing, Present + +present = Present( + title="vbvps2: block time & proof time both go up, down to the SAME direction, then restores", + desc=""" +""", + days=21, + config=Config( + max_blocks=2048, + lamda=590, + fee_base=100.0, + fee_maf=1024, + reward_multiplier=4.0, + time_avg_maf=1024, + block_time_sd_pctg=0, + proof_time_sd_pctg=0, + timing=[ + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15*1.3, + proof_time_avg_minute=45*1.3, + ), + Timing( + block_time_avg_second=15*1.3*1.3, + proof_time_avg_minute=45*1.3*1.3, + ), + Timing( + block_time_avg_second=15*1.3, + proof_time_avg_minute=45*1.3, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + Timing( + block_time_avg_second=15/1.3, + proof_time_avg_minute=45/1.3, + ), + Timing( + block_time_avg_second=15/1.3/1.3, + proof_time_avg_minute=45/1.3/1.3, + ), + Timing( + block_time_avg_second=15/1.3, + proof_time_avg_minute=45/1.3, + ), + Timing( + block_time_avg_second=15, + proof_time_avg_minute=45, + ), + ], + ), +) From 53cc4b949555215146a9e25bfa99d824a4ffb021 Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Fri, 2 Dec 2022 12:09:13 +0800 Subject: [PATCH 18/57] Update generate_genesis.test.ts --- packages/protocol/test/genesis/generate_genesis.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/test/genesis/generate_genesis.test.ts b/packages/protocol/test/genesis/generate_genesis.test.ts index 3ffafc66242..f92955c6bfd 100644 --- a/packages/protocol/test/genesis/generate_genesis.test.ts +++ b/packages/protocol/test/genesis/generate_genesis.test.ts @@ -183,7 +183,7 @@ action("Generate Genesis", function () { const tx = await V1TaikoL2.invalidateBlock( bytes, - 6, // hint: TX_INVALID_SIG + 5, // hint: TX_INVALID_SIG 0 ) From 78d9e8b43790b92d5b1ffccdcb1e8240d263ca28 Mon Sep 17 00:00:00 2001 From: RogerLamTd Date: Mon, 5 Dec 2022 11:16:28 -0500 Subject: [PATCH 19/57] switched isSignalReceived test to l1 so eth_getProof is supported --- packages/protocol/test/bridge/Bridge.test.ts | 125 ++++++++++--------- 1 file changed, 63 insertions(+), 62 deletions(-) diff --git a/packages/protocol/test/bridge/Bridge.test.ts b/packages/protocol/test/bridge/Bridge.test.ts index 7bffcf862a9..131b1dfb9d2 100644 --- a/packages/protocol/test/bridge/Bridge.test.ts +++ b/packages/protocol/test/bridge/Bridge.test.ts @@ -1376,16 +1376,22 @@ describe("integration:Bridge", function () { describe("isSignalReceived()", function () { it.only("should return true", async function () { - const { l2Bridge, headerSync, l2Signer } = + const { l1Bridge, headerSync, srcChainId } = await deployBridgeFixture() const signal = ethers.utils.hexlify(ethers.utils.randomBytes(32)) - const tx = await l2Bridge.connect(l2Signer).sendSignal(signal) + const nonOwnerSigner = await ethers.provider.getSigner() + + const tx = await l1Bridge.connect(nonOwnerSigner).sendSignal(signal) await tx.wait() - const sender = await l2Signer.getAddress() + // const tx = await l2Bridge.connect(l2Signer).sendSignal(signal) + + // await tx.wait() + + const sender = await nonOwnerSigner.getAddress() const key = ethers.utils.keccak256( ethers.utils.solidityPack( @@ -1394,43 +1400,41 @@ describe("integration:Bridge", function () { ) ) - // use this instead of ethers.provider.getBlock() beccause it doesnt have stateRoot - // in the response - const block: Block = await l2Signer.provider.send( + const block: Block = await ethers.provider.send( "eth_getBlockByNumber", ["latest", false] ) await headerSync.setSyncedHeader(block.hash) - // 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, - // } + 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, + } // get storageValue for the key - const storageValue = await l2Signer.provider.getStorageAt( - l2Bridge.address, + const storageValue = await ethers.provider.getStorageAt( + l1Bridge.address, key, block.number ) @@ -1439,36 +1443,33 @@ describe("integration:Bridge", function () { "0x0000000000000000000000000000000000000000000000000000000000000001" ) - // // rpc call to get the merkle proof what value is at key on the bridge contract - // const proof: EthGetProofResponse = await ethers.provider.send( - // "eth_getProof", - // [l2Bridge.address, [key], block.hash] - // ) - - // // RLP encode the proof together for LibTrieProof to decode - // const encodedProof = ethers.utils.defaultAbiCoder.encode( - // ["bytes", "bytes"], - // [ - // RLP.encode(proof.accountProof), - // RLP.encode(proof.storageProof[0].proof), - // ] - // ) - // // encode the SignalProof struct from LibBridgeSignal - // 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)", - // ], - // [{ header: blockHeader, proof: encodedProof }] - // ) - - // expect( - // await l2Bridge.isSignalReceived( - // signal, - // srcChainId, - // sender, - // signalProof - // ) - // ).to.be.eq(true) + // rpc call to get the merkle proof what value is at key on the bridge contract + const proof: EthGetProofResponse = await ethers.provider.send( + "eth_getProof", + [l1Bridge.address, [key], block.hash] + ) + + // RLP encode the proof together for LibTrieProof to decode + const encodedProof = ethers.utils.defaultAbiCoder.encode( + ["bytes", "bytes"], + [ + RLP.encode(proof.accountProof), + RLP.encode(proof.storageProof[0].proof), + ] + ) + // encode the SignalProof struct from LibBridgeSignal + 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)", + ], + [{ header: blockHeader, proof: encodedProof }] + ) + + expect( + await l1Bridge + .connect(nonOwnerSigner) + .isSignalReceived(signal, srcChainId, sender, signalProof) + ).to.be.eq(true) }) }) }) From 80098c79e8bdce029a5171c558b1d24ab30742d9 Mon Sep 17 00:00:00 2001 From: RogerLamTd Date: Mon, 5 Dec 2022 14:44:15 -0500 Subject: [PATCH 20/57] fix isSignalReceived() test --- packages/protocol/test/bridge/Bridge.test.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/protocol/test/bridge/Bridge.test.ts b/packages/protocol/test/bridge/Bridge.test.ts index 131b1dfb9d2..88345dc24f8 100644 --- a/packages/protocol/test/bridge/Bridge.test.ts +++ b/packages/protocol/test/bridge/Bridge.test.ts @@ -1375,15 +1375,13 @@ describe("integration:Bridge", function () { }) describe("isSignalReceived()", function () { - it.only("should return true", async function () { - const { l1Bridge, headerSync, srcChainId } = + it("should return true", async function () { + const { l1Bridge, headerSync, srcChainId, l2Bridge, owner } = await deployBridgeFixture() const signal = ethers.utils.hexlify(ethers.utils.randomBytes(32)) - const nonOwnerSigner = await ethers.provider.getSigner() - - const tx = await l1Bridge.connect(nonOwnerSigner).sendSignal(signal) + const tx = await l1Bridge.connect(owner).sendSignal(signal) await tx.wait() @@ -1391,7 +1389,7 @@ describe("integration:Bridge", function () { // await tx.wait() - const sender = await nonOwnerSigner.getAddress() + const sender = owner.address const key = ethers.utils.keccak256( ethers.utils.solidityPack( @@ -1466,9 +1464,12 @@ describe("integration:Bridge", function () { ) expect( - await l1Bridge - .connect(nonOwnerSigner) - .isSignalReceived(signal, srcChainId, sender, signalProof) + await l2Bridge.isSignalReceived( + signal, + srcChainId, + sender, + signalProof + ) ).to.be.eq(true) }) }) From f4a86817917f001707f533f669c860f1fd6ad401 Mon Sep 17 00:00:00 2001 From: RogerLamTd Date: Mon, 5 Dec 2022 15:12:16 -0500 Subject: [PATCH 21/57] fix isSignalReceived() test --- packages/protocol/test/bridge/Bridge.test.ts | 7 ++----- packages/protocol/test/bridge/libs/LibBridgeData.test.ts | 1 - 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/protocol/test/bridge/Bridge.test.ts b/packages/protocol/test/bridge/Bridge.test.ts index 88345dc24f8..a35c08b83f0 100644 --- a/packages/protocol/test/bridge/Bridge.test.ts +++ b/packages/protocol/test/bridge/Bridge.test.ts @@ -1385,10 +1385,6 @@ describe("integration:Bridge", function () { await tx.wait() - // const tx = await l2Bridge.connect(l2Signer).sendSignal(signal) - - // await tx.wait() - const sender = owner.address const key = ethers.utils.keccak256( @@ -1462,7 +1458,8 @@ describe("integration:Bridge", function () { ], [{ header: blockHeader, proof: encodedProof }] ) - + // proving functionality; l2Bridge can check if l1Bridge receives a signal + // allowing for dapp cross layer communication expect( await l2Bridge.isSignalReceived( signal, diff --git a/packages/protocol/test/bridge/libs/LibBridgeData.test.ts b/packages/protocol/test/bridge/libs/LibBridgeData.test.ts index 029726d78ac..7da0b4791eb 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeData.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeData.test.ts @@ -4,7 +4,6 @@ import { TestLibBridgeData } from "../../../typechain" import { K_BRIDGE_MESSAGE } from "../../constants/messages" import { MessageStatus } from "../../../tasks/utils" import { Message } from "../../utils/message" -import { TAIKO_BRIDGE_MESSAGE } from "../../constants/messages" describe("LibBridgeData", function () { async function deployLibBridgeDataFixture() { From 6049e3bd09bc21f40863aeb653d87e29f95f7181 Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Tue, 6 Dec 2022 13:40:19 +0800 Subject: [PATCH 22/57] Update LibConstants.sol --- packages/protocol/contracts/libs/LibConstants.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/protocol/contracts/libs/LibConstants.sol b/packages/protocol/contracts/libs/LibConstants.sol index 804497d523f..bcdfd0d608c 100644 --- a/packages/protocol/contracts/libs/LibConstants.sol +++ b/packages/protocol/contracts/libs/LibConstants.sol @@ -17,7 +17,6 @@ library LibConstants { // This number is calculated from K_MAX_NUM_BLOCKS to make // the 'the maximum value of the multiplier' close to 20.0 uint256 public constant K_ZKPROOFS_PER_BLOCK = 1; - uint256 public constant K_VERIFICATION_DELAY = 60 minutes; uint256 public constant K_MAX_VERIFICATIONS_PER_TX = 20; uint256 public constant K_COMMIT_DELAY_CONFIRMS = 4; uint256 public constant K_MAX_PROOFS_PER_FORK_CHOICE = 5; From 714cdbcd5594369e2dcca075dc48e3fd10f402fd Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Tue, 6 Dec 2022 13:41:07 +0800 Subject: [PATCH 23/57] format --- packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol | 2 +- packages/protocol/contracts/test/bridge/TestSignalSender.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol b/packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol index c30a04608ae..9925ccef2bf 100644 --- a/packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol +++ b/packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol @@ -95,7 +95,7 @@ library LibBridgeProcess { uint256 gasLimit = msg.sender == message.owner ? gasleft() : message.gasLimit; - + bool success = LibBridgeInvoke.invokeMessageCall({ state: state, message: message, diff --git a/packages/protocol/contracts/test/bridge/TestSignalSender.sol b/packages/protocol/contracts/test/bridge/TestSignalSender.sol index 9008f65fc9a..3844d58df3e 100644 --- a/packages/protocol/contracts/test/bridge/TestSignalSender.sol +++ b/packages/protocol/contracts/test/bridge/TestSignalSender.sol @@ -14,4 +14,4 @@ contract TestSignalSender { function sendSignal(IBridge bridge, bytes32 signal) public { bridge.sendSignal((signal)); } -} \ No newline at end of file +} From 79e62ef3fbc940af7ddee7d08139be26f582c811 Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Tue, 6 Dec 2022 13:42:32 +0800 Subject: [PATCH 24/57] Update LibConstants.sol --- packages/protocol/contracts/libs/LibConstants.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/protocol/contracts/libs/LibConstants.sol b/packages/protocol/contracts/libs/LibConstants.sol index 804497d523f..bcdfd0d608c 100644 --- a/packages/protocol/contracts/libs/LibConstants.sol +++ b/packages/protocol/contracts/libs/LibConstants.sol @@ -17,7 +17,6 @@ library LibConstants { // This number is calculated from K_MAX_NUM_BLOCKS to make // the 'the maximum value of the multiplier' close to 20.0 uint256 public constant K_ZKPROOFS_PER_BLOCK = 1; - uint256 public constant K_VERIFICATION_DELAY = 60 minutes; uint256 public constant K_MAX_VERIFICATIONS_PER_TX = 20; uint256 public constant K_COMMIT_DELAY_CONFIRMS = 4; uint256 public constant K_MAX_PROOFS_PER_FORK_CHOICE = 5; From 498dd592d2d9a0ff6210568a0155f9ffd385b5c6 Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Tue, 6 Dec 2022 13:49:27 +0800 Subject: [PATCH 25/57] Update LibConstants.sol --- packages/protocol/contracts/libs/LibConstants.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/contracts/libs/LibConstants.sol b/packages/protocol/contracts/libs/LibConstants.sol index bcdfd0d608c..a5782a01250 100644 --- a/packages/protocol/contracts/libs/LibConstants.sol +++ b/packages/protocol/contracts/libs/LibConstants.sol @@ -18,7 +18,7 @@ library LibConstants { // the 'the maximum value of the multiplier' close to 20.0 uint256 public constant K_ZKPROOFS_PER_BLOCK = 1; uint256 public constant K_MAX_VERIFICATIONS_PER_TX = 20; - uint256 public constant K_COMMIT_DELAY_CONFIRMS = 4; + uint256 public constant K_COMMIT_DELAY_CONFIRMS = 0; uint256 public constant K_MAX_PROOFS_PER_FORK_CHOICE = 5; uint256 public constant K_BLOCK_MAX_GAS_LIMIT = 5000000; // TODO uint256 public constant K_BLOCK_MAX_TXS = 20; // TODO From 103ee0010e7e1667e04479584fa7cb4ef3c29369 Mon Sep 17 00:00:00 2001 From: RogerLamTd Date: Tue, 6 Dec 2022 15:44:30 -0500 Subject: [PATCH 26/57] migrated repeated code to fixtures and util functions --- packages/protocol/tasks/utils.ts | 65 ++ packages/protocol/test/bridge/Bridge.test.ts | 653 +++---------------- 2 files changed, 161 insertions(+), 557 deletions(-) diff --git a/packages/protocol/tasks/utils.ts b/packages/protocol/tasks/utils.ts index 8c2c948c00e..57d5577ddd2 100644 --- a/packages/protocol/tasks/utils.ts +++ b/packages/protocol/tasks/utils.ts @@ -1,5 +1,7 @@ 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, @@ -84,6 +86,67 @@ const MessageStatus = { 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, + blockHash: string, + blockHeader: BlockHeader +) { + const proof: EthGetProofResponse = await hre.ethers.provider.send( + "eth_getProof", + [contractAddress, [key], blockHash] + ) + + // 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, @@ -94,4 +157,6 @@ export { getSlot, decode, MessageStatus, + getLatestBlockHeader, + getSignalProof, } diff --git a/packages/protocol/test/bridge/Bridge.test.ts b/packages/protocol/test/bridge/Bridge.test.ts index 1ea4c3306ae..5249313b3ce 100644 --- a/packages/protocol/test/bridge/Bridge.test.ts +++ b/packages/protocol/test/bridge/Bridge.test.ts @@ -1,5 +1,5 @@ import { expect } from "chai" -import { ethers } from "hardhat" +import hre, { ethers } from "hardhat" import { BigNumber, Signer } from "ethers" import { Message } from "../utils/message" import { @@ -12,6 +12,7 @@ import { TestLibBridgeData, } from "../../typechain" import { Block, BlockHeader, EthGetProofResponse } from "../utils/rpc" +import { getLatestBlockHeader, getSignalProof } from "../../tasks/utils" import RLP from "rlp" async function deployBridge( @@ -494,6 +495,22 @@ describe("integration:Bridge", function () { .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, @@ -507,6 +524,7 @@ describe("integration:Bridge", function () { l2EtherVault, srcChainId, headerSync, + m, } } @@ -567,30 +585,8 @@ describe("integration:Bridge", function () { }) it("should throw if messageStatus of message is != NEW", async function () { - const { - owner, - l1Bridge, - srcChainId, - enabledDestChainId, - l2Bridge, - headerSync, - } = await deployBridgeFixture() - - 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: "", - } + const { l1Bridge, l2Bridge, headerSync, m } = + await deployBridgeFixture() const expectedAmount = m.depositValue + m.callValue + m.processingFee @@ -613,58 +609,16 @@ describe("integration:Bridge", function () { ) ) - const block: Block = await ethers.provider.send( - "eth_getBlockByNumber", - ["latest", false] - ) + const { block, blockHeader } = await getLatestBlockHeader(hre) await headerSync.setSyncedHeader(block.hash) - 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, - } - - // rpc call to get the merkle proof what value is at key on the bridge contract - const proof: EthGetProofResponse = await ethers.provider.send( - "eth_getProof", - [l1Bridge.address, [key], block.hash] - ) - - // RLP encode the proof together for LibTrieProof to decode - const encodedProof = ethers.utils.defaultAbiCoder.encode( - ["bytes", "bytes"], - [ - RLP.encode(proof.accountProof), - RLP.encode(proof.storageProof[0].proof), - ] - ) - // encode the SignalProof struct from LibBridgeSignal - 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)", - ], - [{ header: blockHeader, proof: encodedProof }] + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.hash, + blockHeader ) // upon successful processing, this immediately gets marked as DONE @@ -677,35 +631,8 @@ describe("integration:Bridge", function () { }) it("should throw if message signalproof is not valid", async function () { - const { - owner, - l1Bridge, - srcChainId, - enabledDestChainId, - l2Bridge, - headerSync, - } = await deployBridgeFixture() - - 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: "", - } - - const block: Block = await ethers.provider.send( - "eth_getBlockByNumber", - ["latest", false] - ) + const { l1Bridge, l2Bridge, headerSync, m } = + await deployBridgeFixture() const libData: TestLibBridgeData = await ( await ethers.getContractFactory("TestLibBridgeData") @@ -721,53 +648,16 @@ describe("integration:Bridge", function () { [sender, signal] ) ) + const { block, blockHeader } = await getLatestBlockHeader(hre) await headerSync.setSyncedHeader(ethers.constants.HashZero) - 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, - } - - const proof: EthGetProofResponse = await ethers.provider.send( - "eth_getProof", - [l1Bridge.address, [key], block.hash] - ) - - // RLP encode the proof together for LibTrieProof to decode - const encodedProof = ethers.utils.defaultAbiCoder.encode( - ["bytes", "bytes"], - [ - RLP.encode(proof.accountProof), - RLP.encode(proof.storageProof[0].proof), - ] - ) - // encode the SignalProof struct from LibBridgeSignal - 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)", - ], - [{ header: blockHeader, proof: encodedProof }] + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.hash, + blockHeader ) await expect( @@ -776,30 +666,8 @@ describe("integration:Bridge", function () { }) it("should throw if message has not been received", async function () { - const { - owner, - l1Bridge, - srcChainId, - enabledDestChainId, - l2Bridge, - headerSync, - } = await deployBridgeFixture() - - 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: "", - } + const { l1Bridge, l2Bridge, headerSync, m } = + await deployBridgeFixture() const expectedAmount = m.depositValue + m.callValue + m.processingFee @@ -828,38 +696,10 @@ describe("integration:Bridge", function () { ) ) - const block: Block = await ethers.provider.send( - "eth_getBlockByNumber", - ["latest", false] - ) + const { block, blockHeader } = await getLatestBlockHeader(hre) await headerSync.setSyncedHeader(ethers.constants.HashZero) - 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, - } - // get storageValue for the key const storageValue = await ethers.provider.getStorageAt( l1Bridge.address, @@ -870,26 +710,13 @@ describe("integration:Bridge", function () { expect(storageValue).to.be.eq( "0x0000000000000000000000000000000000000000000000000000000000000001" ) - // rpc call to get the merkle proof what value is at key on the bridge contract - const proof: EthGetProofResponse = await ethers.provider.send( - "eth_getProof", - [l1Bridge.address, [key], block.hash] - ) - // RLP encode the proof together for LibTrieProof to decode - const encodedProof = ethers.utils.defaultAbiCoder.encode( - ["bytes", "bytes"], - [ - RLP.encode(proof.accountProof), - RLP.encode(proof.storageProof[0].proof), - ] - ) - // encode the SignalProof struct from LibBridgeSignal - 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)", - ], - [{ header: blockHeader, proof: encodedProof }] + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.hash, + blockHeader ) await expect( @@ -898,30 +725,8 @@ describe("integration:Bridge", function () { }) it("processes a message when the signal has been verified from the sending chain", async () => { - const { - owner, - l1Bridge, - srcChainId, - enabledDestChainId, - l2Bridge, - headerSync, - } = await deployBridgeFixture() - - 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: "", - } + const { l1Bridge, l2Bridge, headerSync, m } = + await deployBridgeFixture() const expectedAmount = m.depositValue + m.callValue + m.processingFee @@ -950,38 +755,10 @@ describe("integration:Bridge", function () { ) ) - const block: Block = await ethers.provider.send( - "eth_getBlockByNumber", - ["latest", false] - ) + const { block, blockHeader } = await getLatestBlockHeader(hre) await headerSync.setSyncedHeader(block.hash) - 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, - } - // get storageValue for the key const storageValue = await ethers.provider.getStorageAt( l1Bridge.address, @@ -992,56 +769,26 @@ describe("integration:Bridge", function () { expect(storageValue).to.be.eq( "0x0000000000000000000000000000000000000000000000000000000000000001" ) - // rpc call to get the merkle proof what value is at key on the bridge contract - const proof: EthGetProofResponse = await ethers.provider.send( - "eth_getProof", - [l1Bridge.address, [key], block.hash] - ) - // RLP encode the proof together for LibTrieProof to decode - const encodedProof = ethers.utils.defaultAbiCoder.encode( - ["bytes", "bytes"], - [ - RLP.encode(proof.accountProof), - RLP.encode(proof.storageProof[0].proof), - ] - ) - // encode the SignalProof struct from LibBridgeSignal - 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)", - ], - [{ header: blockHeader, proof: encodedProof }] + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.hash, + 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 { owner, l1Bridge, srcChainId, enabledDestChainId } = - await deployBridgeFixture() - - 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: "", - } + 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") @@ -1052,24 +799,7 @@ describe("integration:Bridge", function () { }) it("should return true if message was sent properly", async function () { - const { owner, l1Bridge, srcChainId, enabledDestChainId } = - await deployBridgeFixture() - - 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: "", - } + const { l1Bridge, m } = await deployBridgeFixture() const expectedAmount = m.depositValue + m.callValue + m.processingFee @@ -1269,38 +999,10 @@ describe("integration:Bridge", function () { ) ) - const block: Block = await ethers.provider.send( - "eth_getBlockByNumber", - ["latest", false] - ) + const { block, blockHeader } = await getLatestBlockHeader(hre) await headerSync.setSyncedHeader(block.hash) - 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, - } - // get storageValue for the key const storageValue = await ethers.provider.getStorageAt( l1Bridge.address, @@ -1312,57 +1014,22 @@ describe("integration:Bridge", function () { "0x0000000000000000000000000000000000000000000000000000000000000001" ) - // rpc call to get the merkle proof what value is at key on the bridge contract - const proof: EthGetProofResponse = await ethers.provider.send( - "eth_getProof", - [l1Bridge.address, [key], block.hash] + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.hash, + blockHeader ) - // RLP encode the proof together for LibTrieProof to decode - const encodedProof = ethers.utils.defaultAbiCoder.encode( - ["bytes", "bytes"], - [ - RLP.encode(proof.accountProof), - RLP.encode(proof.storageProof[0].proof), - ] - ) - // encode the SignalProof struct from LibBridgeSignal - 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)", - ], - [{ header: blockHeader, proof: encodedProof }] - ) await expect( l2Bridge.isMessageReceived(signal, srcChainId, signalProof) ).to.be.revertedWith("Invalid large internal hash") }) it("should return true", async function () { - const { - owner, - l1Bridge, - srcChainId, - enabledDestChainId, - l2Bridge, - headerSync, - } = await deployBridgeFixture() - - 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: "", - } + const { l1Bridge, srcChainId, l2Bridge, headerSync, m } = + await deployBridgeFixture() const expectedAmount = m.depositValue + m.callValue + m.processingFee @@ -1385,40 +1052,10 @@ describe("integration:Bridge", function () { ) ) - // use this instead of ethers.provider.getBlock() beccause it doesnt have stateRoot - // in the response - const block: Block = await ethers.provider.send( - "eth_getBlockByNumber", - ["latest", false] - ) + const { block, blockHeader } = await getLatestBlockHeader(hre) await headerSync.setSyncedHeader(block.hash) - 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, - } - // get storageValue for the key const storageValue = await ethers.provider.getStorageAt( l1Bridge.address, @@ -1430,26 +1067,12 @@ describe("integration:Bridge", function () { "0x0000000000000000000000000000000000000000000000000000000000000001" ) - // rpc call to get the merkle proof what value is at key on the bridge contract - const proof: EthGetProofResponse = await ethers.provider.send( - "eth_getProof", - [l1Bridge.address, [key], block.hash] - ) - - // RLP encode the proof together for LibTrieProof to decode - const encodedProof = ethers.utils.defaultAbiCoder.encode( - ["bytes", "bytes"], - [ - RLP.encode(proof.accountProof), - RLP.encode(proof.storageProof[0].proof), - ] - ) - // encode the SignalProof struct from LibBridgeSignal - 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)", - ], - [{ header: blockHeader, proof: encodedProof }] + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.hash, + blockHeader ) expect( @@ -1515,38 +1138,10 @@ describe("integration:Bridge", function () { ) ) - const block: Block = await ethers.provider.send( - "eth_getBlockByNumber", - ["latest", false] - ) + const { block, blockHeader } = await getLatestBlockHeader(hre) await headerSync.setSyncedHeader(block.hash) - 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, - } - // get storageValue for the key const storageValue = await ethers.provider.getStorageAt( l1Bridge.address, @@ -1558,26 +1153,12 @@ describe("integration:Bridge", function () { "0x0000000000000000000000000000000000000000000000000000000000000001" ) - // rpc call to get the merkle proof what value is at key on the bridge contract - const proof: EthGetProofResponse = await ethers.provider.send( - "eth_getProof", - [l1Bridge.address, [key], block.hash] - ) - - // RLP encode the proof together for LibTrieProof to decode - const encodedProof = ethers.utils.defaultAbiCoder.encode( - ["bytes", "bytes"], - [ - RLP.encode(proof.accountProof), - RLP.encode(proof.storageProof[0].proof), - ] - ) - // encode the SignalProof struct from LibBridgeSignal - 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)", - ], - [{ header: blockHeader, proof: encodedProof }] + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.hash, + blockHeader ) await expect( @@ -1609,38 +1190,10 @@ describe("integration:Bridge", function () { ) ) - const block: Block = await ethers.provider.send( - "eth_getBlockByNumber", - ["latest", false] - ) + const { block, blockHeader } = await getLatestBlockHeader(hre) await headerSync.setSyncedHeader(block.hash) - 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, - } - // get storageValue for the key const storageValue = await ethers.provider.getStorageAt( l1Bridge.address, @@ -1652,26 +1205,12 @@ describe("integration:Bridge", function () { "0x0000000000000000000000000000000000000000000000000000000000000001" ) - // rpc call to get the merkle proof what value is at key on the bridge contract - const proof: EthGetProofResponse = await ethers.provider.send( - "eth_getProof", - [l1Bridge.address, [key], block.hash] - ) - - // RLP encode the proof together for LibTrieProof to decode - const encodedProof = ethers.utils.defaultAbiCoder.encode( - ["bytes", "bytes"], - [ - RLP.encode(proof.accountProof), - RLP.encode(proof.storageProof[0].proof), - ] - ) - // encode the SignalProof struct from LibBridgeSignal - 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)", - ], - [{ header: blockHeader, proof: encodedProof }] + const signalProof = await getSignalProof( + hre, + l1Bridge.address, + key, + block.hash, + blockHeader ) // proving functionality; l2Bridge can check if l1Bridge receives a signal // allowing for dapp cross layer communication From 82b38cf4b97125f8d197424921588c1d706792f0 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 7 Dec 2022 17:00:20 +0800 Subject: [PATCH 27/57] fix(protocol): fix `BlockVerified` event --- packages/protocol/contracts/L1/v1/V1Verifying.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/contracts/L1/v1/V1Verifying.sol b/packages/protocol/contracts/L1/v1/V1Verifying.sol index 03ea7876d90..c57776928f6 100644 --- a/packages/protocol/contracts/L1/v1/V1Verifying.sol +++ b/packages/protocol/contracts/L1/v1/V1Verifying.sol @@ -71,8 +71,8 @@ library V1Verifying { latestL2Hash = fc.blockHash; } processed += 1; - _cleanUp(fc); emit BlockVerified(i, fc.blockHash); + _cleanUp(fc); } } From 64070a2151bc9b51471f0ccaafde782ad90db7cc Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Wed, 7 Dec 2022 23:18:32 +0800 Subject: [PATCH 28/57] Update Bridge.test.ts --- packages/protocol/test/bridge/Bridge.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/protocol/test/bridge/Bridge.test.ts b/packages/protocol/test/bridge/Bridge.test.ts index 6c50416dbfe..77a4dc47697 100644 --- a/packages/protocol/test/bridge/Bridge.test.ts +++ b/packages/protocol/test/bridge/Bridge.test.ts @@ -11,7 +11,6 @@ import { TestHeaderSync, TestLibBridgeData, } from "../../typechain" -import { Block, BlockHeader, EthGetProofResponse } from "../utils/rpc" import { getLatestBlockHeader, getSignalProof } from "../../tasks/utils" async function deployBridge( From 930426a3f7cd7470f79f8b5c3d31c76fada4547f Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Thu, 8 Dec 2022 22:48:45 +0800 Subject: [PATCH 29/57] Update V1Proposing.sol --- packages/protocol/contracts/L1/v1/V1Proposing.sol | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/protocol/contracts/L1/v1/V1Proposing.sol b/packages/protocol/contracts/L1/v1/V1Proposing.sol index bdd7ed9b1f5..6add00c3b10 100644 --- a/packages/protocol/contracts/L1/v1/V1Proposing.sol +++ b/packages/protocol/contracts/L1/v1/V1Proposing.sol @@ -98,6 +98,14 @@ library V1Proposing { meta.mixHash = bytes32(block.difficulty); } + state.avgBlockTime = V1Utils + .movingAverage({ + maValue: state.avgBlockTime, + newValue: meta.timestamp - state.lastProposedAt, + maf: LibConstants.K_BLOCK_TIME_MAF + }) + .toUint64(); + state.saveProposedBlock( state.nextBlockId, LibData.ProposedBlock({ From 1755eaa84bd329b9a3551972255149c567b3e281 Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Thu, 8 Dec 2022 22:48:59 +0800 Subject: [PATCH 30/57] Update V1Verifying.sol --- packages/protocol/contracts/L1/v1/V1Verifying.sol | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/protocol/contracts/L1/v1/V1Verifying.sol b/packages/protocol/contracts/L1/v1/V1Verifying.sol index c57776928f6..78dc35b3622 100644 --- a/packages/protocol/contracts/L1/v1/V1Verifying.sol +++ b/packages/protocol/contracts/L1/v1/V1Verifying.sol @@ -70,6 +70,15 @@ library V1Verifying { latestL2Height += 1; latestL2Hash = fc.blockHash; } + + state.avgProofTime = V1Utils + .movingAverage({ + maValue: state.avgProofTime, + newValue: fc.provenAt - target.proposedAt, + maf: LibConstants.K_PROOF_TIME_MAF + }) + .toUint64(); + processed += 1; emit BlockVerified(i, fc.blockHash); _cleanUp(fc); From fbd28ce58063afa4fa2798a6e4f832b46efd59da Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Thu, 8 Dec 2022 22:53:12 +0800 Subject: [PATCH 31/57] Update V1Verifying.sol --- packages/protocol/contracts/L1/v1/V1Verifying.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/protocol/contracts/L1/v1/V1Verifying.sol b/packages/protocol/contracts/L1/v1/V1Verifying.sol index 78dc35b3622..5125ff6073f 100644 --- a/packages/protocol/contracts/L1/v1/V1Verifying.sol +++ b/packages/protocol/contracts/L1/v1/V1Verifying.sol @@ -61,6 +61,10 @@ library V1Verifying { i++ ) { LibData.ForkChoice storage fc = state.forkChoices[i][latestL2Hash]; + LibData.ProposedBlock storage target = LibData.getProposedBlock( + state, + i + ); // Uncle proof can not take more than 2x time the first proof did. if (!_isVerifiable(state, fc)) { From 0fd073f87d4c933422bea2cc5506cd3c0c6b2086 Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Thu, 8 Dec 2022 22:53:57 +0800 Subject: [PATCH 32/57] Update LibConstants.sol --- packages/protocol/contracts/libs/LibConstants.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/protocol/contracts/libs/LibConstants.sol b/packages/protocol/contracts/libs/LibConstants.sol index a5782a01250..56feecbe865 100644 --- a/packages/protocol/contracts/libs/LibConstants.sol +++ b/packages/protocol/contracts/libs/LibConstants.sol @@ -26,6 +26,9 @@ library LibConstants { uint256 public constant K_TX_MIN_GAS_LIMIT = 21000; // TODO uint256 public constant K_ANCHOR_TX_GAS_LIMIT = 250000; + uint256 public constant K_BLOCK_TIME_MAF = 1024; + uint256 public constant K_PROOF_TIME_MAF = 1024; + bytes4 public constant K_ANCHOR_TX_SELECTOR = bytes4(keccak256("anchor(uint256,bytes32)")); From 94181da12b451c5db8626d08991037bfe2405393 Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Thu, 8 Dec 2022 22:55:09 +0800 Subject: [PATCH 33/57] Update V1Utils.sol --- packages/protocol/contracts/L1/v1/V1Utils.sol | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/protocol/contracts/L1/v1/V1Utils.sol b/packages/protocol/contracts/L1/v1/V1Utils.sol index b339b911899..877a8c7a543 100644 --- a/packages/protocol/contracts/L1/v1/V1Utils.sol +++ b/packages/protocol/contracts/L1/v1/V1Utils.sol @@ -101,6 +101,18 @@ library V1Utils { return fc.provenAt + state.avgProofTime; } + function movingAverage( + uint256 maValue, + uint256 newValue, + uint256 maf + ) internal pure returns (uint256) { + if (maValue == 0) { + return newValue; + } + uint256 _ma = (maValue * (maf - 1) + newValue) / maf; + return _ma > 0 ? _ma : maValue; + } + function setBit( LibData.State storage state, uint64 mask, From 677894fd739d877553152fcf326eed8cabb3d9d2 Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Thu, 8 Dec 2022 22:57:09 +0800 Subject: [PATCH 34/57] update --- packages/protocol/contracts/L1/v1/V1Proposing.sol | 3 ++- packages/protocol/contracts/L1/v1/V1Verifying.sol | 8 -------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/protocol/contracts/L1/v1/V1Proposing.sol b/packages/protocol/contracts/L1/v1/V1Proposing.sol index 0985856cb7a..b3b93c7148f 100644 --- a/packages/protocol/contracts/L1/v1/V1Proposing.sol +++ b/packages/protocol/contracts/L1/v1/V1Proposing.sol @@ -134,8 +134,9 @@ library V1Proposing { maf: LibConstants.K_BLOCK_TIME_MAF }) .toUint64(); + state.lastProposedAt = meta.timestamp; - + emit BlockProposed(state.nextBlockId++, meta); } diff --git a/packages/protocol/contracts/L1/v1/V1Verifying.sol b/packages/protocol/contracts/L1/v1/V1Verifying.sol index ff6bcf391c7..63da2f53ba7 100644 --- a/packages/protocol/contracts/L1/v1/V1Verifying.sol +++ b/packages/protocol/contracts/L1/v1/V1Verifying.sol @@ -99,14 +99,6 @@ library V1Verifying { newValue: newFeeBase, maf: LibConstants.K_FEE_BASE_MAF }); - - state.avgProofTime = V1Utils - .movingAverage({ - maValue: state.avgProofTime, - newValue: fc.provenAt - target.proposedAt, - maf: LibConstants.K_PROOF_TIME_MAF - }) - .toUint64(); } if (fc.blockHash != LibConstants.K_BLOCK_DEADEND_HASH) { From e974fd5fc279288597bb4515a214332b0518862e Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Thu, 8 Dec 2022 23:00:31 +0800 Subject: [PATCH 35/57] Update LibConstants.sol --- packages/protocol/contracts/libs/LibConstants.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/protocol/contracts/libs/LibConstants.sol b/packages/protocol/contracts/libs/LibConstants.sol index f070ab6c3ff..7ffc83be66c 100644 --- a/packages/protocol/contracts/libs/LibConstants.sol +++ b/packages/protocol/contracts/libs/LibConstants.sol @@ -41,9 +41,6 @@ library LibConstants { uint64 public constant K_PROOF_TIME_CAP = 60 minutes; uint64 public constant K_HALVING = 180 days; - uint256 public constant K_BLOCK_TIME_MAF = 1024; - uint256 public constant K_PROOF_TIME_MAF = 1024; - bytes4 public constant K_ANCHOR_TX_SELECTOR = bytes4(keccak256("anchor(uint256,bytes32)")); From 1affc84e1c077ab9158d96a09d04553d042ac763 Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Thu, 8 Dec 2022 23:01:10 +0800 Subject: [PATCH 36/57] more --- packages/protocol/contracts/L1/v1/V1Proposing.sol | 2 +- packages/protocol/contracts/L1/v1/V1Verifying.sol | 2 +- packages/protocol/contracts/libs/LibConstants.sol | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/protocol/contracts/L1/v1/V1Proposing.sol b/packages/protocol/contracts/L1/v1/V1Proposing.sol index b3b93c7148f..6d153ce4113 100644 --- a/packages/protocol/contracts/L1/v1/V1Proposing.sol +++ b/packages/protocol/contracts/L1/v1/V1Proposing.sol @@ -99,7 +99,7 @@ library V1Proposing { } uint256 deposit; - if (LibConstants.K_TOKENOMICS_ENABLED) { + if (LibConstants.K_ENABLE_TOKENOMICS) { uint256 newFeeBase; { uint256 fee; diff --git a/packages/protocol/contracts/L1/v1/V1Verifying.sol b/packages/protocol/contracts/L1/v1/V1Verifying.sol index 63da2f53ba7..14eb6f63ffd 100644 --- a/packages/protocol/contracts/L1/v1/V1Verifying.sol +++ b/packages/protocol/contracts/L1/v1/V1Verifying.sol @@ -75,7 +75,7 @@ library V1Verifying { if (!_isVerifiable(state, fc)) { break; } else { - if (LibConstants.K_TOKENOMICS_ENABLED) { + if (LibConstants.K_ENABLE_TOKENOMICS) { uint256 newFeeBase; { uint256 reward; diff --git a/packages/protocol/contracts/libs/LibConstants.sol b/packages/protocol/contracts/libs/LibConstants.sol index 7ffc83be66c..f834c4e57e2 100644 --- a/packages/protocol/contracts/libs/LibConstants.sol +++ b/packages/protocol/contracts/libs/LibConstants.sol @@ -48,5 +48,5 @@ library LibConstants { bytes32 public constant K_INVALIDATE_BLOCK_LOG_TOPIC = keccak256("BlockInvalidated(bytes32)"); - bool public constant K_TOKENOMICS_ENABLED = true; + bool public constant K_ENABLE_TOKENOMICS = true; } From 3cc4994b6c0c479858620798412a9cfa580d9031 Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Fri, 9 Dec 2022 11:45:17 +0800 Subject: [PATCH 37/57] fix David's concern --- packages/protocol/contracts/L1/v1/V1Proving.sol | 3 ++- packages/protocol/contracts/L1/v1/V1Utils.sol | 9 +++++++-- packages/protocol/contracts/L1/v1/V1Verifying.sol | 7 ++++--- packages/protocol/contracts/libs/LibConstants.sol | 2 ++ 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/protocol/contracts/L1/v1/V1Proving.sol b/packages/protocol/contracts/L1/v1/V1Proving.sol index 4182c022a51..0c0158e4e5f 100644 --- a/packages/protocol/contracts/L1/v1/V1Proving.sol +++ b/packages/protocol/contracts/L1/v1/V1Proving.sol @@ -269,7 +269,8 @@ library V1Proving { ); require( - block.timestamp < V1Utils.uncleProofDeadline(state, fc), + block.timestamp < + V1Utils.uncleProofDeadline(state, fc, target.id), "L1:tooLate" ); diff --git a/packages/protocol/contracts/L1/v1/V1Utils.sol b/packages/protocol/contracts/L1/v1/V1Utils.sol index 877a8c7a543..5b1921ea073 100644 --- a/packages/protocol/contracts/L1/v1/V1Utils.sol +++ b/packages/protocol/contracts/L1/v1/V1Utils.sol @@ -96,9 +96,14 @@ library V1Utils { // Returns a deterministic deadline for uncle proof submission. function uncleProofDeadline( LibData.State storage state, - LibData.ForkChoice storage fc + LibData.ForkChoice storage fc, + uint256 blockId ) internal view returns (uint64) { - return fc.provenAt + state.avgProofTime; + if (blockId <= 2 * LibConstants.K_MAX_NUM_BLOCKS) { + return fc.provenAt + LibConstants.K_INITIAL_UNCLE_DELAY; + } else { + return fc.provenAt + state.avgProofTime; + } } function movingAverage( diff --git a/packages/protocol/contracts/L1/v1/V1Verifying.sol b/packages/protocol/contracts/L1/v1/V1Verifying.sol index 5125ff6073f..9f33dcc1356 100644 --- a/packages/protocol/contracts/L1/v1/V1Verifying.sol +++ b/packages/protocol/contracts/L1/v1/V1Verifying.sol @@ -67,7 +67,7 @@ library V1Verifying { ); // Uncle proof can not take more than 2x time the first proof did. - if (!_isVerifiable(state, fc)) { + if (!_isVerifiable(state, fc, i)) { break; } else { if (fc.blockHash != LibConstants.K_BLOCK_DEADEND_HASH) { @@ -111,10 +111,11 @@ library V1Verifying { function _isVerifiable( LibData.State storage state, - LibData.ForkChoice storage fc + LibData.ForkChoice storage fc, + uint256 blockId ) private view returns (bool) { return fc.blockHash != 0 && - block.timestamp > V1Utils.uncleProofDeadline(state, fc); + block.timestamp > V1Utils.uncleProofDeadline(state, fc, blockId); } } diff --git a/packages/protocol/contracts/libs/LibConstants.sol b/packages/protocol/contracts/libs/LibConstants.sol index 56feecbe865..4804c038d8f 100644 --- a/packages/protocol/contracts/libs/LibConstants.sol +++ b/packages/protocol/contracts/libs/LibConstants.sol @@ -29,6 +29,8 @@ library LibConstants { uint256 public constant K_BLOCK_TIME_MAF = 1024; uint256 public constant K_PROOF_TIME_MAF = 1024; + uint64 public constant K_INITIAL_UNCLE_DELAY = 60 minutes; + bytes4 public constant K_ANCHOR_TX_SELECTOR = bytes4(keccak256("anchor(uint256,bytes32)")); From 621a45c226e21460006d16d1aea5b01a16895bc6 Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Fri, 9 Dec 2022 15:23:40 +0800 Subject: [PATCH 38/57] Update LibData.sol --- packages/protocol/contracts/L1/LibData.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/protocol/contracts/L1/LibData.sol b/packages/protocol/contracts/L1/LibData.sol index d753a798a83..d7d7f884fcc 100644 --- a/packages/protocol/contracts/L1/LibData.sol +++ b/packages/protocol/contracts/L1/LibData.sol @@ -63,7 +63,9 @@ library LibData { // Changed when a block is proven/finalized uint64 latestVerifiedHeight; uint64 latestVerifiedId; - uint64 avgProofTime; // the proof time moving average + // the proof time moving average, note that for each block, only the + // first proof's time is considered. + uint64 avgProofTime; uint64 __reservedC1; // Reserved uint256[42] __gap; From 019fa64c3a965bfc2e073a921dfff32d037cde6a Mon Sep 17 00:00:00 2001 From: dantaik <99078276+dantaik@users.noreply.github.com> Date: Fri, 9 Dec 2022 16:56:49 +0800 Subject: [PATCH 39/57] Update LibConstants.sol --- packages/protocol/contracts/libs/LibConstants.sol | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/protocol/contracts/libs/LibConstants.sol b/packages/protocol/contracts/libs/LibConstants.sol index c30dc9131cc..896750ef29c 100644 --- a/packages/protocol/contracts/libs/LibConstants.sol +++ b/packages/protocol/contracts/libs/LibConstants.sol @@ -42,17 +42,12 @@ library LibConstants { uint64 public constant K_HALVING = 180 days; uint64 public constant K_INITIAL_UNCLE_DELAY = 60 minutes; - uint256 public constant K_BLOCK_TIME_MAF = 1024; - uint256 public constant K_PROOF_TIME_MAF = 1024; - - uint64 public constant K_INITIAL_UNCLE_DELAY = 60 minutes; - - bytes4 public constant K_ANCHOR_TX_SELECTOR = - bytes4(keccak256("anchor(uint256,bytes32)")); - bytes32 public constant K_BLOCK_DEADEND_HASH = bytes32(uint256(1)); bytes32 public constant K_INVALIDATE_BLOCK_LOG_TOPIC = keccak256("BlockInvalidated(bytes32)"); + bytes4 public constant K_ANCHOR_TX_SELECTOR = + bytes4(keccak256("anchor(uint256,bytes32)")); + bool public constant K_ENABLE_TOKENOMICS = true; } From 4d01ce3349bfc05151622a273362054acef96894 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Fri, 23 Dec 2022 17:59:01 +0800 Subject: [PATCH 40/57] Update LibConstants.sol --- packages/protocol/contracts/libs/LibConstants.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/contracts/libs/LibConstants.sol b/packages/protocol/contracts/libs/LibConstants.sol index 896750ef29c..c0d2903b982 100644 --- a/packages/protocol/contracts/libs/LibConstants.sol +++ b/packages/protocol/contracts/libs/LibConstants.sol @@ -49,5 +49,5 @@ library LibConstants { bytes4 public constant K_ANCHOR_TX_SELECTOR = bytes4(keccak256("anchor(uint256,bytes32)")); - bool public constant K_ENABLE_TOKENOMICS = true; + bool public constant K_ENABLE_TOKENOMICS = false; } From 61be420fa47e1fbee6697fde2378bf8aa45cf50f Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Sat, 24 Dec 2022 19:22:42 +0800 Subject: [PATCH 41/57] refactor(protocol): use a Config object to replace LibConstants (#478) --- .github/workflows/solidity.yml | 12 +- packages/protocol/.solhintignore | 1 + packages/protocol/contracts/L1/LibData.sol | 85 +++--- packages/protocol/contracts/L1/TaikoL1.sol | 81 +++--- .../protocol/contracts/L1/v1/V1Proposing.sol | 64 +++-- .../protocol/contracts/L1/v1/V1Proving.sol | 243 ++++++++++-------- packages/protocol/contracts/L1/v1/V1Utils.sol | 93 ++++++- .../protocol/contracts/L1/v1/V1Verifying.sol | 140 ++++++---- packages/protocol/contracts/L2/TaikoL2.sol | 48 +--- .../contracts/libs/LibBlockHeader.sol | 4 +- .../protocol/contracts/libs/LibConstants.sol | 53 ---- .../contracts/libs/LibInvalidTxList.sol | 30 +-- .../contracts/libs/LibSharedConfig.sol | 50 ++++ .../protocol/contracts/libs/LibTxDecoder.sol | 10 +- .../protocol/contracts/libs/LibTxUtils.sol | 7 +- .../contracts/libs/LibUint512Math.sol | 4 +- .../contracts/test/L1/TestTaikoL1.sol | 54 ++++ .../contracts/test/libs/TestLibTxUtils.sol | 6 +- packages/protocol/hardhat.config.ts | 27 -- packages/protocol/package.json | 2 +- packages/protocol/tasks/deploy_L1.ts | 4 +- packages/protocol/test/L1/TaikoL1.test.ts | 19 +- .../test/genesis/generate_genesis.test.ts | 26 +- .../protocol/test/libs/LibTxDecoder.test.ts | 20 +- .../protocol/test/libs/LibTxUtils.test.ts | 43 ++-- 25 files changed, 648 insertions(+), 478 deletions(-) delete mode 100644 packages/protocol/contracts/libs/LibConstants.sol create mode 100644 packages/protocol/contracts/libs/LibSharedConfig.sol create mode 100644 packages/protocol/contracts/test/L1/TestTaikoL1.sol diff --git a/.github/workflows/solidity.yml b/.github/workflows/solidity.yml index 5a9874d61f3..2f6b3ea8018 100644 --- a/.github/workflows/solidity.yml +++ b/.github/workflows/solidity.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version: "16.15" - + - uses: pnpm/action-setup@v2 name: Install pnpm id: pnpm-install @@ -46,7 +46,15 @@ jobs: - name: protocol - Unit Tests working-directory: ./packages/protocol - run: pnpm install && pnpm clean && pnpm test:coverage && pnpm test:integration && pnpm export:abi + run: pnpm install && pnpm clean && pnpm test + + - name: protocol - Integration Tests + working-directory: ./packages/protocol + run: pnpm test:integration + + - name: protocol - Test Coverage + working-directory: ./packages/protocol + run: pnpm test:coverage - name: protocol - Generate Genesis working-directory: ./packages/protocol diff --git a/packages/protocol/.solhintignore b/packages/protocol/.solhintignore index b15d9ed051c..6d2e7e583c8 100644 --- a/packages/protocol/.solhintignore +++ b/packages/protocol/.solhintignore @@ -5,4 +5,5 @@ contracts/test/TestLibRLPWriter.sol contracts/libs/Lib1559Math.sol contracts/libs/LibAddress.sol contracts/libs/LibMath.sol +contracts/libs/LibUint512Math.sol **/contracts/thirdparty/**/*.sol \ No newline at end of file diff --git a/packages/protocol/contracts/L1/LibData.sol b/packages/protocol/contracts/L1/LibData.sol index ed20051316e..9ab517bbb45 100644 --- a/packages/protocol/contracts/L1/LibData.sol +++ b/packages/protocol/contracts/L1/LibData.sol @@ -8,10 +8,42 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; -import "../libs/LibConstants.sol"; - /// @author dantaik library LibData { + struct Config { + uint256 chainId; + // up to 2048 pending blocks + uint256 maxNumBlocks; + uint256 blockHashHistory; + // This number is calculated from maxNumBlocks to make + // the 'the maximum value of the multiplier' close to 20.0 + uint256 zkProofsPerBlock; + uint256 maxVerificationsPerTx; + uint256 commitConfirmations; + uint256 maxProofsPerForkChoice; + uint256 blockMaxGasLimit; + uint256 maxTransactionsPerBlock; + uint256 maxBytesPerTxList; + uint256 minTxGasLimit; + uint256 anchorTxGasLimit; + uint256 feePremiumLamda; + uint256 rewardBurnBips; + uint256 proposerDepositPctg; + // Moving average factors + uint256 feeBaseMAF; + uint256 blockTimeMAF; + uint256 proofTimeMAF; + uint64 rewardMultiplierPctg; + uint64 feeGracePeriodPctg; + uint64 feeMaxPeriodPctg; + uint64 blockTimeCap; + uint64 proofTimeCap; + uint64 boostrapDiscountHalvingPeriod; + uint64 initialUncleDelay; + bool enableTokenomics; + bool skipProofValidation; + } + struct BlockMetadata { uint256 id; uint256 l1Height; @@ -43,7 +75,7 @@ library LibData { // This struct takes 9 slots. struct State { - // block id => block hash + // block id => block hash (some blocks' hashes won't be persisted) mapping(uint256 => bytes32) l2Hashes; // block id => ProposedBlock mapping(uint256 => ProposedBlock) proposedBlocks; @@ -82,51 +114,4 @@ library LibData { // // Reserved uint256[46] __gap; } - - function saveProposedBlock( - LibData.State storage state, - uint256 id, - ProposedBlock memory blk - ) internal { - state.proposedBlocks[id % LibConstants.K_MAX_NUM_BLOCKS] = blk; - } - - function getProposedBlock( - State storage state, - uint256 id - ) internal view returns (ProposedBlock storage) { - return state.proposedBlocks[id % LibConstants.K_MAX_NUM_BLOCKS]; - } - - function getL2BlockHash( - State storage state, - uint256 number - ) internal view returns (bytes32) { - require(number <= state.latestVerifiedHeight, "L1:id"); - return state.l2Hashes[number]; - } - - function getStateVariables( - State storage state - ) - internal - view - returns ( - uint64 genesisHeight, - uint64 latestVerifiedHeight, - uint64 latestVerifiedId, - uint64 nextBlockId - ) - { - genesisHeight = state.genesisHeight; - latestVerifiedHeight = state.latestVerifiedHeight; - latestVerifiedId = state.latestVerifiedId; - nextBlockId = state.nextBlockId; - } - - function hashMetadata( - BlockMetadata memory meta - ) internal pure returns (bytes32) { - return keccak256(abi.encode(meta)); - } } diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index e776cd083dd..860da450c14 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -11,6 +11,7 @@ pragma solidity ^0.8.9; import "../common/EssentialContract.sol"; import "../common/IHeaderSync.sol"; import "../libs/LibAnchorSignature.sol"; +import "../libs/LibSharedConfig.sol"; import "./LibData.sol"; import "./v1/V1Events.sol"; import "./v1/V1Proposing.sol"; @@ -22,7 +23,7 @@ import "./v1/V1Verifying.sol"; * @author dantaik */ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { - using LibData for LibData.State; + using V1Utils for LibData.State; LibData.State public state; LibData.TentativeState public tentative; @@ -55,7 +56,12 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { * `calculateCommitHash(beneficiary, txListHash)`. */ function commitBlock(uint64 commitSlot, bytes32 commitHash) external { - V1Proposing.commitBlock(state, commitSlot, commitHash); + V1Proposing.commitBlock({ + state: state, + config: getConfig(), + commitSlot: commitSlot, + commitHash: commitHash + }); } /** @@ -78,16 +84,19 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { * transactions in the L2 block. */ function proposeBlock(bytes[] calldata inputs) external nonReentrant { + LibData.Config memory config = getConfig(); V1Proposing.proposeBlock({ state: state, + config: config, tentative: tentative, resolver: AddressResolver(this), inputs: inputs }); V1Verifying.verifyBlocks({ state: state, + config: getConfig(), resolver: AddressResolver(this), - maxBlocks: LibConstants.K_MAX_VERIFICATIONS_PER_TX, + maxBlocks: config.maxVerificationsPerTx, checkHalt: false }); } @@ -111,17 +120,20 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { uint256 blockId, bytes[] calldata inputs ) external nonReentrant { + LibData.Config memory config = getConfig(); V1Proving.proveBlock({ state: state, tentative: tentative, + config: config, resolver: AddressResolver(this), blockId: blockId, inputs: inputs }); V1Verifying.verifyBlocks({ state: state, + config: config, resolver: AddressResolver(this), - maxBlocks: LibConstants.K_MAX_VERIFICATIONS_PER_TX, + maxBlocks: config.maxVerificationsPerTx, checkHalt: false }); } @@ -144,17 +156,21 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { uint256 blockId, bytes[] calldata inputs ) external nonReentrant { + LibData.Config memory config = getConfig(); + V1Proving.proveBlockInvalid({ state: state, tentative: tentative, + config: config, resolver: AddressResolver(this), blockId: blockId, inputs: inputs }); V1Verifying.verifyBlocks({ state: state, + config: config, resolver: AddressResolver(this), - maxBlocks: LibConstants.K_MAX_VERIFICATIONS_PER_TX, + maxBlocks: config.maxVerificationsPerTx, checkHalt: false }); } @@ -167,6 +183,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { require(maxBlocks > 0, "L1:maxBlocks"); V1Verifying.verifyBlocks({ state: state, + config: getConfig(), resolver: AddressResolver(this), maxBlocks: maxBlocks, checkHalt: true @@ -254,7 +271,10 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { } function getBlockFee() public view returns (uint256) { - (, uint fee, uint deposit) = V1Proposing.getBlockFee(state); + (, uint fee, uint deposit) = V1Proposing.getBlockFee( + state, + getConfig() + ); return fee + deposit; } @@ -264,6 +284,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { ) public view returns (uint256 reward) { (, reward, ) = V1Verifying.getProofReward({ state: state, + config: getConfig(), provenAt: provenAt, proposedAt: proposedAt }); @@ -285,6 +306,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { return V1Proposing.isCommitValid( state, + getConfig().commitConfirmations, commitSlot, commitHeight, commitHash @@ -294,13 +316,14 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { function getProposedBlock( uint256 id ) public view returns (LibData.ProposedBlock memory) { - return state.getProposedBlock(id); + return state.getProposedBlock(getConfig().maxNumBlocks, id); } function getSyncedHeader( uint256 number - ) public view override returns (bytes32) { - return state.getL2BlockHash(number); + ) public view override returns (bytes32 header) { + header = state.getL2BlockHash(number); + require(header != 0, "L1:number"); } function getLatestSyncedHeader() public view override returns (bytes32) { @@ -312,9 +335,15 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { view returns ( uint64 /*genesisHeight*/, + uint64 /*genesisTimestamp*/, + uint64 /*statusBits*/, + uint256 /*feeBase*/, + uint64 /*nextBlockId*/, + uint64 /*lastProposedAt*/, + uint64 /*avgBlockTime*/, uint64 /*latestVerifiedHeight*/, uint64 /*latestVerifiedId*/, - uint64 /*nextBlockId*/ + uint64 /*avgProofTime*/ ) { return state.getStateVariables(); @@ -334,35 +363,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { return state.forkChoices[id][parentHash].provers; } - function getConstants() - public - pure - returns ( - uint256, // K_ZKPROOFS_PER_BLOCK - uint256, // K_CHAIN_ID - uint256, // K_MAX_NUM_BLOCKS - uint256, // K_MAX_VERIFICATIONS_PER_TX - uint256, // K_COMMIT_DELAY_CONFIRMS - uint256, // K_MAX_PROOFS_PER_FORK_CHOICE - uint256, // K_BLOCK_MAX_GAS_LIMIT - uint256, // K_BLOCK_MAX_TXS - uint256, // K_TXLIST_MAX_BYTES - uint256, // K_TX_MIN_GAS_LIMIT - uint256 // K_ANCHOR_TX_GAS_LIMIT - ) - { - return ( - LibConstants.K_ZKPROOFS_PER_BLOCK, - LibConstants.K_CHAIN_ID, - LibConstants.K_MAX_NUM_BLOCKS, - LibConstants.K_MAX_VERIFICATIONS_PER_TX, - LibConstants.K_COMMIT_DELAY_CONFIRMS, - LibConstants.K_MAX_PROOFS_PER_FORK_CHOICE, - LibConstants.K_BLOCK_MAX_GAS_LIMIT, - LibConstants.K_BLOCK_MAX_TXS, - LibConstants.K_TXLIST_MAX_BYTES, - LibConstants.K_TX_MIN_GAS_LIMIT, - LibConstants.K_ANCHOR_TX_GAS_LIMIT - ); + function getConfig() public pure virtual returns (LibData.Config memory) { + return LibSharedConfig.getConfig(); } } diff --git a/packages/protocol/contracts/L1/v1/V1Proposing.sol b/packages/protocol/contracts/L1/v1/V1Proposing.sol index 006525e1809..5df5c2a5826 100644 --- a/packages/protocol/contracts/L1/v1/V1Proposing.sol +++ b/packages/protocol/contracts/L1/v1/V1Proposing.sol @@ -9,7 +9,6 @@ pragma solidity ^0.8.9; import "../../common/ConfigManager.sol"; -import "../../libs/LibConstants.sol"; import "../../libs/LibTxDecoder.sol"; import "../TkoToken.sol"; import "./V1Utils.sol"; @@ -18,7 +17,8 @@ import "./V1Utils.sol"; library V1Proposing { using LibTxDecoder for bytes; using SafeCastUpgradeable for uint256; - using LibData for LibData.State; + using V1Utils for LibData.BlockMetadata; + using V1Utils for LibData.State; event BlockCommitted( uint64 commitSlot, @@ -36,10 +36,11 @@ library V1Proposing { function commitBlock( LibData.State storage state, + LibData.Config memory config, uint64 commitSlot, bytes32 commitHash ) public { - assert(LibConstants.K_COMMIT_DELAY_CONFIRMS > 0); + assert(config.commitConfirmations > 0); // It's OK to allow committing block when the system is halt. // By not checking the halt status, this method will be cheaper. // @@ -59,6 +60,7 @@ library V1Proposing { function proposeBlock( LibData.State storage state, + LibData.Config memory config, LibData.TentativeState storage tentative, AddressResolver resolver, bytes[] calldata inputs @@ -70,21 +72,25 @@ library V1Proposing { inputs[0], (LibData.BlockMetadata) ); - _verifyBlockCommit(state, meta); - _validateMetadata(meta); + _verifyBlockCommit({ + state: state, + commitConfirmations: config.commitConfirmations, + meta: meta + }); + _validateMetadata(config, meta); { bytes calldata txList = inputs[1]; // perform validation and populate some fields require( - txList.length >= 0 && - txList.length <= LibConstants.K_TXLIST_MAX_BYTES && + txList.length > 0 && + txList.length <= config.maxBytesPerTxList && meta.txListHash == txList.hashTxList(), "L1:txList" ); require( state.nextBlockId < - state.latestVerifiedId + LibConstants.K_MAX_NUM_BLOCKS, + state.latestVerifiedId + config.maxNumBlocks, "L1:tooMany" ); @@ -99,11 +105,11 @@ library V1Proposing { } uint256 deposit; - if (LibConstants.K_ENABLE_TOKENOMICS) { + if (config.enableTokenomics) { uint256 newFeeBase; { uint256 fee; - (newFeeBase, fee, deposit) = getBlockFee(state); + (newFeeBase, fee, deposit) = getBlockFee(state, config); TkoToken(resolver.resolve("tko_token")).burn( msg.sender, fee + deposit @@ -113,14 +119,15 @@ library V1Proposing { state.feeBase = V1Utils.movingAverage({ maValue: state.feeBase, newValue: newFeeBase, - maf: LibConstants.K_FEE_BASE_MAF + maf: config.feeBaseMAF }); } state.saveProposedBlock( + config.maxNumBlocks, state.nextBlockId, LibData.ProposedBlock({ - metaHash: LibData.hashMetadata(meta), + metaHash: meta.hashMetadata(), deposit: deposit, proposer: msg.sender, proposedAt: meta.timestamp @@ -131,7 +138,7 @@ library V1Proposing { .movingAverage({ maValue: state.avgBlockTime, newValue: meta.timestamp - state.lastProposedAt, - maf: LibConstants.K_BLOCK_TIME_MAF + maf: config.blockTimeMAF }) .toUint64(); @@ -141,43 +148,47 @@ library V1Proposing { } function getBlockFee( - LibData.State storage state + LibData.State storage state, + LibData.Config memory config ) public view returns (uint256 newFeeBase, uint256 fee, uint256 deposit) { (newFeeBase, ) = V1Utils.getTimeAdjustedFee({ state: state, + config: config, isProposal: true, tNow: uint64(block.timestamp), tLast: state.lastProposedAt, - tAvg: state.avgBlockTime, - tCap: LibConstants.K_BLOCK_TIME_CAP + tAvg: state.avgBlockTime }); fee = V1Utils.getSlotsAdjustedFee({ state: state, + config: config, isProposal: true, feeBase: newFeeBase }); - fee = V1Utils.getBootstrapDiscountedFee(state, fee); - deposit = (fee * LibConstants.K_PROPOSER_DEPOSIT_PCTG) / 100; + fee = V1Utils.getBootstrapDiscountedFee(state, config, fee); + deposit = (fee * config.proposerDepositPctg) / 100; } function isCommitValid( LibData.State storage state, + uint256 commitConfirmations, uint256 commitSlot, uint256 commitHeight, bytes32 commitHash ) public view returns (bool) { - assert(LibConstants.K_COMMIT_DELAY_CONFIRMS > 0); + assert(commitConfirmations > 0); bytes32 hash = _aggregateCommitHash(commitHeight, commitHash); return state.commits[msg.sender][commitSlot] == hash && - block.number >= commitHeight + LibConstants.K_COMMIT_DELAY_CONFIRMS; + block.number >= commitHeight + commitConfirmations; } function _verifyBlockCommit( LibData.State storage state, + uint256 commitConfirmations, LibData.BlockMetadata memory meta ) private { - if (LibConstants.K_COMMIT_DELAY_CONFIRMS == 0) { + if (commitConfirmations == 0) { return; } bytes32 commitHash = _calculateCommitHash( @@ -188,6 +199,7 @@ library V1Proposing { require( isCommitValid({ state: state, + commitConfirmations: commitConfirmations, commitSlot: meta.commitSlot, commitHeight: meta.commitHeight, commitHash: commitHash @@ -202,7 +214,10 @@ library V1Proposing { } } - function _validateMetadata(LibData.BlockMetadata memory meta) private pure { + function _validateMetadata( + LibData.Config memory config, + LibData.BlockMetadata memory meta + ) private pure { require( meta.id == 0 && meta.l1Height == 0 && @@ -214,10 +229,7 @@ library V1Proposing { "L1:placeholder" ); - require( - meta.gasLimit <= LibConstants.K_BLOCK_MAX_GAS_LIMIT, - "L1:gasLimit" - ); + require(meta.gasLimit <= config.blockMaxGasLimit, "L1:gasLimit"); require(meta.extraData.length <= 32, "L1:extraData"); } diff --git a/packages/protocol/contracts/L1/v1/V1Proving.sol b/packages/protocol/contracts/L1/v1/V1Proving.sol index 0c0158e4e5f..93158998dd9 100644 --- a/packages/protocol/contracts/L1/v1/V1Proving.sol +++ b/packages/protocol/contracts/L1/v1/V1Proving.sol @@ -12,7 +12,6 @@ import "../../common/AddressResolver.sol"; import "../../common/ConfigManager.sol"; import "../../libs/LibAnchorSignature.sol"; import "../../libs/LibBlockHeader.sol"; -import "../../libs/LibConstants.sol"; import "../../libs/LibReceiptDecoder.sol"; import "../../libs/LibTxDecoder.sol"; import "../../libs/LibTxUtils.sol"; @@ -26,15 +25,22 @@ import "./V1Utils.sol"; /// @author david library V1Proving { using LibBlockHeader for BlockHeader; - using LibData for LibData.State; + using V1Utils for LibData.BlockMetadata; + using V1Utils for LibData.State; struct Evidence { LibData.BlockMetadata meta; BlockHeader header; address prover; - bytes[] proofs; // The first K_ZKPROOFS_PER_BLOCK are ZKPs + bytes[] proofs; // The first zkProofsPerBlock are ZKPs } + bytes32 public constant INVALIDATE_BLOCK_LOG_TOPIC = + keccak256("BlockInvalidated(bytes32)"); + + bytes4 public constant ANCHOR_TX_SELECTOR = + bytes4(keccak256("anchor(uint256,bytes32)")); + event BlockProven( uint256 indexed id, bytes32 parentHash, @@ -54,6 +60,7 @@ library V1Proving { function proveBlock( LibData.State storage state, LibData.TentativeState storage tentative, + LibData.Config memory config, AddressResolver resolver, uint256 blockId, bytes[] calldata inputs @@ -68,69 +75,79 @@ library V1Proving { // Check evidence require(evidence.meta.id == blockId, "L1:id"); + + uint256 zkProofsPerBlock = config.zkProofsPerBlock; require( - evidence.proofs.length == 2 + LibConstants.K_ZKPROOFS_PER_BLOCK, + evidence.proofs.length == 2 + zkProofsPerBlock, "L1:proof:size" ); - // Check anchor tx is valid - LibTxDecoder.Tx memory _tx = LibTxDecoder.decodeTx(anchorTx); - require(_tx.txType == 0, "L1:anchor:type"); - require( - _tx.destination == - resolver.resolve(LibConstants.K_CHAIN_ID, "taiko"), - "L1:anchor:dest" - ); - require( - _tx.gasLimit == LibConstants.K_ANCHOR_TX_GAS_LIMIT, - "L1:anchor:gasLimit" - ); + { + // Check anchor tx is valid + LibTxDecoder.Tx memory _tx = LibTxDecoder.decodeTx( + config.chainId, + anchorTx + ); + require(_tx.txType == 0, "L1:anchor:type"); + require( + _tx.destination == resolver.resolve(config.chainId, "taiko"), + "L1:anchor:dest" + ); + require( + _tx.gasLimit == config.anchorTxGasLimit, + "L1:anchor:gasLimit" + ); - // Check anchor tx's signature is valid and deterministic - _validateAnchorTxSignature(_tx); + // Check anchor tx's signature is valid and deterministic + _validateAnchorTxSignature(config.chainId, _tx); - // Check anchor tx's calldata is valid - require( - LibBytesUtils.equal( - _tx.data, - bytes.concat( - LibConstants.K_ANCHOR_TX_SELECTOR, - bytes32(evidence.meta.l1Height), - evidence.meta.l1Hash - ) - ), - "L1:anchor:calldata" - ); + // Check anchor tx's calldata is valid + require( + LibBytesUtils.equal( + _tx.data, + bytes.concat( + ANCHOR_TX_SELECTOR, + bytes32(evidence.meta.l1Height), + evidence.meta.l1Hash + ) + ), + "L1:anchor:calldata" + ); + } - // Check anchor tx is the 1st tx in the block - require( - LibMerkleTrie.verifyInclusionProof({ - _key: LibRLPWriter.writeUint(0), - _value: anchorTx, - _proof: evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK], - _root: evidence.header.transactionsRoot - }), - "L1:tx:proof" - ); + if (!config.skipProofValidation) { + // Check anchor tx is the 1st tx in the block + require( + LibMerkleTrie.verifyInclusionProof({ + _key: LibRLPWriter.writeUint(0), + _value: anchorTx, + _proof: evidence.proofs[zkProofsPerBlock], + _root: evidence.header.transactionsRoot + }), + "L1:tx:proof" + ); - // Check anchor tx does not throw - LibReceiptDecoder.Receipt memory receipt = LibReceiptDecoder - .decodeReceipt(anchorReceipt); + // Check anchor tx does not throw - require(receipt.status == 1, "L1:receipt:status"); - require( - LibMerkleTrie.verifyInclusionProof({ - _key: LibRLPWriter.writeUint(0), - _value: anchorReceipt, - _proof: evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK + 1], - _root: evidence.header.receiptsRoot - }), - "L1:receipt:proof" - ); + LibReceiptDecoder.Receipt memory receipt = LibReceiptDecoder + .decodeReceipt(anchorReceipt); + + require(receipt.status == 1, "L1:receipt:status"); + require( + LibMerkleTrie.verifyInclusionProof({ + _key: LibRLPWriter.writeUint(0), + _value: anchorReceipt, + _proof: evidence.proofs[zkProofsPerBlock + 1], + _root: evidence.header.receiptsRoot + }), + "L1:receipt:proof" + ); + } // ZK-prove block and mark block proven to be valid. _proveBlock({ state: state, + config: config, resolver: resolver, evidence: evidence, target: evidence.meta, @@ -141,6 +158,7 @@ library V1Proving { function proveBlockInvalid( LibData.State storage state, LibData.TentativeState storage tentative, + LibData.Config memory config, AddressResolver resolver, uint256 blockId, bytes[] calldata inputs @@ -159,54 +177,58 @@ library V1Proving { // Check evidence require(evidence.meta.id == blockId, "L1:id"); require( - evidence.proofs.length == 1 + LibConstants.K_ZKPROOFS_PER_BLOCK, + evidence.proofs.length == 1 + config.zkProofsPerBlock, "L1:proof:size" ); - // Check the 1st receipt is for an InvalidateBlock tx with - // a BlockInvalidated event - LibReceiptDecoder.Receipt memory receipt = LibReceiptDecoder - .decodeReceipt(invalidateBlockReceipt); - require(receipt.status == 1, "L1:receipt:status"); - require(receipt.logs.length == 1, "L1:receipt:logsize"); + if (!config.skipProofValidation) { + // Check the 1st receipt is for an InvalidateBlock tx with + // a BlockInvalidated event + LibReceiptDecoder.Receipt memory receipt = LibReceiptDecoder + .decodeReceipt(invalidateBlockReceipt); + require(receipt.status == 1, "L1:receipt:status"); + require(receipt.logs.length == 1, "L1:receipt:logsize"); - LibReceiptDecoder.Log memory log = receipt.logs[0]; - require( - log.contractAddress == - resolver.resolve(LibConstants.K_CHAIN_ID, "taiko"), - "L1:receipt:addr" - ); - require(log.data.length == 0, "L1:receipt:data"); - require( - log.topics.length == 2 && - log.topics[0] == LibConstants.K_INVALIDATE_BLOCK_LOG_TOPIC && - log.topics[1] == target.txListHash, - "L1:receipt:topics" - ); + LibReceiptDecoder.Log memory log = receipt.logs[0]; + require( + log.contractAddress == + resolver.resolve(config.chainId, "taiko"), + "L1:receipt:addr" + ); + require(log.data.length == 0, "L1:receipt:data"); + require( + log.topics.length == 2 && + log.topics[0] == INVALIDATE_BLOCK_LOG_TOPIC && + log.topics[1] == target.txListHash, + "L1:receipt:topics" + ); - // Check the event is the first one in the throw-away block - require( - LibMerkleTrie.verifyInclusionProof({ - _key: LibRLPWriter.writeUint(0), - _value: invalidateBlockReceipt, - _proof: evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK], - _root: evidence.header.receiptsRoot - }), - "L1:receipt:proof" - ); + // Check the event is the first one in the throw-away block + require( + LibMerkleTrie.verifyInclusionProof({ + _key: LibRLPWriter.writeUint(0), + _value: invalidateBlockReceipt, + _proof: evidence.proofs[config.zkProofsPerBlock], + _root: evidence.header.receiptsRoot + }), + "L1:receipt:proof" + ); + } // ZK-prove block and mark block proven as invalid. _proveBlock({ state: state, + config: config, resolver: resolver, evidence: evidence, target: target, - blockHashOverride: LibConstants.K_BLOCK_DEADEND_HASH + blockHashOverride: V1Utils.BLOCK_DEADEND_HASH }); } function _proveBlock( LibData.State storage state, + LibData.Config memory config, AddressResolver resolver, Evidence memory evidence, LibData.BlockMetadata memory target, @@ -215,25 +237,32 @@ library V1Proving { require(evidence.meta.id == target.id, "L1:height"); require(evidence.prover != address(0), "L1:prover"); - _checkMetadata(state, target); - _validateHeaderForMetadata(evidence.header, evidence.meta); + _checkMetadata({state: state, config: config, meta: target}); + _validateHeaderForMetadata({ + config: config, + header: evidence.header, + meta: evidence.meta + }); bytes32 blockHash = evidence.header.hashBlockHeader(); - for (uint256 i = 0; i < LibConstants.K_ZKPROOFS_PER_BLOCK; i++) { - LibZKP.verify({ - verificationKey: ConfigManager( - resolver.resolve("config_manager") - ).getValue(string(abi.encodePacked("zk_vkey_", i))), - zkproof: evidence.proofs[i], - blockHash: blockHash, - prover: evidence.prover, - txListHash: evidence.meta.txListHash - }); + for (uint256 i = 0; i < config.zkProofsPerBlock; i++) { + if (!config.skipProofValidation) { + LibZKP.verify({ + verificationKey: ConfigManager( + resolver.resolve("config_manager") + ).getValue(string(abi.encodePacked("zk_vkey_", i))), + zkproof: evidence.proofs[i], + blockHash: blockHash, + prover: evidence.prover, + txListHash: evidence.meta.txListHash + }); + } } _markBlockProven({ state: state, + config: config, prover: evidence.prover, target: target, parentHash: evidence.header.parentHash, @@ -243,6 +272,7 @@ library V1Proving { function _markBlockProven( LibData.State storage state, + LibData.Config memory config, address prover, LibData.BlockMetadata memory target, bytes32 parentHash, @@ -264,13 +294,18 @@ library V1Proving { } require( - fc.provers.length < LibConstants.K_MAX_PROOFS_PER_FORK_CHOICE, + fc.provers.length < config.maxProofsPerForkChoice, "L1:proof:tooMany" ); require( block.timestamp < - V1Utils.uncleProofDeadline(state, fc, target.id), + V1Utils.uncleProofDeadline({ + state: state, + config: config, + fc: fc, + blockId: target.id + }), "L1:tooLate" ); @@ -292,6 +327,7 @@ library V1Proving { } function _validateAnchorTxSignature( + uint256 chainId, LibTxDecoder.Tx memory _tx ) private view { require( @@ -301,7 +337,7 @@ library V1Proving { if (_tx.r == LibAnchorSignature.GX2) { (, , uint256 s) = LibAnchorSignature.signTransaction( - LibTxUtils.hashUnsignedTx(_tx), + LibTxUtils.hashUnsignedTx(chainId, _tx), 1 ); require(s == 0, "L1:sig:s"); @@ -310,6 +346,7 @@ library V1Proving { function _checkMetadata( LibData.State storage state, + LibData.Config memory config, LibData.BlockMetadata memory meta ) private view { require( @@ -317,13 +354,14 @@ library V1Proving { "L1:meta:id" ); require( - LibData.getProposedBlock(state, meta.id).metaHash == - LibData.hashMetadata(meta), + state.getProposedBlock(config.maxNumBlocks, meta.id).metaHash == + meta.hashMetadata(), "L1:metaHash" ); } function _validateHeaderForMetadata( + LibData.Config memory config, BlockHeader memory header, LibData.BlockMetadata memory meta ) private pure { @@ -331,8 +369,7 @@ library V1Proving { header.parentHash != 0 && header.beneficiary == meta.beneficiary && header.difficulty == 0 && - header.gasLimit == - meta.gasLimit + LibConstants.K_ANCHOR_TX_GAS_LIMIT && + header.gasLimit == meta.gasLimit + config.anchorTxGasLimit && header.gasUsed > 0 && header.timestamp == meta.timestamp && header.extraData.length == meta.extraData.length && diff --git a/packages/protocol/contracts/L1/v1/V1Utils.sol b/packages/protocol/contracts/L1/v1/V1Utils.sol index a991797bbe7..80e88dc6bd0 100644 --- a/packages/protocol/contracts/L1/v1/V1Utils.sol +++ b/packages/protocol/contracts/L1/v1/V1Utils.sol @@ -19,11 +19,22 @@ library V1Utils { uint64 public constant MASK_HALT = 1 << 0; + bytes32 public constant BLOCK_DEADEND_HASH = bytes32(uint256(1)); + event WhitelistingEnabled(bool whitelistProposers, bool whitelistProvers); event ProposerWhitelisted(address indexed proposer, bool whitelisted); event ProverWhitelisted(address indexed prover, bool whitelisted); event Halted(bool halted); + function saveProposedBlock( + LibData.State storage state, + uint256 maxNumBlocks, + uint256 id, + LibData.ProposedBlock memory blk + ) internal { + state.proposedBlocks[id % maxNumBlocks] = blk; + } + function enableWhitelisting( LibData.TentativeState storage tentative, bool whitelistProposers, @@ -71,6 +82,54 @@ library V1Utils { emit Halted(toHalt); } + function getProposedBlock( + LibData.State storage state, + uint256 maxNumBlocks, + uint256 id + ) internal view returns (LibData.ProposedBlock storage) { + return state.proposedBlocks[id % maxNumBlocks]; + } + + function getL2BlockHash( + LibData.State storage state, + uint256 number + ) internal view returns (bytes32) { + require(number <= state.latestVerifiedHeight, "L1:id"); + return state.l2Hashes[number]; + } + + function getStateVariables( + LibData.State storage state + ) + internal + view + returns ( + uint64 genesisHeight, + uint64 genesisTimestamp, + uint64 statusBits, + uint256 feeBase, + uint64 nextBlockId, + uint64 lastProposedAt, + uint64 avgBlockTime, + uint64 latestVerifiedHeight, + uint64 latestVerifiedId, + uint64 avgProofTime + ) + { + genesisHeight = state.genesisHeight; + genesisTimestamp = state.genesisTimestamp; + statusBits = state.statusBits; + + feeBase = state.feeBase; + nextBlockId = state.nextBlockId; + lastProposedAt = state.lastProposedAt; + avgBlockTime = state.avgBlockTime; + + latestVerifiedHeight = state.latestVerifiedHeight; + latestVerifiedId = state.latestVerifiedId; + avgProofTime = state.avgProofTime; + } + function isHalted( LibData.State storage state ) internal view returns (bool) { @@ -96,25 +155,26 @@ library V1Utils { // Implement "Incentive Multipliers", see the whitepaper. function getTimeAdjustedFee( LibData.State storage state, + LibData.Config memory config, bool isProposal, uint64 tNow, uint64 tLast, - uint64 tAvg, - uint64 tCap + uint64 tAvg ) internal view returns (uint256 newFeeBase, uint256 tRelBp) { if (tAvg == 0) { newFeeBase = state.feeBase; tRelBp = 0; } else { - uint256 _tAvg = tAvg > tCap ? tCap : tAvg; - uint256 tGrace = (LibConstants.K_FEE_GRACE_PERIOD_PCTG * _tAvg) / - 100; - uint256 tMax = (LibConstants.K_FEE_MAX_PERIOD_PCTG * _tAvg) / 100; + uint256 _tAvg = tAvg > config.proofTimeCap + ? config.proofTimeCap + : tAvg; + uint256 tGrace = (config.feeGracePeriodPctg * _tAvg) / 100; + uint256 tMax = (config.feeMaxPeriodPctg * _tAvg) / 100; uint256 a = tLast + tGrace; uint256 b = tNow > a ? tNow - a : 0; tRelBp = (b.min(tMax) * 10000) / tMax; // [0 - 10000] uint256 alpha = 10000 + - ((LibConstants.K_REWARD_MULTIPLIER_PCTG - 100) * tRelBp) / + ((config.rewardMultiplierPctg - 100) * tRelBp) / 100; if (isProposal) { newFeeBase = (state.feeBase * 10000) / alpha; // fee @@ -127,13 +187,12 @@ library V1Utils { // Implement "Slot-availability Multipliers", see the whitepaper. function getSlotsAdjustedFee( LibData.State storage state, + LibData.Config memory config, bool isProposal, uint256 feeBase ) internal view returns (uint256) { // m is the `n'` in the whitepaper - uint256 m = LibConstants.K_MAX_NUM_BLOCKS - - 1 + - LibConstants.K_FEE_PREMIUM_LAMDA; + uint256 m = config.maxNumBlocks - 1 + config.feePremiumLamda; // n is the number of unverified blocks uint256 n = state.nextBlockId - state.latestVerifiedId - 1; // k is `m − n + 1` or `m − n - 1`in the whitepaper @@ -144,10 +203,11 @@ library V1Utils { // Implement "Bootstrap Discount Multipliers", see the whitepaper. function getBootstrapDiscountedFee( LibData.State storage state, + LibData.Config memory config, uint256 feeBase ) internal view returns (uint256) { uint256 halves = uint256(block.timestamp - state.genesisTimestamp) / - LibConstants.K_HALVING; + config.boostrapDiscountHalvingPeriod; uint256 gamma = 1024 - (1024 >> halves); return (feeBase * gamma) / 1024; } @@ -155,16 +215,23 @@ library V1Utils { // Returns a deterministic deadline for uncle proof submission. function uncleProofDeadline( LibData.State storage state, + LibData.Config memory config, LibData.ForkChoice storage fc, uint256 blockId ) internal view returns (uint64) { - if (blockId <= 2 * LibConstants.K_MAX_NUM_BLOCKS) { - return fc.provenAt + LibConstants.K_INITIAL_UNCLE_DELAY; + if (blockId <= 2 * config.maxNumBlocks) { + return fc.provenAt + config.initialUncleDelay; } else { return fc.provenAt + state.avgProofTime; } } + function hashMetadata( + LibData.BlockMetadata memory meta + ) internal pure returns (bytes32) { + return keccak256(abi.encode(meta)); + } + function movingAverage( uint256 maValue, uint256 newValue, diff --git a/packages/protocol/contracts/L1/v1/V1Verifying.sol b/packages/protocol/contracts/L1/v1/V1Verifying.sol index 0450027bfa2..8242dc5faed 100644 --- a/packages/protocol/contracts/L1/v1/V1Verifying.sol +++ b/packages/protocol/contracts/L1/v1/V1Verifying.sol @@ -15,6 +15,8 @@ import "./V1Utils.sol"; /// @author dantaik library V1Verifying { using SafeCastUpgradeable for uint256; + using V1Utils for LibData.State; + event BlockVerified(uint256 indexed id, bytes32 blockHash); event HeaderSynced( @@ -43,6 +45,7 @@ library V1Verifying { function verifyBlocks( LibData.State storage state, + LibData.Config memory config, AddressResolver resolver, uint256 maxBlocks, bool checkHalt @@ -58,7 +61,6 @@ library V1Verifying { uint64 latestL2Height = state.latestVerifiedHeight; bytes32 latestL2Hash = state.l2Hashes[latestL2Height]; uint64 processed = 0; - TkoToken tkoToken; for ( uint256 i = state.latestVerifiedId + 1; @@ -66,54 +68,31 @@ library V1Verifying { i++ ) { LibData.ForkChoice storage fc = state.forkChoices[i][latestL2Hash]; - LibData.ProposedBlock storage target = LibData.getProposedBlock( - state, + LibData.ProposedBlock storage target = state.getProposedBlock( + config.maxNumBlocks, i ); // Uncle proof can not take more than 2x time the first proof did. - if (!_isVerifiable(state, fc, i)) { + if ( + !_isVerifiable({ + state: state, + config: config, + fc: fc, + blockId: i + }) + ) { break; } else { - if (LibConstants.K_ENABLE_TOKENOMICS) { - uint256 newFeeBase; - { - uint256 reward; - uint256 tRelBp; // [0-10000], see the whitepaper - (newFeeBase, reward, tRelBp) = getProofReward({ - state: state, - provenAt: fc.provenAt, - proposedAt: target.proposedAt - }); - - if (address(tkoToken) == address(0)) { - tkoToken = TkoToken(resolver.resolve("tko_token")); - } - - _rewardProvers(fc, reward, tkoToken); - _refundProposerDeposit(target, tRelBp, tkoToken); - } - // Update feeBase and avgProofTime - state.feeBase = V1Utils.movingAverage({ - maValue: state.feeBase, - newValue: newFeeBase, - maf: LibConstants.K_FEE_BASE_MAF - }); - } - - if (fc.blockHash != LibConstants.K_BLOCK_DEADEND_HASH) { - latestL2Height += 1; - latestL2Hash = fc.blockHash; - } - - state.avgProofTime = V1Utils - .movingAverage({ - maValue: state.avgProofTime, - newValue: fc.provenAt - target.proposedAt, - maf: LibConstants.K_PROOF_TIME_MAF - }) - .toUint64(); - + (latestL2Height, latestL2Hash) = _verifyBlock({ + state: state, + config: config, + resolver: resolver, + fc: fc, + target: target, + latestL2Height: latestL2Height, + latestL2Hash: latestL2Hash + }); processed += 1; emit BlockVerified(i, fc.blockHash); _cleanUp(fc); @@ -125,7 +104,12 @@ library V1Verifying { if (latestL2Height > state.latestVerifiedHeight) { state.latestVerifiedHeight = latestL2Height; - state.l2Hashes[latestL2Height] = latestL2Hash; + + // Note that not all L2 hashes are stored on L1, only the last + // verified one in a batch. + state.l2Hashes[ + latestL2Height % config.blockHashHistory + ] = latestL2Hash; emit HeaderSynced(block.number, latestL2Height, latestL2Hash); } } @@ -133,23 +117,25 @@ library V1Verifying { function getProofReward( LibData.State storage state, + LibData.Config memory config, uint64 provenAt, uint64 proposedAt ) public view returns (uint256 newFeeBase, uint256 reward, uint256 tRelBp) { (newFeeBase, tRelBp) = V1Utils.getTimeAdjustedFee({ state: state, + config: config, isProposal: false, tNow: provenAt, tLast: proposedAt, - tAvg: state.avgProofTime, - tCap: LibConstants.K_PROOF_TIME_CAP + tAvg: state.avgProofTime }); reward = V1Utils.getSlotsAdjustedFee({ state: state, + config: config, isProposal: false, feeBase: newFeeBase }); - reward = (reward * (10000 - LibConstants.K_REWARD_BURN_BP)) / 10000; + reward = (reward * (10000 - config.rewardBurnBips)) / 10000; } function _refundProposerDeposit( @@ -181,6 +167,57 @@ library V1Verifying { } } + function _verifyBlock( + LibData.State storage state, + LibData.Config memory config, + AddressResolver resolver, + LibData.ForkChoice storage fc, + LibData.ProposedBlock storage target, + uint64 latestL2Height, + bytes32 latestL2Hash + ) private returns (uint64 _latestL2Height, bytes32 _latestL2Hash) { + if (config.enableTokenomics) { + uint256 newFeeBase; + { + uint256 reward; + uint256 tRelBp; // [0-10000], see the whitepaper + (newFeeBase, reward, tRelBp) = getProofReward({ + state: state, + config: config, + provenAt: fc.provenAt, + proposedAt: target.proposedAt + }); + + TkoToken tkoToken = TkoToken(resolver.resolve("tko_token")); + + _rewardProvers(fc, reward, tkoToken); + _refundProposerDeposit(target, tRelBp, tkoToken); + } + // Update feeBase and avgProofTime + state.feeBase = V1Utils.movingAverage({ + maValue: state.feeBase, + newValue: newFeeBase, + maf: config.feeBaseMAF + }); + } + + state.avgProofTime = V1Utils + .movingAverage({ + maValue: state.avgProofTime, + newValue: fc.provenAt - target.proposedAt, + maf: config.proofTimeMAF + }) + .toUint64(); + + if (fc.blockHash != V1Utils.BLOCK_DEADEND_HASH) { + _latestL2Height = latestL2Height + 1; + _latestL2Hash = fc.blockHash; + } else { + _latestL2Height = latestL2Height; + _latestL2Hash = latestL2Hash; + } + } + function _cleanUp(LibData.ForkChoice storage fc) private { fc.blockHash = 0; fc.provenAt = 0; @@ -192,11 +229,18 @@ library V1Verifying { function _isVerifiable( LibData.State storage state, + LibData.Config memory config, LibData.ForkChoice storage fc, uint256 blockId ) private view returns (bool) { return fc.blockHash != 0 && - block.timestamp > V1Utils.uncleProofDeadline(state, fc, blockId); + block.timestamp > + V1Utils.uncleProofDeadline({ + state: state, + config: config, + fc: fc, + blockId: blockId + }); } } diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 1daa811e8bf..655a9ef8c6e 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -16,7 +16,7 @@ import "../common/AddressResolver.sol"; import "../common/IHeaderSync.sol"; import "../libs/LibAnchorSignature.sol"; import "../libs/LibInvalidTxList.sol"; -import "../libs/LibConstants.sol"; +import "../libs/LibSharedConfig.sol"; import "../libs/LibTxDecoder.sol"; /// @author dantaik @@ -104,6 +104,7 @@ contract TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { require(tx.gasprice == 0, "L2:gasPrice"); LibInvalidTxList.Reason reason = LibInvalidTxList.isTxListInvalid({ + config: getConfig(), encoded: txList, hint: hint, txIdx: txIdx @@ -119,6 +120,16 @@ contract TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { * Public Functions * **********************/ + function getConfig() + public + view + virtual + returns (LibData.Config memory config) + { + config = LibSharedConfig.getConfig(); + config.chainId = block.chainid; + } + function getSyncedHeader( uint256 number ) public view override returns (bytes32) { @@ -143,41 +154,6 @@ contract TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { * Private Functions * **********************/ - // NOTE: If the order of the return values of this function changes, then - // some test cases that using this function in generate_genesis.test.ts - // may also needs to be modified accordingly. - function getConstants() - public - pure - returns ( - uint256, // K_ZKPROOFS_PER_BLOCK - uint256, // K_CHAIN_ID - uint256, // K_MAX_NUM_BLOCKS - uint256, // K_MAX_VERIFICATIONS_PER_TX - uint256, // K_COMMIT_DELAY_CONFIRMS - uint256, // K_MAX_PROOFS_PER_FORK_CHOICE - uint256, // K_BLOCK_MAX_GAS_LIMIT - uint256, // K_BLOCK_MAX_TXS - uint256, // K_TXLIST_MAX_BYTES - uint256, // K_TX_MIN_GAS_LIMIT - uint256 // K_ANCHOR_TX_GAS_LIMIT - ) - { - return ( - LibConstants.K_ZKPROOFS_PER_BLOCK, - LibConstants.K_CHAIN_ID, - LibConstants.K_MAX_NUM_BLOCKS, - LibConstants.K_MAX_VERIFICATIONS_PER_TX, - LibConstants.K_COMMIT_DELAY_CONFIRMS, - LibConstants.K_MAX_PROOFS_PER_FORK_CHOICE, - LibConstants.K_BLOCK_MAX_GAS_LIMIT, - LibConstants.K_BLOCK_MAX_TXS, - LibConstants.K_TXLIST_MAX_BYTES, - LibConstants.K_TX_MIN_GAS_LIMIT, - LibConstants.K_ANCHOR_TX_GAS_LIMIT - ); - } - function _checkPublicInputs() private { // Check the latest 256 block hashes (excluding the parent hash). bytes32[255] memory ancestors; diff --git a/packages/protocol/contracts/libs/LibBlockHeader.sol b/packages/protocol/contracts/libs/LibBlockHeader.sol index a312d314848..e05ebdf8a76 100644 --- a/packages/protocol/contracts/libs/LibBlockHeader.sol +++ b/packages/protocol/contracts/libs/LibBlockHeader.sol @@ -9,7 +9,6 @@ pragma solidity ^0.8.9; import "../thirdparty/LibRLPWriter.sol"; -import "./LibConstants.sol"; /// @author david struct BlockHeader { @@ -73,12 +72,13 @@ library LibBlockHeader { } function isPartiallyValidForTaiko( + uint256 blockMaxGasLimit, BlockHeader calldata header ) internal pure returns (bool) { return header.parentHash != 0 && header.ommersHash == EMPTY_OMMERS_HASH && - header.gasLimit <= LibConstants.K_BLOCK_MAX_GAS_LIMIT && + header.gasLimit <= blockMaxGasLimit && header.extraData.length <= 32 && header.difficulty == 0 && header.nonce == 0; diff --git a/packages/protocol/contracts/libs/LibConstants.sol b/packages/protocol/contracts/libs/LibConstants.sol deleted file mode 100644 index c0d2903b982..00000000000 --- a/packages/protocol/contracts/libs/LibConstants.sol +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ -// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ -// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ -// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ -// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ -// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ -pragma solidity ^0.8.9; - -/// @author dantaik -library LibConstants { - // https://github.com/ethereum-lists/chains/pull/1611 - uint256 public constant K_CHAIN_ID = 167; - // up to 2048 pending blocks - uint256 public constant K_MAX_NUM_BLOCKS = 2049; - // This number is calculated from K_MAX_NUM_BLOCKS to make - // the 'the maximum value of the multiplier' close to 20.0 - uint256 public constant K_ZKPROOFS_PER_BLOCK = 1; - uint256 public constant K_MAX_VERIFICATIONS_PER_TX = 20; - uint256 public constant K_COMMIT_DELAY_CONFIRMS = 0; - uint256 public constant K_MAX_PROOFS_PER_FORK_CHOICE = 5; - uint256 public constant K_BLOCK_MAX_GAS_LIMIT = 5000000; // TODO - uint256 public constant K_BLOCK_MAX_TXS = 20; // TODO - uint256 public constant K_TXLIST_MAX_BYTES = 10240; // TODO - uint256 public constant K_TX_MIN_GAS_LIMIT = 21000; // TODO - uint256 public constant K_ANCHOR_TX_GAS_LIMIT = 250000; - uint256 public constant K_FEE_PREMIUM_LAMDA = 590; - uint256 public constant K_REWARD_BURN_BP = 100; // 100 basis points or 1% - uint256 public constant K_PROPOSER_DEPOSIT_PCTG = 25; // 25% - - // Moving average factors - uint256 public constant K_FEE_BASE_MAF = 1024; - uint256 public constant K_BLOCK_TIME_MAF = 1024; - uint256 public constant K_PROOF_TIME_MAF = 1024; - - uint64 public constant K_REWARD_MULTIPLIER_PCTG = 400; // 400% - uint64 public constant K_FEE_GRACE_PERIOD_PCTG = 125; // 125% - uint64 public constant K_FEE_MAX_PERIOD_PCTG = 375; // 375% - uint64 public constant K_BLOCK_TIME_CAP = 48 seconds; - uint64 public constant K_PROOF_TIME_CAP = 60 minutes; - uint64 public constant K_HALVING = 180 days; - uint64 public constant K_INITIAL_UNCLE_DELAY = 60 minutes; - - bytes32 public constant K_BLOCK_DEADEND_HASH = bytes32(uint256(1)); - bytes32 public constant K_INVALIDATE_BLOCK_LOG_TOPIC = - keccak256("BlockInvalidated(bytes32)"); - - bytes4 public constant K_ANCHOR_TX_SELECTOR = - bytes4(keccak256("anchor(uint256,bytes32)")); - - bool public constant K_ENABLE_TOKENOMICS = false; -} diff --git a/packages/protocol/contracts/libs/LibInvalidTxList.sol b/packages/protocol/contracts/libs/LibInvalidTxList.sol index 40d31051297..8be7ce079b5 100644 --- a/packages/protocol/contracts/libs/LibInvalidTxList.sol +++ b/packages/protocol/contracts/libs/LibInvalidTxList.sol @@ -8,7 +8,7 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; -import "../libs/LibConstants.sol"; +import "../L1/LibData.sol"; import "../libs/LibTxDecoder.sol"; import "../libs/LibTxUtils.sol"; import "../thirdparty/LibRLPReader.sol"; @@ -18,18 +18,19 @@ import "../thirdparty/LibRLPWriter.sol"; * A library to invalidate a txList using the following rules: * * A txList is valid if and only if: - * 1. The txList's length is no more than `K_TXLIST_MAX_BYTES`. + * 1. The txList's length is no more than `maxBytesPerTxList`. * 2. The txList is well-formed RLP, with no additional trailing bytes. - * 3. The total number of transactions is no more than `K_BLOCK_MAX_TXS`. + * 3. The total number of transactions is no more than + * `maxTransactionsPerBlock`. * 4. The sum of all transaction gas limit is no more than - * `K_BLOCK_MAX_GAS_LIMIT`. + * `blockMaxGasLimit`. * * A transaction is valid if and only if: * 1. The transaction is well-formed RLP, with no additional trailing bytes * (rule #1 in Ethereum yellow paper). * 2. The transaction's signature is valid (rule #2 in Ethereum yellow paper). * 3. The transaction's the gas limit is no smaller than the intrinsic gas - * `K_TX_MIN_GAS_LIMIT` (rule #5 in Ethereum yellow paper). + * `minTxGasLimit` (rule #5 in Ethereum yellow paper). * * @title LibInvalidTxList * @author david @@ -49,25 +50,23 @@ library LibInvalidTxList { } function isTxListInvalid( + LibData.Config memory config, bytes calldata encoded, Reason hint, uint256 txIdx ) internal pure returns (Reason) { - if (encoded.length > LibConstants.K_TXLIST_MAX_BYTES) { + if (encoded.length > config.maxBytesPerTxList) { return Reason.BINARY_TOO_LARGE; } - try LibTxDecoder.decodeTxList(encoded) returns ( + try LibTxDecoder.decodeTxList(config.chainId, encoded) returns ( LibTxDecoder.TxList memory txList ) { - if (txList.items.length > LibConstants.K_BLOCK_MAX_TXS) { + if (txList.items.length > config.maxTransactionsPerBlock) { return Reason.BLOCK_TOO_MANY_TXS; } - if ( - LibTxDecoder.sumGasLimit(txList) > - LibConstants.K_BLOCK_MAX_GAS_LIMIT - ) { + if (LibTxDecoder.sumGasLimit(txList) > config.blockMaxGasLimit) { return Reason.BLOCK_GAS_LIMIT_TOO_LARGE; } @@ -76,17 +75,14 @@ library LibInvalidTxList { if (hint == Reason.TX_INVALID_SIG) { require( - LibTxUtils.recoverSender(_tx) == address(0), + LibTxUtils.recoverSender(config.chainId, _tx) == address(0), "bad hint TX_INVALID_SIG" ); return Reason.TX_INVALID_SIG; } if (hint == Reason.TX_GAS_LIMIT_TOO_SMALL) { - require( - _tx.gasLimit >= LibConstants.K_TX_MIN_GAS_LIMIT, - "bad hint" - ); + require(_tx.gasLimit >= config.minTxGasLimit, "bad hint"); return Reason.TX_GAS_LIMIT_TOO_SMALL; } diff --git a/packages/protocol/contracts/libs/LibSharedConfig.sol b/packages/protocol/contracts/libs/LibSharedConfig.sol new file mode 100644 index 00000000000..95cc56588fe --- /dev/null +++ b/packages/protocol/contracts/libs/LibSharedConfig.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +// +// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ +// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ +// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ +// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ +// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ +// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ +pragma solidity ^0.8.9; + +import "../L1/LibData.sol"; + +library LibSharedConfig { + /// Returns shared configs for both TaikoL1 and TaikoL2 for production. + function getConfig() internal pure returns (LibData.Config memory) { + return + LibData.Config({ + chainId: 167, + maxNumBlocks: 2049, // up to 2048 pending blocks + blockHashHistory: 100000, + // This number is calculated from maxNumBlocks to make + // the 'the maximum value of the multiplier' close to 20.0 + zkProofsPerBlock: 1, + maxVerificationsPerTx: 20, + commitConfirmations: 0, + maxProofsPerForkChoice: 5, + blockMaxGasLimit: 5000000, // TODO + maxTransactionsPerBlock: 20, // TODO + maxBytesPerTxList: 10240, // TODO + minTxGasLimit: 21000, // TODO + anchorTxGasLimit: 250000, + feePremiumLamda: 590, + rewardBurnBips: 100, // 100 basis points or 1% + proposerDepositPctg: 25, // 25% + // Moving average factors + feeBaseMAF: 1024, + blockTimeMAF: 1024, + proofTimeMAF: 1024, + rewardMultiplierPctg: 400, // 400% + feeGracePeriodPctg: 125, // 125% + feeMaxPeriodPctg: 375, // 375% + blockTimeCap: 48 seconds, + proofTimeCap: 60 minutes, + boostrapDiscountHalvingPeriod: 180 days, + initialUncleDelay: 60 minutes, + enableTokenomics: false, + skipProofValidation: false + }); + } +} diff --git a/packages/protocol/contracts/libs/LibTxDecoder.sol b/packages/protocol/contracts/libs/LibTxDecoder.sol index 9dff797f56a..c7b35a2ba1c 100644 --- a/packages/protocol/contracts/libs/LibTxDecoder.sol +++ b/packages/protocol/contracts/libs/LibTxDecoder.sol @@ -8,7 +8,6 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; -import "../libs/LibConstants.sol"; import "../thirdparty/LibBytesUtils.sol"; import "../thirdparty/LibRLPReader.sol"; @@ -76,6 +75,7 @@ library LibTxDecoder { } function decodeTxList( + uint256 chainId, bytes calldata encoded ) public pure returns (TxList memory txList) { if (encoded.length == 0) { @@ -85,13 +85,14 @@ library LibTxDecoder { Tx[] memory _txList = new Tx[](txs.length); for (uint256 i = 0; i < txs.length; i++) { - _txList[i] = decodeTx(LibRLPReader.readBytes(txs[i])); + _txList[i] = decodeTx(chainId, LibRLPReader.readBytes(txs[i])); } txList = TxList(_txList); } function decodeTx( + uint256 chainId, bytes memory txBytes ) public pure returns (Tx memory _tx) { uint8 txType; @@ -108,7 +109,7 @@ library LibTxDecoder { LibRLPReader.RLPItem[] memory txBody = LibRLPReader.readList( txBytes ); - TransactionLegacy memory txLegacy = decodeLegacyTx(txBody); + TransactionLegacy memory txLegacy = decodeLegacyTx(chainId, txBody); _tx.gasLimit = txLegacy.gasLimit; _tx.destination = txLegacy.destination; _tx.v = txLegacy.v; @@ -152,6 +153,7 @@ library LibTxDecoder { } function decodeLegacyTx( + uint256 chainId, LibRLPReader.RLPItem[] memory body ) internal pure returns (TransactionLegacy memory txLegacy) { require(body.length == 9, "invalid items length"); @@ -164,7 +166,7 @@ library LibTxDecoder { txLegacy.data = LibRLPReader.readBytes(body[5]); // EIP-155 is enabled on L2 txLegacy.v = uint8( - LibRLPReader.readUint256(body[6]) - LibConstants.K_CHAIN_ID * 2 + 35 + LibRLPReader.readUint256(body[6]) - chainId * 2 + 35 ); txLegacy.r = LibRLPReader.readUint256(body[7]); txLegacy.s = LibRLPReader.readUint256(body[8]); diff --git a/packages/protocol/contracts/libs/LibTxUtils.sol b/packages/protocol/contracts/libs/LibTxUtils.sol index d68edfbbdc3..df4b31a065e 100644 --- a/packages/protocol/contracts/libs/LibTxUtils.sol +++ b/packages/protocol/contracts/libs/LibTxUtils.sol @@ -10,7 +10,6 @@ pragma solidity ^0.8.9; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import "../libs/LibConstants.sol"; import "../libs/LibTxDecoder.sol"; import "../thirdparty/LibBytesUtils.sol"; import "../thirdparty/LibRLPReader.sol"; @@ -19,6 +18,7 @@ import "../thirdparty/LibRLPWriter.sol"; /// @author david library LibTxUtils { function hashUnsignedTx( + uint256 chainId, LibTxDecoder.Tx memory transaction ) internal @@ -72,7 +72,7 @@ library LibTxUtils { // For legacy transactions, there are three more RLP items to // encode defined in EIP-155. if (transaction.txType == 0 && i == list.length - 4) { - list[i + 1] = LibRLPWriter.writeUint(LibConstants.K_CHAIN_ID); + list[i + 1] = LibRLPWriter.writeUint(chainId); list[i + 2] = LibRLPWriter.writeUint64(0); list[i + 3] = LibRLPWriter.writeUint64(0); break; @@ -93,11 +93,12 @@ library LibTxUtils { } function recoverSender( + uint256 chainId, LibTxDecoder.Tx memory transaction ) internal pure returns (address) { return ecrecover( - hashUnsignedTx(transaction), + hashUnsignedTx(chainId, transaction), transaction.v + 27, bytes32(transaction.r), bytes32(transaction.s) diff --git a/packages/protocol/contracts/libs/LibUint512Math.sol b/packages/protocol/contracts/libs/LibUint512Math.sol index 5c29772cd9a..02e06359878 100644 --- a/packages/protocol/contracts/libs/LibUint512Math.sol +++ b/packages/protocol/contracts/libs/LibUint512Math.sol @@ -41,8 +41,8 @@ library LibUint512Math { } /** - * Simple 512-bit addition. - * Taken from: https://xn--2-umb.com/17/512-bit-division/#add-subtract-two-512-bit-numbers + * Simple 512-bit addition. Taken from: + * https://xn--2-umb.com/17/512-bit-division/#add-subtract-two-512-bit-numbers */ function add( uint256 a0, diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1.sol b/packages/protocol/contracts/test/L1/TestTaikoL1.sol new file mode 100644 index 00000000000..e330b512753 --- /dev/null +++ b/packages/protocol/contracts/test/L1/TestTaikoL1.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +// +// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ +// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ +// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ +// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ +// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ +// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ +pragma solidity ^0.8.9; + +import "../../L1/TaikoL1.sol"; + +contract TestTaikoL1NoTokenomicsNoProofValidation is TaikoL1 { + function getConfig() + public + pure + override + returns (LibData.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 = 0; + config.maxProofsPerForkChoice = 5; + config.blockMaxGasLimit = 5000000; // 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 = false; + config.skipProofValidation = true; + } +} diff --git a/packages/protocol/contracts/test/libs/TestLibTxUtils.sol b/packages/protocol/contracts/test/libs/TestLibTxUtils.sol index f88b4bc89d5..d127fc3919a 100644 --- a/packages/protocol/contracts/test/libs/TestLibTxUtils.sol +++ b/packages/protocol/contracts/test/libs/TestLibTxUtils.sol @@ -12,14 +12,16 @@ import "../../libs/LibTxUtils.sol"; contract TestLibTxUtils { function hashUnsignedTx( + uint256 chainId, LibTxDecoder.Tx memory transaction ) public pure returns (bytes32 hash) { - return LibTxUtils.hashUnsignedTx(transaction); + return LibTxUtils.hashUnsignedTx(chainId, transaction); } function recoverSender( + uint256 chainId, LibTxDecoder.Tx memory transaction ) public pure returns (address) { - return LibTxUtils.recoverSender(transaction); + return LibTxUtils.recoverSender(chainId, transaction); } } diff --git a/packages/protocol/hardhat.config.ts b/packages/protocol/hardhat.config.ts index 94cc257500f..9a0fc840e4e 100644 --- a/packages/protocol/hardhat.config.ts +++ b/packages/protocol/hardhat.config.ts @@ -96,33 +96,6 @@ const config: HardhatUserConfig = { }, version: "0.8.9", }, - preprocess: { - eachLine: () => ({ - transform: (line) => { - for (const constantName of [ - "K_CHAIN_ID", - "K_COMMIT_DELAY_CONFIRMS", - "TAIKO_BLOCK_MAX_TXS", - "TAIKO_TXLIST_MAX_BYTES", - "TAIKO_BLOCK_MAX_GAS_LIMIT", - "K_MAX_NUM_BLOCKS", - "K_INITIAL_UNCLE_DELAY", - ]) { - if ( - process.env[constantName] && - line.includes(`uint256 public constant ${constantName}`) - ) { - return `${line.slice(0, line.indexOf(" ="))} = ${ - process.env[constantName] - };`; - } - } - - return line; - }, - files: "libs/LibConstants.sol", - }), - }, }; export default config; diff --git a/packages/protocol/package.json b/packages/protocol/package.json index fbebf637597..bb4b7ff2b00 100644 --- a/packages/protocol/package.json +++ b/packages/protocol/package.json @@ -12,7 +12,7 @@ "eslint:fix": "pnpm exec eslint --ignore-path .eslintignore --ext .js,.ts . --fix", "test": "pnpm hardhat test --grep '^[^integration]'", "coverage": "pnpm hardhat coverage --solcoverjs ./.solcover.js --show-stack-traces", - "test:coverage": "pnpm test && pnpm coverage", + "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", diff --git a/packages/protocol/tasks/deploy_L1.ts b/packages/protocol/tasks/deploy_L1.ts index 4a4ac638064..e288b53f357 100644 --- a/packages/protocol/tasks/deploy_L1.ts +++ b/packages/protocol/tasks/deploy_L1.ts @@ -124,12 +124,12 @@ export async function deployContracts(hre: any) { await deployBaseLibs(hre) ); - const feeBase = hre.ethers.BigNumber.from(10).pow(18) + const feeBase = hre.ethers.BigNumber.from(10).pow(18); await utils.waitTx( hre, await TaikoL1.init(AddressManager.address, l2GenesisBlockHash, feeBase) - ) + ); // Used by LibBridgeRead await utils.waitTx( diff --git a/packages/protocol/test/L1/TaikoL1.test.ts b/packages/protocol/test/L1/TaikoL1.test.ts index a7525b10c11..c492764c3b0 100644 --- a/packages/protocol/test/L1/TaikoL1.test.ts +++ b/packages/protocol/test/L1/TaikoL1.test.ts @@ -46,16 +46,19 @@ describe("TaikoL1", function () { genesisHash = randomBytes32(); const feeBase = BigNumber.from(10).pow(18); taikoL1 = await ( - await ethers.getContractFactory("TaikoL1", { - libraries: { - V1Verifying: v1Verifying.address, - V1Proposing: v1Proposing.address, - V1Proving: v1Proving.address, - }, - }) + await ethers.getContractFactory( + "TestTaikoL1NoTokenomicsNoProofValidation", + { + libraries: { + V1Verifying: v1Verifying.address, + V1Proposing: v1Proposing.address, + V1Proving: v1Proving.address, + }, + } + ) ).deploy(); await taikoL1.init(addressManager.address, genesisHash, feeBase); - }) + }); describe("getLatestSyncedHeader()", async function () { it("should be genesisHash because no headers have been synced", async function () { diff --git a/packages/protocol/test/genesis/generate_genesis.test.ts b/packages/protocol/test/genesis/generate_genesis.test.ts index d7721e37cc5..3e8593a966b 100644 --- a/packages/protocol/test/genesis/generate_genesis.test.ts +++ b/packages/protocol/test/genesis/generate_genesis.test.ts @@ -134,6 +134,13 @@ action("Generate Genesis", function () { }); it("LibTxDecoder", async function () { + const TaikoL2Alloc = getContractAlloc("TaikoL2"); + const TaikoL2 = new hre.ethers.Contract( + TaikoL2Alloc.address, + require("../../artifacts/contracts/L2/TaikoL2.sol/TaikoL2.json").abi, + signer + ); + const config = await TaikoL2.getConfig(); const LibTxDecoderAlloc = getContractAlloc("LibTxDecoder"); const LibTxDecoder = new hre.ethers.Contract( @@ -143,6 +150,7 @@ action("Generate Genesis", function () { ); const decoded = await LibTxDecoder.callStatic.decodeTxList( + config.chainId, ethers.utils.RLP.encode([]) ); @@ -186,7 +194,7 @@ action("Generate Genesis", function () { 5, // hint: TX_INVALID_SIG 0 ) - ).to.be.revertedWith("L2:sender") + ).to.be.revertedWith("L2:sender"); const taikoL2WithGoldenTouchSigner = new hre.ethers.Contract( TaikoL2Alloc.address, @@ -195,7 +203,7 @@ action("Generate Genesis", function () { "92954368afd3caa1f3ce3ead0069c1af414054aefe1ef9aeacc1bf426222ce38", provider ) - ) + ); const tx = await taikoL2WithGoldenTouchSigner.invalidateBlock( bytes, @@ -351,11 +359,11 @@ action("Generate Genesis", function () { }); async function generateMaxSizeInvalidTxList(TaikoL2: any) { - const constants = await TaikoL2.getConstants(); + const config = await TaikoL2.getConfig(); - const chainId = constants[0].toNumber(); - const blockMaxTxNums = constants[7].toNumber(); - const txListMaxBytes = constants[9].toNumber(); + const chainId = config.chainId; + const maxTransactionsPerBlock = config.maxTransactionsPerBlock; + const maxBytesPerTxList = config.maxBytesPerTxList; const tx = { type: 2, @@ -368,7 +376,7 @@ async function generateMaxSizeInvalidTxList(TaikoL2: any) { gasLimit: Math.ceil(Math.random() * 1024000), accessList: [], data: ethers.utils.randomBytes( - Math.floor(txListMaxBytes / blockMaxTxNums) + Math.floor(maxBytesPerTxList / maxTransactionsPerBlock) ), }; @@ -378,13 +386,13 @@ async function generateMaxSizeInvalidTxList(TaikoL2: any) { s: "0x5cf4b3b2b3957e7016366d180493c2c226ea8ad12aed7faddbc0ce3a6789256d", }; - const txs = new Array(blockMaxTxNums).fill(tx); + const txs = new Array(maxTransactionsPerBlock).fill(tx); let txListBytes = ethers.utils.RLP.encode( txs.map((tx) => ethers.utils.serializeTransaction(tx, invalidSig)) ); - while (ethers.utils.arrayify(txListBytes).length > txListMaxBytes) { + while (ethers.utils.arrayify(txListBytes).length > maxBytesPerTxList) { txs[0] = Object.assign(txs[0], { data: txs[0].data.slice(10) }); txListBytes = ethers.utils.RLP.encode( diff --git a/packages/protocol/test/libs/LibTxDecoder.test.ts b/packages/protocol/test/libs/LibTxDecoder.test.ts index 672c2831e6e..2e70cf3f3ac 100644 --- a/packages/protocol/test/libs/LibTxDecoder.test.ts +++ b/packages/protocol/test/libs/LibTxDecoder.test.ts @@ -8,6 +8,8 @@ describe("LibTxDecoder", function () { let libTxDecoder: any; let signer0: any; + const chainId = 167; + before(async function () { rlpWriter = await ( await ethers.getContractFactory("TestLibRLPWriter") @@ -35,13 +37,12 @@ describe("LibTxDecoder", function () { const txListBytes = await rlpEncodeTxList(txList); let decoded = await libTxDecoder.callStatic.decodeTxList( + chainId, txListBytes ); expect(decoded.items.length).to.be.eql(0); - - decoded = await libTxDecoder.callStatic.decodeTxList([]); - + decoded = await libTxDecoder.callStatic.decodeTxList(chainId, []); expect(decoded.items.length).to.be.eql(0); }); @@ -50,14 +51,15 @@ describe("LibTxDecoder", function () { ethers.utils.randomBytes(73) ); - await expect(libTxDecoder.callStatic.decodeTxList(randomBytes)).to - .be.reverted; + await expect( + libTxDecoder.callStatic.decodeTxList(chainId, randomBytes) + ).to.be.reverted; }); it("can decode txList with legacy transaction", async function () { const txLegacy: UnsignedTransaction = { nonce: 1, - chainId: 167, + chainId: chainId, gasPrice: 11e9, gasLimit: 123456, to: ethers.Wallet.createRandom().address, @@ -77,6 +79,7 @@ describe("LibTxDecoder", function () { log.debug("txListBytes: ", txListBytes); const decodedTxList = await libTxDecoder.callStatic.decodeTxList( + chainId, txListBytes ); // log.debug('decodedT: ', decodedTxList) @@ -112,6 +115,7 @@ describe("LibTxDecoder", function () { log.debug("txListBytes: ", txListBytes); const decodedTxList = await libTxDecoder.callStatic.decodeTxList( + chainId, txListBytes ); expect(decodedTxList.items.length).to.equal(1); @@ -147,6 +151,7 @@ describe("LibTxDecoder", function () { log.debug("txListBytes: ", txListBytes); const decodedTxList = await libTxDecoder.callStatic.decodeTxList( + chainId, txListBytes ); expect(decodedTxList.items.length).to.equal(1); @@ -159,7 +164,7 @@ describe("LibTxDecoder", function () { const signature = await signer0.signMessage("123456abcdef"); const txLegacy: UnsignedTransaction = { nonce: 1, - chainId: 167, + chainId: chainId, gasPrice: 11e9, gasLimit: 123456, to: ethers.Wallet.createRandom().address, @@ -201,6 +206,7 @@ describe("LibTxDecoder", function () { const txListBytes = await rlpEncodeTxList(txRawBytesArr); const decodedTxList = await libTxDecoder.callStatic.decodeTxList( + chainId, txListBytes ); // log.debug('decodedT: ', decodedTxList) diff --git a/packages/protocol/test/libs/LibTxUtils.test.ts b/packages/protocol/test/libs/LibTxUtils.test.ts index 68f2e352b8c..88285dd5c01 100644 --- a/packages/protocol/test/libs/LibTxUtils.test.ts +++ b/packages/protocol/test/libs/LibTxUtils.test.ts @@ -6,9 +6,8 @@ describe("LibTxUtils", function () { let libTxUtils: any; let libRLPWriter: any; let libRLPReader: any; - let libConstants: any; let testUnsignedTxs: Array; - let chainId: any; + const chainId = 167; const signingKey = new ethers.utils.SigningKey( ethers.utils.randomBytes(32) @@ -16,10 +15,6 @@ describe("LibTxUtils", function () { const signerAddress = new ethers.Wallet(signingKey.privateKey).address; before(async function () { - libConstants = await ( - await ethers.getContractFactory("LibConstants") - ).deploy(); - libTxUtils = await ( await ethers.getContractFactory("TestLibTxUtils") ).deploy(); @@ -32,8 +27,6 @@ describe("LibTxUtils", function () { await ethers.getContractFactory("TestLibRLPWriter") ).deploy(); - chainId = (await libConstants.K_CHAIN_ID()).toNumber(); - const unsignedLegacyTx: UnsignedTransaction = { type: 0, // if chainId is defined, ether.js will automatically use EIP-155 @@ -93,19 +86,23 @@ describe("LibTxUtils", function () { const signature = signingKey.signDigest(expectedHash); - const hash = await libTxUtils.hashUnsignedTx({ - txType: unsignedTx.type, - destination: unsignedTx.to, - data: unsignedTx.data, - gasLimit: unsignedTx.gasLimit, - v: signature.v, - r: signature.r, - s: signature.s, - txData: ethers.utils.serializeTransaction( - unsignedTx, - signature - ), - }); + const hash = await libTxUtils.hashUnsignedTx( + chainId, + + { + txType: unsignedTx.type, + destination: unsignedTx.to, + data: unsignedTx.data, + gasLimit: unsignedTx.gasLimit, + v: signature.v, + r: signature.r, + s: signature.s, + txData: ethers.utils.serializeTransaction( + unsignedTx, + signature + ), + } + ); expect(hash).to.be.equal(expectedHash); } @@ -119,7 +116,7 @@ describe("LibTxUtils", function () { const signature = signingKey.signDigest(expectedHash); expect( - await libTxUtils.recoverSender({ + await libTxUtils.recoverSender(chainId, { txType: unsignedTx.type, destination: unsignedTx.to, data: unsignedTx.data, @@ -158,7 +155,7 @@ describe("LibTxUtils", function () { ); expect( - await libTxUtils.recoverSender({ + await libTxUtils.recoverSender(chainId, { txType: unsignedTx.type, destination: unsignedTx.to, data: unsignedTx.data, From 5788dd38c8fb4be95659469178d3ce3132dfa507 Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Sat, 24 Dec 2022 22:55:02 +0800 Subject: [PATCH 42/57] refactor(protocol): reorganize contract files (no logic change) (#484) --- .../L1/{LibData.sol => TaikoData.sol} | 2 +- .../L1/{v1/V1Events.sol => TaikoEvents.sol} | 6 +- packages/protocol/contracts/L1/TaikoL1.sol | 68 +++++++++--------- .../V1Proposing.sol => libs/LibProposing.sol} | 70 +++++++++++-------- .../{v1/V1Proving.sol => libs/LibProving.sol} | 62 ++++++++-------- .../L1/{v1/V1Utils.sol => libs/LibUtils.sol} | 59 +++++++--------- .../V1Verifying.sol => libs/LibVerifying.sol} | 56 ++++++++------- packages/protocol/contracts/L2/TaikoL2.sol | 3 +- .../contracts/libs/LibInvalidTxList.sol | 4 +- .../contracts/libs/LibSharedConfig.sol | 6 +- .../contracts/test/L1/TestTaikoL1.sol | 2 +- packages/protocol/docs/L1/TaikoL1.md | 6 +- packages/protocol/hardhat.config.ts | 2 +- packages/protocol/tasks/deploy_L1.ts | 14 ++-- packages/protocol/test/L1/TaikoL1.test.ts | 18 ++--- .../test/bridge/libs/LibBridgeProcess.test.ts | 6 +- .../test/bridge/libs/LibBridgeRetry.test.ts | 12 ++-- packages/relayer/TaikoL1.json | 4 +- packages/relayer/contracts/TaikoL1.go | 22 +++--- .../reference/smart-contracts/L1/TaikoL1.md | 6 +- 20 files changed, 216 insertions(+), 212 deletions(-) rename packages/protocol/contracts/L1/{LibData.sol => TaikoData.sol} (99%) rename packages/protocol/contracts/L1/{v1/V1Events.sol => TaikoEvents.sol} (90%) rename packages/protocol/contracts/L1/{v1/V1Proposing.sol => libs/LibProposing.sol} (81%) rename packages/protocol/contracts/L1/{v1/V1Proving.sol => libs/LibProving.sol} (89%) rename packages/protocol/contracts/L1/{v1/V1Utils.sol => libs/LibUtils.sol} (85%) rename packages/protocol/contracts/L1/{v1/V1Verifying.sol => libs/LibVerifying.sol} (84%) diff --git a/packages/protocol/contracts/L1/LibData.sol b/packages/protocol/contracts/L1/TaikoData.sol similarity index 99% rename from packages/protocol/contracts/L1/LibData.sol rename to packages/protocol/contracts/L1/TaikoData.sol index 9ab517bbb45..bb54cbb8e2e 100644 --- a/packages/protocol/contracts/L1/LibData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -9,7 +9,7 @@ pragma solidity ^0.8.9; /// @author dantaik -library LibData { +library TaikoData { struct Config { uint256 chainId; // up to 2048 pending blocks diff --git a/packages/protocol/contracts/L1/v1/V1Events.sol b/packages/protocol/contracts/L1/TaikoEvents.sol similarity index 90% rename from packages/protocol/contracts/L1/v1/V1Events.sol rename to packages/protocol/contracts/L1/TaikoEvents.sol index e9dd9d3a933..ebd97dd9211 100644 --- a/packages/protocol/contracts/L1/v1/V1Events.sol +++ b/packages/protocol/contracts/L1/TaikoEvents.sol @@ -8,10 +8,10 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; -import "../LibData.sol"; +import "./TaikoData.sol"; /// @author david -abstract contract V1Events { +abstract contract TaikoEvents { // The following events must match the definitions in other V1 libraries. event BlockVerified(uint256 indexed id, bytes32 blockHash); @@ -21,7 +21,7 @@ abstract contract V1Events { bytes32 commitHash ); - event BlockProposed(uint256 indexed id, LibData.BlockMetadata meta); + event BlockProposed(uint256 indexed id, TaikoData.BlockMetadata meta); event BlockProven( uint256 indexed id, diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 860da450c14..1232dcda875 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -12,21 +12,21 @@ import "../common/EssentialContract.sol"; import "../common/IHeaderSync.sol"; import "../libs/LibAnchorSignature.sol"; import "../libs/LibSharedConfig.sol"; -import "./LibData.sol"; -import "./v1/V1Events.sol"; -import "./v1/V1Proposing.sol"; -import "./v1/V1Proving.sol"; -import "./v1/V1Utils.sol"; -import "./v1/V1Verifying.sol"; +import "./TaikoData.sol"; +import "./TaikoEvents.sol"; +import "./libs/LibProposing.sol"; +import "./libs/LibProving.sol"; +import "./libs/LibUtils.sol"; +import "./libs/LibVerifying.sol"; /** * @author dantaik */ -contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { - using V1Utils for LibData.State; +contract TaikoL1 is EssentialContract, IHeaderSync, TaikoEvents { + using LibUtils for TaikoData.State; - LibData.State public state; - LibData.TentativeState public tentative; + TaikoData.State public state; + TaikoData.TentativeState public tentative; uint256[50] private __gap; function init( @@ -35,7 +35,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { uint256 _feeBase ) external initializer { EssentialContract._init(_addressManager); - V1Verifying.init({ + LibVerifying.init({ state: state, genesisBlockHash: _genesisBlockHash, feeBase: _feeBase @@ -56,7 +56,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { * `calculateCommitHash(beneficiary, txListHash)`. */ function commitBlock(uint64 commitSlot, bytes32 commitHash) external { - V1Proposing.commitBlock({ + LibProposing.commitBlock({ state: state, config: getConfig(), commitSlot: commitSlot, @@ -84,15 +84,15 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { * transactions in the L2 block. */ function proposeBlock(bytes[] calldata inputs) external nonReentrant { - LibData.Config memory config = getConfig(); - V1Proposing.proposeBlock({ + TaikoData.Config memory config = getConfig(); + LibProposing.proposeBlock({ state: state, config: config, tentative: tentative, resolver: AddressResolver(this), inputs: inputs }); - V1Verifying.verifyBlocks({ + LibVerifying.verifyBlocks({ state: state, config: getConfig(), resolver: AddressResolver(this), @@ -120,8 +120,8 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { uint256 blockId, bytes[] calldata inputs ) external nonReentrant { - LibData.Config memory config = getConfig(); - V1Proving.proveBlock({ + TaikoData.Config memory config = getConfig(); + LibProving.proveBlock({ state: state, tentative: tentative, config: config, @@ -129,7 +129,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { blockId: blockId, inputs: inputs }); - V1Verifying.verifyBlocks({ + LibVerifying.verifyBlocks({ state: state, config: config, resolver: AddressResolver(this), @@ -156,9 +156,9 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { uint256 blockId, bytes[] calldata inputs ) external nonReentrant { - LibData.Config memory config = getConfig(); + TaikoData.Config memory config = getConfig(); - V1Proving.proveBlockInvalid({ + LibProving.proveBlockInvalid({ state: state, tentative: tentative, config: config, @@ -166,7 +166,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { blockId: blockId, inputs: inputs }); - V1Verifying.verifyBlocks({ + LibVerifying.verifyBlocks({ state: state, config: config, resolver: AddressResolver(this), @@ -181,7 +181,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { */ function verifyBlocks(uint256 maxBlocks) external nonReentrant { require(maxBlocks > 0, "L1:maxBlocks"); - V1Verifying.verifyBlocks({ + LibVerifying.verifyBlocks({ state: state, config: getConfig(), resolver: AddressResolver(this), @@ -199,7 +199,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { bool whitelistProposers, bool whitelistProvers ) public onlyOwner { - V1Utils.enableWhitelisting({ + LibUtils.enableWhitelisting({ tentative: tentative, whitelistProposers: whitelistProposers, whitelistProvers: whitelistProvers @@ -216,7 +216,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { address proposer, bool whitelisted ) public onlyOwner { - V1Utils.whitelistProposer({ + LibUtils.whitelistProposer({ tentative: tentative, proposer: proposer, whitelisted: whitelisted @@ -233,7 +233,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { address prover, bool whitelisted ) public onlyOwner { - V1Utils.whitelistProver({ + LibUtils.whitelistProver({ tentative: tentative, prover: prover, whitelisted: whitelisted @@ -245,7 +245,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { * @param toHalt True to halt, false to resume. */ function halt(bool toHalt) public onlyOwner { - V1Utils.halt(state, toHalt); + LibUtils.halt(state, toHalt); } /** @@ -257,7 +257,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { function isProposerWhitelisted( address proposer ) public view returns (bool) { - return V1Utils.isProposerWhitelisted(tentative, proposer); + return LibUtils.isProposerWhitelisted(tentative, proposer); } /** @@ -267,11 +267,11 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { * @return True if the prover is whitelisted, false otherwise. */ function isProverWhitelisted(address prover) public view returns (bool) { - return V1Utils.isProverWhitelisted(tentative, prover); + return LibUtils.isProverWhitelisted(tentative, prover); } function getBlockFee() public view returns (uint256) { - (, uint fee, uint deposit) = V1Proposing.getBlockFee( + (, uint fee, uint deposit) = LibProposing.getBlockFee( state, getConfig() ); @@ -282,7 +282,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { uint64 provenAt, uint64 proposedAt ) public view returns (uint256 reward) { - (, reward, ) = V1Verifying.getProofReward({ + (, reward, ) = LibVerifying.getProofReward({ state: state, config: getConfig(), provenAt: provenAt, @@ -295,7 +295,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { * @return True if halted, false otherwise. */ function isHalted() public view returns (bool) { - return V1Utils.isHalted(state); + return LibUtils.isHalted(state); } function isCommitValid( @@ -304,7 +304,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { bytes32 commitHash ) public view returns (bool) { return - V1Proposing.isCommitValid( + LibProposing.isCommitValid( state, getConfig().commitConfirmations, commitSlot, @@ -315,7 +315,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { function getProposedBlock( uint256 id - ) public view returns (LibData.ProposedBlock memory) { + ) public view returns (TaikoData.ProposedBlock memory) { return state.getProposedBlock(getConfig().maxNumBlocks, id); } @@ -363,7 +363,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { return state.forkChoices[id][parentHash].provers; } - function getConfig() public pure virtual returns (LibData.Config memory) { + function getConfig() public pure virtual returns (TaikoData.Config memory) { return LibSharedConfig.getConfig(); } } diff --git a/packages/protocol/contracts/L1/v1/V1Proposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol similarity index 81% rename from packages/protocol/contracts/L1/v1/V1Proposing.sol rename to packages/protocol/contracts/L1/libs/LibProposing.sol index 5df5c2a5826..562eed41157 100644 --- a/packages/protocol/contracts/L1/v1/V1Proposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -11,23 +11,25 @@ pragma solidity ^0.8.9; import "../../common/ConfigManager.sol"; import "../../libs/LibTxDecoder.sol"; import "../TkoToken.sol"; -import "./V1Utils.sol"; +import "./LibUtils.sol"; /// @author dantaik -library V1Proposing { +library LibProposing { using LibTxDecoder for bytes; using SafeCastUpgradeable for uint256; - using V1Utils for LibData.BlockMetadata; - using V1Utils for LibData.State; + using LibUtils for TaikoData.BlockMetadata; + using LibUtils for TaikoData.State; event BlockCommitted( uint64 commitSlot, uint64 commitHeight, bytes32 commitHash ); - event BlockProposed(uint256 indexed id, LibData.BlockMetadata meta); + event BlockProposed(uint256 indexed id, TaikoData.BlockMetadata meta); - modifier onlyWhitelistedProposer(LibData.TentativeState storage tentative) { + modifier onlyWhitelistedProposer( + TaikoData.TentativeState storage tentative + ) { if (tentative.whitelistProposers) { require(tentative.proposers[msg.sender], "L1:whitelist"); } @@ -35,8 +37,8 @@ library V1Proposing { } function commitBlock( - LibData.State storage state, - LibData.Config memory config, + TaikoData.State storage state, + TaikoData.Config memory config, uint64 commitSlot, bytes32 commitHash ) public { @@ -44,7 +46,7 @@ library V1Proposing { // It's OK to allow committing block when the system is halt. // By not checking the halt status, this method will be cheaper. // - // assert(!V1Utils.isHalted(state)); + // assert(!LibUtils.isHalted(state)); bytes32 hash = _aggregateCommitHash(block.number, commitHash); @@ -59,18 +61,18 @@ library V1Proposing { } function proposeBlock( - LibData.State storage state, - LibData.Config memory config, - LibData.TentativeState storage tentative, + TaikoData.State storage state, + TaikoData.Config memory config, + TaikoData.TentativeState storage tentative, AddressResolver resolver, bytes[] calldata inputs ) public onlyWhitelistedProposer(tentative) { - assert(!V1Utils.isHalted(state)); + assert(!LibUtils.isHalted(state)); require(inputs.length == 2, "L1:inputs:size"); - LibData.BlockMetadata memory meta = abi.decode( + TaikoData.BlockMetadata memory meta = abi.decode( inputs[0], - (LibData.BlockMetadata) + (TaikoData.BlockMetadata) ); _verifyBlockCommit({ state: state, @@ -116,17 +118,18 @@ library V1Proposing { ); } // Update feeBase and avgBlockTime - state.feeBase = V1Utils.movingAverage({ + state.feeBase = LibUtils.movingAverage({ maValue: state.feeBase, newValue: newFeeBase, maf: config.feeBaseMAF }); } - state.saveProposedBlock( + _saveProposedBlock( + state, config.maxNumBlocks, state.nextBlockId, - LibData.ProposedBlock({ + TaikoData.ProposedBlock({ metaHash: meta.hashMetadata(), deposit: deposit, proposer: msg.sender, @@ -134,7 +137,7 @@ library V1Proposing { }) ); - state.avgBlockTime = V1Utils + state.avgBlockTime = LibUtils .movingAverage({ maValue: state.avgBlockTime, newValue: meta.timestamp - state.lastProposedAt, @@ -148,10 +151,10 @@ library V1Proposing { } function getBlockFee( - LibData.State storage state, - LibData.Config memory config + TaikoData.State storage state, + TaikoData.Config memory config ) public view returns (uint256 newFeeBase, uint256 fee, uint256 deposit) { - (newFeeBase, ) = V1Utils.getTimeAdjustedFee({ + (newFeeBase, ) = LibUtils.getTimeAdjustedFee({ state: state, config: config, isProposal: true, @@ -159,18 +162,18 @@ library V1Proposing { tLast: state.lastProposedAt, tAvg: state.avgBlockTime }); - fee = V1Utils.getSlotsAdjustedFee({ + fee = LibUtils.getSlotsAdjustedFee({ state: state, config: config, isProposal: true, feeBase: newFeeBase }); - fee = V1Utils.getBootstrapDiscountedFee(state, config, fee); + fee = LibUtils.getBootstrapDiscountedFee(state, config, fee); deposit = (fee * config.proposerDepositPctg) / 100; } function isCommitValid( - LibData.State storage state, + TaikoData.State storage state, uint256 commitConfirmations, uint256 commitSlot, uint256 commitHeight, @@ -183,10 +186,19 @@ library V1Proposing { block.number >= commitHeight + commitConfirmations; } + function _saveProposedBlock( + TaikoData.State storage state, + uint256 maxNumBlocks, + uint256 id, + TaikoData.ProposedBlock memory blk + ) private { + state.proposedBlocks[id % maxNumBlocks] = blk; + } + function _verifyBlockCommit( - LibData.State storage state, + TaikoData.State storage state, uint256 commitConfirmations, - LibData.BlockMetadata memory meta + TaikoData.BlockMetadata memory meta ) private { if (commitConfirmations == 0) { return; @@ -215,8 +227,8 @@ library V1Proposing { } function _validateMetadata( - LibData.Config memory config, - LibData.BlockMetadata memory meta + TaikoData.Config memory config, + TaikoData.BlockMetadata memory meta ) private pure { require( meta.id == 0 && diff --git a/packages/protocol/contracts/L1/v1/V1Proving.sol b/packages/protocol/contracts/L1/libs/LibProving.sol similarity index 89% rename from packages/protocol/contracts/L1/v1/V1Proving.sol rename to packages/protocol/contracts/L1/libs/LibProving.sol index 93158998dd9..e1ccc47704b 100644 --- a/packages/protocol/contracts/L1/v1/V1Proving.sol +++ b/packages/protocol/contracts/L1/libs/LibProving.sol @@ -19,17 +19,17 @@ import "../../libs/LibZKP.sol"; import "../../thirdparty/LibBytesUtils.sol"; import "../../thirdparty/LibMerkleTrie.sol"; import "../../thirdparty/LibRLPWriter.sol"; -import "./V1Utils.sol"; +import "./LibUtils.sol"; /// @author dantaik /// @author david -library V1Proving { +library LibProving { using LibBlockHeader for BlockHeader; - using V1Utils for LibData.BlockMetadata; - using V1Utils for LibData.State; + using LibUtils for TaikoData.BlockMetadata; + using LibUtils for TaikoData.State; struct Evidence { - LibData.BlockMetadata meta; + TaikoData.BlockMetadata meta; BlockHeader header; address prover; bytes[] proofs; // The first zkProofsPerBlock are ZKPs @@ -50,7 +50,7 @@ library V1Proving { address prover ); - modifier onlyWhitelistedProver(LibData.TentativeState storage tentative) { + modifier onlyWhitelistedProver(TaikoData.TentativeState storage tentative) { if (tentative.whitelistProvers) { require(tentative.provers[msg.sender], "L1:whitelist"); } @@ -58,14 +58,14 @@ library V1Proving { } function proveBlock( - LibData.State storage state, - LibData.TentativeState storage tentative, - LibData.Config memory config, + TaikoData.State storage state, + TaikoData.TentativeState storage tentative, + TaikoData.Config memory config, AddressResolver resolver, uint256 blockId, bytes[] calldata inputs ) public onlyWhitelistedProver(tentative) { - assert(!V1Utils.isHalted(state)); + assert(!LibUtils.isHalted(state)); // Check and decode inputs require(inputs.length == 3, "L1:inputs:size"); @@ -156,21 +156,21 @@ library V1Proving { } function proveBlockInvalid( - LibData.State storage state, - LibData.TentativeState storage tentative, - LibData.Config memory config, + TaikoData.State storage state, + TaikoData.TentativeState storage tentative, + TaikoData.Config memory config, AddressResolver resolver, uint256 blockId, bytes[] calldata inputs ) public onlyWhitelistedProver(tentative) { - assert(!V1Utils.isHalted(state)); + assert(!LibUtils.isHalted(state)); // Check and decode inputs require(inputs.length == 3, "L1:inputs:size"); Evidence memory evidence = abi.decode(inputs[0], (Evidence)); - LibData.BlockMetadata memory target = abi.decode( + TaikoData.BlockMetadata memory target = abi.decode( inputs[1], - (LibData.BlockMetadata) + (TaikoData.BlockMetadata) ); bytes calldata invalidateBlockReceipt = inputs[2]; @@ -222,16 +222,16 @@ library V1Proving { resolver: resolver, evidence: evidence, target: target, - blockHashOverride: V1Utils.BLOCK_DEADEND_HASH + blockHashOverride: LibUtils.BLOCK_DEADEND_HASH }); } function _proveBlock( - LibData.State storage state, - LibData.Config memory config, + TaikoData.State storage state, + TaikoData.Config memory config, AddressResolver resolver, Evidence memory evidence, - LibData.BlockMetadata memory target, + TaikoData.BlockMetadata memory target, bytes32 blockHashOverride ) private { require(evidence.meta.id == target.id, "L1:height"); @@ -271,14 +271,14 @@ library V1Proving { } function _markBlockProven( - LibData.State storage state, - LibData.Config memory config, + TaikoData.State storage state, + TaikoData.Config memory config, address prover, - LibData.BlockMetadata memory target, + TaikoData.BlockMetadata memory target, bytes32 parentHash, bytes32 blockHash ) private { - LibData.ForkChoice storage fc = state.forkChoices[target.id][ + TaikoData.ForkChoice storage fc = state.forkChoices[target.id][ parentHash ]; @@ -289,7 +289,7 @@ library V1Proving { if (fc.blockHash != blockHash) { // We have a problem here: two proofs are both valid but claims // the new block has different hashes. - V1Utils.halt(state, true); + LibUtils.halt(state, true); return; } @@ -300,7 +300,7 @@ library V1Proving { require( block.timestamp < - V1Utils.uncleProofDeadline({ + LibUtils.uncleProofDeadline({ state: state, config: config, fc: fc, @@ -345,9 +345,9 @@ library V1Proving { } function _checkMetadata( - LibData.State storage state, - LibData.Config memory config, - LibData.BlockMetadata memory meta + TaikoData.State storage state, + TaikoData.Config memory config, + TaikoData.BlockMetadata memory meta ) private view { require( meta.id > state.latestVerifiedId && meta.id < state.nextBlockId, @@ -361,9 +361,9 @@ library V1Proving { } function _validateHeaderForMetadata( - LibData.Config memory config, + TaikoData.Config memory config, BlockHeader memory header, - LibData.BlockMetadata memory meta + TaikoData.BlockMetadata memory meta ) private pure { require( header.parentHash != 0 && diff --git a/packages/protocol/contracts/L1/v1/V1Utils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol similarity index 85% rename from packages/protocol/contracts/L1/v1/V1Utils.sol rename to packages/protocol/contracts/L1/libs/LibUtils.sol index 80e88dc6bd0..1b50ce7667d 100644 --- a/packages/protocol/contracts/L1/v1/V1Utils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -11,10 +11,10 @@ pragma solidity ^0.8.9; import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import "../../libs/LibMath.sol"; -import "../LibData.sol"; +import "../TaikoData.sol"; /// @author dantaik -library V1Utils { +library LibUtils { using LibMath for uint256; uint64 public constant MASK_HALT = 1 << 0; @@ -26,17 +26,8 @@ library V1Utils { event ProverWhitelisted(address indexed prover, bool whitelisted); event Halted(bool halted); - function saveProposedBlock( - LibData.State storage state, - uint256 maxNumBlocks, - uint256 id, - LibData.ProposedBlock memory blk - ) internal { - state.proposedBlocks[id % maxNumBlocks] = blk; - } - function enableWhitelisting( - LibData.TentativeState storage tentative, + TaikoData.TentativeState storage tentative, bool whitelistProposers, bool whitelistProvers ) internal { @@ -46,7 +37,7 @@ library V1Utils { } function whitelistProposer( - LibData.TentativeState storage tentative, + TaikoData.TentativeState storage tentative, address proposer, bool whitelisted ) internal { @@ -62,7 +53,7 @@ library V1Utils { } function whitelistProver( - LibData.TentativeState storage tentative, + TaikoData.TentativeState storage tentative, address prover, bool whitelisted ) internal { @@ -76,22 +67,22 @@ library V1Utils { emit ProverWhitelisted(prover, whitelisted); } - function halt(LibData.State storage state, bool toHalt) internal { + function halt(TaikoData.State storage state, bool toHalt) internal { require(isHalted(state) != toHalt, "L1:precondition"); setBit(state, MASK_HALT, toHalt); emit Halted(toHalt); } function getProposedBlock( - LibData.State storage state, + TaikoData.State storage state, uint256 maxNumBlocks, uint256 id - ) internal view returns (LibData.ProposedBlock storage) { + ) internal view returns (TaikoData.ProposedBlock storage) { return state.proposedBlocks[id % maxNumBlocks]; } function getL2BlockHash( - LibData.State storage state, + TaikoData.State storage state, uint256 number ) internal view returns (bytes32) { require(number <= state.latestVerifiedHeight, "L1:id"); @@ -99,7 +90,7 @@ library V1Utils { } function getStateVariables( - LibData.State storage state + TaikoData.State storage state ) internal view @@ -131,13 +122,13 @@ library V1Utils { } function isHalted( - LibData.State storage state + TaikoData.State storage state ) internal view returns (bool) { return isBitOne(state, MASK_HALT); } function isProposerWhitelisted( - LibData.TentativeState storage tentative, + TaikoData.TentativeState storage tentative, address proposer ) internal view returns (bool) { assert(tentative.whitelistProposers); @@ -145,7 +136,7 @@ library V1Utils { } function isProverWhitelisted( - LibData.TentativeState storage tentative, + TaikoData.TentativeState storage tentative, address prover ) internal view returns (bool) { assert(tentative.whitelistProvers); @@ -154,8 +145,8 @@ library V1Utils { // Implement "Incentive Multipliers", see the whitepaper. function getTimeAdjustedFee( - LibData.State storage state, - LibData.Config memory config, + TaikoData.State storage state, + TaikoData.Config memory config, bool isProposal, uint64 tNow, uint64 tLast, @@ -186,8 +177,8 @@ library V1Utils { // Implement "Slot-availability Multipliers", see the whitepaper. function getSlotsAdjustedFee( - LibData.State storage state, - LibData.Config memory config, + TaikoData.State storage state, + TaikoData.Config memory config, bool isProposal, uint256 feeBase ) internal view returns (uint256) { @@ -202,8 +193,8 @@ library V1Utils { // Implement "Bootstrap Discount Multipliers", see the whitepaper. function getBootstrapDiscountedFee( - LibData.State storage state, - LibData.Config memory config, + TaikoData.State storage state, + TaikoData.Config memory config, uint256 feeBase ) internal view returns (uint256) { uint256 halves = uint256(block.timestamp - state.genesisTimestamp) / @@ -214,9 +205,9 @@ library V1Utils { // Returns a deterministic deadline for uncle proof submission. function uncleProofDeadline( - LibData.State storage state, - LibData.Config memory config, - LibData.ForkChoice storage fc, + TaikoData.State storage state, + TaikoData.Config memory config, + TaikoData.ForkChoice storage fc, uint256 blockId ) internal view returns (uint64) { if (blockId <= 2 * config.maxNumBlocks) { @@ -227,7 +218,7 @@ library V1Utils { } function hashMetadata( - LibData.BlockMetadata memory meta + TaikoData.BlockMetadata memory meta ) internal pure returns (bytes32) { return keccak256(abi.encode(meta)); } @@ -245,7 +236,7 @@ library V1Utils { } function setBit( - LibData.State storage state, + TaikoData.State storage state, uint64 mask, bool one ) private { @@ -255,7 +246,7 @@ library V1Utils { } function isBitOne( - LibData.State storage state, + TaikoData.State storage state, uint64 mask ) private view returns (bool) { return state.statusBits & mask != 0; diff --git a/packages/protocol/contracts/L1/v1/V1Verifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol similarity index 84% rename from packages/protocol/contracts/L1/v1/V1Verifying.sol rename to packages/protocol/contracts/L1/libs/LibVerifying.sol index 8242dc5faed..b4dfaaaf45b 100644 --- a/packages/protocol/contracts/L1/v1/V1Verifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -10,12 +10,12 @@ pragma solidity ^0.8.9; import "../../common/AddressResolver.sol"; import "../TkoToken.sol"; -import "./V1Utils.sol"; +import "./LibUtils.sol"; /// @author dantaik -library V1Verifying { +library LibVerifying { using SafeCastUpgradeable for uint256; - using V1Utils for LibData.State; + using LibUtils for TaikoData.State; event BlockVerified(uint256 indexed id, bytes32 blockHash); @@ -26,7 +26,7 @@ library V1Verifying { ); function init( - LibData.State storage state, + TaikoData.State storage state, bytes32 genesisBlockHash, uint256 feeBase ) public { @@ -44,13 +44,13 @@ library V1Verifying { } function verifyBlocks( - LibData.State storage state, - LibData.Config memory config, + TaikoData.State storage state, + TaikoData.Config memory config, AddressResolver resolver, uint256 maxBlocks, bool checkHalt ) public { - bool halted = V1Utils.isHalted(state); + bool halted = LibUtils.isHalted(state); if (checkHalt) { require(!halted, "L1:halted"); } else if (halted) { @@ -67,8 +67,10 @@ library V1Verifying { i < state.nextBlockId && processed <= maxBlocks; i++ ) { - LibData.ForkChoice storage fc = state.forkChoices[i][latestL2Hash]; - LibData.ProposedBlock storage target = state.getProposedBlock( + TaikoData.ForkChoice storage fc = state.forkChoices[i][ + latestL2Hash + ]; + TaikoData.ProposedBlock storage target = state.getProposedBlock( config.maxNumBlocks, i ); @@ -116,12 +118,12 @@ library V1Verifying { } function getProofReward( - LibData.State storage state, - LibData.Config memory config, + TaikoData.State storage state, + TaikoData.Config memory config, uint64 provenAt, uint64 proposedAt ) public view returns (uint256 newFeeBase, uint256 reward, uint256 tRelBp) { - (newFeeBase, tRelBp) = V1Utils.getTimeAdjustedFee({ + (newFeeBase, tRelBp) = LibUtils.getTimeAdjustedFee({ state: state, config: config, isProposal: false, @@ -129,7 +131,7 @@ library V1Verifying { tLast: proposedAt, tAvg: state.avgProofTime }); - reward = V1Utils.getSlotsAdjustedFee({ + reward = LibUtils.getSlotsAdjustedFee({ state: state, config: config, isProposal: false, @@ -139,7 +141,7 @@ library V1Verifying { } function _refundProposerDeposit( - LibData.ProposedBlock storage target, + TaikoData.ProposedBlock storage target, uint256 tRelBp, TkoToken tkoToken ) private { @@ -150,7 +152,7 @@ library V1Verifying { } function _rewardProvers( - LibData.ForkChoice storage fc, + TaikoData.ForkChoice storage fc, uint256 reward, TkoToken tkoToken ) private { @@ -168,11 +170,11 @@ library V1Verifying { } function _verifyBlock( - LibData.State storage state, - LibData.Config memory config, + TaikoData.State storage state, + TaikoData.Config memory config, AddressResolver resolver, - LibData.ForkChoice storage fc, - LibData.ProposedBlock storage target, + TaikoData.ForkChoice storage fc, + TaikoData.ProposedBlock storage target, uint64 latestL2Height, bytes32 latestL2Hash ) private returns (uint64 _latestL2Height, bytes32 _latestL2Hash) { @@ -194,14 +196,14 @@ library V1Verifying { _refundProposerDeposit(target, tRelBp, tkoToken); } // Update feeBase and avgProofTime - state.feeBase = V1Utils.movingAverage({ + state.feeBase = LibUtils.movingAverage({ maValue: state.feeBase, newValue: newFeeBase, maf: config.feeBaseMAF }); } - state.avgProofTime = V1Utils + state.avgProofTime = LibUtils .movingAverage({ maValue: state.avgProofTime, newValue: fc.provenAt - target.proposedAt, @@ -209,7 +211,7 @@ library V1Verifying { }) .toUint64(); - if (fc.blockHash != V1Utils.BLOCK_DEADEND_HASH) { + if (fc.blockHash != LibUtils.BLOCK_DEADEND_HASH) { _latestL2Height = latestL2Height + 1; _latestL2Hash = fc.blockHash; } else { @@ -218,7 +220,7 @@ library V1Verifying { } } - function _cleanUp(LibData.ForkChoice storage fc) private { + function _cleanUp(TaikoData.ForkChoice storage fc) private { fc.blockHash = 0; fc.provenAt = 0; for (uint i = 0; i < fc.provers.length; i++) { @@ -228,15 +230,15 @@ library V1Verifying { } function _isVerifiable( - LibData.State storage state, - LibData.Config memory config, - LibData.ForkChoice storage fc, + TaikoData.State storage state, + TaikoData.Config memory config, + TaikoData.ForkChoice storage fc, uint256 blockId ) private view returns (bool) { return fc.blockHash != 0 && block.timestamp > - V1Utils.uncleProofDeadline({ + LibUtils.uncleProofDeadline({ state: state, config: config, fc: fc, diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 655a9ef8c6e..d9a3b3698f6 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -8,7 +8,6 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; -import "hardhat/console.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; @@ -124,7 +123,7 @@ contract TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { public view virtual - returns (LibData.Config memory config) + returns (TaikoData.Config memory config) { config = LibSharedConfig.getConfig(); config.chainId = block.chainid; diff --git a/packages/protocol/contracts/libs/LibInvalidTxList.sol b/packages/protocol/contracts/libs/LibInvalidTxList.sol index 8be7ce079b5..4d917d48a80 100644 --- a/packages/protocol/contracts/libs/LibInvalidTxList.sol +++ b/packages/protocol/contracts/libs/LibInvalidTxList.sol @@ -8,7 +8,7 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; -import "../L1/LibData.sol"; +import "../L1/TaikoData.sol"; import "../libs/LibTxDecoder.sol"; import "../libs/LibTxUtils.sol"; import "../thirdparty/LibRLPReader.sol"; @@ -50,7 +50,7 @@ library LibInvalidTxList { } function isTxListInvalid( - LibData.Config memory config, + TaikoData.Config memory config, bytes calldata encoded, Reason hint, uint256 txIdx diff --git a/packages/protocol/contracts/libs/LibSharedConfig.sol b/packages/protocol/contracts/libs/LibSharedConfig.sol index 95cc56588fe..bfd693cca50 100644 --- a/packages/protocol/contracts/libs/LibSharedConfig.sol +++ b/packages/protocol/contracts/libs/LibSharedConfig.sol @@ -8,13 +8,13 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; -import "../L1/LibData.sol"; +import "../L1/TaikoData.sol"; library LibSharedConfig { /// Returns shared configs for both TaikoL1 and TaikoL2 for production. - function getConfig() internal pure returns (LibData.Config memory) { + function getConfig() internal pure returns (TaikoData.Config memory) { return - LibData.Config({ + TaikoData.Config({ chainId: 167, maxNumBlocks: 2049, // up to 2048 pending blocks blockHashHistory: 100000, diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1.sol b/packages/protocol/contracts/test/L1/TestTaikoL1.sol index e330b512753..c4ae8b8b290 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1.sol @@ -15,7 +15,7 @@ contract TestTaikoL1NoTokenomicsNoProofValidation is TaikoL1 { public pure override - returns (LibData.Config memory config) + returns (TaikoData.Config memory config) { config.chainId = 167; // up to 2048 pending blocks diff --git a/packages/protocol/docs/L1/TaikoL1.md b/packages/protocol/docs/L1/TaikoL1.md index 34624912bb2..222411f6509 100644 --- a/packages/protocol/docs/L1/TaikoL1.md +++ b/packages/protocol/docs/L1/TaikoL1.md @@ -3,13 +3,13 @@ ### state ```solidity -struct LibData.State state +struct TaikoData.State state ``` ### tentative ```solidity -struct LibData.TentativeState tentative +struct TaikoData.TentativeState tentative ``` ### \_\_gap @@ -223,7 +223,7 @@ function isCommitValid(uint256 commitSlot, uint256 commitHeight, bytes32 commitH ### getProposedBlock ```solidity -function getProposedBlock(uint256 id) public view returns (struct LibData.ProposedBlock) +function getProposedBlock(uint256 id) public view returns (struct TaikoData.ProposedBlock) ``` ### getSyncedHeader diff --git a/packages/protocol/hardhat.config.ts b/packages/protocol/hardhat.config.ts index 9a0fc840e4e..01cfec2b7b1 100644 --- a/packages/protocol/hardhat.config.ts +++ b/packages/protocol/hardhat.config.ts @@ -16,7 +16,7 @@ const config: HardhatUserConfig = { docgen: { exclude: [ "bridge/libs/", - "L1/v1/", + "L1/libs/", "libs/", "test/", "thirdparty/", diff --git a/packages/protocol/tasks/deploy_L1.ts b/packages/protocol/tasks/deploy_L1.ts index e288b53f357..06fe2a836e6 100644 --- a/packages/protocol/tasks/deploy_L1.ts +++ b/packages/protocol/tasks/deploy_L1.ts @@ -89,7 +89,7 @@ export async function deployContracts(hre: any) { hre, await AddressManager.setAddress(`${chainId}.team_vault`, teamVault) ); - // Used by V1Proving + // Used by LibProving await utils.waitTx( hre, await AddressManager.setAddress(`${l2ChainId}.taiko`, taikoL2Address) @@ -202,19 +202,19 @@ async function deployBaseLibs(hre: any) { ); const libTxDecoder = await utils.deployContract(hre, "LibTxDecoder"); - const v1Verifying = await utils.deployContract(hre, "V1Verifying", {}); - const v1Proposing = await utils.deployContract(hre, "V1Proposing", {}); + const libVerifying = await utils.deployContract(hre, "LibVerifying", {}); + const libProposing = await utils.deployContract(hre, "LibProposing", {}); - const v1Proving = await utils.deployContract(hre, "V1Proving", { + const libProving = await utils.deployContract(hre, "LibProving", { LibZKP: libZKP.address, LibReceiptDecoder: libReceiptDecoder.address, LibTxDecoder: libTxDecoder.address, }); return { - V1Verifying: v1Verifying.address, - V1Proposing: v1Proposing.address, - V1Proving: v1Proving.address, + LibVerifying: libVerifying.address, + LibProposing: libProposing.address, + LibProving: libProving.address, }; } diff --git a/packages/protocol/test/L1/TaikoL1.test.ts b/packages/protocol/test/L1/TaikoL1.test.ts index c492764c3b0..18763fc237d 100644 --- a/packages/protocol/test/L1/TaikoL1.test.ts +++ b/packages/protocol/test/L1/TaikoL1.test.ts @@ -25,12 +25,12 @@ describe("TaikoL1", function () { await ethers.getContractFactory("LibZKP") ).deploy(); - const v1Proposing = await ( - await ethers.getContractFactory("V1Proposing") + const libProposing = await ( + await ethers.getContractFactory("LibProposing") ).deploy(); - const v1Proving = await ( - await ethers.getContractFactory("V1Proving", { + const libProving = await ( + await ethers.getContractFactory("LibProving", { libraries: { LibReceiptDecoder: libReceiptDecoder.address, LibTxDecoder: libTxDecoder.address, @@ -39,8 +39,8 @@ describe("TaikoL1", function () { }) ).deploy(); - const v1Verifying = await ( - await ethers.getContractFactory("V1Verifying") + const libVerifying = await ( + await ethers.getContractFactory("LibVerifying") ).deploy(); genesisHash = randomBytes32(); @@ -50,9 +50,9 @@ describe("TaikoL1", function () { "TestTaikoL1NoTokenomicsNoProofValidation", { libraries: { - V1Verifying: v1Verifying.address, - V1Proposing: v1Proposing.address, - V1Proving: v1Proving.address, + LibVerifying: libVerifying.address, + LibProposing: libProposing.address, + LibProving: libProving.address, }, } ) diff --git a/packages/protocol/test/bridge/libs/LibBridgeProcess.test.ts b/packages/protocol/test/bridge/libs/LibBridgeProcess.test.ts index 7b197db18a0..7c597e2394e 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeProcess.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeProcess.test.ts @@ -45,7 +45,7 @@ describe("LibBridgeProcess", async function () { let libTrieLink; let libProcessLink; let libProcess: TestLibBridgeProcess; - let testLibData: TestLibBridgeData; + let testTaikoData: TestLibBridgeData; const stateSlot = getStateSlot(); const srcChainId = 1; const blockChainId = hre.network.config.chainId ?? 0; @@ -110,7 +110,7 @@ describe("LibBridgeProcess", async function () { await libProcess.init(addressManager.address); - testLibData = await ( + testTaikoData = await ( await ethers.getContractFactory("TestLibBridgeData") ).deploy(); @@ -180,7 +180,7 @@ describe("LibBridgeProcess", async function () { memo: "", }; - const signal = await testLibData.hashMessage(message); + const signal = await testTaikoData.hashMessage(message); await helpers.setStorageAt( libProcess.address, diff --git a/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts b/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts index 59bc5e3a26e..929c7ea8444 100644 --- a/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts +++ b/packages/protocol/test/bridge/libs/LibBridgeRetry.test.ts @@ -20,7 +20,7 @@ describe("LibBridgeRetry", function () { let etherVault: EtherVault; let libRetry: TestLibBridgeRetry; let badLibRetry: TestLibBridgeRetry; - let testLibData: TestLibBridgeData; + let testTaikoData: TestLibBridgeData; before(async function () { [owner, nonOwner, refundAddress, etherVaultOwner] = @@ -90,7 +90,7 @@ describe("LibBridgeRetry", function () { .connect(etherVaultOwner) .authorize(libRetry.address, true); - testLibData = await ( + testTaikoData = await ( await ethers.getContractFactory("TestLibBridgeData") ).deploy(); }); @@ -186,7 +186,7 @@ describe("LibBridgeRetry", function () { memo: "", }; - const signal = await testLibData.hashMessage(message); + const signal = await testTaikoData.hashMessage(message); await helpers.setStorageAt( badLibRetry.address, @@ -235,7 +235,7 @@ describe("LibBridgeRetry", function () { memo: "", }; - const signal = await testLibData.hashMessage(message); + const signal = await testTaikoData.hashMessage(message); await helpers.setStorageAt( libRetry.address, @@ -287,7 +287,7 @@ describe("LibBridgeRetry", function () { memo: "", }; - const signal = await testLibData.hashMessage(message); + const signal = await testTaikoData.hashMessage(message); await helpers.setStorageAt( libRetry.address, @@ -341,7 +341,7 @@ describe("LibBridgeRetry", function () { memo: "", }; - const signal = await testLibData.hashMessage(message); + const signal = await testTaikoData.hashMessage(message); await helpers.setStorageAt( libRetry.address, diff --git a/packages/relayer/TaikoL1.json b/packages/relayer/TaikoL1.json index d2f0a18e6f7..912fabe60c6 100644 --- a/packages/relayer/TaikoL1.json +++ b/packages/relayer/TaikoL1.json @@ -92,7 +92,7 @@ } ], "indexed": false, - "internalType": "struct LibData.BlockMetadata", + "internalType": "struct TaikoData.BlockMetadata", "name": "meta", "type": "tuple" } @@ -415,7 +415,7 @@ "type": "bytes32" } ], - "internalType": "struct LibData.ProposedBlock", + "internalType": "struct TaikoData.ProposedBlock", "name": "", "type": "tuple" } diff --git a/packages/relayer/contracts/TaikoL1.go b/packages/relayer/contracts/TaikoL1.go index 1600a5e8f89..df384021d0e 100644 --- a/packages/relayer/contracts/TaikoL1.go +++ b/packages/relayer/contracts/TaikoL1.go @@ -26,8 +26,8 @@ var ( _ = event.NewSubscription ) -// LibDataBlockMetadata is an auto generated low-level Go binding around an user-defined struct. -type LibDataBlockMetadata struct { +// TaikoDataBlockMetadata is an auto generated low-level Go binding around an user-defined struct. +type TaikoDataBlockMetadata struct { Id *big.Int L1Height *big.Int L1Hash [32]byte @@ -41,13 +41,13 @@ type LibDataBlockMetadata struct { CommitSlot uint64 } -// LibDataProposedBlock is an auto generated low-level Go binding around an user-defined struct. -type LibDataProposedBlock struct { +// TaikoDataProposedBlock is an auto generated low-level Go binding around an user-defined struct. +type TaikoDataProposedBlock struct { MetaHash [32]byte } // TaikoL1ABI is the input ABI used to generate the binding from. -const TaikoL1ABI = "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"commitSlot\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"commitHeight\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"commitHash\",\"type\":\"bytes32\"}],\"name\":\"BlockCommitted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"l1Height\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"l1Hash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"beneficiary\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"txListHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"mixHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"gasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"commitHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"commitSlot\",\"type\":\"uint64\"}],\"indexed\":false,\"internalType\":\"structLibData.BlockMetadata\",\"name\":\"meta\",\"type\":\"tuple\"}],\"name\":\"BlockProposed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"provenAt\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"}],\"name\":\"BlockProven\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"name\":\"BlockVerified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"halted\",\"type\":\"bool\"}],\"name\":\"Halted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"height\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"srcHeight\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"srcHash\",\"type\":\"bytes32\"}],\"name\":\"HeaderSynced\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"whitelisted\",\"type\":\"bool\"}],\"name\":\"ProverWhitelisted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"addressManager\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"commitSlot\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"commitHash\",\"type\":\"bytes32\"}],\"name\":\"commitBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"}],\"name\":\"getBlockProvers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConstants\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"bytes4\",\"name\":\"\",\"type\":\"bytes4\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLatestSyncedHeader\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getProposedBlock\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"metaHash\",\"type\":\"bytes32\"}],\"internalType\":\"structLibData.ProposedBlock\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStateVariables\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"number\",\"type\":\"uint256\"}],\"name\":\"getSyncedHeader\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"toHalt\",\"type\":\"bool\"}],\"name\":\"halt\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addressManager\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"_genesisBlockHash\",\"type\":\"bytes32\"}],\"name\":\"init\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"commitSlot\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"commitHeight\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"commitHash\",\"type\":\"bytes32\"}],\"name\":\"isCommitValid\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isHalted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"}],\"name\":\"isProverWhitelisted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"inputs\",\"type\":\"bytes[]\"}],\"name\":\"proposeBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockIndex\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"inputs\",\"type\":\"bytes[]\"}],\"name\":\"proveBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockIndex\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"inputs\",\"type\":\"bytes[]\"}],\"name\":\"proveBlockInvalid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"k\",\"type\":\"uint8\"}],\"name\":\"signWithGoldenTouch\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"r\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"s\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"state\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"statusBits\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"genesisHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"latestVerifiedHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"latestVerifiedId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nextBlockId\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"maxBlocks\",\"type\":\"uint256\"}],\"name\":\"verifyBlocks\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"whitelisted\",\"type\":\"bool\"}],\"name\":\"whitelistProver\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" +const TaikoL1ABI = "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"commitSlot\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"commitHeight\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"commitHash\",\"type\":\"bytes32\"}],\"name\":\"BlockCommitted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"l1Height\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"l1Hash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"beneficiary\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"txListHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"mixHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"gasLimit\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"commitHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"commitSlot\",\"type\":\"uint64\"}],\"indexed\":false,\"internalType\":\"structTaikoData.BlockMetadata\",\"name\":\"meta\",\"type\":\"tuple\"}],\"name\":\"BlockProposed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"provenAt\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"}],\"name\":\"BlockProven\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"name\":\"BlockVerified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"halted\",\"type\":\"bool\"}],\"name\":\"Halted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"height\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"srcHeight\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"srcHash\",\"type\":\"bytes32\"}],\"name\":\"HeaderSynced\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"whitelisted\",\"type\":\"bool\"}],\"name\":\"ProverWhitelisted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"addressManager\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"commitSlot\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"commitHash\",\"type\":\"bytes32\"}],\"name\":\"commitBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"}],\"name\":\"getBlockProvers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConstants\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"bytes4\",\"name\":\"\",\"type\":\"bytes4\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLatestSyncedHeader\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getProposedBlock\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"metaHash\",\"type\":\"bytes32\"}],\"internalType\":\"structTaikoData.ProposedBlock\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStateVariables\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"number\",\"type\":\"uint256\"}],\"name\":\"getSyncedHeader\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"toHalt\",\"type\":\"bool\"}],\"name\":\"halt\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addressManager\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"_genesisBlockHash\",\"type\":\"bytes32\"}],\"name\":\"init\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"commitSlot\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"commitHeight\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"commitHash\",\"type\":\"bytes32\"}],\"name\":\"isCommitValid\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isHalted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"}],\"name\":\"isProverWhitelisted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"inputs\",\"type\":\"bytes[]\"}],\"name\":\"proposeBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockIndex\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"inputs\",\"type\":\"bytes[]\"}],\"name\":\"proveBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockIndex\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"inputs\",\"type\":\"bytes[]\"}],\"name\":\"proveBlockInvalid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"k\",\"type\":\"uint8\"}],\"name\":\"signWithGoldenTouch\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"r\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"s\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"state\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"statusBits\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"genesisHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"latestVerifiedHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"latestVerifiedId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nextBlockId\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"maxBlocks\",\"type\":\"uint256\"}],\"name\":\"verifyBlocks\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"whitelisted\",\"type\":\"bool\"}],\"name\":\"whitelistProver\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" // TaikoL1 is an auto generated Go binding around an Ethereum contract. type TaikoL1 struct { @@ -331,15 +331,15 @@ func (_TaikoL1 *TaikoL1CallerSession) GetLatestSyncedHeader() ([32]byte, error) // GetProposedBlock is a free data retrieval call binding the contract method 0x8972b10c. // // Solidity: function getProposedBlock(uint256 id) view returns((bytes32)) -func (_TaikoL1 *TaikoL1Caller) GetProposedBlock(opts *bind.CallOpts, id *big.Int) (LibDataProposedBlock, error) { +func (_TaikoL1 *TaikoL1Caller) GetProposedBlock(opts *bind.CallOpts, id *big.Int) (TaikoDataProposedBlock, error) { var out []interface{} err := _TaikoL1.contract.Call(opts, &out, "getProposedBlock", id) if err != nil { - return *new(LibDataProposedBlock), err + return *new(TaikoDataProposedBlock), err } - out0 := *abi.ConvertType(out[0], new(LibDataProposedBlock)).(*LibDataProposedBlock) + out0 := *abi.ConvertType(out[0], new(TaikoDataProposedBlock)).(*TaikoDataProposedBlock) return out0, err @@ -348,14 +348,14 @@ func (_TaikoL1 *TaikoL1Caller) GetProposedBlock(opts *bind.CallOpts, id *big.Int // GetProposedBlock is a free data retrieval call binding the contract method 0x8972b10c. // // Solidity: function getProposedBlock(uint256 id) view returns((bytes32)) -func (_TaikoL1 *TaikoL1Session) GetProposedBlock(id *big.Int) (LibDataProposedBlock, error) { +func (_TaikoL1 *TaikoL1Session) GetProposedBlock(id *big.Int) (TaikoDataProposedBlock, error) { return _TaikoL1.Contract.GetProposedBlock(&_TaikoL1.CallOpts, id) } // GetProposedBlock is a free data retrieval call binding the contract method 0x8972b10c. // // Solidity: function getProposedBlock(uint256 id) view returns((bytes32)) -func (_TaikoL1 *TaikoL1CallerSession) GetProposedBlock(id *big.Int) (LibDataProposedBlock, error) { +func (_TaikoL1 *TaikoL1CallerSession) GetProposedBlock(id *big.Int) (TaikoDataProposedBlock, error) { return _TaikoL1.Contract.GetProposedBlock(&_TaikoL1.CallOpts, id) } @@ -1136,7 +1136,7 @@ func (it *TaikoL1BlockProposedIterator) Close() error { // TaikoL1BlockProposed represents a BlockProposed event raised by the TaikoL1 contract. type TaikoL1BlockProposed struct { Id *big.Int - Meta LibDataBlockMetadata + Meta TaikoDataBlockMetadata Raw types.Log // Blockchain specific contextual infos } diff --git a/packages/website/docs/reference/smart-contracts/L1/TaikoL1.md b/packages/website/docs/reference/smart-contracts/L1/TaikoL1.md index a351c69e3f3..3a52bde5cf9 100644 --- a/packages/website/docs/reference/smart-contracts/L1/TaikoL1.md +++ b/packages/website/docs/reference/smart-contracts/L1/TaikoL1.md @@ -3,13 +3,13 @@ ### state ```solidity -struct LibData.State state +struct TaikoData.State state ``` ### tentative ```solidity -struct LibData.TentativeState tentative +struct TaikoData.TentativeState tentative ``` ### init @@ -217,7 +217,7 @@ function isCommitValid(uint256 commitSlot, uint256 commitHeight, bytes32 commitH ### getProposedBlock ```solidity -function getProposedBlock(uint256 id) public view returns (struct LibData.ProposedBlock) +function getProposedBlock(uint256 id) public view returns (struct TaikoData.ProposedBlock) ``` ### getSyncedHeader From dcb9ab7d233f03b7c678f6941068ba96cf90d6d1 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 3 Jan 2023 12:46:22 +0800 Subject: [PATCH 43/57] fixed an issue --- packages/protocol/contracts/L1/libs/LibProposing.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 562eed41157..00e2b4c5469 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -85,7 +85,7 @@ library LibProposing { bytes calldata txList = inputs[1]; // perform validation and populate some fields require( - txList.length > 0 && + txList.length >= 0 && txList.length <= config.maxBytesPerTxList && meta.txListHash == txList.hashTxList(), "L1:txList" From 972f527f87a973b13fb314d72ac3153ffe744657 Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Tue, 3 Jan 2023 12:49:28 +0800 Subject: [PATCH 44/57] refactor(protocol): clean up permission code (whitelisting disabled) (#2096) --- packages/protocol/contracts/L1/TaikoData.sol | 9 --- .../protocol/contracts/L1/TaikoEvents.sol | 6 -- packages/protocol/contracts/L1/TaikoL1.sol | 81 +------------------ .../contracts/L1/libs/LibProposing.sol | 12 +-- .../protocol/contracts/L1/libs/LibProving.sol | 13 +-- .../protocol/contracts/L1/libs/LibUtils.sol | 60 -------------- 6 files changed, 4 insertions(+), 177 deletions(-) diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index bb54cbb8e2e..66c01c1c79b 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -105,13 +105,4 @@ library TaikoData { // Reserved uint256[42] __gap; } - - struct TentativeState { - mapping(address => bool) proposers; // Whitelisted proposers - mapping(address => bool) provers; // Whitelisted provers - bool whitelistProposers; - bool whitelistProvers; - // // Reserved - uint256[46] __gap; - } } diff --git a/packages/protocol/contracts/L1/TaikoEvents.sol b/packages/protocol/contracts/L1/TaikoEvents.sol index ebd97dd9211..ff39318598e 100644 --- a/packages/protocol/contracts/L1/TaikoEvents.sol +++ b/packages/protocol/contracts/L1/TaikoEvents.sol @@ -32,11 +32,5 @@ abstract contract TaikoEvents { address prover ); - event WhitelistingEnabled(bool whitelistProposers, bool whitelistProvers); - - event ProposerWhitelisted(address indexed prover, bool whitelisted); - - event ProverWhitelisted(address indexed prover, bool whitelisted); - event Halted(bool halted); } diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 1232dcda875..2305a40abc1 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -26,8 +26,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, TaikoEvents { using LibUtils for TaikoData.State; TaikoData.State public state; - TaikoData.TentativeState public tentative; - uint256[50] private __gap; + uint256[100] private __gap; function init( address _addressManager, @@ -40,9 +39,6 @@ contract TaikoL1 is EssentialContract, IHeaderSync, TaikoEvents { genesisBlockHash: _genesisBlockHash, feeBase: _feeBase }); - - tentative.whitelistProposers = false; - tentative.whitelistProvers = true; } /** @@ -88,7 +84,6 @@ contract TaikoL1 is EssentialContract, IHeaderSync, TaikoEvents { LibProposing.proposeBlock({ state: state, config: config, - tentative: tentative, resolver: AddressResolver(this), inputs: inputs }); @@ -123,7 +118,6 @@ contract TaikoL1 is EssentialContract, IHeaderSync, TaikoEvents { TaikoData.Config memory config = getConfig(); LibProving.proveBlock({ state: state, - tentative: tentative, config: config, resolver: AddressResolver(this), blockId: blockId, @@ -160,7 +154,6 @@ contract TaikoL1 is EssentialContract, IHeaderSync, TaikoEvents { LibProving.proveBlockInvalid({ state: state, - tentative: tentative, config: config, resolver: AddressResolver(this), blockId: blockId, @@ -190,56 +183,6 @@ contract TaikoL1 is EssentialContract, IHeaderSync, TaikoEvents { }); } - /** - * Enable or disable proposer and prover whitelisting - * @param whitelistProposers True to enable proposer whitelisting. - * @param whitelistProvers True to enable prover whitelisting. - */ - function enableWhitelisting( - bool whitelistProposers, - bool whitelistProvers - ) public onlyOwner { - LibUtils.enableWhitelisting({ - tentative: tentative, - whitelistProposers: whitelistProposers, - whitelistProvers: whitelistProvers - }); - } - - /** - * Add or remove a proposer from the whitelist. - * - * @param proposer The proposer to be added or removed. - * @param whitelisted True to add; remove otherwise. - */ - function whitelistProposer( - address proposer, - bool whitelisted - ) public onlyOwner { - LibUtils.whitelistProposer({ - tentative: tentative, - proposer: proposer, - whitelisted: whitelisted - }); - } - - /** - * Add or remove a prover from the whitelist. - * - * @param prover The prover to be added or removed. - * @param whitelisted True to add; remove otherwise. - */ - function whitelistProver( - address prover, - bool whitelisted - ) public onlyOwner { - LibUtils.whitelistProver({ - tentative: tentative, - prover: prover, - whitelisted: whitelisted - }); - } - /** * Halt or resume the chain. * @param toHalt True to halt, false to resume. @@ -248,28 +191,6 @@ contract TaikoL1 is EssentialContract, IHeaderSync, TaikoEvents { LibUtils.halt(state, toHalt); } - /** - * Check whether a proposer is whitelisted. - * - * @param proposer The proposer. - * @return True if the proposer is whitelisted, false otherwise. - */ - function isProposerWhitelisted( - address proposer - ) public view returns (bool) { - return LibUtils.isProposerWhitelisted(tentative, proposer); - } - - /** - * Check whether a prover is whitelisted. - * - * @param prover The prover. - * @return True if the prover is whitelisted, false otherwise. - */ - function isProverWhitelisted(address prover) public view returns (bool) { - return LibUtils.isProverWhitelisted(tentative, prover); - } - function getBlockFee() public view returns (uint256) { (, uint fee, uint deposit) = LibProposing.getBlockFee( state, diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 00e2b4c5469..8525da78284 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -27,15 +27,6 @@ library LibProposing { ); event BlockProposed(uint256 indexed id, TaikoData.BlockMetadata meta); - modifier onlyWhitelistedProposer( - TaikoData.TentativeState storage tentative - ) { - if (tentative.whitelistProposers) { - require(tentative.proposers[msg.sender], "L1:whitelist"); - } - _; - } - function commitBlock( TaikoData.State storage state, TaikoData.Config memory config, @@ -63,10 +54,9 @@ library LibProposing { function proposeBlock( TaikoData.State storage state, TaikoData.Config memory config, - TaikoData.TentativeState storage tentative, AddressResolver resolver, bytes[] calldata inputs - ) public onlyWhitelistedProposer(tentative) { + ) public { assert(!LibUtils.isHalted(state)); require(inputs.length == 2, "L1:inputs:size"); diff --git a/packages/protocol/contracts/L1/libs/LibProving.sol b/packages/protocol/contracts/L1/libs/LibProving.sol index e1ccc47704b..87d601e4b60 100644 --- a/packages/protocol/contracts/L1/libs/LibProving.sol +++ b/packages/protocol/contracts/L1/libs/LibProving.sol @@ -50,21 +50,13 @@ library LibProving { address prover ); - modifier onlyWhitelistedProver(TaikoData.TentativeState storage tentative) { - if (tentative.whitelistProvers) { - require(tentative.provers[msg.sender], "L1:whitelist"); - } - _; - } - function proveBlock( TaikoData.State storage state, - TaikoData.TentativeState storage tentative, TaikoData.Config memory config, AddressResolver resolver, uint256 blockId, bytes[] calldata inputs - ) public onlyWhitelistedProver(tentative) { + ) public { assert(!LibUtils.isHalted(state)); // Check and decode inputs @@ -157,12 +149,11 @@ library LibProving { function proveBlockInvalid( TaikoData.State storage state, - TaikoData.TentativeState storage tentative, TaikoData.Config memory config, AddressResolver resolver, uint256 blockId, bytes[] calldata inputs - ) public onlyWhitelistedProver(tentative) { + ) public { assert(!LibUtils.isHalted(state)); // Check and decode inputs diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index 1b50ce7667d..84adbc26a63 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -21,52 +21,8 @@ library LibUtils { bytes32 public constant BLOCK_DEADEND_HASH = bytes32(uint256(1)); - event WhitelistingEnabled(bool whitelistProposers, bool whitelistProvers); - event ProposerWhitelisted(address indexed proposer, bool whitelisted); - event ProverWhitelisted(address indexed prover, bool whitelisted); event Halted(bool halted); - function enableWhitelisting( - TaikoData.TentativeState storage tentative, - bool whitelistProposers, - bool whitelistProvers - ) internal { - tentative.whitelistProposers = whitelistProvers; - tentative.whitelistProvers = whitelistProvers; - emit WhitelistingEnabled(whitelistProposers, whitelistProvers); - } - - function whitelistProposer( - TaikoData.TentativeState storage tentative, - address proposer, - bool whitelisted - ) internal { - assert(tentative.whitelistProposers); - require( - proposer != address(0) && - tentative.proposers[proposer] != whitelisted, - "L1:precondition" - ); - - tentative.proposers[proposer] = whitelisted; - emit ProposerWhitelisted(proposer, whitelisted); - } - - function whitelistProver( - TaikoData.TentativeState storage tentative, - address prover, - bool whitelisted - ) internal { - assert(tentative.whitelistProvers); - require( - prover != address(0) && tentative.provers[prover] != whitelisted, - "L1:precondition" - ); - - tentative.provers[prover] = whitelisted; - emit ProverWhitelisted(prover, whitelisted); - } - function halt(TaikoData.State storage state, bool toHalt) internal { require(isHalted(state) != toHalt, "L1:precondition"); setBit(state, MASK_HALT, toHalt); @@ -127,22 +83,6 @@ library LibUtils { return isBitOne(state, MASK_HALT); } - function isProposerWhitelisted( - TaikoData.TentativeState storage tentative, - address proposer - ) internal view returns (bool) { - assert(tentative.whitelistProposers); - return tentative.proposers[proposer]; - } - - function isProverWhitelisted( - TaikoData.TentativeState storage tentative, - address prover - ) internal view returns (bool) { - assert(tentative.whitelistProvers); - return tentative.provers[prover]; - } - // Implement "Incentive Multipliers", see the whitepaper. function getTimeAdjustedFee( TaikoData.State storage state, From cacf8d23c66f724c63551e97c2e279dd9c387259 Mon Sep 17 00:00:00 2001 From: shadab-taiko <108871478+shadab-taiko@users.noreply.github.com> Date: Tue, 27 Dec 2022 04:37:05 +0530 Subject: [PATCH 45/57] feat(bridge): change pending message for l2 to l1 (#486) --- .../src/components/ERC20Faucet.svelte | 72 ------------------- .../src/components/Transaction.svelte | 6 +- 2 files changed, 3 insertions(+), 75 deletions(-) delete mode 100644 packages/bridge-ui/src/components/ERC20Faucet.svelte diff --git a/packages/bridge-ui/src/components/ERC20Faucet.svelte b/packages/bridge-ui/src/components/ERC20Faucet.svelte deleted file mode 100644 index 77ea0ec016f..00000000000 --- a/packages/bridge-ui/src/components/ERC20Faucet.svelte +++ /dev/null @@ -1,72 +0,0 @@ - - -
- - -
- - - -

- You can request 1000 {$token.symbol}. {$token.symbol} is only available to - be minted on Ethereum A1. If you are on Taiko A1, your network will be changed - first. You must have a small amount of ETH in your Ethereum A1 wallet to - send the transaction. -

-
-
diff --git a/packages/bridge-ui/src/components/Transaction.svelte b/packages/bridge-ui/src/components/Transaction.svelte index 2ed40a0f354..e40029a24db 100644 --- a/packages/bridge-ui/src/components/Transaction.svelte +++ b/packages/bridge-ui/src/components/Transaction.svelte @@ -133,7 +133,7 @@ {#if !processable} - Pending... + Pending {fromChain.id === CHAIN_TKO.id ? '(2-3hrs)': ''}... {:else if !transaction.receipt && transaction.status === MessageStatus.New}
{:else if transaction.receipt && transaction.status === MessageStatus.New} await claim(transaction)} > Claim {:else if transaction.status === MessageStatus.Retriable} await claim(transaction)} > Retry From de61a6e214639f62ec5d3a305202e51aa880519e Mon Sep 17 00:00:00 2001 From: dave | d1onys1us <13951458+d1onys1us@users.noreply.github.com> Date: Tue, 27 Dec 2022 00:26:14 -0500 Subject: [PATCH 46/57] feat(website): create testnet docs (#428) --- .../_category_.json | 5 +- .../docs/alpha-1-testnet/configure-wallet.mdx | 25 +++++ .../docs/alpha-1-testnet/deploy-a-contract.md | 14 +++ .../alpha-1-testnet/explore-the-network.md | 44 +++++++++ .../website/docs/alpha-1-testnet/get-help.md | 12 +++ .../request-eth-from-faucet.md | 8 ++ .../docs/alpha-1-testnet/run-a-node.md | 13 +++ .../docs/alpha-1-testnet/start-here.md | 41 +++++++++ .../docs/alpha-1-testnet/submit-feedback.md | 9 ++ .../docs/alpha-1-testnet/use-the-bridge.md | 24 +++++ packages/website/docs/intro/_category_.json | 3 +- .../{faq/index.md => docs/intro/faq.md} | 8 +- packages/website/docs/intro/index.md | 10 +- .../{talks/index.md => docs/intro/talks.md} | 6 +- .../docs/{reference => intro}/whitepaper.md | 6 +- .../smart-contracts/L1/TaikoL1.md | 0 .../smart-contracts/L1/TkoToken.md | 0 .../smart-contracts/L2/TaikoL2.md | 0 .../smart-contracts/_category_.json | 2 +- .../smart-contracts/bridge/Bridge.md | 0 .../smart-contracts/bridge/BridgedERC20.md | 0 .../smart-contracts/bridge/EtherVault.md | 0 .../smart-contracts/bridge/IBridge.md | 0 .../smart-contracts/bridge/TokenVault.md | 0 .../smart-contracts/common/AddressResolver.md | 0 .../smart-contracts/common/ConfigManager.md | 0 .../smart-contracts/common/IAddressManager.md | 0 .../smart-contracts/common/IHeaderSync.md | 0 .../smart-contracts/common/IMintableERC20.md | 0 packages/website/docusaurus.config.js | 92 ++++++++++--------- .../AddEthereumChainButton/index.tsx | 64 +++++++++++++ .../website/src/components/Hero/index.tsx | 4 +- packages/website/src/pages/index.tsx | 1 + packages/website/static/img/metamask-fox.svg | 0 34 files changed, 335 insertions(+), 56 deletions(-) rename packages/website/docs/{reference => alpha-1-testnet}/_category_.json (54%) create mode 100644 packages/website/docs/alpha-1-testnet/configure-wallet.mdx create mode 100644 packages/website/docs/alpha-1-testnet/deploy-a-contract.md create mode 100644 packages/website/docs/alpha-1-testnet/explore-the-network.md create mode 100644 packages/website/docs/alpha-1-testnet/get-help.md create mode 100644 packages/website/docs/alpha-1-testnet/request-eth-from-faucet.md create mode 100644 packages/website/docs/alpha-1-testnet/run-a-node.md create mode 100644 packages/website/docs/alpha-1-testnet/start-here.md create mode 100644 packages/website/docs/alpha-1-testnet/submit-feedback.md create mode 100644 packages/website/docs/alpha-1-testnet/use-the-bridge.md rename packages/website/{faq/index.md => docs/intro/faq.md} (98%) rename packages/website/{talks/index.md => docs/intro/talks.md} (84%) rename packages/website/docs/{reference => intro}/whitepaper.md (70%) rename packages/website/docs/{reference => }/smart-contracts/L1/TaikoL1.md (100%) rename packages/website/docs/{reference => }/smart-contracts/L1/TkoToken.md (100%) rename packages/website/docs/{reference => }/smart-contracts/L2/TaikoL2.md (100%) rename packages/website/docs/{reference => }/smart-contracts/_category_.json (65%) rename packages/website/docs/{reference => }/smart-contracts/bridge/Bridge.md (100%) rename packages/website/docs/{reference => }/smart-contracts/bridge/BridgedERC20.md (100%) rename packages/website/docs/{reference => }/smart-contracts/bridge/EtherVault.md (100%) rename packages/website/docs/{reference => }/smart-contracts/bridge/IBridge.md (100%) rename packages/website/docs/{reference => }/smart-contracts/bridge/TokenVault.md (100%) rename packages/website/docs/{reference => }/smart-contracts/common/AddressResolver.md (100%) rename packages/website/docs/{reference => }/smart-contracts/common/ConfigManager.md (100%) rename packages/website/docs/{reference => }/smart-contracts/common/IAddressManager.md (100%) rename packages/website/docs/{reference => }/smart-contracts/common/IHeaderSync.md (100%) rename packages/website/docs/{reference => }/smart-contracts/common/IMintableERC20.md (100%) create mode 100644 packages/website/src/components/AddEthereumChainButton/index.tsx create mode 100644 packages/website/static/img/metamask-fox.svg diff --git a/packages/website/docs/reference/_category_.json b/packages/website/docs/alpha-1-testnet/_category_.json similarity index 54% rename from packages/website/docs/reference/_category_.json rename to packages/website/docs/alpha-1-testnet/_category_.json index b49710abb9e..31f1bdb4e09 100644 --- a/packages/website/docs/reference/_category_.json +++ b/packages/website/docs/alpha-1-testnet/_category_.json @@ -1,7 +1,8 @@ { - "label": "Reference", + "label": "Alpha-1 Testnet Guide", "collapsed": false, "link": { "type": "generated-index" - } + }, + "position": 2 } diff --git a/packages/website/docs/alpha-1-testnet/configure-wallet.mdx b/packages/website/docs/alpha-1-testnet/configure-wallet.mdx new file mode 100644 index 00000000000..3e94507d1aa --- /dev/null +++ b/packages/website/docs/alpha-1-testnet/configure-wallet.mdx @@ -0,0 +1,25 @@ +--- +sidebar_position: 1 +--- + +import AddEthereumChainButton from "../../src/components/AddEthereumChainButton"; + +# ➕ Add networks to wallet + +## Automatically add networks + +Use the buttons below to add Ethereum A1 and Taiko A1 to any wallet that supports `wallet_addEthereumChain` (e.g., MetaMask). + +| Network | Add to wallet | +| ----------- | ------------------------------------------------------------------ | +| Ethereum A1 | | +| Taiko A1 | | + +## Manually add networks + +Use this RPC configuration to add Ethereum A1 and Taiko A1 to other wallets. + +| Network | Chain ID | RPC URL | Symbol | Block Explorer URL | +| ----------- | -------- | -------------------------- | ------ | ------------------------------- | +| Ethereum A1 | 31338 | https://l1rpc.a1.taiko.xyz | ETH | https://l1explorer.a1.taiko.xyz | +| Taiko A1 | 167003 | https://l2rpc.a1.taiko.xyz | ETH | https://l2explorer.a1.taiko.xyz | diff --git a/packages/website/docs/alpha-1-testnet/deploy-a-contract.md b/packages/website/docs/alpha-1-testnet/deploy-a-contract.md new file mode 100644 index 00000000000..e4cd3564285 --- /dev/null +++ b/packages/website/docs/alpha-1-testnet/deploy-a-contract.md @@ -0,0 +1,14 @@ +--- +sidebar_position: 4 +--- + +# 🚀 Deploy a contract + +We will deploy a smart contract to Taiko A1 using Foundry. + +1. Follow the Foundry Book to install Foundry and init the default project: https://book.getfoundry.sh/getting-started/first-steps +2. From `~/hello_foundry` run `forge create --legacy --rpc-url https://l2rpc.a1.taiko.xyz --private-key src/Counter.sol:Counter` (replace `` with the private key of the account deploying the contract) + +We are using the `--legacy` flag because EIP-1559 is currently disabled on Taiko. We have plans to re-enable it in the future. + +You can use the block explorer to verify that the contract was deployed: https://l2explorer.a1.taiko.xyz/ diff --git a/packages/website/docs/alpha-1-testnet/explore-the-network.md b/packages/website/docs/alpha-1-testnet/explore-the-network.md new file mode 100644 index 00000000000..5f0d42ad368 --- /dev/null +++ b/packages/website/docs/alpha-1-testnet/explore-the-network.md @@ -0,0 +1,44 @@ +--- +sidebar_position: 7 +--- + +# 🔍 Explore the network + +Taiko's Alpha-1 testnet consists of L1 / L2 nodes with all [Taiko protocol contracts](/docs/category/contract-documentation) deployed. The mining interval of the L1 node is set to 12 seconds. + +## Endpoints + +### L1 + +- **Block Explorer:** +- **HTTP RPC Endpoint:** +- **Web Socket RPC Endpoint:** +- **ETH faucet:** +- **Chain ID:** `31338` + +### L2 + +- **Block Explorer:** +- **HTTP RPC Endpoint:** +- **Web Socket RPC Endpoint:** +- **ETH faucet:** +- **Chain ID:** `167003` + +## Contract addresses + +### L1 + +- **TaikoL1:** `0x7B3AF414448ba906f02a1CA307C56c4ADFF27ce7` +- **TokenVault:** `0xD0dfd5baCf160B97C8eE3ecb463F18c08673160c` +- **Bridge:** `0x3612E284D763f42f5E4CB72B1602b23DAEC3cA60` + +### L2 + +- **TaikoL2:** `0x0000777700000000000000000000000000000001` +- **TokenVault:** `0x0000777700000000000000000000000000000002` +- **EtherVault:** `0x0000777700000000000000000000000000000003` +- **Bridge:** `0x0000777700000000000000000000000000000004` + +## Cron job + +There will be a cron job service that proposes empty blocks periodically (every 2 minutes). diff --git a/packages/website/docs/alpha-1-testnet/get-help.md b/packages/website/docs/alpha-1-testnet/get-help.md new file mode 100644 index 00000000000..d10f4528f80 --- /dev/null +++ b/packages/website/docs/alpha-1-testnet/get-help.md @@ -0,0 +1,12 @@ +--- +sidebar_position: 8 +--- + +# ❓ Get help + +Here are two places for you to get help: + +- [Post your question on GitHub Discussions](https://github.com/orgs/taikoxyz/discussions/new?category=questions) +- [Chat with us on Discord](https://discord.gg/taikoxyz) + +GitHub Discussions is preferred because your question might help others who are also running into the same issue. Conversely, you might find the answer to your problem here! diff --git a/packages/website/docs/alpha-1-testnet/request-eth-from-faucet.md b/packages/website/docs/alpha-1-testnet/request-eth-from-faucet.md new file mode 100644 index 00000000000..c8aaf33de79 --- /dev/null +++ b/packages/website/docs/alpha-1-testnet/request-eth-from-faucet.md @@ -0,0 +1,8 @@ +--- +sidebar_position: 2 +--- + +# 💧 Request ETH from faucet + +- Request ETH from Ethereum A1: https://l1faucet.a1.taiko.xyz/. +- Request ETH from Taiko A1: https://l2faucet.a1.taiko.xyz/. diff --git a/packages/website/docs/alpha-1-testnet/run-a-node.md b/packages/website/docs/alpha-1-testnet/run-a-node.md new file mode 100644 index 00000000000..13ec107832a --- /dev/null +++ b/packages/website/docs/alpha-1-testnet/run-a-node.md @@ -0,0 +1,13 @@ +--- +sidebar_position: 5 +--- + +# 🌐 Run a node + +This is arguably the most important part of the testnet! Taiko is fully decentralized and relies on the community to run nodes. + +Follow the guide at [simple-taiko-node/README.md](https://github.com/taikoxyz/simple-taiko-node/blob/main/README.md) to get started. + +:::note +You will only be able to run a node regularly or as a proposer. In this testnet we are not running any provers. +::: diff --git a/packages/website/docs/alpha-1-testnet/start-here.md b/packages/website/docs/alpha-1-testnet/start-here.md new file mode 100644 index 00000000000..0a63675a742 --- /dev/null +++ b/packages/website/docs/alpha-1-testnet/start-here.md @@ -0,0 +1,41 @@ +--- +sidebar_position: 0 +--- + +# 👋 Start here + +Welcome to Taiko's Alpha-1 testnet, **Snæfellsjökull**. There are two networks involved in the testnet, which we'll define: + +- **Taiko A1** is the Alpha-1 testnet release of Taiko — a fully decentralized, Ethereum-equivalent ZK-Rollup. +- **Ethereum A1** is Taiko's privately deployed Ethereum network, which serves as the L1 for Taiko's Alpha-1 testnet. + +## What can I do? + +In this testnet, you can: + +- 🌉 Bridge tokens between Ethereum A1 and Taiko A1. +- 🤝 Send transactions (e.g., deploy contracts). +- 🌐 Run a node — this makes _you_ a part of Taiko! +- 📝 Submit feedback and be forever loved. +- 🥇 Earn a POAP by using your wallet to complete any 2 of the 3 following actions by January 31st 23:59 UTC: + - Use the bridge + - Contract interaction (e.g., interact w/ dapp, deploy a contract) + - Transfer between accounts + +## Create a feedback log + +Create a feedback log by clicking [here](https://github.com/orgs/taikoxyz/discussions/new?category=feedback&title=Testnet%20feedback%20form&body=%23+Friction+log%0D%0A-+TODO%0D%0A%0D%0A%23+Other+notes%0D%0A-+TODO%0D%0A), and use this to write down any [friction points, bugs, suggestions, etc.] while testing Taiko. + +Once you are finished testing the network, please submit the feedback log to help improve Taiko! + +## Navigating Snæfellsjökull + +All of the relevant links can be found in this site's navigation. The relevant links are: + +| Link | Purpose | +| ----------------------------------------------- | -------------------------------------------------- | +| [Bridge](https://bridge.a1.taiko.xyz/) | Bridge ETH/tokens between Ethereum A1 and Taiko A1 | +| [L1 faucet](https://l1faucet.a1.taiko.xyz/) | For receiving ETH on Ethereum A1 | +| [L2 faucet](https://l2faucet.a1.taiko.xyz/) | For receiving ETH on Taiko A1 | +| [L1 explorer](https://l1explorer.a1.taiko.xyz/) | Explore blocks on Ethereum A1 | +| [L2 explorer](https://l2explorer.a1.taiko.xyz/) | Explore blocks on Taiko A1 | diff --git a/packages/website/docs/alpha-1-testnet/submit-feedback.md b/packages/website/docs/alpha-1-testnet/submit-feedback.md new file mode 100644 index 00000000000..ad38c9b9727 --- /dev/null +++ b/packages/website/docs/alpha-1-testnet/submit-feedback.md @@ -0,0 +1,9 @@ +--- +sidebar_position: 7 +--- + +# 📝 Submit feedback + +Submit your [feedback log](/docs/alpha-1-testnet/start-here#create-a-feedback-log) to [GitHub Discussions](https://github.com/orgs/taikoxyz/discussions). + +Thank you for participating in the Snæfellsjökull testnet! 🌋 diff --git a/packages/website/docs/alpha-1-testnet/use-the-bridge.md b/packages/website/docs/alpha-1-testnet/use-the-bridge.md new file mode 100644 index 00000000000..c1f21d1af11 --- /dev/null +++ b/packages/website/docs/alpha-1-testnet/use-the-bridge.md @@ -0,0 +1,24 @@ +--- +sidebar_position: 3 +--- + +# 🌉 Use the bridge + +The bridge contract can be found: + +- Deployed on L1: https://l1explorer.a1.taiko.xyz/address/0x3612E284D763f42f5E4CB72B1602b23DAEC3cA60 +- Deployed on L2: https://l2explorer.a1.taiko.xyz/address/0x0000777700000000000000000000000000000002 + +## Test the bridge + +Use the [bridge](https://bridge.a1.taiko.xyz/) for the following actions: + +- Bridge ETH from Ethereum A1 to Taiko A1 + +## Bridge contract explained + +Read the bridge documentation on our GitHub: https://github.com/taikoxyz/taiko-mono/tree/main/packages/protocol/contracts/bridge. + +## Why is my L2 -> L1 transfer taking so long? + +The transfer from L2 to L1 can take a while because Taiko has a several hours delay in syncing block headers to allow uncle proof generation time, and we need the synced header to match so the Merkle proof of the message being sent on L2 is valid on L1. diff --git a/packages/website/docs/intro/_category_.json b/packages/website/docs/intro/_category_.json index 18d2f319168..efdf4817727 100644 --- a/packages/website/docs/intro/_category_.json +++ b/packages/website/docs/intro/_category_.json @@ -1,6 +1,7 @@ { - "label": "Introduction", + "label": "Learn", "collapsed": false, + "position": 1, "link": { "type": "generated-index" } diff --git a/packages/website/faq/index.md b/packages/website/docs/intro/faq.md similarity index 98% rename from packages/website/faq/index.md rename to packages/website/docs/intro/faq.md index da02f7c4750..cafbbf9aa23 100644 --- a/packages/website/faq/index.md +++ b/packages/website/docs/intro/faq.md @@ -1,4 +1,8 @@ -# Frequently Asked Questions +--- +sidebar_position: 4 +--- + +# FAQs ## What is Taiko? @@ -65,7 +69,7 @@ There are 2 types of zero-knowledge proofs: ZK-SNARKs and ZK-STARKs. Taiko uses ## What does "Taiko" mean? -It comes from an old Chinese saying 一鼓作气 (Yīgǔzuòqì). The literal meaning is that the first drum beat arouses courage. The implied meaning of the idiom is to accomplish a task or goal in one intense effort. +It comes from an old Chinese saying 一鼓作气 (Yīgǔzuòqì). The literal meaning is that the first drum beat arouses courage. The implied meaning of the idiom is to accomplish a task or goal in one intense effort. Taiko (太鼓) is the Japanese term for a drum. For us, Taiko is the "drum" that arouses courage and leads to accomplishing goals. diff --git a/packages/website/docs/intro/index.md b/packages/website/docs/intro/index.md index 7a39aa8557e..336d41e24ef 100644 --- a/packages/website/docs/intro/index.md +++ b/packages/website/docs/intro/index.md @@ -2,11 +2,11 @@ sidebar_position: 1 --- -# Quickstart +# What is Taiko? -Welcome to Taiko! 🥁 +Taiko is a fully decentralized, Ethereum-equivalent ZK-Rollup. This is also referred to as a [Type 1 ZK-EVM](https://mirror.xyz/labs.taiko.eth/w7NSKDeKfJoEy0p89I9feixKfdK-20JgWF9HZzxfeBo). -## Learn about Taiko +## Learn more about Taiko Here are the best places to learn about Taiko. @@ -15,8 +15,8 @@ Here are the best places to learn about Taiko. - [Introduction to Taiko](https://mirror.xyz/labs.taiko.eth/oRy3ZZ_4-6IEQcuLCMMlxvdH6E-T3_H7UwYVzGDsgf4) - [The Type 1 ZK-EVM](https://mirror.xyz/labs.taiko.eth/w7NSKDeKfJoEy0p89I9feixKfdK-20JgWF9HZzxfeBo) - [Whitepaper](https://taikoxyz.github.io/taiko-mono/taiko-whitepaper.pdf) -- [Contract documentation](/docs/category/smart-contracts/) -- [Frequently Asked Questions (FAQ)](/faq/) +- [Contract documentation](/docs/category/contract-documentation/) +- [Frequently Asked Questions (FAQ)](/docs/intro/faq.md) ### Follow us diff --git a/packages/website/talks/index.md b/packages/website/docs/intro/talks.md similarity index 84% rename from packages/website/talks/index.md rename to packages/website/docs/intro/talks.md index f3cd1512abe..174cea78371 100644 --- a/packages/website/talks/index.md +++ b/packages/website/docs/intro/talks.md @@ -1,4 +1,8 @@ -# Talks +--- +sidebar_position: 3 +--- + +# Presentations - `2022-11-01`: [Taiko ZK-EVM: Layer 2 Finality](https://hackmd.io/@taikolabs/HkN7GR64i) (Old Friends Reunion - ETH Lisbon) - `2022-10-10`: [Taiko ZK-EVM: Overview and Optimizations](https://hackmd.io/@taikolabs/S1haywHIj) (Rollup Day - Devcon) diff --git a/packages/website/docs/reference/whitepaper.md b/packages/website/docs/intro/whitepaper.md similarity index 70% rename from packages/website/docs/reference/whitepaper.md rename to packages/website/docs/intro/whitepaper.md index 2150c7b0a45..0945340c5e3 100644 --- a/packages/website/docs/reference/whitepaper.md +++ b/packages/website/docs/intro/whitepaper.md @@ -1,3 +1,7 @@ -# Whitepaper +--- +sidebar_position: 2 +--- + +# Read the whitepaper Read about the Taiko protocol in depth with the [whitepaper](https://taikoxyz.github.io/taiko-mono/taiko-whitepaper.pdf). diff --git a/packages/website/docs/reference/smart-contracts/L1/TaikoL1.md b/packages/website/docs/smart-contracts/L1/TaikoL1.md similarity index 100% rename from packages/website/docs/reference/smart-contracts/L1/TaikoL1.md rename to packages/website/docs/smart-contracts/L1/TaikoL1.md diff --git a/packages/website/docs/reference/smart-contracts/L1/TkoToken.md b/packages/website/docs/smart-contracts/L1/TkoToken.md similarity index 100% rename from packages/website/docs/reference/smart-contracts/L1/TkoToken.md rename to packages/website/docs/smart-contracts/L1/TkoToken.md diff --git a/packages/website/docs/reference/smart-contracts/L2/TaikoL2.md b/packages/website/docs/smart-contracts/L2/TaikoL2.md similarity index 100% rename from packages/website/docs/reference/smart-contracts/L2/TaikoL2.md rename to packages/website/docs/smart-contracts/L2/TaikoL2.md diff --git a/packages/website/docs/reference/smart-contracts/_category_.json b/packages/website/docs/smart-contracts/_category_.json similarity index 65% rename from packages/website/docs/reference/smart-contracts/_category_.json rename to packages/website/docs/smart-contracts/_category_.json index d33e5f152f7..7c0061995d3 100644 --- a/packages/website/docs/reference/smart-contracts/_category_.json +++ b/packages/website/docs/smart-contracts/_category_.json @@ -1,5 +1,5 @@ { - "label": "Smart contracts", + "label": "Contract documentation", "collapsed": true, "link": { "type": "generated-index" diff --git a/packages/website/docs/reference/smart-contracts/bridge/Bridge.md b/packages/website/docs/smart-contracts/bridge/Bridge.md similarity index 100% rename from packages/website/docs/reference/smart-contracts/bridge/Bridge.md rename to packages/website/docs/smart-contracts/bridge/Bridge.md diff --git a/packages/website/docs/reference/smart-contracts/bridge/BridgedERC20.md b/packages/website/docs/smart-contracts/bridge/BridgedERC20.md similarity index 100% rename from packages/website/docs/reference/smart-contracts/bridge/BridgedERC20.md rename to packages/website/docs/smart-contracts/bridge/BridgedERC20.md diff --git a/packages/website/docs/reference/smart-contracts/bridge/EtherVault.md b/packages/website/docs/smart-contracts/bridge/EtherVault.md similarity index 100% rename from packages/website/docs/reference/smart-contracts/bridge/EtherVault.md rename to packages/website/docs/smart-contracts/bridge/EtherVault.md diff --git a/packages/website/docs/reference/smart-contracts/bridge/IBridge.md b/packages/website/docs/smart-contracts/bridge/IBridge.md similarity index 100% rename from packages/website/docs/reference/smart-contracts/bridge/IBridge.md rename to packages/website/docs/smart-contracts/bridge/IBridge.md diff --git a/packages/website/docs/reference/smart-contracts/bridge/TokenVault.md b/packages/website/docs/smart-contracts/bridge/TokenVault.md similarity index 100% rename from packages/website/docs/reference/smart-contracts/bridge/TokenVault.md rename to packages/website/docs/smart-contracts/bridge/TokenVault.md diff --git a/packages/website/docs/reference/smart-contracts/common/AddressResolver.md b/packages/website/docs/smart-contracts/common/AddressResolver.md similarity index 100% rename from packages/website/docs/reference/smart-contracts/common/AddressResolver.md rename to packages/website/docs/smart-contracts/common/AddressResolver.md diff --git a/packages/website/docs/reference/smart-contracts/common/ConfigManager.md b/packages/website/docs/smart-contracts/common/ConfigManager.md similarity index 100% rename from packages/website/docs/reference/smart-contracts/common/ConfigManager.md rename to packages/website/docs/smart-contracts/common/ConfigManager.md diff --git a/packages/website/docs/reference/smart-contracts/common/IAddressManager.md b/packages/website/docs/smart-contracts/common/IAddressManager.md similarity index 100% rename from packages/website/docs/reference/smart-contracts/common/IAddressManager.md rename to packages/website/docs/smart-contracts/common/IAddressManager.md diff --git a/packages/website/docs/reference/smart-contracts/common/IHeaderSync.md b/packages/website/docs/smart-contracts/common/IHeaderSync.md similarity index 100% rename from packages/website/docs/reference/smart-contracts/common/IHeaderSync.md rename to packages/website/docs/smart-contracts/common/IHeaderSync.md diff --git a/packages/website/docs/reference/smart-contracts/common/IMintableERC20.md b/packages/website/docs/smart-contracts/common/IMintableERC20.md similarity index 100% rename from packages/website/docs/reference/smart-contracts/common/IMintableERC20.md rename to packages/website/docs/smart-contracts/common/IMintableERC20.md diff --git a/packages/website/docusaurus.config.js b/packages/website/docusaurus.config.js index 95de7e7bd9f..e67036a6a57 100644 --- a/packages/website/docusaurus.config.js +++ b/packages/website/docusaurus.config.js @@ -24,28 +24,7 @@ const config = { locales: ["en"], }, - plugins: [ - [ - "content-docs", - { - id: "talks", - path: "talks", - routeBasePath: "talks", - editUrl: - "https://github.com/taikoxyz/taiko-mono/tree/main/packages/website/", - }, - ], - [ - "content-docs", - { - id: "faq", - path: "faq", - routeBasePath: "faq", - editUrl: - "https://github.com/taikoxyz/taiko-mono/tree/main/packages/website/", - }, - ], - ], + plugins: [], presets: [ [ @@ -81,6 +60,14 @@ const config = { themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ ({ + announcementBar: { + id: "alpha-1-testnet", + content: + 'Snæfellsjökull is erupting 🌋 start here', + backgroundColor: "#fafbfc", + textColor: "#171717", + isCloseable: false, + }, colorMode: { defaultMode: "light", respectPrefersColorScheme: false, @@ -94,28 +81,47 @@ const config = { items: [ { to: "docs/intro", - label: "Learn", - position: "left", + label: "Docs", }, { href: "https://mirror.xyz/labs.taiko.eth", label: "Blog", - position: "left", }, { - href: "https://github.com/taikoxyz/taiko-mono/discussions", - label: "Discussions", - position: "left", + href: "https://github.com/orgs/taikoxyz/discussions", + label: "Discuss", + }, + { + href: "https://bridge.a1.taiko.xyz/", + label: "Bridge", }, { - to: "talks", - label: "Talks", - position: "left", + label: "Faucet", + type: "dropdown", + items: [ + { + href: "https://l1faucet.a1.taiko.xyz/", + label: "L1 Faucet", + }, + { + href: "https://l2faucet.a1.taiko.xyz/", + label: "L2 Faucet", + }, + ], }, { - to: "faq", - label: "FAQ", - position: "left", + label: "Block Explorer", + type: "dropdown", + items: [ + { + href: "https://l1explorer.a1.taiko.xyz/", + label: "L1 Explorer", + }, + { + href: "https://l2explorer.a1.taiko.xyz/", + label: "L2 Explorer", + }, + ], }, { href: "https://discord.gg/taikoxyz", @@ -152,24 +158,28 @@ const config = { items: [ { label: "Careers", - href: "https://www.notion.so/taikoxyz/Taiko-Jobs-828fd7232d2c4150a11e10c8baa910a2", + to: "https://www.notion.so/taikoxyz/Taiko-Jobs-828fd7232d2c4150a11e10c8baa910a2", }, { label: "Media kit", - href: "https://github.com/taikoxyz/taiko-mono/tree/main/packages/branding/", + to: "https://github.com/taikoxyz/taiko-mono/tree/main/packages/branding/", }, ], }, { title: "Developers", items: [ + { + label: "Discussions", + to: "https://github.com/taikoxyz/taiko-mono/discussions", + }, { label: "Getting started", to: "docs/intro", }, { label: "GitHub", - href: "https://github.com/taikoxyz", + to: "https://github.com/taikoxyz", }, ], }, @@ -178,19 +188,19 @@ const config = { items: [ { label: "Discord", - href: "https://discord.gg/taikoxyz", + to: "https://discord.gg/taikoxyz", }, { label: "Reddit", - href: "https://www.reddit.com/r/taiko_xyz/", + to: "https://www.reddit.com/r/taiko_xyz/", }, { label: "Twitter", - href: "https://twitter.com/taikoxyz", + to: "https://twitter.com/taikoxyz", }, { label: "YouTube", - href: "https://www.youtube.com/@taikoxyz", + to: "https://www.youtube.com/@taikoxyz", }, ], }, diff --git a/packages/website/src/components/AddEthereumChainButton/index.tsx b/packages/website/src/components/AddEthereumChainButton/index.tsx new file mode 100644 index 00000000000..fc16e80300e --- /dev/null +++ b/packages/website/src/components/AddEthereumChainButton/index.tsx @@ -0,0 +1,64 @@ +import React from "react"; + +type Props = { + buttonText: string; + chain: string; +}; + +async function addEthereumChain(chain: string) { + interface AddEthereumChainParameter { + chainId: string; // A 0x-prefixed hexadecimal string + chainName: string; + nativeCurrency: { + name: string; + symbol: string; // 2-6 characters long + decimals: 18; + }; + rpcUrls: string[]; + blockExplorerUrls?: string[]; + iconUrls?: string[]; // Currently ignored. + } + const l1params: AddEthereumChainParameter = { + chainId: "0x7A6A", + chainName: "Taiko Testnet L1", + nativeCurrency: { + name: "ETH", + symbol: "eth", + decimals: 18, + }, + rpcUrls: ["https://l1rpc.a1.taiko.xyz"], + blockExplorerUrls: ["https://l1explorer.a1.taiko.xyz/"], + iconUrls: [], + }; + + const l2params: AddEthereumChainParameter = { + chainId: "0x28C5B", + chainName: "Taiko Testnet", + nativeCurrency: { + name: "ETH", + symbol: "eth", + decimals: 18, + }, + rpcUrls: ["https://l2rpc.a1.taiko.xyz"], + blockExplorerUrls: ["https://l2explorer.a1.taiko.xyz/"], + iconUrls: [], + }; + + const params = chain === "l1" ? l1params : l2params; + + await (window as any).ethereum.request({ + method: "wallet_addEthereumChain", + params: [params], + }); +} + +export default function AddEthereumChainButton(props: Props): JSX.Element { + return ( +
addEthereumChain(props.chain)} + className="hover:cursor-pointer text-neutral-900 bg-white hover:bg-neutral-100 border-solid border-neutral-200 focus:ring-4 focus:outline-none focus:ring-neutral-100 font-medium rounded-lg text-sm px-3 py-2 text-center inline-flex items-center dark:focus:ring-neutral-600 dark:bg-neutral-800 dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-700" + > + {props.buttonText} +
+ ); +} diff --git a/packages/website/src/components/Hero/index.tsx b/packages/website/src/components/Hero/index.tsx index ba103f8a18b..4ca99117d54 100644 --- a/packages/website/src/components/Hero/index.tsx +++ b/packages/website/src/components/Hero/index.tsx @@ -52,10 +52,10 @@ export default function Hero() { diff --git a/packages/website/src/pages/index.tsx b/packages/website/src/pages/index.tsx index e82bf99fa73..3138458ffc7 100644 --- a/packages/website/src/pages/index.tsx +++ b/packages/website/src/pages/index.tsx @@ -5,6 +5,7 @@ import JoinUs from "../components/JoinUs"; import Features from "../components/Features"; import Hero from "../components/Hero"; import Head from "@docusaurus/Head"; +import AddEthereumChainButton from "../components/AddEthereumChainButton"; export default function Home(): JSX.Element { return ( diff --git a/packages/website/static/img/metamask-fox.svg b/packages/website/static/img/metamask-fox.svg new file mode 100644 index 00000000000..e69de29bb2d From b4dc36359109ba8563a20ac5aef6009b63107eb2 Mon Sep 17 00:00:00 2001 From: dave | d1onys1us <13951458+d1onys1us@users.noreply.github.com> Date: Tue, 27 Dec 2022 00:40:07 -0500 Subject: [PATCH 47/57] chore(website): small updates to testnet docs (#488) --- packages/website/docs/alpha-1-testnet/start-here.md | 4 ++-- .../website/docs/alpha-1-testnet/use-the-bridge.md | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/website/docs/alpha-1-testnet/start-here.md b/packages/website/docs/alpha-1-testnet/start-here.md index 0a63675a742..885f197d30a 100644 --- a/packages/website/docs/alpha-1-testnet/start-here.md +++ b/packages/website/docs/alpha-1-testnet/start-here.md @@ -15,11 +15,11 @@ In this testnet, you can: - 🌉 Bridge tokens between Ethereum A1 and Taiko A1. - 🤝 Send transactions (e.g., deploy contracts). -- 🌐 Run a node — this makes _you_ a part of Taiko! +- 🌐 Run a node — this makes **you** a part of Taiko! - 📝 Submit feedback and be forever loved. - 🥇 Earn a POAP by using your wallet to complete any 2 of the 3 following actions by January 31st 23:59 UTC: - Use the bridge - - Contract interaction (e.g., interact w/ dapp, deploy a contract) + - Interact with contract (e.g., interact with dapp, deploy a contract) - Transfer between accounts ## Create a feedback log diff --git a/packages/website/docs/alpha-1-testnet/use-the-bridge.md b/packages/website/docs/alpha-1-testnet/use-the-bridge.md index c1f21d1af11..3714fdeeef9 100644 --- a/packages/website/docs/alpha-1-testnet/use-the-bridge.md +++ b/packages/website/docs/alpha-1-testnet/use-the-bridge.md @@ -13,12 +13,13 @@ The bridge contract can be found: Use the [bridge](https://bridge.a1.taiko.xyz/) for the following actions: -- Bridge ETH from Ethereum A1 to Taiko A1 - -## Bridge contract explained - -Read the bridge documentation on our GitHub: https://github.com/taikoxyz/taiko-mono/tree/main/packages/protocol/contracts/bridge. +- Bridge ETH between Ethereum A1 and Taiko A1 +- Bridge HORSE between Ethereum A1 and Taiko A1 ## Why is my L2 -> L1 transfer taking so long? The transfer from L2 to L1 can take a while because Taiko has a several hours delay in syncing block headers to allow uncle proof generation time, and we need the synced header to match so the Merkle proof of the message being sent on L2 is valid on L1. + +## Bridge contract explained + +Read the bridge documentation on our GitHub: https://github.com/taikoxyz/taiko-mono/tree/main/packages/protocol/contracts/bridge. From 3b967477a9b89051c3404a364d5e021652654307 Mon Sep 17 00:00:00 2001 From: jeff <113397187+cyberhorsey@users.noreply.github.com> Date: Mon, 26 Dec 2022 21:54:26 -0800 Subject: [PATCH 48/57] fix(relayer): gas limit + use loading as priorioty on bridge form (#487) * relayer increase gas limit, still cant estimate, and show loader if there are pending transcations * loading priority button --- .../src/components/form/BridgeForm.svelte | 18 +++++++++--------- packages/relayer/message/process_message.go | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/bridge-ui/src/components/form/BridgeForm.svelte b/packages/bridge-ui/src/components/form/BridgeForm.svelte index 55d5a2c46fb..5ff95209c76 100644 --- a/packages/bridge-ui/src/components/form/BridgeForm.svelte +++ b/packages/bridge-ui/src/components/form/BridgeForm.svelte @@ -315,15 +315,7 @@ -{#if !requiresAllowance} - -{:else if loading} +{#if loading} +{:else if !requiresAllowance} + {:else}
diff --git a/packages/bridge-ui/src/components/Transaction.svelte b/packages/bridge-ui/src/components/Transaction.svelte index e40029a24db..fe82e40f19a 100644 --- a/packages/bridge-ui/src/components/Transaction.svelte +++ b/packages/bridge-ui/src/components/Transaction.svelte @@ -133,7 +133,7 @@ {#if !processable} - Pending {fromChain.id === CHAIN_TKO.id ? '(2-3hrs)': ''}... + Pending {:else if !transaction.receipt && transaction.status === MessageStatus.New}
  • - Custom: You can set a custom fee if you want to pay - less (and wait). + Custom:You can set a custom fee for the relayer to + incentivize them to prioritize your request. A lower fee may result in + longer processing time.
  • None: You can select no fee if you want to come back diff --git a/packages/relayer/.default.env b/packages/relayer/.default.env index 7445d2e4e49..5097c633e8f 100644 --- a/packages/relayer/.default.env +++ b/packages/relayer/.default.env @@ -1,22 +1,21 @@ -HTTP_PORT=4102 -PROMETHEUS_HTTP_PORT=6060 +HTTP_PORT=4101 +PROMETHEUS_HTTP_PORT=6061 MYSQL_USER=root MYSQL_PASSWORD=root MYSQL_DATABASE=relayer MYSQL_HOST=localhost:3306 +MYSQL_MAX_IDLE_CONNS=50 +MYSQL_MAX_OPEN_CONNS=3000 +MYSQL_CONN_MAX_LIFETIME_IN_MS=100000 RELAYER_ECDSA_KEY= -L1_BRIDGE_ADDRESS=0xB12d6112D64B213880Fa53F815aF1F29c91CaCe9 -L2_BRIDGE_ADDRESS=0xA53a0f12879Aba509A24D2e5C959a36cdD18F9d0 -L1_TAIKO_ADDRESS=0x9b557777Be33A8A2fE6aF93E017A0d139B439E5D -L2_TAIKO_ADDRESS=0x80EDBd3f3618b348526F0970AEAFA52b6f204449 -L1_RPC_URL=ws://34.132.67.34:8546 -L2_RPC_URL=ws://ws.a1.testnet.taiko.xyz -BLOCK_BATCH_SIZE=2 -MYSQL_MAX_IDLE_CONNS= -MYSQL_MAX_OPEN_CONNS= -MYSQL_CONN_MAX_LIFETIME_IN_MS= -NUM_GOROUTINES=20 -SUBSCRIPTION_BACKOFF_IN_SECONDS=3 -CONFIRMATIONS_BEFORE_PROCESSING=15 +L1_BRIDGE_ADDRESS=0x3612E284D763f42f5E4CB72B1602b23DAEC3cA60 +L2_BRIDGE_ADDRESS=0x0000777700000000000000000000000000000004 +L1_TAIKO_ADDRESS=0x7B3AF414448ba906f02a1CA307C56c4ADFF27ce7 +L2_TAIKO_ADDRESS=0x0000777700000000000000000000000000000001 +L1_RPC_URL=wss://l1ws.a1.taiko.xyz +L2_RPC_URL=wss://l2ws.a1.taiko.xyz +CONFIRMATIONS_BEFORE_PROCESSING=13 CORS_ORIGINS=* +NUM_GOROUTINES=100 +BLOCK_BATCH_SIZE=10 HEADER_SYNC_INTERVAL_IN_SECONDS=60 \ No newline at end of file diff --git a/packages/relayer/README.md b/packages/relayer/README.md index f12149044b5..b12f5bfe8fe 100644 --- a/packages/relayer/README.md +++ b/packages/relayer/README.md @@ -5,6 +5,12 @@ A relayer for the Bridge to watch and sync event between Layer 1 and Taiko Layer 2. +## Running the app + +run `cp .default.env .env`, and add your own private key as `RELAYER_ECDSA_KEY` in `.env`. You need to be running a MySQL instance, and replace all the `MYSQL_` env vars with yours. + +Run `go run cmd/main.go --help` to see a list of possible configuration flags, or `go run cmd/main.go` to run with defaults, which will process messages from L1 to L2, and from L2 to L1, and start indexing blocks from 0. + ## Project structure ### bin From 456a521c98fa3130e82b59b75581a9468f37185e Mon Sep 17 00:00:00 2001 From: dave | d1onys1us <13951458+d1onys1us@users.noreply.github.com> Date: Thu, 29 Dec 2022 00:15:15 -0500 Subject: [PATCH 50/57] feat(docs): improve docs (#1646) --- .../docs/alpha-1-testnet/configure-wallet.mdx | 8 ++++---- .../docs/alpha-1-testnet/deploy-a-contract.md | 19 ++++++++++++++++--- .../request-eth-from-faucet.md | 8 -------- .../alpha-1-testnet/request-from-faucet.md | 14 ++++++++++++++ .../AddEthereumChainButton/index.tsx | 4 ++-- 5 files changed, 36 insertions(+), 17 deletions(-) delete mode 100644 packages/website/docs/alpha-1-testnet/request-eth-from-faucet.md create mode 100644 packages/website/docs/alpha-1-testnet/request-from-faucet.md diff --git a/packages/website/docs/alpha-1-testnet/configure-wallet.mdx b/packages/website/docs/alpha-1-testnet/configure-wallet.mdx index 3e94507d1aa..5cf42054453 100644 --- a/packages/website/docs/alpha-1-testnet/configure-wallet.mdx +++ b/packages/website/docs/alpha-1-testnet/configure-wallet.mdx @@ -10,10 +10,10 @@ import AddEthereumChainButton from "../../src/components/AddEthereumChainButton" Use the buttons below to add Ethereum A1 and Taiko A1 to any wallet that supports `wallet_addEthereumChain` (e.g., MetaMask). -| Network | Add to wallet | -| ----------- | ------------------------------------------------------------------ | -| Ethereum A1 | | -| Taiko A1 | | +| Network | Add to wallet | +| ----------- | --------------------------------------------------------------- | +| Ethereum A1 | | +| Taiko A1 | | ## Manually add networks diff --git a/packages/website/docs/alpha-1-testnet/deploy-a-contract.md b/packages/website/docs/alpha-1-testnet/deploy-a-contract.md index e4cd3564285..fb2b08c2175 100644 --- a/packages/website/docs/alpha-1-testnet/deploy-a-contract.md +++ b/packages/website/docs/alpha-1-testnet/deploy-a-contract.md @@ -4,10 +4,23 @@ sidebar_position: 4 # 🚀 Deploy a contract -We will deploy a smart contract to Taiko A1 using Foundry. +These steps will show you how to deploy a smart contract to Taiko A1 using Foundry. You can find the latest Foundry docs at the Foundry Book: https://book.getfoundry.sh/getting-started/first-steps. This guide uses snippets / examples from there. -1. Follow the Foundry Book to install Foundry and init the default project: https://book.getfoundry.sh/getting-started/first-steps -2. From `~/hello_foundry` run `forge create --legacy --rpc-url https://l2rpc.a1.taiko.xyz --private-key src/Counter.sol:Counter` (replace `` with the private key of the account deploying the contract) +## Prerequisites + +- Have the private key to an account that has some ETH on Taiko A1. This is to pay the small transaction fee for deploying the contract. + +## Steps + +1. [Install Foundry](https://book.getfoundry.sh/getting-started/installation) +2. Create a project with Foundry, and `cd` into it: + ```sh + forge init hello_foundry && cd hello_foundry + ``` +3. Deploy the contract from your project, located at `src/Counter.sol`. Replace `` with your private key, mentioned in the previous prerequisites section. + ```sh + forge create --legacy --rpc-url https://l2rpc.a1.taiko.xyz --private-key src/Counter.sol:Counter + ``` We are using the `--legacy` flag because EIP-1559 is currently disabled on Taiko. We have plans to re-enable it in the future. diff --git a/packages/website/docs/alpha-1-testnet/request-eth-from-faucet.md b/packages/website/docs/alpha-1-testnet/request-eth-from-faucet.md deleted file mode 100644 index c8aaf33de79..00000000000 --- a/packages/website/docs/alpha-1-testnet/request-eth-from-faucet.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -sidebar_position: 2 ---- - -# 💧 Request ETH from faucet - -- Request ETH from Ethereum A1: https://l1faucet.a1.taiko.xyz/. -- Request ETH from Taiko A1: https://l2faucet.a1.taiko.xyz/. diff --git a/packages/website/docs/alpha-1-testnet/request-from-faucet.md b/packages/website/docs/alpha-1-testnet/request-from-faucet.md new file mode 100644 index 00000000000..8c75a9d98da --- /dev/null +++ b/packages/website/docs/alpha-1-testnet/request-from-faucet.md @@ -0,0 +1,14 @@ +--- +sidebar_position: 2 +--- + +# 💧 Request from faucet + +## Request ETH + +- Request ETH from Ethereum A1: https://l1faucet.a1.taiko.xyz/. +- Request ETH from Taiko A1: https://l2faucet.a1.taiko.xyz/. + +## Request HORSE + +- Select HORSE from the token dropdown on the [bridge](https://bridge.a1.taiko.xyz/) and a faucet button should appear. diff --git a/packages/website/src/components/AddEthereumChainButton/index.tsx b/packages/website/src/components/AddEthereumChainButton/index.tsx index fc16e80300e..4ebbddcce99 100644 --- a/packages/website/src/components/AddEthereumChainButton/index.tsx +++ b/packages/website/src/components/AddEthereumChainButton/index.tsx @@ -20,7 +20,7 @@ async function addEthereumChain(chain: string) { } const l1params: AddEthereumChainParameter = { chainId: "0x7A6A", - chainName: "Taiko Testnet L1", + chainName: "Ethereum A1 (Taiko)", nativeCurrency: { name: "ETH", symbol: "eth", @@ -33,7 +33,7 @@ async function addEthereumChain(chain: string) { const l2params: AddEthereumChainParameter = { chainId: "0x28C5B", - chainName: "Taiko Testnet", + chainName: "Taiko A1 (Taiko)", nativeCurrency: { name: "ETH", symbol: "eth", From 71a99f9c7d8887d1d68a9b65e31aa9b14d85d6b0 Mon Sep 17 00:00:00 2001 From: shadab-taiko <108871478+shadab-taiko@users.noreply.github.com> Date: Fri, 30 Dec 2022 20:02:51 +0530 Subject: [PATCH 51/57] fix(bridge): ui fixes (#489) * feat(bridge): add invalid chain balance section in dropdown * feat(bridge): add invalid chain icon and fix mobile view * fix(bridge): open faucet in new tab * fix(bridge): metamask icon * fix(bridge): transaction list mobile view * fix(bridge): transaction detail mobile view * feat(bridge): move message status tooltip into component * Update HeaderAnnouncement.svelte * fix(bridge): invalid chain icon alignment * fix(bridge): status alignment Co-authored-by: dave | d1onys1us <13951458+d1onys1us@users.noreply.github.com> --- .../src/components/AddressDropdown.svelte | 8 +- .../src/components/ChainDropdown.svelte | 9 +- .../src/components/HeaderAnnouncement.svelte | 3 +- .../components/MessageStatusTooltip.svelte | 36 ++++ .../src/components/Transaction.svelte | 39 +--- .../src/components/TransactionDetail.svelte | 4 +- .../src/components/Transactions.svelte | 11 +- .../src/components/buttons/SelectToken.svelte | 2 +- .../src/components/icons/MetaMask.svelte | 204 ++++-------------- packages/bridge-ui/src/pages/home/Home.svelte | 2 +- packages/bridge-ui/src/store/transactions.ts | 3 +- 11 files changed, 108 insertions(+), 213 deletions(-) create mode 100644 packages/bridge-ui/src/components/MessageStatusTooltip.svelte diff --git a/packages/bridge-ui/src/components/AddressDropdown.svelte b/packages/bridge-ui/src/components/AddressDropdown.svelte index 3efc80d23fa..81e2ef37a21 100644 --- a/packages/bridge-ui/src/components/AddressDropdown.svelte +++ b/packages/bridge-ui/src/components/AddressDropdown.svelte @@ -86,11 +86,11 @@ {/if} - +
  • diff --git a/packages/bridge-ui/src/components/ChainDropdown.svelte b/packages/bridge-ui/src/components/ChainDropdown.svelte index 93ddf19bb9c..60a5bb0a2b5 100644 --- a/packages/bridge-ui/src/components/ChainDropdown.svelte +++ b/packages/bridge-ui/src/components/ChainDropdown.svelte @@ -6,7 +6,7 @@ import { ethers } from "ethers"; import { signer } from "../store/signer"; import { switchNetwork } from "@wagmi/core"; - import { ChevronDown } from "svelte-heros-v2"; + import { ChevronDown, ExclamationTriangle } from "svelte-heros-v2"; const changeChain = async (chain: Chain) => { await switchNetwork({ chainId: chain.id, @@ -31,10 +31,13 @@ {:else} - + + + + {/if} - +
      Receive some tokens for bridging with our faucet. diff --git a/packages/bridge-ui/src/components/MessageStatusTooltip.svelte b/packages/bridge-ui/src/components/MessageStatusTooltip.svelte new file mode 100644 index 00000000000..8edad54e024 --- /dev/null +++ b/packages/bridge-ui/src/components/MessageStatusTooltip.svelte @@ -0,0 +1,36 @@ + + + + +
      + A bridge message will pass through various states: +

      +
        +
      • + Pending: Your asset is not ready to be bridged. Taiko + A1 => Ethereum A1 bridging can take several hours before being ready. + Ethereum A1 => Taiko A1 should be available to claim within minutes. +
      • +
      • + Claimable: Your asset is ready to be claimed on the + destination chain, and requires a transaction. +
      • +
      • + Claimed: Your asset has finished bridging, and is + available to you on the destination chain. +
      • +
      • + Retry: The relayer has failed to process this + message, and you must retry the processing yourself. +
      • +
      • + Failed: Your bridged asset is unable to be processed, + and is available to you on the source chain. +
      • +
      +
      +
      +
      \ No newline at end of file diff --git a/packages/bridge-ui/src/components/Transaction.svelte b/packages/bridge-ui/src/components/Transaction.svelte index fe82e40f19a..2fbc7b2cdb6 100644 --- a/packages/bridge-ui/src/components/Transaction.svelte +++ b/packages/bridge-ui/src/components/Transaction.svelte @@ -10,6 +10,7 @@ import { pendingTransactions, showTransactionDetails, + showMessageStatusTooltip, } from "../store/transactions"; import { errorToast, successToast } from "../utils/toast"; import { _ } from "svelte-i18n"; @@ -25,7 +26,6 @@ import { providers } from "../store/providers"; import { fetchSigner, switchNetwork } from "@wagmi/core"; import Tooltip from "./Tooltip.svelte"; - import TooltipModal from "./modals/TooltipModal.svelte"; import Bridge from "../constants/abi/Bridge"; export let transaction: BridgeTransaction; @@ -33,7 +33,6 @@ export let fromChain: Chain; export let toChain: Chain; - let tooltipOpen: boolean = false; let processable: boolean = false; onMount(async () => { processable = await isProcessable(); @@ -166,9 +165,9 @@ Failed {:else if transaction.status === MessageStatus.Done} - Claimed + Claimed {/if} - (tooltipOpen = true)}> + ($showMessageStatusTooltip = true)}> @@ -183,38 +182,6 @@ - - -
      - A bridge message will pass through various states: -

      -
        -
      • - Pending: Your asset is not ready to be bridged. Taiko - A1 => Ethereum A1 bridging can take several hours before being ready. - Ethereum A1 => Taiko A1 should be available to claim within minutes. -
      • -
      • - Claimable: Your asset is ready to be claimed on the - destination chain, and requires a transaction. -
      • -
      • - Claimed: Your asset has finished bridging, and is - available to you on the destination chain. -
      • -
      • - Retry: The relayer has failed to process this - message, and you must retry the processing yourself. -
      • -
      • - Failed: Your bridged asset is unable to be processed, - and is available to you on the source chain. -
      • -
      -
      -
      -
      - - + - - - - - - - + + + + + + + - - + + - - + + - - - - + + + + - - - - + + + + - - - - + + + + - - + + - - - - + + + + \ No newline at end of file diff --git a/packages/bridge-ui/src/pages/home/Home.svelte b/packages/bridge-ui/src/pages/home/Home.svelte index 358f34620f7..36052cb92b7 100644 --- a/packages/bridge-ui/src/pages/home/Home.svelte +++ b/packages/bridge-ui/src/pages/home/Home.svelte @@ -9,7 +9,7 @@
      -
      +
      ([]); const transactions = writable([]); const transactioner = writable(); const showTransactionDetails = writable(); -export { pendingTransactions, transactions, transactioner, showTransactionDetails }; +const showMessageStatusTooltip = writable(); +export { pendingTransactions, transactions, transactioner, showTransactionDetails, showMessageStatusTooltip }; From 5d65ba83a851ea6137ffa3853066afa4e08b17c7 Mon Sep 17 00:00:00 2001 From: shadab-taiko <108871478+shadab-taiko@users.noreply.github.com> Date: Fri, 30 Dec 2022 20:11:19 +0530 Subject: [PATCH 52/57] fix(bridge): use metamask wagmi connector (#2080) * fix(bridge): use metamask wagmi connector * Update packages/bridge-ui/src/components/buttons/Connect.svelte Co-authored-by: dave | d1onys1us <13951458+d1onys1us@users.noreply.github.com> Co-authored-by: dave | d1onys1us <13951458+d1onys1us@users.noreply.github.com> Co-authored-by: Daniel Wang <99078276+dantaik@users.noreply.github.com> --- packages/bridge-ui/src/App.svelte | 4 +-- .../src/components/buttons/Connect.svelte | 35 ++++++++++++------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/packages/bridge-ui/src/App.svelte b/packages/bridge-ui/src/App.svelte index f8eecf34bee..10329e6a3b1 100644 --- a/packages/bridge-ui/src/App.svelte +++ b/packages/bridge-ui/src/App.svelte @@ -7,12 +7,12 @@ import { configureChains, createClient, - InjectedConnector, } from "@wagmi/core"; import { publicProvider } from "@wagmi/core/providers/public"; import { jsonRpcProvider } from "@wagmi/core/providers/jsonRpc"; import { CoinbaseWalletConnector } from "@wagmi/core/connectors/coinbaseWallet"; import { WalletConnectConnector } from "@wagmi/core/connectors/walletConnect"; + import { MetaMaskConnector } from '@wagmi/core/connectors/metaMask' import Home from "./pages/home/Home.svelte"; import { setupI18n } from "./i18n"; @@ -85,7 +85,7 @@ $wagmiClient = createClient({ provider, connectors: [ - new InjectedConnector({ + new MetaMaskConnector({ chains: wagmiChains, }), new CoinbaseWalletConnector({ diff --git a/packages/bridge-ui/src/components/buttons/Connect.svelte b/packages/bridge-ui/src/components/buttons/Connect.svelte index 7486ab9d76b..21d1791f852 100644 --- a/packages/bridge-ui/src/components/buttons/Connect.svelte +++ b/packages/bridge-ui/src/components/buttons/Connect.svelte @@ -9,6 +9,7 @@ fetchSigner, watchAccount, watchNetwork, + ConnectorNotFoundError } from "@wagmi/core"; import { CHAIN_MAINNET, CHAIN_TKO } from "../../domain/chain"; @@ -47,19 +48,27 @@ } async function connectWithConnector(connector: Connector) { - const { chain } = await wagmiConnect({ connector }); - await setSigner(); - await changeChain(chain.id); - unwatchNetwork = watchNetwork( - async (network) => await changeChain(network.chain.id) - ); - unwatchAccount = watchAccount(async () => { - const s = await setSigner(); - transactions.set( - await $transactioner.GetAllByAddress(await s.getAddress()) - ); - }); - successToast("Connected"); + try { + const { chain } = await wagmiConnect({ connector }); + await setSigner(); + await changeChain(chain.id); + unwatchNetwork = watchNetwork( + async (network) => await changeChain(network.chain.id) + ); + unwatchAccount = watchAccount(async () => { + const s = await setSigner(); + transactions.set( + await $transactioner.GetAllByAddress(await s.getAddress()) + ); + }); + successToast("Connected"); + } catch(error) { + if(error instanceof ConnectorNotFoundError) { + errorToast(`${connector.name} not installed`); + } else { + errorToast(`Error while connecting to wallet`); + } + } } const iconMap = { From cf9693b428a4d22ec63cb7690280406e4dfa46b8 Mon Sep 17 00:00:00 2001 From: Aniketh Paul Date: Tue, 3 Jan 2023 10:01:14 +0530 Subject: [PATCH 53/57] docs(website): update `deploy-a-contract.md` (#3692) --- packages/website/docs/alpha-1-testnet/deploy-a-contract.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/website/docs/alpha-1-testnet/deploy-a-contract.md b/packages/website/docs/alpha-1-testnet/deploy-a-contract.md index fb2b08c2175..a25a066e4d5 100644 --- a/packages/website/docs/alpha-1-testnet/deploy-a-contract.md +++ b/packages/website/docs/alpha-1-testnet/deploy-a-contract.md @@ -21,6 +21,7 @@ These steps will show you how to deploy a smart contract to Taiko A1 using Found ```sh forge create --legacy --rpc-url https://l2rpc.a1.taiko.xyz --private-key src/Counter.sol:Counter ``` + Note: Remove "<" and ">" from We are using the `--legacy` flag because EIP-1559 is currently disabled on Taiko. We have plans to re-enable it in the future. From 8ef75610c4b4991038c520d6378cb4d6d1ac9105 Mon Sep 17 00:00:00 2001 From: dave | d1onys1us <13951458+d1onys1us@users.noreply.github.com> Date: Mon, 2 Jan 2023 23:35:06 -0500 Subject: [PATCH 54/57] chore(branding): add geometry banner (#2532) --- .../branding/Backdrops_Static/geom-banner.png | Bin 0 -> 70011 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 packages/branding/Backdrops_Static/geom-banner.png diff --git a/packages/branding/Backdrops_Static/geom-banner.png b/packages/branding/Backdrops_Static/geom-banner.png new file mode 100644 index 0000000000000000000000000000000000000000..aeb764cb7ac11ad83ed1c4287e98eb4ce8dab2e2 GIT binary patch literal 70011 zcmYgY2{hF0_tzpJQPvV#Em@N#yA;}n>|02dvJGNvW2l5zwu-WhU5$N^ea04PB5RDn z7~9A;3>xc<<^T16f2Ox{I-Q*3nfpA?y`THJpL?H)e5j|(#&VX0fq{YT?w#9)3=E7T z3=D@>j~@emA`-SF0sO=4dB?(=fq|8C|L-A&lvF<8hljikb+s6fUFYV2{~UdK6Lgb- zp)8Jd+m4ap&?%0)w{IHzA6g*TMn9T?O>b2fz>Uu}Ri8WqWT9)+_yy=+5^GoY& zA6mZ=rVq#0o)7A5TEAM~h98YzC>W;HL4vU!Lg_{``e{ZXKbAz^qK6ic$}|1W z$NWca)jUXZxfx_7z}o#k*XMT7A>{S-bRUfrXg3UoVIQg4UM2H`&6Xl(m29KZX@rrb zJ@QUT4QJXVa6PP303Xm5bFUi)rUdLIIg=~YA^gSs9e7vsJIP< zMDs-RGS8HY%Gl0J-4>$lT@lkXt3kfC#^m;>IbceA8`O{EUn-xfIr8sX*XLKKuk9K1 zEfP<@Ez!xU)LQyHszu(oD#|O66I&??Dl73}-#@= z!!K3-T|*|6>j~b6CTc`95gSqe9gAuu2MYy?ihgKoy`)O6yuZWt#1_}iA5M+=MIq~cZkR3=`eAO1VQdI2AsvzF|wHnV>D74-42Y=sK%?MZ>jaS zJ%03eC$f@#i%;qoS#XscrMVjk8NM3l_BNU}bYE^*a+TtSR6@j0_VA);*Bf=$NVHs zkXPa=Yypu)L#=M^xSQ?0Vi1%(O8(b~rsx~>0UaU6A3AQJe^k!RmKN~!uACbS=nCo2 zZpugN6769)`8g*jNjd1N+8k0?%X{8IMvwm6CvNlx_{tDQSDo@xxXel$eyL3zAx&{X zYbNr&B{v8(>8*Q-c%*Gx_6wWuz1KrTSO$v+ob-KQ$SNfJu2hNMu{?hIRUAo42an;Q z?GdB^lWS(v>VrHsg-5Jkq8n^Aa$}4a%`x21#bT@83N)+HNg|iQGS3G4LNRg>Ndq(X$lFVrj|E35y#IDkY zPtHslDFk$AJF@H4o)ScskN5AAX(QY3_ww#8mdbuE+U24K3zfPO`-%#ZOh9GTNVU>K z&O8FRQPB~b1+rfRlr^*@#L(V#6Z|3Sk ztJx;kNQ-QHW#mW?Pz~wYVgRPnqM%kFBMyQw5(&L~!ISCV(?s-_0uF^wnKX@wLJY=V zKnLse5mFq4bQzFrrp1l0NAS-nt~X2Swfq225Tknnm!aev?AJQ=y{f&M>Wu{UK`cf+ zwjfCsj1Dd>cHSt{@T+q!knq`HO4}e7Rvvmyk72C>?XovqVI*m=puqOfvp}o(-@};| zkp)TF%`w*Tm>VbkvP;LVy6Zt=*Bhr@eP%r{{&Yw*(#cwz>$DHmkY-AoVtGI2(l{eB z#9gI-%dvf>t30%uJ5XqL(a$V2CDE&9i26-(y=s(O(@#bCFM`Md@r4&|HQ_EF7+n^^ z)L(EuRaI6PAk0!<(q-*O^y!05@emrmW>lU(6#ds#9rXZXgLWsomL5Rl@73JMY-Io? zN&9Z7D9(JJccR(Ppmw_Rwic;7_p++!su{5{k+E~V!w9`znmQU*9HBt`N`opY5Lhf52oZZEPSi?dIe;Ih`GH z%Ae3d^7zJ~Swu!^Al1;3L-Cq)?>PkeV)bl$e*4N^uX1l9!m}9wS8S{qvZ{m@#}r5- z)Lb58EESn;V)`CQztF=IK4@&)BWDkHEtFro)|6*?Km2R>XWS(7M(e1$qEwq7!@{E4 z?-zH^AGn;bL~2hkYSpNb-9k)KZ#0Meb$)39sk0~q}`bN8MpV!8$#S2ib@}Z2HLhoS@stFlRPp1`MlG<)w!T*xKL~SR6Oznv!Y*T zLHs)aHoh9~8kW?GZItSL*RMebk0V{>B-d2@EB;#gTsR$UW}Yf9DP{Mf{#V*a0SE;Q`GW;j;B!%@YYR_BTu9O`xK{`{suA(elM2q|a5LO;>P;(LQ zfZckgbhGu-HTH+;3G*6D3n{yA@jGI~48=sa@u9f!nrrdy;g^U1C_7@?25|r;{dN85 z-U)Ixd#L=5J10qd$fwrD@+3}GUj4xkaGmOY?)Yw8>%7A+1-PV@Sb6S7|F}0PgsI@- zfk%Iz-REjvl{QmYTm#mmn{bb-Xt7B0^&rK{t*ag~wzlIA}?BskOXUNN8#%zlMU zhyL^1mrs=Z9B50byx|!lg*0)t90D$oe~~}5J27zWK`nm8t)e7Xu)8wzNx0>i|G<*4 z0s-mr@ykSInHa^Zzug$PhEI_}tb~lYe($0Hj5oT{gtZ)xy>7zJrG4O_(KpS*3?V3M zu70b3iZO16Sm2{vfjqII>CwnVZFt)!5qgojHv}1&tyD0 zuqf2^hT`^FmvU3J#8(d$XI^A+AtDY@XL4xMtmL+vZ*6>}8R+nMVm{A_S&YHV{f*WF z(LiSOTLKysANNa9nrfk9YSKIstup(Qh2-#uhk^V}jqFp?BHYJ|rs=Zu#gHBAL$xMQ z|=&uE*QOqQ^BV1NtQ)yd#B38@%Fu7%s-87}8n1dU6X> zMD_P&i!%N*IEFK@51r%D%f(}sDYDe4TP%GV&o#V-*kAd;c0RJ>H24?T--aE8(zAy* z2uSA@)U&=GBXS;lnY>y$@6nH^1Sz5rStpnj%LqE+*o9zjM;-=n>q|6})ye8cmioO+ zb@;^@gR!?Y$p_*xevWs`tCLRTLdu*vc}^?Q8|&C-0hym;X{S zwi>s=Iptq~Sn*@)imKbyz{$#I3ykr-zxR= zK_S7Ww9h;rxrYqi5goi;{J7jF-p5BUt0QAhZ(!b1ES zQXtsOeDZ%UjFDF>$zN4=ahx9o5UvJIsDuX1aMu8G?dv zTC3Qa0sY|QSrxb3Tu=nje@w@b=b`;~1A8D3s8-y~Y^PnE+r=?uL_)qo-LF%BLVAtod7~eI0_I2kaUUQU*tCZF z2~P4v1we%fWahoVxsg8zEJS+54K^w6>y~3kNQ}FeORb@^Jb438_X{h9_DNvHlzSR} zdjUcX1wUc+CQ`FPxQj2fYz;h9af%*&i`g-Dh3bjvPy4|%uQQGPDmU3IL&!2g7bhgJ`C-HW43h}3Gv54 zh5hZ(uI2qbm8OlR-_sRiux-yyJx<6cTX~H)Bgr zvoPxCG@N*7Dy-`*kLw>aD$C1o{9QzWppLZsC}B1%RCAp?`1rY+^zDq>Gj+%~t=ZtU z&TMpYQV|Y!d(5b^Q7uU7F6)S-x$WF~?i@r_qn>u`tFjKgjEQ~>MDA4t-Z2LT>#;(P z2bzzocXb-Z4_M+x?k&A#uOp=1KWElx6r4cz^%g%lVzTqRz+2AmiQ0AwXoo~B9?(5X z5~bH1d|&fBb9RZJQ22N`#}Fq<(pbVO_QIIrK2(sraY61Kh91y3=%AQwm7Z`Riw@VP zjQYLpo{UDBjj9aujE}fe<#f86Cwk(aZhe0gVO8Z<+!pa3=}zKZw~uFs_xT-f&f`aR zHJ}*Y{P^At>GgH=k$Tu?1!<685y`t=W#SRnHqw_+y%teTbCJ=q>i965;-4ov>iTDMJ zP>0vt$I!p#Ji3%6O9Y&R=nnD{@lEo4x{n|9mzDR@Rsla_yYoxhW#Co`UKwCgN@E|j zgNfw(X!ytKH^QY`LOHY3pJx;@HZIh=I3c^N$JYH=XrJ9+Y=09~-ucwiX6#hC+F!d( zY0q_;I4A6mtF?6+7C*KAAfp4LP^A+_>MHJ(rvI*@bu2#DzrCl{ut;K|E(R!^j$hI= zp<~cpmLmBu$vy>wlwEO0B>A1Y5t%qBA@6#muTGgy#TS%XlJ8M}yEI+f4rNoKMtJtl z4oo8bTp?AT3fpvh|K@LYx-#UmDhH_+$!D^j2s9Tr+HlHmOJBRlpLA8T5h(vQ3Z*du z^R{Cvp*L|Tg^XfKN&!;G7rlt_Ij`(w;o3VEOQqxWm9$v#hg>K0Nj?>aA&g|eFj4qt z7Z>X|Zat)xJctJ>Xv>`Ak~FaH{3pYAY*v55FP*Ty1Nkyj-r#G{8ZD{XGZy<-y1VjP zV7AX(#J(Q_u9Oug+){rb{l%i7!FJ3HaT4LuO$B5Ml@Tkxb7q`NJT=@*~I({kz6nPT;PE~g`M-C zt$75}KgWB3V*aD?UfKy4x?siS<3h>XyHQM7bcQDll09`h>0W)9>({Wu>Xuj%7$=OFIEGNJs>_Fj%@!`QGZ~Rc#jf_u z1LX^3HT6x^RNoS~mY+4h_4S>nwnkS@J`_2Clgt+Y#O+`Z1^Qn;fr<++B4&jVXaZ;G z$qlpVu~qr&eOlaVPGXiaH8(b@T$|Gdo_4KeyUq#vMX2m($Gfr}+y2jSD#;1BXF=i# zKsFtSa(fb(O<5Qpge|LT>wV`|I}J{^RJ6{}uI`q$lvtbGU02vy%%ca=+e@k75?8H9 z6DTbw*1@%a-h2p5V)#$cLi&wWcCxNr4=bL<<`zFlw^mK=+> z`JCf=QjRW^@J;R)G}kO9PMbD!5q=oQ*%^^=mYsSQOV9OB2vYzl2{i(#v(!xT85bOA zdz2p1U8~pFLZD(v0EK&v>;Qtxi5^^MxD2gig7C2U1^~8dKgeh+$ODA__|AV`RgB8Dzs~*U16zHUVJ{V&?82E))O;k1-*zLO-1aBs_P@2 zSATbX{W^mt5Klg%YfT&v2VE;gQaBnpJQkGN@|R`Za?J1&@^i)hUq%$91aga%&(KmN zkG#@jt8T(oz?hv&ujBf}e~c}g^4UL06sz~rX16}1VG3b!<7RLrL~RYKFsvwSD9$N$ z>~bNv16zaH(Uf_o{)e(oz0jCe&U40TB8?>pM9&{?N=OT4H2(Lm5%exm&LjP-6psVw zZ4?iODPD-$jj<57#~?^4rV#rOk*IoDW!R)|>;!3x5%WF&*|CH4$Wh*&5*uPxkZgM_M8@l;B65IB= z3u6l(=rnRKHx66mk$7qWko1taF8j~;?F8sMfy^v6SjBb^WAM)Cliv3eh$S?QkoOVI#1xm0179Lnj`dyf z)V+QHvCBVx!oiAtr~sFD056jJEhTOsN?852d+%HjJ5Y!7uYAvkNF0@DvLZl_ZKHEBxz3dSZ1`tk=l8br3-|TQN7UqwuSQ_olGaT3 zpguM^5=Uoec+oo))M;rcH!N1puff0KzzfIUA?8oBc1uqk9VmE`T-9mx6W~KE-Hx?= zkK$&7+1Pl%irY5YZN-VQ%wXJg~b^4(q&lH_XQ8C;m(7qmGe&dcU`^C z*Z(S3^c>@69QF-zoOk$k*w1?Gsu4MArCW50o%doz)8i#Q^Zi&N(68^#mq>8f7e@vD z4+qO6PRcww6r2{tB>weV&`O-?ZV z*BqKZ<_8CGuC zxQZjVRd}2T9=Q7Cjrp4+86)BF^!YOt;S)f%pQqKk0bknaVq|kz|phB zJT!NIxe!)cZ7tB;X~e#|jz(Ar=viuPi6XyQlXAd^U0DyO=#>nQ{l8?YUE(^p0k(q~ z-tcO~tC3&O$>8`qyS~MglO^J%CrRcFgU(g|N)!_TsQ_4;49O#h34$n|h-m<-!Wza< z#1`+nEMwTx8{v$nRW0ep+&PZ!CY>wvKO-kJWGgESZS`u|B33f+ALINgq88IJqf!Fx3@)-qX{Lm07p%I7DE zI=I@nR`Im9Z{8yn6%k`(aQy)BR990{!HHgO>BId_`B{J@eUtm=y=nmC(jcmg5&{>-=n}H7bMROI_2=B^KuWg z^((z>Jmry^;s@GqBx5e(W_;`Nnh^Pa8-;>_m{2mMs8de&GiKm*jRdYPz$G5Qs-e|7 zg{zWA;+%TpxLQ#Ty&QHG!NNachK}Ci$b5?*$tg_j2l{N&EfCN~XDO6ifFYru2H2F~ zfS%*PA3MLvjpF4Nv`=$8b?Xatg?k;pCiQ3Amkr>ue#m0}F)5#Ltp zl{9O6@3>#DH>(F0PPX+Kr$r0Ig^{~`ejV|%;ybQrAu#=s6cT5XtIVrIr+}&z-<1eb zED<=%ozlS12+L$Iw~r|fMr3h9XjnsIzVey>???e|mb+VTP0H?umDh8(hkPybx;lT* zU3d{OFyP=%>08t>A{*kS^Y^Q!wpTDv2(MB^d|J+~y%EM=a`cCM@uX8CUirJY_tws0 zcGxM>pRvA6p1Lr)Gd=v(4>6J#Y3Z*Gn7^TTpDnCXHee=`Xtl5&x{N?l0#zrFj4!4V zXirL4^6Sx=|KQ2j_ue%LK>i+Q8v?I5jKe4kNI~4wmG>+2BGc*GR4M9ju*O@D0=@5o zc;R0ecOQ&MV=5H`!L;;?3YNGXjGFafzy62=-(*ko%;}Bt_CR|T+Ee0xd(b~- z1XalCeV^*zyr-zBT%O081FLNrF(atd28Dz;g*=g z$xb zxpnasL`FOpSk=<$ec=0jmzvpo@}yVOdY;F$QGF~(zriI?!l$~;mvzKFepnXS%1P0h zjir~s<5|Eo0IR;peoPB5`~;^f&jEMg*85n@Aj(@+U5N3IS?DtAuItLT`q(Qcwo4fM zmb3}6iXd(UJ#A~cKlGH2dIL&Q>Ip>?1^qOjw}01;7j}agJY(dDsw;3U4lYIBOKz#( zJ*o7hf#TJWG+m+~2CgGywl zp6=Xy|ZzSIiW*h8pn^1TvOfwA2c)`f46v856s)hn@C&$5i*8U6$Vx1-EZNX0V-V4+i-1g zdkqSgb_XTKl{FHIx~;v%y((jDmJE1=!L1tTT;1{5;KyM8+4=NAEOcZq0BEA_xUBk3 zWUSTujtwq_?k>`O^Z{$=e8qX_*}>`jT!lEQ<;MzQY84H^#4@we%+U7m1}a zh8%(K&F%oI=HTA8-5`9(u7!{E`UXRdz+OEWbO8MwMV=^FiozL?Jv{fS8!o}0uSlN+ z;I}{tw*3bn7dm8Bhb(X9naJ2RAtRrLA@VSzqei>-$vq>8E>gmY&PD4p>%Cf(-SD~$ zHuB4YrO|^*uBk)0H(($nbsdl;#Jm{E!>Vi8(cd@6Rf>TSpxXTw0bgUeEn*^aN!;sm zjD$AsdBWMO<#Sv|^+}Z(T&$ff>XZr%efwvld%G*rnFnt8*hwHRbY9#$KDkOtd_;B4 zVJ0ubDPtN42(E(m%i`u0kKe5an6NlQ)L9a71fPMe=!=qa-T0#wkv&1;=f(s-l8Dkl znGNz5>T^9oTnBNUbTg0lh!pGuYUrNq5DfWhl+ z3TI62sNFX?UllO_#IG@<=OoDiVNqjOa2Yy_xtY~CyH@qW!WOO6ta7BnYd#x~yQAs; zMd46oPmsrxFG0gv-UYsI4%D8e4(42ZF08!&lP9HLk=&@7M^0L&lu~(w9&W7D1gYg@ zkfPqtw9wrgWQH)>EHGk3%P+&*8~FvlvRc_pw)>(*v4g(|w=IbZn|jZaVwItkwdFuR z;AuBVdiQ`2x~$Vk(ry1_uN1{^QM+&>aZAA}=~A|Qqy(B_i4e7c$iiO;-=dUukL*Uz9cWE2lCzg_h~ivWe6PgBbpDP;BMD() z4$WcqgZBtMEsnPFL)$1?Wz>8IYp9yZ$BnLVti$gQ^*jp6DC#^TEG{=z;1(bw7t&Ad z5Ynb!oS5CF%3}SD1y=*IN@GMJU!$|)YvEvX6QFUM6@MtZO93ni{~(>X1oOrdVH>;m zk#9;_ZEn`saMDm~$@mQBPJ^QdVC@u+_*ktq??;d*7lYA)EP2^Kq9dwI#(sRB9mcXx>z{}hTA5Jnp1Pmd4uL`Ef$my>qM`YEyIQZz#jWhrQ7$^A{>C1iJ596HXe&-Ap zA)O1peCipIU8KP-Z*Zy`=fk^jxoVTI=~mRZ^~6oig<)pckZBscC0SdxE=r6HSg>CLDGgdi6@-38T(I ztL;T{=KSg8HDF$7K-}vEFel>Zr;Kz`>QkC-nYx%2@D8N;^B(@FF{YF&V#u`m32d5* zRwUOiG_JR%jftL1H!J%!XDiLPFp>Q_Y^rMT;W}T%L_sZfe1+J1^M6v-i)7EBH)y;& zXHUgl??RWkkg@pC^VVIXA;HUAZJjyfPb%{|?|4#vnmYc}4b|WsI%@nuGCNbH_D5MS zN@_ezQIFAmBoGL04w~UD zpa@J6x%J1PF`$O|8E5=7&o&}#x{5h(r}oY>n-9Ws6oovPc%Y=7HY;Pws}QXGtr9f0 zL3B$0ms3744hFtCc75hv+0_&_D&Bze06ib5VUhnJiXuQ4bNAPJX8O#gvO6fL0;H+k zU4IUjQ&&n+303z7y%*ocUf!xv)bD&EFO^*Ym)q~37?Gd-K1sbOS1T3hW}We=a@I9) z_p{BG;KmWJ&mGu=^Fn!a05OtG1>YuzF5RNGwKT|C*EPj(`?P+XxGLtHtWNZ>(qG45|H&Y z1?cXC)$^?i?Bu>9B(>>1;SgZ?3=ErgyhSN!2GEsT($2G(;{Jub{x~pJQ3eDXAGJOu z*laCYcoKemglT8-45qBN;Z)F)diCg4o^&fsX}6@mGX7(ie7$c}w07Ts^FBC&L(&z7 z&5eEBH15r10UelFmGXd&wD}1w$Ml$HZj5t@dpR@z0#;Uu1DKM^i3t}eW zSJ9ka^HL(Wu0$-XZ560PM*^3F(DHm4l+0VUym8W5A^ z3g*R=o+Cezny|r(Z;;s0*`&$<*IqGNYfU5UJ?;#9M%DkuauIKZKT2*A{t&m-T z-$j%1i6lh+*hTIUD?6lLZ98sIq2#?}uajO7t#<3E%o5ra4mW=-} zynK5CVd7Y7x$=zgUb!$89%E@-!N-m-My|HVBi@O_=Ykx|TK@$=2 zEB|pllPV7xl-j5CO0f-KR!A81oYH?4s62VN=FLTW zB!M8LtFsS2{M#t4X=BrQ>fT;I3sB7XK~)G8w~a;?d1gIU3Xae+VXfcU!MM4rIi)Yt z{sd0BxAa%OiesZs&AiAzrT4n=Y(&@Ha@pTDPC$0O>z3{Uk+tMAFX6jw_5a;qFqf&A zF~ZjRWuv5v#P^pl9)$$^UgD7+px3dO7XSGs{3F+3U#9wAA5dWZbL|wOq2$|) zl1p*)&cghoT9Ol{L2jycNQN6kueXeQURQzyXG~HCSJ8+c^4uCR1$xzRFM#d?En?oY{4YSu|AwM| zagN@1b7|_mYRmE9nrfLF9}BOD+YVUm%nV!%v)=m@mX2+cA;eM-#JfY``*`RP%-|Z)~}Z}hSOJf z7N0R3xT>(!f{L`|*VB6T`bLQ~K)(_OB!b|wC))P~1w4AMW?b~?sQU4iJCMD+zq|?7 zVqzChVqHj_b1$(mySQws8ic5J~bM^;6SJAx9j{6ZV!8#SQP48$>fko`M+ zFYIy!c`x7csVvBd3;~LLE^vC_b4p&J5dk7AT-0nKRTfYH`c_g|5V7Z5%vEIti?)cS z;rB0|ZqGM2_Pj#SYyG|i+Q9dcrI-o}7ega>u7ZJx{pcIp?hmgB6!ZvZC}X_1H@48f zLemZxe9fy^qUR|};6IC`j;c;;#GZYq-;zLE?=&2PKwMlX#_*&P6}QAvQ^cK(piNXv zplYaPvVS%Kn78%=SI&9-m6~NvP540f9Ts8N4$OmAMlztuokrX`__YuY)|O&L2L0;5gsOo_<~&uLn#T zW!uCyJS`jZWy}auRmrpW*eYe$`3e_%kB}vJ(p@RP?2NYTWOjo=oMZ)S4>fYt%K}j2 z5fHrw8T|j5Z@%y;NK$_S)CllE>*zvT9ks?%z3D$em7Ok8rIu&H7R3n*+J_~Z?xgjt z|9^YqdfF~T`CLdHcE2@6-=4YHY~^xgWT!Sp?f2*u05ZzeF}@miYy|DvcN}to`I>l$ zY<7MSYeR%2hs1s&#<`@bH=flPc$@Vm7$2k8s~)E2q<>3qA8-4DlT_?R_dpjC2PPcV*g1tMiWXg^n` z52+#_f!8qomCMVV1A)<(&0^27=HJ56_WLDgMNyT;YKGa6DwapJB4D=j+G;Y%e{uK| zcKkIwc_2VFaCE9UYS#^(fB5gQn=d@qk1i&uj?}ICgj$n{!+?l|wEfU&RBaPF&7=C> zt$n99Au`BH?%9(+Bd>z3xa?Yt<>-Q@eb!^_tM2>u$Dzd;kFxx+NQ&L}S|BQFl-!a# zjWE_C_7WpF5{DGW>nIts>3NOxm~X;GzO3p6A+Ke&_H2pS-9(3lkVT)8p$B|SC2f)>zfwm>TM8K$2&c`l94eVl)o;v zu`gdau(CKR=vUeUa~xItif}1*S4W_Kv!fff0`e(zwYw1X=&ECn+yOn51CIboUZpJ0 zJ)F{S^++JTDl(T%A7Kq0DE*~+PNy+JVn_lrutc`~d*MQxB7U2Vuh;~)`h=-`0T%>V?-W$=pgm6R=`mg;~mjuF^4X?UC z30T@B!xnYa>{X<%b&D``r&#a%IVS+24oYXuMdROg6VoJv9yKDk*d^cQ|bymi4UG-T(v2`<=^TK}6oZ0tIALEDtB5SWoAq zA8%+k?^*Z_99n`0y>l-a7$C$yTSHG~%yHD@DA7OPq^4|EF1*k8!4AkeLvrOAVOF2@%^PjG;i+u5Z`{f%pR=&Fem^5+V ze}C1@G?6yo1=QiKhB6aUj$_Ub6Wo&uaQEZ)_9y$NbP6=n4jkekKQ?Ue@@55);}9JQ zG&#T_fLuqd?@T;qTYWa|E~~4JTkowy{+yMv&`H{|rRosU@=VByUhiwaW9bjgG8LXq zMtX%cm|5Wn6)cMCo$%6hAX&w`QH)v3N^XZ#vE35ssiQ}>}0pop1e62@3ilCo)qKckIHAtI7Sf5i^O|R zE@2JAUQ&xQy+dH+;VL4-JYCCpw}$SaVI|6CnWX}Z$mRP)>*aaR^p7Ad?pOH9R@%yB zm}EzuDWZ!bD2}8NC%n_*k`uG(EuhO;ze1-+Its3Ri%x?Hv?7zKq~)3`u7M#VJj9rL zkT~<>NarB*E@Ys6aLdXuX6Tv6PHp6<|BC}X=@Ro102Ri3%Y|LrVFL>HuHM!7Nar3Exvk`fp zV%V%$6#O_<__g*l;}D=zwRIUNIx847jE)YjuZgdy837^TR~20Yr?B^IrvM!W|a9%;8i^Or4Bz+*O{lyo#u;q7f3&)f&UuMmxgsI~ZU^c#rh zw4R0{t;9Bnl{HRImZ-%Q&x1s(=_sPyeG-9x>V8@^A@zB6^xvGb!z z=vTu%+pV2|z+Z&OE}d)$zA*a3*ASjtFxeVQYJU`nX+)Mw4o07Sg@4kf^GG26`>VaX zdOO64+Xt%0z5PjFJ9A}H7ZWQJFEKOF)cj%EBhj9j_z_k46O|ER+jcqk&r*%P2r6LyUfn4dzU|zy zh5&+@2^uR8YLuS{R-@1smR42clc<=MsDq3DoEA%Kk_Xif?>4DPG&6fL4wiq(cQOUq zA=>YNng=zAU(!H!u{G*9YX;X&PUlZtY%6u90!_j3&2^8g6Ur=dQP;M2FVze*|cyAe-Wh;8b9OBdK^9{o_~#0MQ@pE0{N| z7}vSXU4e&0_g8Cjbm%5Y6iEdnA@JB8K$x7>zy&y8uzL^KbV~1CE->h@zqt{m8~)^r z&GxFmi=Xx{ESZJ3fQi`3O=w=i&A(WeB>a)vg(uBCox4D$EYd}YDy(oeZCcEaqym)M z%rQ_(|IH9+WcuE|zHk4*@{H99)IdWzt)8^<8f}BOn48C}(ld@7@H}fPjz!ypCvP!{ zTVbWR%~wU*U9KREk?~8P>hIueO3|LV!yyM?lLe!#uNGvZt}A@Dtul3@0g>z7Xj@Va z>gyx^Z3ks?0Oeh#dJ!O^e~>7b`E667_dVL1(%(`aoLTZ1-7y)haJ}+;&}XZlc32!~ z3{D-&?yeq~w~Y#us+7%RI$#DYWpnGEEZcS*Kq^T6mJYBRS)iBp2sr604~FJ1NV2$n z+nFCIw0#X)@Zg&DP$YTp*jvr-cPe`WsdKr||2=r+)B3;MKb^I_2$&@ zZ$UqX7ZcO}dn@;rc#n2OqdtH5BsMW<|CM&jcnnRNX8A$fTd0QB+v90)MuC-{`?|QJ z&ES@REmYQ7B!_5#3Bclrf9lP)bxk@FieX}bZo_q}Kx#l;?Y~w+17UpCpLR1qhC()W z&9-MyJdRi6y4-i)EqygJF?=T&bp9vM_@Fz6PFAj#Wf+{tuv->8Wgf9rNJ%7MMbC9* z$EaqI&aI4ob$o^&B{PAR#LXGoTyJvRHl}h`sUUo-uy^M(&_P}N z35&M=I$glh^>g0^n_$(@y7e$7)xf-je`mckFOY8f2ZAYEI*KFbA~+3RNbxU{ZngOK z-lm1s`fbnTD%+-P){3viOY_RWfxH7PQl2g2di+x~N|q;;WQh;Yv+z;-bnkJn z~Ol#7E5@V=7?pr6!(m(yWe z!J2Ap@uMbTS<5c87A=h&hRtICdCM8~y9zNduZF=VH$Ms^))xoo^PXBn2@z|_@3O|K z0e-15PhfFed~P-4&dTw37+6#rHr6LQXN}#@5IN23vKL6C6}h^uo!c=+6OPZynyx;s zNM00`D8+3_tj*^emo>qP!<%*t>muiX;*4=xVWPpnZg>po_+iffJe?SSehr&`nmvx{ z+FkNGeuRX(ulJ95LM7OXbrB=EkS-*D`J}6r9yqqa|FrMVM%!ECNJ_bPh2ND=YPm!I z&<1}FatbX;0_l*RQa?M(4!`PI8r^#Xzp_7?1h~P_vqq*}3q`QPocq_ky#Eb|7@UjE zozk8AsQ-mhfs=GD6sg{WRmu9Ou!FkBo<2Vz-FFi zY~U$MA5tYd-TcsL*!S9Kg80ZSuUt>q7?=}Ka0g!q5NKw6NfcWWEoCz*G0lr#(a?`j zaiJ%m_U7?`fNyiNUU|U7y7yY+cO?|eYiuFP8c|nYxm_CJB)AySEIUd|8@ibN0>qIY zEbdv104o)SZx8#J=+p`^&}l8O($>f{OZVO2Bi zRoOQSo~&>!_)OO9q(prx?eC;|(Z%iNL$Ojo_73LMLbXj!vvC*<9N z4@%qwtCXhddjim|m{T%uJOC!`KN&SRO*C(*rP55U$d4qo4& z+~w|_O>#9%6e%`iuhL!Xoawy0|8*J~A=70grt=!zmJZ7yylES?q7^22C76LHxBe{o zZ3uP;EnJK`VH%NxyL93UW%N{O?&M*{cDRG}ui{g|AGN~+4aELc=3lszZyzFvQ#?@n zftn$zu4+~UAK4p;RfI8Z-%#$1DzGo_4HiwV%%8@PfwqXPnEfIDw3%WXmk|JbO+$*MCl=Yb7>zOY-gr%i0 zIbR7Vk?q;qOwm&D{Y^4!7y|sw&&VA1UQK6I<%}Eb-K;Y)jHB9( zjbI5`w_Msk2W%~0t{BXHEjNc_3%hkcEVv6%&pqg(Wg{;d{{$Wtd+Ef0WlGj4pc9qgHDIqV_;CyTk03tZD4z3A-*p4)H4Rr0|eEs%c(6~)Y^ z5L`l3{ZH&9`i>IK9YiU9fem&u@+Vt(&VaQs4-VL_G0ShMj>bii7nMrCKZ`C)%GwF& z-O0cQjfSX8l)C@gf8lTMk`k78lZ=5GiPkSK?dvO;~PEpMVQ%SZqDb zr8jP6>y<->)No3Zp~_)1+KLZN5;tgmdm~H^z;kt0eGKj{&`9iwA#JUx7K|Ij#Hb6& z{}Av4o^e%BtPEz6tvmv^fcJo|s$|APlEc{=ybaw^Yp*Q>*$7c#>VD`X5`mLkh+@W7zzOqsmR7h^FBsAv z6UqF)uY!5|6V=9jo1361KQjI&h-)y`yWRRi$}5kuQk2W`IrV87o|Dpy@omZ;dxPAs zp6)~=Vy{RuTz-MH_3xzc9D85np(Y;G#hUI>ss3C$Zt?3(SNgYsi%ISmi!h~>Uo)5- zZ87L07$|;g{&jVycLc#+*^F@lkcdWfXo1t{lV4`HfG?JyvV1PhvBs?wuS3<33;h=0 zqN<^Smf-Al;ep!#ZO3bEOjU{=ltkmFjIQ{rbA)+3C7xvu!&yuu9C| zF1RjBg_-%*)fWG>zKLkI-0H;G@VPqn#4YX2jZ?33sqqWoWmv4F2~bB4)Vwu&bq?vz z_xi-049ITa&L8SPOXmY_;X*w}L;l0OpDDd0hoz5qY)U+-=mj4?zGhOIKnpSh+dYfg zxrka9$;njP9z`LBZcQ`>1OhLRht%AR7AGb_@3l54SH_YLh{f{phc&B2=ok`mSXDCN zXF+)>D!i^!gK}$eyC4SmTBF9Vh5HTlU4NhdBkIZnng0KO>7&DuJ~<e5o`LWY+Z1AU#Zq3tyRJckp{^6CZ(1S;4b*zwhB& z8#MYR7eR7+#sPCKVPU7k_xlNgH^|yl~Jls=8`UlISC!N$xP{((k zM%A*x*^WU|(l%5;2|rry&bC{~Q0gtN4#4OWf`IQeV?$G4FWNf16h76Cdp1#Ap>bS1 zDGJ#H_>k0`tCLlsKKFF11VQ6-Hy&1-{rXP$YyUgnXBbIW>~;Tn!VhYXOn!5&HX1L%yjFA@yPkurTsl*d;pcHdg&CL5w|2tW(ua1>E>E6Yw?pqOD() za~e8vY`lqXpi?`vfny+kgIR`G(2if519RoZ{XG()xDXH z;v!DwZCHdo2tv$3L@X2?i7t=}b5|@dN${E_7$p{0y|}wYQVyeNND_qUp^99lTOsFw z?stj-JI9Y@*RB?xz>3GAmZCqjFxPZ_v3I4clCee)1;MppC+wn7nXAxu>3FooRFbiO;M$ZtAXFe(>0! z9Wn{w08($>PhjiWjM%nhWqa_mEfG&`Jcm5!s9l`4{iS~Bd!S>5NJd5 zgBi7@MA(QYwPIhDxoSEb zYpWs7-3I*crAxfvQb?Gl}%PKEBtpppI#6J53X z17*l1BC5#ssTsBL&pt8eE?c61UQ%E7IbB+lRmqolr>&;_tfYlr+vA~6H#XH**iw=z zF88>UELM+i{Miv4D$eWKQW^~k^+^)T+1PNq-l{Tg1#L|I7EU&2@)S=;KurlIQ8BPuye73Iu} z7^H>}RxRV9z^a@ru8GyNAh|P;n;>*sz?r;XbHb8V6Nb=BfwgY1e!Zw_EjHO?gD|v6 z25UIBP}%b#vv0_65h_28ihdJ_XMH>JjUKFpUG-pKJ~*QjS-bnWjYO)#t`21?^dedf zNF^!x<4`7f;xViAupx;9aNp|A=KX;%^8O99wEgtc8D{66!&uGBBGRaq^Vt@met~w_ zD0&5ke78&kjBGos+=)}kzP%dA7N|BS!K2pDtQF!lW+vGG2AwcOjX=D}iM)5}f9=il z*f$X_coCI~hgZNy0>=QC%RTczC|t|Y^1p^Y*GpjUUhKLCck0dZ6Ne~60h?2IKe(r~ zXw$0heX)`b{~Laqt!Km6=exaa-704b)l@O#WGn)Td9qvWvy-4&DXPyc-~4^H_Dhou zY8V&TA{0h!ZhPuY;D3^l>gOZZ@Z0YQk1kTi8 zG3VoEPKSUL)imW7D@t=55&hi}*Tl_coZkY4j`zw<{q)B9O;FSxOc3qVf_SN!72z;V z?dqJp&2BQSQvT5Z<$Hbiu;2EfZAxpV=8CB7a_Ia_A71Ffun@;<1(n~nhx_TnYQ?;g zBN{U$ZzF!<3Mt?hjY9F{)f_(cN4!fkcU82S9(s~eurb;W3gSDC7uoU+%zt}AyXi}Q zCwjw=NmTuJe0?4p4_I9Q1jZ)g=BL?RTxl(NL#ik`!ij?-{N;TV~1Y2s_S}5Id{^V}r17zzm+yPy5a3M2*KVf(5+YJNhzL=i6UY zD%wWT5_0a(HOXp>u{Io9f9g2fQGCzp<9>xEP`gSaS9kp~9kxk6Au2x*vTt$xN=$Kua>LEmTQn23LJ+a8e_W;$@7wl&G}wa%{-ii z0lX@2?NATzh%atK;qUH_ECeVBEoZPtv%v4{iq${g&X~KiMI3TY@tJ$^30p-_Zu7EHB?!|thW8^za?288z%)_ z@pr(85QNbI^^2yKpLF6A=O2(J@}vxr9k>OD^1A%0@Bj?ku%_7~68FsND7ECfCL^2T zNg#ob!it#r%~x4VvqU?lO>fjc-ansi>w-V9t4?uqxlv~92;K>9QjzmyRF<~ZqSsdV zTV!Wz)tNX7gz$4oPb6dJ9-@qjSNqr3#E*^_+SmCoT`qwX(wi3;s{d0V3Op_)m&C8DJC4b!g~e)Jf8Y@z$V4YJn-r;McmF9QXpyMUrV`ydS3hp z-+z5_2@z7+GpJSZ^>1;58QsU?@6Lw4We{ho|LYJ>9F4336QV4co=^fx5&gL@n%cj@#-{mnpE7gEG?bj8#v(xo+EVAXT@hFw zFYnK%9{%2NbRtS23J2+%+O)(WR{mGE4k&FHb*CQY`jnW)^IGtv^`{Vxj zO=m86(1RiU(h=w*kk^*Hw}Fhu$v?Ty3hs=3gFm5nGnZ#6K=PB`m=)$HUo6a?^8?6s z@beBM5KCoHy?Q6@W0gF|u9V@mcX!x(HAlRX-xaQF0Fio%s@aKhy67=#tU~j=pTaZb zVr$UT?%ZAK80wgM-)Z3x;mGI}=#l%WRRmn^9hSu+1Q&&czuZ(U?;CoYv_C?!MtTGx zt&5C710g^SOGRI+iB)3tNkWLQv@L0#;#xpF#5_*1;{+*?2lDtt}eXlRp z7kw>U%Wn42-<4MZE(%5o@s=yjT6_tp(f#Cg3#A2nyT1yqNVcy#Y7$r_dYBAW@+8?8 zBQ8`-4So*XK~o^h#!8Y~W?LCKmS5m+2<;Gz5x8-6K#NOtD?{e)=#K_FW5D+6lJ@l= z?GWKR&ot168ZngvG)B_g;j+2v1@V!bog1r-1GazH%a$RM5ibzTo&FG3{Tm08ZG9ou zA%EjF_DFlDB4&YW73M;cWF+ZPu}5yxEV&f@e+)e~X{^d)JfO;m7+8i{ql?qNi^>e4 z2G<|{i!&*8wC}Ih5Qv5u?{xTmK3kzWrlEs^59B_}D>FR@Y!+ND+@NX5KgvM7KUVI) zIWbjN>zVyvV!m<>nE5iiP0PB`2klCP1}S>p>cSc&G1v1N9O7<3*tB^4v$T7(!Px=u zgm<@l>eXP}5IoF0yTI*dLB(r*eP)=v?!nEbZa7E)i0MZZT)Yp`|y$1IG4=K z;43v^$ZkF6Z+2s=u&2{BSrb|d0ravdxwc7S9cxA>hNG@z8o7J3c5#F35?+Mrz~J}s z)44;m{j?BPe{QquR6vR!9|1xubVRRq7)5(vUrNw?reGv;!Sk@P=0!;x<`UxPG&KO$ zhm8qVJxkwLN*CaSVTpFn2K1BG#V`ZS1aEov5GrSdQDNmU=Jp(#VOp@E-@TbX?0Un( zOg6)YMzmomZK9VzGAXQ%e(EkmcT<$rIqk5kb6IboQrknzIhp=qdA+Tvh_hM4q8WiM z?O4@{UO`*1RM9wp@z=v!C$*Bo4M0OIRY9(Dx*oSYx*sae>gk73_O-+J!79N_jLeCB zzD#K*28_&1G)ujY{K^{4-RQ1l&FL4h`%G)@zoA}rX9^fL*b%*@Al3#GHV-?pd<|8# zM#S%uG>p(kuv5DwtIe60cS8*PE9eadSVwlN&BXm#bU6=yXl$?(xS5E4TZo1Wewo}X z-j^4XB46nru-m?suM6<9|3DL=fB3DBmks3XA-nf$rcw3s)3Tc-u&V$Culc(W+wzF{ z$_(|xV|SK+HYT>iAGT9I`uZS4hH39Tu9|V;>3$XouM$_-Vqu-?ilr@8JAT?z#qNsk z(J*l4cl(5r0%Vu02p`kuLvSVjHvosKswW_$%zYqzGHoSb-*~xnT0nruQqu zKvc;P_7M9P9EkoWgL_9CUGwKQ z7c2%s$3)}Kbw5GLI+Yu|ZalrI)0Y={gKZ&Ocq0x`t_z8#WJr0V=Rs{)a>$*wQYZ=n zo4BrGia9S-AB($9_+>r!bO>|`xU*=_peN$?3QPp&C?z2+iMX32Mi(>fNK(1u$Q9=> z>fB5O-+prDdvePbwf&mBb?<1O6<;d`fez>iJ6e3JM%#IUBc_!WM6#B1`lo(7C-9XQ zm>8L%>pwVVZ>Gb|mW&TtmVd44HmQ8@_MXt#i)l~TayYhDU65|KY!3wlE3Z6!sjbNhs-*;-m83o-%J0!CwLuO0}+76bDO5S zp~Up$tj$dea?R4qWw(EP_J>Vv79GhcG30t8c$%E9vIOs0{ho=|7W8?H_NmHuv$z;9 z^T0l-WE@sJV1wK9oBUA~W~`ItSuAm(?-?nNRk}7RwI&EEckfa&(>ldj#@3-`@Zcdc z=;LU$;PLMvi%qM42aan^yu$Wr&5`ruK33h+lkA(8Jeyr2)mmHJ0nzf;hO2#uaobja zW^aa+FTn`XU`%}fEue|U5yp($vqHo^F`D7sSzcdT=v~C>=e;DTAO{A~LRM~O0 zkJNFcR26riYotw^*GHOtb~c7Q8c$>s%Nhf1x+`9JY*8sK<}_valL-WAl+6siElv#1 zZ$+f83Tn32dAP$Ix|xOZqwD0xa&t=5K_nu72J>i31zitXHkpu^F{X!Xjj2z5)8=BX z+ALmzw`fQx&Q4O1;Yd_4MMb>X;SQP=nok$uTmjv!!+mbGB=!91^x{pdiXIsWJW_9J z%G3Sgdwygrh~5$}Csrz@ESG4ClTBThQKa^PP$KbkDZ3TJZQ~PwooA>6kXZ5wSw&>cPPC8q%7Q;z?}lF^ISY zr@6Vs1S0Ub)vQBZnk}~~x)^Qw4lCb0J-_dmJu;@zueDBnwK}G0>Kd+13LF*ky@Ooo z+zn*!d2*9Smg|oKKfZU%SfmA7QrEp4JfQhi6+b>gEyCh4ap#+Qs)2IRf8avrPyhR% z98GW>wl;&qD}&iHpSJ7p=gJ6djld1Mp(XI`orAdOtWSzr2pz%A^YXJb8{^$o^XN+& z+H4;3R;BzC8!y<-O(N8k>+Qne5NEWqv zYgl5go~gL>))&SR1~}M^pwt=HmKbu^FgZYNoiB?o9@?xrDK!eS9NLJS3GShMmHyXY zF#Q?V*~)j|>CE#nVjtw#%!T=ho519C@C4$M(a6UPczw5Lgj9U8*1E-^LSH*oWiIo| z0StMxNs6XNDmpPKbg`nilS4a9>l8;~GP+SQ<-w);#ThmMjOW5{k+gzmT3b{YMSrfl zE>L(8bz>$t0Kd)yZ77^=@CM}#2fL!}v6sQoZ=$mI=GGO^mAyIESF6Xt5bGDmO*0!8-GX2}mUU0&U@+}~9_DAw9M+f1{62DCm? zHaL@mlfboma3Yg=@E0ov?{c?5Lb+0JgJfjoE_(`Zb@q+DixVqf=eRYId>PCNAovqo z8|Mf1Jj(pIHFqG1JynFn+qCyHlX{eISCc+ zz9!3F)?%nW{s0$d%fD;=&BdTj?`6};D74H2 zz~LCZiNo-MW9uESPN*2>rxk-cbaE$dC|}DdmV;CX&*0uPZjwxPq%7W=y(cRyi@M(n zr8G4}I7h#5$b@|ZWNdwP!PK^KEd22IouPOT(}e5$9wT9r0+ULrPjG71d{1!uE{G2y zU(|HQRI53!0s>j=MREgkbulA{uwKl_&W0`P^go8gPn^q``2dFKUS8fw)G1kOO*dae z5Y_EqV``J@OW8+3Cfvx~SN*l&ZJ*%2bg>ruUvY=6Q;nEh`7TUH%5{Ihg{D* zBL_D&(z=K7IeN)o2RearpuegHo8o;Bje#>g$Sq!IbF-O@EJCaGTr)zJ>yXz;)*aei z^5LYO*QdP(trt+$IF;!r^IH{RS~n66YbmW7ADbOWd7UY1+1$Zw(RRmnMGh1=Wn~p! zY1i^l-}CJm3aVuUj(ylQ9gw@52*VWbpAOm1V!8#0<0}p?pN*a*=QKqBl|53WB_)8S zh@XdNS0h5)3hcXtINFKw6gNItLk)fUazCbah2hEX6qc*@&#7t@&)f=| zgdxuSdfcyH2rx5oX1`gV9xmElRN`sw9Z@sk9k5A48cz0MEbF7e@C?(JZsOBZSv8k5 z1{w+|l}dIa1Z(!>p1nV#QJ$0lGIl6_roN zt@GO4xSMPt&A%N=$pEPuOnx(<6gvuL!`^lZj`=TwCYnBq!#-4DU_9$FWfv(=9!z<- zbz9~tysr-$mUOTS~>!9*~CT3Ybsa3Bu@D@+9v&qO#}yJu93*8=*-mqd;MOS%tc^O9i z@1gNUtKZMWkAL)k_V!kO3qThC8D4DKDW@w?PhFkF<&a^D^o`Y&6EopbK8LKh*Ecvq z!*bCTegj%ZSppAYlN-4Si>Rc;5X(^e`ZM{Ld>J`X?*29Zv+dhH5)IRT@zGu$Q?Id4 zDtKel=8{@vw~gMXG+M0FZqhO!Wo%^!2F2!h9F@J_jqEmzsRye!3z^2RIuXjzv6l>x zl|m;rkH=V-zaMG}_t_}ACF4cCSvF5BF})qAXcB{r9qTS#&_m*0EM`ywJ&Hnn7d1~P zn6N`!7+!_C(QoT=!s$yn|FBU$eU7%9;i}8|?FuKlv@?TckoNtei9ioy&xhJsM=e}~ z1G+QIh)B_+fN=TgJ$4DRW&CIm@5pIvo{lgR9~r<)Hao0%@vSyQugPW;<{znQ1V;4G zt3chl1c#5IfOTp5h_#N{|HvM^Yv!uh$A0gqiT&9cLM<8wH|vsf_kZ!Sn&1KO zm}aPSjL>a=1@?D5BZS>1>{EW@Zd}Y3#nTtmsi{?BwlJj06x^eZbu;K|50ScNcX~vj z=?Z+t>(}qy6N_1!V6I0quwTr%amn;9V)jOb+P!2RoDVw5LsRm(g z0rS%#wfASvMRH8!PBUBiR8WQ`z#$dAI4-W0*9vk`2Wu4%FY`)KRo#$=({-Q55!q7P zUL2M>ju5%|C*ZIkg}Ez2D{T&oD`J1)^9#VuGA5|jePZZxbngq-AYz3+xLRs-%L@9n zg=ATo*uL$n3?h}w6~*~cc@kx(Z~9xMBlM1BCrcQ=Knk6gYP#Yr5hDCLg&=}@T`_2f zyH7PAYS!8I&)vi|FP}@3p!811qGBld`};gtqd(=6*OSY#BIa>M^x0yLNp=_9YfNpu za`bANBqN5}Akh^OUZxyu7o%nQHc(8)7I$Z-s`CK%8o+)wBWqDuGOGJvt3c(@F_;um zzeHApZ!Gl7H!tS4TQ)6sx5}ms-f9`AQ5f~H{>DS?<_J+%|S zJWpzlNl6P3{(zrwe(Ec3CN<9w;i2;ivmOQyd^drCGeFccLmM$;mwUAL+7!8)*)83Tf!03j2Xz{?ba0P7eT~5wK^l8A!Z~X@bf0k2O1^uOlTAxY{IK&O z-G#GNKlR6RMQ3IrFT<0Ef?uw7D{WQ`M@niF=Ng4uzv|bscllq_Yg9Grn^vkwU)d+* z4Pgg%&^~#jF00~38S)!CU+qK&RmV^xniBq(J@V&5J3+-#AHCu;EH+ZIG>i|EYSfR9 z1$M&JD{gB6#xYLF1~w6*eNokZNn)yg-OY+-CY>EAbgoWhdp4#?^2*#lv|}YVwcQ){ zG5uXqv8t1d&hA#z9a1~jaeNf;wLc^ls8Z-3A`B*}SJ-H;Fp|wC!9e{FxTd8Ux=NvW z)6VlTtt+Jf!+VE!z(<>pBiI#lt31U`A zI6hQ?%sBitA#u(|8jX>161M)uBV-I>a;8+F?+$>mSLbMyz#(#|XZww56;wa3-EHeQ z<++$9`X8;uqX?CsPz}UgH;o(Kx+3B7Z(2Ap38h-)i*D@^6$hB(_3Oc^ zh7*xGWuoHTi9Vcha&D>`+upo(B}i(}x7Y!EIiy#y>c<#r6HDruiu5}+aGj^0!7Vrh zds><^&dhys3(dsK^f1qD`nZVHsSjOo@b8#D@#i}nC6ltZPViw2^m$5M_Zn8?`Bw0p z`f!t}HsoVA>lDdYV+fkV`wNfFeZM)x`L@c%UZ*fTJae>Cx#tF3IQDy8gavy{KG!)_ zZzFQbJ6>MT-eGLYbxxPONH!kFirC#B>nweLCuVE*{$zp%Yz zo%h~gAf%j4gbebO!|3GO6Sc~4cT@EP2pxK2OtG>siFYtbBMk1=$g*4N6?86cO6+!S z-0^=%pwEw00gma-ja9^~6r7kWMi2NW7I}-fn^!*O$H0NrgS|d?%ldzP+c)wAJGXj2brPZ6k%F^$ z5`7ZR|IVdVFkf^qI(w0p)0K;R2HfZN+Llz@por6D6O%wyQ+{>Yw>p8XF0f=8QDFAHC6+u+gh`m!hWyZ6#Eel<5%RjO3P#O zEfR_miy&V7g}NoQP_jj#8RR>`=lOCkw|XU@eCSxx^~#6Nvd|f&2RKX#@oclh`|k|p z(AFyZVt7lh2*vp_|04gtH7(xS>Po>>qV?1T)%(|?E0t6yM=Jg5ZvtL;PJL{M__^Vi z7G@U}5yFY;&?PD@;{4q&Jm+GChg(yQ!%KdEna8cNM6{4NQYUuNwr(w-i1dv# zW!RlAI^saY%2!5o0}F9$4{h~Qwp*au+1+EVw-zk!sfXM1wXO;>#af27($NRWGF$&m zUXCBv7N-}|m?SPVd3x68oxpqfGj;b~Dy1JEuyzo$$Sw4>0GEc_n{@_@dB%Xf-Fq`7;t2jt1E7hfwv7+SKXKhJo4Ti2m zv$kGW&{T+*3IWy{6L@t~{*iV9Vx)kjRYx&LC3bRzR*Q<`ztpo(b6Pv}59dZ$!Nfw3 zy5-|d1L$GEvXzdJ-&EigLn$FMT}5-nRp97X0@u^P?Yy>`6EbSkrT*+C z{|}=y;bVBfPDYGa51D;2yOp=n-OLuqt!Zo7C%|_1vooV&Ac{gCDeE1)7Lf3??~ZP4 z+_MD%J@UMmU;vIYh2mZ=?f`Mhy_%QE8bU+6OWE2WfkEoTvp@>k28QM&f+5B`Mu)S_ zvT1kzj?_y#lWzIwzxhiF%vYz|ZTOcxfIAWx?aI}qa6^GVCYKO8V_EuxbGl1N*&mRk zI`|6th9=Ek?;XNaRB7_yz0NTo4Bodhpw=n6syxl9FMSkuvExbf3UkS2EbhksO}jV@ z|E(nCn_+iXvT_AN4^}VwcQS-`E6itFXFds-R5lorg)K+pHZ{ukJB2Q&-D9JM{+Eat z*6=K3FnBREqtw2)&_0aG5FVejTFbrBe{Sp&wBqVh&ZKbMrjJ=XOD*m*&^H1p`)jdpKLD>Pg2f>szS z9a%g`vq|VGy%n5HawECS{hVnOy-;yrh{R4BIy^$0bN&GFKIxtmR=d^nsAKFxc5`^z zu-Zugf9w6Wh|wSC-splUF1bINP2R<+0~0&8bx*Kufj>!^f>W2r`I0VH24WwK0hIl5 zn7m`VYdJ&nR$9hm#I&j-M2(dZcmGY>KhOp5{TP?vk&fC+JbiixGsxz>53O7Ycs;h9 zy#%XgR-4fd$VBrApAxV@_J=Ko)BDs9!^SE0#ZoTmHKbEU*&gRj@FE+Qk;gWl=o#3( zgD67TG`mDQ>Pf4YLNZ33`BjQ{FyT}utO*#`fB+~~LhlY#CI)GFH=Yn^J*3C~lMV0m z-;vJ^9oWP8FxVLG4mq2HRFONPS{HcY0oit#>?rWsWBmawcZK$76TP@4bM+gNG#GxY zFZ)RI^i~kOe95>j$)_L}VD=C3OSCv|Km(Jce&R1>wuoZ~70FT^gr6`9x**3{hY=qpT3h)p! z2l000oVkm^who`=v21wLpobsd+=w-CI*4BuwsxvSx@OBt7NF{wfV_>HBf z0y%Q7l1>FgM0DbYI|HqA9GJK#168c6azy6$uXt=A0UrY{ew;UF#v#*QAzme3sQ?%z zS9q17Csg4-*p_#HtGMOx%&ky+Tk1379ac~K59ncId#2J1M-N1&{|gDaz5(dnp9G~^ zJB5**Q(kSbPw8F@h@Rq-A4{_jbahW&4ltTC_qP&Esc?Dg`^zDJmEB^V_B@%P3h~a2 z8n?!sJUp+qRdLf&^)%ZC&5TK?+@Wrwl*K+CPquhf$n}QpRJHesMy9euI8uuzMWVuXlNC}GXwQug*;^4&U17Ti80nNe zwk$?2$_ETs>wm_`WIfmd0XI}`#F$6i#8XyIpP*~>}56s{*Y3%BUcan0h-aX zu=DiwgBbZwBO6O1qmxYcd)KaW0lvux)yvYWBA#HFBW+KbIV0G-0gfm$QUT?yMWktT zM4yN0MEdlF(fv!O5GB=X!M}q_hiCRR#@L@``+>vnWb21Vs19{|LC$i`EBsKn zpR{31`jW>FWy6E$ap8gkMh6LH{b`}kL7Ue*0J^os{T<&#Rqcqk3e9^+pC zg(Z4D^F+PX%9b8+CconuM!i@>VgZV3rowUEncul78+#?K@;CHhiO6SZZc6$AAW> z`XwQggzT5?N49Fe1$SS(Zte2|>u~g_yVD)&+7NZWYQC>Le&|Q*O6(g0*7lU9Oqbo| zjz5pxHv#2X%uJH*M7ZqnaKTcy4#UE#J~^<;({Nd?Z@p0wY7oTiRQocUvKm;{X;5s_$2H2W!GI9_YM(&O-| zgR~|>K8jBco0)q`V}*n9xwp9-AUxgfQ%{0?3i=KFeOB2KCZvZehJ?)JaR6;DPIKS0 zD*cJz^?5}5lBJUt)@w*NnUc0Z+(6Qr`Zpqx!jmW1c_5?8{Og9vS^qs}rEl9yar16- z;i+F;1y9$0Vn20k%SVpkC#8ZG?OBN&N`Pk20gw}U+Gnk)iI|#?`~IGKm8DY4_8PgM z5(l8fc0VgZ3BqlFuX3d2J%s`F@;`u!6_1+8hml-YD?$1!;4yX}l6`_aJKYTLm7#^? z`dqw`+%0bOi9&%^aH~f3^n@68=SQE#jLfpdGP%^cBdhf6A&=zcE0UHIIong9yjB4M zjSH3RFo2efB}ivH#(7*J>;C+x8~CRw7nLT6y@!#7MopBam91bMXcipIsB#P7TMe{j8k?B{l&sa%kSI4eiAU<g_&r3CJl81jl zCD?SX^!5YV<@*oFcc`m{q$%^|RRdv6HWqWYonr13v0ILd-i1~>X{cEWM z-EWXTOLnFtd)blP%t%o0%xjkEHbw2X2TY4uH+TwBXCD5%-ofkJab`;6r=ZMsB(ri2 z?Yda$_K34sd!mK*j`7`*=C#Bw zT=V6;YFb`b&_zN>FEHEIx0=~-AIGV%Bzxjhs@IkNLCn_Xh%xQ-KOMGyL4vYiyeyAd z>VvVRZFEV0pNs|sp=0CVH@SgYvq@rUYO)%i&2G(yz{n-Jf>S>M2phob#Omdbb-GsN z=S{6xTjDli?Qm^UJfDV6z`%{|v9>1JX?e4CaM(h@5=Acir-n{;$)eTkRtX&pqpP05 zeg!S>mVE=(7EhLYddj*KM)PxavBl7)JIoJQA@I*QAz)qOcWlFFl<7pdhG=!*y@^fO zuW-`|WI_;R54$_T?JXMB?X#t59Tzl5P0{^fTGE+(3ya7Vsy}{*c2gvWqK%6%4MOsk z3%M647gwy&o{YlEYnA@sLX?T%tMAStef_N_5Lh$bm*{qbZ0CVz7d3EZtBdvf z+cZ+Wgjr@E8De4r_ox-panVK84e6CydZG1QSBOCjYRQ-9SP6_x0LOW>VRlxTth;?sx{d^0cnT!pG)2X(`Z$OR@}_W;m%@0-*B^_ zf{LOtDz|t!rCF`Hj@PFHr;PM#CaZ)9HODx!Co4~GOovA@?NYSwh^3vu*x+Kw%5@dl zoch^c|0D2{(Z+J8ElR^Q5EFmfd0?dU5GX_fIdIud+G(Z-ymfgyM3IZGKBc~=n<}2< z;6JrqjMdf?atu`+O!3ym|M>Xd5dSC#mUd zk%C$_mIn>P?E=|0m}6(s%+$Y(YAs5y6*{`fy=1%S0G_o^)0&Q$O}im;`>jJ&9FeSnx}oaw!Wnx$f(Kf-*Vcy_Pkyp2>UO2*3pF*zELCf6p@gd!^ebHlVwLMle=uj0p{2Zg`sUer8gKAVU9}Nq8&8i43;SyW zI|7cWYoh>yk0&s zgX$Xd{2p-YbjbP+g~^BC9?k20VtdfNG1z^afoNJ281$VxG%ih?vKM@9B7h0 zfJy4(n#*>`&fjEV|8E^0A91FVwLE7u*xyE3T_JHaWQ&O!4ykOO*-e(C-jSkJ^`BJ*B zd0;XPvkkS%zVlA^UgkRss}DF~M0f4RQ5|Hj+#OFkRw28Px!02yh`-PgVC}J$bUhSc zObxB{&F(3!GkMgV*F#pouYL%U!&_<)7~79t+)18ui@PNxK*_!^qD#sv3)Kw3ICjYS z!iQBgB(b%(xcN&?(w-wCb1akB=9;g)hz$P+zUwq{Iw}*9S@~FJ!-kYL}ghOy= z4Kd5FU*)n(G}a*BlDXA^f9$AIKHlt78!>;hff7gV4tRkW;KDPSJoyyRovKGBNnvsq zPhYwxR{9!P&EuH)su*LTVa#0!@l-pQ*-gHuB7Q?IdSH(EMD@H=xc#lNtb3l1KM2*! zE1;g!oLh{aEBuw2p4efJBR;y8Rb#xNyO($x^3JBaWAzFr@&(Z8=@<0JzZ%D zY=&;zi*(T{4Z16D+HQ)L`Y|8(aOEq2e5)vV-yW|>44<1bUJZk-we&bDXINPRlV>0& zjG8Hd-_<2U+APOxzXZ`fD-3g#t!AVDzd3m)@Wtv&NzStTtu2F@l&y+m>SpG%&Rgdp zCWEd?!bXD@!Kb-JHY5y)9{f#n9ze)dD;+1oim?+Zx>L{Y82stz3VYr55zrJ1ZYB;Y zhg*IEJ=WO$Uvh~hZ`=_*x3+dfJ`Y2@1`$$iGCBi!yX76Fg(*3l%lcY$S;(y&FL@t1 z`Ns z5zi{onCQzYQIqD)zPr~XN3a1R??3C+I#uuRT1Z-9)}E~uU|4fkvgr|XIiZLjFl`2^uTuILUNhDzT#jJxXyN5VV;3xHUkrHSc`avM^U% z=+q}qZr+TEfjgQ;KS;h+FSRtr=a2&Ia{YT2Tpll5m^hgIFH0)#K=h@lYvyz| zyk=B9mD0@7X3Z8117rnni2LOII%odm>PSJ7ng1am#zF`_S_d*5etH?GUft zJnC5K-?xQuIi`MWj4iqN;c@VBA;fWwR3+uozSivU6JeEMVfZKE zUuZefa4P|j@Z_Ta!AYes#>Qie6LmfI8}56^Yt)%9Iq%n|Lt1e?-Vr< zg)|!NeK7EuS+RW?grW&tB!Ow8ZmmuiF&Gw@(!_z9z6O&OCzLRtaqz z_;t1;;^ru7P&HV!^>PQVbFIA(rBIUexjxaF`>&n^Q_A$AvjS}C3RVYwoQvso$Xs{( zNWw|zyhDDD zJ1Jy~-+-ShLR4v3H<>-Ta&FnP;eZ|gyhgFFoQ|R^W^xAIfSv8~V}Wv85`iQ!Z})Pd zR`eH*g>dR_!(p6tUE6Z>?!FehY_*P3+kn?e@_li!rth%zZIJPOj&9M= zw3j(lXI=2?1xoG3{9cdE&lOPQ@W#bXDKjLezA``&n38*z;WJm zVstbn(c1H>nRs_k*3u^U7aNAXK9$k{IGwhV#1~2F+h86DQiWFB@_{zA$_6Taw*vMz zF$UP-;y(_TbxSbDXVd)w?8Lbjb*n>l2dt=SRmjWnV0I!;hj~cs&Bn4y`efxV1FAwb zXVjZjvu16#IMrr5)&Kx&c79r9EQcDG6joTRCr{*++wP}1D8c`8;q#(nF2POT?ss99xUctA+1;v3T&mp3AXAqcW5!-iyKPCg38TOH zWVv%oa;bQI*9i8s=zb@dPC4x`z3vieviUnWg2Avu?7s2_d@o^dGwW|E$x$kBKd^S# zH(t-(Q?IZ+WX}YjEj5D9@AqBB;yzznRIOuNR`hS+bzkxLbM>eCKVw!3KGiNpRp>f$ zH#$4@+9l6M^4^r+{D8aYqk$={<2B#9Qqfm^ES$Txk9`W7T^3Orvsc&{X$y03IfB;J zDL1-a8@&7?PesvF9ofx4PCuo&3by+$bQ1q0C|L+|Au%YDfrnRY$Gb4Gt-ZP2SO%DOS@T)2g7S*M)Ixh9cSEe|Yu9pS!4Q;LJ1lbNEIy z-Q@$O;~WnGHIa|g4mF6}SNvlDa(5{A!VH&7pSOZ52G7-1tTp)Xr3-UFgB=R_EbieM ztNPj-_Zwfpi-T9T%w;4F4@12=X4Y=Nld3*3?meD2jouUTAGel2k37sc5;d~Z(H<}~ z5QQPtjQ6(Yvg2o%-JSLu{HsU4-#dl5e-o#Yby8iAdQdSuipf-CdSPJ=c9?^WNPojg z5q7N9UC18jW)N#Nw`BGHBmNb5g6u;FN9v>Mz`tBI#CG51HU8d@f^IP3L zE8w@gi#HT%k3LqJ^BTd^#gA)#k7awCi(t!Xjql-@>UWG(MLL@XW zUdT*k2q889jn3{%1zqBfSy}< z!QSjbCqF};xlg;2nJJnYrKx2b5ZqHZXkm0==vHbp4>!J3%~0`VfkPZ|F3+*Sh$SV% z)8Mvpb-f@7;2?iuoD{$(fJtl}(4q$eBx5rV;2mA|ZKrK=Eejp7SJkwT^9nX?bq%x> z{7(rQ2`de~p|$WhFc(kBQP}eqAspe_H3(LJz3QNa#dQ2kWO!DsMfrmjQ-`pp(C@cF ze`dN9gHM*mKd=3K+peGa%<`pLg4rt9aYYj?`woBa?Dh=d=whIIaOgEC>t|c;mPkm< zU;P;z4H*d)I1j7r2SLk#*514he$kp52m&)zC zeWCdN+QbkWs*Fi=yd6LG3fXZfvq{dp!4RSt@A}X#?f`qdjR|3`(wHQ27*02GA2UP~ zYU+jnR19GXjYfroVU8h(vJ#Ux`Dzl}6+Lk%9U=d@Sh0yeLFu3A{M8QIxgL+J?@Ins z0!ucrEk(=v?2(<9|97Dxk-w z4&7`EZoERKY*2Rs9jrdvbm*6t`MB^)Zc+j6!ofc=hk-fyltfALl9qgDI~Id zpO*LNy|;c6!aYH46#^x$?7hc2cbolxJDINWQPCs>RqP(^mi5f?fQa`|8x*3*jSK8W zw|L?4!DwCS+TPTj2VTuYe0)fv)!s#;TVf|9Griv?zIut);`Xbi{T?@W$%xwUgBtB% zZvTzMzTy&^KZS3aG5140(msp;%MdW;&b-`(#D*6yLqhYPjNKQ-i$Ja~&i~F$?M9l| zoo$XL(tl07=2Bk6lja&X}-Dgl6Yb9Qe0Dv2Bi4(#h&h71)N5yE>L^?H=u{ z=jn$O+t!BN;lW%~6-d=kPwhNra#fNcY>R0t2XM#E?M}V%D8m)@E|hK8malu{Ec>Z5 zbLb`YV=EIM=>xFc@kfrMKL(-{%yB}A`c-NU^e$ZEGTHy?W6{eG6mr|n1YC2 z$#@(F(G}y5xs}m=meQ2tEc9GnVXSUxt|>YnX{?-bTv))rwf$W*iU#%_R)9@E5HdPS&4lo#iOjXg{1; zy82Uf%bv)i^pE_TfMZIet6JU1=&{k|&2g;sxX27v@JZfx0=@m9NG&soPwqW);3FQ8 zDcj12@gpbxb2r;i5=5ly{kFcpQ4&8cRYPnv9tGUJg>}Zu7_V4(P`M;rY>mmDe^^+W zzL_uGL2zxN{^_EF-a+Z_Y65@U{BTWsm#iQ04Y#*JeFxq3=en@M>l}P|+v%PN$qfNs zdZtTxaD>3x0C` z_-eqz374Ylz)u4Sck{c`+dEq5Q9~wMG2SrjGF;9`^0nNF-K7^ZDBd**{hKdjFvB%% zYU$t?l`xX{h3?M_e)BH-#~Mr+x3n%fc;=k%QN<9*1LpTh##pRf#u;-8;Xx4Bp+jpk z?~wx0sm#Ad_am7YCw;f{mU?p(ad-Kb+fTYgE0rjaQ;y-$p)cVp?D6gV zfP=Bl*l z_*l<%GNWg=Rh)1%{#!NpUR6JotnVMyU}YR@c%D*vV6)j*j9!NoHXo{pP+wZlg<$lYb#?@sa#;}Xwqk@cyqkH_}A|3 z`cIRf*OguqB{>voocWRmDms!cw9fcd&EGykwIOy z16kT3A@^`%mut|a{3VC?%+iW+d?62k5wWoHhm}wW^62rK21nz5k36ki#=v1~*_+as z6vjnVdIz8K!_2XxBlA1mom#`BLK+Ioq6Qj@qWI@$!Nyt>j^frvCvUxvq_%ELciz2H z?14yjlz+8AubKV6#N};V6|?7A*`$`d-eO$tsy=P=t|h1#)p?0-uO6`;t8zgFPDOLM z8%4w#jROk3rh?HftU8flW5xOQ15v@?R2wJ2C{4>{Utt7XUM}*(Do48e zl#O43nt3GAYwYysFyQK1r?#cFrM0}}>hI-4!~8_}mH&Q~#BW6CG7O4ZKD>cz!u9*f zp`9$;LvME{Pp`~8U#(~^Y^16b57e=88*c}_n3tBWnk76)q#_@S4vi`rV()A-gja^z zZ{3ZCG+aWjw3JL3X9ZPV@mXvcp58pwK4Z-$va^OV40beK$_23J70ra+2m(?mh23O3>@I}wo;N5)K;xO^y>a~W9yb51IT=Y z<=fvu)zuA}i1QwY%U979CY6D+qs({fggS9gZFGfGvo5r0nGJeBwhH!B*~IZFCqcULVi}|Bgr$d*dEf5)f=48=eXs}n@n_-m32QN zq-9jJ zNN^Zk*888&Yorh*mhjp#Ipb3nSW|kT7^$3Q?$$LiVXBQbzM7dzAmH+hnq1&YOA=Yy z<{tjG<2E2qIYFpxCqoN#!V7hmQ+G>|Lp}EFn@3nbu)j4YL1ZX z-NM%H(rBLxCM>}#Bq`#2@X2s+qzv+bO@CwSZv~n&@v&CRVynL4J6t6Xa&yx05Kise3rde@SWTWJpLH}@&GfB% z!ld(Rs%qrlH!@DpE)b`?WR2lgzA`TS;kI$Ir}R8TRzrTrFXod-C(5ooy@XHhI`#JQHS277K2d%jBIXpHk0{`3@p1~WchKXh~LgeW@00`&fN3AQiv`TBEI zzAj*zv=e+snqmL;T*Ddm${^fXVtatl@4xRBAk%WT!GOyGM}J-_1}b_m7+MDFJ)u~P z{|^+_S48uztJ}!Uzs_(M!x1Enn8nX&Ykf3^LsmU#S|>Et`51OQBJuS&b2$2F`8YcJZql-C}`{1!GcSo8fSBw~3GdGzX&F zRcpXD_l!WVG$K$xNwMAvDzWy;s?Ez+fM`B}xPI03Uoeh-WLRp9*u%^B1JT<#xR6NK z_i^rhpLEp<3Usjq0VH&G&&uD07WJN*u2T7qZW<00R!jLRos}QcEaJL+?e(yvxb zNG?*76EL$Mmr^3c^4{2R0Ut&o%LU649MUd6?|ip6Z@w<#V#IrFq`QXsf$oDfp^u14 zE}NSyE?h26Hc0{+(Of#~K6M(6TrRtP;zEK~iDY3@%*7)=244X=)s`| z;hBSy58Ya)^(~O892!7ClE_ZV`l3$SO1wrCE|VWzk39Q+EH7-N^RP*=|Mea^ zH_spSYC{oTKG9z*%ZGM^zN??v$$`O0PgobaQKJnvJKOv*C(Y9&K?crhuG}0t(o6dq zbD98Ju)gW8!ck;(SG%8><*swH;cQnzo;mpff5)j3L-Pgz^n1mC&|rEnKM_o zB|EXbbO?Q-FG#8b$yoSm1>}SbM{#mGJhzB=*}mHF0p7CQJsvHL5nM}?eiBE!uDHj!>_X)$DD@NmftEr0g56I8jGQm z&5g|6{osmXTQo3JaI*lg}N}Bo!o&IR)H4d~;ntQ(R>aYsG~fg>XuS z4f}p{_SwqZ7)!f|EwgeKlOV*}-+h1X>rXku@7@BawY#)-!+G4&C~EzfpQ#p38f7KhH~hxA@6ML@tqF(@url{)BaoHddfR+u5~y_x@4$BClV*wl%95 z-%I>nHa_I7_<4kZQq>Y$6RS5GsTRv)4&P6npZ42?`Qt>7_9&e-N?pCB`zMMc+Vy{@ z8&js8tZy+sZf5RF9|M$xg?$g?2=rVHsF{?GfBkH&c}LqmVH`bS<&+S1=6bYT$VEJ0 z@1(Ne>b*=Et$q0>MMPkWLALgCaBx403#D`(Ke3X!H6i4xG~>=#;xrW)yx{TNrcj{p zaepsJgx~vB{6EX!HfR^1O*tB`;V4Ha+FKZ&RyjVs~_*?pidwnkipt||O3T3ze z^4_iDisAu{2F)Q|M$%_J;`xN&CZ_CQiUgNayoWQT#Baz|$!LpSzHU#daei&#a5^L+ zJ0%$sQgVHTUf$T*be0-ew;8*Wg~t;1N6mx~B;pvaeTkyadlPk#Wi3@|w_7TJU(Hw% zC`{ShF|uE++1it1J#f}uU}vh*V94X{<$W6S@@tH%E9xOba1 zY=cL#6sU4jytM-7B3<2PNK+4Gi@Kcro<{$&COuMV4nHi4*|=(lrb4iMEb+jG8MJE?iiw-+7hlTe$a_XswkYy@fjW*OsPEbF{) zF%-b^wt8zF5YOX&(sa%#+6WH)qlA2y z$IqDP(>mt_5o1&aDeoMp=;aB5#rhgI0@~_Mb2IDil%KWha@OT=~a@2=G`kDP1(GhUql2UvLkPvHTQoLckmSo zz2QfSO|ie<7|K%~jGMvqXsP_bfl-9ZK9nD+M__#!chW-=xKF&8sW8W>St zg?y5~aaLq5%Miq0c#~_zY&+XzD&Cc&9`T{rO5S$sri;phP7vBYdysm0eby#s=zQK{ zkiuW8nNm8(t7T28o>FT4phAypUAM9&FUDjNwkmYX^TLK^+-OV1ESe%xBF(ZW?8+K<$@IjX=;Xs)07=`o#inlYJR z^J6v+a8-SLxSd)YkiFKykF^@b*43+^jktNOw8^4w9jTTOvubvm(+Zj2Qdj(uu? z0|)gOLLjOneMZZ}xDg(si6Uv=+`mJD+XQh-tjhx24vz1(rf=CA@Oo{=%#myrPuR|v zCl7qKmJg?%4TU}5>>TO4kX=sZmkrg+1NC~n_b7@|`7`!4q8R9V*W9f^`ThPMeoWbn z)dirh!+Sm&%ZG%)LjqOh3weQ)mEZs@#EZ z6?x?Xd$9?Rj~DtPLZ16UpbWfZ%D;Hpeq8SZoLIT^0TGA(aCiS9Q|jP^^JgVLOJ4Z}k}7gPHN-#ac@7_*9HhzRKDsdvl&<5ucWW7MkAQ~s;q{9b5P z+g06-6%>yy>iA#&f9S{5L8`}wsdxw&1eh_109pmsQvETyZ>ew81sVTw3>#L#_MZkn z@{yqPKr^!D9%X(!%_=%-1`;F0z@giGAEs|I51%+br5)0|k@{=o!Y*?v40V;Ez#t;UZ| z=C-TUk>H%L7?q{%^Q|_g7hl1?in@CxqPKOW-4JZd{xrVhHpiqhz3X;SB=lV**FQi@ z59=^?huYI3UF#k6Wa1a{7zt@u5U-|*A!<29bPISDZmHEzcUkVcjPFd#OoOLh3-*bd zJEWcv4LQYAXRv&MQd^O{X12Rr~zn()r3mjHY^KYHOT&H!5@J7mSXI ztgo19nzF|HDZUh4-8rXx-+ORR?j^E+2{w5)ew-qrLjO2lXEa>2!*brxZ_!j!!I3Rn z6m#q9Bsg135;8L#=#@1z7^%2C8P1h|o^3yP1lZ^t3`MHHz_qRJ3`L>B$T83Y1PSj5 zIl-Ft7`YYp`+t*JJR8=8iLvAw^@Y0csUEH^Sb~Ho_+fQcS%}Lnk#y1_VhGJ8#DGq* zJ+u^V&keFyUc!cR#Kjf+L+6bd&J?SK!R~;C)VT!A5X0mt?Cy3JUAyR1@un%t2I@&( zR;=HqOuBFJX7oL{b4=z0S)U>pZn8JUYk%!d2j5tRFqLb#h7clsDbMv}Mn|hyZII^9=QPTo5TwGQg~*kn{PF*CV}< zG`kuL&fDVtrb{Wqu8%H4vXsyM4|a0J5bi3I+p3U6a4NZld-VnXVJ}D0m*vm!l@o<+ z7+@-igt`n<)3NGN4fv&pN;f*C9qwew={fVmYhuKL9kzZ54GjHuCmZstjvZTjE||Qr zRUdV0Ytt~9DU&&D*)3Mf)~Ax;T(pOq3jR$Pb=u3{2dw}(8jS6j`X`yUj!7Y_iL={7 z1itdi0k#~@%eQ~^C|zQMTf>pqGl%|1uWFXWQ_k-Og0?XJ9_+PjEZyT>C}JoPT-4D` ztpr%FqIH9RT#lZKz^+@(DF;GAGRl?{12gh9hSp!+-6V`Mm4U{^8lauPJS8C2r z`djctXq4#~en>nV&$!5Ru(2fuv)fT18aB!?#D2g^6|?ckE|0U9Z&NE6vPXDmlu((y zTh*VwVSnHBC(3$|D3-P^-bMw-dmZjv_-uKZTCZT!Z!8uoe9^ZwR**r>m zGI|rbKe&eOY~k*V6!yi~*)}%k6?QdI$DwVU)*(%HHTAE%mi~ug!Ncq>R{DWgam4TJ zilWKmcr>vV=wB+66l}8-!al^>e;0BYYpevi z@367;$%gXWaj-LwG&6gaD5CaACqe9XYii344KJ@X08?(KSVcmhWf6s~;;r*X`wr18 zXKh}Y4*nnxO1DUnC-f>(iRfY2F#9mNqc;oP%AsJ+PbMByyvCo9`NNM+%85H4rl&VW z*oJQ-{5q05s(IWMopFai%q;t+7uVy?<`h}=q_?Vn``Ub1=4U!cUXT^oo^ z=%PPErMpfViHWwRL@C_F5Ig3tv*x3oEvsX0+U3Xs>y$-TG;&-5>t`AjzG^jj9KDDI zx8*_VqfYL-0b=6V4;d3smwZ0LudZz6tR=jy{`z{k+h2S82O+YRnPoW*+#SFpAU3?Y zkzHToT`s3sGdhk_;q-V%+2?E9G@w$1J*FX%pVYCx-={tiMo+e^V>S!=#c(>o!V_Q(VjJIK`qQC>k_@U8ueY zkHgWEqIByN_A*#TeOTuqsg4aW{{Hec8W;9Ems=xMMFMlpQW)!2oFG)8=(zh%`C{pO zvEg1UGUoLH>JnK_K(PmYlt7*E?f~ zT_>d#L}1IfVI3plr0PQ$YCL#_{_^$>P_lQs%tEn)FQ$9kh_P><1%UaE=cTVmUwZk+ zKxd{Qm^df78}Vv%Ql_w7)Me?r%nY4sXYZzg>n#TKmXEHkk;X(MwuflZwzHX{;r2C~ zLaUtV6v!Pv?oR0=D%{m?T`vVTR*s=GhZ2Ma1NQI=-hBfyt0{jsqy246vh@LG0NA>G zHEIP%0h1RuU>g@SSs|zc2G$!CP}??rW_}*+WwjV*|D(4x_dLu7u7W2 z%1DhJVidkj26_KWZ1S^Vyn}eDSqz=+_;U%@gu^rGBTf%$k9@CbbYo}92J8la$pWaG zeSus$9z`cskGC(MMu_wp6zKjubEyNBzdn0BE>rn}OXYa+2tbRZfN>yu*ZvT#8Mdf) z9FTkQ3Ae>?k8YuzK+%4l0OO{;!PW5h6V*Y zB5Gd-<8ERfK{swKWM;^O_IEsf`lc8_kG&X71Z7T~BIPcodc6et|E+pL9xv~MW;^u1Lx z)S27U5Vfb6ie!F6GaHgh6;a)~2zd-pTNl~Sn&ks3P6GdE7}Z)bDX{_)91Ca=%aP<7 z#4h~LLUWoTyn=JyS&ih|jYNOuw)>@n2NuWp7K7t#^5`L)e+-qnxK&+?wLAcFzvHpC z5)_pj`|;!1=xz(p6u^lYda%^_N<_C2wD+`^iy?elXc+027bsX0Y_VYlCoz@~u&3%d z+tT3`xyt24qiN=&{}Q*}fVx8V3Fa$iM5jhTxIj&`9+|wk6mv~>TYr4cT8c|sZ;dwa_wymd2mVb&3!u;FO<04+Clvm z|6}Ek>e;2*n9A7jhvJR=$g%o;u*Z_Xw6NTiW28FEM^p9!^5=k~7K;Yj{{LG$C{bi; zA|7SxO0x5I0{Ozl(2PR3jM8{Dwy548<)|!S3Hfe51!EHn_;C&wt|sl>BnQ9|kjWw+ zq+cry-tU&;9saxRcLb}T8T`($9df%=73)x)81sp{B{CbpvEiZ7W@lZ=M(wN9-(N1Q zDGh;589yI&=y;hX9droQE;Y$H2N=)ZSsw zc3m;-h=hyebv1u*o|5k(Ja&&lOH1fTCbvT+F2^_nuAhd=%=`ZIm{N2f9Cf%JwzX0M zDy}?s=<_X`kQ1>RAc_@R%NTpBQ?k#SF3e@ey3D9+Qc1iZy}ZDAc`%RU`m=c&J?i$> z9xjto3WzVJrA<^EIRmI-=aGtD zBQ0Dn`99+C4W3{Q3WBldOV-+41iM0M!eT(b&P;qKp8ZSXcyE$>vCS?PwpFvAe1ajD zQ%7@@Qdd<%n+s&|;drylX47NwSCsKULJ54bt_twu@`A_Z%EYAXxO8tY@h zh>us!)>0(;gox?|cX>U-{Q*6N zz4u`8Hv$TVyO8%eV(sDa-t5qCE6+8e+(FXqEEXG5Geg)|e>Hd+iDVG+A^A$vO@zf# zE|8f}FVtvCmGnrvHh{QW0J6VX!@7f{ZQ(11I#^%Ftq_Ipw=Q)1JY`{e$2iu69N%8# z8g{3weXueT)VxR@PZrA>%h!}V$6z_%qx4suYDP{(1)5?hS-sz= zKqzh#Z!X0^tL+DwAz}5X4)xw2Jy7+_W8AeeNq$53D@eyR2%U`y#LbS)kQzc^eJqf@ zqOi^#Pm`XRGYZ7QWUc*;*E26Oq3vC1n>=oxf#I)Ul@7NUKs4O>2H;C
      *xHm$;AHI7Z7zmx66P)uJz6C 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 z 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 Date: Mon, 2 Jan 2023 23:38:20 -0500 Subject: [PATCH 55/57] chore(docs): update issue template (#2741) --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index e688613e0bb..a04649a757e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,6 +1,6 @@ name: Bug report 🐛 description: Report a bug -title: "[Bug] " +title: "fix: INSERT_DESCRIPTIVE_TITLE" labels: ["bug", "triage"] body: - type: markdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 1f94e4b2501..7712a2c658d 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,6 +1,6 @@ name: Feature request 💡 description: Request a feature -title: "[Feat] " +title: "feat: INSERT_DESCRIPTIVE_TITLE" labels: ["feat", "triage"] body: - type: markdown From 0cbe6db3e7721fdd10945c923410fcaddbfb72d9 Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Tue, 3 Jan 2023 23:05:08 +0800 Subject: [PATCH 56/57] refactor(protocol): `i++` to `++i` to reduce 5 gas (#2033) * protocol: i++ to ++j to reduce 5 gas * more --- packages/protocol/contracts/L1/libs/LibProving.sol | 4 ++-- .../protocol/contracts/L1/libs/LibVerifying.sol | 4 ++-- packages/protocol/contracts/L2/TaikoL2.sol | 4 ++-- .../protocol/contracts/libs/LibReceiptDecoder.sol | 4 ++-- packages/protocol/contracts/libs/LibTxDecoder.sol | 8 ++++---- packages/protocol/contracts/libs/LibTxUtils.sol | 2 +- .../contracts/test/thirdparty/TestLibRLPReader.sol | 2 +- .../contracts/thirdparty/LibBytesUtils.sol | 4 ++-- .../contracts/thirdparty/LibMerkleTrie.sol | 6 +++--- .../protocol/contracts/thirdparty/LibRLPReader.sol | 2 +- .../protocol/contracts/thirdparty/LibRLPWriter.sol | 14 +++++++------- 11 files changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/protocol/contracts/L1/libs/LibProving.sol b/packages/protocol/contracts/L1/libs/LibProving.sol index 87d601e4b60..c29c18ab06e 100644 --- a/packages/protocol/contracts/L1/libs/LibProving.sol +++ b/packages/protocol/contracts/L1/libs/LibProving.sol @@ -237,7 +237,7 @@ library LibProving { bytes32 blockHash = evidence.header.hashBlockHeader(); - for (uint256 i = 0; i < config.zkProofsPerBlock; i++) { + for (uint256 i = 0; i < config.zkProofsPerBlock; ++i) { if (!config.skipProofValidation) { LibZKP.verify({ verificationKey: ConfigManager( @@ -300,7 +300,7 @@ library LibProving { "L1:tooLate" ); - for (uint256 i = 0; i < fc.provers.length; i++) { + for (uint256 i = 0; i < fc.provers.length; ++i) { require(fc.provers[i] != prover, "L1:prover:dup"); } } diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index b4dfaaaf45b..406cdf4c029 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -157,7 +157,7 @@ library LibVerifying { TkoToken tkoToken ) private { uint sum = 2 ** fc.provers.length - 1; - for (uint i = 0; i < fc.provers.length; i++) { + for (uint i = 0; i < fc.provers.length; ++i) { uint weight = (1 << (fc.provers.length - i - 1)); uint proverReward = (reward * weight) / sum; @@ -223,7 +223,7 @@ library LibVerifying { function _cleanUp(TaikoData.ForkChoice storage fc) private { fc.blockHash = 0; fc.provenAt = 0; - for (uint i = 0; i < fc.provers.length; i++) { + for (uint i = 0; i < fc.provers.length; ++i) { fc.provers[i] = address(0); } delete fc.provers; diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index d9a3b3698f6..620697c88b1 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -49,7 +49,7 @@ contract TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { bytes32[255] memory ancestors; uint256 number = block.number; - for (uint256 i = 0; i < 255 && number >= i + 2; i++) { + for (uint256 i = 0; i < 255 && number >= i + 2; ++i) { ancestors[i] = blockhash(number - i - 2); } @@ -159,7 +159,7 @@ contract TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { uint256 number = block.number; uint256 chainId = block.chainid; - for (uint256 i = 2; i <= 256 && number >= i; i++) { + for (uint256 i = 2; i <= 256 && number >= i; ++i) { ancestors[(number - i) % 255] = blockhash(number - i); } diff --git a/packages/protocol/contracts/libs/LibReceiptDecoder.sol b/packages/protocol/contracts/libs/LibReceiptDecoder.sol index d02ebab4690..50ab4e4cc28 100644 --- a/packages/protocol/contracts/libs/LibReceiptDecoder.sol +++ b/packages/protocol/contracts/libs/LibReceiptDecoder.sol @@ -62,7 +62,7 @@ library LibReceiptDecoder { ) internal pure returns (Log[] memory) { Log[] memory logs = new Log[](logsRlp.length); - for (uint256 i = 0; i < logsRlp.length; i++) { + for (uint256 i = 0; i < logsRlp.length; ++i) { LibRLPReader.RLPItem[] memory rlpItems = LibRLPReader.readList( logsRlp[i] ); @@ -79,7 +79,7 @@ library LibReceiptDecoder { ) internal pure returns (bytes32[] memory) { bytes32[] memory topics = new bytes32[](topicsRlp.length); - for (uint256 i = 0; i < topicsRlp.length; i++) { + for (uint256 i = 0; i < topicsRlp.length; ++i) { topics[i] = LibRLPReader.readBytes32(topicsRlp[i]); } diff --git a/packages/protocol/contracts/libs/LibTxDecoder.sol b/packages/protocol/contracts/libs/LibTxDecoder.sol index c7b35a2ba1c..fd4d24c4341 100644 --- a/packages/protocol/contracts/libs/LibTxDecoder.sol +++ b/packages/protocol/contracts/libs/LibTxDecoder.sol @@ -84,7 +84,7 @@ library LibTxDecoder { LibRLPReader.RLPItem[] memory txs = LibRLPReader.readList(encoded); Tx[] memory _txList = new Tx[](txs.length); - for (uint256 i = 0; i < txs.length; i++) { + for (uint256 i = 0; i < txs.length; ++i) { _txList[i] = decodeTx(chainId, LibRLPReader.readBytes(txs[i])); } @@ -213,7 +213,7 @@ library LibTxDecoder { LibRLPReader.RLPItem[] memory accessListRLP ) internal pure returns (AccessItem[] memory accessList) { accessList = new AccessItem[](accessListRLP.length); - for (uint256 i = 0; i < accessListRLP.length; i++) { + for (uint256 i = 0; i < accessListRLP.length; ++i) { LibRLPReader.RLPItem[] memory items = LibRLPReader.readList( accessListRLP[i] ); @@ -222,7 +222,7 @@ library LibTxDecoder { items[1] ); bytes32[] memory slots = new bytes32[](slotListRLP.length); - for (uint256 j = 0; j < slotListRLP.length; j++) { + for (uint256 j = 0; j < slotListRLP.length; ++j) { slots[j] = LibRLPReader.readBytes32(slotListRLP[j]); } accessList[i] = AccessItem(addr, slots); @@ -233,7 +233,7 @@ library LibTxDecoder { TxList memory txList ) internal pure returns (uint256 sum) { Tx[] memory items = txList.items; - for (uint256 i = 0; i < items.length; i++) { + for (uint256 i = 0; i < items.length; ++i) { sum += items[i].gasLimit; } } diff --git a/packages/protocol/contracts/libs/LibTxUtils.sol b/packages/protocol/contracts/libs/LibTxUtils.sol index df4b31a065e..22273a40197 100644 --- a/packages/protocol/contracts/libs/LibTxUtils.sol +++ b/packages/protocol/contracts/libs/LibTxUtils.sol @@ -57,7 +57,7 @@ library LibTxUtils { transaction.txType == 0 ? txRLPItems.length : txRLPItems.length - 3 ); - for (uint256 i = 0; i < list.length; i++) { + for (uint256 i = 0; i < list.length; ++i) { // For Non-legacy transactions, accessList is always the // fourth to last item. if (transaction.txType != 0 && i == list.length - 1) { diff --git a/packages/protocol/contracts/test/thirdparty/TestLibRLPReader.sol b/packages/protocol/contracts/test/thirdparty/TestLibRLPReader.sol index cfd14bf9d71..e2e6eca8609 100644 --- a/packages/protocol/contracts/test/thirdparty/TestLibRLPReader.sol +++ b/packages/protocol/contracts/test/thirdparty/TestLibRLPReader.sol @@ -10,7 +10,7 @@ contract TestLibRLPReader { function readList(bytes memory _in) public pure returns (bytes[] memory) { LibRLPReader.RLPItem[] memory decoded = LibRLPReader.readList(_in); bytes[] memory out = new bytes[](decoded.length); - for (uint256 i = 0; i < out.length; i++) { + for (uint256 i = 0; i < out.length; ++i) { out[i] = LibRLPReader.readRawBytes(decoded[i]); } return out; diff --git a/packages/protocol/contracts/thirdparty/LibBytesUtils.sol b/packages/protocol/contracts/thirdparty/LibBytesUtils.sol index fe11d3cca98..92afabd82df 100644 --- a/packages/protocol/contracts/thirdparty/LibBytesUtils.sol +++ b/packages/protocol/contracts/thirdparty/LibBytesUtils.sol @@ -141,7 +141,7 @@ library LibBytesUtils { ) internal pure returns (bytes memory) { bytes memory nibbles = new bytes(_bytes.length * 2); - for (uint256 i = 0; i < _bytes.length; i++) { + for (uint256 i = 0; i < _bytes.length; ++i) { nibbles[i * 2] = _bytes[i] >> 4; nibbles[i * 2 + 1] = bytes1(uint8(_bytes[i]) % 16); } @@ -154,7 +154,7 @@ library LibBytesUtils { ) internal pure returns (bytes memory) { bytes memory ret = new bytes(_bytes.length / 2); - for (uint256 i = 0; i < ret.length; i++) { + for (uint256 i = 0; i < ret.length; ++i) { ret[i] = (_bytes[i * 2] << 4) | (_bytes[i * 2 + 1]); } diff --git a/packages/protocol/contracts/thirdparty/LibMerkleTrie.sol b/packages/protocol/contracts/thirdparty/LibMerkleTrie.sol index cd667a2466c..41472ee5bc8 100644 --- a/packages/protocol/contracts/thirdparty/LibMerkleTrie.sol +++ b/packages/protocol/contracts/thirdparty/LibMerkleTrie.sol @@ -166,7 +166,7 @@ library LibMerkleTrie { TrieNode memory currentNode; // Proof is top-down, so we start at the first element (root). - for (uint256 i = 0; i < _proof.length; i++) { + for (uint256 i = 0; i < _proof.length; ++i) { currentNode = _proof[i]; currentKeyIndex += currentKeyIncrement; @@ -285,7 +285,7 @@ library LibMerkleTrie { LibRLPReader.RLPItem[] memory nodes = LibRLPReader.readList(_proof); TrieNode[] memory proof = new TrieNode[](nodes.length); - for (uint256 i = 0; i < nodes.length; i++) { + for (uint256 i = 0; i < nodes.length; ++i) { bytes memory encoded = LibRLPReader.readBytes(nodes[i]); proof[i] = TrieNode({ encoded: encoded, @@ -355,7 +355,7 @@ library LibMerkleTrie { ) private pure returns (uint256 _shared) { uint256 i = 0; while (_a.length > i && _b.length > i && _a[i] == _b[i]) { - i++; + ++i; } return i; } diff --git a/packages/protocol/contracts/thirdparty/LibRLPReader.sol b/packages/protocol/contracts/thirdparty/LibRLPReader.sol index 3309b893e1a..7da6e7fee28 100644 --- a/packages/protocol/contracts/thirdparty/LibRLPReader.sol +++ b/packages/protocol/contracts/thirdparty/LibRLPReader.sol @@ -423,7 +423,7 @@ library LibRLPReader { } // Copy over as many complete words as we can. - for (uint256 i = 0; i < _length / 32; i++) { + for (uint256 i = 0; i < _length / 32; ++i) { assembly { mstore(dest, mload(src)) } diff --git a/packages/protocol/contracts/thirdparty/LibRLPWriter.sol b/packages/protocol/contracts/thirdparty/LibRLPWriter.sol index f1622b38d17..69821770e95 100644 --- a/packages/protocol/contracts/thirdparty/LibRLPWriter.sol +++ b/packages/protocol/contracts/thirdparty/LibRLPWriter.sol @@ -146,13 +146,13 @@ library LibRLPWriter { uint256 lenLen; uint256 i = 1; while (_len / i != 0) { - lenLen++; + ++lenLen; i *= 256; } encoded = new bytes(lenLen + 1); encoded[0] = bytes1(uint8(lenLen) + uint8(_offset) + 55); - for (i = 1; i <= lenLen; i++) { + for (i = 1; i <= lenLen; ++i) { encoded[i] = bytes1( uint8((_len / (256 ** (lenLen - i))) % 256) ); @@ -172,14 +172,14 @@ library LibRLPWriter { bytes memory b = abi.encodePacked(_x); uint256 i = 0; - for (; i < 32; i++) { + for (; i < 32; ++i) { if (b[i] != 0) { break; } } bytes memory res = new bytes(32 - i); - for (uint256 j = 0; j < res.length; j++) { + for (uint256 j = 0; j < res.length; ++j) { res[j] = b[i++]; } @@ -200,7 +200,7 @@ library LibRLPWriter { uint256 i = 0; bytes memory res = new bytes(32); - for (uint256 j = 0; j < res.length; j++) { + for (uint256 j = 0; j < res.length; ++j) { res[j] = b[i++]; } @@ -253,7 +253,7 @@ library LibRLPWriter { uint256 len; uint256 i = 0; - for (; i < _list.length; i++) { + for (; i < _list.length; ++i) { len += _list[i].length; } @@ -263,7 +263,7 @@ library LibRLPWriter { flattenedPtr := add(flattened, 0x20) } - for (i = 0; i < _list.length; i++) { + for (i = 0; i < _list.length; ++i) { bytes memory item = _list[i]; uint256 listPtr; From 470afdf90a5454c70a7dcd7cf12cd066a534b5ba Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Tue, 3 Jan 2023 23:14:23 +0800 Subject: [PATCH 57/57] refactor(protocol): extract IProofVerifier interface (#6800) --- .../protocol/contracts/L1/ProofVerifier.sol | 64 +++++++++++ packages/protocol/contracts/L1/TaikoData.sol | 1 - .../protocol/contracts/L1/libs/LibProving.sol | 107 ++++++++++-------- .../contracts/libs/LibSharedConfig.sol | 3 +- packages/protocol/contracts/libs/LibZKP.sol | 2 +- .../contracts/test/L1/TestTaikoL1.sol | 23 +++- packages/protocol/tasks/deploy_L1.ts | 9 +- packages/protocol/test/L1/TaikoL1.test.ts | 9 +- 8 files changed, 154 insertions(+), 64 deletions(-) create mode 100644 packages/protocol/contracts/L1/ProofVerifier.sol diff --git a/packages/protocol/contracts/L1/ProofVerifier.sol b/packages/protocol/contracts/L1/ProofVerifier.sol new file mode 100644 index 00000000000..cc8bbc8e9d7 --- /dev/null +++ b/packages/protocol/contracts/L1/ProofVerifier.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +// +// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ +// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ +// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ +// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ +// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ +// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ +pragma solidity ^0.8.9; + +import "../thirdparty/LibMerkleTrie.sol"; +import "../libs/LibZKP.sol"; + +/// @author dantaik +interface IProofVerifier { + function verifyZKP( + bytes memory verificationKey, + bytes calldata zkproof, + bytes32 blockHash, + address prover, + bytes32 txListHash + ) external pure returns (bool verified); + + function verifyMKP( + bytes memory key, + bytes memory value, + bytes memory proof, + bytes32 root + ) external pure returns (bool verified); +} + +contract ProofVerifier is IProofVerifier { + function verifyZKP( + bytes memory verificationKey, + bytes calldata zkproof, + bytes32 blockHash, + address prover, + bytes32 txListHash + ) external pure returns (bool) { + return + LibZKP.verify({ + verificationKey: verificationKey, + zkproof: zkproof, + blockHash: blockHash, + prover: prover, + txListHash: txListHash + }); + } + + function verifyMKP( + bytes memory key, + bytes memory value, + bytes memory proof, + bytes32 root + ) external pure returns (bool) { + return + LibMerkleTrie.verifyInclusionProof({ + _key: key, + _value: value, + _proof: proof, + _root: root + }); + } +} diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 66c01c1c79b..795a3380b91 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -41,7 +41,6 @@ library TaikoData { uint64 boostrapDiscountHalvingPeriod; uint64 initialUncleDelay; bool enableTokenomics; - bool skipProofValidation; } struct BlockMetadata { diff --git a/packages/protocol/contracts/L1/libs/LibProving.sol b/packages/protocol/contracts/L1/libs/LibProving.sol index c29c18ab06e..b0afe88b99b 100644 --- a/packages/protocol/contracts/L1/libs/LibProving.sol +++ b/packages/protocol/contracts/L1/libs/LibProving.sol @@ -8,6 +8,7 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; +import {IProofVerifier} from "../ProofVerifier.sol"; import "../../common/AddressResolver.sol"; import "../../common/ConfigManager.sol"; import "../../libs/LibAnchorSignature.sol"; @@ -15,9 +16,7 @@ import "../../libs/LibBlockHeader.sol"; import "../../libs/LibReceiptDecoder.sol"; import "../../libs/LibTxDecoder.sol"; import "../../libs/LibTxUtils.sol"; -import "../../libs/LibZKP.sol"; import "../../thirdparty/LibBytesUtils.sol"; -import "../../thirdparty/LibMerkleTrie.sol"; import "../../thirdparty/LibRLPWriter.sol"; import "./LibUtils.sol"; @@ -107,40 +106,43 @@ library LibProving { ); } - if (!config.skipProofValidation) { - // Check anchor tx is the 1st tx in the block - require( - LibMerkleTrie.verifyInclusionProof({ - _key: LibRLPWriter.writeUint(0), - _value: anchorTx, - _proof: evidence.proofs[zkProofsPerBlock], - _root: evidence.header.transactionsRoot - }), - "L1:tx:proof" - ); + IProofVerifier proofVerifier = IProofVerifier( + resolver.resolve("proof_verifier") + ); - // Check anchor tx does not throw + // Check anchor tx is the 1st tx in the block + require( + proofVerifier.verifyMKP({ + key: LibRLPWriter.writeUint(0), + value: anchorTx, + proof: evidence.proofs[zkProofsPerBlock], + root: evidence.header.transactionsRoot + }), + "L1:tx:proof" + ); - LibReceiptDecoder.Receipt memory receipt = LibReceiptDecoder - .decodeReceipt(anchorReceipt); + // Check anchor tx does not throw - require(receipt.status == 1, "L1:receipt:status"); - require( - LibMerkleTrie.verifyInclusionProof({ - _key: LibRLPWriter.writeUint(0), - _value: anchorReceipt, - _proof: evidence.proofs[zkProofsPerBlock + 1], - _root: evidence.header.receiptsRoot - }), - "L1:receipt:proof" - ); - } + LibReceiptDecoder.Receipt memory receipt = LibReceiptDecoder + .decodeReceipt(anchorReceipt); + + require(receipt.status == 1, "L1:receipt:status"); + require( + proofVerifier.verifyMKP({ + key: LibRLPWriter.writeUint(0), + value: anchorReceipt, + proof: evidence.proofs[zkProofsPerBlock + 1], + root: evidence.header.receiptsRoot + }), + "L1:receipt:proof" + ); // ZK-prove block and mark block proven to be valid. _proveBlock({ state: state, config: config, resolver: resolver, + proofVerifier: proofVerifier, evidence: evidence, target: evidence.meta, blockHashOverride: 0 @@ -172,14 +174,29 @@ library LibProving { "L1:proof:size" ); - if (!config.skipProofValidation) { - // Check the 1st receipt is for an InvalidateBlock tx with - // a BlockInvalidated event - LibReceiptDecoder.Receipt memory receipt = LibReceiptDecoder - .decodeReceipt(invalidateBlockReceipt); - require(receipt.status == 1, "L1:receipt:status"); - require(receipt.logs.length == 1, "L1:receipt:logsize"); + IProofVerifier proofVerifier = IProofVerifier( + resolver.resolve("proof_verifier") + ); + // Check the event is the first one in the throw-away block + require( + proofVerifier.verifyMKP({ + key: LibRLPWriter.writeUint(0), + value: invalidateBlockReceipt, + proof: evidence.proofs[config.zkProofsPerBlock], + root: evidence.header.receiptsRoot + }), + "L1:receipt:proof" + ); + + // Check the 1st receipt is for an InvalidateBlock tx with + // a BlockInvalidated event + LibReceiptDecoder.Receipt memory receipt = LibReceiptDecoder + .decodeReceipt(invalidateBlockReceipt); + require(receipt.status == 1, "L1:receipt:status"); + require(receipt.logs.length == 1, "L1:receipt:logsize"); + + { LibReceiptDecoder.Log memory log = receipt.logs[0]; require( log.contractAddress == @@ -193,17 +210,6 @@ library LibProving { log.topics[1] == target.txListHash, "L1:receipt:topics" ); - - // Check the event is the first one in the throw-away block - require( - LibMerkleTrie.verifyInclusionProof({ - _key: LibRLPWriter.writeUint(0), - _value: invalidateBlockReceipt, - _proof: evidence.proofs[config.zkProofsPerBlock], - _root: evidence.header.receiptsRoot - }), - "L1:receipt:proof" - ); } // ZK-prove block and mark block proven as invalid. @@ -211,6 +217,7 @@ library LibProving { state: state, config: config, resolver: resolver, + proofVerifier: proofVerifier, evidence: evidence, target: target, blockHashOverride: LibUtils.BLOCK_DEADEND_HASH @@ -221,6 +228,7 @@ library LibProving { TaikoData.State storage state, TaikoData.Config memory config, AddressResolver resolver, + IProofVerifier proofVerifier, Evidence memory evidence, TaikoData.BlockMetadata memory target, bytes32 blockHashOverride @@ -238,8 +246,8 @@ library LibProving { bytes32 blockHash = evidence.header.hashBlockHeader(); for (uint256 i = 0; i < config.zkProofsPerBlock; ++i) { - if (!config.skipProofValidation) { - LibZKP.verify({ + require( + proofVerifier.verifyZKP({ verificationKey: ConfigManager( resolver.resolve("config_manager") ).getValue(string(abi.encodePacked("zk_vkey_", i))), @@ -247,8 +255,9 @@ library LibProving { blockHash: blockHash, prover: evidence.prover, txListHash: evidence.meta.txListHash - }); - } + }), + "L1:zkp" + ); } _markBlockProven({ diff --git a/packages/protocol/contracts/libs/LibSharedConfig.sol b/packages/protocol/contracts/libs/LibSharedConfig.sol index bfd693cca50..80e83fcf614 100644 --- a/packages/protocol/contracts/libs/LibSharedConfig.sol +++ b/packages/protocol/contracts/libs/LibSharedConfig.sol @@ -43,8 +43,7 @@ library LibSharedConfig { proofTimeCap: 60 minutes, boostrapDiscountHalvingPeriod: 180 days, initialUncleDelay: 60 minutes, - enableTokenomics: false, - skipProofValidation: false + enableTokenomics: false }); } } diff --git a/packages/protocol/contracts/libs/LibZKP.sol b/packages/protocol/contracts/libs/LibZKP.sol index de3ef163189..6486b19f214 100644 --- a/packages/protocol/contracts/libs/LibZKP.sol +++ b/packages/protocol/contracts/libs/LibZKP.sol @@ -19,7 +19,7 @@ library LibZKP { bytes32 blockHash, address prover, bytes32 txListHash - ) public pure { + ) internal pure returns (bool verified) { // TODO } } diff --git a/packages/protocol/contracts/test/L1/TestTaikoL1.sol b/packages/protocol/contracts/test/L1/TestTaikoL1.sol index c4ae8b8b290..3df5886ad2d 100644 --- a/packages/protocol/contracts/test/L1/TestTaikoL1.sol +++ b/packages/protocol/contracts/test/L1/TestTaikoL1.sol @@ -8,9 +8,10 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; +import {IProofVerifier} from "../../L1/ProofVerifier.sol"; import "../../L1/TaikoL1.sol"; -contract TestTaikoL1NoTokenomicsNoProofValidation is TaikoL1 { +contract TestTaikoL1 is TaikoL1, IProofVerifier { function getConfig() public pure @@ -49,6 +50,24 @@ contract TestTaikoL1NoTokenomicsNoProofValidation is TaikoL1 { config.boostrapDiscountHalvingPeriod = 180 days; config.initialUncleDelay = 1 minutes; config.enableTokenomics = false; - config.skipProofValidation = 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/tasks/deploy_L1.ts b/packages/protocol/tasks/deploy_L1.ts index 06fe2a836e6..3a4aab593ef 100644 --- a/packages/protocol/tasks/deploy_L1.ts +++ b/packages/protocol/tasks/deploy_L1.ts @@ -81,6 +81,13 @@ export async function deployContracts(hre: any) { // AddressManager const AddressManager = await utils.deployContract(hre, "AddressManager"); await utils.waitTx(hre, await AddressManager.init()); + + const ProofVerifier = await utils.deployContract(hre, "ProofVerifier"); + await utils.waitTx( + hre, + await AddressManager.setAddress(`${chainId}.proof_verifier`, ProofVerifier.address) + ); + await utils.waitTx( hre, await AddressManager.setAddress(`${chainId}.dao_vault`, daoVault) @@ -195,7 +202,6 @@ export async function deployContracts(hre: any) { } async function deployBaseLibs(hre: any) { - const libZKP = await utils.deployContract(hre, "LibZKP"); const libReceiptDecoder = await utils.deployContract( hre, "LibReceiptDecoder" @@ -206,7 +212,6 @@ async function deployBaseLibs(hre: any) { const libProposing = await utils.deployContract(hre, "LibProposing", {}); const libProving = await utils.deployContract(hre, "LibProving", { - LibZKP: libZKP.address, LibReceiptDecoder: libReceiptDecoder.address, LibTxDecoder: libTxDecoder.address, }); diff --git a/packages/protocol/test/L1/TaikoL1.test.ts b/packages/protocol/test/L1/TaikoL1.test.ts index 18763fc237d..402f9d91f73 100644 --- a/packages/protocol/test/L1/TaikoL1.test.ts +++ b/packages/protocol/test/L1/TaikoL1.test.ts @@ -21,10 +21,6 @@ describe("TaikoL1", function () { await ethers.getContractFactory("LibTxDecoder") ).deploy(); - const libZKP = await ( - await ethers.getContractFactory("LibZKP") - ).deploy(); - const libProposing = await ( await ethers.getContractFactory("LibProposing") ).deploy(); @@ -33,8 +29,7 @@ describe("TaikoL1", function () { await ethers.getContractFactory("LibProving", { libraries: { LibReceiptDecoder: libReceiptDecoder.address, - LibTxDecoder: libTxDecoder.address, - LibZKP: libZKP.address, + LibTxDecoder: libTxDecoder.address }, }) ).deploy(); @@ -47,7 +42,7 @@ describe("TaikoL1", function () { const feeBase = BigNumber.from(10).pow(18); taikoL1 = await ( await ethers.getContractFactory( - "TestTaikoL1NoTokenomicsNoProofValidation", + "TestTaikoL1", { libraries: { LibVerifying: libVerifying.address,