#!/bin/bash

# Reproduce the Duoram experiments from our paper:

# Adithya Vadapalli, Ryan Henry, Ian Goldberg. Duoram: A
# Bandwidth-Efficient Distributed ORAM for 2- and 3-Party Computation.
# USENIX Security Symposium 2023.

# cd into the directory containing this script (from the bash faq 028)
if [[ $BASH_SOURCE = */* ]]; then
  cd -- "${BASH_SOURCE%/*}/" || exit
fi

# If the master NUMA commands are set, use them for Duoram
if [ "$NUMA_P0" != "" ]; then
    export DUORAM_NUMA_P0="$NUMA_P0"
fi
if [ "$NUMA_P1" != "" ]; then
    export DUORAM_NUMA_P1="$NUMA_P1"
fi
if [ "$NUMA_P2" != "" ]; then
    export DUORAM_NUMA_P2="$NUMA_P2"
fi

# Allow running only subsets of the experiment suite.  Valid values are
# "test", "small", "large", "scaling", "all", "none".  ("none" is useful
# if you just want to re-parse the output of existing logs.  "scaling"
# is the subset of "small" that generates the data points for Figure 10;
# see the README for that one, since you need a machine with at least 64
# cores to generate it.)  You can also say "single" followed by all the
# arguments to "run" (below) to run a single experiment; for example:
# ./repro single readwrite 20 1us 100gbit 128
if [ "$1" = "" ]; then
    whichexps="test"
else
    whichexps="$1"
fi

# The number of operations per run; the graphs in the paper use 128
if [ "$whichexps" = "single" -o "$2" = "" ]; then
    # If there's an explicit experiment on the command line, don't read
    # the next argument as the number of operations.  $numops will be
    # ignored, anyway, since it will be specified as part of the
    # command.
    numops=128
else
    numops="$2"
fi

# The maximum amount of memory to use (in GB).  Set the environment
# variable DUORAM_MAXGB to increase it beyond 16 (don't set it lower
# than 16).
if [ "$DUORAM_MAXGB" = "" ]; then
    maxgb=16
elif [ "$DUORAM_MAXGB" -gt 16 ]; then
    maxgb=$DUORAM_MAXGB
else
    maxgb=16
fi

# Run one experiment
# Arguments:
# $1: mode (read, write, readwrite)
# $2: depth (the ORAM has 2^depth elements)
# $3: latency (e.g., 30ms)
# $4: bandwidth (e.g., 100mbit)
# $5: number of operations (e.g., 128)
# $6: phase (preproc or online)
# $7: number of parties (3P or 2P)
runone() {
    now=`date`
    echo "$now: Running $1 $2 $3 $4 $5 $6 $7 ..."
    logfile="${1}_${3}_${4}_${5}_${6}_${7}.out${LOGSUFFIX}"
    ./set-networking $3 $4
    echo "Max GB: $maxgb" >> $logfile
    echo "Network setup: $3 $4" >> $logfile
    ./run-experiment $1 $2 $5 $6 $7 $maxgb >> $logfile
}

# Run one set of Duoram experiments: 2P and 3P, read and write,
# preprocessing and online
# Arguments:
# $1: depth (the ORAM has 2^depth elements)
# $2: latency (e.g., 30ms)
# $3: bandwidth (e.g., 100mbit)
# $4: number of operations (e.g., 128)
run() {
    # The 2P read actually does both preprocessing and online
    runone read $1 $2 $3 $4 online 2P
    runone write $1 $2 $3 $4 preproc 2P
    runone write $1 $2 $3 $4 online 2P
    # The 2P read and write protocols are completely different, so
    # readwrite is just the sum of read and write.
    # The 3P preprocessing protocol is identical for reads and writes,
    # so we just do it once, and count it twice for readwrite.
    runone read $1 $2 $3 $4 preproc 3P
    # The 3P online protocols do reads, writes, and readwrites in a
    # single run.
    runone readwrite $1 $2 $3 $4 online 3P
    # Clean up any preprocessed DPFs
    docker exec -w /root/duoram/duoram-online/preprocflags duoram_p0 bash -c "rm -f *" &
    docker exec -w /root/duoram/duoram-online/preprocflags duoram_p1 bash -c "rm -f *" &
    docker exec -w /root/duoram/duoram-online/preprocflags duoram_p2 bash -c "rm -f *" &
    wait
}

# Parse the output logs.  We run this in the docker in case you don't
# have perl installed on the host.
# Arguments: a list of logfiles
parse() {
    if [ "$DUORAM_PARSE_HOST" = "1" ]; then
        ./parse_logs $*
    else
        cat $* | docker exec -w /root/duoram/Docker -i duoram_p0 ./parse_logs
    fi
}

# A very small kick-the-tires test to ensure everything compiled and
# built properly
if [ "$whichexps" = "test" ]; then
    echo "Running test experiment..."
    run 16 1us 100gbit 2
    echo
    echo "# Test output"
    echo
    parse read_1us_100gbit_2_online_2P.out${LOGSUFFIX} \
        write_1us_100gbit_2_preproc_2P.out${LOGSUFFIX} \
        write_1us_100gbit_2_online_2P.out${LOGSUFFIX} \
        read_1us_100gbit_2_preproc_3P.out${LOGSUFFIX} \
        readwrite_1us_100gbit_2_online_3P.out${LOGSUFFIX} \
        | egrep '(Onln|Totl).*readwrite' | sort
    echo
    echo "# End test output"
    echo
    exit
fi

# Be able to run a single experiment specified on the command line
if [ "$whichexps" = "single" ]; then
    echo "Running single experiment..."
    shift
    runone $*
    exit
fi

now=`date`
echo "$now: Starting experiments"

if [ "$whichexps" = "small" -o "$whichexps" = "all" ]; then
    echo "Running small experiments..."
    # Figure 7(b)
    run 20 30ms 10mbit ${numops}
    run 20 30ms 30mbit ${numops}
    run 20 30ms 50mbit ${numops}
    run 20 30ms 70mbit ${numops}
    run 20 30ms 90mbit ${numops}
    run 20 30ms 110mbit ${numops}
    # Figure 7(c)
    run 20 10ms 100mbit ${numops}
    run 20 50ms 100mbit ${numops}
    run 20 70ms 100mbit ${numops}
    # Figures 8(a), 8(b), 8(c), 9(b), and 9(c)
    # Note that we set the latency to 1us, which really means "don't add
    # artificial latency", but we measure the one-way latency to
    # actually be 30us, which is what we report in the paper. (pings
    # from one docker to the other take about 60us round trip.)
    run 16 1us 100gbit ${numops}
    run 18 1us 100gbit ${numops}
    run 20 1us 100gbit ${numops}
    run 22 1us 100gbit ${numops}
    run 24 1us 100gbit ${numops}
    run 26 1us 100gbit ${numops}
    # Figures 7(a), 9(a)
    run 18 30ms 100mbit ${numops}
    run 22 30ms 100mbit ${numops}
    run 24 30ms 100mbit ${numops}
fi
if [ "$whichexps" = "small" -o "$whichexps" = "all" ]; then
    # Figures 7(a), 9(a)
    run 16 30ms 100mbit ${numops}
    run 20 30ms 100mbit ${numops}
    run 26 30ms 100mbit ${numops}
fi
if [ "$whichexps" = "scaling" ]; then
    echo "Running scaling experiments..."
    # Figure 10
    runone read 16 30ms 100mbit ${numops} online 2P
    runone read 20 30ms 100mbit ${numops} online 2P
    runone read 26 30ms 100mbit ${numops} online 2P
    runone read 16 30ms 100mbit ${numops} online 3P
    runone read 20 30ms 100mbit ${numops} online 3P
    runone read 26 30ms 100mbit ${numops} online 3P
fi
if [ "$whichexps" = "large" -o "$whichexps" = "all" ]; then
    echo "Running large experiments..."
    # Figure 9(a)
    runone read 28 30ms 100mbit ${numops} preproc 3P
    runone read 28 30ms 100mbit ${numops} online 3P
    runone read 30 30ms 100mbit ${numops} preproc 3P
    runone read 30 30ms 100mbit ${numops} online 3P
    runone read 32 30ms 100mbit ${numops} preproc 3P
    runone read 32 30ms 100mbit ${numops} online 3P
    # Figures 9(b) and 9(c)
    runone read 28 1us 100gbit ${numops} preproc 3P
    runone read 28 1us 100gbit ${numops} online 3P
    runone read 30 1us 100gbit ${numops} preproc 3P
    runone read 30 1us 100gbit ${numops} online 3P
    runone read 32 1us 100gbit ${numops} preproc 3P
    runone read 32 1us 100gbit ${numops} online 3P
fi

now=`date`
echo "$now: Experiments complete"

# If you specified a custom log suffix, you're going to be parsing the
# outputs differently.
if [ "$LOGSUFFIX" = "" ]; then

parse *_${numops}_{online,preproc}_{2P,3P}.out > duoram_${numops}.dat

echo
echo "# Figure 7(a)"
egrep '2PDuoramTotl readwrite .* 30ms 100mbit .* s$' duoram_${numops}.dat | sort -k3 -n
echo
egrep '2PDuoramOnln readwrite .* 30ms 100mbit .* s$' duoram_${numops}.dat | sort -k3 -n
echo
egrep '3PDuoramTotl readwrite .* 30ms 100mbit .* s$' duoram_${numops}.dat | sort -k3 -n
echo
egrep '3PDuoramOnln readwrite .* 30ms 100mbit .* s$' duoram_${numops}.dat | sort -k3 -n
echo
echo "# Figure 7(b)"
egrep '2PDuoramTotl readwrite 20 30ms .* s$' duoram_${numops}.dat | sort -k5 -n
echo
egrep '2PDuoramOnln readwrite 20 30ms .* s$' duoram_${numops}.dat | sort -k5 -n
echo
egrep '3PDuoramTotl readwrite 20 30ms .* s$' duoram_${numops}.dat | sort -k5 -n
echo
egrep '3PDuoramOnln readwrite 20 30ms .* s$' duoram_${numops}.dat | sort -k5 -n
echo
echo "# Figure 7(c)"
egrep '2PDuoramTotl readwrite 20 .* 100mbit .* s$' duoram_${numops}.dat | sort -k4 -n
echo
egrep '2PDuoramOnln readwrite 20 .* 100mbit .* s$' duoram_${numops}.dat | sort -k4 -n
echo
egrep '3PDuoramTotl readwrite 20 .* 100mbit .* s$' duoram_${numops}.dat | sort -k4 -n
echo
egrep '3PDuoramOnln readwrite 20 .* 100mbit .* s$' duoram_${numops}.dat | sort -k4 -n
echo
echo "# Figure 8(a)"
egrep '2PDuoramTotl read .* 1us 100gbit .* KiB$' duoram_${numops}.dat | sort -k3 -n
echo
egrep '2PDuoramOnln read .* 1us 100gbit .* KiB$' duoram_${numops}.dat | sort -k3 -n
echo
egrep '3PDuoramTotl read .* 1us 100gbit .* KiB$' duoram_${numops}.dat | sort -k3 -n
echo
egrep '3PDuoramOnln read .* 1us 100gbit .* KiB$' duoram_${numops}.dat | sort -k3 -n
echo
echo "# Figure 8(b)"
egrep '2PDuoramTotl write .* 1us 100gbit .* KiB$' duoram_${numops}.dat | sort -k3 -n
echo
egrep '2PDuoramOnln write .* 1us 100gbit .* KiB$' duoram_${numops}.dat | sort -k3 -n
echo
egrep '3PDuoramTotl write .* 1us 100gbit .* KiB$' duoram_${numops}.dat | sort -k3 -n
echo
egrep '3PDuoramOnln write .* 1us 100gbit .* KiB$' duoram_${numops}.dat | sort -k3 -n
echo
echo "# Figure 8(c)"
egrep '2PDuoramTotl readwrite .* 30ms 100mbit .* KiB$' duoram_${numops}.dat | sort -k3 -n
echo
egrep '2PDuoramOnln readwrite .* 30ms 100mbit .* KiB$' duoram_${numops}.dat | sort -k3 -n
echo
egrep '3PDuoramTotl readwrite .* 30ms 100mbit .* KiB$' duoram_${numops}.dat | sort -k3 -n
echo
egrep '3PDuoramOnln readwrite .* 30ms 100mbit .* KiB$' duoram_${numops}.dat | sort -k3 -n
echo
echo "# Figure 9(a)"
egrep '3PDuoramTotl read .* 30ms 100mbit .* s$' duoram_${numops}.dat | sort -k3 -n
echo
egrep '3PDuoramOnln read .* 30ms 100mbit .* s$' duoram_${numops}.dat | sort -k3 -n
echo
echo "# Figure 9(b)"
egrep '3PDuoramTotl read .* 1us 100gbit .* s$' duoram_${numops}.dat | sort -k3 -n
echo
egrep '3PDuoramOnln read .* 1us 100gbit .* s$' duoram_${numops}.dat | sort -k3 -n
echo
echo "# Figure 9(c)"
egrep '3PDuoramTotl read .* 1us 100gbit .* KiB$' duoram_${numops}.dat | sort -k3 -n
echo
egrep '3PDuoramOnln read .* 1us 100gbit .* KiB$' duoram_${numops}.dat | sort -k3 -n
echo

if [ "$DUORAM_EXTENDED_PLOTS" = "1" ]; then
    # Also show the plots for the extended version (Figures 11, 12)
    echo "# Figure 11(a)"
    egrep '2PDuoramTotl read .* 30ms 100mbit .* s$' duoram_${numops}.dat | sort -k3 -n
    echo
    egrep '2PDuoramOnln read .* 30ms 100mbit .* s$' duoram_${numops}.dat | sort -k3 -n
    echo
    egrep '3PDuoramTotl read .* 30ms 100mbit .* s$' duoram_${numops}.dat | sort -k3 -n
    echo
    egrep '3PDuoramOnln read .* 30ms 100mbit .* s$' duoram_${numops}.dat | sort -k3 -n
    echo
    echo "# Figure 11(b)"
    egrep '2PDuoramTotl read 20 30ms .* s$' duoram_${numops}.dat | sort -k5 -n
    echo
    egrep '2PDuoramOnln read 20 30ms .* s$' duoram_${numops}.dat | sort -k5 -n
    echo
    egrep '3PDuoramTotl read 20 30ms .* s$' duoram_${numops}.dat | sort -k5 -n
    echo
    egrep '3PDuoramOnln read 20 30ms .* s$' duoram_${numops}.dat | sort -k5 -n
    echo
    echo "# Figure 11(c)"
    egrep '2PDuoramTotl read 20 .* 100mbit .* s$' duoram_${numops}.dat | sort -k4 -n
    echo
    egrep '2PDuoramOnln read 20 .* 100mbit .* s$' duoram_${numops}.dat | sort -k4 -n
    echo
    egrep '3PDuoramTotl read 20 .* 100mbit .* s$' duoram_${numops}.dat | sort -k4 -n
    echo
    egrep '3PDuoramOnln read 20 .* 100mbit .* s$' duoram_${numops}.dat | sort -k4 -n
    echo
    echo
    echo "# Figure 12(a)"
    egrep '2PDuoramTotl write .* 30ms 100mbit .* s$' duoram_${numops}.dat | sort -k3 -n
    echo
    egrep '2PDuoramOnln write .* 30ms 100mbit .* s$' duoram_${numops}.dat | sort -k3 -n
    echo
    egrep '3PDuoramTotl write .* 30ms 100mbit .* s$' duoram_${numops}.dat | sort -k3 -n
    echo
    egrep '3PDuoramOnln write .* 30ms 100mbit .* s$' duoram_${numops}.dat | sort -k3 -n
    echo
    echo "# Figure 12(b)"
    egrep '2PDuoramTotl write 20 30ms .* s$' duoram_${numops}.dat | sort -k5 -n
    echo
    egrep '2PDuoramOnln write 20 30ms .* s$' duoram_${numops}.dat | sort -k5 -n
    echo
    egrep '3PDuoramTotl write 20 30ms .* s$' duoram_${numops}.dat | sort -k5 -n
    echo
    egrep '3PDuoramOnln write 20 30ms .* s$' duoram_${numops}.dat | sort -k5 -n
    echo
    echo "# Figure 12(c)"
    egrep '2PDuoramTotl write 20 .* 100mbit .* s$' duoram_${numops}.dat | sort -k4 -n
    echo
    egrep '2PDuoramOnln write 20 .* 100mbit .* s$' duoram_${numops}.dat | sort -k4 -n
    echo
    egrep '3PDuoramTotl write 20 .* 100mbit .* s$' duoram_${numops}.dat | sort -k4 -n
    echo
    egrep '3PDuoramOnln write 20 .* 100mbit .* s$' duoram_${numops}.dat | sort -k4 -n
    echo
fi

echo "# End figures"
echo

fi