challenge_fault.sh 5.27 KB
Newer Older
1
#!/usr/bin/env bash
2

3 4 5 6 7 8 9
# The following variables can be overridden as environment variables:
# * BLOCK (block whose transition will be challenged)
# * SKIP_NODE (skip forking a node, useful if you've already forked a node)
#
# Example usage:
# SKIP_NODE=1 BLOCK=13284469 ./demo/challenge_fault.sh

10 11
# --- DOC ----------------------------------------------------------------------

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
# Unlike the simple scenario (cf. challenge_simple.sh), in this
# challenge-response scenario we use the correct block data (preimages) and
# instead use the `OUTPUTFAULT` environment variable to request a fault in the
# challenger's execution, making his challenge invalid.
#
# The "fault" in question is a behaviour hardcoded in `mipsevm` (Unicorn mode
# only) which triggers when the `OUTPUTFAULT` env var is set: when writing to
# MIPS address 0x30000804 (address where the output hash is written at the end
# of execution), it will write a wrong value instead.
#
# Alternatively, if `REGFAULT` is set, it should contain a MIPS execution step
# number and causes the MIPS register V0 to be set to a bogus value at the given
# execution step. (Just like before, this behaviour is hardcoded in `mipsevm` in
# Unicorn mode and triggers when `REGFAULT` is set.)
#
# This is much slower than the previous scenario because:
#
# - Since we write to the output hash at the end of execution, we will execute ~
#   `log(n) * 3/4 * n` MIPS steps (where `n` = number of steps in full
#   execution) vs `log(n) * 1/4 * n`in the previous example. (This is the
#   difference of having the fault occur in the first vs (one of) the last
#   steps.)
#
# - The challenged block contains almost 4x as many transactions as the original
#   (8.5M vs 30M gas).

38 39

# --- SCRIPT SETUP -------------------------------------------------------------
40

41 42 43 44 45 46 47 48
shout() {
    echo ""
    echo "----------------------------------------"
    echo "$1"
    echo "----------------------------------------"
    echo ""
}

49 50 51
# Exit if any command fails.
set -e

52
exit_trap() {
53 54
    # Print an error if the last command failed
    # (in which case the script is exiting because of set -e).
55 56 57 58
    [[ $? == 0 ]] && return
    echo "----------------------------------------"
    echo "EARLY EXIT: SCRIPT FAILED"
    echo "----------------------------------------"
59 60 61 62 63 64

    # Kill (send SIGTERM) to the whole process group, also killing
    # any background processes.
    # I think the trap command resets SIGTERM before resending it to the whole
    # group. (cf. https://stackoverflow.com/a/2173421)
    trap - SIGTERM && kill -- -$$
65
}
66 67 68 69
trap "exit_trap" SIGINT SIGTERM EXIT

# --- BOOT MAINNET FORK --------------------------------------------------------

70 71
if [[ ! "$SKIP_NODE" ]]; then
    NODE_LOG="challenge_fault_node.log"
72

73
    shout "BOOTING MAINNET FORK NODE IN BACKGROUND (LOG: $NODE_LOG)"
74

75 76
    # get directory containing this file
    SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
77

78 79
    # run a hardhat mainnet fork node
    "$SCRIPT_DIR/forked_node.sh" > "$NODE_LOG" 2>&1 &
80

81 82 83
    # give the node some time to boot up
    sleep 10
fi
84 85

# --- CHALLENGE SETUP ----------------------------------------------------------
86

87 88 89 90
# hardhat network to use
NETWORK=${NETWORK:-l1}
export NETWORK

91 92
# block whose transition will be challenged
# this variable is read by challenge.js, respond.js and assert.js
93 94
BLOCK=${BLOCK:-13284491}
export BLOCK
95

96
# challenge ID, read by respond.js and assert.js
97 98 99 100 101
export ID=0

# clear data from previous runs
mkdir -p /tmp/cannon /tmp/cannon_fault && rm -rf /tmp/cannon/* /tmp/cannon_fault/*

102 103
# stored in /tmp/cannon/golden.json
shout "GENERATING INITIAL MEMORY STATE CHECKPOINT"
104
mipsevm/mipsevm --outputGolden
105

106
shout "DEPLOYING CONTRACTS"
107
npx hardhat run scripts/deploy.js --network $NETWORK
108 109 110 111

# challenger will use same initial memory checkpoint and deployed contracts
cp /tmp/cannon/{golden,deployed}.json /tmp/cannon_fault/

112
shout "FETCHING PREIMAGES FOR REAL BLOCK"
113 114
minigeth/go-ethereum $BLOCK

115
shout "COMPUTING REAL MIPS FINAL MEMORY CHECKPOINT"
116
mipsevm/mipsevm --blockNumber=$BLOCK
117

118 119
# these are the preimages for the real block (but go into a different basedir)
shout "FETCHING PREIMAGES FOR FAULTY BLOCK"
120 121
BASEDIR=/tmp/cannon_fault minigeth/go-ethereum $BLOCK

122 123 124
# since the computation includes a fault, the output file will be different than
# for the real block
shout "COMPUTE FAKE MIPS CHECKPOINT"
125
OUTPUTFAULT=1 BASEDIR=/tmp/cannon_fault mipsevm/mipsevm --blockNumber=$BLOCK
126 127

# alternatively, to inject a fault in registers instead of memory
128
# REGFAULT=13240000 BASEDIR=/tmp/cannon_fault mipsevm/mipsevm --blockNumber=$BLOCK
129

130 131
# --- BINARY SEARCH ------------------------------------------------------------

132
shout "STARTING CHALLENGE"
133
BASEDIR=/tmp/cannon_fault npx hardhat run scripts/challenge.js --network $NETWORK
134

135
shout "BINARY SEARCH"
136
for i in {1..25}; do
137 138 139
    echo ""
    echo "--- STEP $i / 25 --"
    echo ""
140 141
    OUTPUTFAULT=1 BASEDIR=/tmp/cannon_fault CHALLENGER=1 npx hardhat run scripts/respond.js --network $NETWORK
    npx hardhat run scripts/respond.js --network $NETWORK
142 143
done

144 145
# --- SINGLE STEP EXECUTION ----------------------------------------------------

146
shout "ASSERTING AS CHALLENGER (should fail)"
147
set +e # this should fail!
148
BASEDIR=/tmp/cannon_fault CHALLENGER=1 npx hardhat run scripts/assert.js --network $NETWORK
149 150
set -e

151
shout "ASSERTING AS DEFENDER (should pass)"
152
npx hardhat run scripts/assert.js --network $NETWORK