diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2e6b9ee5d..880e559ba 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -45,7 +45,10 @@ jobs: with: repository: taikoxyz/taiko-mono path: ${{ env.TAIKO_MONO_DIR }} +<<<<<<< HEAD ref: alpha_4_test_fixes +======= +>>>>>>> main - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 diff --git a/bindings/.githead b/bindings/.githead index 22806a990..49ab85096 100644 --- a/bindings/.githead +++ b/bindings/.githead @@ -1 +1 @@ -00c23d1ece9f23b600d4cfe77efab90285741cc2 +c133bdd2bee6192bab2531f8463100c778c71820 diff --git a/cmd/flags/prover.go b/cmd/flags/prover.go index 50ae3a731..eeda1e286 100644 --- a/cmd/flags/prover.go +++ b/cmd/flags/prover.go @@ -69,12 +69,6 @@ var ( Category: proverCategory, Value: "", } - ExpectedReward = &cli.Uint64Flag{ - Name: "expectedReward", - Usage: "The expected prover reward for each block", - Category: proverCategory, - Value: 100_000_000, - } TaikoProverPoolL1Address = &cli.StringFlag{ Name: "taikoProverPoolL1", Usage: "TaikoProverPoolL1 contract address", @@ -98,6 +92,5 @@ var ProverFlags = MergeFlags(CommonFlags, []cli.Flag{ OracleProver, OracleProverPrivateKey, Graffiti, - ExpectedReward, TaikoProverPoolL1Address, }) diff --git a/proposer/proposer.go b/proposer/proposer.go index f82082c97..8966ffea1 100644 --- a/proposer/proposer.go +++ b/proposer/proposer.go @@ -190,10 +190,6 @@ func (p *Proposer) ProposeOp(ctx context.Context) error { log.Info("Comparing proposer TKO balance to block fee", "proposer", p.l1ProposerAddress.Hex()) - if err := p.checkTaikoTokenBalance(); err != nil { - return fmt.Errorf("failed to check Taiko token balance: %w", err) - } - // Wait until L2 execution engine is synced at first. if err := p.rpc.WaitTillL2ExecutionEngineSynced(ctx); err != nil { return fmt.Errorf("failed to wait until L2 execution engine synced: %w", err) @@ -400,9 +396,9 @@ func getTxOpts( return opts, nil } -// checkTaikoTokenBalance ensures you have at least the block fee in your balance, and approved, before -// attempting to propose block, as it will use transferFrom. -func (p *Proposer) checkTaikoTokenBalance() error { +// CheckTaikoTokenBalance checks if the current proposer has enough balance to pay +// the current block fee. +func (p *Proposer) CheckTaikoTokenBalance() error { fee, err := p.rpc.TaikoL1.GetBlockFee(nil, p.protocolConfigs.BlockMaxGasLimit) if err != nil { return fmt.Errorf("failed to get block fee: %w", err) diff --git a/prover/config.go b/prover/config.go index c0f33a098..273e861c6 100644 --- a/prover/config.go +++ b/prover/config.go @@ -33,7 +33,6 @@ type Config struct { Graffiti string RandomDummyProofDelayLowerBound *time.Duration RandomDummyProofDelayUpperBound *time.Duration - ExpectedReward uint64 BackOffMaxRetrys uint64 BackOffRetryInterval time.Duration } @@ -116,7 +115,6 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) { Graffiti: c.String(flags.Graffiti.Name), RandomDummyProofDelayLowerBound: randomDummyProofDelayLowerBound, RandomDummyProofDelayUpperBound: randomDummyProofDelayUpperBound, - ExpectedReward: c.Uint64(flags.ExpectedReward.Name), BackOffMaxRetrys: c.Uint64(flags.BackOffMaxRetrys.Name), BackOffRetryInterval: time.Duration(c.Uint64(flags.BackOffRetryInterval.Name)) * time.Second, }, nil diff --git a/prover/proof_producer/dummy_producer.go b/prover/proof_producer/dummy_producer.go index 5c2125a42..50235e98c 100644 --- a/prover/proof_producer/dummy_producer.go +++ b/prover/proof_producer/dummy_producer.go @@ -1,6 +1,7 @@ package producer import ( + "bytes" "context" "math/big" "math/rand" @@ -39,16 +40,9 @@ func (d *DummyProofProducer) RequestProof( BlockID: blockID, Meta: meta, Header: header, - // proof must be 32 bytes min since protocol uses bytes utils and reads 32 bytes, so even - // make proofs must be 32 bytes - ZkProof: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, - }, - Degree: CircuitsDegree10Txs, - Opts: opts, + ZkProof: bytes.Repeat([]byte{0xff}, 100), + Degree: CircuitsIdx, + Opts: opts, } }) diff --git a/prover/proof_producer/proof_producer.go b/prover/proof_producer/proof_producer.go index d5022129a..eadea1997 100644 --- a/prover/proof_producer/proof_producer.go +++ b/prover/proof_producer/proof_producer.go @@ -2,7 +2,6 @@ package producer import ( "context" - "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -11,11 +10,7 @@ import ( ) const ( - CircuitsDegree10Txs = 19 - CircuitsDegree80Txs = 21 - - CircuitsIdx10Txs = 0 - CircuitsIdx80Txs = 1 + CircuitsIdx = 0 // Currently we only have one verification contract in protocol. ) // ProofRequestOptions contains all options that need to be passed to zkEVM rpcd service. @@ -57,12 +52,5 @@ type ProofProducer interface { } func DegreeToCircuitsIdx(degree uint64) (uint16, error) { - switch degree { - case CircuitsDegree10Txs: - return CircuitsIdx10Txs, nil - case CircuitsDegree80Txs: - return CircuitsIdx80Txs, nil - default: - return 0, fmt.Errorf("invalid degree: %d", degree) - } + return CircuitsIdx, nil } diff --git a/prover/proof_producer/zkevm_cmd_producer.go b/prover/proof_producer/zkevm_cmd_producer.go index dc8348aee..a49b08603 100644 --- a/prover/proof_producer/zkevm_cmd_producer.go +++ b/prover/proof_producer/zkevm_cmd_producer.go @@ -71,7 +71,7 @@ func (p *ZkevmCmdProducer) RequestProof( Header: header, Meta: meta, ZkProof: proof, - Degree: CircuitsDegree10Txs, + Degree: CircuitsIdx, Opts: opts, } diff --git a/prover/proof_producer/zkevm_rpcd_producer.go b/prover/proof_producer/zkevm_rpcd_producer.go index 382d41fbe..ecb695250 100644 --- a/prover/proof_producer/zkevm_rpcd_producer.go +++ b/prover/proof_producer/zkevm_rpcd_producer.go @@ -45,28 +45,34 @@ type RequestProofBody struct { // RequestProofBody represents the JSON body of RequestProofBody's `param` field. type RequestProofBodyParam struct { - Circuit string `json:"circuit"` - Block *big.Int `json:"block"` - L2RPC string `json:"l2_rpc"` - Retry bool `json:"retry"` - Param string `json:"param"` - VerifyProof bool `json:"verify_proof"` - Mock bool `json:"mock"` - Aggregate bool `json:"aggregate"` - Prover string `json:"prover"` - L1SignalService string `json:"l1_signal_service"` - L2SignalService string `json:"l2_signal_service"` - TaikoL2 string `json:"l2_contract"` - MetaHash string `json:"meta_hash"` - BlockHash string `json:"block_hash"` - ParentHash string `json:"parent_hash"` - SignalRoot string `json:"signal_root"` - Graffiti string `json:"graffiti"` - GasUsed uint64 `json:"gas_used"` - ParentGasUsed uint64 `json:"parent_gas_used"` - BlockMaxGasLimit uint64 `json:"block_max_gas_limit"` - MaxTransactionsPerBlock uint64 `json:"max_transactions_per_block"` - MaxBytesPerTxList uint64 `json:"max_bytes_per_tx_list"` + Circuit string `json:"circuit"` + Block *big.Int `json:"block"` + L2RPC string `json:"rpc"` + Retry bool `json:"retry"` + Param string `json:"param"` + VerifyProof bool `json:"verify_proof"` + Mock bool `json:"mock"` + MockFeedback bool `json:"mock_feedback"` + Aggregate bool `json:"aggregate"` + ProtocolInstance *ProtocolInstance `json:"protocol_instance"` +} + +// RequestProofBody represents the JSON body of RequestProofBody.Param's `protocol_instance` field. +type ProtocolInstance struct { + L1SignalService string `json:"l1_signal_service"` + L2SignalService string `json:"l2_signal_service"` + TaikoL2 string `json:"l2_contract"` + MetaHash string `json:"meta_hash"` + BlockHash string `json:"block_hash"` + ParentHash string `json:"parent_hash"` + SignalRoot string `json:"signal_root"` + Graffiti string `json:"graffiti"` + Prover string `json:"prover"` + GasUsed uint64 `json:"gas_used"` + ParentGasUsed uint64 `json:"parent_gas_used"` + BlockMaxGasLimit uint64 `json:"block_max_gas_limit"` + MaxTransactionsPerBlock uint64 `json:"max_transactions_per_block"` + MaxBytesPerTxList uint64 `json:"max_bytes_per_tx_list"` } // RequestProofBodyResponse represents the JSON body of the response of the proof requests. @@ -87,6 +93,11 @@ type RpcdOutput struct { Proof string `json:"proof"` Degree uint64 `json:"k"` } `json:"circuit"` + Aggregation struct { + Instances []string `json:"instance"` + Proof string `json:"proof"` + Degree uint64 `json:"k"` + } `json:"aggregation"` } // NewZkevmRpcdProducer creates a new `ZkevmRpcdProducer` instance. @@ -172,8 +183,17 @@ func (p *ZkevmRpcdProducer) callProverDaemon(ctx context.Context, opts *ProofReq log.Info("Proof generating", "height", opts.Height, "time", time.Since(start)) return errProofGenerating } - proof = common.Hex2Bytes(output.Circuit.Proof[2:]) - degree = output.Circuit.Degree + + log.Debug("Proof generation output", "output", output) + + var proofOutput string + for _, instance := range output.Aggregation.Instances { + proofOutput += instance[2:] + } + proofOutput += output.Aggregation.Proof[2:] + + proof = common.Hex2Bytes(proofOutput) + degree = output.Aggregation.Degree log.Info("Proof generated", "height", opts.Height, "degree", degree, "time", time.Since(start)) return nil }, backoff.NewConstantBackOff(proofPollingInterval)); err != nil { @@ -189,28 +209,31 @@ func (p *ZkevmRpcdProducer) requestProof(opts *ProofRequestOptions) (*RpcdOutput ID: common.Big1, Method: "proof", Params: []*RequestProofBodyParam{{ - Circuit: "pi", - Block: opts.Height, - L2RPC: p.L2Endpoint, - Retry: true, - Param: p.Param, - VerifyProof: true, - Mock: false, - Aggregate: false, - Prover: opts.ProverAddress.Hex()[2:], - L1SignalService: opts.L1SignalService.Hex()[2:], - L2SignalService: opts.L2SignalService.Hex()[2:], - TaikoL2: opts.TaikoL2.Hex()[2:], - MetaHash: opts.MetaHash.Hex()[2:], - BlockHash: opts.BlockHash.Hex()[2:], - ParentHash: opts.ParentHash.Hex()[2:], - SignalRoot: opts.SignalRoot.Hex()[2:], - Graffiti: opts.Graffiti, - GasUsed: opts.GasUsed, - ParentGasUsed: opts.ParentGasUsed, - BlockMaxGasLimit: uint64(p.ProtocolConfig.BlockMaxGasLimit), - MaxTransactionsPerBlock: p.ProtocolConfig.BlockMaxTransactions, - MaxBytesPerTxList: p.ProtocolConfig.BlockMaxTxListBytes, + Circuit: "super", + Block: opts.Height, + L2RPC: p.L2Endpoint, + Retry: true, + Param: p.Param, + VerifyProof: true, + Mock: false, + MockFeedback: false, + Aggregate: true, + ProtocolInstance: &ProtocolInstance{ + Prover: opts.ProverAddress.Hex()[2:], + L1SignalService: opts.L1SignalService.Hex()[2:], + L2SignalService: opts.L2SignalService.Hex()[2:], + TaikoL2: opts.TaikoL2.Hex()[2:], + MetaHash: opts.MetaHash.Hex()[2:], + BlockHash: opts.BlockHash.Hex()[2:], + ParentHash: opts.ParentHash.Hex()[2:], + SignalRoot: opts.SignalRoot.Hex()[2:], + Graffiti: opts.Graffiti, + GasUsed: opts.GasUsed, + ParentGasUsed: opts.ParentGasUsed, + BlockMaxGasLimit: uint64(p.ProtocolConfig.BlockMaxGasLimit), + MaxTransactionsPerBlock: p.ProtocolConfig.BlockMaxTransactions, + MaxBytesPerTxList: p.ProtocolConfig.BlockMaxTxListBytes, + }, }}, } diff --git a/prover/proof_producer/zkevm_rpcd_producer_test.go b/prover/proof_producer/zkevm_rpcd_producer_test.go index f97dee970..2650d8e60 100644 --- a/prover/proof_producer/zkevm_rpcd_producer_test.go +++ b/prover/proof_producer/zkevm_rpcd_producer_test.go @@ -23,7 +23,7 @@ func TestNewZkevmRpcdProducer(t *testing.T) { require.Nil(t, err) dummyZkevmRpcdProducer.CustomProofHook = func() ([]byte, uint64, error) { - return []byte{0}, CircuitsDegree10Txs, nil + return []byte{0}, CircuitsIdx, nil } resCh := make(chan *ProofWithHeader, 1) diff --git a/prover/proof_submitter/util.go b/prover/proof_submitter/util.go index 77eaf5c5d..c2fcdcdcc 100644 --- a/prover/proof_submitter/util.go +++ b/prover/proof_submitter/util.go @@ -67,7 +67,6 @@ func sendTxWithBackoff( cli *rpc.Client, blockID *big.Int, proposedAt uint64, - expectedReward uint64, meta *bindings.TaikoDataBlockMetadata, sendTxFunc func() (*types.Transaction, error), retryInterval time.Duration, @@ -104,23 +103,20 @@ func sendTxWithBackoff( return nil } - // Check the expected reward. - if expectedReward != 0 { - // Check if this proof is still needed at first. - needNewProof, err := rpc.NeedNewProof(ctx, cli, blockID, common.Address{}) - if err != nil { - log.Warn( - "Failed to check if the generated proof is needed", - "blockID", blockID, - "error", err, - ) - return err - } + // Check if this proof is still needed at first. + needNewProof, err := rpc.NeedNewProof(ctx, cli, blockID, common.Address{}) + if err != nil { + log.Warn( + "Failed to check if the generated proof is needed", + "blockID", blockID, + "error", err, + ) + return err + } - if !needNewProof { - log.Info("Proof was submitted another prover, skip the current proof submission", "blockID", blockID) - return nil - } + if !needNewProof { + log.Info("Proof was submitted another prover, skip the current proof submission", "blockID", blockID) + return nil } tx, err := sendTxFunc() diff --git a/prover/proof_submitter/util_test.go b/prover/proof_submitter/util_test.go index 4c1c20258..e57f0039c 100644 --- a/prover/proof_submitter/util_test.go +++ b/prover/proof_submitter/util_test.go @@ -37,7 +37,6 @@ func (s *ProofSubmitterTestSuite) TestSendTxWithBackoff() { s.RpcClient, common.Big1, 0, - 0, meta, func() (*types.Transaction, error) { return nil, errors.New("L1_TEST") }, 12*time.Second, @@ -48,7 +47,6 @@ func (s *ProofSubmitterTestSuite) TestSendTxWithBackoff() { s.RpcClient, common.Big1, 0, - 0, meta, func() (*types.Transaction, error) { height, err := s.RpcClient.L1.BlockNumber(context.Background()) diff --git a/prover/proof_submitter/valid_proof_submitter.go b/prover/proof_submitter/valid_proof_submitter.go index 736f47c05..eb7b699a2 100644 --- a/prover/proof_submitter/valid_proof_submitter.go +++ b/prover/proof_submitter/valid_proof_submitter.go @@ -38,7 +38,6 @@ type ValidProofSubmitter struct { mutex *sync.Mutex isOracleProver bool graffiti [32]byte - expectedReward uint64 retryInterval time.Duration } @@ -52,7 +51,6 @@ func NewValidProofSubmitter( mutex *sync.Mutex, isOracleProver bool, graffiti string, - expectedReward uint64, retryInterval time.Duration, ) (*ValidProofSubmitter, error) { anchorValidator, err := anchorTxValidator.New(taikoL2Address, rpcClient.L2ChainID, rpcClient) @@ -70,11 +68,6 @@ func NewValidProofSubmitter( return nil, err } - // OracleProver does not care about the expected proof reward. - if isOracleProver { - expectedReward = 0 - } - return &ValidProofSubmitter{ rpc: rpcClient, proofProducer: proofProducer, @@ -88,7 +81,6 @@ func NewValidProofSubmitter( mutex: mutex, isOracleProver: isOracleProver, graffiti: rpc.StringToBytes32(graffiti), - expectedReward: expectedReward, retryInterval: retryInterval, }, nil } @@ -257,7 +249,6 @@ func (s *ValidProofSubmitter) SubmitProof( s.rpc, blockID, block.Header().Time, - s.expectedReward, proofWithHeader.Meta, sendTx, s.retryInterval, diff --git a/prover/proof_submitter/valid_proof_submitter_test.go b/prover/proof_submitter/valid_proof_submitter_test.go index 4e9944063..be25d864f 100644 --- a/prover/proof_submitter/valid_proof_submitter_test.go +++ b/prover/proof_submitter/valid_proof_submitter_test.go @@ -1,6 +1,7 @@ package submitter import ( + "bytes" "context" "os" "sync" @@ -103,7 +104,7 @@ func (s *ProofSubmitterTestSuite) TestValidProofSubmitterSubmitProofMetadataNotF BlockID: common.Big256, Meta: &bindings.TaikoDataBlockMetadata{}, Header: &types.Header{}, - ZkProof: []byte{0xff}, + ZkProof: bytes.Repeat([]byte{0xff}, 100), }, ), ) diff --git a/prover/prover.go b/prover/prover.go index 652ebd1c7..43a290bb9 100644 --- a/prover/prover.go +++ b/prover/prover.go @@ -202,7 +202,6 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { p.submitProofTxMutex, p.cfg.OracleProver, p.cfg.Graffiti, - p.cfg.ExpectedReward, p.cfg.BackOffRetryInterval, ); err != nil { return err @@ -508,7 +507,15 @@ func (p *Prover) submitProofOp(ctx context.Context, proofWithHeader *proofProduc }() if err := backoff.Retry( - func() error { return p.validProofSubmitter.SubmitProof(p.ctx, proofWithHeader) }, + func() error { + err := p.validProofSubmitter.SubmitProof(p.ctx, proofWithHeader) + if err != nil { + log.Error("Submit proof error", "error", err) + return err + } + + return nil + }, backoff.WithMaxRetries(backoff.NewConstantBackOff(p.cfg.BackOffRetryInterval), p.cfg.BackOffMaxRetrys), ); err != nil { log.Error("Submit proof error", "error", err) diff --git a/testutils/suite.go b/testutils/suite.go index 91b65fdb1..204307d8a 100644 --- a/testutils/suite.go +++ b/testutils/suite.go @@ -65,7 +65,6 @@ func (s *ClientTestSuite) SetupTest() { RetryInterval: backoff.DefaultMaxInterval, }) s.Nil(err) - s.RpcClient = rpcCli // set allowance diff --git a/testutils/testdata/zkchain_proof.json b/testutils/testdata/zkchain_proof.json index 45394532a..f0ed728d7 100644 --- a/testutils/testdata/zkchain_proof.json +++ b/testutils/testdata/zkchain_proof.json @@ -3,25 +3,30 @@ "id": 1, "result": { "aggregation": { - "duration": 0, - "instance": [], - "k": 0, - "label": "pi-300000-a", - "proof": "0x", - "randomness": "0x" - }, - "circuit": { "duration": 276176, "instance": [ - "0x101", - "0x138441ed4bea3492374e1caa6f1dcc8a947ef58ec290d16cf8092905be8fc0bd", - "0xc3b", - "0x208afa142625879c94271d82cb04b3cde0c93641ad9f92ef6a593b0f8c8443f8", - "0x1b23eb43f1d78910deeead51dee8039294565db225c7cfc708a88ddc4a9f91f3" + "0x00000000000000000000000000000000ddaaede96e2ecd386556d4d3a708772c", + "0x0000000000000000000000000000000014259cc4685da43ecb73b241f0c63e54", + "0x000000000000000000000000000000000000000000000007eed1cf8ff77aef74", + "0x00000000000000000000000000000000000000000000000fa773ef9ec7ce0726", + "0x00000000000000000000000000000000000000000000000b1f8d7a4b882df364", + "0x00000000000000000000000000000000000000000000000000006fee4348abf4", + "0x00000000000000000000000000000000000000000000000cea83d6f1c53916fc", + "0x000000000000000000000000000000000000000000000006085eded6b36aff9c", + "0x0000000000000000000000000000000000000000000000049d3fa0c3eee77969", + "0x0000000000000000000000000000000000000000000000000000dfea17975271", + "0x00000000000000000000000000000000000000000000000656a4f6132836f967", + "0x00000000000000000000000000000000000000000000000b2f687f67cc2a0f3a", + "0x000000000000000000000000000000000000000000000009c4256b4b8d3d5b56", + "0x0000000000000000000000000000000000000000000000000000e3bfe610b631", + "0x00000000000000000000000000000000000000000000000279a6c68bd46b5242", + "0x000000000000000000000000000000000000000000000001ce586bca9845679d", + "0x00000000000000000000000000000000000000000000000e612c9ba637b98007", + "0x0000000000000000000000000000000000000000000000000001bae7f495655e" ], "k": 20, "label": "pi-300000", - "proof": "0x278ded153590c76a42003e1a3e7bbfb0f19211bc2377a0902637dcc83b694d910028e4d5d41058312208d45a7f7ca736bb59171d5eca4bb3aa3180790dd07a99245bf820ba7460dcd2bb942e0e150305b173933c0cdb7219ee6273ea6abb87402c143c17c397feb2e0e8e0714da11965797e1308661f69932cd4451dbfa74085167114e1f18978090ba1077a2172c0e4d3ed5fabd570a8c4a5611a29eb55dfab1c5de7c6efdda8d5fadd48e28d68130051ad2858ae8931df88accf56692cbc2b0022336d94672043021050ecec9d3474745fae186e7536edec1d1e373ecc1c220a1a51374eee268110bac9595857c515352b7df0027fdd85739573edae695b34017f549a762859e1199cd0f6d6acd74653925730b4b9fe8931482e9a5a37eb95227c97bc8855c227fc9f4ae02a8cf9408bdc7b203a1c5bb36280418efa2578782d58b31f1c28940af0e3db232ecaf31f03571d45340205150022902602974c5320c7928eca8fd502177d2c4992ce39a9e3ab492f17beb21a89cadad4ff6b5f0b226fcd236b07f691d0776e8381f1eb38827e5219faaf4864b4c2bfa69aa0e45f18021769bb2a9caea749e047f504f694b6e1ebcfe1a387169edd88b867081c222b0533672c8937bab235f7b65744e043c8f1f090a60ecdee957f5765b566e94f286ceb1f7f2a9aa16c991aa6f2c0a09b051a028eeae2021779fe5da69ecd8b2e058d1fc940c49782b62907c7560e5a09c52709c801ae7b0d6f533c2c75467ebe0b14b34dca9f270cc2a84803c81c3b40bf52c9b33adee667c37e14d9066bb1b3073ee19f77b0c7659d07099116727bca7f6b5efb244be08a0c921fc0356a2baf04430bbc517927c8023e74ba61e9b1bbc2cd7bfc4b7b3f1e3410de000c63af1d1c683e5a60cbf83d5f482549d86bfe1a495ea58d35ed0e762ea4f15139fbe0681d1e7236df3df504d3c747c1506c534ce4f6c3f8a4a7fc63889188535cdafc58205800af17d1f5cabfbaad0dc8fe89081f81fab0653bd92da481fdac6a9e963d19fbf5fc74c7765bd5e18ed97ab32ae4823e29704d475b6b2ec09d2369351b0c17849f4fb1023dd277ab0d0875dd43f3dc79a0072661db1f4c7264ba48e3f367070150ef1dd85005318398fe00abfa39b51588c2ea6b5e7259559d53d4849b4b042e835e8b6037f6e9d15475106486b63e97e2933fa5e6aaf8ab7829b793b7481078be0955176a719cb025a1b97ef1310c7a219160a261268aee53ba33506f3f1e5bfebab60f5c328cd4c729f3d0ea66dea312640d0bf68acd592ae450e348ac257c9e9602950c60bd09ba656f5b638de3dddfcdcde83e8a0f381feb2d5e5b2e00a5dca9c97a0de1ef0dac1da5a62af8feadbfe16a1990c2d48b4d50e0f4b1af2185968f43b794c7c6b468758f3d18199671ba6f317cb6363bf93698b4982e9a2364493a1d5d7c12ff3237e2367f8d91a7aaa801fa29aa90818e920ae782379e02f1f1325f447b2f21522376c79fdf06db3d9b33b56dacb384c5c3ca121c57c01600dbbace1c93d1f1cc5af97502e8150fcae525d0a1d4f0bb32b2f2b4e72f79277b825dcf72cf2054fff0da188d26b93741421d68e9476e3cf12096e8774cde2d2a91994f0d09fa5cf6a117ff1012607407c851a63899acab8b39c083602f7626cb3f300f3022f61a97030c00f6e563982d77ec75ba2030c835e616328ec76b0b70852696a9d27a444ff30a08482a24c932abf7214ec063708352a4f46dc94e0cc7f0b6a4272f1c93bc84899c4e3befe0c0c060f8a1b9d4dad9cf23cc4e1b171d80bcef820ee9351a5e46ceb56b876592b4ddd22ddcbd4454e6d93316fbf36c16a805c6fc4e9df736bb1b13f9a1de9f08d884c582aab98fa4669cce753f7cdc1fb3eb2b7249497436bf6d288affb1f7e5d53c27feff9ec9c1aff091e4fb0e2918f9cc4fb73f21bd9776fc6afa12ae6ce675adec378a7485760a51b1f60158491f81b25ce32daeb66480f72f855e06b316ac319b0c380a5084ce5de521cef8bf1c2b07cb80d013cd6cb32b1461674f465d31faa4d50010d5b514e7340a6d791e113482d3a3e7f3083cad42853872934717e85554464904bc1ed7aaa1f3cc0bd02662865173cc1af2762d64dfe4b48cd5af094a3093c26b3cd629347377598444272ee87f9031909976a11781cec904d95cfc48c091f53bd8c851493cd98c3d8c2e834df54fb36b68043a38b6b6499a7d8320bcb98a62d7c6cc1531ea0ddc731c0d832801d04ef747776ca6ecbe36bfa241a53d092bff0328ca084a9bd1be349117af1c95f8760916478920125d94c4787cffc6cbcaeb2501339d14ea3548707e123d631817e2e0d3e9ff7d1a2da48dcc915bbdeb5c6b0739b09af427f7f4176a1cc18646f25fbf50a57104e61b9cde7c34f27a8cda14e9807505a7731a4d38c215ee08ba00ef0800068e4fe3cbf102c6dd96fa47b835fce4becc6df6457c79c10eb498c8c6235eaa980fac0d16ce8761913512d16f31e0979d7071ad5e67b2401ec698676985846ef07d778de0791f48de4f767b5f16e0cb3ff5ea528e96ff4f2e56c10333eb2b6ad95eafafe12f72357a5ea1c1ee615ec4754714ea424cbf31070daca28aa1dc0cfc6f41af34e1af1dc0b91e89a2c3c2358f3339ef37c3f7fd053f342fe444395376258352b1d3656ce3e7d72ee91c2ac072ac0ebc1c8189941eb569484b928b4d3f3e2c62d041b64453a5db338a6215ef28c899cc05aea5442a20543e92e1a68df04e69d377fb1029946ef8466e5940569e3ad4e3e0ec9b4b0e445b832584d1996dcf2edd7fb78e3eae8939e7d275d1ce9f418f889e6632bd22d4d7e72ab0365fcdaba96bfd3c26337028ad9f2aa2d8031099082ee01038d608e92de4e55351f6e495332bebe7306f57f8ae0373bae78cb4ae36826cbb6cbe0d2b9e68aa282273f985a7777075d99df9779f8f230f369afb0addb7d74e190502e0efbc5b0024357c50d382eef0cb1e8ed54ec1c5195b9dce9289f782f59c5b1f85db20cca34eedf47341fab2588ddfb6ce224aedb0c53159fae62cf5a5c6b9190788b13ba00ad4591f0fc90663f869f3862ce7c4d5cd90a682b4a646de08e3230516ebcaf786dd64f0a75f3f46f3aa808562b96d387f327716b3c34ea1071209a32e7e08121c9667f962e20da7c806a56e7ce840f413e95001abf82380f947132cf32a3c879b22fc4d88342b77656d6359ce3fbc53985a060eea19f9f0b75029e81c08c70ec9f0205e210567aff85725d8009125d2e97ef057531818db522d0f28d239c5db0e341fff78b014bc65d373c28c2b4672b3cbad39f298a823774f0409e206dadfcd7fe5b191d17d2c9638a846c5cae30faa8423dff7e81ec9f3df054c54e9be6bfaacefcc9f108b96cdd00c1f8a0065865eb0b5e876ccfd33d2242fcc7cb753fa9723eb619a3720125398cbbc9fe7ab3fb4d65107459e5c98de6f14fcb043eaa98b927e4703d17cb77ad3c2539e21e38a67751aa0d92fbfe192e5204739d5bad022327904d3512f1da7218be4595db0c22da093ec24ab89a5d334024c58dde366c314d733f965476ddd41da858aceffed8678d1027cea3b2bbf9e0a634529008e5de56e39bc98990814408447bf8ff10f43f19a782cff4a458af5304e06505f19f38d67a92f8522d7ff945ac5600547a2dad89878acf398c9876804d5a554b9237f673ef093a42910633f9e9171bd1f78d4db762906d434d6891f02d3c33eaacb97defb7699b21e3c430bf2d652891c426bb23ed6d70efc3332201200f8633f10d3c66ec5e846a16d03b711e46b101829c03e7172522b33423c1902a25a4b2e30b90b82942b7a6764c7ac4e8f1708d9b2e5da0d5e6648064e85d504bd16eb87691cf7a58d9480377c28c7c6c2f11d567a7c3c0b630bb24028adf1242e5dd8b4040f1e094c7dd77dc9531df82793a63c1d476c5f85e09fd3b95b4830487cea213f9a10cc3ec8b59dfac5e2be257679b8e3d3b41a4e33a04df6088e0644b1514743a3fdda56623ece87d8aa946389285092e1d0a62f5bae3748f7581e184b34a262ef00f5449c2971d598f733102ee98fd737329f6872ce67d4a27627e1458e9822feed2bedc6c239c7e8972e400774279416d0d7ba27896922690010c7cb7c70dc6bd597f369381fae2c9871c906ea2f1e8226c3310fd1e7546dc527313f06175782fe0eb84c427bd7e455cff99f7b3130b44b22202d0f7667ada8305fa180e77cad4e6ea0342959a8af17f046e879b8a85d618e4908965fa248720bbaa7aba93ae5c15092559ad6e9d4f0db441ef02dd18a5b1703b7e618d7274702b1a3fcc5b19b517b711409f04a9aeb2f945fc5ce0ff2b758e54e767f98ecfc1a20a78327f1edd20dfe36719a2f83c0d521de4c754f2b5e417ba662938487e22263639b666c62b53381ed1476bdaf97c1de9eb80b8095d423f8242d8e86369602d27e61f5828e25b5cc6536869d88073d6a91e2e79c35bfe81bb544e771587f147941e6998d20e9d6f59933318dc302389befdbf071736f9493222791f1b0b3142a72133fbdf083ddbf12fba8085cc0df781d566ddbe3f2b3591a5f7bc17cf7251ee83127c2a0bffe0a172af1d82f159ba0492291feb1a858ee96b680b5151325cdc59d78c5ca19d254bf50c8e40a986b0d5ac6d6a06080eb3f5e8d38901a8c11f060e6f1fbc4a164f027291957162f25c8b51cc34dbf91eca332527eef3c900e6e08809729440418dbff5d132ac6bf952156d5b895a3e35093dbe5a074105f2dce06d937fb94c56a5f62aa72fc6fe5f8582abe02adfd821d98b06f24d3e1781128bcbb0f898e70ed5b0a72ac4bfa608667fd2b418b6fad1a6815488acdd9e017125204fc980c7bd2c5fa5323df5b14c1df537161bc6b87013a429e8a4f2beb2fac45bf68c329f5901bf3bd368278a12c35a3b5384b29a22eb8d31b703d0ba31f89cc4cccdf60242a4c882746c5e57c61581a913fa29ef1df335b5012e3ca100a9f583d52062d15f5301286660947b9e8e30c0e1ffc0955a1af79dbbe9e21e61893bad1e7dbf562af8b1678454d12d01713076543152053ab2ed28ea06d6b2608cd08c69163b960407fa58d64a5fb18c051482aef70a5f66a87c45a39afc69a2bdee0e29de28beaa63a36ab4a3f9f1f5f63690ad74459c1bc9c2ff4ed49a8ce0beff55785fb17442226c29d82da9681419d9c624d22d3aa58992e2342de983719ed90454a06313b70e8017425850d495d04c62d7b65de39caf6594000d8232619a33023cf6c44f0b18a6a1e0c38162d7be034b7ae6a787520d1d41a5c39a0b5222de4652fbee0444e03f79c9474155fd945d9675cbedbdcab8c74727480899a", + "proof": "0x25ea129334f31df21a3153b7bc547537acd96b1ac5bf9306623653da2bef563d304cfbf67cb1869fe1a225a20dd1bfaa2cb51e2afe78b23ef734daa01e0b67c00cac83d36cb241f9b8e9ef3860b62586947e3e6f76d83aff0b11f15a14ba642a26ef1f867efe5f30ab3d71f7a06900f995a1f369a72da8143054cd6c85966da3015bc1f1d19dfcb350d92f70098369c96f0a8fb284c4bc0aa27765e7b3833bb413dc80bc80e6753f745390bc5d9c64d94fc9daef7930f02f6183af82295388f41379ca854b012a922b9ef3ce76452b8ad26b34fcc1ceb37cfc87b6544658b2c82d3f1cf31bf1b15f6bb3cd986b76ab0e9594fc3ad6fcf5e8c7c80726d85c666920352562ed745b8f861eaccc709acf717c2051b3cbd474e2a30e97ac914aedda0997e4c1e38ed39bec2ffa1d4dd10f64e6a6c840329ed8031d802582829c2917190ffee4e76a03f748bc43bbd9aabeef45b5c2b5784c681738569f72be8f35de139c2a7030e55d9631dab075419841283b1faf3319d79f3a46ffd1323a28ca10268439ba1adbb6eec9eb78d93459b04667ce0cad2a03167c681207a2539354470698c698e2fdcaa00d34b1f038bfb8b8ab1a4809b8e942364bd23269cad3833c2b1a94f5db0bdfedc69a368081021bb89b0e2404a4e5301ec126db00b86487e0285909754b2ff8a9c6f5121bebe8f8860ca0220eae28eff5b5bbe18c2d8b891716034437ca3ed93a8014d909c50e26479ef56d532363ce544cc7a297b86d22041fc5bfa837baf459d5ad9f1d7205012d5cddb156fe0033a40f3b8ebdaf2dc8da062c1112a7a6d40682b5bbe7c12a88eff31e302fde6a7648438bced0108f7da223293473c6297184f6b649c840fc18c168629e8dfdd810e1becc3e628c9d1e9a081bf5163f40ab582a767300316084af824361948b7173440d87875188ccbcfc2ef8e2a9c7b94ecfd077a0a73febff067826d2f547bf5842072a9fd933afd406266209888fd0a028efdb9dad735a3ab7926a1b8a34dda5b728b3f9c5eebd510b115ed8aa99cee724f3ce2620315379f5f84a99eec04d00101677f35cda09ba540d68a6ce67fe4fb91020ea4ba2fea07503cd7835ad3d9500fdf8f5bf692fb8de0ce87bc70c48d0eb24bf28d984f5a4497ce545da83f098e387921b66f26b783d07c83433ee5c4d62b35a355f747f6b3a37ef5eeed42a7ba567fa4c571aa002422d478be4fc1f5a6ab615c923cf18b4f9fc6f59c6c096b5ce8c55a146acea3b251401e55052af714370ebd24f15bf492036ed34e0cc2f29361eec56a83a683b150b0cdce35056c92c201903708503921b678ef8a1d509d755d66bb20266ac1fb9031f6ce5143ec727eb31b43f1da2891273bee70dbc850a84742b85030334f76e0c3a0c7a72f3776cbabd0ce9b856958352ac094af95cd867ee08257b9d1e61b2296c5e6a65ec4e4b83fbe4041be82d0bdc9febc389f5c235c4d006b99774de7413e20fd33d7d8d93cc3ff6a83ec53dbfc82c704da4f0f8abd8dc8e8f058a4c1c1384fd39c7aadb848dccad9552293697599d6d760d41852b65033a9d9915bd5319990b2e1d119998d641a9396fdef9a96620c2fdf41baa71f26c6e7642dc69b22396262c440cdad70b5c295cc80e9809075a60eb96938ac116e7e1ad6cdb699413a51ba86b0896921df764009e66925e6fcd8866d78196352ef6017b8920a09b0ae7f0be08d48376c4c62828ed381753437445b2014eaca8d842b78599eda34600f91a3d9eeae69cb00f0c3358cb1f20e0bc8a776411a4289a7606165b4e926614bda68dfe972344e2c09ab65d120029c786c9a4b3464c648684cf61d4542ecf284da4a3c54697f8f9f7b9c67f9f6e122a9a4912d3bf3b39b37c542095728aa91686cf824583b925b4f3f977fddf02a8e79960534871ea82d073e79660ee266d2a1bb3473255feb80c75ca0112839119aa6b46a5123fe3be960994090b064991124c4a49854fbbe7260d35b2f7e667cc9ec72f8b36ce78057e48f8a35ceb0ec12d251041c45e4547a953fb0006c136f9d482fc596339633d68ce0a5c6759a97d185ebb2fca81281852326667cf4419025be0af4786d291f0354371e658e0903617b1c06609d62b34bb91aee5f4a346ad7b31ea00174ba603ecc31f1091109c5c29dd60412e9f69e1e0fe58a1321309b213fdc19dd09f71fbd67e6844def43b972b1a5ad652b78a024b3962e5d544d7a2d95a4cc731a658ef9eff6c5e25ea129b09e0642b42c39551ec2b5432038f3d76525509c565bbd3de3837fed6ebef4c62101b2c6f2cc3b7534e7b0942eae7b1aebea3ba5ad498bba08afaee6dc61568de2669d5ac8f3f95b31281d5582b4c4e46e8ad14c47e22e9d0aaedba3781258fc91000f01f6512769fa0b54e52329a4141d3c907c63cea66c087168df5a1d97d0304236fe0769a8d1f9a7606c466cf99d0ea6894a440c27dfc03de1719976b1ce7001ffd98e5830a0d5b3b10363e58bc14d4b7702c4f38699523fd2a307c1d75b50194c266bdef7775256f4271aa246005917d0935d520399645e34b2064297ee21745ae4af80c07b99afa292065163baa8e2746ad086d6cec18fcc199843731ea14a86e804f91fc0aa3e1b67136d5ac58c97a758a92afc3ee9f337c4a7e05f51f162ec81f91e84671ee027f0261d0214214cf57685ce58a8f8cb9c282828c48600cd84cf2ea1ffd443f96652ec2a47be5be44b08c9d6a879ac249a1e071ea1c6122d0bd0d94d63b41d1c50fa9a0a6dce5f6e655bfdcb8a03a4759b439f348cd2e052d9feac81e1fc4f4854c6e13662746d95e4d5ef0f142caab4dc24b7753b0f60f799f1f3ccbafdb2067d563b262ed42ed23bf3f97764c357abfac6b9a9dca60293c502c135d6d7ac086fda849c026740607139a0f6a9f02b7acffd8183f8fd226286fafdf1206956b67ef83c40c1526416716ba9a1b76279bbebc9699131b532c54b116fb49fe268f0eeedd97710d7e6b87ce8e9ae184bdf5ef62dddc2d4e6b0caa9bb1765d0d34cb956db725303eee1366b23d51810013540edb5dd44c992f1406a10cde626fbc2dea75334e77fe1a05ceb14665044bcca6b3da8da368925f05bd0118c1d4336e47fc8c168b9d1a4a75e2274dcbabd8723acbbab9689a7abf15e15ac114b5182a342ac218f6c66f46355d1c5bba009464048ba9e547ffdf0e103b9c6d1ab444e4344f1b87d7720495ce69fe5a0a5a1827741a569707b1cf942a4016181d648dc24b03573cd0997850317b3a9e11aefcea5b0383797940f8b015c016f8b008714175d7f5fbfe6c867be8a27e45afbcd48ee5f220471a2008e809e8dda3a1fdbce7566dcdb9c18e659da7863a22c7c88e9f91f7fc2ae6f3650d1d4e06da928043913bc4e09c5785f367b41470d1b6c547bc597b24645099be470749d19297a63708ed8e88265d52b2eb907f7d60e5ea6ea4d54c99de176458b703680224c5cabecbd0e339907f45c3f3366f1bb007ef310de8a995f73e98258a2cea76901c324fa3124669fabb8d6ba0dc051a7bf00064673e2206a6f51b69fb04ed376bbfc9c19cde520ccb8c0da2b34a8da1552b35d46d3ef0839fb9b53eaf10aaaa767213e9db31e28c856087fc261fbe33110c771852c0b5186159b55c9324daf9b78fde7118049b0716c65c60d53e231b0b942e579bfda05a7ffba2650800c1131f76b2d03343b35d09acca83278551caef4c88c854775502139c94ddfc22c8b578721a315bd16ea3bc2f99748eee81f80de55d2981671d53f825f0d7cf1c56215f20cb23acfc6050dd643962901eeb856a5bed4152fa7c0b0ba12588080c6fa77a733a1877c697b806b25c432083d1f9bdec3b0d7a7045b166146a93702796d1861f12bdc01d380717ec0444876223e07c90246e3ed14f3dfdf73f2bd21b3f1492f32e47037164b7f7fbba703d3a15891daaa9c41460c33715af49c02b03eaf2c4c34eba7bbec7ebcdc10cd90a1ef60f906e272f11a8c2bdc00e4498d91d4b2a33d45333f2a1a50e89706178605482bcc3dbd1774f39844c3cd9a040df244134235e45d0c513b969b4a3ce0e4dd8896f9e69222b7f216174948a44f84006817ce631df4760cf9f6b0310eacb352afb5ee221fafa2dbb38030fc92950be0546a559aab15f3433976cc46f80cd98bb846015477e6143f9db1fdf9bbcbfa1300eaa515b358fba504ec958081f717b0818f31141ceda76298376d36065b5311449fe4e503da733074bcfde90cc50318ea42c1b52f95ba8b30e0b09ae06d7002de5a9d7b29b2d0697e6a76d8dc76baf6d9c77719003bfb8209cc6a1d4e49f0d13eea1003abea0f4ef72b2ad2e0c1b31b2e61e5d2752c04d1a4eef82e1a64dd21715b3d961f58f56a15c1b6f90ccff04399a6fb8f56de3ff40b15260116ca0bd157071f2ff43243895276a7035fad39df0a36701119e77611acae5ddc96ee6c40709330f44465cf25bc76050a5f9f16a9e2d14e929d0c62d468b3a9d16e253d80df587890102ffec99ba150ffb96337768978899d0290f28edefe966af621b1e204852bd8d4ca5dbff6be94f36293b773d47b77b19d8312b89ca7d2116d60b4007b29fa4d8e43bfef61574deb5d3969562e1a10ff437be1225484215bacdb6211bf36d0ab0e530f7be7c7ae7828cd2051eb03b5af58720cbe872835324f7b93805fee0960122b691118b46627b4b2be970f09641dc178fef98a1ba765f08ce180532ba4120021fe4b00dbbc22b0d1e76c944d83306dd5b97c9c5cc7c3e728ab21fb65736f4e2335ab05c7c092a3cfb07d35c2d1b06096e7039af145fcc931f7e2b864bca1e47cb4bda0dc63937d9b0eaac96b7a5708b792235aa080178246c361ff5dd49bf1ce44e94043b82cda677f117e4b74b3e62c62f10f1af03273014dc1b7453940b97cad12ad544b59d5339670b49627fb78edb9b23ad2fc10250b54021ac49d0eb05b89639b723bcca67265889fc8875ab0cf3ae685588c5ad309ecb0246aba9faa737eeda58e118c76abb13acbbada67a2b5495711201ef6e79d3b02191b8a3a6918d1bf82607d11dfac78c56da8920c087d59afadfb7bd08886f6426d92fb8b576623b431217b712106464f00e6a02b4de08b3aa2f85dd0b1962db0ce7088fcb219173051d174ee2fcdd356316eda52cc7ac8f9fd27d9a119aedb605b415ab86c264b29232b03265a93b51a3e382ab8b9ddaec01f81a92bc405bdf118cef9327e2c176772dba4cddbfd5e832ea2bdba913b3afebe74edad55e68ca099a00f7d0b2beb1dfd2980c42db24b76efeb50f0e8c5ab90b6e2576725e69de12f69077c5d0e12a637844849e30e897b3a49072fb2b994f4340214cfac473f6", "randomness": "0x" }, "config": {