polygon cdk aggregator 报execution reverted (0x09bde339)
错误信息 2024-11-18T08:36:32.892Z ERROR ethtxmanager/ethtxmanager.go:270 failed to estimate gas: execution reverted (0x09bde339), data: 1489ed1000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000520000000000000000000000000000000000000000000000000000000000000000cbec6bce507ef87ba7cb1adaca9d65f2791bfc03cc77c6602c4e6f7be7eb23e100000000000000000000000047ce1498ae40c634d60314f447816a2184602a7a20227cbcef731b6cbdc0edd5850c63dc7fbc27fb58d12cd4d08298799cf66a0512c230867d3375a1f4669e7267dad2c31ebcddbaccea6abd67798ceae35ae7611c665b6069339e6812d015e239594aa71c4e217288e374448c358f6459e057c91ad2ef514570b5dea21508e214430daadabdd23433820000fe98b1c6fa81d5c512b86fbf87bd7102775f8ef1da7e8014dc7aab225503237c7927c032e589e9a01a0eab9fda82ffe834c2a4977f36cc9bcb1f2327bdac5fb48ffbeb9656efcdf70d2656c328903e9fb96e4e3f470c447b3053cc68d68cf0ad317fe10aa7f254222e47ea07f3c1c3aacb74e5926a67262f261c1ed3120576ab877b49a81fb8aac51431858662af6b1a8138a44e9d0812d032340369459ccc98b109347cc874c7202dceecc3dbb09d7f9e5658f1ca3a92d22be1fa28f9945205d853e2c866d9b649301ac9857b07b92e4865283d3d5e2b711ea5f85cb2da71965382ece050508d3d008bbe4df5458f70bd3e1bfcc50b34222b43cd28cbe39a3bab6e464664a742161df99c607638e415ced49d0cd719518539ed5f561f81d07fe40d3ce85508e0332465313e60ad9ae271d580022ffca4fbe4d72d38d18e7a6e20d020a1d1e5a8f411291ab95521386fa538ddfe6a391d4a3669cc64c40f07895f031550b32f7d73205a69c214a8ef3cdf996c495e3fd24c00873f30ea6b2bfabfd38de1c3da357d1fefe203573fdad22f675cb5cfabbec0a041b1b31274f70193da8e90cfc4d6dc054c7cd26d09c1dadd064ec52b6ddcfa0cb144d65d9e131c0c88f8004f90d363034d839aa7760167b5302c36d2c2f6714b41782070b10c51c178bd923182d28502f36e19b079b190008c46d19c399331fd60b6b6bde898bd1dd0a71ee7ec7ff7124cc3d374846614389e7b5975b77c4059bc42b810673dbb6f8b951e5b636bdf24afd2a3cbe96ce8600e8a79731b4a56c697596e0bff7b73f413bdbc75069b002b00d713fae8d6450428246f1b794d56717050fdb77bbe094ac2ee6af54a153e2fb8ce1d31a86c4fdd523783b910bedf7db58a46ba6ce48ac3ca194f3cf2275e {"pid": 40} github.com/0xPolygon/zkevm-ethtx-manager/ethtxmanager.(*Client).add /go/pkg/mod/github.com/0x!polygon/zkevm-ethtx-manager@v0.2.1/ethtxmanager/ethtxmanager.go:270 github.com/0xPolygon/zkevm-ethtx-manager/ethtxmanager.(*Client).Add /go/pkg/mod/github.com/0x!polygon/zkevm-ethtx-manager@v0.2.1/ethtxmanager/ethtxmanager.go:177 github.com/0xPolygon/cdk/aggregator.(*Aggregator).settleDirect /go/src/github.com/0xPolygon/cdk/aggregator/aggregator.go:587 github.com/0xPolygon/cdk/aggregator.(*Aggregator).sendFinalProof /go/src/github.com/0xPolygon/cdk/aggregator/aggregator.go:508 2024-11-18T08:36:32.892Z ERROR aggregator/aggregator.go:589 Error Adding TX to ethTxManager: failed to estimate gas: execution reverted (0x09bde339), data: 1489ed1000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000520000000000000000000000000000000000000000000000000000000000000000cbec6bce507ef87ba7cb1adaca9d65f2791bfc03cc77c6602c4e6f7be7eb23e100000000000000000000000047ce1498ae40c634d60314f447816a2184602a7a20227cbcef731b6cbdc0edd5850c63dc7fbc27fb58d12cd4d08298799cf66a0512c230867d3375a1f4669e7267dad2c31ebcddbaccea6abd67798ceae35ae7611c665b6069339e6812d015e239594aa71c4e217288e374448c358f6459e057c91ad2ef514570b5dea21508e214430daadabdd23433820000fe98b1c6fa81d5c512b86fbf87bd7102775f8ef1da7e8014dc7aab225503237c7927c032e589e9a01a0eab9fda82ffe834c2a4977f36cc9bcb1f2327bdac5fb48ffbeb9656efcdf70d2656c328903e9fb96e4e3f470c447b3053cc68d68cf0ad317fe10aa7f254222e47ea07f3c1c3aacb74e5926a67262f261c1ed3120576ab877b49a81fb8aac51431858662af6b1a8138a44e9d0812d032340369459ccc98b109347cc874c7202dceecc3dbb09d7f9e5658f1ca3a92d22be1fa28f9945205d853e2c866d9b649301ac9857b07b92e4865283d3d5e2b711ea5f85cb2da71965382ece050508d3d008bbe4df5458f70bd3e1bfcc50b34222b43cd28cbe39a3bab6e464664a742161df99c607638e415ced49d0cd719518539ed5f561f81d07fe40d3ce85508e0332465313e60ad9ae271d580022ffca4fbe4d72d38d18e7a6e20d020a1d1e5a8f411291ab95521386fa538ddfe6a391d4a3669cc64c40f07895f031550b32f7d73205a69c214a8ef3cdf996c495e3fd24c00873f30ea6b2bfabfd38de1c3da357d1fefe203573fdad22f675cb5cfabbec0a041b1b31274f70193da8e90cfc4d6dc054c7cd26d09c1dadd064ec52b6ddcfa0cb144d65d9e131c0c88f8004f90d363034d839aa7760167b5302c36d2c2f6714b41782070b10c51c178bd923182d28502f36e19b079b190008c46d19c399331fd60b6b6bde898bd1dd0a71ee7ec7ff7124cc3d374846614389e7b5975b77c4059bc42b810673dbb6f8b951e5b636bdf24afd2a3cbe96ce8600e8a79731b4a56c697596e0bff7b73f413bdbc75069b002b00d713fae8d6450428246f1b794d56717050fdb77bbe094ac2ee6af54a153e2fb8ce1d31a86c4fdd523783b910bedf7db58a46ba6ce48ac3ca194f3cf2275e {"pid": 40, "version": "v0.4.0-beta5", "module": "aggregator"} github.com/0xPolygon/cdk/aggregator.(*Aggregator).settleDirect /go/src/github.com/0xPolygon/cdk/aggregator/aggregator.go:589 github.com/0xPolygon/cdk/aggregator.(*Aggregator).sendFinalProof /go/src/github.com/0xPolygon/cdk/aggregator/aggregator.go:508 2024-11-18T08:36:32.892Z ERROR aggregator/aggregator.go:591 Error to add batch verification tx to eth tx manager: failed to estimate gas: execution reverted (0x09bde339), data: 1489ed1000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000520000000000000000000000000000000000000000000000000000000000000000cbec6bce507ef87ba7cb1adaca9d65f2791bfc03cc77c6602c4e6f7be7eb23e100000000000000000000000047ce1498ae40c634d60314f447816a2184602a7a20227cbcef731b6cbdc0edd5850c63dc7fbc27fb58d12cd4d08298799cf66a0512c230867d3375a1f4669e7267dad2c31ebcddbaccea6abd67798ceae35ae7611c665b6069339e6812d015e239594aa71c4e217288e374448c358f6459e057c91ad2ef514570b5dea21508e214430daadabdd23433820000fe98b1c6fa81d5c512b86fbf87bd7102775f8ef1da7e8014dc7aab225503237c7927c032e589e9a01a0eab9fda82ffe834c2a4977f36cc9bcb1f2327bdac5fb48ffbeb9656efcdf70d2656c328903e9fb96e4e3f470c447b3053cc68d68cf0ad317fe10aa7f254222e47ea07f3c1c3aacb74e5926a67262f261c1ed3120576ab877b49a81fb8aac51431858662af6b1a8138a44e9d0812d032340369459ccc98b109347cc874c7202dceecc3dbb09d7f9e5658f1ca3a92d22be1fa28f9945205d853e2c866d9b649301ac9857b07b92e4865283d3d5e2b711ea5f85cb2da71965382ece050508d3d008bbe4df5458f70bd3e1bfcc50b34222b43cd28cbe39a3bab6e464664a742161df99c607638e415ced49d0cd719518539ed5f561f81d07fe40d3ce85508e0332465313e60ad9ae271d580022ffca4fbe4d72d38d18e7a6e20d020a1d1e5a8f411291ab95521386fa538ddfe6a391d4a3669cc64c40f07895f031550b32f7d73205a69c214a8ef3cdf996c495e3fd24c00873f30ea6b2bfabfd38de1c3da357d1fefe203573fdad22f675cb5cfabbec0a041b1b31274f70193da8e90cfc4d6dc054c7cd26d09c1dadd064ec52b6ddcfa0cb144d65d9e131c0c88f8004f90d363034d839aa7760167b5302c36d2c2f6714b41782070b10c51c178bd923182d28502f36e19b079b190008c46d19c399331fd60b6b6bde898bd1dd0a71ee7ec7ff7124cc3d374846614389e7b5975b77c4059bc42b810673dbb6f8b951e5b636bdf24afd2a3cbe96ce8600e8a79731b4a56c697596e0bff7b73f413bdbc75069b002b00d713fae8d6450428246f1b794d56717050fdb77bbe094ac2ee6af54a153e2fb8ce1d31a86c4fdd523783b910bedf7db58a46ba6ce48ac3ca194f3cf2275e {"pid": 40, "monitoredTxId": "0x0000000000000000000000000000000000000000000000000000000000000000", "from": "0x47ce1498Ae40c634D60314F447816a2184602A7a", "to": "0x12dF12067fcCf1830424b419E338101E8FC09d41"} github.com/0xPolygon/cdk/aggregator.(*Aggregator).settleDirect /go/src/github.com/0xPolygon/cdk/aggregator/aggregator.go:591 github.com/0xPolygon/cdk/aggregator.(*Aggregator).sendFinalProof /go/src/github.com/0xPolygon/cdk/aggregator/aggregator.go:508 链代码分析 // settleDirect sends the final proof to the L1 smart contract directly. func (a *Aggregator) settleDirect( ctx context.Context, proof *state.Proof, inputs ethmanTypes.FinalProofInputs) bool { // add batch verification to be monitored sender := common.HexToAddress(a.cfg.SenderAddress) to, data, err := a.etherman.BuildTrustedVerifyBatchesTxData( func (etherMan *Client) BuildTrustedVerifyBatchesTxData( lastVerifiedBatch, newVerifiedBatch uint64, inputs *ethmanTypes.FinalProofInputs, beneficiary common.Address, ) (to *common.Address, data []byte, err error) { opts, err := etherMan.generateRandomAuth() if err != nil { return nil, nil, fmt.Errorf("failed to build trusted verify batches, err: %w", err) } opts.NoSend = true // force nonce, gas limit and gas price to avoid querying it from the chain opts.Nonce = big.NewInt(1) opts.GasLimit = uint64(1) opts.GasPrice = big.NewInt(1) var newLocalExitRoot [32]byte copy(newLocalExitRoot[:], inputs.NewLocalExitRoot) var newStateRoot [32]byte copy(newStateRoot[:], inputs.NewStateRoot) proof, err := convertProof(inputs.FinalProof.Proof) if err != nil { log.Errorf("error converting proof. Error: %v, Proof: %s", err, inputs.FinalProof.Proof) return nil, nil, err } const pendStateNum = 0 // TODO hardcoded for now until we implement the pending state feature tx, err := etherMan.Contracts.Banana.RollupManager.VerifyBatchesTrustedAggregator( &opts, etherMan.RollupID, pendStateNum, lastVerifiedBatch, newVerifiedBatch, newLocalExitRoot, newStateRoot, beneficiary, proof, ) if err != nil { if parsedErr, ok := TryParseError(err); ok { err = parsedErr } return nil, nil, err } return tx.To(), tx.Data(), nil } 调用的合约为 Banana.RollupManager.VerifyBatchesTrustedAggregator 函数选择器->函数名 https://chaintool.tech/querySelector 0x09bde339->InvalidProof() 合约代码 function verifyBatchesTrustedAggregator( uint32 rollupID, uint64 pendingStateNum, uint64 initNumBatch, uint64 finalNewBatch, bytes32 newLocalExitRoot, bytes32 newStateRoot, address beneficiary, bytes32[24] calldata proof ) external onlyRole(_TRUSTED_AGGREGATOR_ROLE) { RollupData storage rollup = rollupIDToRollupData[rollupID]; _verifyAndRewardBatches( rollup, pendingStateNum, initNumBatch, finalNewBatch, newLocalExitRoot, newStateRoot, beneficiary, proof ); // Consolidate state rollup.lastVerifiedBatch = finalNewBatch; rollup.batchNumToStateRoot[finalNewBatch] = newStateRoot; rollup.lastLocalExitRoot = newLocalExitRoot; // Clean pending state if any if (rollup.lastPendingState > 0) { rollup.lastPendingState = 0; rollup.lastPendingStateConsolidated = 0; } // Interact with globalExitRootManager globalExitRootManager.updateExitRoot(getRollupExitRoot()); emit VerifyBatchesTrustedAggregator( rollupID, finalNewBatch, newStateRoot, newLocalExitRoot, msg.sender ); } function _verifyAndRewardBatches( RollupData storage rollup, uint64 pendingStateNum, uint64 initNumBatch, uint64 finalNewBatch, bytes32 newLocalExitRoot, bytes32 newStateRoot, address beneficiary, bytes32[24] calldata proof ) internal virtual { bytes32 oldStateRoot; uint64 currentLastVerifiedBatch = _getLastVerifiedBatch(rollup); if (initNumBatch < rollup.lastVerifiedBatchBeforeUpgrade) { revert InitBatchMustMatchCurrentForkID(); } // Use pending state if specified, otherwise use consolidated state if (pendingStateNum != 0) { // Check that pending state exist // Already consolidated pending states can be used aswell if (pendingStateNum > rollup.lastPendingState) { revert PendingStateDoesNotExist(); } // Check choosen pending state PendingState storage currentPendingState = rollup .pendingStateTransitions[pendingStateNum]; // Get oldStateRoot from pending batch oldStateRoot = currentPendingState.stateRoot; // Check initNumBatch matches the pending state if (initNumBatch != currentPendingState.lastVerifiedBatch) { revert InitNumBatchDoesNotMatchPendingState(); } } else { // Use consolidated state oldStateRoot = rollup.batchNumToStateRoot[initNumBatch]; if (oldStateRoot == bytes32(0)) { revert OldStateRootDoesNotExist(); } // Check initNumBatch is inside the range, sanity check if (initNumBatch > currentLastVerifiedBatch) { revert InitNumBatchAboveLastVerifiedBatch(); } } // Check final batch if (finalNewBatch <= currentLastVerifiedBatch) { revert FinalNumBatchBelowLastVerifiedBatch(); } // Get snark bytes bytes memory snarkHashBytes = _getInputSnarkBytes( rollup, initNumBatch, finalNewBatch, newLocalExitRoot, oldStateRoot, newStateRoot ); // Calulate the snark input uint256 inputSnark = uint256(sha256(snarkHashBytes)) % _RFIELD; // Verify proof if (!rollup.verifier.verifyProof(proof, [inputSnark])) { revert InvalidProof(); // 这里报错 } // Pay POL rewards uint64 newVerifiedBatches = finalNewBatch - currentLastVerifiedBatch; pol.safeTransfer( beneficiary, calculateRewardPerBatch() * newVerifiedBatches ); // Update aggregation parameters totalVerifiedBatches += newVerifiedBatches; lastAggregationTimestamp = uint64(block.timestamp); // Callback to the rollup address rollup.rollupContract.onVerifyBatches( finalNewBatch, newStateRoot, msg.sender ); } contracts/verifiers/FflonkVerifier_12.sol function verifyProof( bytes32[24] calldata proof, uint256[1] calldata pubSignals ) public view returns (bool) { ..... 合约验证电路，细节不用展开了 对于电路，当前合约中包含了10 ，11，12 三种电路，排除下是否是电路版本不对应 排除合约版本是否为fork12 contracts/PolygonZkEVM.sol constructor( IPolygonZkEVMGlobalExitRoot _globalExitRootManager, IERC20Upgradeable _matic, IVerifierRollup _rollupVerifier, // 构造时传入对应版本FflonkVerifier IPolygonZkEVMBridge _bridgeAddress, uint64 _chainID, uint64 _forkID ) { globalExitRootManager = _globalExitRootManager; matic = _matic; rollupVerifier = _rollupVerifier; bridgeAddress = _bridgeAddress; chainID = _chainID; forkID = _forkID; } deployment/v2/4_createRollup.ts let verifierContract; if (realVerifier === true) { let verifierName = `FflonkVerifier_${forkID}`; const VerifierRollup = await ethers.getContractFactory(verifierName, deployer); verifierContract = await VerifierRollup.deploy(); await verifierContract.waitForDeployment(); } else deployment/v2/create_rollup_parameters.json { "realVerifier": true, ... "forkID": 12, ... } 合约内版本是正确的，fork12 确认prover和电路keys版本 https://hub.docker.com/r/hermeznetwork/zkevm-prover/tags?name=v8.0.0 从官方信息https://github.com/0xPolygon/kurtosis-cdk/blob/60f6e4cdbe0c218d14eb443fb7c8618a06c485b5/CDK_VERSION_MATRIX.MD Fork12 对应的 ZkEVM Prover：v8.0.0-RC14-fork.12 看一下Prover Key https://storage.googleapis.com/zkevm/ 在fork12版本下，只有一个 <Contents> <Key>zkproverc/v8.0.0-rc.9-fork.12.tgz</Key> <Generation>1725622722765425</Generation> <MetaGeneration>1</MetaGeneration> <LastModified>2024-09-06T11:38:42.770Z</LastModified> <ETag>"93c4e4ea1755059d9e1f6325c089472c"</ETag> <Size>45066205192</Size> </Contents> 目前怀疑ZkEVM Proverv8.0.0-RC14-fork.12与 key v8.0.0-rc.9-fork.12.tgz存在不兼容 由于目前只有v8.0.0-rc.9-fork.12.tgz版本的key，所以目前将ZkEVM Prover降级为v8.0.0-RC9-fork.12 docker pull hermeznetwork/zkevm-prover:v8.0.0-RC9-fork.12 降级完测试，问题依旧存在，排除版本不一致问题 继续分析日志 2024-11-18T10:02:05.927Z WARN aggregator/aggregator.go:653 NewLocalExitRoot and NewStateRoot look like a mock values, using values from executor instead: LER: 000000..000000, SR: 959a88..208620 {"pid": 39, "version": "v0.4.0-beta5", "module": "aggregator", "prover": "PROVER-2", "proverId": "7f32fd37-7b07-43de-b0e8-b7a861d5f495", "proverAddr": "172.18.34.135:60064", "recursiveProofId": "b47f3cf8-42d8-43b3-a529-7c37ebd7351a", "batches": "1-368", "finalProofId": "40ac43d6-7a7b-498e-b083-30d1a0656596"} github.com/0xPolygon/cdk/aggregator.(*Aggregator).buildFinalProof /go/src/github.com/0xPolygon/cdk/aggregator/aggregator.go:653 github.com/0xPolygon/cdk/aggregator.(*Aggregator).tryBuildFinalProof /go/src/github.com/0xPolygon/cdk/aggregator/aggregator.go:730 github.com/0xPolygon/cdk/aggregator.(*Aggregator).Channel /go/src/github.com/0xPolygon/cdk/aggregator/aggregator.go:442 github.com/0xPolygon/cdk/aggregator/prover._AggregatorService_Channel_Handler /go/src/github.com/0xPolygon/cdk/aggregator/prover/aggregator_grpc.pb.go:109 google.golang.org/grpc.(*Server).processStreamingRPC /go/pkg/mod/google.golang.org/grpc@v1.64.0/server.go:1673 google.golang.org/grpc.(*Server).handleStream /go/pkg/mod/google.golang.org/grpc@v1.64.0/server.go:1794 google.golang.org/grpc.(*Server).serveStreams.func2.1 /go/pkg/mod/google.golang.org/grpc@v1.64.0/server.go:1029 从mock关键词分析，当前prover配置是否开始mock配置 { "runAggregatorClient": false, "runAggregatorClientMock": true, ... 确实开始了mock配置，导致没有生成真实的证明，修改配置如下 { "runAggregatorClient": true, "runAggregatorClientMock": false, ... 重启后，提示如下错误 20241118_104820_282634 5781eab 010bc40 c12aGenericCHelpers=config/c12a/c12a.chelpers_generic.bin 20241118_104820_282636 5781eab 010bc40 recursive1GenericCHelpers=config/recursive1/recursive1.chelpers_generic.bin 20241118_104820_282638 5781eab 010bc40 recursive2GenericCHelpers=config/recursive2/recursive2.chelpers_generic.bin 20241118_104820_282639 5781eab 010bc40 recursivefGenericCHelpers=config/recursivef/recursivef.chelpers_generic.bin 20241118_104820_282641 5781eab 010bc40 publicsOutput=public.json 20241118_104820_282643 5781eab 010bc40 proofFile=proof.json 20241118_104820_282645 5781eab 010bc40 keccakScriptFile=config/scripts/keccak_script.json 20241118_104820_282647 5781eab 010bc40 sha256ScriptFile=config/scripts/sha256_script.json 20241118_104820_282649 5781eab 010bc40 keccakPolsFile=keccak_pols.json 20241118_104820_282745 5781eab 010bc40 zkError: required file config.zkevmConstPols=config/zkevm/zkevm.const does not exist 20241118_104820_282754 5781eab 010bc40 zkError: required file config.c12aConstPols=config/c12a/c12a.const does not exist 20241118_104820_282759 5781eab 010bc40 zkError: required file config.recursive1ConstPols=config/recursive1/recursive1.const does not exist 20241118_104820_282764 5781eab 010bc40 zkError: required file config.recursive2ConstPols=config/recursive2/recursive2.const does not exist 20241118_104820_282768 5781eab 010bc40 zkError: required file config.recursivefConstPols=config/recursivef/recursivef.const does not exist 20241118_104820_282773 5781eab 010bc40 zkError: required file config.zkevmVerifier=config/zkevm/zkevm.verifier.dat does not exist 20241118_104820_282776 5781eab 010bc40 zkError: required file config.zkevmVerkey=config/zkevm/zkevm.verkey.json does not exist 20241118_104820_282780 5781eab 010bc40 zkError: required file config.c12aVerkey=config/c12a/c12a.verkey.json does not exist 20241118_104820_282784 5781eab 010bc40 zkError: required file config.recursive1Verifier=config/recursive1/recursive1.verifier.dat does not exist 20241118_104820_282788 5781eab 010bc40 zkError: required file config.recursive1Verkey=config/recursive1/recursive1.verkey.json does not exist 20241118_104820_282793 5781eab 010bc40 zkError: required file config.recursive2Verifier=config/recursive2/recursive2.verifier.dat does not exist 20241118_104820_282652 5781eab 010bc40 sha256PolsFile=sha256_connections.json 20241118_104820_282653 5781eab 010bc40 keccakConnectionsFile=keccak_connections.json 20241118_104820_282656 5781eab 010bc40 storageRomFile=config/scripts/storage_sm_rom.json 20241118_104820_282658 5781eab 010bc40 zkevmStarkInfo=config/zkevm/zkevm.starkinfo.json 20241118_104820_282660 5781eab 010bc40 c12aStarkInfo=config/c12a/c12a.starkinfo.json 20241118_104820_282662 5781eab 010bc40 databaseURL=postg... 20241118_104820_282664 5781eab 010bc40 dbNodesTableName=state.nodes 20241118_104820_282796 5781eab 010bc40 zkError: required file config.recursive2Verkey=config/recursive2/recursive2.verkey.json does not exist 20241118_104820_282802 5781eab 010bc40 zkError: required file config.finalVerifier=config/final/final.verifier.dat does not exist 20241118_104820_282805 5781eab 010bc40 zkError: required file config.recursivefVerifier=config/recursivef/recursivef.verifier.dat does not exist 20241118_104820_282810 5781eab 010bc40 zkError: required file config.recursivefVerkey=config/recursivef/recursivef.verkey.json does not exist 20241118_104820_282814 5781eab 010bc40 zkError: required file config.finalStarkZkey=config/final/final.fflonk.zkey does not exist 20241118_104820_282832 5781eab 010bc40 zkError: required file config.zkevmStarkInfo=config/zkevm/zkevm.starkinfo.json does not exist 20241118_104820_282837 5781eab 010bc40 zkError: required file config.c12aStarkInfo=config/c12a/c12a.starkinfo.json does not exist 20241118_104820_282841 5781eab 010bc40 zkError: required file config.recursive1StarkInfo=config/recursive1/recursive1.starkinfo.json does not exist 20241118_104820_282846 5781eab 010bc40 zkError: required file config.recursive2StarkInfo=config/recursive2/recursive2.starkinfo.json does not exist 20241118_104820_282850 5781eab 010bc40 zkError: required file config.recursivefStarkInfo=config/recursivef/recursivef.starkinfo.json does not exist 20241118_104820_282853 5781eab 010bc40 zkError: required file config.zkevmCHelpers=config/zkevm/zkevm.chelpers.bin does not exist 20241118_104820_282857 5781eab 010bc40 zkError: required file config.c12aCHelpers=config/c12a/c12a.chelpers.bin does not exist 20241118_104820_282666 5781eab 010bc40 dbProgramTableName=state.program 20241118_104820_282861 5781eab 010bc40 zkError: required file config.recursive1CHelpers=config/recursive1/recursive1.chelpers.bin does not exist 20241118_104820_282668 5781eab 010bc40 dbMultiWrite=1 20241118_104820_282865 5781eab 010bc40 zkError: required file config.recursive2CHelpers=config/recursive2/recursive2.chelpers.bin does not exist 20241118_104820_282670 5781eab 010bc40 dbMultiWriteSingleQuerySize=20971520 20241118_104820_282869 5781eab 010bc40 zkError: required file config.recursivefCHelpers=config/recursivef/recursivef.chelpers.bin does not exist 20241118_104820_282672 5781eab 010bc40 dbConnectionsPool=1 20241118_104820_282872 5781eab 010bc40 zkError: required file config.zkevmGenericCHelpers=config/zkevm/zkevm.chelpers_generic.bin does not exist 20241118_104820_282674 5781eab 010bc40 dbNumberOfPoolConnections=30 20241118_104820_282876 5781eab 010bc40 zkError: required file config.c12aGenericCHelpers=config/c12a/c12a.chelpers_generic.bin does not exist 20241118_104820_282676 5781eab 010bc40 dbMetrics=1 20241118_104820_282880 5781eab 010bc40 zkError: required file config.recursive1GenericCHelpers=config/recursive1/recursive1.chelpers_generic.bin does not exist 20241118_104820_282677 5781eab 010bc40 dbClearCache=0 20241118_104820_282883 5781eab 010bc40 zkError: required file config.recursive2GenericCHelpers=config/recursive2/recursive2.chelpers_generic.bin does not exist 20241118_104820_282679 5781eab 010bc40 dbGetTree=1 20241118_104820_282886 5781eab 010bc40 zkError: required file config.recursivefGenericCHelpers=config/recursivef/recursivef.chelpers_generic.bin does not exist 20241118_104820_282681 5781eab 010bc40 dbReadOnly=0 20241118_104820_282890 5781eab 010bc40 zkError: required file config.c12aExec=config/c12a/c12a.exec does not exist 20241118_104820_282682 5781eab 010bc40 dbReadRetryCounter=10 20241118_104820_282894 5781eab 010bc40 zkError: required file config.recursive1Exec=config/recursive1/recursive1.exec does not exist 20241118_104820_282684 5781eab 010bc40 dbReadRetryDelay=100000 20241118_104820_282898 5781eab 010bc40 zkError: required file config.recursive2Exec=config/recursive2/recursive2.exec does not exist 20241118_104820_282687 5781eab 010bc40 stateManager=1 20241118_104820_282688 5781eab 010bc40 stateManagerPurge=1 20241118_104820_282690 5781eab 010bc40 cleanerPollingPeriod=600 20241118_104820_282902 5781eab 010bc40 zkError: required file config.recursivefExec=config/recursivef/recursivef.exec does not exist 20241118_104820_282692 5781eab 010bc40 requestsPersistence=3600 20241118_104820_282905 5781eab 010bc40 zkError: main() failed calling config.check() 20241118_104820_282693 5781eab 010bc40 maxExecutorThreads=20 20241118_104820_282696 5781eab 010bc40 maxExecutorReceiveMessageSize=1073741824 20241118_104820_282698 5781eab 010bc40 maxExecutorSendMessageSize=0 20241118_104820_282700 5781eab 010bc40 maxHashDBThreads=8 20241118_104820_282702 5781eab 010bc40 dbMTCacheSize=1024 20241118_104820_282703 5781eab 010bc40 dbProgramCacheSize=1024 20241118_104820_282705 5781eab 010bc40 fullTracerTraceReserveSize=262144 20241118_104820_282717 5781eab 010bc40 --> WHOLE_PROCESS starting... 20241118_104820_283857 5781eab 010bc40 CALL STACK 20241118_104820_283862 5781eab 010bc40 0: call=zkProver(_Z14printCallStackv+0x45) [0x55aedb76ded5] 20241118_104820_283864 5781eab 010bc40 1: call=zkProver(_Z11exitProcessv+0x16) [0x55aedb792dd6] 20241118_104820_283865 5781eab 010bc40 2: call=zkProver(main+0x16f1) [0x55aec780c661] 20241118_104820_283867 5781eab 010bc40 3: call=/lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f7b80d09d90] 20241118_104820_283868 5781eab 010bc40 4: call=/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f7b80d09e40] 20241118_104820_283869 5781eab 010bc40 5: call=zkProver(_start+0x25) [0x55aec785e115] 20241118_104820_284026 5781eab 010bc40 MEMORY INFO MemTotal: 1027509.960938 MB MemFree: 765092.031250 MB MemAvailable: 1012630.906250 MB Buffers: 1117.746094 MB Cached: 251721.140625 MB SwapCached: 0.000000 MB SwapTotal: 0.000000 MB SwapFree: 0.000000 MB VM: 504.105469 MB RSS: 0.006591 MB 20241118_104820_284047 5781eab 010bc40 PROCESS INFO Pid: 1 User time: 0.010000 s Kernel time: 0.010000 s Total time: 0.020000 s Num threads: 1 Virtual mem: 504 MB 从错误分析，当前配置没有正确加载对应目录，增加以下参数 { "outputPath": "runtime/output", "configPath": "/usr/src/app/config" } 结束 prover正确运行，并开始计算证明，且cpu利用率拉满 20241118_110311_263112 06517aa 4b83640 <-- SAVE_PUBLICS_JSON_BATCH_PROOF done: 0.000241 s 20241118_110311_263114 06517aa 4b83640 --> STARK_PROOF_BATCH_PROOF starting... 20241118_110311_263212 06517aa 4b83640 --> STARK_INITIALIZATION starting... 20241118_110311_263248 06517aa 4b83640 <-- STARK_INITIALIZATION done: 0.000035 s 20241118_110311_263249 06517aa 4b83640 --> STARK_STEP_1 starting... 20241118_110311_263251 06517aa 4b83640 --> STARK_STEP_1_LDE_AND_MERKLETREE starting... 20241118_110311_263252 06517aa 4b83640 --> STARK_STEP_1_LDE starting... 20241118_110534_424148 06517aa 4b83640 <-- STARK_STEP_1_LDE done: 143.160871 s 20241118_110534_424181 06517aa 4b83640 --> STARK_STEP_1_MERKLETREE starting... verifyBatchesTrustedAggregator 交易已上链 https://sepolia.etherscan.io/address/0x47ce1498Ae40c634D60314F447816a2184602A7a
polygon cdk Aggregator使用SequencerPrivateKey的时机
在Aggregator中有如下配置，需要配置Sequencer的私钥 [Aggregator.SequencerPrivateKey] Path = "/etc/cdk/sequencer.keystore" Password = "password" 查看链代码 if !cfg.SyncModeOnlyEnabled && cfg.SettlementBackend == AggLayer { aggLayerClient = agglayer.NewAggLayerClient(cfg.AggLayerURL) sequencerPrivateKey, err = cdkcommon.NewKeyFromKeystore(cfg.SequencerPrivateKey) if err != nil { return nil, err } } 以下条件时被设置 cfg.SyncModeOnlyEnabled == false cfg.SettlementBackend == AggLayer 查看SequencerPrivateKey使用逻辑 func (a *Aggregator) settleWithAggLayer( ctx context.Context, proof *state.Proof, inputs ethmanTypes.FinalProofInputs) bool { proofStrNo0x := strings.TrimPrefix(inputs.FinalProof.Proof, "0x") proofBytes := common.Hex2Bytes(proofStrNo0x) tx := agglayer.Tx{ LastVerifiedBatch: cdkTypes.ArgUint64(proof.BatchNumber - 1), NewVerifiedBatch: cdkTypes.ArgUint64(proof.BatchNumberFinal), ZKP: agglayer.ZKP{ NewStateRoot: common.BytesToHash(inputs.NewStateRoot), NewLocalExitRoot: common.BytesToHash(inputs.NewLocalExitRoot), Proof: cdkTypes.ArgBytes(proofBytes), }, RollupID: a.etherman.GetRollupId(), } signedTx, err := tx.Sign(a.sequencerPrivateKey) if err != nil { a.logger.Errorf("failed to sign tx: %v", err) a.handleFailureToAddVerifyBatchToBeMonitored(ctx, proof) return false } switch a.cfg.SettlementBackend { case AggLayer: if success := a.settleWithAggLayer(ctx, proof, inputs); !success { continue } default: if success := a.settleDirect(ctx, proof, inputs); !success { continue } } SettlementBackend 配置选项 agglayer l1 (默认) 总结 当使用agglayer结算时，才需要设置SequencerPrivateKey，所以独立部署时Aggregator不需要配置Aggregator.SequencerPrivateKey
查询DA数据库最新存储的batch高度 select batch_num from data_node.offchain_data ORDER BY batch_num DESC Limit 1; 例如返回 batch_num ----------- 335 再查看最新提交的交易->SequenceBatches时间 https://sepolia.etherscan.io/tx/0x9edf5c7c7a87ca47fd85e3002361d1266028e2f2a08d1bfd4cf1e483f6c19104#eventlog numBatch：335 与数据库中高度一致，则代表DA服务正常
polygon cdk sequence sender 发送 sequenceBatchesValidium的时机
通过RPC curl http://L2RPC访问地址/ \ -X POST \ -H "Content-Type: application/json" \ --data '{"method":"zkevm_virtualBatchNumber","params":[],"id":1,"jsonrpc":"2.0"}' 例如返回 curl http://172.18.39.103:8123/ \ > -X POST \ > -H "Content-Type: application/json" \ > --data '{"method":"zkevm_virtualBatchNumber","params":[],"id":1,"jsonrpc":"2.0"}' {"jsonrpc":"2.0","id":1,"result":"0x52"} 0x52对应10进制为82 https://tool.lu/hexconvert/ 合约逻辑 function sequenceBatchesValidium( ValidiumBatchData[] calldata batches, uint32 l1InfoTreeLeafCount, uint64 maxSequenceTimestamp, bytes32 expectedFinalAccInputHash, address l2Coinbase, bytes calldata dataAvailabilityMessage ) external onlyTrustedSequencer { uint256 batchesNum = batches.length; if (batchesNum == 0) { revert SequenceZeroBatches(); } if (batchesNum > _MAX_VERIFY_BATCHES) { revert ExceedMaxVerifyBatches(); } // Check max sequence timestamp inside of range if ( uint256(maxSequenceTimestamp) > (block.timestamp + TIMESTAMP_RANGE) ) { revert MaxTimestampSequenceInvalid(); } // Update global exit root if there are new deposits bridgeAddress.updateGlobalExitRoot(); // Get global batch variables bytes32 l1InfoRoot = globalExitRootManager.l1InfoRootMap( l1InfoTreeLeafCount ); if (l1InfoRoot == bytes32(0)) { revert L1InfoTreeLeafCountInvalid(); } // Store storage variables in memory, to save gas, because will be overrided multiple times uint64 currentLastForceBatchSequenced = lastForceBatchSequenced; bytes32 currentAccInputHash = lastAccInputHash; // Store in a temporal variable, for avoid access again the storage slot uint64 initLastForceBatchSequenced = currentLastForceBatchSequenced; for (uint256 i = 0; i < batchesNum; i++) { // Load current sequence ValidiumBatchData memory currentBatch = batches[i]; // Check if it's a forced batch if (currentBatch.forcedTimestamp > 0) { currentLastForceBatchSequenced++; // Check forced data matches bytes32 hashedForcedBatchData = keccak256( abi.encodePacked( currentBatch.transactionsHash, currentBatch.forcedGlobalExitRoot, currentBatch.forcedTimestamp, currentBatch.forcedBlockHashL1 ) ); if ( hashedForcedBatchData != forcedBatches[currentLastForceBatchSequenced] ) { revert ForcedDataDoesNotMatch(); } // Calculate next accumulated input hash currentAccInputHash = keccak256( abi.encodePacked( currentAccInputHash, currentBatch.transactionsHash, currentBatch.forcedGlobalExitRoot, currentBatch.forcedTimestamp, l2Coinbase, currentBatch.forcedBlockHashL1 ) ); // Delete forceBatch data since won't be used anymore delete forcedBatches[currentLastForceBatchSequenced]; } else { // Note that forcedGlobalExitRoot and forcedBlockHashL1 remain unused and unchecked in this path // The synchronizer should be aware of that // Calculate next accumulated input hash currentAccInputHash = keccak256( abi.encodePacked( currentAccInputHash, currentBatch.transactionsHash, l1InfoRoot, maxSequenceTimestamp, l2Coinbase, bytes32(0) ) ); } } // Sanity check, should be unreachable if (currentLastForceBatchSequenced > lastForceBatch) { revert ForceBatchesOverflow(); } // Store back the storage variables lastAccInputHash = currentAccInputHash; uint256 nonForcedBatchesSequenced = batchesNum; // Check if there has been forced batches if (currentLastForceBatchSequenced != initLastForceBatchSequenced) { uint64 forcedBatchesSequenced = currentLastForceBatchSequenced - initLastForceBatchSequenced; // substract forced batches nonForcedBatchesSequenced -= forcedBatchesSequenced; // Transfer pol for every forced batch submitted pol.safeTransfer( address(rollupManager), calculatePolPerForceBatch() * (forcedBatchesSequenced) ); // Store new last force batch sequenced lastForceBatchSequenced = currentLastForceBatchSequenced; } // Pay collateral for every non-forced batch submitted if (nonForcedBatchesSequenced != 0) { pol.safeTransferFrom( msg.sender, address(rollupManager), rollupManager.getBatchFee() * nonForcedBatchesSequenced ); // Validate that the data availability protocol accepts the dataAvailabilityMessage // note This is a view function, so there's not much risk even if this contract was vulnerable to reentrant attacks dataAvailabilityProtocol.verifyMessage( expectedFinalAccInputHash, dataAvailabilityMessage ); } uint64 currentBatchSequenced = rollupManager.onSequenceBatches( uint64(batchesNum), currentAccInputHash ); // Check expectedFinalAccInputHash if (currentAccInputHash != expectedFinalAccInputHash) { revert FinalAccInputHashDoesNotMatch(); } emit SequenceBatches(currentBatchSequenced, l1InfoRoot); } 查看举例交易：https://sepolia.etherscan.io/tx/0xdb8636d8853348d4d48df1d602a3ce6fe8c37c6be3c30ea9d83f2568e7562fa1#eventlog 对应事件：SequenceBatches 最新提交batch num: 82 与RPC 查询数据一致
error: latest Synced BlockNumber (7089137) is higher than the latest Proposed block (1008) in the network
问题 cdk 跨链桥启动后，发起一笔跨链复现networkID数据错乱，重启后报错 2024-11-16T14:36:29.512Z INFO cmd/run.go:59 main network id: 0 {"pid": 1, "version": "v0.6.0-RC1"} 2024-11-16T14:36:29.512Z INFO cmd/run.go:64 l2 network id: 0 {"pid": 1, "version": "v0.6.0-RC1"} 2024-11-16T14:36:29.542Z DEBUG cmd/run.go:102 trusted sequencer URL http://172.18.39.103:8123 {"pid": 1, "version": "v0.6.0-RC1"} 2024-11-16T14:36:29.542Z INFO server/server.go:93 gRPC Server is serving at 9090 {"pid": 1, "version": "v0.6.0-RC1"} 2024-11-16T14:36:29.542Z INFO server/server.go:166 Restful Server is serving at 8080 {"pid": 1, "version": "v0.6.0-RC1"} 2024-11-16T14:36:29.548Z INFO synchronizer/synchronizer.go:104 NetworkID: 0, Synchronization started {"pid": 1, "version": "v0.6.0-RC1"} 2024-11-16T14:36:29.549Z DEBUG synchronizer/synchronizer.go:119 NetworkID: 0, initial lastBlockSynced: &{ID:3 BlockNumber:7089137 BlockHash:0xbcb0638af2a8014b561ee72c2f568bda819039ca93f16c7de22896fc1037c1a3 ParentHash:0x919c0185dfa6dbc92fb9e13f535c09130d04d1f980749b952b057ffed68f77b6 NetworkID:0 GlobalExitRoots:[] Deposits:[] Claims:[] Tokens:[] VerifiedBatches:[] ActivateEtrog:[] ReceivedAt:2024-11-16 13:48:00 +0000 UTC} {"pid": 1, "version": "v0.6.0-RC1"} 2024-11-16T14:36:29.549Z DEBUG synchronizer/synchronizer.go:126 NetworkID: 0, syncing... {"pid": 1, "version": "v0.6.0-RC1"} 2024-11-16T14:36:29.550Z INFO synchronizer/synchronizer.go:543 NetworkID: 0, [checkReorg function] Checking Block 7089137 in L1 {"pid": 1, "version": "v0.6.0-RC1"} 2024-11-16T14:36:29.553Z ERROR synchronizer/synchronizer.go:546 networkID: 0, error getting latest block synced from blockchain. Block: 7089137, error: Not found%!(EXTRA string= /src/log/log.go:142 github.com/0xPolygonHermez/zkevm-bridge-service/log.appendStackTraceMaybeArgs() /src/log/log.go:251 github.com/0xPolygonHermez/zkevm-bridge-service/log.Errorf() /src/synchronizer/synchronizer.go:546 github.com/0xPolygonHermez/zkevm-bridge-service/synchronizer.(*ClientSynchronizer).checkReorg() /src/synchronizer/synchronizer.go:239 github.com/0xPolygonHermez/zkevm-bridge-service/synchronizer.(*ClientSynchronizer).syncBlocks() /src/synchronizer/synchronizer.go:128 github.com/0xPolygonHermez/zkevm-bridge-service/synchronizer.(*ClientSynchronizer).Sync() /src/cmd/run.go:245 main.runSynchronizer() ) {"pid": 1, "version": "v0.6.0-RC1"} github.com/0xPolygonHermez/zkevm-bridge-service/synchronizer.(*ClientSynchronizer).checkReorg /src/synchronizer/synchronizer.go:546 github.com/0xPolygonHermez/zkevm-bridge-service/synchronizer.(*ClientSynchronizer).syncBlocks /src/synchronizer/synchronizer.go:239 github.com/0xPolygonHermez/zkevm-bridge-service/synchronizer.(*ClientSynchronizer).Sync /src/synchronizer/synchronizer.go:128 main.runSynchronizer /src/cmd/run.go:245 2024-11-16T14:36:29.553Z ERROR synchronizer/synchronizer.go:241 networkID: 0, error checking reorgs. Retrying... Err: Not found {"pid": 1, "version": "v0.6.0-RC1"} github.com/0xPolygonHermez/zkevm-bridge-service/synchronizer.(*ClientSynchronizer).syncBlocks /src/synchronizer/synchronizer.go:241 github.com/0xPolygonHermez/zkevm-bridge-service/synchronizer.(*ClientSynchronizer).Sync /src/synchronizer/synchronizer.go:128 main.runSynchronizer /src/cmd/run.go:245 2024-11-16T14:36:29.553Z WARN synchronizer/synchronizer.go:129 networkID: 0, error syncing blocks: networkID: 0, error checking reorgs {"pid": 1, "version": "v0.6.0-RC1"} github.com/0xPolygonHermez/zkevm-bridge-service/synchronizer.(*ClientSynchronizer).Sync /src/synchronizer/synchronizer.go:129 main.runSynchronizer /src/cmd/run.go:245 2024-11-16T14:36:29.554Z FATAL synchronizer/synchronizer.go:161 networkID: 0, error: latest Synced BlockNumber (7089137) is higher than the latest Proposed block (1008) in the network {"pid": 1, "version": "v0.6.0-RC1"} github.com/0xPolygonHermez/zkevm-bridge-service/synchronizer.(*ClientSynchronizer).Sync /src/synchronizer/synchronizer.go:161 main.runSynchronizer /src/cmd/run.go:245 2024-11-16T14:37:29.896Z INFO cmd/run.go:26 Starting application {"pid": 1, "version": "v0.6.0-RC1", "gitRevision": "002f436", "gitBranch": "HEAD", "goVersion": "go1.21.13", "built": "Thu, 26 Sep 2024 10:05:53 +0000", "os/arch": "linux/amd64"} 2024-11-16T14:37:29.915Z INFO pgstorage/utils.go:63 successfully ran 0 migrations {"pid": 1, "version": "v0.6.0-RC1"} 2024-11-16T14:37:29.923Z WARN etherman/etherman.go:187 Claim compressor Address not configured {"pid": 1, "version": "v0.6.0-RC1"} github.com/0xPolygonHermez/zkevm-bridge-service/etherman.NewL2Client /src/etherman/etherman.go:187 main.newEthermans /src/cmd/run.go:230 main.start /src/cmd/run.go:52 github.com/urfave/cli/v2.(*Command).Run /go/pkg/mod/github.com/urfave/cli/v2@v2.27.4/command.go:276 github.com/urfave/cli/v2.(*Command).Run /go/pkg/mod/github.com/urfave/cli/v2@v2.27.4/command.go:269 github.com/urfave/cli/v2.(*App).RunContext /go/pkg/mod/github.com/urfave/cli/v2@v2.27.4/app.go:333 github.com/urfave/cli/v2.(*App).Run /go/pkg/mod/github.com/urfave/cli/v2@v2.27.4/app.go:307 main.main /src/cmd/main.go:56 runtime.main /usr/local/go/src/runtime/proc.go:267 func (s *ClientSynchronizer) Sync() error { lastBlockSynced, err := s.storage.GetLastBlock(s.ctx, s.networkID, nil) .... if !s.synced { // Check latest Block header, err := s.etherMan.HeaderByNumber(s.ctx, nil) if err != nil { log.Warnf("networkID: %d, error getting latest block from. Error: %s", s.networkID, err.Error()) continue } lastKnownBlock := header.Number.Uint64() if lastBlockSynced.BlockNumber == lastKnownBlock && !s.synced { log.Infof("NetworkID %d Synced!", s.networkID) waitDuration = s.cfg.SyncInterval.Duration s.synced = true s.chSynced <- s.networkID } if lastBlockSynced.BlockNumber > lastKnownBlock { if s.networkID == 0 { log.Fatalf("networkID: %d, error: latest Synced BlockNumber (%d) is higher than the latest Proposed block (%d) in the network", s.networkID, lastBlockSynced.BlockNumber, lastKnownBlock) } lastBlockSynced = 7089137 lastKnownBlock = 1008 lastBlockSynced为L1数据，lastKnownBlock为L2数据，说明s.etherMan应使用L1 RPC，目前使用了L2RPC导致出错 s.storage.GetLastBlock查询时有networkID区分，所以被对比数据是正确的 s.networkID等于0， s.etherMan使用的L2 RPC, 也就是networkID与etherMan对应关系乱了 func runSynchronizer(ctx context.Context, genBlockNumber uint64, brdigeCtrl *bridgectrl.BridgeController, etherman *etherman.Client, cfg synchronizer.Config, storage db.Storage, zkEVMClient *client.Client, chExitRootEventL2 chan *etherman.GlobalExitRoot, chsExitRootEvent []chan *etherman.GlobalExitRoot, chSynced chan uint, allNetworkIDs []uint) { sy, err := synchronizer.NewSynchronizer(ctx, storage, brdigeCtrl, etherman, zkEVMClient, genBlockNumber, chExitRootEventL2, chsExitRootEvent, chSynced, cfg, allNetworkIDs) if err != nil { log.Fatal(err) } if err := sy.Sync(); err != nil { log.Fatal(err) } } for i, l2EthermanClient := range l2Ethermans { log.Debug("trusted sequencer URL ", c.Etherman.L2URLs[i]) zkEVMClient := client.NewClient(c.Etherman.L2URLs[i]) chExitRootEventL2 := make(chan *etherman.GlobalExitRoot) chSyncedL2 := make(chan uint) chsExitRootEvent = append(chsExitRootEvent, chExitRootEventL2) chsSyncedL2 = append(chsSyncedL2, chSyncedL2) go runSynchronizer(ctx.Context, 0, bridgeController, l2EthermanClient, c.Synchronizer, storage, zkEVMClient, chExitRootEventL2, nil, chSyncedL2, []uint{}) } chSynced := make(chan uint) go runSynchronizer(ctx.Context, c.NetworkConfig.GenBlockNumber, bridgeController, l1Etherman, c.Synchronizer, storage, nil, nil, chsExitRootEvent, chSynced, networkIDs) func newEthermans(c *config.Config) (*etherman.Client, []*etherman.Client, error) { l1Etherman, err := etherman.NewClient(c.Etherman, c.NetworkConfig.PolygonBridgeAddress, c.NetworkConfig.PolygonZkEVMGlobalExitRootAddress, c.NetworkConfig.PolygonRollupManagerAddress) if err != nil { log.Error("L1 etherman error: ", err) return nil, nil, err } if len(c.L2PolygonBridgeAddresses) != len(c.Etherman.L2URLs) { log.Fatal("environment configuration error. zkevm bridge addresses and zkevm node urls mismatch") } var l2Ethermans []*etherman.Client for i, addr := range c.L2PolygonBridgeAddresses { l2Etherman, err := etherman.NewL2Client(c.Etherman.L2URLs[i], addr, c.NetworkConfig.L2ClaimCompressorAddress) if err != nil { log.Error("L2 etherman ", i, c.Etherman.L2URLs[i], ", error: ", err) return l1Etherman, nil, err } l2Ethermans = append(l2Ethermans, l2Etherman) } return l1Etherman, l2Ethermans, nil } 对于l1Etherman networkID直接没有赋值，所以等于0 func NewClient(cfg Config, polygonBridgeAddr, polygonZkEVMGlobalExitRootAddress, polygonRollupManagerAddress common.Address) (*Client, error) { logger := log.WithFields("networkID", 0) // Connect to ethereum node ethClient, err := ethclient.Dial(cfg.L1URL) 对于l2Etherman，经过NewL2Client func NewL2Client(url string, polygonBridgeAddr, claimCompressorAddress common.Address) (*Client, error) { // Connect to ethereum node ethClient, err := ethclient.Dial(url) if err != nil { log.Errorf("error connecting to %s: %+v", url, err) return nil, err } // Create smc clients bridge, err := polygonzkevmbridge.NewPolygonzkevmbridge(polygonBridgeAddr, ethClient) if err != nil { return nil, err } oldpolygonBridge, err := oldpolygonzkevmbridge.NewOldpolygonzkevmbridge(polygonBridgeAddr, ethClient) if err != nil { return nil, err } var claimCompressor *claimcompressor.Claimcompressor if claimCompressorAddress == (common.Address{}) { log.Warn("Claim compressor Address not configured") } else { log.Infof("Grouping claims allowed, claimCompressor=%s", claimCompressorAddress.String()) claimCompressor, err = claimcompressor.NewClaimcompressor(claimCompressorAddress, ethClient) if err != nil { log.Errorf("error creating claimCompressor: %+v", err) return nil, err } } networkID, err := bridge.NetworkID(&bind.CallOpts{Pending: false}) // 从合约读取 if err != nil { return nil, err } scAddresses := []common.Address{polygonBridgeAddr} logger := log.WithFields("networkID", networkID) return &Client{ logger: logger, EtherClient: ethClient, PolygonBridge: bridge, OldPolygonBridge: oldpolygonBridge, SCAddresses: scAddresses, ClaimCompressor: claimCompressor, NetworkID: networkID, }, nil } 对于L2 会使用对应的L2 RPC从二层桥合约读取对应的NetworkID // Solidity: function networkID() view returns(uint32) func (_Polygonzkevmbridge *PolygonzkevmbridgeCaller) NetworkID(opts *bind.CallOpts) (uint32, error) { var out []interface{} err := _Polygonzkevmbridge.contract.Call(opts, &out, "networkID") if err != nil { return *new(uint32), err } out0 := *abi.ConvertType(out[0], new(uint32)).(*uint32) return out0, err } 查询二层networkID，确实是0 目前的问题就是因为二层桥合约错误的设置了networkID，导致逻辑错乱，进一步分析下，二层networkID什么时候设置的，如何导致的错误 经过确认，PolygonZkEVMBridgeV2合约没有初始化 结论 经过确认和重置对比测试，该错误是因为当时部署时RPC不稳定造成的交易丢失，所以部署时尽量选择稳定的RPC以及对应链相对空闲时段部署，并且部署完成后检查一下L2的networkID是否为1
数据产生 synchronizer/synchronizer.go func (s *ClientSynchronizer) syncBlocks(lastBlockSynced *etherman.Block) (*etherman.Block, error) { ... blocks, order, err := s.etherMan.GetRollupInfoByBlockRange(s.ctx, fromBlock, &toBlock) etherMan.readEvents func (etherMan *Client) GetRollupInfoByBlockRange(ctx context.Context, fromBlock uint64, toBlock *uint64) ([]Block, map[common.Hash][]Order, error) { // Filter query query := ethereum.FilterQuery{ FromBlock: new(big.Int).SetUint64(fromBlock), Addresses: etherMan.SCAddresses, Topics: [][]common.Hash{{updateGlobalExitRootSignatureHash, updateL1InfoTreeSignatureHash, depositEventSignatureHash, claimEventSignatureHash, oldClaimEventSignatureHash, newWrappedTokenEventSignatureHash, verifyBatchesTrustedAggregatorSignatureHash, rollupManagerVerifyBatchesSignatureHash}}, } if toBlock != nil { query.ToBlock = new(big.Int).SetUint64(*toBlock) } blocks, blocksOrder, err := etherMan.readEvents(ctx, query) if err != nil { return nil, nil, err } return blocks, blocksOrder, nil } func (etherMan *Client) readEvents(ctx context.Context, query ethereum.FilterQuery) ([]Block, map[common.Hash][]Order, error) { logs, err := etherMan.EtherClient.FilterLogs(ctx, query) if err != nil { return nil, nil, err } var blocks []Block blocksOrder := make(map[common.Hash][]Order) for _, vLog := range logs { err := etherMan.processEvent(ctx, vLog, &blocks, &blocksOrder) func (etherMan *Client) processEvent(ctx context.Context, vLog types.Log, blocks *[]Block, blocksOrder *map[common.Hash][]Order) error { switch vLog.Topics[0] { case updateGlobalExitRootSignatureHash: return etherMan.updateGlobalExitRootEvent(ctx, vLog, blocks, blocksOrder) updateGlobalExitRootSignatureHash = crypto.Keccak256Hash([]byte("UpdateGlobalExitRoot(bytes32,bytes32)")) func (etherMan *Client) updateGlobalExitRootEvent(ctx context.Context, vLog types.Log, blocks *[]Block, blocksOrder *map[common.Hash][]Order) error { etherMan.logger.Debug("UpdateGlobalExitRoot event detected. Processing...") return etherMan.processUpdateGlobalExitRootEvent(ctx, vLog.Topics[1], vLog.Topics[2], vLog, blocks, blocksOrder) } func (etherMan *Client) processUpdateGlobalExitRootEvent(ctx context.Context, mainnetExitRoot, rollupExitRoot common.Hash, vLog types.Log, blocks *[]Block, blocksOrder *map[common.Hash][]Order) error { var gExitRoot GlobalExitRoot gExitRoot.ExitRoots = make([]common.Hash, 0) gExitRoot.ExitRoots = append(gExitRoot.ExitRoots, mainnetExitRoot) gExitRoot.ExitRoots = append(gExitRoot.ExitRoots, rollupExitRoot) gExitRoot.GlobalExitRoot = hash(mainnetExitRoot, rollupExitRoot) gExitRoot.BlockNumber = vLog.BlockNumber if len(*blocks) == 0 || ((*blocks)[len(*blocks)-1].BlockHash != vLog.BlockHash || (*blocks)[len(*blocks)-1].BlockNumber != vLog.BlockNumber) { fullBlock, err := etherMan.EtherClient.HeaderByHash(ctx, vLog.BlockHash) if err != nil { return fmt.Errorf("error getting hashParent. BlockNumber: %d. Error: %v", vLog.BlockNumber, err) } t := time.Unix(int64(fullBlock.Time), 0) block := prepareBlock(vLog, t, fullBlock) block.GlobalExitRoots = append(block.GlobalExitRoots, gExitRoot) *blocks = append(*blocks, block) } else if (*blocks)[len(*blocks)-1].BlockHash == vLog.BlockHash && (*blocks)[len(*blocks)-1].BlockNumber == vLog.BlockNumber { (*blocks)[len(*blocks)-1].GlobalExitRoots = append((*blocks)[len(*blocks)-1].GlobalExitRoots, gExitRoot) } else { etherMan.logger.Error("Error processing UpdateGlobalExitRoot event. BlockHash:", vLog.BlockHash, ". BlockNumber: ", vLog.BlockNumber) return fmt.Errorf("error processing UpdateGlobalExitRoot event") } or := Order{ Name: GlobalExitRootsOrder, Pos: len((*blocks)[len(*blocks)-1].GlobalExitRoots) - 1, } (*blocksOrder)[(*blocks)[len(*blocks)-1].BlockHash] = append((*blocksOrder)[(*blocks)[len(*blocks)-1].BlockHash], or) return nil } GlobalExitRootsOrder EventOrder = "GlobalExitRoot" func (s *ClientSynchronizer) syncBlocks(lastBlockSynced *etherman.Block) (*etherman.Block, error) { ... err = s.processBlockRange(blocks, order) func (s *ClientSynchronizer) processBlockRange(blocks []etherman.Block, order map[common.Hash][]etherman.Order) error { ... for _, element := range order[blocks[i].BlockHash] { switch element.Name { case etherman.GlobalExitRootsOrder: isNewGer = true err = s.processGlobalExitRoot(blocks[i].GlobalExitRoots[element.Pos], blockID, dbTx) func (s *ClientSynchronizer) processGlobalExitRoot(globalExitRoot etherman.GlobalExitRoot, blockID uint64, dbTx pgx.Tx) error { // Store GlobalExitRoot globalExitRoot.BlockID = blockID err := s.storage.AddGlobalExitRoot(s.ctx, &globalExitRoot, dbTx) func (p *PostgresStorage) AddGlobalExitRoot(ctx context.Context, exitRoot *etherman.GlobalExitRoot, dbTx pgx.Tx) error { const addExitRootSQL = "INSERT INTO sync.exit_root (block_id, global_exit_root, exit_roots) VALUES ($1, $2, $3)" e := p.getExecQuerier(dbTx) _, err := e.Exec(ctx, addExitRootSQL, exitRoot.BlockID, exitRoot.GlobalExitRoot, pq.Array([][]byte{exitRoot.ExitRoots[0][:], exitRoot.ExitRoots[1][:]})) return err } 数据消费 var chsExitRootEvent []chan *etherman.GlobalExitRoot var chsSyncedL2 []chan uint for i, l2EthermanClient := range l2Ethermans { log.Debug("trusted sequencer URL ", c.Etherman.L2URLs[i]) zkEVMClient := client.NewClient(c.Etherman.L2URLs[i]) chExitRootEventL2 := make(chan *etherman.GlobalExitRoot) chSyncedL2 := make(chan uint) chsExitRootEvent = append(chsExitRootEvent, chExitRootEventL2) chsSyncedL2 = append(chsSyncedL2, chSyncedL2) go runSynchronizer(ctx.Context, 0, bridgeController, l2EthermanClient, c.Synchronizer, storage, zkEVMClient, chExitRootEventL2, nil, chSyncedL2, []uint{}) } func runSynchronizer(ctx context.Context, genBlockNumber uint64, brdigeCtrl *bridgectrl.BridgeController, etherman *etherman.Client, cfg synchronizer.Config, storage db.Storage, zkEVMClient *client.Client, chExitRootEventL2 chan *etherman.GlobalExitRoot, chsExitRootEvent []chan *etherman.GlobalExitRoot, chSynced chan uint, allNetworkIDs []uint) { sy, err := synchronizer.NewSynchronizer(ctx, storage, brdigeCtrl, etherman, zkEVMClient, genBlockNumber, chExitRootEventL2, chsExitRootEvent, chSynced, cfg, allNetworkIDs) func (s *ClientSynchronizer) processBlockRange(blocks []etherman.Block, order map[common.Hash][]etherman.Order) error { var isNewGer bool for i := range blocks { ... for _, element := range order[blocks[i].BlockHash] { switch element.Name { case etherman.GlobalExitRootsOrder: isNewGer = true err = s.processGlobalExitRoot(blocks[i].GlobalExitRoots[element.Pos], blockID, dbTx) ... if isNewGer { // Send latest GER stored to claimTxManager ger, err := s.storage.GetLatestL1SyncedExitRoot(s.ctx, nil) if err != nil { log.Errorf("networkID: %d, error getting latest GER stored on database. Error: %v", s.networkID, err) return err } if s.l1RollupExitRoot != ger.ExitRoots[1] { log.Debugf("Updating ger: %+v", ger) s.l1RollupExitRoot = ger.ExitRoots[1] for _, ch := range s.chsExitRootEvent { ch <- ger } } } func (s *ClientSynchronizer) processGlobalExitRoot(globalExitRoot etherman.GlobalExitRoot, blockID uint64, dbTx pgx.Tx) error { // Store GlobalExitRoot globalExitRoot.BlockID = blockID err := s.storage.AddGlobalExitRoot(s.ctx, &globalExitRoot, dbTx) func (p *PostgresStorage) GetLatestL1SyncedExitRoot(ctx context.Context, dbTx pgx.Tx) (*etherman.GlobalExitRoot, error) { var ( ger etherman.GlobalExitRoot exitRoots [][]byte ) const getLatestL1SyncedExitRootSQL = "SELECT block_id, global_exit_root, exit_roots FROM sync.exit_root WHERE block_id > 0 AND network_id = 0 ORDER BY id DESC LIMIT 1" err := p.getExecQuerier(dbTx).QueryRow(ctx, getLatestL1SyncedExitRootSQL).Scan(&ger.BlockID, &ger.GlobalExitRoot, pq.Array(&exitRoots)) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return &ger, gerror.ErrStorageNotFound } return nil, err } ger.ExitRoots = []common.Hash{common.BytesToHash(exitRoots[0]), common.BytesToHash(exitRoots[1])} return &ger, nil } func start(ctx *cli.Context) error { if c.ClaimTxManager.Enabled { for i := 0; i < len(c.Etherman.L2URLs); i++ { ... claimTxManager, err := claimtxman.NewClaimTxManager(ctx, c.ClaimTxManager, chsExitRootEvent[i], chsSyncedL2[i], c.Etherman.L2URLs[i], networkIDs[i+1], c.NetworkConfig.L2PolygonBridgeAddresses[i], bridgeService, storage, rollupID, l2Ethermans[i], nonceCache, auth) func (tm *ClaimTxManager) Start() { ... case ger = <-tm.chExitRootEvent: if tm.synced { log.Debugf("RollupID: %d UpdateDepositsStatus for ger: %s", tm.rollupID, ger.GlobalExitRoot.String()) if tm.cfg.GroupingClaims.Enabled { log.Debugf("rollupID: %d, Ger value updated and ready to be processed...", tm.rollupID) continue } go func() { err := tm.updateDepositsStatus(ger) if err != nil { log.Errorf("rollupID: %d, failed to update deposits status: %v", tm.rollupID, err) } }() func (tm *ClaimTxManager) updateDepositsStatus(ger *etherman.GlobalExitRoot) error { dbTx, err := tm.storage.BeginDBTransaction(tm.ctx) if err != nil { return err } err = tm.processDepositStatus(ger, dbTx) func (tm *ClaimTxManager) processDepositStatus(ger *etherman.GlobalExitRoot, dbTx pgx.Tx) error { if ger.BlockID != 0 { // L2 exit root is updated ... } else { // L1 exit root is updated in the trusted state log.Infof("RollupID: %d, Mainnet exitroot %v is updated", tm.rollupID, ger.ExitRoots[0]) deposits, err := tm.storage.UpdateL1DepositsStatus(tm.ctx, ger.ExitRoots[0][:], tm.l2NetworkID, dbTx) func (p *PostgresStorage) UpdateL1DepositsStatus(ctx context.Context, exitRoot []byte, destinationNetwork uint, dbTx pgx.Tx) ([]*etherman.Deposit, error) { const updateDepositsStatusSQL = `UPDATE sync.deposit SET ready_for_claim = true 合约分析 PolygonZkEVMGlobalExitRoot.sol function updateExitRoot(bytes32 newRoot) external { ... // If it already exists, do not modify the timestamp if (globalExitRootMap[newGlobalExitRoot] == 0) { globalExitRootMap[newGlobalExitRoot] = block.timestamp; emit UpdateGlobalExitRoot( cacheLastMainnetExitRoot, cacheLastRollupExitRoot ); } } PolygonZkEVMGlobalExitRootV2.sol function updateExitRoot(bytes32 newRoot) external { ... emit UpdateL1InfoTree( cacheLastMainnetExitRoot, cacheLastRollupExitRoot ); emit UpdateL1InfoTreeV2( currentL1InfoRoot, uint32(depositCount), lastBlockHash, currentTimestmap ); } } func (etherMan *Client) processEvent(ctx context.Context, vLog types.Log, blocks *[]Block, blocksOrder *map[common.Hash][]Order) error { switch vLog.Topics[0] { ... case updateL1InfoTreeSignatureHash: return etherMan.updateL1InfoTreeEvent(ctx, vLog, blocks, blocksOrder) func (etherMan *Client) updateL1InfoTreeEvent(ctx context.Context, vLog types.Log, blocks *[]Block, blocksOrder *map[common.Hash][]Order) error { etherMan.logger.Debug("UpdateL1InfoTree event detected") globalExitRoot, err := etherMan.PolygonZkEVMGlobalExitRoot.ParseUpdateL1InfoTree(vLog) if err != nil { return err } return etherMan.processUpdateGlobalExitRootEvent(ctx, globalExitRoot.MainnetExitRoot, globalExitRoot.RollupExitRoot, vLog, blocks, blocksOrder) } if isNewGer { // Send latest GER stored to claimTxManager ger, err := s.storage.GetLatestL1SyncedExitRoot(s.ctx, nil) if err != nil { log.Errorf("networkID: %d, error getting latest GER stored on database. Error: %v", s.networkID, err) return err } if s.l1RollupExitRoot != ger.ExitRoots[1] { log.Debugf("Updating ger: %+v", ger) s.l1RollupExitRoot = ger.ExitRoots[1] for _, ch := range s.chsExitRootEvent { ch <- ger } } } func (p *PostgresStorage) GetLatestL1SyncedExitRoot(ctx context.Context, dbTx pgx.Tx) (*etherman.GlobalExitRoot, error) { var ( ger etherman.GlobalExitRoot exitRoots [][]byte ) const getLatestL1SyncedExitRootSQL = "SELECT block_id, global_exit_root, exit_roots FROM sync.exit_root WHERE block_id > 0 AND network_id = 0 ORDER BY id DESC LIMIT 1" err := p.getExecQuerier(dbTx).QueryRow(ctx, getLatestL1SyncedExitRootSQL).Scan(&ger.BlockID, &ger.GlobalExitRoot, pq.Array(&exitRoots)) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return &ger, gerror.ErrStorageNotFound } return nil, err } ger.ExitRoots = []common.Hash{common.BytesToHash(exitRoots[0]), common.BytesToHash(exitRoots[1])} return &ger, nil } // TODO 继续分析 总和分析 PolygonZkEVMGlobalExitRoot合约升级到V2后，UpdateGlobalExitRoot事件由UpdateL1InfoTree代替，但是桥合约内部是统一处理的，不存在兼容性 数据库查看对应数据SELECT block_id, global_exit_root, exit_roots FROM sync.exit_root; block_id | global_exit_root| exit_roots 1 | \x304fb89608cfb05e5eac3b9e7c70df5821e03b6f8166baf4192f4d921c44f651 | {"\\x76ab2578418da24f3754ecfdef1de5ff8631ca577f998799287eee91b4aace2c","\\x0000000000000000000000000000000000000000000000000000000000000000"} 2 | \x8c9a2fea84f58dc994dac422ab7ee94238643f976de700a25ec3c5701b5acf32 | {"\\xcc7402af30b35a4ee3d5b793cfed1678ae8529e7ffa79e75ae2e4baaa890df21","\\x0000000000000000000000000000000000000000000000000000000000000000"} 0 | \x8c9a2fea84f58dc994dac422ab7ee94238643f976de700a25ec3c5701b5acf32 | {"\\xcc7402af30b35a4ee3d5b793cfed1678ae8529e7ffa79e75ae2e4baaa890df21","\\x0000000000000000000000000000000000000000000000000000000000000000"}
问题 因RPC限速导致sequence-sender异常退出 2024-11-15T11:49:59.657Z ERROR sync/evmdownloader.go:248 error calling FilterLogs to eth client: 429 Too Many Requests: {"id":9,"jsonrpc":"2.0","error":{"code":-32005,"message":"rate limit exceeded"}}{"pid": 40, "version": "v0.4.0-beta5", "syncer": "l1infotreesync"} github.com/0xPolygon/cdk/sync.(*EVMDownloaderImplementation).GetLogs /go/src/github.com/0xPolygon/cdk/sync/evmdownloader.go:248 github.com/0xPolygon/cdk/sync.(*EVMDownloaderImplementation).GetEventsByBlockRange /go/src/github.com/0xPolygon/cdk/sync/evmdownloader.go:184 github.com/0xPolygon/cdk/sync.(*EVMDownloader).Download /go/src/github.com/0xPolygon/cdk/sync/evmdownloader.go:97 2024/11/15 11:49:59 getLogs failed too many times (0) 相关代码 type RetryHandler struct { RetryAfterErrorPeriod time.Duration MaxRetryAttemptsAfterError int } func (h *RetryHandler) Handle(funcName string, attempts int) { if h.MaxRetryAttemptsAfterError > -1 && attempts >= h.MaxRetryAttemptsAfterError { log.Fatalf( "%s failed too many times (%d)", funcName, h.MaxRetryAttemptsAfterError, ) } time.Sleep(h.RetryAfterErrorPeriod) } 解决 在sequence-sender 配置->L1InfoTreeSync下增加两个配置 MaxRetryAttemptsAfterError 总共重试多少次后，异常退出，可以设置一个较大值 RetryAfterErrorPeriod：每次重试的间隔时间