diff --git a/src/runtime/program/lib.zig b/src/runtime/program/lib.zig index 7ca06c82b..b2681add0 100644 --- a/src/runtime/program/lib.zig +++ b/src/runtime/program/lib.zig @@ -1,3 +1,5 @@ pub const system_program = @import("system_program/lib.zig"); pub const test_program_execute = @import("test_program_execute.zig"); + +pub const precompile_programs = @import("precompile_programs/lib.zig"); diff --git a/src/runtime/program/precompile_programs/ed25519.zig b/src/runtime/program/precompile_programs/ed25519.zig new file mode 100644 index 000000000..9aa9f9381 --- /dev/null +++ b/src/runtime/program/precompile_programs/ed25519.zig @@ -0,0 +1,296 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const sig = @import("../../../sig.zig"); +const precompile_programs = sig.runtime.program.precompile_programs; + +const PrecompileProgramError = precompile_programs.PrecompileProgramError; +const getInstructionValue = precompile_programs.getInstructionValue; +const getInstructionData = precompile_programs.getInstructionData; + +const Ed25519 = std.crypto.sign.Ed25519; + +pub const ED25519_DATA_START = ED25519_SIGNATURE_OFFSETS_SERIALIZED_SIZE + + ED25519_SIGNATURE_OFFSETS_START; +pub const ED25519_PUBKEY_SERIALIZED_SIZE = 32; +pub const ED25519_SIGNATURE_OFFSETS_SERIALIZED_SIZE = 14; +pub const ED25519_SIGNATURE_OFFSETS_START = 2; +pub const ED25519_SIGNATURE_SERIALIZED_SIZE = 64; + +comptime { + std.debug.assert(ED25519_PUBKEY_SERIALIZED_SIZE == Ed25519.PublicKey.encoded_length); + std.debug.assert(ED25519_SIGNATURE_SERIALIZED_SIZE == Ed25519.Signature.encoded_length); + std.debug.assert(ED25519_SIGNATURE_OFFSETS_SERIALIZED_SIZE == @sizeOf(Ed25519SignatureOffsets)); +} + +pub const Ed25519SignatureOffsets = extern struct { + /// Offset to ed25519 signature of 64 bytes. + signature_offset: u16 = 0, + /// Instruction index to find signature. + signature_instruction_idx: u16 = 0, + /// Offset to public key of 32 bytes. + pubkey_offset: u16 = 0, + /// Instruction index to find public key. + pubkey_instruction_idx: u16 = 0, + /// Offset to start of message data. + message_data_offset: u16 = 0, + /// Size of message data. + message_data_size: u16 = 0, + /// Index of instruction data to get message data. + message_instruction_idx: u16 = 0, +}; + +// TODO: support verify_strict feature https://github.com/anza-xyz/agave/pull/1876/ +// https://github.com/anza-xyz/agave/blob/a8aef04122068ec36a7af0721e36ee58efa0bef2/sdk/src/ed25519_instruction.rs#L88 +// https://github.com/firedancer-io/firedancer/blob/af74882ffb2c24783a82718dbc5111a94e1b5f6f/src/flamenco/runtime/program/fd_precompiles.c#L118 +pub fn verify( + current_instruction_data: []const u8, + all_instruction_datas: []const []const u8, +) PrecompileProgramError!void { + const data = current_instruction_data; + if (data.len < ED25519_DATA_START) { + if (data.len == 2 and data[0] == 0) return; + return error.InvalidInstructionDataSize; + } + + const n_signatures = data[0]; + if (n_signatures == 0) return error.InvalidInstructionDataSize; + + const expected_data_size: u64 = ED25519_SIGNATURE_OFFSETS_START + + @as(u64, n_signatures) * ED25519_SIGNATURE_OFFSETS_SERIALIZED_SIZE; + if (data.len < expected_data_size) return error.InvalidInstructionDataSize; + + for (0..n_signatures) |i| { + const offset = ED25519_SIGNATURE_OFFSETS_START + + i * ED25519_SIGNATURE_OFFSETS_SERIALIZED_SIZE; + + const sig_offsets: *align(1) const Ed25519SignatureOffsets = @ptrCast(data.ptr + offset); + + const signature = try getInstructionValue( + Ed25519.Signature, + data, + all_instruction_datas, + sig_offsets.signature_instruction_idx, + sig_offsets.signature_offset, + ); + const pubkey = try getInstructionValue( + Ed25519.PublicKey, + data, + all_instruction_datas, + sig_offsets.pubkey_instruction_idx, + sig_offsets.pubkey_offset, + ); + const msg = try getInstructionData( + sig_offsets.message_data_size, + data, + all_instruction_datas, + sig_offsets.message_instruction_idx, + sig_offsets.message_data_offset, + ); + signature.verify(msg, pubkey.*) catch return error.InvalidSignature; + } +} + +// https://github.com/anza-xyz/agave/blob/a8aef04122068ec36a7af0721e36ee58efa0bef2/sdk/src/ed25519_instruction.rs#L35 +pub fn newInstruction( + allocator: std.mem.Allocator, + keypair: Ed25519.KeyPair, + message: []const u8, +) !sig.core.Instruction { + if (!builtin.is_test) @compileError("newInstruction is only for use in tests"); + std.debug.assert(message.len < std.math.maxInt(u16)); + + const signature = try keypair.sign(message, null); + + const num_signatures: u8 = 1; + const pubkey_offset = ED25519_DATA_START; + const signature_offset = pubkey_offset + ED25519_PUBKEY_SERIALIZED_SIZE; + const message_data_offset = signature_offset + ED25519_SIGNATURE_SERIALIZED_SIZE; + + const offsets: Ed25519SignatureOffsets = .{ + .signature_offset = signature_offset, + .signature_instruction_idx = std.math.maxInt(u16), + .pubkey_offset = pubkey_offset, + .pubkey_instruction_idx = std.math.maxInt(u16), + .message_data_offset = message_data_offset, + .message_data_size = @intCast(message.len), + .message_instruction_idx = std.math.maxInt(u16), + }; + + var instruction_data = try std.ArrayList(u8).initCapacity( + allocator, + message_data_offset + message.len, + ); + errdefer instruction_data.deinit(); + + // add 2nd byte for padding, so that offset structure is aligned + instruction_data.appendSliceAssumeCapacity(&.{ num_signatures, 0 }); + instruction_data.appendSliceAssumeCapacity(std.mem.asBytes(&offsets)); + std.debug.assert(instruction_data.items.len == pubkey_offset); + instruction_data.appendSliceAssumeCapacity(&keypair.public_key.toBytes()); + std.debug.assert(instruction_data.items.len == signature_offset); + instruction_data.appendSliceAssumeCapacity(&signature.toBytes()); + std.debug.assert(instruction_data.items.len == message_data_offset); + instruction_data.appendSliceAssumeCapacity(message); + + return .{ + .program_id = sig.runtime.ids.PRECOMPILE_ED25519_PROGRAM_ID, + .accounts = &.{}, + .data = try instruction_data.toOwnedSlice(), + }; +} + +// https://github.com/anza-xyz/agave/blob/a8aef04122068ec36a7af0721e36ee58efa0bef2/sdk/src/ed25519_instruction.rs#L258 +fn testCase( + num_signatures: u16, + offsets: Ed25519SignatureOffsets, +) PrecompileProgramError!void { + if (!builtin.is_test) @compileError("testCase is only for use in tests"); + + var instruction_data: [ED25519_DATA_START]u8 align(2) = undefined; + @memcpy(instruction_data[0..2], std.mem.asBytes(&num_signatures)); + @memcpy(instruction_data[2..], std.mem.asBytes(&offsets)); + + return try verify(&instruction_data, &.{&(.{0} ** 100)}); +} + +// https://github.com/anza-xyz/agave/blob/a8aef04122068ec36a7af0721e36ee58efa0bef2/sdk/src/ed25519_instruction.rs#L279 +test "ed25519 invalid offsets" { + const allocator = std.testing.allocator; + var instruction_data = try std.ArrayListAligned(u8, 2).initCapacity( + allocator, + ED25519_DATA_START, + ); + defer instruction_data.deinit(); + + const offsets: Ed25519SignatureOffsets = .{}; + + // Set up instruction data with invalid size + instruction_data.appendSliceAssumeCapacity(std.mem.asBytes(&1)); + instruction_data.appendSliceAssumeCapacity(std.mem.asBytes(&offsets)); + try instruction_data.resize(instruction_data.items.len - 1); + + try std.testing.expectEqual( + verify(instruction_data.items, &.{}), + error.InvalidInstructionDataSize, + ); + + // invalid signature instruction index + const invalid_signature_offsets: Ed25519SignatureOffsets = .{ + .signature_instruction_idx = 1, + }; + try std.testing.expectEqual( + testCase(1, invalid_signature_offsets), + error.InvalidDataOffsets, + ); + + // invalid message instruction index + const invalid_message_offsets: Ed25519SignatureOffsets = .{ + .message_instruction_idx = 1, + }; + try std.testing.expectEqual( + testCase(1, invalid_message_offsets), + error.InvalidDataOffsets, + ); + + // invalid public key instruction index + const invalid_pubkey_offsets: Ed25519SignatureOffsets = .{ + .pubkey_instruction_idx = 1, + }; + try std.testing.expectEqual( + testCase(1, invalid_pubkey_offsets), + error.InvalidDataOffsets, + ); +} + +// https://github.com/anza-xyz/agave/blob/a8aef04122068ec36a7af0721e36ee58efa0bef2/sdk/src/ed25519_instruction.rs#L326 +test "ed25519 message data offsets" { + { + const offsets: Ed25519SignatureOffsets = .{ + .message_data_offset = 99, + .message_data_size = 1, + }; + try std.testing.expectEqual( + testCase(1, offsets), + error.InvalidSignature, + ); + } + + { + const offsets: Ed25519SignatureOffsets = .{ + .message_data_offset = 100, + .message_data_size = 1, + }; + try std.testing.expectEqual( + testCase(1, offsets), + error.InvalidDataOffsets, + ); + } + + { + const offsets: Ed25519SignatureOffsets = .{ + .message_data_offset = 100, + .message_data_size = 1000, + }; + try std.testing.expectEqual( + testCase(1, offsets), + error.InvalidDataOffsets, + ); + } + + { + const offsets: Ed25519SignatureOffsets = .{ + .message_data_offset = std.math.maxInt(u16), + .message_data_size = std.math.maxInt(u16), + }; + try std.testing.expectEqual( + testCase(1, offsets), + error.InvalidDataOffsets, + ); + } +} + +// https://github.com/anza-xyz/agave/blob/a8aef04122068ec36a7af0721e36ee58efa0bef2/sdk/src/ed25519_instruction.rs#L369 +test "ed25519 pubkey offset" { + { + const offsets: Ed25519SignatureOffsets = .{ + .pubkey_offset = std.math.maxInt(u16), + }; + try std.testing.expectEqual( + testCase(1, offsets), + error.InvalidDataOffsets, + ); + } + + { + const offsets: Ed25519SignatureOffsets = .{ + .pubkey_offset = 100 - ED25519_PUBKEY_SERIALIZED_SIZE + 1, + }; + try std.testing.expectEqual( + testCase(1, offsets), + error.InvalidDataOffsets, + ); + } +} + +// https://github.com/anza-xyz/agave/blob/a8aef04122068ec36a7af0721e36ee58efa0bef2/sdk/src/ed25519_instruction.rs#L389-L390 +test "ed25519 signature offset" { + { + const offsets: Ed25519SignatureOffsets = .{ + .signature_offset = std.math.maxInt(u16), + }; + try std.testing.expectEqual( + testCase(1, offsets), + error.InvalidDataOffsets, + ); + } + + { + const offsets: Ed25519SignatureOffsets = .{ + .signature_offset = 100 - ED25519_SIGNATURE_SERIALIZED_SIZE + 1, + }; + try std.testing.expectEqual( + testCase(1, offsets), + error.InvalidDataOffsets, + ); + } +} diff --git a/src/runtime/program/precompile_programs/lib.zig b/src/runtime/program/precompile_programs/lib.zig new file mode 100644 index 000000000..ae3014a97 --- /dev/null +++ b/src/runtime/program/precompile_programs/lib.zig @@ -0,0 +1,245 @@ +const std = @import("std"); +const sig = @import("../../../sig.zig"); + +pub const ed25519 = @import("ed25519.zig"); +pub const secp256k1 = @import("secp256k1.zig"); +pub const secp256r1 = @import("secp256r1.zig"); + +const Pubkey = sig.core.Pubkey; +const Ed25519 = std.crypto.sign.Ed25519; + +pub const ed25519Verify = ed25519.verify; +pub const secp256k1Verify = secp256k1.verify; +pub const secp256r1Verify = secp256r1.verify; + +/// https://github.com/anza-xyz/agave/blob/df063a8c6483ad1d2bbbba50ab0b7fd7290eb7f4/cost-model/src/block_cost_limits.rs#L15 +/// Cluster averaged compute unit to micro-sec conversion rate +pub const COMPUTE_UNIT_TO_US_RATIO: u64 = 30; +/// Number of compute units for one signature verification. +pub const SIGNATURE_COST: u64 = COMPUTE_UNIT_TO_US_RATIO * 24; +/// Number of compute units for one secp256k1 signature verification. +pub const SECP256K1_VERIFY_COST: u64 = COMPUTE_UNIT_TO_US_RATIO * 223; +/// Number of compute units for one ed25519 signature verification. +pub const ED25519_VERIFY_COST: u64 = COMPUTE_UNIT_TO_US_RATIO * 76; + +// TODO: should be moved to global features file +pub const SECP256R1_FEATURE_ID = + Pubkey.parseBase58String("sr11RdZWgbHTHxSroPALe6zgaT5A1K9LcE4nfsZS4gi") catch unreachable; + +pub const PRECOMPILES = [_]Precompile{ + .{ + .program_id = sig.runtime.ids.PRECOMPILE_ED25519_PROGRAM_ID, + .function = ed25519Verify, + .required_feature = null, + }, + .{ + .program_id = sig.runtime.ids.PRECOMPILE_SECP256K1_PROGRAM_ID, + .function = secp256k1Verify, + .required_feature = null, + }, + .{ + .program_id = sig.runtime.ids.PRECOMPILE_SECP256R1_PROGRAM_ID, + .function = secp256r1Verify, + .required_feature = SECP256R1_FEATURE_ID, + }, +}; + +// https://github.com/anza-xyz/agave/blob/f9d4939d1d6ad2783efc8ec60db058809bb87f55/cost-model/src/cost_model.rs#L115 +// https://github.com/anza-xyz/agave/blob/6ea38fce866595908486a01c7d6b7182988f3b2d/sdk/program/src/message/sanitized.rs#L378 +pub fn verifyPrecompilesComputeCost( + transaction: sig.core.Transaction, + feature_set: sig.runtime.FeatureSet, +) u64 { + // TODO: support verify_strict feature https://github.com/anza-xyz/agave/pull/1876/ + _ = feature_set; + + var n_secp256k1_instruction_signatures: u64 = 0; + var n_ed25519_instruction_signatures: u64 = 0; + + // https://github.com/anza-xyz/agave/blob/6ea38fce866595908486a01c7d6b7182988f3b2d/sdk/program/src/message/sanitized.rs#L385 + for (transaction.msg.instructions) |instruction| { + if (instruction.data.len == 0) continue; + + const program_id = transaction.msg.account_keys[instruction.program_index]; + if (program_id.equals(&sig.runtime.ids.PRECOMPILE_SECP256K1_PROGRAM_ID)) { + n_secp256k1_instruction_signatures +|= instruction.data[0]; + } + if (program_id.equals(&sig.runtime.ids.PRECOMPILE_ED25519_PROGRAM_ID)) { + n_ed25519_instruction_signatures +|= instruction.data[0]; + } + } + + return transaction.msg.signature_count *| SIGNATURE_COST +| + n_secp256k1_instruction_signatures *| SECP256K1_VERIFY_COST +| + n_ed25519_instruction_signatures *| ED25519_VERIFY_COST; +} + +pub fn verifyPrecompiles( + allocator: std.mem.Allocator, + transaction: sig.core.Transaction, + feature_set: sig.runtime.FeatureSet, +) (PrecompileProgramError || error{OutOfMemory})!void { + // could remove this alloc by passing in the transaction in directly, but maybe less clean + var instruction_datas: ?[]const []const u8 = null; + defer if (instruction_datas) |instr_datas| allocator.free(instr_datas); + + for (transaction.msg.instructions) |instruction| { + const program_id = transaction.msg.account_keys[instruction.program_index]; + for (PRECOMPILES) |precompile| { + if (!precompile.program_id.equals(&program_id)) continue; + + const precompile_feature_enabled = precompile.required_feature == null or + feature_set.active.contains(precompile.required_feature.?); + if (!precompile_feature_enabled) continue; + + const datas = instruction_datas orelse blk: { + const buf = try allocator.alloc([]const u8, transaction.msg.instructions.len); + for (transaction.msg.instructions, 0..) |instr, i| buf[i] = instr.data; + instruction_datas = buf; + break :blk buf; + }; + + try precompile.function(instruction.data, datas); + } + } +} + +pub const PrecompileFn = fn ( + current_instruction_data: []const u8, + all_instruction_datas: []const []const u8, +) PrecompileProgramError!void; + +pub const Precompile = struct { + program_id: Pubkey, + function: *const PrecompileFn, + required_feature: ?Pubkey, +}; + +// custom errors +// https://github.com/anza-xyz/agave/blob/a8aef04122068ec36a7af0721e36ee58efa0bef2/sdk/precompile-error/src/lib.rs#L6 +pub const PrecompileProgramError = error{ + InvalidPublicKey, + InvalidRecoveryId, + InvalidSignature, + InvalidDataOffsets, + InvalidInstructionDataSize, +}; + +pub fn getInstructionValue( + T: type, + current_instruction_data: []const u8, + all_instruction_datas: []const []const u8, + instruction_idx: u16, + offset: usize, +) error{InvalidDataOffsets}!*align(1) const T { + return @ptrCast(try getInstructionData( + @sizeOf(T), + current_instruction_data, + all_instruction_datas, + instruction_idx, + offset, + )); +} + +// https://github.com/firedancer-io/firedancer/blob/af74882ffb2c24783a82718dbc5111a94e1b5f6f/src/flamenco/runtime/program/fd_precompiles.c#L74 +pub fn getInstructionData( + len: usize, + current_instruction_data: []const u8, + all_instruction_datas: []const []const u8, + instruction_idx: u16, + offset: usize, +) error{InvalidDataOffsets}![]const u8 { + const data: []const u8 = if (instruction_idx == std.math.maxInt(u16)) + current_instruction_data + else data: { + if (instruction_idx >= all_instruction_datas.len) return error.InvalidDataOffsets; + break :data all_instruction_datas[instruction_idx]; + }; + + if (offset +| len > data.len) return error.InvalidDataOffsets; + return data[offset..][0..len]; +} + +test "verify ed25519" { + try verifyPrecompiles( + std.testing.allocator, + sig.core.Transaction.EMPTY, + sig.runtime.FeatureSet.EMPTY, + ); + + const bad_ed25519_tx = std.mem.zeroInit(sig.core.Transaction, .{ + .msg = .{ + .account_keys = &.{sig.runtime.ids.PRECOMPILE_ED25519_PROGRAM_ID}, + .instructions = &.{ + .{ + .program_index = 0, + .account_indexes = &.{0}, + .data = "hello", + }, + }, + }, + .version = .legacy, + }); + + try std.testing.expectError( + error.InvalidInstructionDataSize, + verifyPrecompiles(std.testing.allocator, bad_ed25519_tx, sig.runtime.FeatureSet.EMPTY), + ); + + const keypair = try Ed25519.KeyPair.create(null); + const ed25519_instruction = try ed25519.newInstruction( + std.testing.allocator, + keypair, + "hello!", + ); + defer std.testing.allocator.free(ed25519_instruction.data); + + const ed25519_tx: sig.core.Transaction = .{ + .msg = .{ + .account_keys = &.{sig.runtime.ids.PRECOMPILE_ED25519_PROGRAM_ID}, + .instructions = &.{ + .{ .program_index = 0, .account_indexes = &.{0}, .data = ed25519_instruction.data }, + }, + .signature_count = 1, + .readonly_signed_count = 1, + .readonly_unsigned_count = 0, + .recent_blockhash = sig.core.Hash.ZEROES, + }, + .version = .legacy, + .signatures = &.{}, + }; + + try verifyPrecompiles(std.testing.allocator, ed25519_tx, sig.runtime.FeatureSet.EMPTY); +} + +test "verify cost" { + const keypair = try Ed25519.KeyPair.create(null); + const ed25519_instruction = try ed25519.newInstruction( + std.testing.allocator, + keypair, + "hello!", + ); + defer std.testing.allocator.free(ed25519_instruction.data); + + const ed25519_tx: sig.core.Transaction = .{ + .msg = .{ + .account_keys = &.{sig.runtime.ids.PRECOMPILE_ED25519_PROGRAM_ID}, + .instructions = &.{ + .{ .program_index = 0, .account_indexes = &.{0}, .data = ed25519_instruction.data }, + }, + .signature_count = 1, + .readonly_signed_count = 1, + .readonly_unsigned_count = 0, + .recent_blockhash = sig.core.Hash.ZEROES, + }, + .version = .legacy, + .signatures = &.{}, + }; + + const expected_cost = 1 *| SIGNATURE_COST +| 1 *| ED25519_VERIFY_COST; + // cross-checked with agave (FeatureSet::default()) + try std.testing.expectEqual(3000, expected_cost); + + const compute_units = verifyPrecompilesComputeCost(ed25519_tx, sig.runtime.FeatureSet.EMPTY); + try std.testing.expectEqual(expected_cost, compute_units); +} diff --git a/src/runtime/program/precompile_programs/secp256k1.zig b/src/runtime/program/precompile_programs/secp256k1.zig new file mode 100644 index 000000000..2990f428d --- /dev/null +++ b/src/runtime/program/precompile_programs/secp256k1.zig @@ -0,0 +1,50 @@ +const std = @import("std"); +const sig = @import("../../../sig.zig"); +const precompile_programs = sig.runtime.program.precompile_programs; + +const PrecompileProgramError = precompile_programs.PrecompileProgramError; + +const Keccak256 = std.crypto.hash.sha3.Keccak256; +const Secp256k1 = std.crypto.ecc.Secp256k1; +const Ecdsa = std.crypto.sign.ecdsa.Ecdsa(Secp256k1, Keccak256); + +pub const SECP256K1_DATA_START = SECP256K1_SIGNATURE_OFFSETS_SERIALIZED_SIZE + + SECP256K1_SIGNATURE_OFFSETS_START; +pub const SECP256K1_PUBKEY_SERIALIZED_SIZE = 20; +pub const SECP256K1_SIGNATURE_OFFSETS_SERIALIZED_SIZE = 11; +pub const SECP256K1_SIGNATURE_OFFSETS_START = 1; +pub const SECP256K1_SIGNATURE_SERIALIZED_SIZE = 64; + +comptime { + std.debug.assert(SECP256K1_SIGNATURE_OFFSETS_SERIALIZED_SIZE == + @bitSizeOf(Secp256k1SignatureOffsets) / 8); +} + +pub const Secp256k1SignatureOffsets = packed struct { + /// Offset to 64-byte signature plus 1-byte recovery ID. + signature_offset: u16, + /// Within the transaction, the index of the instruction whose instruction data contains the signature. + signature_instruction_idx: u8, + /// Offset to 20-byte Ethereum address. + eth_address_offset: u16, + /// Within the transaction, the index of the instruction whose instruction data contains the address. + eth_address_instruction_idx: u8, + /// Offset to start of message data. + message_data_offset: u16, + /// Size of message data in bytes. + message_data_size: u16, + /// Within the transaction, the index of the instruction whose instruction data contains the message. + message_instruction_idx: u8, +}; + +// https://github.com/firedancer-io/firedancer/blob/af74882ffb2c24783a82718dbc5111a94e1b5f6f/src/flamenco/runtime/program/fd_precompiles.c#L227 +// https://github.com/anza-xyz/agave/blob/a8aef04122068ec36a7af0721e36ee58efa0bef2/sdk/src/secp256k1_instruction.rs#L925 +pub fn verify( + current_instruction_data: []const u8, + all_instruction_datas: []const []const u8, +) PrecompileProgramError!void { + _ = current_instruction_data; + _ = all_instruction_datas; + + @panic("TODO"); +} diff --git a/src/runtime/program/precompile_programs/secp256r1.zig b/src/runtime/program/precompile_programs/secp256r1.zig new file mode 100644 index 000000000..1d4a817ce --- /dev/null +++ b/src/runtime/program/precompile_programs/secp256r1.zig @@ -0,0 +1,18 @@ +const std = @import("std"); +const sig = @import("../../../sig.zig"); +const precompile_programs = sig.runtime.program.precompile_programs; + +const PrecompileProgramError = precompile_programs.PrecompileProgramError; + +// Part of SIMD-0075, which is accepted. +// Firedancer puts this one behind an ifdef. Maybe we don't need it yet? +// https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0075-precompile-for-secp256r1-sigverify.md +// https://github.com/firedancer-io/firedancer/blob/49056135a4c7ba024cb75a45925439239904238b/src/flamenco/runtime/program/fd_precompiles.c#L376pub fn verify( +pub fn verify( + current_instruction_data: []const u8, + all_instruction_datas: []const []const u8, +) PrecompileProgramError!void { + _ = current_instruction_data; + _ = all_instruction_datas; + @panic("TODO"); +} diff --git a/src/tests.zig b/src/tests.zig index 6655a374c..b51fc6e86 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -4,6 +4,7 @@ const sig = @import("sig.zig"); comptime { refAllDeclsRecursive(sig, 2); refAllDeclsRecursive(sig.ledger, 2); + refAllDeclsRecursive(sig.runtime.program, 2); } /// Like std.testing.refAllDeclsRecursive, except: