#!/bin/sh
# Decompile Bounty Protocol, public verifier.
# Re-runs a published verdict on your own machine and checks every hash along the way.
#
#   curl -fsSL "$SITE/verifier/verify.sh" | SITE="$SITE" sh -s -- 0001
#
# Needs: curl, tar, cargo (Rust 1.89 or newer), and python3 or sed.
# Downloads, all from the site:
#   /api/v1/verdicts/<n>             the verdict record with its hashes
#   /api/v1/corpus/<sha256>          the frozen corpus bundle
#   /api/v1/teardown/<program>/elf   the original ELF read from mainnet at the snapshot
#   /api/v1/candidates/<sha256>      the binary the judge built from the hunter's source
#   /verifier/replayer-src.tar.gz    the replayer source
set -eu

SITE="${SITE:-}"
if [ -z "$SITE" ]; then
  echo "set SITE to the site's origin, for example: SITE=https://<site> sh verify.sh 0001" >&2
  exit 2
fi
SITE="${SITE%/}"
N="${1:-}"
if [ -z "$N" ]; then
  echo "usage: verify.sh <verdict number>" >&2
  exit 2
fi
FUZZ="${FUZZ:-10000}"
WORK="${WORK:-./decomp-verify-$N}"

need() { command -v "$1" >/dev/null 2>&1 || { echo "missing: $1" >&2; exit 2; }; }
need curl; need tar; need cargo

sha() {
  if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1
  else shasum -a 256 "$1" | cut -d' ' -f1; fi
}

# first scalar value of a key, top level first, then nested objects
field() {
  if command -v python3 >/dev/null 2>&1; then
    python3 - "$1" "$2" <<'PY'
import json, sys
d = json.load(open(sys.argv[1])); k = sys.argv[2]
def find(o):
    if isinstance(o, dict):
        if k in o and not isinstance(o[k], (dict, list)):
            return o[k]
        for v in o.values():
            r = find(v)
            if r is not None:
                return r
    elif isinstance(o, list):
        for v in o:
            r = find(v)
            if r is not None:
                return r
    return None
r = find(d)
print("" if r is None else r)
PY
  else
    tr -d '\n' < "$1" | sed -n "s/.*\"$2\"[[:space:]]*:[[:space:]]*\"\{0,1\}\([^\",}]*\).*/\1/p"
  fi
}

fail() { echo "FAILED: $*" >&2; exit 1; }
get() { curl -fsSL --retry 3 "$SITE$1" -o "$2" || fail "download $1"; }

mkdir -p "$WORK"
cd "$WORK"
echo "verdict $N from $SITE"

get "/api/v1/verdicts/$N" verdict.json
PROGRAM=$(field verdict.json program)
CORPUS=$(field verdict.json corpus_sha256)
CANDIDATE=$(field verdict.json candidate_sha256)
SOURCE=$(field verdict.json source_sha256)
REPORT=$(field verdict.json report_sha256)
ORIGINAL=$(field verdict.json original_sha256)
[ -n "$PROGRAM" ] && [ -n "$CORPUS" ] && [ -n "$CANDIDATE" ] && [ -n "$REPORT" ] || fail "the verdict record is missing a hash"
echo "program    $PROGRAM"
echo "corpus     $CORPUS"
echo "candidate  $CANDIDATE"
echo "source     $SOURCE"
echo "report     $REPORT"

get /verifier/SHA256SUMS SHA256SUMS
get /verifier/replayer-src.tar.gz replayer-src.tar.gz
want=$(grep ' replayer-src.tar.gz$' SHA256SUMS | cut -d' ' -f1)
[ "$(sha replayer-src.tar.gz)" = "$want" ] || fail "replayer source hash"
echo "ok         replayer source $want"

get "/api/v1/corpus/$CORPUS" corpus.json
[ "$(sha corpus.json)" = "$CORPUS" ] || fail "corpus hash"
echo "ok         corpus bundle"

get "/api/v1/teardown/$PROGRAM/elf" original.so
get "/api/v1/candidates/$CANDIDATE" candidate.so

echo "building the replayer (cargo build --release --locked)"
rm -rf replayer
tar -xzf replayer-src.tar.gz
(cd replayer && cargo build --release --locked --quiet) || fail "replayer build"
R=./replayer/target/release/replayer

[ "$($R hash candidate.so)" = "$CANDIDATE" ] || fail "candidate binary hash"
echo "ok         candidate binary"
if [ -n "$ORIGINAL" ]; then
  [ "$($R hash original.so)" = "$ORIGINAL" ] || fail "original binary hash"
fi
echo "ok         original binary $($R hash original.so)"

echo "replaying (real corpus + $FUZZ fuzzed cases)"
$R replay --original original.so --candidate candidate.so --corpus corpus.json --fuzz "$FUZZ" --source-sha256 "$SOURCE" --out report.json
GOT=$(sha report.json)
echo "report     $GOT"
if [ "$GOT" = "$REPORT" ]; then
  echo "MATCH: the report you just produced is byte for byte the published one"
else
  fail "report hash differs from the published $REPORT (see $WORK/report.json)"
fi
