From 2eefb9d5af0844ea54827227990060f456de8a2c Mon Sep 17 00:00:00 2001 From: Dra Murphy <149679879+kmurphypolygon@users.noreply.github.com> Date: Wed, 24 Apr 2024 12:55:20 +0200 Subject: [PATCH 01/15] docs: add migration docs (#78) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * migration docs added * adding docs cos cannot interleave * adding comments * Update docs/how-to/integrate-da.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * Update docs/how-to/migrate/forkid-7-to-9.md Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> * updates after review * last step amend * docs: update fork migration --------- Co-authored-by: Léo Vincent <28714795+leovct@users.noreply.github.com> Co-authored-by: leovct --- doc-drafts/fork-id-migration.org | 307 ---------------------- docs/how-to/integrate-da.md | 198 ++++++++++++++ docs/how-to/manage-policies.md | 94 +++++++ docs/how-to/migrate/forkid-7-to-9 | 0 docs/how-to/migrate/forkid-7-to-9.md | 313 +++++++++++++++++++++++ docs/how-to/quick-test-stack.md | 59 +++++ docs/img/how-to/account.png | Bin 0 -> 117688 bytes docs/img/how-to/manual-network-entry.png | Bin 0 -> 295601 bytes docs/img/how-to/tx-success.png | Bin 0 -> 122444 bytes mkdocs.yml | 24 +- 10 files changed, 677 insertions(+), 318 deletions(-) delete mode 100644 doc-drafts/fork-id-migration.org create mode 100644 docs/how-to/integrate-da.md create mode 100644 docs/how-to/manage-policies.md delete mode 100644 docs/how-to/migrate/forkid-7-to-9 create mode 100644 docs/how-to/migrate/forkid-7-to-9.md create mode 100644 docs/how-to/quick-test-stack.md create mode 100644 docs/img/how-to/account.png create mode 100644 docs/img/how-to/manual-network-entry.png create mode 100644 docs/img/how-to/tx-success.png diff --git a/doc-drafts/fork-id-migration.org b/doc-drafts/fork-id-migration.org deleted file mode 100644 index f8e40481..00000000 --- a/doc-drafts/fork-id-migration.org +++ /dev/null @@ -1,307 +0,0 @@ -#+TITLE: Fork ID 7 to 9 Migration Process -#+DATE: -#+AUTHOR: John Hilliard -#+EMAIL: jhilliard@polygon.technology -#+CREATOR: John Hilliard -#+DESCRIPTION: - - -#+OPTIONS: toc:nil -#+LATEX_HEADER: \usepackage{geometry} -#+LATEX_HEADER: \usepackage{lmodern} -#+LATEX_HEADER: \geometry{left=1in,right=1in,top=1in,bottom=1in} -#+LaTeX_CLASS_OPTIONS: [letterpaper] - -Let's document the process of upgrading from fork 7 to fork 9 using -our Kurtosis package. These steps would more or less be the same in -production except we would be using a [[https://github.com/0xPolygonHermez/zkevm-contracts/blob/v5.0.1-rc.2-fork.8/contracts/PolygonZkEVMTimelock.sol][timelock]] contract to make the -calls. - -Just to make sure I don't have any lingering state, I'm going to run a -clean: - -#+begin_src bash -kurtosis clean -a -#+end_src - -Now, we need to downgrade all of the necessary params in order to -switch back to fork 7: - -#+begin_src diff -diff --git a/params.yml b/params.yml -index c2dd446..4caf2d0 100644 ---- a/params.yml -+++ b/params.yml -@@ -11,14 +11,14 @@ deployment_suffix: "-001" - stages: [1, 2, 3, 4, 5] - - # Docker images and repositories used to spin up services. --zkevm_prover_image: hermeznetwork/zkevm-prover:v6.0.0 --zkevm_node_image: 0xpolygon/cdk-validium-node:0.6.4-cdk --zkevm_da_image: 0xpolygon/cdk-data-availability:0.0.7 -+zkevm_prover_image: hermeznetwork/zkevm-prover:v4.0.19 -+zkevm_node_image: 0xpolygon/cdk-validium-node:0.5.13-cdk.3 -+zkevm_da_image: 0xpolygon/cdk-data-availability:0.0.6 - zkevm_agglayer_image: nulyjkdhthz/agglayer:v0.1.0 - # a38e68b5466d1997cea8466dbd4fc8dacd4e11d8 --zkevm_contracts_branch: develop # v5.0.1-rc.2-fork.8 -+zkevm_contracts_branch: v4.0.0-fork.7 # v5.0.1-rc.2-fork.8 --zkevm_rollup_fork_id: 9 -+zkevm_rollup_fork_id: 7 - zkevm_bridge_service_image: hermeznetwork/zkevm-bridge-service:v0.4.2 - zkevm_bridge_ui_image: hermeznetwork/zkevm-bridge-ui:latest # TODO: better tags for the bridge ui -#+end_src - -After making those changes we should be able to kick off a full redeployment: - -#+begin_src bash -kurtosis run --enclave cdk-v1 --args-file params.yml --image-download always . -#+end_src - -After running this command, let's confirm onchain that this is running -fork 7. -#+begin_src bash -kurtosis files download cdk-v1 genesis /tmp/fork-7-test -jq -r '.L1Config.polygonRollupManagerAddress' /tmp/fork-7-test/genesis.json -cast call --rpc-url "$(kurtosis port print cdk-v1 el-1-geth-lighthouse rpc)" \ - "$(jq -r '.L1Config.polygonRollupManagerAddress' /tmp/fork-7-test/genesis.json)" \ - "rollupIDToRollupData(uint32)(address,uint64,address,uint64,bytes32,uint64,uint64,uint64,uint64,uint64,uint64,uint8)" 1 -#+end_src - -In my case, this is showing a ~7~ as the 4th parameter so I think -we're in good shape. We should also perform some test transactions and -ensure batches are being verified as expected. - -#+begin_src bash -export ETH_RPC_URL="$(kurtosis port print cdk-v1 zkevm-node-rpc-001 http-rpc)" -cast send --legacy --private-key 0x12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625 --value 0.01ether 0x0000000000000000000000000000000000000000 -cast rpc zkevm_batchNumber -cast rpc zkevm_virtualBatchNumber -cast rpc zkevm_verifiedBatchNumber -#+end_src - -* Clean stop of the sequencer - -Before attempting the upgrade, we need to ensure there is a clean stop -of the sequencer. In order to do this, we'll pick a halting batch -number like this: - -#+begin_src diff -diff --git a/templates/trusted-node/node-config.toml b/templates/trusted-node/node-config.toml -index 6c9b9fa..372d904 100644 ---- a/templates/trusted-node/node-config.toml -+++ b/templates/trusted-node/node-config.toml -@@ -117,7 +117,7 @@ StateConsistencyCheckInterval = "5s" - BatchMaxDeltaTimestamp = "20s" - L2BlockMaxDeltaTimestamp = "4s" - ResourceExhaustedMarginPct = 10 -- HaltOnBatchNumber = 0 -+ HaltOnBatchNumber = 64 - SequentialBatchSanityCheck = false - SequentialProcessL2Block = true - [Sequencer.StreamServer] -#+end_src - -After making that change and re-running ~kurtosis run~, we'll need to -wait for the sequencer to halt and for the verified batch to equal the -latest batch. After making that change, there should be some error logs that look like this: - -#+begin_example -{"level":"error","ts":1711481674.517157,"caller":"sequencer/finalizer.go:806","msg":"halting finalizer, error: finalizer reached stop sequencer on batch number: 64%!(EXTRA string=\n/home/runner/work/cdk-validium-node/cdk-validium-node/log/log.go:142 github.com/0xPolygonHermez/zkevm-node/log.appendStackTraceMaybeArgs()\n/home/runner/work/cdk-validium-node/cdk-validium-node/log/log.go:251 github.com/0xPolygonHermez/zkevm-node/log.Errorf()\n/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/finalizer.go:806 github.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).Halt()\n/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/batch.go:221 github.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).closeAndOpenNewWIPBatch()\n/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/batch.go:163 github.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).finalizeWIPBatch()\n/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/finalizer.go:330 github.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).finalizeBatches()\n/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/finalizer.go:166 github.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).Start()\n)","pid":7,"version":"v0.1.0","stacktrace":"github.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).Halt\n\t/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/finalizer.go:806\ngithub.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).closeAndOpenNewWIPBatch\n\t/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/batch.go:221\ngithub.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).finalizeWIPBatch\n\t/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/batch.go:163\ngithub.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).finalizeBatches\n\t/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/finalizer.go:330\ngithub.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).Start\n\t/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/finalizer.go:166"} -#+end_example - -Now we need to wait for the verified batch number to catch up to the -trusted batch number: -#+begin_src bash -export ETH_RPC_URL="$(kurtosis port print cdk-v1 zkevm-node-rpc-001 http-rpc)" -cast rpc zkevm_batchNumber -cast rpc zkevm_verifiedBatchNumber -#+end_src - -Once those two numbers are the same, we should be in a good position -to stop the services that are going to be upgraded - -#+begin_src bash -kurtosis service stop cdk-v1 zkevm-executor-pless-001 -kurtosis service stop cdk-v1 zkevm-node-aggregator-001 -kurtosis service stop cdk-v1 zkevm-node-eth-tx-manager-001 -kurtosis service stop cdk-v1 zkevm-node-l2-gas-pricer-001 -kurtosis service stop cdk-v1 zkevm-node-rpc-001 -kurtosis service stop cdk-v1 zkevm-node-rpc-pless-001 -kurtosis service stop cdk-v1 zkevm-node-sequence-sender-001 -kurtosis service stop cdk-v1 zkevm-node-sequencer-001 -kurtosis service stop cdk-v1 zkevm-node-synchronizer-001 -kurtosis service stop cdk-v1 zkevm-node-synchronizer-pless-001 -kurtosis service stop cdk-v1 zkevm-prover-001 -#+end_src - -* Smart Contract Calls - -In order to upgrade, we're going to need to make a few smart contract -calls. - -#+begin_src bash -git clone git@github.com:0xPolygonHermez/zkevm-contracts.git -pushd zkevm-contracts/ -git reset --hard a38e68b5466d1997cea8466dbd4fc8dacd4e11d8 -npm i -printf "[profile.default]\nsrc = 'contracts'\nout = 'out'\nlibs = ['node_modules']\n" > foundry.toml -forge build -#+end_src - -Okay so now we have the contracts from a (hopefully) working version -of the repo. We can deploy a new verifier. This isn't strictly -necessary but good to do because in some cases you would need a new -verifier contract. - -#+begin_src bash -forge create --json \ - --rpc-url "http://$(kurtosis port print cdk-v1 el-1-geth-lighthouse rpc)" \ - --private-key 0x12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625 \ - contracts/mocks/VerifierRollupHelperMock.sol:VerifierRollupHelperMock > verifier-out.json -#+end_src - -Okay so we'll first try to create a new rollup type for our upgraded -network. In order to configure this file, we'll need a bunch of values -from the l1 setup. - -#+begin_src bash -kurtosis service exec cdk-v1 contracts-001 "cat /opt/zkevm/combined.json" -#+end_src - -Let's try forge to create the contracts: - -#+begin_src bash -ger="0x1f7ad7caA53e35b4f0D138dC5CBF91aC108a2674" -pol="0xEdE9cf798E0fE25D35469493f43E88FeA4a5da0E" -bridge="0xD71f8F956AD979Cc2988381B8A743a2fE280537D" -mngr="0x2F50ef6b8e8Ee4E579B17619A92dE3E2ffbD8AD2" -forge create --json \ - --rpc-url "http://$(kurtosis port print cdk-v1 el-1-geth-lighthouse rpc)" \ - --private-key 0x12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625 \ - contracts/v2/consensus/validium/migration/PolygonValidiumStorageMigration.sol:PolygonValidiumStorageMigration \ - --constructor-args $ger $pol $bridge $mngr > new-consensus-out.json - -genesis="0xd619a27d32e3050f2265a3f58dd74c8998572812da4874aa052f0886d0dfaf47" -cast send -j --rpc-url "http://$(kurtosis port print cdk-v1 el-1-geth-lighthouse rpc)" \ - --private-key 0x12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625 \ - $mngr \ - 'addNewRollupType(address,address,uint64,uint8,bytes32,string)' \ - "$(jq -r '.deployedTo' new-consensus-out.json)" \ - "$(jq -r '.deployedTo' verifier-out.json)" \ - 9 0 "$genesis" "test!!!" > add-rollup-type-out.json -#+end_src - - -Assuming that all worked somehow, you should be able to get your new -rollup type id: - -#+begin_src bash -cat add-rollup-type-out.json | jq -r '.logs[0].topics[1]' -#+end_src - -Taking that id, we should be able to update our rollup: - -#+begin_src bash -rollup="0x1Fe038B54aeBf558638CA51C91bC8cCa06609e91" -cast send -j --rpc-url "http://$(kurtosis port print cdk-v1 el-1-geth-lighthouse rpc)" \ - --private-key 0x12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625 \ - $mngr \ - 'updateRollup(address,uint32,bytes)' \ - "$rollup" 2 0x > update-rollup-type-out.json -#+end_src - -Now we should also be able to verify that our rollupid has been -updated. Previously the 4th value was a ~7~ and now it should be a -~9~. - -#+begin_src bash -cast call --rpc-url "$(kurtosis port print cdk-v1 el-1-geth-lighthouse rpc)" \ - "$(jq -r '.L1Config.polygonRollupManagerAddress' /tmp/fork-7-test/genesis.json)" \ - "rollupIDToRollupData(uint32)(address,uint64,address,uint64,bytes32,uint64,uint64,uint64,uint64,uint64,uint64,uint8)" 1 -#+end_src - -After updating the rollup it seems like the DA Protcol needs to be -setup again: - -#+begin_src bash -rollup="0x1Fe038B54aeBf558638CA51C91bC8cCa06609e91" -dac="0x5A6896A98c4B7C7E8f16d177C719a1d856b9154c" -cast send -j \ - --private-key "0x12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625" \ - --rpc-url "$(kurtosis port print cdk-v1 el-1-geth-lighthouse rpc)" \ - "$rollup" 'setDataAvailabilityProtocol(address)' $dac > set-dac-out.json -#+end_src - - -* Node Upgrade - -In terms of the smart contracts, the upgrade should more or less be -done, but we need to start the nodes back up. This procedure is very -sensitive and we must ensure that the synchronizer starts first. The -main thing we'll do is revert the parameters back to the versions of -the node that worked with fork 9 specify that ONLY stage 3 should run. - -#+begin_src diff -diff --git a/params.yml b/params.yml -index c2dd446..cdb8338 100644 ---- a/params.yml -+++ b/params.yml -@@ -8,7 +8,7 @@ deployment_suffix: "-001" - # The deployment process is divided into various stages. - # The `stages` parameter indicates the specific stages you wish the deployment to proceed through. - # By default, it will execute all the stages. --stages: [1, 2, 3, 4, 5] -+stages: [3] - - # Docker images and repositories used to spin up services. - zkevm_prover_image: hermeznetwork/zkevm-prover:v6.0.0 -#+end_src - -At this point, we should be able to run Kurtosis and ideally bring -back up the main node components. Before starting the node backup, be -sure to remove the ~HaltOnBatchNumber~ setting that we added earlier -in the process - -#+begin_src bash -kurtosis run --enclave cdk-v1 --args-file params.yml --image-download always . -#+end_src - -At this point, the core services are running and if everything went -well, we should be able to send a transaction and see that the batche -numbers are moving through their normal progression. - -#+begin_src bash -export ETH_RPC_URL="$(kurtosis port print cdk-v1 zkevm-node-rpc-001 http-rpc)" -cast send --legacy --private-key 0x12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625 --value 0.01ether 0x0000000000000000000000000000000000000000 -cast rpc zkevm_batchNumber -cast rpc zkevm_virtualBatchNumber -cast rpc zkevm_verifiedBatchNumber -#+end_src - -* Random Notes - -After starting the nodes back up I'm seeing a decent amount of errors -in the synchronizer like this, it doesn't seem like it actually causes -an issue, but it's a little odd. - -#+begin_example -{"level":"warn","ts":1711502381.03938,"caller":"etherman/etherman.go:661","msg":"Event not registered: {Address:0x1Fe038B54aeBf558638CA51C91bC8cCa06609e91 Topics:[0xd331bd4c4cd1afecb94a225184bded161ff3213624ba4fb58c4f30c5a861144a] Data:[0 0 0 0 0 0 0 0 0 0 0 0 90 104 150 169 140 75 124 126 143 22 209 119 199 25 161 216 86 185 21 76] BlockNumber:108 TxHash:0x1bb5e714dd96434ded2d818458cc517cf7b30f5787dbb3aedb667e5e3e96808e TxIndex:0 BlockHash:0xdf5850cd5a8975859595649a05ce245f02953e84af627e9b22a1f8381077f057 Index:0 Removed:false}","pid":7,"version":"0.6.4+cdk"} -#+end_example - -We can check this event directly from the rpc as well: - -#+begin_src bash -cast logs --rpc-url "http://$(kurtosis port print cdk-v1 el-1-geth-lighthouse rpc)" --address 0x1Fe038B54aeBf558638CA51C91bC8cCa06609e91 --from-block 108 --to-block 108 -#+end_src - -We can reverse which event this is with the following script: - -#+begin_src bash -cat compiled-contracts/*.json | jq '.abi[] | select(.type == "event") | .type = "function"' | jq -s | polycli abi decode | grep d33 -cast sig-event 'SetDataAvailabilityProtocol(address)' -#+end_src - -It looks like the unregistered event is a call to -~SetDataAvailabilityProtocol(address)~, but unclear why that -particular event is not recognized. diff --git a/docs/how-to/integrate-da.md b/docs/how-to/integrate-da.md new file mode 100644 index 00000000..9c2a6423 --- /dev/null +++ b/docs/how-to/integrate-da.md @@ -0,0 +1,198 @@ +--- +comments: true +--- + +This document shows you how to integrate a third-party data availability (DAC) solution into your CDK stack. + +## Prerequisites + +!!! tip + Make sure you have upgraded your CDK stack if necessary. + +## Set up contracts + +This section shows you how to create a custom CDK validium DAC contract. + +1. Clone [zkevm-contracts](https://github.com/0xPolygonHermez/zkevm-contracts). + +2. `cd` into `zkevm-contracts` and checkout tag `v6.0.0-rc.1-fork.9`. + +3. Run `npm install` from the root. + +4. `cd` to the `contracts/v2/consensus/validium` directory. + + !!! tip + - Until further notice, these contracts run on the [etrog release](https://polygon.technology/blog/polygon-zkevm-the-etrog-upgrade-is-live-on-mainnet). + +5. Create your custom contract in the same directory, and make sure it implements the [IDataAvailabilityProtocol](https://github.com/0xPolygonHermez/zkevm-contracts/blob/v6.0.0-rc.1-fork.9/contracts/v2/interfaces/IDataAvailabilityProtocol.sol) interface. + + !!! tip + - Use the Polygon DAC implementation contract: [PolygonDataCommittee.sol](https://github.com/0xPolygonHermez/zkevm-contracts/blob/v6.0.0-rc.1-fork.9/contracts/v2/consensus/validium/PolygonDataCommittee.sol) as a guide. + - The contract supports custom smart contract implementation and, through this, DACs can add their custom on-chain verification logic. + +6. You can leave the `verifyMessage` function empty but make sure the `getProcotolName` function returns a unique name (such as Avail, Celestia, etc). The following example code comes from the [PolygonDataCommitee.sol](https://github.com/0xPolygonHermez/zkevm-contracts/blob/v6.0.0-rc.1-fork.9/contracts/v2/consensus/validium/PolygonDataCommittee.sol) implementation. + + ```solidity + // Name of the data availability protocol + string internal constant _PROTOCOL_NAME = ""; + + ... + + /** + * @notice Return the protocol name + */ + function getProcotolName() external pure override returns (string memory) { + return _PROTOCOL_NAME; + } + ``` + +7. Update the [/deployment/v2/4_createRollup.ts](https://github.com/0xPolygonHermez/zkevm-contracts/blob/54f58c8b64806429bc4d5c52248f29cf80ba401c/deployment/v2/4_createRollup.ts#L77) script to add your contract name. + + ```ts + const supporteDataAvailabilityProtocols = [""]; + ``` + +8. Make your contract deployable by copying, editing for your custom implementation, and pasting back in, the `if` statement from the [/deployment/v2/4_createRollup.ts#L251](https://github.com/0xPolygonHermez/zkevm-contracts/blob/54f58c8b64806429bc4d5c52248f29cf80ba401c/deployment/v2/4_createRollup.ts#L260) node creation script. + +!!! info "`PolygonValidiumEtrog.sol` solution" + + The [Etrog DAC integration contract](https://github.com/0xPolygonHermez/zkevm-contracts/blob/v6.0.0-rc.1-fork.9/contracts/v2/consensus/validium/PolygonValidiumEtrog.sol) is still work-in-progress at the time of writing but there are some interesting things to note. + + 1. It implements the function [`verifyMessage` function](https://github.com/0xPolygonHermez/zkevm-contracts/blob/54f58c8b64806429bc4d5c52248f29cf80ba401c/contracts/v2/consensus/validium/PolygonValidiumEtrog.sol#L231): + + ```solidity + // 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( + accumulatedNonForcedTransactionsHash, + dataAvailabilityMessage + ); + ``` + + where `accumulatedNonForcedTransactionsHash` is used for verification against the protocol and `dataAvailabilityMessage` is a byte array containing the signature and addresses of the committee in ascending order. + + 2. It also implements a function to set the data availability protocol at [line 287](https://github.com/0xPolygonHermez/zkevm-contracts/blob/54f58c8b64806429bc4d5c52248f29cf80ba401c/contracts/v2/consensus/validium/PolygonValidiumEtrog.sol#L287) to see how they do this. + + ```solidity + /** + * @notice Allow the admin to set a new data availability protocol + * @param newDataAvailabilityProtocol Address of the new data availability protocol + */ + function setDataAvailabilityProtocol( + IDataAvailabilityProtocol newDataAvailabilityProtocol + ) external onlyAdmin { + dataAvailabilityProtocol = newDataAvailabilityProtocol; + + emit SetDataAvailabilityProtocol(address(newDataAvailabilityProtocol)); + } + ``` + +## Deploy Docker image + +This section shows you how to deploy the Docker image containing your custom DAC contract. + +1. Edit the following parameters in the [`docker/scripts/v2/deploy_parameters_docker.json`](https://github.com/0xPolygonHermez/zkevm-contracts/blob/v6.0.0-rc.1-fork.9/docker/scripts/v2/deploy_parameters_docker.json) file: + + ```json + "minDelayTimelock": 3600, // BECOMES "minDelayTimelock": 1, + ``` + +2. Edit the following parameters in the [`/docker/scripts/v2/create_rollup_parameters_docker.json`](https://github.com/0xPolygonHermez/zkevm-contracts/blob/v6.0.0-rc.1-fork.9/docker/scripts/v2/create_rollup_parameters_docker.json) file: + + ```json + "consensusContract": "PolygonValidiumEtrog", // CHANGE THIS TO YOUR CONTRACT NAME + "dataAvailabilityProtocol": "PolygonDataCommittee", // ADD THIS PARAMETER + ``` + +3. Run the following command: + + ```sh + cp docker/scripts/v2/hardhat.example.paris hardhat.config.ts + ``` + +4. Edit [docker/scripts/v2/deploy-docker.sh](https://github.com/0xPolygonHermez/zkevm-contracts/blob/v6.0.0-rc.1-fork.9/docker/scripts/v2/deploy-docker.sh) to add the following line: + + ```sh + sudo chmod -R go+rxw docker/gethData before docker build -t hermeznetwork/geth-zkevm-contracts -f docker/Dockerfile . + ``` + +5. In the [deployment/v2/4_createRollup.ts](https://github.com/0xPolygonHermez/zkevm-contracts/blob/54f58c8b64806429bc4d5c52248f29cf80ba401c/deployment/v2/4_createRollup.ts#L290) file, uncomment the 290-291, and add a `console.log` output that grabs the address of the DAC: + + ```ts + // Setup data committee to 0 + await (await polygonDataCommittee?.setupCommittee(0, [], "0x")).wait(); + console.log(dataAvailabilityProtocol, "deployed to:", polygonDataCommittee.target); + ``` + +6. Build the image with the following commands: + + ```sh + sudo npx hardhat compile + sudo npm run docker:contracts + ``` + +7. Tag the image with the following command, where `XXXX` is custom: + + ```sh + docker image tag hermeznetwork/geth-zkevm-contracts hermeznetwork/geth-cdk-validium-contracts:XXXX + ``` + +## Set up the node + +This section shows you how to set up your CDK node that sends and receives data from the DAC. + +1. Create a package that implements the [`DABackender`](https://github.com/0xPolygon/cdk-validium-node/blob/b6ee6cb087099c2e97f3e596f84672fc021b517a/dataavailability/interfaces.go#L14) interface and place it under the [`cdk-validium-node/tree/develop/dataavailability`](https://github.com/0xPolygon/cdk-validium-node/tree/develop/dataavailability) directory. + +2. Add a new constant to the [/dataavailability/config.go](https://github.com/0xPolygon/cdk-validium-node/blob/b6ee6cb087099c2e97f3e596f84672fc021b517a/dataavailability/config.go) file that represents the DAC. + + ```go + const ( + // DataAvailabilityCommittee is the DAC protocol backend + DataAvailabilityCommittee DABackendType = "DataAvailabilityCommittee" + ) + ``` + + where `DataAvailabilityCommittee` matches the `_PROTOCOL_NAME` see in the [Set up contracts](#set-up-contracts) section. + +3. _OPTIONAL_: Add a config struct to the new package inside the main config.go file so that your package can receive custom configurations using the node’s main config file. + +4. Instantiate your package and use it to create the main data availability instance, as done in the Polygon implementation. + +## Test the integration + +!!! tip + - By default, all E2E tests run using the DAC. + - It is possible to run the E2E tests using other DAC backends by amending the `test.node.config.toml` file. + +To test your DAC integration, follow the steps below. + +1. Create an E2E test that uses your protocol by following the [test/e2e/datacommittee_test.go](https://github.com/0xPolygon/cdk-validium-node/blob/develop/test/e2e/datacommittee_test.go) example. + +2. Generate a docker image containing the changes to the node: + + ```sh + make build-docker + ``` + +3. Build the genesis file for the node: + + - First, clone the [cdk-validium-node](https://github.com/0xPolygon/cdk-validium-node) repo and checkout v0.6.4-cdk.5. + - Edit the `test/config/test.genesis.config.json` file taking values in the generated output files created previously in the contract repo's `docker/deploymentOutputs` folder: + + !!! info "Parameters to change" + `l1Config.polygonZkEVMAddres`s ==> `rollupAddress` @ `create_rollup_output.json` + `l1Config.polygonRollupManagerAddress` ==> `polygonRollupManager` @ `deploy_output.json` + `l1Config.polTokenAddress` ==> `polTokenAddress` @ `deploy_output.json` + `l1Config.polygonZkEVMGlobalExitRootAddress` ==> `polygonZkEVMGlobalExitRootAddress` @ `deploy_output.json` + `rollupCreationBlockNumber` ==> `createRollupBlock` @ `create_rollup_output.json` + `rollupManagerCreationBlockNumber` ==> `deploymentBlockNumber` @ `deploy_output.json` + `root` ==> `root` @ `genesis.json` + `genesis` ==> `genesis` @ `genesis.json` + + !!! important + - You should follow this step every time you build a new Docker image. + +4. Update the contracts Docker image tag with the custom tag you created at the [deploy Docker image](#deploy-docker-image) section, step 7, by amending the node's [Docker compose file](https://github.com/0xPolygon/cdk-validium-node/blob/develop/test/docker-compose.yml). + +5. Modify the Makefile so it can run your test. Use the [Polygon DAC Makefile](https://github.com/0xPolygon/cdk-validium-node/blob/develop/test/Makefile) as an example. + diff --git a/docs/how-to/manage-policies.md b/docs/how-to/manage-policies.md new file mode 100644 index 00000000..c2305fb6 --- /dev/null +++ b/docs/how-to/manage-policies.md @@ -0,0 +1,94 @@ +--- +comments: true +--- + +# Manage allowlists, and more, with policies + +!!! important + Policies are currently only available in validium mode. + +Managing allowlists, denylists, and ACLs is done with policies. + +## Policy overview + +A **policy** is a set of rules that govern what actions are allowed or denied in the transaction pool. + +- **Fine-grained control**: Developers can specify policies at a granular level, allowing or denying specific actions for specific addresses. +- **Dynamic updates**: Policies and ACLs can be updated on-the-fly without requiring a node restart. +- **Database-backed**: All policy data is stored in a PostgreSQL database. +- **Extensible**: New policies can be easily added to the system. + +## Validium node + +### Policies + +Currently, there are two defined policies: + +- **SendTx**: governs whether an address may send transactions to the pool. +- **Deploy**: governs whether an address may deploy a contract. + +The CDK validium node offers policy management features that include allowlisting[^1], denylisting[^2], and access control lists (ACLs)[^3]. These features are beneficial for validium-based app-chains that require fine-grained control over transaction pools. + +### Code definitions + +- **Policy management**: [`cmd/policy.go`](https://github.com/0xPolygon/cdk-validium-node/blob/5399f8859af9ffb0eb693bf395e1f09b53b154de/cmd/policy.go) contains the core logic of policy management. +- **Policy definitions**: [`pool/policy.go`](https://github.com/0xPolygon/cdk-validium-node/blob/5399f8859af9ffb0eb693bf395e1f09b53b154de/pool/policy.go) contains structs and utility functions for policies and ACLs. +- **Data**: [`pgpoolstorage/policy.go`](https://github.com/0xPolygon/cdk-validium-node/blob/5399f8859af9ffb0eb693bf395e1f09b53b154de/pool/policy.go) interacts with the data layer (PostgreSQL database) to store and retrieve policy and ACL data. +- **Policy interface**: [`pool/interfaces.go`](https://github.com/0xPolygon/cdk-validium-node/blob/5399f8859af9ffb0eb693bf395e1f09b53b154de/pool/interfaces.go#L42) contains a `policy` interface which defines the methods that policies must implement. + +### How to use a policy + +| Command name | Description | Flags & parameters | +|--------------|-------------------------------------------------------|--------------------------------------------------------------------------------------------------------| +| `policy add` | Add address(es) to a policy exclusion list | `--policy` (or `-p`): Policy name
`--csv`: CSV file with addresses | +| `policy remove` | Remove address(es) from a policy exclusion list | `--policy` (or `-p`): Policy name
`--csv`: CSV file with addresses to remove | +| `policy clear` | Clear all addresses from a policy's exclusion list | `--policy` (or `-p`): Policy name | +| `policy describe` | Describe the default actions for the policies or a specific policy | `--policy` (or `-p`): Policy name (optional)
`--no-header`: Omit header in output (optional) | +| `policy update` | Update the default action for a policy | `--policy` (or `-p`): Policy name
`--allow`: Set policy to 'allow'
`--deny`: Set policy to 'deny' | + +!!! note + The examples demonstrate a `deploy` policy. + +#### Add addresses + +To add one or more addresses to a specific policy, you can use the `policy add` command. If you have a CSV file containing the addresses, you can use the --csv` flag. + +```bash +docker exec -it cdk-validium-aggregator /app/cdk-validium-node policy add --policy deploy 0xAddress1 +``` + +#### Remove addresses + +To remove addresses from a policy, you can use the `policy remove` command. + +```bash +# Remove a single address from the 'deploy' policy +docker exec -it cdk-validium-aggregator /app/cdk-validium-node policy remove --policy deploy 0xAddress1 + +# Remove multiple addresses from the 'deploy' policy using a CSV file +docker exec -it cdk-validium-aggregator /app/cdk-validium-node policy remove --policy deploy --csv addresses.csv +``` + +#### Clear all addresses + +To remove all addresses from a policy's ACL, you can use the `policy clear` command. + +```bash +docker exec -it cdk-validium-aggregator /app/cdk-validium-node policy clear --policy deploy +``` + +#### Get information about a policy + +To get information about a specific policy or all policies, you can use the `policy describe` command. + +```bash +# Describe a specific policy +docker exec -it cdk-validium-aggregator /app/cdk-validium-node policy describe --policy deploy + +# Describe all policies +docker exec -it cdk-validium-aggregator /app/cdk-validium-node policy describe +``` + +[^1]: **Allowlisting**: The process of explicitly allowing addresses to perform certain actions. +[^2]: **Denylisting**: The process of explicitly denying addresses from performing certain actions. +[^3]: **ACL (access control list)**: A list of addresses that are exceptions to a given policy. diff --git a/docs/how-to/migrate/forkid-7-to-9 b/docs/how-to/migrate/forkid-7-to-9 deleted file mode 100644 index e69de29b..00000000 diff --git a/docs/how-to/migrate/forkid-7-to-9.md b/docs/how-to/migrate/forkid-7-to-9.md new file mode 100644 index 00000000..a7e0544e --- /dev/null +++ b/docs/how-to/migrate/forkid-7-to-9.md @@ -0,0 +1,313 @@ +--- +comments: true +--- + +This document shows you how to migrate from fork 7 to fork 9 using the Kurtosis package. + +!!! tip + These steps are similar a production build, except you have to use a [timelock](https://github.com/0xPolygonHermez/zkevm-contracts/blob/v5.0.1-rc.2-fork.8/contracts/PolygonZkEVMTimelock.sol) contract to make the calls. + +## Prequisite steps and set up + +1. Run a clean command to remove any lingering state: + + ```sh + kurtosis clean --all + ``` + +2. Downgrade all the necessary parameters to switch back to fork 7. Open the `params.yml` file and make the following changes: + + ```diff + diff --git a/params.yml b/params.yml + index 175619f..a72d452 100644 + --- a/params.yml + +++ b/params.yml + @@ -29,13 +29,13 @@ args: + deployment_suffix: "-001" + + # Docker images and repositories used to spin up services. + - zkevm_prover_image: hermeznetwork/zkevm-prover:v6.0.0 + + zkevm_prover_image: hermeznetwork/zkevm-prover:v4.0.19 + + - zkevm_node_image: 0xpolygon/cdk-validium-node:0.6.4-cdk.2 + + zkevm_node_image: 0xpolygon/cdk-validium-node:0.5.13-cdk.3 + + - zkevm_da_image: 0xpolygon/cdk-data-availability:0.0.7 + + zkevm_da_image: 0xpolygon/cdk-data-availability:0.0.6 + + zkevm_contracts_image: leovct/zkevm-contracts # the tag is automatically replaced by the value of /zkevm_rollup_fork_id/ + @@ -160,7 +160,7 @@ args: + zkevm_rollup_chain_id: 10101 + + # The fork id of the new rollup. It indicates the prover (zkROM/executor) version. + - zkevm_rollup_fork_id: 9 + + zkevm_rollup_fork_id: 7 + + # The consensus contract name of the new rollup. + zkevm_rollup_consensus: PolygonValidiumEtrog + ``` + +3. Now kick-off a full redeploy: + + ```sh + kurtosis run --enclave cdk-v1 --args-file params.yml --image-download always . + ``` + +4. Confirm onchain that fork 7 is running: + + ```sh + kurtosis files download cdk-v1 genesis /tmp/fork-7-test + cast call \ + --rpc-url "$(kurtosis port print cdk-v1 el-1-geth-lighthouse rpc)" \ + "$(jq -r '.L1Config.polygonRollupManagerAddress' /tmp/fork-7-test/genesis.json)" \ + "rollupIDToRollupData(uint32)(address,uint64,address,uint64,bytes32,uint64,uint64,uint64,uint64,uint64,uint64,uint8)" 1 + ``` + + Should you see `7` showing as the 4th parameter. + +5. Send some test transactions to ensure batches are verified as expected. + + ```sh + export ETH_RPC_URL="$(kurtosis port print cdk-v1 zkevm-node-rpc-001 http-rpc)" + cast send --legacy --private-key "$(yq -r .args.zkevm_l2_admin_private_key params.yml)" --value 0.01ether 0x0000000000000000000000000000000000000000 + cast rpc zkevm_batchNumber + cast rpc zkevm_virtualBatchNumber + cast rpc zkevm_verifiedBatchNumber + ``` + +After a few minutes, the number of verified batches should increase (the first batch checked does not count). + +## Make a clean stop of the sequencer + +1. Before attempting the upgrade, we need to make a clean stop of the sequencer. To do this, pick a halting batch number by updating the `node-config.toml` file like this. Make sure to pick a batch number higher than the current batch number! + + ```sh + cast to-dec $(cast rpc zkevm_batchNumber | sed 's/"//g') + ``` + + ```diff + diff --git a/templates/trusted-node/node-config.toml b/templates/trusted-node/node-config.toml + index 6c9b9fa..372d904 100644 + --- a/templates/trusted-node/node-config.toml + +++ b/templates/trusted-node/node-config.toml + @@ -117,7 +117,7 @@ StateConsistencyCheckInterval = "5s" + BatchMaxDeltaTimestamp = "20s" + L2BlockMaxDeltaTimestamp = "4s" + ResourceExhaustedMarginPct = 10 + - HaltOnBatchNumber = 0 + + HaltOnBatchNumber = 64 + SequentialBatchSanityCheck = false + SequentialProcessL2Block = true + [Sequencer.StreamServer] + ``` + +2. Re-run Kurtosis: + + ```sh + kurtosis run --enclave cdk-v1 --args-file params.yml --image-download always . + ``` + +3. Wait for the sequencer to halt and the verified batch to equal the latest batch and check the logs. + + ```sh + kurtosis service logs cdk-v1 zkevm-node-sequencer-001 --follow + ``` + + You should see error logs that look like this: + + ```json + {"level":"error","ts":1711481674.517157,"caller":"sequencer/finalizer.go:806","msg":"halting finalizer, error: finalizer reached stop sequencer on batch number: 64%!(EXTRA string=\n/home/runner/work/cdk-validium-node/cdk-validium-node/log/log.go:142 github.com/0xPolygonHermez/zkevm-node/log.appendStackTraceMaybeArgs()\n/home/runner/work/cdk-validium-node/cdk-validium-node/log/log.go:251 github.com/0xPolygonHermez/zkevm-node/log.Errorf()\n/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/finalizer.go:806 github.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).Halt()\n/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/batch.go:221 github.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).closeAndOpenNewWIPBatch()\n/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/batch.go:163 github.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).finalizeWIPBatch()\n/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/finalizer.go:330 github.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).finalizeBatches()\n/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/finalizer.go:166 github.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).Start()\n)","pid":7,"version":"v0.1.0","stacktrace":"github.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).Halt\n\t/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/finalizer.go:806\ngithub.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).closeAndOpenNewWIPBatch\n\t/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/batch.go:221\ngithub.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).finalizeWIPBatch\n\t/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/batch.go:163\ngithub.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).finalizeBatches\n\t/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/finalizer.go:330\ngithub.com/0xPolygonHermez/zkevm-node/sequencer.(*finalizer).Start\n\t/home/runner/work/cdk-validium-node/cdk-validium-node/sequencer/finalizer.go:166"} + ``` + +4. Wait for the verified batch number to catch up to the trusted batch number: + + ```sh + export ETH_RPC_URL="$(kurtosis port print cdk-v1 zkevm-node-rpc-001 http-rpc)" + cast rpc zkevm_batchNumber + cast rpc zkevm_verifiedBatchNumber + ``` + +5. When those two numbers are the same, stop the services that are going to be upgraded: + + ```sh + kurtosis service stop cdk-v1 zkevm-executor-pless-001 + kurtosis service stop cdk-v1 zkevm-node-aggregator-001 + kurtosis service stop cdk-v1 zkevm-node-eth-tx-manager-001 + kurtosis service stop cdk-v1 zkevm-node-l2-gas-pricer-001 + kurtosis service stop cdk-v1 zkevm-node-rpc-001 + kurtosis service stop cdk-v1 zkevm-node-rpc-pless-001 + kurtosis service stop cdk-v1 zkevm-node-sequence-sender-001 + kurtosis service stop cdk-v1 zkevm-node-sequencer-001 + kurtosis service stop cdk-v1 zkevm-node-synchronizer-001 + kurtosis service stop cdk-v1 zkevm-node-synchronizer-pless-001 + kurtosis service stop cdk-v1 zkevm-prover-001 + ``` + +## Smart contract calls + +1. From another directory, make the required smart contract calls (this should not be done from the `kurtosis-cdk` directory): + + ```sh + git clone git@github.com:0xPolygonHermez/zkevm-contracts.git + pushd zkevm-contracts/ + git reset --hard a38e68b5466d1997cea8466dbd4fc8dacd4e11d8 + npm install + printf "[profile.default]\nsrc = 'contracts'\nout = 'out'\nlibs = ['node_modules']\n" > foundry.toml + forge build + ``` + +2. Deploy a new verifier. + + !!! tip + This step isn't strictly necessary but good to do because in some cases you need a new verifier contract. + + ```sh + forge create \ + --json \ + --rpc-url "$(kurtosis port print cdk-v1 el-1-geth-lighthouse rpc)" \ + --private-key 0x12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625 \ + contracts/mocks/VerifierRollupHelperMock.sol:VerifierRollupHelperMock > verifier-out.json + ``` + +3. Create the `PolygonValidiumStorageMigration` contract: + + ```sh + export ETH_RPC_URL="$(kurtosis port print cdk-v1 el-1-geth-lighthouse rpc)" + ger="$(kurtosis service exec cdk-v1 contracts-001 "jq -r .polygonZkEVMGlobalExitRootAddress /opt/zkevm/combined.json" | tail -n +2)" + pol="$(kurtosis service exec cdk-v1 contracts-001 "jq -r .polTokenAddress /opt/zkevm/combined.json" | tail -n +2)" + bridge="$(kurtosis service exec cdk-v1 contracts-001 "jq -r .polygonZkEVMBridgeAddress /opt/zkevm/combined.json" | tail -n +2)" + mngr="$(kurtosis service exec cdk-v1 contracts-001 "jq -r .polygonRollupManager /opt/zkevm/combined.json" | tail -n +2)" + forge create \ + --json \ + --private-key 0x12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625 \ + contracts/v2/consensus/validium/migration/PolygonValidiumStorageMigration.sol:PolygonValidiumStorageMigration \ + --constructor-args $ger $pol $bridge $mngr > new-consensus-out.json + ``` + +4. Add a new rollup type to the rollup manager: + + ```sh + genesis="$(kurtosis service exec cdk-v1 contracts-001 "jq -r .genesis /opt/zkevm/combined.json" | tail -n +2)" + cast send \ + --json \ + --private-key 0x12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625 \ + $mngr \ + 'addNewRollupType(address,address,uint64,uint8,bytes32,string)' \ + "$(jq -r '.deployedTo' new-consensus-out.json)" \ + "$(jq -r '.deployedTo' verifier-out.json)" \ + 9 0 "$genesis" 'test!!!' > add-rollup-type-out.json + ``` + +5. Get your new rollup type id: + + ```sh + jq -r '.logs[0].topics[1]' add-rollup-type-out.json + ``` + +6. Update the rollup with the id: + + ```sh + rollup="$(kurtosis service exec cdk-v1 contracts-001 "jq -r .rollupAddress /opt/zkevm/combined.json" | tail -n +2)" + cast send \ + --json \ + --private-key 0x12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625 \ + $mngr \ + 'updateRollup(address,uint32,bytes)' \ + "$rollup" 2 0x > update-rollup-type-out.json + ``` + +7. Verify the updated rollupid. Previously the 4th value was a `7` and now it should be a `9`. + + ```sh + cast call \ + "$(jq -r '.L1Config.polygonRollupManagerAddress' /tmp/fork-7-test/genesis.json)" \ + "rollupIDToRollupData(uint32)(address,uint64,address,uint64,bytes32,uint64,uint64,uint64,uint64,uint64,uint64,uint8)" 1 + ``` + +8. Set up the data availability protcol again: + + ```sh + dac="$(kurtosis service exec cdk-v1 contracts-001 "jq -r .polygonDataCommittee /opt/zkevm/combined.json" | tail -n +2)" + cast send \ + --json \ + --private-key "0x12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625" \ + "$rollup" 'setDataAvailabilityProtocol(address)' $dac > set-dac-out.json + ``` + +## Node upgrade + +At this stage, the smart contracts are upgraded. However, we still need to start the nodes again. + +!!! warning + - This procedure is very sensitive. + - Ensure the synchronizer starts first. + +We're going to revert the parameters back to the versions of the node that worked with fork 9, and specify that _ONLY_ stage 3 should run. + +1. Update the `params.yml` file as follows: + + ```sh + yq -Y --in-place '.deploy_l1 = false' params.yml + yq -Y --in-place '.deploy_zkevm_contracts_on_l1 = false' params.yml + yq -Y --in-place '.deploy_databases = false' params.yml + yq -Y --in-place '.deploy_cdk_bridge_infra = false' params.yml + yq -Y --in-place '.deploy_zkevm_permissionless_node = false' params.yml + yq -Y --in-place '.deploy_observability = false' params.yml + ``` + +2. Remove the `HaltOnBatchNumber` setting that we added earlier. + +3. Run Kurtosis to bring up the main node components. + + ```sh + kurtosis run --enclave cdk-v1 --args-file params.yml --image-download always . + ``` + +4. The core services are now running and we should be able to send a transaction and see the batch numbers moving through their normal progression. + + ```sh + export ETH_RPC_URL="$(kurtosis port print cdk-v1 zkevm-node-rpc-001 http-rpc)" + cast send --legacy --private-key "$(yq -r .args.zkevm_l2_admin_private_key params.yml)" --value 0.01ether 0x0000000000000000000000000000000000000000 + cast rpc zkevm_batchNumber + cast rpc zkevm_virtualBatchNumber + cast rpc zkevm_verifiedBatchNumber + ``` + +## Troubleshooting + +1. If you clone the `zkevm-contracts` repo in the same folder as `kurtosis-cdk`, you may see this error when you try to deploy the stack: + + ```sh + Error: An error occurred running command 'run' + Caused by: An error occurred calling the run function for command 'run' + Caused by: An error starting the Kurtosis code execution '.' + Caused by: Error uploading package '.' prior to executing it + Caused by: There was an error compressing module '.' before upload + Caused by: An error occurred creating the archive from the files at '.' + Caused by: The files you are trying to upload, which are now compressed, exceed or reach 100mb. Manipulation (i.e. uploads or downloads) of files larger than 100mb is currently disallowed in Kurtosis. + ``` + +2. You may also see errors like these: + + ```json + {"level":"warn","ts":1711502381.03938,"caller":"etherman/etherman.go:661","msg":"Event not registered: {Address:0x1Fe038B54aeBf558638CA51C91bC8cCa06609e91 Topics:[0xd331bd4c4cd1afecb94a225184bded161ff3213624ba4fb58c4f30c5a861144a] Data:[0 0 0 0 0 0 0 0 0 0 0 0 90 104 150 169 140 75 124 126 143 22 209 119 199 25 161 216 86 185 21 76] BlockNumber:108 TxHash:0x1bb5e714dd96434ded2d818458cc517cf7b30f5787dbb3aedb667e5e3e96808e TxIndex:0 BlockHash:0xdf5850cd5a8975859595649a05ce245f02953e84af627e9b22a1f8381077f057 Index:0 Removed:false}","pid":7,"version":"0.6.4+cdk"} + ``` + + You can check them directly from the rpc: + + ```sh + cast logs \ + --rpc-url "$(kurtosis port print cdk-v1 el-1-geth-lighthouse rpc)" \ + --address 0x1Fe038B54aeBf558638CA51C91bC8cCa06609e91 \ + --from-block 108 \ + --to-block 108 + ``` + + You can reverse an event with the following script: + + ```sh + cat compiled-contracts/*.json | jq '.abi[] | select(.type == "event") | .type = "function"' | jq -s | polycli abi decode | grep d33 + cast sig-event 'SetDataAvailabilityProtocol(address)' + ``` + +3. In the above example, it looks like the unregistered event is a call to `SetDataAvailabilityProtocol(address)`. diff --git a/docs/how-to/quick-test-stack.md b/docs/how-to/quick-test-stack.md new file mode 100644 index 00000000..0d1e39ac --- /dev/null +++ b/docs/how-to/quick-test-stack.md @@ -0,0 +1,59 @@ +--- +comments: true +--- + +A quick and easy method for testing a running CDK stack, whether in validium or rollup mode, is by sending a zero-value transaction and examining the result. + +First inspect the logs to find the `zkevm-node-rpc` details. For example, in the Kurtosis stack you should see something like: + + ```sh + zkevm-node-rpc-001 http-rpc: 8123/tcp -> http://127.0.0.1:32803 + ``` + +## Send transaction with cast + +In a terminal window, run the following command where the mnemonic is used just for testing and the address can be any valid account address. + +```sh +cast send --legacy --mnemonic 'code code code code code code code code code code code quality' --value 0 --gas-price 0 --rpc-url http://127.0.0.1:8123 0x0bb7AA0b4FdC2D2862c088424260e99ed6299148 +``` + +You should see something like this as output: + +```sh +blockHash 0x5d6d45f46e54c5d0890dd8a4ede989dc8042d7d3aeada375ea11d2e77c91a298 +blockNumber 1 +contractAddress +cumulativeGasUsed 21000 +effectiveGasPrice 0 +from 0x85dA99c8a7C2C95964c8EfD687E95E632Fc533D6 +gasUsed 21000 +logs [] +logsBloom 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +root 0x97b15537641583db08f1e3db15cb1e89212ed8d147670a11f93f368d5960e72f +status 1 +transactionHash 0xd5443cff8dcc1147ead09d978d3abe9179615aa3eecbe4819c6768390bc467a3 +transactionIndex 0 +type 0 +to 0x66ec…89fd +``` + +Status `1` signifies a successful transaction. + +## Send transaction with MetaMask + +1. Add the network details manually to MetaMask and accept the defaults and auto-populating fields. + + ![MetaMask manual network entry](../img/how-to/manual-network-entry.png) + +2. In local/test setup you will find a pre-funded account with private key: `0x12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625`. Import the account into MetaMask. + +
+ ![Import pre-funded account](../img/how-to/account.png){ width=45% } +
+ +3. Send a transaction to another MetaMask account. + + ![Transaction sending success](../img/how-to/tx-success.png) + +
\ No newline at end of file diff --git a/docs/img/how-to/account.png b/docs/img/how-to/account.png new file mode 100644 index 0000000000000000000000000000000000000000..2af19c34079a69e4dd846e43df9935ecccea287a GIT binary patch literal 117688 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%0DRQf)OxgS9d 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%0Afv{>)_t$?cC+?o+Yqtk=i0QLO zq{8jdF&?R=ZqJvl6xKvR|&eyRPE+#xT29_u#tq)Z0T{ z-NAHX--G?>tgHBL-IBikp0{}g7T?;@-#x*x+_#Gm(0 z*emVv2hwkix@suCpW)`}xjetvMmI<*h$!VWSufj6vrp8B-1oh^wp2!&(XlsIzpm4t z@;(@*c%FN8R51R8bsHOI>vG!t0Ja4-Y0kkH#2U%I>?d9s{!{l?H*s6L*bZ!1{q8b$ zPa_OGgK4IX2u>qnGy+ko)V``GT?xsy^GwVnh#1(+mFK3T2dF{dbJ~MbfPer^_eS?{ zq27sBW46Pa=z>Z@WJEHfSf73LlB2Fr~dr5EXN2&p|kMr}YL-8ii8R{mJrvJy@JGOVyJ@LM=ZEIrN z$;7s8+qP}no>;$VV%xTDXR>pj|2ePVJl)T~*VWalySi)Dr&bk-{YYBrM4G)$#c_;c zmrowOAbuw@ip!b#Ls6-cTqiJJBQ;aiWwrhznV7Tw`RxO^(LqO@AKA|+1N8Q+^~+yz zsBc^3iRx^#H%~@dw{P#~vb=k(_I^HRVPuY3iE6AqTyX%5t*@yOGPSL$O`A}GK$kyi z)>C_8*-3AU6g};GaZhK|Pn=fbjv-FEi~F5vO(=;j`je@xtt@Oy{DC6r8Q-z!)~$A$ zdO3;MN@Ofhr2+f6C{7dCY4?<#R(7t=!b@|(4#Pxhl*MZP2ftyXIqWo(QFB=-Umi%u z+hbg{Nu~Y>Wv6W{?ku_7ADV_CSE6ag+fxGEhwe)+SdOd;e%h=ajyb~Sd)yZ4btjcE z4SBwSRQ9z~KFoDGIl)G!(b7yV#wyUuHt0&$Z$JQVKhf1~luefuDGLK-8RxGYpubjE zQOA8fZ~T$|cM+-TX}LxsMTzX39|=_g65k33?~Sx>dEK^QNg-djRE?GsjD{@a_)kXz zQ&fNbdy~cALpS;MsLsDdCVzS@u%4D3zMCJ19^I5V5(rX&j~?@JW`L)@cp_pGOotPB z=f?S9L!_P#4ce5MGYJ~w7ZQ%zj$Qha(E|)tgHm0ZKd0)O4aMML(xExj78%6UC>*15li0 z0!+_5@uALS?~S8@ElUdHn=}$u;7=@uY>==q3-$QSw$kS$pT@A8K4@wNj=r7fnX|z@ z#vaTGBQ05%KR)~U!;{30K8u#tQywXU8-(S@Z{+hrK8c7560BGzt|jKHMD}c$JwSRi z82x4v3oWgEd8!#`wN;uq!w99QP#m%DM1Flj$alK%N|E9lMsR8R5c?#{VuW>3K*OenRNU(|pz#KCdT;`sH4Fh3YGjhe2T6wuoK;>MeKOQCNEyoi0VAp$(gsH; z0c1HIT{s!h!6I!T8$xx^`v4*bX%d0auQVHWC5!(DC?OF9j=gyIk5Je{gZvL5f&2%M z0%L`uME(bm{>vS$kx1;L{tqDi1oqxw>@ZX){{u*LKR$0BO%y5rKY)bvA3$2AERpyh zKx+LDAkhVp^8XJY3H%3;42ly-{$GIfV~TGpRMPAJz3~5C>iZ`@yrQ{6vQc5Rj2faHfuPSL_`#eUIE0P1y|#evnfhShZZihxCVc0v86(u@VJ| z&_?J&!BQqFQ6@%#5(1KG?&P zFoBc=97Bf8$nfF&GSSsehyrPX@;s_f3@wg<{SaiXyylLhs%`^}<}exU_Cs z=eFGP$A$_hRxO6lj4dZ0XhYW{UwIuerNER9E=)m!l`IsZT$l@WCJ~wcu{Oq(6l9F> z@&64s1DtQ1(Ra>Vf`ItZnBYT<7Pvb&<5tCM%2MrD4+-Vw#493Iqlu0&4%~sCg7#~> zAi=eQ9lLgB-+5ujkEpMmaWCz0ZlCc7H5lA0u@fGdQ4$V=aG*hq1PKXLieyoawQ5Kj z*0`uPlCVjQ-@n0R3efk2TINA6N0&@TgcbnSD*d41RUrkMvWvN-GFNLc{M;GPL zPSm5xDh4^mP8x(Lr-Y0IchMJ9W=7i~e z&y2ME_Tt2Gw2_B;4m98ppxCwZNYNt?Sxmzq8(WkXZ96Woo zu6X|?-uHSAN|LLRQIcAAN|vc;By-y)vqGq=AhX?#uv1u_=3XVkM&}kiVgAq{7j-XZ zNIUA0k|Pc+`k8}esh4G?o!3JoNdMOqleLW(V2suF*ts!D(6sS;_o>q|#vB8brT)HW z`*J`By~Mi<>d|v@N2AKv;DKmDToTNy@~7>yY;@W%rClbQZ2Xpc^Y-SP8K3*qlTh7? zbs?1;FYGM(pv%u@7lDA#Iq>dHV+siTbAWJ{WMABRXrov(H1fWLbgGILUSyrAf#kV- zYQROS`wBEqu4W@A27H2zaAgtoDdb`+030YMWWb;b^;Xr$kCkFHI{9heBUa>BHycu~ z&bH-UYc}7CjZ1uP>ue4I#2=g*eh#d#pXzd?T6MxJ!i1YyQ=$^;A&@^I_9~IdE=eA- zobSun;C=6gR=-v4?W|k!uI{=v4@wy^X1kJO!J!0pf$fOk;=v<{)G4-+=*H#M%1SHL z%_|yav4d{Y-n^@mc)V((%{poxkL+Gj3Dnbr)goVe30e7q#RT-jk0B;JF$c;lNr%KV z5RMJAz#rDZ>0NNRn5LbTTeI^Rmp* zB8~IV1LfJ!;cuGiEFXRxJOi#;7K`L0#=GKdDk}yC85Cg=7L9IWnD_IPRM9JvBE}pS zUAR(K7W;+(>mnP&KfsPsu}G75=gd`TeCL8Tt)(ZN5IVv8gG489;( ziL2jb_Pr6A=dYqo86S^wKN5YyGJ!mgaLg@5@@W+KJZIVf3(q`C$0BV-U}PBdd)dhP z(p|U#Va-_%f{@%B&OYXlHP=NoidmB(GdyaN7B)JXYul*$%U|S#GIlH`NoY5_deGj5 zJBaZ6b%cXE9w57OVa_wTy+GGmMZoba4a`qbIH^lO8y{|ot1YjK!B7TYssiTwh*$QT zg3PvQUfdc&kJramz7y43T<{|vaPAUp?G|>`Hh*jvu0gU$Jv2VbLfLOr-8$mhF*V1$ z>*(}uIt^harv)>RDSyY<$5qc19V?MGKhZG9Nntr7`GH72zk0&IWMzN)V$g{dnmust z%m=en{-wrIGp&&;gVb>E-96@fYjpE4(y@JUXRujx=-jc1Ku7&pFGlA zPsEIa76b7s!FZ|cWaja@$0C>j0=E~hVQOpW56~+wv@}~*SFCOx@M-baWt9y+Ydtau z2+Zj%;}mF~GkzRS251AV6vTaQ8U_iR!Kor`{sn{^gYwdin-eowHZqy2_#`=)&49s` z*T1X>pLkr1xO(7>jdM`E-|_uvv=s~cdsT>Ux`c+*uRyr%h&nhPtwk>$vBC2&CqB4% z)W9<6C~4iBSAflvs{^$5e~m7=V%%*tsAVkEpG$v80l`MKnq8?^amKc8OsnFfbV4Ud zOCXFSf}fy@M84H4JBCj|tA+@K#I3h{>|Xl0M{C6^+kYA-T!mctXxYrR|DleAP*Nh@ zgQ5IOt(MDASF0Lwq(-J4Adh46w>PMRv-FkkzRKE)24LkHPJ0X0;|8!M_Do&aH_Wc; zEsupJGXCwmeaTw#uJ2Rh*bUT6t^SuouNMpt4fa!MTEv*hnrT@Yfms?rEV(=P$>!Rg zqXo%(qsR2MXRxy7)+RD?)f58D7wrxg5BbYdtGBwQAsW0!jILiJVzqSALDU}G3GsBz zw>;Z6-D^t&y^z@caWOIv<#H_7Eni7Q)Y$mS?8PQJy?fT?ZJ_6w&sWcG!a@(s@^6U# z)#Dw6PNt*NA1OTOjq2F$>jSztztspZ+FZNE41~BOAX9@r6UPrz5Po*7aLAdd4uDx< zJo|r$#LxE2>UBvL7&y8LIR0;!+iv*>&xHXvFxL%4 zf3GSs2~UYfOsG_{`bn8`EKCcY^43(SACkiUZDE=RxRBQWjCcXYKMVOV3WAVdr7b!dhvh9g{;9HY@vM*m8g>n| za`TznrHX?Rl5O&7anyR*rf|9(24 zK&5P(z38?d4{op1qUJcdEUuoJ$}MO8a~LK~g07Fal>zFHY097^VNU7=ks1B2O9o9< zXIcn6wObD*r?huod=j3U#2?JezmC@7bf!(SZS{z6TlK!m;hU2M?5Y$u4)_&LfJcj_ zKyf!l4)^0>ATEuVgefC4a`G?R{55-?Hr!{CuG$g10nC11npTTUMb7NrC1SdVfxiEu zR1)LQjrO*q9ev&6e-;=2uv)iuo?v%Y>{Ya!oH@lxJ%aON&F)TEGkl7O#eY1zUD;IT zy)?)A{fV7(<0NDUlm7wlW$pN`$NSr%U{0=q!!QUq(Sal?e@)li`)fY{RXQ`iM_6E_%RA_RB7L19IWx4X-0ZsK7rQqhe_v z7z7Kv_{>GGA)sNC#r$Jhcc4Mpt&0kn!dt*bUHX$V0is;WnJ&tKtLVP@W@zI0#cDE# zCt9N04%iyorSe$s#@?nMYeX;3gMA%6Jm+rWGF?27ge(iggN|6D~mu#YvXyFXV@L3mC|rQk!9G zA{C@j4umE*?%$)H{0DJzR$a#x&%V0*EnS2pLrr7w2n*I_g2yV0R#;)!UyS0hv3t9; z>O&r%ugovwBgl2oFpHUGvy_VfkYUZAh88d<+Lxoz-MQ(5PUT|y5Dxljr*M-38M= zFUl()U*3T|GLqwY2eUL4U^I^-$_+T+bk(iH9f{o4C6Wmjix0nyZA!V*8Vi_kFPbxY zo@LaARra5R)Ib%H23LeZmN_n*Y=P8m42cS^nd4IkW6x$7J{)7|r8v!bPFKi8_}#gF z<abU`M_Kr>)3z8yej5av>|c3jE+Vyp>deHU3h zWJ-6Gt#STFlV;H(l3#+Cxd`7aPOLq(%^sa@hDAx*-*S;M5y<}w{q|sKU*yl~(NnHP za5mfYE2#`fpUNTZ^m_e`c8979Lw{qsyn@Yiy2fPF0ky>)8A z`jP)z0d+6fLMkI`kO{5{vI%cFV zk>ocElsoFHo@bnklc*<_x|!G*E(Tv`4D^7JX?mX6*qXZ}Ck1mDSC}xef3S(korsgi z3dg;#=;s(QV2sS9-H8L}lL)|cQsk6z>E+@Rw+$dZ3!v-+r;LW9sCD&q5xLC-otXN8 z8TkWI74W>x?$11x)&%k?22})o1^8jX-jG{smIs|f1a>dh8M~f-fAxtDCsO7$*wlpa z)CVt0AW}0fW}(p2col#s#$BeimTratqD$s@=EnjkN!(|<@yrKkAcrjCqh ze$1r{^t1A2qm1iX4s%`M59KAJ&=7s|!afQ^8T>hTi@LurSxpm{a?&cH(AX%$o*r0p zhX^|w_?(r%w$Sc4KD5yVe~aA5i6`#3B=LYbYx{@F>ot}mUT>j6Ikpp~iSFZKVj^ZS zf1Fq{2}W;Zx|N4s#~I!uL-P0EAj1IRGe+@?`uW+m{Q(4yj?nt%nh;rz9J2_JXKLjM zdD8urwbPC!Uw`hc5qN7AKhhn*s_fc5Vjb&ta`iwWUOjO;C{9&nDLxdL!Hhc~nMYUD zF=uDeQY1%F;Svs!GZPpx7Li&|lwT32f^ypsl8lrp_PtPFBSY(F9)DRUvl|^14bi8r&FMAuf4ELMWqO5=e9>Z1HBQNs`yI|n7G7Q_%`O`zZ z0Rw@cDW+Ce=6|5zih{%hB0jZSj$H$N0MWe~d``L$EfQKu6*LOUUCheX4bQTvJ%i5- zEFn|oXKWFQ%UJ;r#GE0*54UN5;qHyugQ`&N^Gs-n3gv`mz_{Z&r3aI)V$s_x^T2T^ z_wW22h`SKOO)LiG>VF2{qFprP_JVcRiGtKHTb$03U@|}rxz|H(99a(guuf@mv>Ite zi=&*4`!@Pz4vs;fXfi1LSxW^NM;}M*S*X&2WQKCoz;sxqea>DnLZpB`0(Hi|;&+Ws z8Q!|kT8b(NyMCY`N9jhKd|cFiK0ddh!lY}**V;9o*9vg<3Lt@7P;ce(R|ETfez)U$ zL!yvrzuJ7ESiP+yyOs|#PK^Gv@P%QIPsU8@DMf#`Qea5OhHk^kR+ks=dOZZpRC(Rg zCp06%xCC#`jN$GkZ(qUA`#$i)b|8=5KV9`hU_3Z!%6}caZE0UngLo?@A;)5xTn=js z*vZAnX{A+6hDM31jLLEIthn);LBt8#BLW=^zk@Zwe1QneTJ!Do?9U1M@cc{KRT{96(Mp5$Z4@czy@-P+tMb zc3@pwppR94?xS>nX6MZA(DZ??TK8NI50B+5Ko4~w!m0;4Ej-Xf>|V`uBnuLqA3W@! ztJChwD$E2q1it$S1ftRabJxulgrTTDM0=Gt9AM_OiyFU>F|b7>wIt#Ae^H{WKO(ziI_U+-o99~0lYzW z)}~vX(if4r$5{FBA&BDJ1QrTGe(!g3k+*$&H0d&68rniVN!!KHYEcbBCR%&zzA~bY z;`EE=2Zt$&5Wyf0r@Sgz2=cbQhp`{KRCkYXH3xkDYIhC(&L1{JQF z-(@*E!94Ot!@fI~)c?}+`eq)e0_V0Uoo?g#=Tnaw|JOhPDx?rOk5QO^#=7{<7R)YY z^A%R>mjzJo`W__CvfgSw=g!$6htr6BC~+n%q9bsKOD+~4qqr#U6x_=%O{0I2x*r4SKImH9xzz90cju=LS`o$Ue&PKi~s{x}YngNEK*og+oX zR~$|>0PPaU6$&MvhEAAnF74ko0sj#YUE#kU4CUV20Hf#5ucBKo##-@4xu%u9pHm!D zX5o$K!*HmgLOg-G^;=PhcLpVbk1@)-O@cz4!M5klZ5n-Nyd_Ay(&2H{snD=7_oh_T zuU;XOejP&yws0razT9x&nc!Rf2=X_lm=6wy!suUN53BqGlL06rm!LhJQEVG&IM)*k zutT5YY8%Sl8{c)y|Bi&FWKhKLFb^D!>L($g6nun+MacSTcY{s?EN59~TV)%RBv98g zJ~cGOV$ewXLlHhQ-G0)q70aYlV6<_&upIT8d;?{fMy!)oYHf%lL!_cOQuGpS0R^Oi z8>&H6D%E4UTUR07@NlRKreM8?kgx_m5ID;N39bf>e{ZV5AuboP{uKUo>hpi^(tSO| z;J2TWTwobX%C5ju_~+Ld-y><>I*@ifd?gz)<7Q<~+-O)pO`(7-z$+*(>hJ29Ke!KPzzMS3 z%(zL`c|aWJV07q~VsMX)> zJ?=*z2ObEbXyr;ImVtD(Rl=bBX+gj+7{ND~7r|*(^3uLgu?`Kosw>xCX6=#~q-;Qt zb$4X%UI(xa@WLc7?XH4j_6XrDQcR`t>3rV=Kx%ZveegbpQTRmFmHSpQ8K(*J0!{?z z&o6YmP$h0`+Gu<`eYYB&Uznz1(oBkd^X}*4gp<~bGFSZ;Rykvb}p^j@^pYMhg=iHi-aIFQ++Jc zC0#INwA4R7E-YZ!cu%Bd-5;?}= z3#a>%MuB@EhZ|lWZkQ0pDoX%i^TbzPi=0^?@I4m1w z!EIyWAE;RAY$q9Wum}obcGulr_u#wt%n~I{mS^9by~Wjz^FY`hFJBHVMUCRtzCZmo z^t17hR1F%Y!87i_(T=TkivKCpMLHN54wu5CBSQ#feBK(?`SHkveR6j6JMN^g2!2D; zQrL0AoC4+0B3Hp2$f->ea!=eJn`9KVFV z!YANXtA_SQP@l@`{w{1oS>v(!<4ne72`Hk?Vo#PRaWKn?B=d;e8*2XIh3Ydz{EK=^ z9n2qInNlorNi`O7>y(O0<34NSn*LxDK5%Kur~*QV!Y%0lHQfJ-4@fLb+(Lt8XI}2w za$d)}ba`9rjAo6ei`DHG7{P=si1pkP;TXB37o*Q?;aCKat>4bp2csOJF^#B1EQ@pW zwmAWTmrNvFg@!BC3TN-kP(1?Zk(o6{iwa5cX29MxMuD6^5@@zfYLOFuMTH#Ta91*F z(ggCrPs5SdKd^?ag->7%qE3Khtb@?Uz(PsRehWs0LknS@QD7m5X604<&bgDZZ_mX1 zE^{>u96qU&RQfb2M&@TB+Ue3>dr@kEZ)LI*&uGKr)qba+`m}bhyD^D&< z9fJF5y8392!&x+jFO2#Hrz%_r|6Cqfh3#W~W{Bt1@r<9sF=MW!CO|M~@>Ahy0$C z@aGA1%;by_cy!_C7Gyy(-C!K5-Sr6whLA!0!HQS#v;qkpuL$hq=?0Po#~BDV#`77& z@^;CfPcYgNUEorhR*sEwHHpx7f>JExbc+tmu>=L7rTk@JxoA78hl0rQ*dW@f#&ls)N(!fzhQmaD-fWdB)9DWM#;&`owxc%T)0&|>&#FUt1tqiMnE zk#;fi*Gt|LW*d)X9D4B#-1zUfknfpoZ=hAz6!Y)47r(X_>OXGk-5lC!bR1F(urjly*)s59x-fcVG-T(Yg@|KoT zTP@Vr%etKztDR>uSy*mZSZ2B&rg>P7J6DdbtDH4tvV^aCZg^MdrwYp088h6r@RN}` zEsUOxSQ$As#eV0UACy~U8jRCLV*ZR>HIxRRXM0>I*eGZP@=TvBkUUl!6kyJ=5dKCN zepS_%d!PV|dd(0Ze~XKX@puiet7~37#vViaKwIZzhE5N(c8!8_CUbu^dbk&2Nk%H? z?fd%k>m+PHYfQY(0@nq%iQ%7VWvW!NVDarxrJvFbU#PTJxY2BvdFsV>D&T7SblceX z_vS`UBc)usqNBefQ?sr?xuQiW7ZqD&OzY*Kuz8&0G>a;;CDs3v= zz?-a7_zGXw{w1hY+u@rf%HR~z?Bjg|i)6GeV4qan&&D^_D&5QD#1|~ACA&PAfXrN2 zh1;1XT1IZHVn*bde7`XA-zPaAqT%Bl7EY>o%p+dD}oHT{a66JVA0;yZL&^r1RsISEeV!yj_c+TDm1h;F2+<9cTC#D7 zzr5@F&gE@etcVTe?@Qz?rKQU2`@`qdG;GTigDPri7S1}=@mmjcC`e9(16Lfjl@k$8 zUA&BYr-mn@f#X25H@WxjL+}bkwKW$)?UN4g&}>iwME%EqR{Ap>`nW0@s_a`CmL$37 zn+A)ktR-sI+#gCK0Wz}0N$bI?#%)q^!O6!KW_m?_PC`)C^S8J-Z3>4bqiDHKm7+~m zqbkYP{dk+aNZgy4{r2ONMuSxKNP-numw+(3yjAiy5PpNze#5GaCv&mUNaMoqdTf=x z3>Sn&5geMeW$P5XCLFnR<02CS&veO!0hi=K{eyHg<&?|UeCwssl{FGHZ)!%(q4%Ch z7P9WXx0q|&dq$R5HSVHb%h~5p$CY#$p_LA-8_=MoDpS~kW4WCby&Ikte8<(L%G_YF z-)T@^5Ehzb?(lNCW4tFD#(L5|+YByF?$|ApiBi-PEz-)&x;pE~lS0D1I88T2h@d3F zF3>PzpOjx8X6i(1spqw&`o;`cs#dE97~E4}!QOG2xy)!WUv6Rfv&87!@EbMf>Rt=h zXXJ7ud92uMszYV&wNrF6EciB0UoWuuJN&FAu2MxYu0RI<4Zfg>zF%MSSm0Bp)aMD! zO-ap82Wr{uJAtC!Fs00ne&w$aA3)ty;unS8a!QD^@^mAQE!NO@I z7ZyjgzG?3$jVn&(-UH994Tse_da7D_6`7Xm3TpX$h3-`*#31i&aw+Iy|03ZpBHk5AKF@#&CxOPG0Ze7RgLHVH zE*Ny0)+U~lz?<$1{H4{8S}2d#C7XEgN+rwwFLo-fSYb+9)2A zira_`44i!s{{RVm^~WbL0!Q_3jbB<@SW6-4LI{haS;06}N6W&dDlg9J#%)!jZ?v58 zT74yU-Ca&C&6G>;s>dSu>w1imi$V85k1seDo(m&FPqBm;p@myzS9v@g=f!nRilhMhKXp%f-ayCZN{1Ny_pm?ICqG4MG&1^WIWuvoU(P^_% zb$5<=ZYW#BXcY5clwL0Ab5rTUJr42d?LY=uwk{78Ga2uW^y5Z^!jgzeg(EE^V=#|W z{NdH-diq(n>V_;+`QdX{u{ftQlKa&nGYZq=}90hxix&d&Ko=5d_C$|S+09D)V) zC1cJ6LVq&a8{0va6A)5>V3iXb46IdYbA&Mpw`t6NBq{GzVD}V%Oq15a>v9Ga1;3oY zDS98OnyhPH;#2KGNJgl5P2OaiMME=XP?G+W2wgV%2(7&LJ#X|<(q%e`Uas(7_Z1#( zpE%l<$c}EJ@hhBMFv7?=*qK9PABZ}38t+WsUgm4>p|*=!t`~~E#G~SvPtDtVL5$6A?T<`pi6 z1``gAJ1#moI!MqkX9q$oX%;6l()lT3YWnJS$N1FzCd-#0FHu<{U$bA=KjX-<+TH7( zTh7Xo{UkW|X0d1tGxzB|D6{h*S_>y}xH7?slI~R0uR`c}jpVDa%bd1y&ybH};8x?z z=Qso9K)5FIPW2Pb7F3S3N)eVTMu_U#j`jH&m>Nl2@Ih%&li%Bm!z0T3jb~<|z$!g* zsydc)na6ez(18wz4%x1JePH}7$xY3Ye|(x)uwuwL)U{U9G!^Oc`A(jKwxP6tdEG3+V$d>%`hER66XJ70>owQ?p zZ4*6Fx^JW6S^SWg!~pr-+ua}Fh&-x=3o*vU(PYt=>#nNoV-y~Oda${h@Z3j@I>95j zc28t3k6Q<9T<6?u1nyg9z+o9S48N}Uh?Y9;K=k#g7^TYPim}o*np5ZJGpenZoZ;qbyugsq|Pvwnn z*&NoQCa5Rs2{q1KHZY3%azE)koK`BN zayo>nk3Z|1-GH@l+eoAkQ(@9l?-^?Cl>}wR1C#>JbV%u_6ufG#xZ}#Qr^{K8S|>|) zbPTn#tG#QZWeJj3_7ZqNGZji&5tb`)oSrC$LgN4nn`6AY={!$)i4wTsBJy3-;f7b`PcHmK zmN*#Uxl6~Va0`vDG8Czj{mb>t4f90~XBBY=t>Azs3XleW@8a*BXLw3R%l#03fDo&l z7t*JM2Dh2IFon~T=sZktRnKgFWq;Z;NjLYb&$r&`Ap$>K;KXLxy1;(FfQ6HEn@(*L zFx7gLXmQmPOGD(Y&ERj(9EFIDGVPsRyJ*CmYq)|Y83Mo7?J(2{sma)hO?qjo(irnvLgwYm@ z@hh(;`iMxU4zE+z(j3P25^hOYWmQiz-BfG2k5-pFq8gp@yo8TK>S;F)bO{;4i_aB+u`F@U(wL_2MN8Z{GA*TC!m9~VT zQr;|}`QY@Ga{n1@(`~B7aw6P_mj6|CfiONeE53YU^_A#v0jujy%5iTv-mUvo$i30I za=d!%fJ4VJ^QNY2T)QWN7{6{aJc z*!(jnSU+Pc{RHbwT@qJbKc3k^nIZk?}fEvAsTdetk36n#~t@?OcaGwnw5^Q zr?e^QD~++);*(}L`ohsycg;ZJk&?i*T20uBwOQ_;U%%>*WFkUnq=+0tR+DleLnQr| z^$>!B0c9g|bgvpqlA{?tgUt-(mLI0$3`Um zD?Tpy((d~|-1N0IS9bYwuN9b&VJmHd4Px3|>oTi)>K534=QwDIwUSQKbBHaCkF|q?*=&TiTiUyOi~IhNkI) zYZLFcT~%M{i1Q-@)qcFhxlP3c&KH*y+b`@C5xKpJX}05RR%ZtOS|b-~Halakjg?t* zGZPzn`y-bI8C$L6_fwc~AuUJ7Ha6lc9c%UPv*RvacQ3kI9T3LY=0!x_+g{e78GVIj zQ7;?T6A9M2{aA_h16up`aYnpwL$tJ5(X=B`vtvbyw$~Y8c8u=z(UMxlDPBK&`9%Yj zDc~{jk0NX77{VPr2kqRZQKq+`70nSKYoKLwvuSNExn#@hwHNIqywZ|w+521jQKn6$ zNN+Nn&(8$hmb^e%Y^yyu;seyG1sQ&7j*gpu z$?mJlNO?@>s)7ZX$i?1*D@L!ou)SH!lDrI+Tq^pc@wsB9d$x)pl zpOpVdlU2M_;i%mLJ|-egw0w~2R*>KQ9w`?|-q1Av+7k{(S>gQkZdiY)0a4 z60o-RGF`KR@K)SINds$Enj|@t_U`Xn>nI!wPZ8_M)Do|4_Q*uL(!2^!u6hrM1EP~< z4~xTn6(f*hsh~EERQxjEfHipLwxr1Xnc!h729Lng-Gf!0A$8I;iL)}g#p|23QL(0G zm&l&21B8{as^3>Y05j;LSm~Y3w@@{CslwH%M{`veYs$=KrU`^4p^D2+X>e6LU=T6% z>;geWjwfn9phw;UUb>IbcJ14SwxxDM-!}8`s*2(Vvmz^YEfI3uPhRSIiBqiN_G&z@ zLx}cUcyItqC#zOOaM`jgHB$}KI95|&GLK#%T`(2~z_sJxMR|S~f=a6+gAYZ2w;8Kj zJRr}m6-DW`tkKh4ZE>PR0qbT4wTnBkSz1So*_udQZ?T3^Jwcd^F#pS+Wqn~{8B5Hb z_T`Vuxx0#HH2ypIK2G54VaOWqDv0HJ*P%lY?|>w*#<7X&EJ2 zw(BM*u?*9nOt`TA`GZ)!Q^bZ$v1*QfGkKfDM62DEgGX{I3q0Y;LhDGAT1D-nMb%FD zQaQd2Lx%QqA8670&8yhF8!S(N0ls;7y9z`6iSTTkDO$;z~c3q~Rp#LO9hQs7P&VFL#z9+6UJa%CRz zP+0$>Y-;=QlvV2ZOtCmwu+AcAyfk4PD*xt1$@j1xrVJS08tz~`OB!BFi#Z=F+8Hp%tnmb{Nwq>eS1$R;;09`eW_zy;p@dqGOv(abhI)Z@VU|la?2} z58J55@(d?3=L|j{Kf(iKz5_zVqgmE6CyyB!g;RIss0tDM_V>G4TB$^7rBbSP3+Js< zNn`Rslj}qMuX$HY3NdJV#^v8AYrfkbN5#P>({G$28ii@>5GjLc&#h_fBcnB&i^i5u zkVy~z3IY$2O`SnzvMo;x>fCK=T3Z>+v3RpCxluE&6HfSqN`9A@i|ly(bNJlbxG