From 191eb110990d60b49883eb3f3d7841c33421d067 Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Wed, 4 Jan 2023 09:56:11 +0800 Subject: [PATCH] feat(protocol): implement & simulate tokenomics (#376) - Implement tokenomics outlined in our whitepaper - Simulate tokenomics using python - Replaced LibConstants using a Config Object - Various renames for better readability --- .github/workflows/solidity.yml | 12 +- packages/protocol/.solhintignore | 1 + .../protocol/contracts/L1/ProofVerifier.sol | 64 +++ .../L1/{LibData.sol => TaikoData.sol} | 98 ++--- .../L1/{v1/V1Events.sol => TaikoEvents.sol} | 12 +- packages/protocol/contracts/L1/TaikoL1.sol | 215 ++++------ .../V1Proposing.sol => libs/LibProposing.sol} | 147 +++++-- .../{v1/V1Proving.sol => libs/LibProving.sol} | 269 +++++++----- .../protocol/contracts/L1/libs/LibUtils.sol | 194 +++++++++ .../contracts/L1/libs/LibVerifying.sol | 248 +++++++++++ packages/protocol/contracts/L1/v1/V1Utils.sol | 137 ------ .../protocol/contracts/L1/v1/V1Verifying.sol | 121 ------ packages/protocol/contracts/L2/TaikoL2.sol | 53 +-- .../contracts/libs/LibBlockHeader.sol | 4 +- .../protocol/contracts/libs/LibConstants.sol | 40 -- .../contracts/libs/LibInvalidTxList.sol | 30 +- .../contracts/libs/LibReceiptDecoder.sol | 4 +- .../contracts/libs/LibSharedConfig.sol | 49 +++ .../protocol/contracts/libs/LibTxDecoder.sol | 18 +- .../protocol/contracts/libs/LibTxUtils.sol | 9 +- .../contracts/libs/LibUint512Math.sol | 4 +- packages/protocol/contracts/libs/LibZKP.sol | 2 +- .../contracts/test/L1/TestTaikoL1.sol | 73 ++++ .../contracts/test/libs/TestLibTxUtils.sol | 6 +- .../test/thirdparty/TestLibRLPReader.sol | 2 +- .../contracts/thirdparty/LibBytesUtils.sol | 4 +- .../contracts/thirdparty/LibMerkleTrie.sol | 6 +- .../contracts/thirdparty/LibRLPReader.sol | 2 +- .../contracts/thirdparty/LibRLPWriter.sol | 14 +- packages/protocol/docs/L1/TaikoL1.md | 6 +- packages/protocol/hardhat.config.ts | 29 +- packages/protocol/package.json | 2 +- packages/protocol/tasks/deploy_L1.ts | 27 +- packages/protocol/test/L1/TaikoL1.test.ts | 40 +- .../test/bridge/libs/LibBridgeProcess.test.ts | 6 +- .../test/bridge/libs/LibBridgeRetry.test.ts | 12 +- .../test/genesis/generate_genesis.test.ts | 26 +- .../protocol/test/libs/LibTxDecoder.test.ts | 20 +- .../protocol/test/libs/LibTxUtils.test.ts | 43 +- packages/relayer/TaikoL1.json | 4 +- packages/relayer/contracts/TaikoL1.go | 22 +- 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 +++ .../docs/smart-contracts/L1/TaikoL1.md | 6 +- 54 files changed, 2067 insertions(+), 884 deletions(-) create mode 100644 packages/protocol/contracts/L1/ProofVerifier.sol rename packages/protocol/contracts/L1/{LibData.sol => TaikoData.sol} (59%) rename packages/protocol/contracts/L1/{v1/V1Events.sol => TaikoEvents.sol} (75%) rename packages/protocol/contracts/L1/{v1/V1Proposing.sol => libs/LibProposing.sol} (58%) rename packages/protocol/contracts/L1/{v1/V1Proving.sol => libs/LibProving.sol} (53%) create mode 100644 packages/protocol/contracts/L1/libs/LibUtils.sol create mode 100644 packages/protocol/contracts/L1/libs/LibVerifying.sol delete mode 100644 packages/protocol/contracts/L1/v1/V1Utils.sol delete mode 100644 packages/protocol/contracts/L1/v1/V1Verifying.sol 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 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/.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/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/LibData.sol b/packages/protocol/contracts/L1/TaikoData.sol similarity index 59% rename from packages/protocol/contracts/L1/LibData.sol rename to packages/protocol/contracts/L1/TaikoData.sol index d7d7f884fcc..795a3380b91 100644 --- a/packages/protocol/contracts/L1/LibData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -8,10 +8,41 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; -import "../libs/LibConstants.sol"; - /// @author dantaik -library LibData { +library TaikoData { + 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; + } + struct BlockMetadata { uint256 id; uint256 l1Height; @@ -29,6 +60,7 @@ library LibData { // 3 slots struct ProposedBlock { bytes32 metaHash; + uint256 deposit; address proposer; uint64 proposedAt; } @@ -42,7 +74,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; @@ -55,6 +87,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. @@ -70,60 +104,4 @@ library LibData { // 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 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/v1/V1Events.sol b/packages/protocol/contracts/L1/TaikoEvents.sol similarity index 75% rename from packages/protocol/contracts/L1/v1/V1Events.sol rename to packages/protocol/contracts/L1/TaikoEvents.sol index e9dd9d3a933..ff39318598e 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, @@ -32,11 +32,5 @@ 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/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 245b49c0e4a..2305a40abc1 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -11,32 +11,34 @@ pragma solidity ^0.8.9; import "../common/EssentialContract.sol"; import "../common/IHeaderSync.sol"; import "../libs/LibAnchorSignature.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 "../libs/LibSharedConfig.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 LibData for LibData.State; +contract TaikoL1 is EssentialContract, IHeaderSync, TaikoEvents { + using LibUtils for TaikoData.State; - LibData.State public state; - LibData.TentativeState public tentative; - uint256[50] private __gap; + TaikoData.State public state; + uint256[100] private __gap; function init( address _addressManager, - bytes32 _genesisBlockHash + bytes32 _genesisBlockHash, + uint256 _feeBase ) external initializer { EssentialContract._init(_addressManager); - V1Verifying.init({state: state, genesisBlockHash: _genesisBlockHash}); - - tentative.whitelistProposers = false; - tentative.whitelistProvers = true; + LibVerifying.init({ + state: state, + genesisBlockHash: _genesisBlockHash, + feeBase: _feeBase + }); } /** @@ -50,7 +52,12 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { * `calculateCommitHash(beneficiary, txListHash)`. */ function commitBlock(uint64 commitSlot, bytes32 commitHash) external { - V1Proposing.commitBlock(state, commitSlot, commitHash); + LibProposing.commitBlock({ + state: state, + config: getConfig(), + commitSlot: commitSlot, + commitHash: commitHash + }); } /** @@ -73,16 +80,18 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { * transactions in the L2 block. */ function proposeBlock(bytes[] calldata inputs) external nonReentrant { - V1Proposing.proposeBlock({ + TaikoData.Config memory config = getConfig(); + LibProposing.proposeBlock({ state: state, - tentative: tentative, + config: config, resolver: AddressResolver(this), inputs: inputs }); - V1Verifying.verifyBlocks({ + LibVerifying.verifyBlocks({ state: state, + config: getConfig(), resolver: AddressResolver(this), - maxBlocks: LibConstants.K_MAX_VERIFICATIONS_PER_TX, + maxBlocks: config.maxVerificationsPerTx, checkHalt: false }); } @@ -106,17 +115,19 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { uint256 blockId, bytes[] calldata inputs ) external nonReentrant { - V1Proving.proveBlock({ + TaikoData.Config memory config = getConfig(); + LibProving.proveBlock({ state: state, - tentative: tentative, + config: config, resolver: AddressResolver(this), blockId: blockId, inputs: inputs }); - V1Verifying.verifyBlocks({ + LibVerifying.verifyBlocks({ state: state, + config: config, resolver: AddressResolver(this), - maxBlocks: LibConstants.K_MAX_VERIFICATIONS_PER_TX, + maxBlocks: config.maxVerificationsPerTx, checkHalt: false }); } @@ -139,17 +150,20 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { uint256 blockId, bytes[] calldata inputs ) external nonReentrant { - V1Proving.proveBlockInvalid({ + TaikoData.Config memory config = getConfig(); + + LibProving.proveBlockInvalid({ state: state, - tentative: tentative, + config: config, resolver: AddressResolver(this), blockId: blockId, inputs: inputs }); - V1Verifying.verifyBlocks({ + LibVerifying.verifyBlocks({ state: state, + config: config, resolver: AddressResolver(this), - maxBlocks: LibConstants.K_MAX_VERIFICATIONS_PER_TX, + maxBlocks: config.maxVerificationsPerTx, checkHalt: false }); } @@ -160,92 +174,41 @@ 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), 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. - * - * @param prover The prover to be added or removed. - * @param whitelisted True to add; remove otherwise. - */ - function whitelistProver( - address prover, - bool whitelisted - ) public onlyOwner { - V1Utils.whitelistProver({ - tentative: tentative, - prover: prover, - whitelisted: whitelisted - }); - } - /** * Halt or resume the chain. * @param toHalt True to halt, false to resume. */ function halt(bool toHalt) public onlyOwner { - V1Utils.halt(state, toHalt); + 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 V1Utils.isProposerWhitelisted(tentative, proposer); + function getBlockFee() public view returns (uint256) { + (, uint fee, uint deposit) = LibProposing.getBlockFee( + state, + getConfig() + ); + return fee + deposit; } - /** - * 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 V1Utils.isProverWhitelisted(tentative, prover); + function getProofReward( + uint64 provenAt, + uint64 proposedAt + ) public view returns (uint256 reward) { + (, reward, ) = LibVerifying.getProofReward({ + state: state, + config: getConfig(), + provenAt: provenAt, + proposedAt: proposedAt + }); } /** @@ -253,7 +216,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( @@ -262,8 +225,9 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { bytes32 commitHash ) public view returns (bool) { return - V1Proposing.isCommitValid( + LibProposing.isCommitValid( state, + getConfig().commitConfirmations, commitSlot, commitHeight, commitHash @@ -272,14 +236,15 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { function getProposedBlock( uint256 id - ) public view returns (LibData.ProposedBlock memory) { - return state.getProposedBlock(id); + ) public view returns (TaikoData.ProposedBlock memory) { + 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) { @@ -291,9 +256,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(); @@ -313,35 +284,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 (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 58% rename from packages/protocol/contracts/L1/v1/V1Proposing.sol rename to packages/protocol/contracts/L1/libs/LibProposing.sol index 0ef68db1b5d..8525da78284 100644 --- a/packages/protocol/contracts/L1/v1/V1Proposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -9,41 +9,35 @@ pragma solidity ^0.8.9; import "../../common/ConfigManager.sol"; -import "../../libs/LibConstants.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 LibData 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); - - modifier onlyWhitelistedProposer(LibData.TentativeState storage tentative) { - if (tentative.whitelistProposers) { - require(tentative.proposers[msg.sender], "L1:whitelist"); - } - _; - } + event BlockProposed(uint256 indexed id, TaikoData.BlockMetadata meta); function commitBlock( - LibData.State storage state, + TaikoData.State storage state, + TaikoData.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. // - // assert(!V1Utils.isHalted(state)); + // assert(!LibUtils.isHalted(state)); bytes32 hash = _aggregateCommitHash(block.number, commitHash); @@ -58,33 +52,37 @@ library V1Proposing { } function proposeBlock( - LibData.State storage state, - LibData.TentativeState storage tentative, + TaikoData.State storage state, + TaikoData.Config memory config, AddressResolver resolver, bytes[] calldata inputs - ) public onlyWhitelistedProposer(tentative) { - assert(!V1Utils.isHalted(state)); + ) public { + 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, 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 <= config.maxBytesPerTxList && meta.txListHash == txList.hashTxList(), "L1:txList" ); require( state.nextBlockId < - state.latestVerifiedId + LibConstants.K_MAX_NUM_BLOCKS, + state.latestVerifiedId + config.maxNumBlocks, "L1:tooMany" ); @@ -98,45 +96,101 @@ 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(); + uint256 deposit; + if (config.enableTokenomics) { + uint256 newFeeBase; + { + uint256 fee; + (newFeeBase, fee, deposit) = getBlockFee(state, config); + TkoToken(resolver.resolve("tko_token")).burn( + msg.sender, + fee + deposit + ); + } + // Update feeBase and avgBlockTime + state.feeBase = LibUtils.movingAverage({ + maValue: state.feeBase, + newValue: newFeeBase, + maf: config.feeBaseMAF + }); + } - state.saveProposedBlock( + _saveProposedBlock( + state, + config.maxNumBlocks, state.nextBlockId, - LibData.ProposedBlock({ - metaHash: LibData.hashMetadata(meta), + TaikoData.ProposedBlock({ + metaHash: meta.hashMetadata(), + deposit: deposit, proposer: msg.sender, proposedAt: meta.timestamp }) ); + state.avgBlockTime = LibUtils + .movingAverage({ + maValue: state.avgBlockTime, + newValue: meta.timestamp - state.lastProposedAt, + maf: config.blockTimeMAF + }) + .toUint64(); + state.lastProposedAt = meta.timestamp; + emit BlockProposed(state.nextBlockId++, meta); } + function getBlockFee( + TaikoData.State storage state, + TaikoData.Config memory config + ) public view returns (uint256 newFeeBase, uint256 fee, uint256 deposit) { + (newFeeBase, ) = LibUtils.getTimeAdjustedFee({ + state: state, + config: config, + isProposal: true, + tNow: uint64(block.timestamp), + tLast: state.lastProposedAt, + tAvg: state.avgBlockTime + }); + fee = LibUtils.getSlotsAdjustedFee({ + state: state, + config: config, + isProposal: true, + feeBase: newFeeBase + }); + 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, 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 _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, - LibData.BlockMetadata memory meta + TaikoData.State storage state, + uint256 commitConfirmations, + TaikoData.BlockMetadata memory meta ) private { - if (LibConstants.K_COMMIT_DELAY_CONFIRMS == 0) { + if (commitConfirmations == 0) { return; } bytes32 commitHash = _calculateCommitHash( @@ -147,6 +201,7 @@ library V1Proposing { require( isCommitValid({ state: state, + commitConfirmations: commitConfirmations, commitSlot: meta.commitSlot, commitHeight: meta.commitHeight, commitHash: commitHash @@ -161,7 +216,10 @@ library V1Proposing { } } - function _validateMetadata(LibData.BlockMetadata memory meta) private pure { + function _validateMetadata( + TaikoData.Config memory config, + TaikoData.BlockMetadata memory meta + ) private pure { require( meta.id == 0 && meta.l1Height == 0 && @@ -173,10 +231,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/libs/LibProving.sol similarity index 53% rename from packages/protocol/contracts/L1/v1/V1Proving.sol rename to packages/protocol/contracts/L1/libs/LibProving.sol index 0c0158e4e5f..b0afe88b99b 100644 --- a/packages/protocol/contracts/L1/v1/V1Proving.sol +++ b/packages/protocol/contracts/L1/libs/LibProving.sol @@ -8,33 +8,38 @@ // ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ pragma solidity ^0.8.9; +import {IProofVerifier} from "../ProofVerifier.sol"; 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"; -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 LibData 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 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, @@ -44,21 +49,14 @@ library V1Proving { address prover ); - modifier onlyWhitelistedProver(LibData.TentativeState storage tentative) { - if (tentative.whitelistProvers) { - require(tentative.provers[msg.sender], "L1:whitelist"); - } - _; - } - function proveBlock( - LibData.State storage state, - LibData.TentativeState storage tentative, + TaikoData.State storage state, + TaikoData.Config memory config, AddressResolver resolver, uint256 blockId, bytes[] calldata inputs - ) public onlyWhitelistedProver(tentative) { - assert(!V1Utils.isHalted(state)); + ) public { + assert(!LibUtils.isHalted(state)); // Check and decode inputs require(inputs.length == 3, "L1:inputs:size"); @@ -68,62 +66,73 @@ 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" + ); + } + + IProofVerifier proofVerifier = IProofVerifier( + resolver.resolve("proof_verifier") ); // 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 + proofVerifier.verifyMKP({ + 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); 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 + proofVerifier.verifyMKP({ + key: LibRLPWriter.writeUint(0), + value: anchorReceipt, + proof: evidence.proofs[zkProofsPerBlock + 1], + root: evidence.header.receiptsRoot }), "L1:receipt:proof" ); @@ -131,7 +140,9 @@ library V1Proving { // 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 @@ -139,101 +150,119 @@ library V1Proving { } function proveBlockInvalid( - LibData.State storage state, - LibData.TentativeState storage tentative, + TaikoData.State storage state, + TaikoData.Config memory config, AddressResolver resolver, uint256 blockId, bytes[] calldata inputs - ) public onlyWhitelistedProver(tentative) { - assert(!V1Utils.isHalted(state)); + ) public { + 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]; // 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"); - - 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" + IProofVerifier proofVerifier = IProofVerifier( + resolver.resolve("proof_verifier") ); // 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 + 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 == + 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" + ); + } + // ZK-prove block and mark block proven as invalid. _proveBlock({ state: state, + config: config, resolver: resolver, + proofVerifier: proofVerifier, evidence: evidence, target: target, - blockHashOverride: LibConstants.K_BLOCK_DEADEND_HASH + blockHashOverride: LibUtils.BLOCK_DEADEND_HASH }); } function _proveBlock( - LibData.State storage state, + TaikoData.State storage state, + TaikoData.Config memory config, AddressResolver resolver, + IProofVerifier proofVerifier, Evidence memory evidence, - LibData.BlockMetadata memory target, + TaikoData.BlockMetadata memory target, bytes32 blockHashOverride ) private { 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) { + require( + proofVerifier.verifyZKP({ + 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 + }), + "L1:zkp" + ); } _markBlockProven({ state: state, + config: config, prover: evidence.prover, target: target, parentHash: evidence.header.parentHash, @@ -242,13 +271,14 @@ library V1Proving { } function _markBlockProven( - LibData.State storage state, + 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 ]; @@ -259,22 +289,27 @@ 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; } 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), + LibUtils.uncleProofDeadline({ + state: state, + config: config, + fc: fc, + blockId: target.id + }), "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"); } } @@ -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"); @@ -309,30 +345,31 @@ library V1Proving { } function _checkMetadata( - LibData.State storage state, - 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, "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( + TaikoData.Config memory config, BlockHeader memory header, - LibData.BlockMetadata memory meta + TaikoData.BlockMetadata memory meta ) private pure { require( 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/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol new file mode 100644 index 00000000000..84adbc26a63 --- /dev/null +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: MIT +// +// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ +// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ +// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ +// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ +// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ +// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ +pragma solidity ^0.8.9; + +import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; + +import "../../libs/LibMath.sol"; +import "../TaikoData.sol"; + +/// @author dantaik +library LibUtils { + using LibMath for uint256; + + uint64 public constant MASK_HALT = 1 << 0; + + bytes32 public constant BLOCK_DEADEND_HASH = bytes32(uint256(1)); + + event Halted(bool halted); + + 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( + TaikoData.State storage state, + uint256 maxNumBlocks, + uint256 id + ) internal view returns (TaikoData.ProposedBlock storage) { + return state.proposedBlocks[id % maxNumBlocks]; + } + + function getL2BlockHash( + TaikoData.State storage state, + uint256 number + ) internal view returns (bytes32) { + require(number <= state.latestVerifiedHeight, "L1:id"); + return state.l2Hashes[number]; + } + + function getStateVariables( + TaikoData.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( + TaikoData.State storage state + ) internal view returns (bool) { + return isBitOne(state, MASK_HALT); + } + + // Implement "Incentive Multipliers", see the whitepaper. + function getTimeAdjustedFee( + TaikoData.State storage state, + TaikoData.Config memory config, + bool isProposal, + uint64 tNow, + uint64 tLast, + uint64 tAvg + ) internal view returns (uint256 newFeeBase, uint256 tRelBp) { + if (tAvg == 0) { + newFeeBase = state.feeBase; + tRelBp = 0; + } else { + 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 + + ((config.rewardMultiplierPctg - 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( + TaikoData.State storage state, + TaikoData.Config memory config, + bool isProposal, + uint256 feeBase + ) internal view returns (uint256) { + // m is the `n'` in the whitepaper + 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 + 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( + TaikoData.State storage state, + TaikoData.Config memory config, + uint256 feeBase + ) internal view returns (uint256) { + uint256 halves = uint256(block.timestamp - state.genesisTimestamp) / + config.boostrapDiscountHalvingPeriod; + uint256 gamma = 1024 - (1024 >> halves); + return (feeBase * gamma) / 1024; + } + + // Returns a deterministic deadline for uncle proof submission. + function uncleProofDeadline( + TaikoData.State storage state, + TaikoData.Config memory config, + TaikoData.ForkChoice storage fc, + uint256 blockId + ) internal view returns (uint64) { + if (blockId <= 2 * config.maxNumBlocks) { + return fc.provenAt + config.initialUncleDelay; + } else { + return fc.provenAt + state.avgProofTime; + } + } + + function hashMetadata( + TaikoData.BlockMetadata memory meta + ) internal pure returns (bytes32) { + return keccak256(abi.encode(meta)); + } + + 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( + TaikoData.State storage state, + uint64 mask, + bool one + ) private { + state.statusBits = one + ? state.statusBits | mask + : state.statusBits & ~mask; + } + + function isBitOne( + TaikoData.State storage state, + uint64 mask + ) private view returns (bool) { + return state.statusBits & mask != 0; + } +} diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol new file mode 100644 index 00000000000..406cdf4c029 --- /dev/null +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: MIT +// +// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ +// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ +// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ +// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ +// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ +// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ +pragma solidity ^0.8.9; + +import "../../common/AddressResolver.sol"; +import "../TkoToken.sol"; +import "./LibUtils.sol"; + +/// @author dantaik +library LibVerifying { + using SafeCastUpgradeable for uint256; + using LibUtils for TaikoData.State; + + event BlockVerified(uint256 indexed id, bytes32 blockHash); + + event HeaderSynced( + uint256 indexed height, + uint256 indexed srcHeight, + bytes32 srcHash + ); + + function init( + TaikoData.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( + TaikoData.State storage state, + TaikoData.Config memory config, + AddressResolver resolver, + uint256 maxBlocks, + bool checkHalt + ) public { + bool halted = LibUtils.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++ + ) { + TaikoData.ForkChoice storage fc = state.forkChoices[i][ + latestL2Hash + ]; + TaikoData.ProposedBlock storage target = state.getProposedBlock( + config.maxNumBlocks, + i + ); + + // Uncle proof can not take more than 2x time the first proof did. + if ( + !_isVerifiable({ + state: state, + config: config, + fc: fc, + blockId: i + }) + ) { + break; + } else { + (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); + } + } + + if (processed > 0) { + state.latestVerifiedId += processed; + + if (latestL2Height > state.latestVerifiedHeight) { + state.latestVerifiedHeight = latestL2Height; + + // 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); + } + } + } + + function getProofReward( + TaikoData.State storage state, + TaikoData.Config memory config, + uint64 provenAt, + uint64 proposedAt + ) public view returns (uint256 newFeeBase, uint256 reward, uint256 tRelBp) { + (newFeeBase, tRelBp) = LibUtils.getTimeAdjustedFee({ + state: state, + config: config, + isProposal: false, + tNow: provenAt, + tLast: proposedAt, + tAvg: state.avgProofTime + }); + reward = LibUtils.getSlotsAdjustedFee({ + state: state, + config: config, + isProposal: false, + feeBase: newFeeBase + }); + reward = (reward * (10000 - config.rewardBurnBips)) / 10000; + } + + function _refundProposerDeposit( + TaikoData.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( + TaikoData.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 _verifyBlock( + TaikoData.State storage state, + TaikoData.Config memory config, + AddressResolver resolver, + TaikoData.ForkChoice storage fc, + TaikoData.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 = LibUtils.movingAverage({ + maValue: state.feeBase, + newValue: newFeeBase, + maf: config.feeBaseMAF + }); + } + + state.avgProofTime = LibUtils + .movingAverage({ + maValue: state.avgProofTime, + newValue: fc.provenAt - target.proposedAt, + maf: config.proofTimeMAF + }) + .toUint64(); + + if (fc.blockHash != LibUtils.BLOCK_DEADEND_HASH) { + _latestL2Height = latestL2Height + 1; + _latestL2Hash = fc.blockHash; + } else { + _latestL2Height = latestL2Height; + _latestL2Hash = latestL2Hash; + } + } + + function _cleanUp(TaikoData.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( + TaikoData.State storage state, + TaikoData.Config memory config, + TaikoData.ForkChoice storage fc, + uint256 blockId + ) private view returns (bool) { + return + fc.blockHash != 0 && + block.timestamp > + LibUtils.uncleProofDeadline({ + state: state, + config: config, + fc: fc, + blockId: blockId + }); + } +} diff --git a/packages/protocol/contracts/L1/v1/V1Utils.sol b/packages/protocol/contracts/L1/v1/V1Utils.sol deleted file mode 100644 index 5b1921ea073..00000000000 --- a/packages/protocol/contracts/L1/v1/V1Utils.sol +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ -// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ -// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ -// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ -// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ -// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ -pragma solidity ^0.8.9; - -import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; - -import "../../libs/LibMath.sol"; -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 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 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, - uint256 blockId - ) internal view returns (uint64) { - if (blockId <= 2 * LibConstants.K_MAX_NUM_BLOCKS) { - return fc.provenAt + LibConstants.K_INITIAL_UNCLE_DELAY; - } else { - 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, - bool one - ) private { - state.statusBits = one - ? state.statusBits | mask - : state.statusBits & ~mask; - } - - function isBitOne( - LibData.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/v1/V1Verifying.sol deleted file mode 100644 index 9f33dcc1356..00000000000 --- a/packages/protocol/contracts/L1/v1/V1Verifying.sol +++ /dev/null @@ -1,121 +0,0 @@ -// 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]; - 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, i)) { - break; - } else { - 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(); - - processed += 1; - emit BlockVerified(i, fc.blockHash); - _cleanUp(fc); - } - } - - 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, - uint256 blockId - ) private view returns (bool) { - return - fc.blockHash != 0 && - block.timestamp > V1Utils.uncleProofDeadline(state, fc, blockId); - } -} diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index 1daa811e8bf..620697c88b1 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"; @@ -16,7 +15,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 @@ -50,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); } @@ -104,6 +103,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 +119,16 @@ contract TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync { * Public Functions * **********************/ + function getConfig() + public + view + virtual + returns (TaikoData.Config memory config) + { + config = LibSharedConfig.getConfig(); + config.chainId = block.chainid; + } + function getSyncedHeader( uint256 number ) public view override returns (bytes32) { @@ -143,48 +153,13 @@ 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; 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/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 4804c038d8f..00000000000 --- a/packages/protocol/contracts/libs/LibConstants.sol +++ /dev/null @@ -1,40 +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_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)"); -} diff --git a/packages/protocol/contracts/libs/LibInvalidTxList.sol b/packages/protocol/contracts/libs/LibInvalidTxList.sol index 40d31051297..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 "../libs/LibConstants.sol"; +import "../L1/TaikoData.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( + TaikoData.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/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/LibSharedConfig.sol b/packages/protocol/contracts/libs/LibSharedConfig.sol new file mode 100644 index 00000000000..80e83fcf614 --- /dev/null +++ b/packages/protocol/contracts/libs/LibSharedConfig.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT +// +// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ +// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ +// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ +// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ +// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ +// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ +pragma solidity ^0.8.9; + +import "../L1/TaikoData.sol"; + +library LibSharedConfig { + /// Returns shared configs for both TaikoL1 and TaikoL2 for production. + function getConfig() internal pure returns (TaikoData.Config memory) { + return + TaikoData.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 + }); + } +} diff --git a/packages/protocol/contracts/libs/LibTxDecoder.sol b/packages/protocol/contracts/libs/LibTxDecoder.sol index 9dff797f56a..fd4d24c4341 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) { @@ -84,14 +84,15 @@ library LibTxDecoder { LibRLPReader.RLPItem[] memory txs = LibRLPReader.readList(encoded); Tx[] memory _txList = new Tx[](txs.length); - for (uint256 i = 0; i < txs.length; i++) { - _txList[i] = decodeTx(LibRLPReader.readBytes(txs[i])); + for (uint256 i = 0; i < txs.length; ++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]); @@ -211,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] ); @@ -220,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); @@ -231,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 d68edfbbdc3..22273a40197 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 @@ -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) { @@ -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/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 new file mode 100644 index 00000000000..3df5886ad2d --- /dev/null +++ b/packages/protocol/contracts/test/L1/TestTaikoL1.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +// +// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ +// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ +// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ +// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ +// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ +// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ +pragma solidity ^0.8.9; + +import {IProofVerifier} from "../../L1/ProofVerifier.sol"; +import "../../L1/TaikoL1.sol"; + +contract TestTaikoL1 is TaikoL1, IProofVerifier { + function getConfig() + public + pure + override + returns (TaikoData.Config memory config) + { + config.chainId = 167; + // up to 2048 pending blocks + config.maxNumBlocks = 4; + config.blockHashHistory = 3; + // This number is calculated from maxNumBlocks to make + // the 'the maximum value of the multiplier' close to 20.0 + config.zkProofsPerBlock = 1; + config.maxVerificationsPerTx = 2; + config.commitConfirmations = 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; + } + + 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/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/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; 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 94cc257500f..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/", @@ -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 ca45b2b8736..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) @@ -89,7 +96,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) @@ -124,9 +131,11 @@ export async function deployContracts(hre: any) { 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 @@ -193,26 +202,24 @@ 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" ); 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", { - LibZKP: libZKP.address, + const libProving = await utils.deployContract(hre, "LibProving", { 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 c235ea58885..402f9d91f73 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"; import { TaikoL1 } from "../../typechain"; describe("TaikoL1", function () { @@ -20,39 +21,38 @@ describe("TaikoL1", function () { await ethers.getContractFactory("LibTxDecoder") ).deploy(); - const libZKP = await ( - await ethers.getContractFactory("LibZKP") + const libProposing = await ( + await ethers.getContractFactory("LibProposing") ).deploy(); - const v1Proposing = await ( - await ethers.getContractFactory("V1Proposing") - ).deploy(); - - const v1Proving = await ( - await ethers.getContractFactory("V1Proving", { + const libProving = await ( + await ethers.getContractFactory("LibProving", { libraries: { LibReceiptDecoder: libReceiptDecoder.address, - LibTxDecoder: libTxDecoder.address, - LibZKP: libZKP.address, + LibTxDecoder: libTxDecoder.address }, }) ).deploy(); - const v1Verifying = await ( - await ethers.getContractFactory("V1Verifying") + const libVerifying = await ( + await ethers.getContractFactory("LibVerifying") ).deploy(); 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( + "TestTaikoL1", + { + libraries: { + LibVerifying: libVerifying.address, + LibProposing: libProposing.address, + LibProving: libProving.address, + }, + } + ) ).deploy(); - await taikoL1.init(addressManager.address, genesisHash); + await taikoL1.init(addressManager.address, genesisHash, feeBase); }); describe("getLatestSyncedHeader()", async function () { 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/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, 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/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, + ), + ], + ), +) diff --git a/packages/website/docs/smart-contracts/L1/TaikoL1.md b/packages/website/docs/smart-contracts/L1/TaikoL1.md index a351c69e3f3..3a52bde5cf9 100644 --- a/packages/website/docs/smart-contracts/L1/TaikoL1.md +++ b/packages/website/docs/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