Consolidate syscalls under solana_program::syscalls, implement static syscalls (sbfv2) (#24188)

Move all syscall declarations under solana_program::syscalls. If the target
supports static syscalls (sbfv2), then actually define them statically to avoid
dynamic relocations.
This commit is contained in:
Alessandro Decina 2022-05-26 23:01:55 +10:00 committed by GitHub
parent 1925b4f5cb
commit 2c8a3f39a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 181 additions and 210 deletions

View File

@ -1,6 +1,8 @@
#![cfg(target_os = "solana")]
use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, msg, program::invoke,
program_error::ProgramError, pubkey::Pubkey, system_instruction,
program_error::ProgramError, pubkey::Pubkey, syscalls::sol_invoke_signed_c, system_instruction,
};
#[derive(Debug)]
@ -53,16 +55,6 @@ struct SolSignerSeedsC {
len: u64,
}
extern "C" {
fn sol_invoke_signed_c(
instruction_addr: *const SolInstruction,
account_infos_addr: *const SolAccountInfo,
account_infos_len: u64,
signers_seeds_addr: *const SolSignerSeedsC,
signers_seeds_len: u64,
) -> u64;
}
const READONLY_ACCOUNTS: &[SolAccountInfo] = &[
SolAccountInfo {
is_signer: false,
@ -143,10 +135,10 @@ fn process_instruction(
check!(
0,
sol_invoke_signed_c(
&instruction as *const _,
READONLY_ACCOUNTS.as_ptr(),
&instruction as *const _ as *const _,
READONLY_ACCOUNTS.as_ptr() as *const _,
READONLY_ACCOUNTS.len() as u64,
std::ptr::null::<SolSignerSeedsC>(),
std::ptr::null(),
0,
)
);
@ -178,10 +170,10 @@ fn process_instruction(
check!(
0,
sol_invoke_signed_c(
&instruction as *const _,
new_accounts.as_ptr(),
&instruction as *const _ as *const _,
new_accounts.as_ptr() as *const _,
new_accounts.len() as u64,
std::ptr::null::<SolSignerSeedsC>(),
std::ptr::null(),
0,
)
);

View File

@ -73,3 +73,6 @@ targets = ["x86_64-unknown-linux-gnu"]
[lib]
crate-type = ["cdylib", "rlib"]
[features]
default = []

View File

@ -137,12 +137,9 @@ pub fn hashv(vals: &[&[u8]]) -> Hash {
// Call via a system call to perform the calculation
#[cfg(target_os = "solana")]
{
extern "C" {
fn sol_blake3(vals: *const u8, val_len: u64, hash_result: *mut u8) -> u64;
}
let mut hash_result = [0; HASH_BYTES];
unsafe {
sol_blake3(
crate::syscalls::sol_blake3(
vals as *const _ as *const u8,
vals.len() as u64,
&mut hash_result as *mut _ as *mut u8,

View File

@ -157,12 +157,9 @@ pub fn hashv(vals: &[&[u8]]) -> Hash {
// Call via a system call to perform the calculation
#[cfg(target_os = "solana")]
{
extern "C" {
fn sol_sha256(vals: *const u8, val_len: u64, hash_result: *mut u8) -> u64;
}
let mut hash_result = [0; HASH_BYTES];
unsafe {
sol_sha256(
crate::syscalls::sol_sha256(
vals as *const _ as *const u8,
vals.len() as u64,
&mut hash_result as *mut _ as *mut u8,

View File

@ -684,21 +684,11 @@ pub struct ProcessedSiblingInstruction {
pub fn get_processed_sibling_instruction(index: usize) -> Option<Instruction> {
#[cfg(target_os = "solana")]
{
extern "C" {
fn sol_get_processed_sibling_instruction(
index: u64,
meta: *mut ProcessedSiblingInstruction,
program_id: *mut Pubkey,
data: *mut u8,
accounts: *mut AccountMeta,
) -> u64;
}
let mut meta = ProcessedSiblingInstruction::default();
let mut program_id = Pubkey::default();
if 1 == unsafe {
sol_get_processed_sibling_instruction(
crate::syscalls::sol_get_processed_sibling_instruction(
index as u64,
&mut meta,
&mut program_id,
@ -712,7 +702,7 @@ pub fn get_processed_sibling_instruction(index: usize) -> Option<Instruction> {
accounts.resize_with(meta.accounts_len, AccountMeta::default);
let _ = unsafe {
sol_get_processed_sibling_instruction(
crate::syscalls::sol_get_processed_sibling_instruction(
index as u64,
&mut meta,
&mut program_id,
@ -739,12 +729,8 @@ pub const TRANSACTION_LEVEL_STACK_HEIGHT: usize = 1;
/// TRANSACTION_LEVEL_STACK_HEIGHT + 1, etc...
pub fn get_stack_height() -> usize {
#[cfg(target_os = "solana")]
{
extern "C" {
fn sol_get_stack_height() -> u64;
}
unsafe { sol_get_stack_height() as usize }
unsafe {
crate::syscalls::sol_get_stack_height() as usize
}
#[cfg(not(target_os = "solana"))]

View File

@ -137,12 +137,9 @@ pub fn hashv(vals: &[&[u8]]) -> Hash {
// Call via a system call to perform the calculation
#[cfg(target_os = "solana")]
{
extern "C" {
fn sol_keccak256(vals: *const u8, val_len: u64, hash_result: *mut u8) -> u64;
}
let mut hash_result = [0; HASH_BYTES];
unsafe {
sol_keccak256(
crate::syscalls::sol_keccak256(
vals as *const _ as *const u8,
vals.len() as u64,
&mut hash_result as *mut _ as *mut u8,

View File

@ -603,6 +603,8 @@ pub mod slot_hashes;
pub mod slot_history;
pub mod stake;
pub mod stake_history;
#[cfg(target_os = "solana")]
pub mod syscalls;
pub mod system_instruction;
pub mod system_program;
pub mod sysvar;

View File

@ -93,45 +93,31 @@ macro_rules! msg {
pub fn sol_log(message: &str) {
#[cfg(target_os = "solana")]
unsafe {
sol_log_(message.as_ptr(), message.len() as u64);
crate::syscalls::sol_log_(message.as_ptr(), message.len() as u64);
}
#[cfg(not(target_os = "solana"))]
crate::program_stubs::sol_log(message);
}
#[cfg(target_os = "solana")]
extern "C" {
fn sol_log_(message: *const u8, len: u64);
}
/// Print 64-bit values represented as hexadecimal to the log.
#[inline]
pub fn sol_log_64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) {
#[cfg(target_os = "solana")]
unsafe {
sol_log_64_(arg1, arg2, arg3, arg4, arg5);
crate::syscalls::sol_log_64_(arg1, arg2, arg3, arg4, arg5);
}
#[cfg(not(target_os = "solana"))]
crate::program_stubs::sol_log_64(arg1, arg2, arg3, arg4, arg5);
}
#[cfg(target_os = "solana")]
extern "C" {
fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64);
}
/// Print some slices as base64.
pub fn sol_log_data(data: &[&[u8]]) {
#[cfg(target_os = "solana")]
{
extern "C" {
fn sol_log_data(data: *const u8, data_len: u64);
}
unsafe { sol_log_data(data as *const _ as *const u8, data.len() as u64) };
}
unsafe {
crate::syscalls::sol_log_data(data as *const _ as *const u8, data.len() as u64)
};
#[cfg(not(target_os = "solana"))]
crate::program_stubs::sol_log_data(data);
@ -174,13 +160,8 @@ pub fn sol_log_params(accounts: &[AccountInfo], data: &[u8]) {
pub fn sol_log_compute_units() {
#[cfg(target_os = "solana")]
unsafe {
sol_log_compute_units_();
crate::syscalls::sol_log_compute_units_();
}
#[cfg(not(target_os = "solana"))]
crate::program_stubs::sol_log_compute_units();
}
#[cfg(target_os = "solana")]
extern "C" {
fn sol_log_compute_units_();
}

View File

@ -66,18 +66,8 @@ pub fn invoke_signed_unchecked(
) -> ProgramResult {
#[cfg(target_os = "solana")]
{
extern "C" {
fn sol_invoke_signed_rust(
instruction_addr: *const u8,
account_infos_addr: *const u8,
account_infos_len: u64,
signers_seeds_addr: *const u8,
signers_seeds_len: u64,
) -> u64;
}
let result = unsafe {
sol_invoke_signed_rust(
crate::syscalls::sol_invoke_signed_rust(
instruction as *const _ as *const u8,
account_infos as *const _ as *const u8,
account_infos.len() as u64,
@ -101,13 +91,9 @@ pub const MAX_RETURN_DATA: usize = 1024;
/// Set a program's return data
pub fn set_return_data(data: &[u8]) {
#[cfg(target_os = "solana")]
{
extern "C" {
fn sol_set_return_data(data: *const u8, length: u64);
}
unsafe { sol_set_return_data(data.as_ptr(), data.len() as u64) };
}
unsafe {
crate::syscalls::sol_set_return_data(data.as_ptr(), data.len() as u64)
};
#[cfg(not(target_os = "solana"))]
crate::program_stubs::sol_set_return_data(data)
@ -119,15 +105,16 @@ pub fn get_return_data() -> Option<(Pubkey, Vec<u8>)> {
{
use std::cmp::min;
extern "C" {
fn sol_get_return_data(data: *mut u8, length: u64, program_id: *mut Pubkey) -> u64;
}
let mut buf = [0u8; MAX_RETURN_DATA];
let mut program_id = Pubkey::default();
let size =
unsafe { sol_get_return_data(buf.as_mut_ptr(), buf.len() as u64, &mut program_id) };
let size = unsafe {
crate::syscalls::sol_get_return_data(
buf.as_mut_ptr(),
buf.len() as u64,
&mut program_id,
)
};
if size == 0 {
None

View File

@ -34,13 +34,8 @@
#[inline]
pub fn sol_memcpy(dst: &mut [u8], src: &[u8], n: usize) {
#[cfg(target_os = "solana")]
{
extern "C" {
fn sol_memcpy_(dst: *mut u8, src: *const u8, n: u64);
}
unsafe {
sol_memcpy_(dst.as_mut_ptr(), src.as_ptr(), n as u64);
}
unsafe {
crate::syscalls::sol_memcpy_(dst.as_mut_ptr(), src.as_ptr(), n as u64);
}
#[cfg(not(target_os = "solana"))]
@ -69,12 +64,7 @@ pub fn sol_memcpy(dst: &mut [u8], src: &[u8], n: usize) {
#[inline]
pub unsafe fn sol_memmove(dst: *mut u8, src: *mut u8, n: usize) {
#[cfg(target_os = "solana")]
{
extern "C" {
fn sol_memmove_(dst: *mut u8, src: *const u8, n: u64);
}
sol_memmove_(dst, src, n as u64);
}
crate::syscalls::sol_memmove_(dst, src, n as u64);
#[cfg(not(target_os = "solana"))]
crate::program_stubs::sol_memmove(dst, src, n);
@ -109,13 +99,8 @@ pub fn sol_memcmp(s1: &[u8], s2: &[u8], n: usize) -> i32 {
let mut result = 0;
#[cfg(target_os = "solana")]
{
extern "C" {
fn sol_memcmp_(s1: *const u8, s2: *const u8, n: u64, result: *mut i32);
}
unsafe {
sol_memcmp_(s1.as_ptr(), s2.as_ptr(), n as u64, &mut result as *mut i32);
}
unsafe {
crate::syscalls::sol_memcmp_(s1.as_ptr(), s2.as_ptr(), n as u64, &mut result as *mut i32);
}
#[cfg(not(target_os = "solana"))]
@ -151,13 +136,8 @@ pub fn sol_memcmp(s1: &[u8], s2: &[u8], n: usize) -> i32 {
#[inline]
pub fn sol_memset(s: &mut [u8], c: u8, n: usize) {
#[cfg(target_os = "solana")]
{
extern "C" {
fn sol_memset_(s: *mut u8, c: u8, n: u64);
}
unsafe {
sol_memset_(s.as_mut_ptr(), c, n as u64);
}
unsafe {
crate::syscalls::sol_memset_(s.as_mut_ptr(), c, n as u64);
}
#[cfg(not(target_os = "solana"))]

View File

@ -494,19 +494,10 @@ impl Pubkey {
// Call via a system call to perform the calculation
#[cfg(target_os = "solana")]
{
extern "C" {
fn sol_try_find_program_address(
seeds_addr: *const u8,
seeds_len: u64,
program_id_addr: *const u8,
address_bytes_addr: *const u8,
bump_seed_addr: *const u8,
) -> u64;
}
let mut bytes = [0; 32];
let mut bump_seed = std::u8::MAX;
let result = unsafe {
sol_try_find_program_address(
crate::syscalls::sol_try_find_program_address(
seeds as *const _ as *const u8,
seeds.len() as u64,
program_id as *const _ as *const u8,
@ -596,17 +587,9 @@ impl Pubkey {
// Call via a system call to perform the calculation
#[cfg(target_os = "solana")]
{
extern "C" {
fn sol_create_program_address(
seeds_addr: *const u8,
seeds_len: u64,
program_id_addr: *const u8,
address_bytes_addr: *const u8,
) -> u64;
}
let mut bytes = [0; 32];
let result = unsafe {
sol_create_program_address(
crate::syscalls::sol_create_program_address(
seeds as *const _ as *const u8,
seeds.len() as u64,
program_id as *const _ as *const u8,
@ -631,12 +614,9 @@ impl Pubkey {
/// Log a `Pubkey` from a program
pub fn log(&self) {
#[cfg(target_os = "solana")]
{
extern "C" {
fn sol_log_pubkey(pubkey_addr: *const u8);
}
unsafe { sol_log_pubkey(self.as_ref() as *const _ as *const u8) };
}
unsafe {
crate::syscalls::sol_log_pubkey(self.as_ref() as *const _ as *const u8)
};
#[cfg(not(target_os = "solana"))]
crate::program_stubs::sol_log(&self.to_string());

View File

@ -76,18 +76,9 @@ pub fn secp256k1_recover(
) -> Result<Secp256k1Pubkey, Secp256k1RecoverError> {
#[cfg(target_os = "solana")]
{
extern "C" {
fn sol_secp256k1_recover(
hash: *const u8,
recovery_id: u64,
signature: *const u8,
result: *mut u8,
) -> u64;
}
let mut pubkey_buffer = [0u8; SECP256K1_PUBLIC_KEY_LENGTH];
let result = unsafe {
sol_secp256k1_recover(
crate::syscalls::sol_secp256k1_recover(
hash.as_ptr(),
recovery_id as u64,
signature.as_ptr(),

120
sdk/program/src/syscalls.rs Normal file
View File

@ -0,0 +1,120 @@
use crate::{
instruction::{AccountMeta, ProcessedSiblingInstruction},
pubkey::Pubkey,
};
#[cfg(target_feature = "static-syscalls")]
macro_rules! define_syscall {
(fn $name:ident($($arg:ident: $typ:ty),*) -> $ret:ty) => {
#[inline]
pub unsafe fn $name($($arg: $typ),*) -> $ret {
// this enum is used to force the hash to be computed in a const context
#[repr(usize)]
enum Syscall {
Code = sys_hash(stringify!($name)),
}
let syscall: extern "C" fn($($arg: $typ),*) -> $ret = core::mem::transmute(Syscall::Code);
syscall($($arg),*)
}
};
(fn $name:ident($($arg:ident: $typ:ty),*)) => {
define_syscall!(fn $name($($arg: $typ),*) -> ());
}
}
#[cfg(not(target_feature = "static-syscalls"))]
macro_rules! define_syscall {
(fn $name:ident($($arg:ident: $typ:ty),*) -> $ret:ty) => {
extern "C" {
pub fn $name($($arg: $typ),*) -> $ret;
}
};
(fn $name:ident($($arg:ident: $typ:ty),*)) => {
define_syscall!(fn $name($($arg: $typ),*) -> ());
}
}
define_syscall!(fn sol_log_(message: *const u8, len: u64));
define_syscall!(fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64));
define_syscall!(fn sol_log_compute_units_());
define_syscall!(fn sol_log_pubkey(pubkey_addr: *const u8));
define_syscall!(fn sol_create_program_address(seeds_addr: *const u8, seeds_len: u64, program_id_addr: *const u8, address_bytes_addr: *const u8) -> u64);
define_syscall!(fn sol_try_find_program_address(seeds_addr: *const u8, seeds_len: u64, program_id_addr: *const u8, address_bytes_addr: *const u8, bump_seed_addr: *const u8) -> u64);
define_syscall!(fn sol_sha256(vals: *const u8, val_len: u64, hash_result: *mut u8) -> u64);
define_syscall!(fn sol_keccak256(vals: *const u8, val_len: u64, hash_result: *mut u8) -> u64);
define_syscall!(fn sol_secp256k1_recover(hash: *const u8, recovery_id: u64, signature: *const u8, result: *mut u8) -> u64);
define_syscall!(fn sol_blake3(vals: *const u8, val_len: u64, hash_result: *mut u8) -> u64);
define_syscall!(fn sol_zk_token_elgamal_op(op: u64, ct_0: *const u8, ct_1: *const u8, ct_result: *mut u8) -> u64);
define_syscall!(fn sol_zk_token_elgamal_op_with_lo_hi(op: u64, ct_0: *const u8, ct_1_lo: *const u8, ct_1_hi: *const u8, ct_result: *mut u8) -> u64);
define_syscall!(fn sol_zk_token_elgamal_op_with_scalar(op: u64, ct: *const u8, scalar: u64, ct_result: *mut u8) -> u64);
define_syscall!(fn sol_get_clock_sysvar(addr: *mut u8) -> u64);
define_syscall!(fn sol_get_epoch_schedule_sysvar(addr: *mut u8) -> u64);
define_syscall!(fn sol_get_fees_sysvar(addr: *mut u8) -> u64);
define_syscall!(fn sol_get_rent_sysvar(addr: *mut u8) -> u64);
define_syscall!(fn sol_memcpy_(dst: *mut u8, src: *const u8, n: u64));
define_syscall!(fn sol_memmove_(dst: *mut u8, src: *const u8, n: u64));
define_syscall!(fn sol_memcmp_(s1: *const u8, s2: *const u8, n: u64, result: *mut i32));
define_syscall!(fn sol_memset_(s: *mut u8, c: u8, n: u64));
define_syscall!(fn sol_invoke_signed_c(instruction_addr: *const u8, account_infos_addr: *const u8, account_infos_len: u64, signers_seeds_addr: *const u8, signers_seeds_len: u64) -> u64);
define_syscall!(fn sol_invoke_signed_rust(instruction_addr: *const u8, account_infos_addr: *const u8, account_infos_len: u64, signers_seeds_addr: *const u8, signers_seeds_len: u64) -> u64);
define_syscall!(fn sol_set_return_data(data: *const u8, length: u64));
define_syscall!(fn sol_get_return_data(data: *mut u8, length: u64, program_id: *mut Pubkey) -> u64);
define_syscall!(fn sol_log_data(data: *const u8, data_len: u64));
define_syscall!(fn sol_get_processed_sibling_instruction(index: u64, meta: *mut ProcessedSiblingInstruction, program_id: *mut Pubkey, data: *mut u8, accounts: *mut AccountMeta) -> u64);
define_syscall!(fn sol_get_stack_height() -> u64);
define_syscall!(fn sol_curve_validate_point(curve_id: u64, point: *const u8, result: *mut u8) -> u64);
define_syscall!(fn sol_curve_op(curve_id: u64, op_id: u64, left_point: *const u8, right_point: *const u8, result: *mut u8) -> u64);
define_syscall!(fn sol_curve_multiscalar_mul(curve_id: u64, scalars: *const u8, points: *const u8, result: *mut u8) -> u64);
define_syscall!(fn sol_curve_pairing_map(curve_id: u64, point: *const u8, result: *mut u8) -> u64);
#[cfg(target_feature = "static-syscalls")]
pub const fn sys_hash(name: &str) -> usize {
murmur3_32(name.as_bytes(), 0) as usize
}
#[cfg(target_feature = "static-syscalls")]
const fn murmur3_32(buf: &[u8], seed: u32) -> u32 {
const fn pre_mix(buf: [u8; 4]) -> u32 {
u32::from_le_bytes(buf)
.wrapping_mul(0xcc9e2d51)
.rotate_left(15)
.wrapping_mul(0x1b873593)
}
let mut hash = seed;
let mut i = 0;
while i < buf.len() / 4 {
let buf = [buf[i * 4], buf[i * 4 + 1], buf[i * 4 + 2], buf[i * 4 + 3]];
hash ^= pre_mix(buf);
hash = hash.rotate_left(13);
hash = hash.wrapping_mul(5).wrapping_add(0xe6546b64);
i += 1;
}
match buf.len() % 4 {
0 => {}
1 => {
hash = hash ^ pre_mix([buf[i * 4], 0, 0, 0]);
}
2 => {
hash = hash ^ pre_mix([buf[i * 4], buf[i * 4 + 1], 0, 0]);
}
3 => {
hash = hash ^ pre_mix([buf[i * 4], buf[i * 4 + 1], buf[i * 4 + 2], 0]);
}
_ => { /* unreachable!() */ }
}
hash = hash ^ buf.len() as u32;
hash = hash ^ (hash.wrapping_shr(16));
hash = hash.wrapping_mul(0x85ebca6b);
hash = hash ^ (hash.wrapping_shr(13));
hash = hash.wrapping_mul(0xc2b2ae35);
hash = hash ^ (hash.wrapping_shr(16));
hash
}

View File

@ -137,14 +137,10 @@ macro_rules! impl_sysvar_get {
let var_addr = &mut var as *mut _ as *mut u8;
#[cfg(target_os = "solana")]
let result = unsafe {
extern "C" {
fn $syscall_name(var_addr: *mut u8) -> u64;
}
$syscall_name(var_addr)
};
let result = unsafe { crate::syscalls::$syscall_name(var_addr) };
#[cfg(not(target_os = "solana"))]
let result = $crate::program_stubs::$syscall_name(var_addr);
let result = crate::program_stubs::$syscall_name(var_addr);
match result {
$crate::entrypoint::SUCCESS => Ok(var),

View File

@ -83,23 +83,7 @@ pub const MUL: u64 = 2;
// the associated functions in the appropriate trait instantiation. The `curve_op` function
// additionally takes in an `op_id` (e.g. `ADD`) that controls which associated functions to run in
// `GroupOperations`.
extern "C" {
pub fn sol_curve_validate_point(curve_id: u64, point: *const u8, result: *mut u8) -> u64;
pub fn sol_curve_op(
curve_id: u64,
op_id: u64,
left_point: *const u8,
right_point: *const u8,
result: *mut u8,
) -> u64;
pub fn sol_curve_multiscalar_mul(
curve_id: u64,
scalars: *const u8,
points: *const u8,
result: *mut u8,
) -> u64;
pub fn sol_curve_pairing_map(curve_id: u64, point: *const u8, result: *mut u8) -> u64;
}
#[cfg(target_os = "solana")]
pub use solana_program::syscalls::{
sol_curve_multiscalar_mul, sol_curve_op, sol_curve_pairing_map, sol_curve_validate_point,
};

View File

@ -101,7 +101,7 @@ mod target_arch {
) -> Option<pod::ElGamalCiphertext> {
let mut ct_result = pod::ElGamalCiphertext::zeroed();
let result = unsafe {
sol_zk_token_elgamal_op(
solana_program::syscalls::sol_zk_token_elgamal_op(
op,
&ct_0.0 as *const u8,
&ct_1.0 as *const u8,
@ -124,7 +124,7 @@ mod target_arch {
) -> Option<pod::ElGamalCiphertext> {
let mut ct_result = pod::ElGamalCiphertext::zeroed();
let result = unsafe {
sol_zk_token_elgamal_op_with_lo_hi(
solana_program::syscalls::sol_zk_token_elgamal_op_with_lo_hi(
op,
&ct_0.0 as *const u8,
&ct_1_lo.0 as *const u8,
@ -147,7 +147,7 @@ mod target_arch {
) -> Option<pod::ElGamalCiphertext> {
let mut ct_result = pod::ElGamalCiphertext::zeroed();
let result = unsafe {
sol_zk_token_elgamal_op_with_scalar(
solana_program::syscalls::sol_zk_token_elgamal_op_with_scalar(
op,
&ct.0 as *const u8,
scalar,
@ -207,28 +207,6 @@ mod target_arch {
pub const OP_ADD: u64 = 0;
pub const OP_SUB: u64 = 1;
extern "C" {
pub fn sol_zk_token_elgamal_op(
op: u64,
ct_0: *const u8,
ct_1: *const u8,
ct_result: *mut u8,
) -> u64;
pub fn sol_zk_token_elgamal_op_with_lo_hi(
op: u64,
ct_0: *const u8,
ct_1_lo: *const u8,
ct_1_hi: *const u8,
ct_result: *mut u8,
) -> u64;
pub fn sol_zk_token_elgamal_op_with_scalar(
op: u64,
ct: *const u8,
scalar: u64,
ct_result: *mut u8,
) -> u64;
}
#[cfg(test)]
mod tests {
use {