• Matthew Slipper's avatar
    ctb: Fix multithreaded test race · f1ec8e63
    Matthew Slipper authored
    `generate-l2-genesis.sh` is causing spurious test failures. The `.testdata/genesis.json` file comes up as zero-length in tests, even when the script checks for its length before exiting. I believe this is because multiple processes are executing the `generate-l2-genesis.sh` file at once, which causes a race condition where different invocations of the script cause the genesis file to be truncated.
    
    This PR attempts to fix the issue by using a temporary directory as a lock to prevent multiple invocations of the `generate-l2-genesis.sh` script from running at the same time. Directory creations are atomic in Bash, so we can use this technique to create a mutex. If the directory can't be created, then the script will assume that another script is writing the genesis file and wait up to 5 minutes for it to be created. The script uses `trap` to delete the mutex directory when it's done.
    f1ec8e63
generate-l2-genesis.sh 2.02 KB
#!/usr/bin/env bash

set -euo pipefail

# Create a L2 genesis.json suitable for the solidity tests to
# ingest using `vm.loadAllocs(string)`.
# This script depends on the relative path to the op-node from
# contracts-bedrock

SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null && pwd)"
CONTRACTS_DIR="$(realpath "$SCRIPTS_DIR/..")"
MONOREPO_BASE="$(realpath "$CONTRACTS_DIR/../..")"

DEPLOY_ARTIFACT="$CONTRACTS_DIR/deployments/hardhat/.deploy"
OP_NODE="$MONOREPO_BASE/op-node/cmd/main.go"
L1_STARTING_BLOCK_PATH="$CONTRACTS_DIR/test/mocks/block.json"
TESTDATA_DIR="$CONTRACTS_DIR/.testdata"

OUTFILE_L2="$TESTDATA_DIR/genesis.json"
OUTFILE_ROLLUP="$TESTDATA_DIR/rollup.json"

LOCKDIR="/tmp/lock-generate-l2-genesis"

cleanup() {
  rm -rf -- "$LOCKDIR"
}

# Wait for the L2 outfile to be over 8M for up to $2 iterations
# of $1 seconds. This is a hack to ensure that the outfile is fully
# written before the solidity tests try to read it
wait_l2_outfile() {
  i=1
  while [ $i -le $2 ]; do
    i=$(($i + 1))

    if [ ! -f "$OUTFILE_L2" ]; then
      sleep $1
      continue
    fi

    if [ $(du -m "$OUTFILE_L2" | cut -f1) -lt 8 ]; then
      sleep $1
      continue
    fi

    exit 0
  done

  echo "L2 genesis file not generated in time. Exiting."
  exit 1
}

# Directory creations are atomic, so we can use mkdir to
# create a lockfile that prevents subsequent invocations
# of the script from running concurrently.
if mkdir -- "$LOCKDIR" > /dev/null 2>&1; then
  trap 'cleanup' EXIT

  mkdir -p "$TESTDATA_DIR"

  if [ ! -f "$DEPLOY_ARTIFACT" ]; then
    forge script $CONTRACTS_DIR/scripts/Deploy.s.sol:Deploy > /dev/null 2>&1
  fi

  if [ ! -f "$OUTFILE_L2" ]; then
    go run $OP_NODE genesis l2 \
      --deploy-config "$CONTRACTS_DIR/deploy-config/hardhat.json" \
      --l1-deployments "$DEPLOY_ARTIFACT" \
      --l1-starting-block "$L1_STARTING_BLOCK_PATH" \
      --outfile.l2 "$OUTFILE_L2" \
      --outfile.rollup "$OUTFILE_ROLLUP" > /dev/null 2>&1
  fi
else
  # Wait up to 5 minutes for the lock to be released
  wait_l2_outfile 0.25 1200
fi