Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

syscall: add harness ctx #41

Merged
merged 1 commit into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions generators/all.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash
TESTS_PATH=./test-vectors/instr/inputs/20240425
rm ${TESTS_PATH}/ed25519/* ; python3 generators/ed25519.py
rm ${TESTS_PATH}/secp256k1/* ; python3 generators/secp256k1.py
rm ${TESTS_PATH}/syscalls/keccak256/*
rm ${TESTS_PATH}/syscalls/blake3/*
rm ${TESTS_PATH}/syscalls/sha256/* ; python3 generators/syscalls_hash.py
rm ${TESTS_PATH}/syscalls/secp256k1/* ; python3 generators/syscalls_secp256k1.py
rm ${TESTS_PATH}/syscalls/poseidon/* ; python3 generators/syscalls_poseidon.py
rm ${TESTS_PATH}/syscalls/alt_bn128/* ; python3 generators/syscalls_alt_bn128.py
1 change: 0 additions & 1 deletion generators/ed25519.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import hashlib
import test_suite.invoke_pb2 as pb
from dataclasses import dataclass
import datetime
import requests

OUTPUT_DIR = "./test-vectors/instr/inputs/20240425/ed25519"
Expand Down
5 changes: 2 additions & 3 deletions generators/secp256k1.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from eth_hash.auto import keccak
import test_suite.invoke_pb2 as pb
from dataclasses import dataclass
import datetime
import requests

OUTPUT_DIR = "./test-vectors/instr/inputs/20240425/secp256k1"
Expand Down Expand Up @@ -78,8 +77,8 @@
# tested above
# https://github.com/anza-xyz/agave/blob/v1.18.12/sdk/src/secp256k1_instruction.rs#L975-L978
# signature fails to decode
[1, 32, 0, 0, 12, 0, 0, 97, 0, 5, 0, 0, 129, 246, 169, 169, 105, 76, 208, 128, 223, 135, 27, 68, 249, 42, 201, 69, 55, 2, 173, 101, 255, 196, 198, 193, 237, 0, 14, 83, 87, 183, 25, 69, 136, 43, 251, 73, 44, 194, 141, 230, 102, 16, 220, 6, 46, 214, 214, 125, 120, 16, 103, 254, 39, 121, 88, 223, 156, 229, 186, 211, 38, 101, 196, 233, 125, 150, 136, 177, 123, 197, 48, 219, 28, 26, 10, 76, 198, 127, 91, 80, 88, 191, 6, 3, 255, 104, 101, 108, 108, 111],
# \--- pubkey (eth) ---/ \--- sig ---/ \--- msg ---/
[1, 32, 0, 0, 12, 0, 0, 97, 0, 5, 0, 0, 129, 246, 169, 169, 105, 76, 208, 128, 223, 135, 27, 68, 249, 42, 201, 69, 55, 2, 173, 101, 255, 196, 198, 193, 237, 0, 14, 83, 87, 183, 25, 69, 136, 43, 251, 73, 44, 194, 141, 230, 102, 16, 220, 6, 46, 214, 214, 125, 120, 16, 103, 254, 39, 121, 88, 223, 156, 229, 186, 211, 38, 101, 196, 233, 125, 150, 136, 177, 123, 197, 48, 219, 28, 26, 10, 76, 198, 127, 91, 80, 88, 191, 6, 3, 1, 104, 101, 108, 108, 111],
# \--- pubkey (eth) ---/ \--- sig ---/ \--- msg ---/

# InvalidRecoveryId (result: 2)
# https://github.com/anza-xyz/agave/blob/v1.18.12/sdk/src/secp256k1_instruction.rs#L981C43-L981C60
Expand Down
898 changes: 898 additions & 0 deletions generators/syscalls_alt_bn128.py

Large diffs are not rendered by default.

208 changes: 208 additions & 0 deletions generators/syscalls_hash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import hashlib
import test_suite.invoke_pb2 as pb
import struct

OUTPUT_DIR = "./test-vectors/instr/inputs/20240425/syscalls"
HEAP_START = 0x300000000
CU_BASE = 85
CU_PER_BYTE = 1 # this is actually every 2 bytes...
CU_MEM_OP = 10

def heap_vec(data_vec, start):
res = []
last = start + len(data_vec) * 16
for data in data_vec:
res += struct.pack('<Q', last)
res += struct.pack('<Q', len(data))
last += len(data)
for data in data_vec:
if isinstance(data, str):
res += bytes(data, "ascii")
else:
res += bytes(data)
return res

def exact_cu_cost(data_vec):
return CU_BASE + sum([max((len(x) / 2)*CU_PER_BYTE, CU_MEM_OP) for x in data_vec])

# https://github.com/solana-labs/solana/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L2521
test_hello = ["hello"]
test_hello_world = ["hello", " world"]
test_vectors_hash = [
{
# empty hash = valid
"heap_prefix": [0]*32,
"result_addr": HEAP_START,
"cu_avail": CU_BASE
},
{
# hash("hello") = valid
"heap_prefix": [0]*32 + heap_vec(test_hello, HEAP_START + 32),
"vals_addr": HEAP_START + 32,
"vals_len": len(test_hello),
"result_addr": HEAP_START,
"cu_avail": exact_cu_cost(test_hello)
},
{
# hash("hello") = valid
# result at the end
"heap_prefix": heap_vec(test_hello, HEAP_START + 32) + [0]*32,
"vals_addr": HEAP_START,
"vals_len": len(test_hello),
"result_addr": HEAP_START + len(heap_vec(test_hello, HEAP_START + 32)),
"cu_avail": exact_cu_cost(test_hello)
},
{
# hash("hello") = valid
# result overwrites input
"heap_prefix": heap_vec(test_hello, HEAP_START) + [0]*11,
"vals_addr": HEAP_START,
"vals_len": len(test_hello),
"result_addr": HEAP_START,
"cu_avail": exact_cu_cost(test_hello)
},
{
# hash("hello world") = valid
"heap_prefix": [0]*32 + heap_vec(test_hello_world, HEAP_START + 32),
"vals_addr": HEAP_START + 32,
"vals_len": len(test_hello_world),
"result_addr": HEAP_START,
"cu_avail": exact_cu_cost(test_hello_world)
},
# SyscallError::TooManySlices
# https://github.com/solana-labs/solana/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1919
{
# fail max slices
"heap_prefix": [0]*32 + heap_vec(test_hello_world, HEAP_START + 32),
"vals_addr": HEAP_START + 32,
"vals_len": 20001,
"result_addr": HEAP_START,
"cu_avail": exact_cu_cost(test_hello_world)
},
# ComputationalBudgetExceeded
# https://github.com/solana-labs/solana/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1922
{
# fail cu begin
"heap_prefix": [0]*32 + heap_vec(test_hello_world, HEAP_START + 32),
"vals_addr": HEAP_START + 32,
"vals_len": len(test_hello_world),
"result_addr": HEAP_START,
"cu_avail": 1
},

# translate_slice_mut
# https://github.com/solana-labs/solana/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1924-L1929
# TODO: cover all errors, e.g. UnalignedPointer
{
# fail alloc result
"heap_prefix": [0]*32 + heap_vec(test_hello_world, HEAP_START + 32),
"vals_addr": HEAP_START + 32,
"vals_len": len(test_hello_world),
"result_addr": HEAP_START - 1,
"cu_avail": exact_cu_cost(test_hello_world)
},
{
# fail alloc result
"heap_prefix": [0]*32 + heap_vec(test_hello_world, HEAP_START + 32),
"vals_addr": HEAP_START + 32,
"vals_len": len(test_hello_world),
"result_addr": HEAP_START - 64,
"cu_avail": exact_cu_cost(test_hello_world)
},
{
# fail alloc result (2)
"heap_prefix": [0]*32 + heap_vec(test_hello_world, HEAP_START + 32),
"vals_addr": HEAP_START + 32,
"vals_len": len(test_hello_world),
"result_addr": HEAP_START + 100,
"cu_avail": exact_cu_cost(test_hello_world)
},

# translate_slice
# https://github.com/solana-labs/solana/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1932-L1937
# TODO: cover all errors, e.g. UnalignedPointer
{
# fail alloc vec
"heap_prefix": [0]*32 + heap_vec(test_hello_world, HEAP_START + 32),
"vals_addr": HEAP_START - 1,
"vals_len": len(test_hello_world),
"result_addr": HEAP_START,
"cu_avail": exact_cu_cost(test_hello_world)
},
{
# fail alloc vec (2)
"heap_prefix": [0]*32 + heap_vec(test_hello_world, HEAP_START + 32),
"vals_addr": HEAP_START + 100,
"vals_len": len(test_hello_world),
"result_addr": HEAP_START,
"cu_avail": exact_cu_cost(test_hello_world)
},
# https://github.com/solana-labs/solana/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1939-L1944
{
# fail alloc elem
"heap_prefix": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100],
"vals_addr": HEAP_START + 32,
"vals_len": len(test_hello_world),
"result_addr": HEAP_START,
"cu_avail": exact_cu_cost(test_hello_world)
},
{
# fail alloc elem
"heap_prefix": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 3, 0, 0, 0, 105, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100],
"vals_addr": HEAP_START + 32,
"vals_len": len(test_hello_world),
"result_addr": HEAP_START,
"cu_avail": exact_cu_cost(test_hello_world)
},

# ComputationalBudgetExceeded
# https://github.com/solana-labs/solana/blob/v1.18.12/programs/bpf_loader/src/syscalls/mod.rs#L1945-L1952
{
# fail cu middle
"heap_prefix": [0]*32 + heap_vec(test_hello_world, HEAP_START + 32),
"vals_addr": HEAP_START + 32,
"vals_len": len(test_hello_world),
"result_addr": HEAP_START,
"cu_avail": CU_BASE
},
{
# fail cu end
"heap_prefix": [0]*32 + heap_vec(test_hello_world, HEAP_START + 32),
"vals_addr": HEAP_START + 32,
"vals_len": len(test_hello_world),
"result_addr": HEAP_START,
"cu_avail": CU_BASE + 12
},
]

def _into_key_data(key_prefix, test_vectors):
return [(key_prefix + str(j), data) for j, data in enumerate(test_vectors)]

test_vectors = _into_key_data("h", test_vectors_hash)

if __name__ == "__main__":
print("Generating syscalls sha256, keccak256, blake3 tests...")

for (key, test) in test_vectors:
for hash in ["sha256", "keccak256", "blake3"]:
heap_prefix = test.get("heap_prefix", [])
syscall_ctx = pb.SyscallContext()
syscall_ctx.syscall_invocation.function_name = bytes("sol_" + hash, "ascii")
syscall_ctx.syscall_invocation.heap_prefix = bytes(heap_prefix)
syscall_ctx.vm_ctx.heap_max = len(heap_prefix)
syscall_ctx.vm_ctx.r1 = test.get("vals_addr", 0)
syscall_ctx.vm_ctx.r2 = test.get("vals_len", 0)
syscall_ctx.vm_ctx.r3 = test.get("result_addr", 0)
syscall_ctx.instr_ctx.cu_avail = test.get("cu_avail", 0)
syscall_ctx.instr_ctx.program_id = bytes([0]*32) # solfuzz-agave expectes a program_id
syscall_ctx.vm_ctx.rodata = b"x" # fd expects some bytes

syscall_ctx.instr_ctx.epoch_context.features.features.extend([0xe994a4b8eeea84f4]) # enable blake3

filename = str(key) + "_" + hashlib.sha3_256(syscall_ctx.instr_ctx.data).hexdigest()[:16]

serialized_instr = syscall_ctx.SerializeToString(deterministic=True)
with open(f"{OUTPUT_DIR}/{hash}/{filename}.bin", "wb") as f:
f.write(serialized_instr)

print("done!")
Loading
Loading