Source previews
acurast.sh
3868 bytes · SHA-256 1c7a080219896c1d5c6d8f12263a20403dddbacaf8187d8cdcd45011ca16b9de
#!/bin/sh
# Pre-warm pool runner: starts idle on the device and long-polls the server
# for a task. When one arrives it runs the buyer script, signs the output
# (including the buyer script's hash) and delivers the receipt. One-shot:
# exits after the task, or when the idle budget runs out.
# Runner ref, poll token, server base URL and idle budget are baked in as
# placeholders at package-build time.
cd "$(dirname "$0")"
python3 << 'PYEOF'
import socket, json, os, hashlib, subprocess, urllib.request, urllib.error, time
RUNNER_REF = "054cbac296cd77bb"
POLL_TOKEN = "c16cf549d4b3d70a957f955d81b40071"
SERVER_BASE = "https://tee.papers.tech"
IDLE_BUDGET_S = int("3540")
MAX_CAPTURE = 64 * 1024
def call_bridge(method, params=None):
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect('\0' + os.environ['BRIDGE_SOCKET'])
req = json.dumps({"jsonrpc": "2.0", "id": 1, "method": method, "params": params or []})
s.sendall((req + "\n").encode())
res = json.loads(s.recv(65536).decode())
s.close()
if "error" in res:
raise Exception("Bridge error: " + str(res["error"]))
return res["result"]
# 1. Long-poll the server until a task arrives or the idle budget runs out.
# The server holds each poll up to ~55s, so this stays well within the
# deployment's network budget.
poll_url = "%s/pool/task/%s?token=%s" % (SERVER_BASE, RUNNER_REF, POLL_TOKEN)
deadline = time.time() + IDLE_BUDGET_S
task = None
while time.time() < deadline:
try:
res = urllib.request.urlopen(poll_url, timeout=70)
body = res.read().decode()
if res.status == 200 and body.strip():
task = json.loads(body)
break
except urllib.error.HTTPError as e:
if e.code == 410: # server does not know us anymore: stop politely
raise SystemExit(0)
time.sleep(10)
except Exception:
time.sleep(10)
if task is None:
print("idle budget exhausted, exiting unused")
raise SystemExit(0)
# 2. Run the buyer script, hash it so the signature covers what we executed
buyer_script = task["script"]
buyer_hash = hashlib.sha256(buyer_script.encode()).hexdigest()
with open("buyer.sh", "w") as f:
f.write(buyer_script)
try:
proc = subprocess.run(["sh", "buyer.sh"], capture_output=True, timeout=int(task["timeoutS"]))
exit_code = proc.returncode
stdout = proc.stdout.decode("utf-8", "replace")[:MAX_CAPTURE]
stderr = proc.stderr.decode("utf-8", "replace")[:MAX_CAPTURE]
except subprocess.TimeoutExpired as e:
exit_code = -1
stdout = (e.stdout or b"").decode("utf-8", "replace")[:MAX_CAPTURE]
stderr = "buyer script timed out after %ss" % task["timeoutS"]
# 3. Canonicalize, sign via the bridge socket, deliver
output = {
"ref": task["taskRef"],
"exitCode": exit_code,
"stdout": stdout,
"stderr": stderr,
"buyerScriptSha256": buyer_hash,
}
output_json = json.dumps(output, sort_keys=True, separators=(",", ":"))
digest = hashlib.sha256(output_json.encode()).hexdigest()
public_key = call_bridge("signer_publicKey", [{"curve": "secp256k1"}])["publicKey"]
signature = call_bridge("signer_sign", [{"curve": "secp256k1", "bytes": digest}])["bytes"]
receipt = {
"output": output_json,
"sha256": digest,
"signature": signature,
"publicKey": public_key,
}
print("receipt:", json.dumps(receipt))
body = json.dumps(receipt).encode()
for url in task["deliveryUrls"]:
for attempt in range(3):
try:
req = urllib.request.Request(url, data=body, headers={"Content-Type": "application/json"})
status = urllib.request.urlopen(req, timeout=30).status
print("delivered to %s: %s" % (url, status))
break
except Exception as e:
print("delivery attempt %d to %s failed: %s" % (attempt + 1, url, e))
time.sleep(5)
PYEOF
manifest.json
294 bytes · SHA-256 4d19eb43aa04a9e64a9d7ce2e6737fdc90602368526042af59840bf8f9e1e09a
{
"version": 1,
"restartPolicy": "No",
"entrypoint": "acurast.sh",
"image": {
"url": "https://github.com/termux/proot-distro/releases/download/v4.30.1/ubuntu-questing-aarch64-pd-v4.30.1.tar.xz",
"sha256": "5ab35b90cd9a9f180656261ba400a135c4c01c2da4b74522118342f985c2d328"
}
}