Commit d5baafd7 authored by Matt Solomon's avatar Matt Solomon Committed by GitHub

Kontrol documentation and cleanup (#9254)

* docs: change directory structure format

* build: add kontrol installation commands

* chore: simpler kprove profile

* chore: update help text in run-kontrol.sh

* docs: refactor and clarify README content

* chore: fix typo in test name

* ci: check kontrol state diff in snapshot checks

* feat: cleanup even when forge script fails

* perf: improve assumptions and better document them

* doc: more docs

* doc: update description of make-summary-deployment.sh
Co-authored-by: default avatarJuan C. <38925412+JuanCoRo@users.noreply.github.com>

* perf: revert back to the 320 byte assumption for now

* doc: clarify optionality of build step

* fix: add output directory to shellcheck ignored dirs

* fix/refactor: snapshots job needs kontrol via docker in CI

* fix: shellcheck lints

* docs: fix links and add brief summary of adding new proofs

* fix: remove shift, which cause early exit

we only have 1 input param so don't need shift: when shift fails
it would cause the script to exit early due to set -e

* fix: run in proper environment

* fix: start docker when needed

* chore: cleanup docker

* fix: another fix from shell script refactor

* ci: add docker to bedrock tests

* fix(ci): move remote docker from contracts-bedrock-tests to contracts-bedrock-checks

* ci: maybe docker setup needs to come earlier

* fix: various changes so make-summary-deployment works in docker

- avoid copying node_modules to container, which caused a symlink error
- fix which script commands are run in docker
- restore order of setup_remote_docker step

* fix: bind mount instead of copy into docker

* style: remove unneded braces

* fix: -v to create dir on docker, restore file copying

* fix: copy new files

* fix permissions error

---------
Co-authored-by: default avatarJuan C. <38925412+JuanCoRo@users.noreply.github.com>
parent 592daa70
...@@ -397,6 +397,8 @@ jobs: ...@@ -397,6 +397,8 @@ jobs:
- attach_workspace: { at: "." } - attach_workspace: { at: "." }
- check-changed: - check-changed:
patterns: contracts-bedrock,op-node patterns: contracts-bedrock,op-node
- setup_remote_docker:
docker_layer_caching: true
# populate node modules from the cache # populate node modules from the cache
- run: - run:
name: Install dependencies name: Install dependencies
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
"lint:ts:check": "npx nx run-many --target=lint:ts:check", "lint:ts:check": "npx nx run-many --target=lint:ts:check",
"lint:check": "npx nx run-many --target=lint:check", "lint:check": "npx nx run-many --target=lint:check",
"lint:fix": "npx nx run-many --target=lint:fix", "lint:fix": "npx nx run-many --target=lint:fix",
"lint:shellcheck": "find . -type f -name '*.sh' -not -path '*/node_modules/*' -not -path './packages/contracts-bedrock/lib/*' -not -path './.husky/_/husky.sh' -exec sh -c 'echo \"Checking $1\"; shellcheck \"$1\"' _ {} \\;", "lint:shellcheck": "find . -type f -name '*.sh' -not -path '*/node_modules/*' -not -path './packages/contracts-bedrock/lib/*' -not -path './packages/contracts-bedrock/kout*/*' -not -path './.husky/_/husky.sh' -exec sh -c 'echo \"Checking $1\"; shellcheck \"$1\"' _ {} \\;",
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
"ready": "pnpm lint && pnpm test", "ready": "pnpm lint && pnpm test",
"prepare": "husky install", "prepare": "husky install",
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
"release:version": "changeset version && pnpm install --lockfile-only", "release:version": "changeset version && pnpm install --lockfile-only",
"install:foundry": "curl -L https://foundry.paradigm.xyz | bash && pnpm update:foundry", "install:foundry": "curl -L https://foundry.paradigm.xyz | bash && pnpm update:foundry",
"update:foundry": "bash ./ops/scripts/install-foundry.sh", "update:foundry": "bash ./ops/scripts/install-foundry.sh",
"install:kontrol": "curl -L https://kframework.org/install | bash && pnpm update:kontrol",
"update:kontrol": "kup install kontrol --version v$(jq -r .kontrol < versions.json)",
"install:abigen": "go install github.com/ethereum/go-ethereum/cmd/abigen@$(jq -r .abigen < versions.json)", "install:abigen": "go install github.com/ethereum/go-ethereum/cmd/abigen@$(jq -r .abigen < versions.json)",
"print:abigen": "abigen --version | sed -e 's/[^0-9]/ /g' -e 's/^ *//g' -e 's/ *$//g' -e 's/ /./g' -e 's/^/v/'", "print:abigen": "abigen --version | sed -e 's/[^0-9]/ /g' -e 's/^ *//g' -e 's/ *$//g' -e 's/ /./g' -e 's/^/v/'",
"check:abigen": "[[ $(pnpm -s print:abigen) = $(cat versions.json | jq -r '.abigen') ]] && echo '✓ abigen versions match' || (echo '✗ abigen version mismatch. Run `pnpm upgrade:abigen` to upgrade.' && exit 1)", "check:abigen": "[[ $(pnpm -s print:abigen) = $(cat versions.json | jq -r '.abigen') ]] && echo '✓ abigen versions match' || (echo '✗ abigen version mismatch. Run `pnpm upgrade:abigen` to upgrade.' && exit 1)",
......
...@@ -86,4 +86,4 @@ script = 'scripts-kontrol' ...@@ -86,4 +86,4 @@ script = 'scripts-kontrol'
src = 'test/kontrol/proofs' src = 'test/kontrol/proofs'
out = 'kout-proofs' out = 'kout-proofs'
test = 'test/kontrol/proofs' test = 'test/kontrol/proofs'
script = 'scripts-kontrol' script = 'test/kontrol/proofs'
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
"gas-snapshot:no-build": "forge snapshot --match-contract GasBenchMark", "gas-snapshot:no-build": "forge snapshot --match-contract GasBenchMark",
"statediff": "./scripts/statediff.sh && git diff --exit-code", "statediff": "./scripts/statediff.sh && git diff --exit-code",
"gas-snapshot": "pnpm build:go-ffi && pnpm gas-snapshot:no-build", "gas-snapshot": "pnpm build:go-ffi && pnpm gas-snapshot:no-build",
"snapshots": "npx tsx scripts/generate-snapshots.ts", "snapshots": "npx tsx scripts/generate-snapshots.ts && ./test/kontrol/scripts/make-summary-deployment.sh",
"snapshots:check": "./scripts/check-snapshots.sh", "snapshots:check": "./scripts/check-snapshots.sh",
"slither": "./scripts/slither.sh", "slither": "./scripts/slither.sh",
"slither:check": "pnpm slither && git diff --exit-code", "slither:check": "pnpm slither && git diff --exit-code",
......
...@@ -33,7 +33,9 @@ contract L1CrossDomainMessengerKontrol is DeploymentSummary, KontrolUtils { ...@@ -33,7 +33,9 @@ contract L1CrossDomainMessengerKontrol is DeploymentSummary, KontrolUtils {
{ {
setUpInlined(); setUpInlined();
// ASSUME: conservative upper bound on the `_message` length // ASSUME: Conservative upper bound on the `_message` length. Most contract calls will have
// a message length less than 600 bytes. This assumption can be removed once Kontrol
// supports symbolic `bytes`: https://github.com/runtimeverification/kontrol/issues/272
bytes memory _message = freshBigBytes(600); bytes memory _message = freshBigBytes(600);
// Pause System // Pause System
......
...@@ -21,7 +21,7 @@ contract L1ERC721BridgeKontrol is DeploymentSummary, KontrolUtils { ...@@ -21,7 +21,7 @@ contract L1ERC721BridgeKontrol is DeploymentSummary, KontrolUtils {
/// TODO: Replace symbolic workarounds with the appropriate /// TODO: Replace symbolic workarounds with the appropriate
/// types once Kontrol supports symbolic `bytes` and `bytes[]` /// types once Kontrol supports symbolic `bytes` and `bytes[]`
/// Tracking issue: https://github.com/runtimeverification/kontrol/issues/272 /// Tracking issue: https://github.com/runtimeverification/kontrol/issues/272
function prove_finalizeBridgeERC21_paused( function prove_finalizeBridgeERC721_paused(
address _localToken, address _localToken,
address _remoteToken, address _remoteToken,
address _from, address _from,
...@@ -41,8 +41,10 @@ contract L1ERC721BridgeKontrol is DeploymentSummary, KontrolUtils { ...@@ -41,8 +41,10 @@ contract L1ERC721BridgeKontrol is DeploymentSummary, KontrolUtils {
bytes32(uint256(uint160(address(l1ERC721Bridge.otherBridge())))) bytes32(uint256(uint160(address(l1ERC721Bridge.otherBridge()))))
); );
// ASSUME: conservative upper bound on the `_extraData` length // ASSUME: Conservative upper bound on the `_extraData` length, since extra data is optional
bytes memory _extraData = freshBigBytes(320); // for convenience of off-chain tooling. This assumption can be removed once Kontrol
// supports symbolic `bytes`: https://github.com/runtimeverification/kontrol/issues/272
bytes memory _extraData = freshBigBytes(64);
// Pause Standard Bridge // Pause Standard Bridge
vm.prank(superchainConfig.guardian()); vm.prank(superchainConfig.guardian());
......
...@@ -41,8 +41,11 @@ contract L1StandardBridgeKontrol is DeploymentSummary, KontrolUtils { ...@@ -41,8 +41,11 @@ contract L1StandardBridgeKontrol is DeploymentSummary, KontrolUtils {
bytes32(uint256(uint160(address(l1standardBridge.otherBridge())))) bytes32(uint256(uint160(address(l1standardBridge.otherBridge()))))
); );
// ASSUME: conservative upper bound on the `_extraData` length // ASSUME: Upper bound on the `_extraData` length, since extra data is optional for
bytes memory _extraData = freshBigBytes(320); // for convenience of off-chain tooling, and should not affect execution This assumption
// can be removed once Kontrol supports symbolic `bytes`:
// https://github.com/runtimeverification/kontrol/issues/272
bytes memory _extraData = freshBigBytes(32);
// Pause Standard Bridge // Pause Standard Bridge
vm.prank(superchainConfig.guardian()); vm.prank(superchainConfig.guardian());
...@@ -71,8 +74,11 @@ contract L1StandardBridgeKontrol is DeploymentSummary, KontrolUtils { ...@@ -71,8 +74,11 @@ contract L1StandardBridgeKontrol is DeploymentSummary, KontrolUtils {
bytes32(uint256(uint160(address(l1standardBridge.otherBridge())))) bytes32(uint256(uint160(address(l1standardBridge.otherBridge()))))
); );
// ASSUME: conservative upper bound on the `_extraData` length // ASSUME: Upper bound on the `_extraData` length, since extra data is optional for
bytes memory _extraData = freshBigBytes(320); // for convenience of off-chain tooling, and should not affect execution This assumption
// can be removed once Kontrol supports symbolic `bytes`:
// https://github.com/runtimeverification/kontrol/issues/272
bytes memory _extraData = freshBigBytes(32);
// Pause Standard Bridge // Pause Standard Bridge
vm.prank(superchainConfig.guardian()); vm.prank(superchainConfig.guardian());
......
...@@ -42,7 +42,11 @@ contract OptimismPortalKontrol is DeploymentSummary, KontrolUtils { ...@@ -42,7 +42,11 @@ contract OptimismPortalKontrol is DeploymentSummary, KontrolUtils {
{ {
setUpInlined(); setUpInlined();
// ASSUME: conservative upper bound on the `_data` length // ASSUME: This bound on the `_data` length is too low, and should be at least 1000 bytes.
// Kontrol currently hangs and fails with this value because of the resulting configuration
// size. For now we leave this as a low value to avoid the hang, but it should be increased
// once Kontrol is improved and supports symbolic `bytes`:
// https://github.com/runtimeverification/kontrol/issues/272
bytes memory _data = freshBigBytes(320); bytes memory _data = freshBigBytes(320);
bytes[] memory _withdrawalProof = freshWithdrawalProof(); bytes[] memory _withdrawalProof = freshWithdrawalProof();
...@@ -74,7 +78,11 @@ contract OptimismPortalKontrol is DeploymentSummary, KontrolUtils { ...@@ -74,7 +78,11 @@ contract OptimismPortalKontrol is DeploymentSummary, KontrolUtils {
{ {
setUpInlined(); setUpInlined();
// ASSUME: conservative upper bound on the `_data` length // ASSUME: This bound on the `_data` length is too low, and should be at least 1000 bytes.
// Kontrol currently hangs and fails with this value because of the resulting configuration
// size. For now we leave this as a low value to avoid the hang, but it should be increased
// once Kontrol is improved and supports symbolic `bytes`:
// https://github.com/runtimeverification/kontrol/issues/272
bytes memory _data = freshBigBytes(320); bytes memory _data = freshBigBytes(320);
// Pause Optimism Portal // Pause Optimism Portal
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Vm } from "forge-std/Vm.sol"; import { Vm } from "forge-std/Vm.sol";
import { Types } from "src/libraries/Types.sol";
import { KontrolCheats } from "kontrol-cheatcodes/KontrolCheats.sol"; import { KontrolCheats } from "kontrol-cheatcodes/KontrolCheats.sol";
// The GhostBytes contracts are a workaround to create a symbolic bytes array. This is slow, but
// required until symbolic bytes are supported in Kontrol: https://github.com/runtimeverification/kontrol/issues/272
contract GhostBytes { contract GhostBytes {
bytes public ghostBytes; bytes public ghostBytes;
} }
...@@ -36,12 +37,9 @@ contract GhostBytes10 { ...@@ -36,12 +37,9 @@ contract GhostBytes10 {
} }
} }
/// @notice tests inheriting this contract cannot be run with forge /// @notice Tests inheriting this contract cannot be run with forge
abstract contract KontrolUtils is KontrolCheats { abstract contract KontrolUtils is KontrolCheats {
/// @dev we only care about the vm signature Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
// Cheat code address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D.
address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
Vm internal constant vm = Vm(VM_ADDRESS);
/// @dev Creates a fresh bytes with length greater than 31 /// @dev Creates a fresh bytes with length greater than 31
/// @param bytesLength: Length of the fresh bytes. Should be concrete /// @param bytesLength: Length of the fresh bytes. Should be concrete
...@@ -65,55 +63,18 @@ abstract contract KontrolUtils is KontrolCheats { ...@@ -65,55 +63,18 @@ abstract contract KontrolUtils is KontrolCheats {
sBytes = ghostBytes.ghostBytes(); sBytes = ghostBytes.ghostBytes();
} }
/// @dev Creates a bounded symbolic bytes[] memory representing a withdrawal proof /// @dev Creates a bounded symbolic bytes[] memory representing a withdrawal proof.
/// Each element is 17 * 32 = 544 bytes long, plus ~10% margin for RLP encoding: each element is 600 bytes
/// The length of the array to 10 or fewer elements
function freshWithdrawalProof() public returns (bytes[] memory withdrawalProof) { function freshWithdrawalProof() public returns (bytes[] memory withdrawalProof) {
// Assume arrayLength = 2 for faster proof speeds // ASSUME: Withdrawal proofs do not currently exceed 6 elements in length. This can be
// TODO: have the array length range between 0 and 10 elements // shrank to 2 for faster proof speeds during testing and development.
// TODO: Allow the array length range between 0 and 10 elements. This can be done once
// symbolic bytes are supported in Kontrol: https://github.com/runtimeverification/kontrol/issues/272
uint256 arrayLength = 6; uint256 arrayLength = 6;
withdrawalProof = new bytes[](arrayLength); withdrawalProof = new bytes[](arrayLength);
// Deploy ghost contract
// GhostBytes10 ghostBytes10 = new GhostBytes10();
// Make the storage of the ghost contract symbolic
// kevm.symbolicStorage(address(ghostBytes10));
// Each bytes element will have a length of 600
// uint256 bytesSlotValue = 600 * 2 + 1;
// Load the size encoding into the first slot of ghostBytes
// vm.store(address(ghostBytes10), bytes32(uint256(0)), bytes32(bytesSlotValue));
// vm.store(address(ghostBytes10), bytes32(uint256(1)), bytes32(bytesSlotValue));
// vm.store(address(ghostBytes10), bytes32(uint256(2)), bytes32(bytesSlotValue));
// vm.store(address(ghostBytes10), bytes32(uint256(3)), bytes32(bytesSlotValue));
// vm.store(address(ghostBytes10), bytes32(uint256(4)), bytes32(bytesSlotValue));
// vm.store(address(ghostBytes10), bytes32(uint256(5)), bytes32(bytesSlotValue));
// vm.store(address(ghostBytes10), bytes32(uint256(6)), bytes32(bytesSlotValue));
// vm.store(address(ghostBytes10), bytes32(uint256(7)), bytes32(bytesSlotValue));
// vm.store(address(ghostBytes10), bytes32(uint256(8)), bytes32(bytesSlotValue));
// vm.store(address(ghostBytes10), bytes32(uint256(9)), bytes32(bytesSlotValue));
// withdrawalProof = ghostBytes10.getGhostBytesArray();
// Second approach
// withdrawalProof[0] = ghostBytes10.ghostBytes0();
// withdrawalProof[1] = ghostBytes10.ghostBytes1();
// withdrawalProof[2] = ghostBytes10.ghostBytes2();
// withdrawalProof[3] = ghostBytes10.ghostBytes3();
// withdrawalProof[4] = ghostBytes10.ghostBytes4();
// withdrawalProof[5] = ghostBytes10.ghostBytes5();
// withdrawalProof[6] = ghostBytes10.ghostBytes6();
// withdrawalProof[7] = ghostBytes10.ghostBytes7();
// withdrawalProof[8] = ghostBytes10.ghostBytes8();
// withdrawalProof[9] = ghostBytes10.ghostBytes9();
// First approach
for (uint256 i = 0; i < withdrawalProof.length; ++i) { for (uint256 i = 0; i < withdrawalProof.length; ++i) {
// ASSUME: Each element is 600 bytes. Proof elements are 17 * 32 = 544 bytes long, plus
// ~10% margin for RLP encoding:, giving us the 600 byte assumption.
withdrawalProof[i] = freshBigBytes(600); withdrawalProof[i] = freshBigBytes(600);
} }
} }
......
#!/bin/bash
# Common functions and variables for run-kontrol.sh and make-summary-deployment.sh
notif() { echo "== $0: $*" >&2 ; }
usage() {
echo "Usage: $0 [-h|--help] [container|local|dev]" 1>&2
echo "Options:" 1>&2
echo " -h, --help Display this help message." 1>&2
echo " container Run in docker container. Reproduce CI execution. (Default)" 1>&2
echo " local Run locally, enforces registered versions.json version for better reproducibility. (Recommended)" 1>&2
echo " dev Run locally, does NOT enforce registered version. (Useful for developing with new versions and features)" 1>&2
exit 0
}
# Set Run Directory <root>/packages/contracts-bedrock
WORKSPACE_DIR=$( cd "$SCRIPT_HOME/../../.." >/dev/null 2>&1 && pwd )
# Variables
export CONTAINER_NAME=kontrol-tests
KONTROLRC=$(jq -r .kontrol < "$WORKSPACE_DIR/../../versions.json")
export KONTROL_RELEASE=$KONTROLRC
export LOCAL=false
# Argument Parsing
parse_args() {
if [ $# -gt 1 ]; then
usage
elif [ $# -eq 0 ] || [ "$1" == "container" ]; then
notif "Running in docker container (DEFAULT)"
export LOCAL=false
elif [ "$1" == "-h" ] || [ "$1" == "--help" ]; then
usage
elif [ "$1" == "local" ]; then
notif "Running with LOCAL install, .kontrolrc CI version ENFORCED"
check_kontrol_version
elif [ "$1" == "dev" ]; then
notif "Running with LOCAL install, IGNORING .kontrolrc version"
export LOCAL=true
pushd "$WORKSPACE_DIR" > /dev/null || exit
else
usage
fi
}
check_kontrol_version() {
if [ "$(kontrol version | awk -F': ' '{print$2}')" == "$KONTROLRC" ]; then
notif "Kontrol version matches $KONTROLRC"
export LOCAL=true
pushd "$WORKSPACE_DIR" > /dev/null || exit
else
notif "Kontrol version does NOT match $KONTROLRC"
notif "Please run 'kup install kontrol --version v$KONTROLRC'"
exit 1
fi
}
conditionally_start_docker() {
if [ "$LOCAL" == false ]; then
# Is old docker container running?
if [ "$(docker ps -q -f name="$CONTAINER_NAME")" ]; then
# Stop old docker container
notif "Stopping old docker container"
clean_docker
fi
start_docker
fi
}
start_docker () {
docker run \
--name "$CONTAINER_NAME" \
--rm \
--interactive \
--detach \
--env FOUNDRY_PROFILE="$FOUNDRY_PROFILE" \
--workdir /home/user/workspace \
-v "$WORKSPACE_DIR":/home/user/workspace \
runtimeverificationinc/kontrol:ubuntu-jammy-"$KONTROL_RELEASE"
copy_to_docker
}
copy_to_docker() {
# Copy test content to container. We need to avoid copying node_modules because
# it results in the below error, so we copy the workspace to a temp directory
# and then copy it to the container.
# Error response from daemon: invalid symlink "/home/user/workspace/node_modules/@typescript-eslint/eslint-plugin" -> "../../../../node_modules/.pnpm/@typescript-eslint+eslint-plugin@6.19.1_@typescript-eslint+parser@6.19.1_eslint@8.56.0_typescript@5.3.3/node_modules/@typescript-eslint/eslint-plugin"
# Even though we use a bind mount, we still need to copy the files to the
# container because we are running Docker on a remote host.
TMP_DIR=$(mktemp -d)
cp -r "$WORKSPACE_DIR/." "$TMP_DIR"
rm -rf "$TMP_DIR/node_modules"
docker cp --follow-link "$TMP_DIR/." $CONTAINER_NAME:/home/user/workspace
rm -rf "$TMP_DIR"
docker exec --user root "$CONTAINER_NAME" chown -R user:user /home/user
}
clean_docker(){
notif "Stopping Docker Container"
docker stop "$CONTAINER_NAME"
}
docker_exec () {
docker exec --user user --workdir /home/user/workspace $CONTAINER_NAME "${@}"
}
run () {
if [ "$LOCAL" = true ]; then
notif "Running local"
# shellcheck disable=SC2086
"${@}"
else
notif "Running in docker"
docker_exec "${@}"
fi
}
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
export FOUNDRY_PROFILE=kdeploy
SCRIPT_HOME="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
# shellcheck source=/dev/null
source "$SCRIPT_HOME/common.sh"
parse_args "$@"
cleanup() {
# Restore the original script from the backup
if [ -f "$DEPLOY_SCRIPT.bak" ]; then
cp "$DEPLOY_SCRIPT.bak" "$DEPLOY_SCRIPT"
rm "$DEPLOY_SCRIPT.bak"
fi
if [ -f "snapshots/state-diff/Deploy.json" ]; then
rm "snapshots/state-diff/Deploy.json"
fi
if [ "$LOCAL" = false ]; then
clean_docker
fi
}
# Set trap to call cleanup function on exit
trap cleanup EXIT
# create deployments/hardhat/.deploy and snapshots/state-diff/Deploy.json if necessary # create deployments/hardhat/.deploy and snapshots/state-diff/Deploy.json if necessary
if [ ! -d "deployments/hardhat" ]; then if [ ! -d "deployments/hardhat" ]; then
mkdir deployments/hardhat; mkdir deployments/hardhat;
...@@ -16,40 +42,39 @@ if [ ! -f "snapshots/state-diff/Deploy.json" ]; then ...@@ -16,40 +42,39 @@ if [ ! -f "snapshots/state-diff/Deploy.json" ]; then
fi fi
DEPLOY_SCRIPT="./scripts/Deploy.s.sol" DEPLOY_SCRIPT="./scripts/Deploy.s.sol"
conditionally_start_docker
# Create a backup # Create a backup
cp ${DEPLOY_SCRIPT} ${DEPLOY_SCRIPT}.bak cp $DEPLOY_SCRIPT $DEPLOY_SCRIPT.bak
# Replace mustGetAddress by getAddress in Deploy.s.sol # Replace mustGetAddress by getAddress in Deploy.s.sol
# This is needed because the Kontrol deployment is only a partial # This is needed because the Kontrol deployment is only a partial
# version of the full Optimism deployment. Since not all the components # version of the full Optimism deployment. Since not all the components
# of the system are deployed, we'd get some reverts on the `mustGetAddress` functions # of the system are deployed, we'd get some reverts on the `mustGetAddress` functions
awk '{gsub(/mustGetAddress/, "getAddress")}1' ${DEPLOY_SCRIPT} > temp && mv temp ${DEPLOY_SCRIPT} awk '{gsub(/mustGetAddress/, "getAddress")}1' $DEPLOY_SCRIPT > temp && mv temp $DEPLOY_SCRIPT
FOUNDRY_PROFILE=kdeploy forge script -vvv test/kontrol/deployment/KontrolDeployment.sol:KontrolDeployment --sig 'runKontrolDeployment()' forge script -vvv test/kontrol/deployment/KontrolDeployment.sol:KontrolDeployment --sig 'runKontrolDeployment()'
echo "Created state diff json" echo "Created state diff json"
# Restore the file from the backup
cp ${DEPLOY_SCRIPT}.bak ${DEPLOY_SCRIPT}
rm ${DEPLOY_SCRIPT}.bak
# Clean and store the state diff json in snapshots/state-diff/Kontrol-Deploy.json # Clean and store the state diff json in snapshots/state-diff/Kontrol-Deploy.json
JSON_SCRIPTS=test/kontrol/scripts/json JSON_SCRIPTS=test/kontrol/scripts/json
GENERATED_STATEDIFF=Deploy.json # Name of the statediff json produced by the deployment script GENERATED_STATEDIFF=Deploy.json # Name of the statediff json produced by the deployment script
STATEDIFF=Kontrol-${GENERATED_STATEDIFF} # Name of the Kontrol statediff STATEDIFF=Kontrol-$GENERATED_STATEDIFF # Name of the Kontrol statediff
mv snapshots/state-diff/${GENERATED_STATEDIFF} snapshots/state-diff/${STATEDIFF} mv snapshots/state-diff/$GENERATED_STATEDIFF snapshots/state-diff/$STATEDIFF
python3 ${JSON_SCRIPTS}/clean_json.py snapshots/state-diff/${STATEDIFF} python3 $JSON_SCRIPTS/clean_json.py snapshots/state-diff/$STATEDIFF
jq . snapshots/state-diff/${STATEDIFF} > temp && mv temp snapshots/state-diff/${STATEDIFF} # Prettify json jq . snapshots/state-diff/$STATEDIFF > temp && mv temp snapshots/state-diff/$STATEDIFF # Prettify json
echo "Cleaned state diff json" echo "Cleaned state diff json"
CONTRACT_NAMES=deployments/hardhat/.deploy CONTRACT_NAMES=deployments/hardhat/.deploy
python3 ${JSON_SCRIPTS}/reverse_key_values.py ${CONTRACT_NAMES} ${CONTRACT_NAMES}Reversed python3 $JSON_SCRIPTS/reverse_key_values.py $CONTRACT_NAMES ${CONTRACT_NAMES}Reversed
CONTRACT_NAMES=${CONTRACT_NAMES}Reversed CONTRACT_NAMES=${CONTRACT_NAMES}Reversed
SUMMARY_DIR=test/kontrol/proofs/utils SUMMARY_DIR=test/kontrol/proofs/utils
SUMMARY_NAME=DeploymentSummary SUMMARY_NAME=DeploymentSummary
LICENSE=MIT LICENSE=MIT
kontrol summary ${SUMMARY_NAME} snapshots/state-diff/${STATEDIFF} --contract-names ${CONTRACT_NAMES} --output-dir ${SUMMARY_DIR} --license ${LICENSE}
forge fmt ${SUMMARY_DIR}/${SUMMARY_NAME}.sol copy_to_docker # Copy the newly generated files to the docker container
forge fmt ${SUMMARY_DIR}/${SUMMARY_NAME}Code.sol run kontrol summary $SUMMARY_NAME snapshots/state-diff/$STATEDIFF --contract-names $CONTRACT_NAMES --output-dir $SUMMARY_DIR --license $LICENSE
echo "Added state updates to ${SUMMARY_DIR}/${SUMMARY_NAME}.sol" forge fmt $SUMMARY_DIR/$SUMMARY_NAME.sol
forge fmt $SUMMARY_DIR/${SUMMARY_NAME}Code.sol
echo "Added state updates to $SUMMARY_DIR/$SUMMARY_NAME.sol"
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
#####################
# Support Functions #
#####################
blank_line() { echo '' >&2 ; }
notif() { echo "== $0: $*" >&2 ; }
usage() {
echo "Usage: $0 [-h|--help] [container|local|dev]" 1>&2
echo "Options:" 1>&2
echo " -h, --help Display this help message." 1>&2
echo " container Run tests in docker container. Reproduce CI execution. (Default)" 1>&2
echo " local Run locally, enforces CI Registered .kontrolrc Kontrol version for better reproducibility. (Recommended)" 1>&2
echo " dev Run locally, do NOT enforce CI registered Kontrol version (Recomended w/ greater kup & kontrol experience)" 1>&2
exit 0
}
#############
# Variables #
#############
# Set Script Directory Variables <root>/packages/contracts-bedrock/test/kontrol
SCRIPT_HOME="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
notif "Script Home: $SCRIPT_HOME"
blank_line
# Set Run Directory <root>/packages/contracts-bedrock
WORKSPACE_DIR=$( cd "${SCRIPT_HOME}/../../.." >/dev/null 2>&1 && pwd )
notif "Run Directory: ${WORKSPACE_DIR}"
blank_line
export FOUNDRY_PROFILE=kprove export FOUNDRY_PROFILE=kprove
export CONTAINER_NAME=kontrol-tests
KONTROLRC=$(jq -r .kontrol < "${WORKSPACE_DIR}/../../versions.json")
export KONTROL_RELEASE=${KONTROLRC}
export LOCAL=false
####################### SCRIPT_HOME="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
# Check for arguments # # shellcheck source=/dev/null
####################### source "$SCRIPT_HOME/common.sh"
if [ $# -gt 1 ]; then parse_args "$@"
usage
else
if [ $# -eq 0 ] || [ "$1" == "container" ]; then
notif "Running in docker container (DEFAULT)"
blank_line
export LOCAL=false
[ $# -gt 1 ] && shift
elif [ "$1" == "-h" ] || [ "$1" == "--help" ]; then
usage
elif [ "$1" == "local" ]; then
notif "Running with LOCAL install, .kontrolrc CI version ENFORCED"
if [ "$(kontrol version | awk -F': ' '{print$2}')" == "${KONTROLRC}" ]; then
notif "Kontrol version matches ${KONTROLRC}"
blank_line
export LOCAL=true
shift
pushd "${WORKSPACE_DIR}" > /dev/null
else
notif "Kontrol version does NOT match ${KONTROLRC}"
notif "Please run 'kup install kontrol --version v${KONTROLRC}'"
blank_line
exit 1
fi
elif [ "$1" == "dev" ]; then
notif "Running with LOCAL install, IGNORING .kontrolrc version"
blank_line
export LOCAL=true
shift
pushd "${WORKSPACE_DIR}" > /dev/null
else
# Unexpected argument passed
usage
fi
fi
############# #############
# Functions # # Functions #
############# #############
kontrol_build() { kontrol_build() {
notif "Kontrol Build" notif "Kontrol Build"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
run kontrol build \ run kontrol build \
--verbose \ --verbose \
--require ${lemmas} \ --require $lemmas \
--module-import ${module} \ --module-import $module \
${rekompile} $rekompile
}
kontrol_prove() {
notif "Kontrol Prove"
# shellcheck disable=SC2086
run kontrol prove \
--max-depth ${max_depth} \
--max-iterations ${max_iterations} \
--smt-timeout ${smt_timeout} \
--workers ${workers} \
${reinit} \
${bug_report} \
${break_on_calls} \
${auto_abstract} \
${tests} \
${use_booster} \
--init-node-from ${state_diff}
} }
start_docker () { kontrol_prove() {
docker run \ notif "Kontrol Prove"
--name "${CONTAINER_NAME}" \ # shellcheck disable=SC2086
--rm \ run kontrol prove \
--interactive \ --max-depth $max_depth \
--detach \ --max-iterations $max_iterations \
--env FOUNDRY_PROFILE="${FOUNDRY_PROFILE}" \ --smt-timeout $smt_timeout \
--workdir /home/user/workspace \ --workers $workers \
runtimeverificationinc/kontrol:ubuntu-jammy-"${KONTROL_RELEASE}" $reinit \
$bug_report \
# Copy test content to container $break_on_calls \
docker cp --follow-link "${WORKSPACE_DIR}/." ${CONTAINER_NAME}:/home/user/workspace $auto_abstract \
docker exec --user root ${CONTAINER_NAME} chown -R user:user /home/user $tests \
} $use_booster \
--init-node-from $state_diff
docker_exec () {
docker exec --user user --workdir /home/user/workspace ${CONTAINER_NAME} "${@}"
}
run () {
if [ "${LOCAL}" = true ]; then
notif "Running local"
# shellcheck disable=SC2086
"${@}"
else
notif "Running in docker"
docker_exec "${@}"
fi
} }
dump_log_results(){ dump_log_results(){
trap clean_docker ERR trap clean_docker ERR
RESULTS_FILE="results-$(date +'%Y-%m-%d-%H-%M-%S').tar.gz" RESULTS_FILE="results-$(date +'%Y-%m-%d-%H-%M-%S').tar.gz"
LOG_PATH="test/kontrol/logs" LOG_PATH="test/kontrol/logs"
RESULTS_LOG="${LOG_PATH}/${RESULTS_FILE}" RESULTS_LOG="$LOG_PATH/$RESULTS_FILE"
if [ ! -d ${LOG_PATH} ]; then if [ ! -d $LOG_PATH ]; then
mkdir ${LOG_PATH} mkdir $LOG_PATH
fi fi
notif "Generating Results Log: ${LOG_PATH}" notif "Generating Results Log: $LOG_PATH"
blank_line
run tar -czvf results.tar.gz kout-proofs/ > /dev/null 2>&1 run tar -czvf results.tar.gz kout-proofs/ > /dev/null 2>&1
if [ "${LOCAL}" = true ]; then if [ "$LOCAL" = true ]; then
mv results.tar.gz "${RESULTS_LOG}" mv results.tar.gz "$RESULTS_LOG"
else else
docker cp ${CONTAINER_NAME}:/home/user/workspace/results.tar.gz "${RESULTS_LOG}" docker cp "$CONTAINER_NAME:/home/user/workspace/results.tar.gz" "$RESULTS_LOG"
fi fi
if [ -f "${RESULTS_LOG}" ]; then if [ -f "$RESULTS_LOG" ]; then
cp "${RESULTS_LOG}" "${LOG_PATH}/kontrol-results_latest.tar.gz" cp "$RESULTS_LOG" "$LOG_PATH/kontrol-results_latest.tar.gz"
else else
notif "Results Log: ${RESULTS_LOG} not found, skipping.." notif "Results Log: $RESULTS_LOG not found, skipping.."
blank_line
fi fi
# Report where the file was generated and placed # Report where the file was generated and placed
notif "Results Log: $(dirname "${RESULTS_LOG}") generated" notif "Results Log: $(dirname "$RESULTS_LOG") generated"
if [ "${LOCAL}" = false ]; then if [ "$LOCAL" = false ]; then
notif "Results Log: ${RESULTS_LOG} generated" notif "Results Log: $RESULTS_LOG generated"
blank_line
RUN_LOG="run-kontrol-$(date +'%Y-%m-%d-%H-%M-%S').log" RUN_LOG="run-kontrol-$(date +'%Y-%m-%d-%H-%M-%S').log"
docker logs ${CONTAINER_NAME} > "${LOG_PATH}/${RUN_LOG}" docker logs "$CONTAINER_NAME" > "$LOG_PATH/$RUN_LOG"
fi fi
} }
clean_docker(){
notif "Stopping Docker Container"
docker stop ${CONTAINER_NAME}
blank_line
}
# Define the function to run on failure # Define the function to run on failure
on_failure() { on_failure() {
dump_log_results dump_log_results
if [ "${LOCAL}" = false ]; then if [ "$LOCAL" = false ]; then
clean_docker clean_docker
fi fi
notif "Cleanup complete." notif "Cleanup complete."
blank_line exit 1
exit 1
} }
# Set up the trap to run the function on failure # Set up the trap to run the function on failure
...@@ -201,7 +95,7 @@ trap on_failure ERR INT ...@@ -201,7 +95,7 @@ trap on_failure ERR INT
# empty assignment to activate/deactivate the corresponding flag # empty assignment to activate/deactivate the corresponding flag
lemmas=test/kontrol/pausability-lemmas.k lemmas=test/kontrol/pausability-lemmas.k
base_module=PAUSABILITY-LEMMAS base_module=PAUSABILITY-LEMMAS
module=OptimismPortalKontrol:${base_module} module=OptimismPortalKontrol:$base_module
rekompile=--rekompile rekompile=--rekompile
rekompile= rekompile=
regen=--regen regen=--regen
...@@ -215,7 +109,7 @@ regen= ...@@ -215,7 +109,7 @@ regen=
test_list=( "OptimismPortalKontrol.prove_finalizeWithdrawalTransaction_paused" \ test_list=( "OptimismPortalKontrol.prove_finalizeWithdrawalTransaction_paused" \
"L1StandardBridgeKontrol.prove_finalizeBridgeERC20_paused" \ "L1StandardBridgeKontrol.prove_finalizeBridgeERC20_paused" \
"L1StandardBridgeKontrol.prove_finalizeBridgeETH_paused" \ "L1StandardBridgeKontrol.prove_finalizeBridgeETH_paused" \
"L1ERC721BridgeKontrol.prove_finalizeBridgeERC21_paused" \ "L1ERC721BridgeKontrol.prove_finalizeBridgeERC721_paused" \
"L1CrossDomainMessengerKontrol.prove_relayMessage_paused" "L1CrossDomainMessengerKontrol.prove_relayMessage_paused"
) )
tests="" tests=""
...@@ -247,27 +141,16 @@ state_diff="./snapshots/state-diff/Kontrol-Deploy.json" ...@@ -247,27 +141,16 @@ state_diff="./snapshots/state-diff/Kontrol-Deploy.json"
############# #############
# RUN TESTS # # RUN TESTS #
############# #############
if [ "${LOCAL}" == false ]; then conditionally_start_docker
# Is old docker container running?
if [ "$(docker ps -q -f name=${CONTAINER_NAME})" ]; then
# Stop old docker container
notif "Stopping old docker container"
clean_docker
blank_line
fi
start_docker
fi
kontrol_build kontrol_build
kontrol_prove kontrol_prove
dump_log_results dump_log_results
if [ "${LOCAL}" == false ]; then if [ "$LOCAL" == false ]; then
notif "Stopping docker container" notif "Stopping docker container"
clean_docker clean_docker
fi fi
blank_line
notif "DONE" notif "DONE"
blank_line
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment