"""Exercise the compiled program over JSON-RPC in a disposable local Surfpool SVM.""" import argparse import base64 import hashlib import json import struct import subprocess import time import urllib.error import urllib.request from pathlib import Path from solders.hash import Hash from solders.instruction import AccountMeta, Instruction from solders.keypair import Keypair from solders.pubkey import Pubkey from solders.system_program import CreateAccountParams, TransferParams, create_account, transfer from solders.transaction import Transaction ROOT = Path(__file__).resolve().parent SYSTEM = Pubkey.default() LOADER = Pubkey.from_string("BPFLoaderUpgradeab1e11111111111111111111111") SYS_RENT = Pubkey.from_string("SysvarRent111111111111111111111111111111111") SYS_CLOCK = Pubkey.from_string("SysvarC1ock11111111111111111111111111111111") AUDIENCE = hashlib.sha256(b"https://solmate.io").digest() BIN = ROOT / "build/solmate_registry.so" class RpcError(RuntimeError): pass class Rpc: def __init__(self, url, interval=0): self.url = url self.interval = interval self.last_call = 0 def call(self, method, *params): payload = json.dumps(dict(jsonrpc="2.0", id=1, method=method, params=params)).encode() req = urllib.request.Request(self.url, payload, {"Content-Type": "application/json"}) for attempt in range(5): time.sleep(max(0, self.last_call + self.interval - time.monotonic())) self.last_call = time.monotonic() try: with urllib.request.urlopen(req, timeout=25) as res: body = json.load(res) break except urllib.error.HTTPError as error: if error.code != 429 or attempt == 4: raise retry_after = error.headers.get("Retry-After", "") delay = int(retry_after) if retry_after.isdigit() else 2 ** (attempt + 1) time.sleep(max(1, delay)) if "error" in body: raise RpcError(json.dumps(body["error"])) return body["result"] def confirmed(self, sig, timeout=35): deadline = time.monotonic() + timeout while time.monotonic() < deadline: st = self.call("getSignatureStatuses", [sig])["value"][0] if st and st["err"]: raise RpcError(str(st["err"])) if st and st["confirmationStatus"] in ("confirmed", "finalized"): return sig time.sleep(0.2) raise TimeoutError(sig) def send(self, instructions, payer, others=()): h = self.call("getLatestBlockhash", {"commitment": "confirmed"})["value"]["blockhash"] tx = Transaction.new_signed_with_payer(instructions, payer.pubkey(), [payer, *others], Hash.from_string(h)) encoded = base64.b64encode(bytes(tx)).decode() sig = self.call("sendTransaction", encoded, {"encoding": "base64", "preflightCommitment": "confirmed"}) return self.confirmed(sig) def account(self, key): info = self.call("getAccountInfo", str(key), {"commitment": "confirmed", "encoding": "base64"})["value"] if info: info["data"] = base64.b64decode(info["data"][0]) return info def meta(key, signer=False, write=False): return AccountMeta(key, signer, write) def account_ix(rpc, payer, target, length): return create_account(CreateAccountParams( from_pubkey=payer.pubkey(), to_pubkey=target.pubkey(), space=length, owner=LOADER, lamports=rpc.call("getMinimumBalanceForRentExemption", length), )) def read_upload(rpc, buffer_address, authority, binary): info = rpc.account(buffer_address) header = struct.pack("