LCOV - code coverage report
Current view: top level - flamenco/vm/syscall - fd_vm_syscall_curve.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 197 342 57.6 %
Date: 2025-07-15 04:56:17 Functions: 4 5 80.0 %

          Line data    Source code
       1             : #include "fd_vm_syscall.h"
       2             : 
       3             : #include "../../../ballet/ed25519/fd_curve25519.h"
       4             : #include "../../../ballet/ed25519/fd_ristretto255.h"
       5             : 
       6             : int
       7             : fd_vm_syscall_sol_curve_validate_point( /**/            void *  _vm,
       8             :                                         /**/            ulong   curve_id,
       9             :                                         /**/            ulong   point_addr,
      10             :                                         FD_PARAM_UNUSED ulong   r3,
      11             :                                         FD_PARAM_UNUSED ulong   r4,
      12             :                                         FD_PARAM_UNUSED ulong   r5,
      13           0 :                                         /**/            ulong * _ret ) {
      14             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L871 */
      15           0 :   fd_vm_t * vm = (fd_vm_t *)_vm;
      16           0 :   ulong     ret = 1UL; /* by default return Ok(1) == error */
      17             : 
      18           0 :   uchar const * point = NULL;
      19           0 :   switch( curve_id ) {
      20             : 
      21           0 :   case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS:
      22             : 
      23           0 :     FD_VM_CU_UPDATE( vm, FD_VM_CURVE25519_EDWARDS_VALIDATE_POINT_COST );
      24           0 :     point = FD_VM_MEM_HADDR_LD( vm, point_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
      25           0 :     ret = (ulong)!fd_ed25519_point_validate( point ); /* 0 if valid point, 1 if not */
      26           0 :     break;
      27             : 
      28           0 :   case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO:
      29             : 
      30           0 :     FD_VM_CU_UPDATE( vm, FD_VM_CURVE25519_RISTRETTO_VALIDATE_POINT_COST );
      31           0 :     point = FD_VM_MEM_HADDR_LD( vm, point_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
      32           0 :     ret = (ulong)!fd_ristretto255_point_validate( point ); /* 0 if valid point, 1 if not */
      33           0 :     break;
      34             : 
      35           0 :   default:
      36             :     /* https://github.com/anza-xyz/agave/blob/5b3390b99a6e7665439c623062c1a1dda2803524/programs/bpf_loader/src/syscalls/mod.rs#L919-L928 */
      37           0 :     if( FD_FEATURE_ACTIVE_BANK( vm->instr_ctx->txn_ctx->bank, abort_on_invalid_curve ) ) {
      38           0 :       FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE );
      39           0 :       return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
      40           0 :     }
      41           0 :   }
      42             : 
      43           0 :   *_ret = ret;
      44           0 :   return FD_VM_SUCCESS;
      45           0 : }
      46             : 
      47             : int
      48             : fd_vm_syscall_sol_curve_group_op( void *  _vm,
      49             :                                   ulong   curve_id,
      50             :                                   ulong   group_op,
      51             :                                   ulong   left_input_addr,
      52             :                                   ulong   right_input_addr,
      53             :                                   ulong   result_point_addr,
      54          12 :                                   ulong * _ret ) {
      55             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L928 */
      56          12 :   fd_vm_t * vm = (fd_vm_t *)_vm;
      57          12 :   ulong     ret = 1UL; /* by default return Ok(1) == error */
      58             : 
      59             :   /* Note: we don't strictly follow the Rust implementation, but instead combine
      60             :      common code across switch cases. Similar to fd_vm_syscall_sol_alt_bn128_group_op. */
      61             : 
      62             : /* MATCH_ID_OP allows us to unify 2 switch/case into 1.
      63             :    For better readability, we also temp define EDWARDS, RISTRETTO.
      64             : 
      65             :    The first time we check that both curve_id and group_op are valid
      66             :    with 2 nested switch/case. Using MATCH_ID_OP leads to undesidered
      67             :    edge cases. The second time, when we know that curve_id and group_op
      68             :    are correct, then we can use MATCH_ID_OP and a single switch/case. */
      69          24 : #define MATCH_ID_OP(crv_id,grp_op) ((crv_id << 4) | grp_op)
      70          12 : #define EDWARDS   FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS
      71          12 : #define RISTRETTO FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO
      72             : 
      73          12 :   ulong cost = 0UL;
      74          12 :   switch( curve_id ) {
      75             : 
      76           0 :   case EDWARDS:
      77           0 :     switch( group_op ) {
      78             : 
      79           0 :     case FD_VM_SYSCALL_SOL_CURVE_ADD:
      80           0 :       cost = FD_VM_CURVE25519_EDWARDS_ADD_COST;
      81           0 :       break;
      82             : 
      83           0 :     case FD_VM_SYSCALL_SOL_CURVE_SUB:
      84           0 :       cost = FD_VM_CURVE25519_EDWARDS_SUBTRACT_COST;
      85           0 :       break;
      86             : 
      87           0 :     case FD_VM_SYSCALL_SOL_CURVE_MUL:
      88           0 :       cost = FD_VM_CURVE25519_EDWARDS_MULTIPLY_COST;
      89           0 :       break;
      90             : 
      91           0 :     default:
      92           0 :       goto invalid_error;
      93           0 :     }
      94           0 :     break;
      95             : 
      96          12 :   case RISTRETTO:
      97          12 :     switch( group_op ) {
      98             : 
      99           6 :     case FD_VM_SYSCALL_SOL_CURVE_ADD:
     100           6 :       cost = FD_VM_CURVE25519_RISTRETTO_ADD_COST;
     101           6 :       break;
     102             : 
     103           3 :     case FD_VM_SYSCALL_SOL_CURVE_SUB:
     104           3 :       cost = FD_VM_CURVE25519_RISTRETTO_SUBTRACT_COST;
     105           3 :       break;
     106             : 
     107           3 :     case FD_VM_SYSCALL_SOL_CURVE_MUL:
     108           3 :       cost = FD_VM_CURVE25519_RISTRETTO_MULTIPLY_COST;
     109           3 :       break;
     110             : 
     111           0 :     default:
     112           0 :       goto invalid_error;
     113          12 :     }
     114          12 :     break;
     115             : 
     116          12 :   default:
     117           0 :     goto invalid_error;
     118          12 :   }
     119             : 
     120             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L944-L947 */
     121          24 :   FD_VM_CU_UPDATE( vm, cost );
     122             : 
     123             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L949-L958 */
     124             : 
     125             :   /* Note: left_input_addr is a point for add, sub, BUT it's a scalar for mul.
     126             :      However, from a memory mapping perspective it's always 32 bytes, so we unify the code. */
     127          36 :   uchar const * inputL = FD_VM_MEM_HADDR_LD( vm, left_input_addr,   FD_VM_ALIGN_RUST_POD_U8_ARRAY, 32UL );
     128          36 :   uchar const * inputR = FD_VM_MEM_HADDR_LD( vm, right_input_addr,  FD_VM_ALIGN_RUST_POD_U8_ARRAY, FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
     129             : 
     130          12 :   switch( MATCH_ID_OP( curve_id, group_op ) ) {
     131             : 
     132           0 :   case MATCH_ID_OP( EDWARDS, FD_VM_SYSCALL_SOL_CURVE_ADD ): {
     133           0 :     fd_ed25519_point_t p0[1], p1[1], r[1];
     134           0 :     if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p0, inputL ) ) ) {
     135           0 :       goto soft_error;
     136           0 :     }
     137           0 :     if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p1, inputR ) ) ) {
     138           0 :       goto soft_error;
     139           0 :     }
     140             : 
     141             :     /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1098-L1102 */
     142           0 :     fd_vm_haddr_query_t result_query = {
     143           0 :       .vaddr    = result_point_addr,
     144           0 :       .align    = FD_VM_ALIGN_RUST_POD_U8_ARRAY,
     145           0 :       .sz       = FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ,
     146           0 :       .is_slice = 0,
     147           0 :     };
     148             : 
     149           0 :     fd_vm_haddr_query_t * queries[] = { &result_query };
     150           0 :     FD_VM_TRANSLATE_MUT( vm, queries );
     151             : 
     152           0 :     fd_ed25519_point_add( r, p0, p1 );
     153           0 :     fd_ed25519_point_tobytes( result_query.haddr, r );
     154           0 :     ret = 0UL;
     155           0 :     break;
     156           0 :   }
     157             : 
     158           0 :   case MATCH_ID_OP( EDWARDS, FD_VM_SYSCALL_SOL_CURVE_SUB ): {
     159           0 :     fd_ed25519_point_t p0[1], p1[1], r[1];
     160           0 :     if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p0, inputL ) ) ) {
     161           0 :       goto soft_error;
     162           0 :     }
     163           0 :     if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p1, inputR ) ) ) {
     164           0 :       goto soft_error;
     165           0 :     }
     166             : 
     167             :     /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1127-L1131 */
     168           0 :     fd_vm_haddr_query_t result_query = {
     169           0 :       .vaddr    = result_point_addr,
     170           0 :       .align    = FD_VM_ALIGN_RUST_POD_U8_ARRAY,
     171           0 :       .sz       = FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ,
     172           0 :       .is_slice = 0,
     173           0 :     };
     174             : 
     175           0 :     fd_vm_haddr_query_t * queries[] = { &result_query };
     176           0 :     FD_VM_TRANSLATE_MUT( vm, queries );
     177             : 
     178           0 :     fd_ed25519_point_sub( r, p0, p1 );
     179           0 :     fd_ed25519_point_tobytes( result_query.haddr, r );
     180           0 :     ret = 0UL;
     181           0 :     break;
     182           0 :   }
     183             : 
     184           0 :   case MATCH_ID_OP( EDWARDS, FD_VM_SYSCALL_SOL_CURVE_MUL ): {
     185           0 :     fd_ed25519_point_t p[1], r[1];
     186           0 :     if( FD_UNLIKELY( !fd_curve25519_scalar_validate( inputL ) ) ) {
     187           0 :       goto soft_error;
     188           0 :     }
     189           0 :     if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p, inputR ) ) ) {
     190           0 :       goto soft_error;
     191           0 :     }
     192             : 
     193             :     /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1156-L1160 */
     194           0 :     fd_vm_haddr_query_t result_query = {
     195           0 :       .vaddr    = result_point_addr,
     196           0 :       .align    = FD_VM_ALIGN_RUST_POD_U8_ARRAY,
     197           0 :       .sz       = FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ,
     198           0 :       .is_slice = 0,
     199           0 :     };
     200             : 
     201           0 :     fd_vm_haddr_query_t * queries[] = { &result_query };
     202           0 :     FD_VM_TRANSLATE_MUT( vm, queries );
     203             : 
     204           0 :     fd_ed25519_scalar_mul( r, inputL, p );
     205           0 :     fd_ed25519_point_tobytes( result_query.haddr, r );
     206           0 :     ret = 0UL;
     207           0 :     break;
     208           0 :   }
     209             : 
     210           6 :   case MATCH_ID_OP( RISTRETTO, FD_VM_SYSCALL_SOL_CURVE_ADD ): {
     211           6 :     fd_ristretto255_point_t p0[1], p1[1], r[1];
     212           6 :     if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p0, inputL ) ) ) {
     213           0 :       goto soft_error;
     214           0 :     }
     215           6 :     if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p1, inputR ) ) ) {
     216           0 :       goto soft_error;
     217           0 :     }
     218             : 
     219             :     /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1195-L1199 */
     220           6 :     fd_vm_haddr_query_t result_query = {
     221           6 :       .vaddr    = result_point_addr,
     222           6 :       .align    = FD_VM_ALIGN_RUST_POD_U8_ARRAY,
     223           6 :       .sz       = FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ,
     224           6 :       .is_slice = 0,
     225           6 :     };
     226             : 
     227           6 :     fd_vm_haddr_query_t * queries[] = { &result_query };
     228           6 :     FD_VM_TRANSLATE_MUT( vm, queries );
     229             : 
     230           6 :     fd_ristretto255_point_add( r, p0, p1 );
     231           6 :     fd_ristretto255_point_tobytes( result_query.haddr, r );
     232           6 :     ret = 0UL;
     233           6 :     break;
     234           6 :   }
     235             : 
     236           3 :   case MATCH_ID_OP( RISTRETTO, FD_VM_SYSCALL_SOL_CURVE_SUB ): {
     237           3 :     fd_ristretto255_point_t p0[1], p1[1], r[1];
     238           3 :     if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p0, inputL ) ) ) {
     239           0 :       goto soft_error;
     240           0 :     }
     241           3 :     if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p1, inputR ) ) ) {
     242           0 :       goto soft_error;
     243           0 :     }
     244             : 
     245             :     /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1226-L1230 */
     246           3 :     fd_vm_haddr_query_t result_query = {
     247           3 :       .vaddr    = result_point_addr,
     248           3 :       .align    = FD_VM_ALIGN_RUST_POD_U8_ARRAY,
     249           3 :       .sz       = FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ,
     250           3 :       .is_slice = 0,
     251           3 :     };
     252             : 
     253           3 :     fd_vm_haddr_query_t * queries[] = { &result_query };
     254           3 :     FD_VM_TRANSLATE_MUT( vm, queries );
     255             : 
     256           3 :     fd_ristretto255_point_sub( r, p0, p1 );
     257           3 :     fd_ristretto255_point_tobytes( result_query.haddr, r );
     258           3 :     ret = 0UL;
     259           3 :     break;
     260           3 :   }
     261             : 
     262           3 :   case MATCH_ID_OP( RISTRETTO, FD_VM_SYSCALL_SOL_CURVE_MUL ): {
     263           3 :     fd_ristretto255_point_t p[1], r[1];
     264           3 :     if( FD_UNLIKELY( !fd_curve25519_scalar_validate( inputL ) ) ) {
     265           0 :       goto soft_error;
     266           0 :     }
     267           3 :     if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p, inputR ) ) ) {
     268           0 :       goto soft_error;
     269           0 :     }
     270             : 
     271             :     /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1255-L1259 */
     272           3 :     fd_vm_haddr_query_t result_query = {
     273           3 :       .vaddr    = result_point_addr,
     274           3 :       .align    = FD_VM_ALIGN_RUST_POD_U8_ARRAY,
     275           3 :       .sz       = FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ,
     276           3 :       .is_slice = 0,
     277           3 :     };
     278             : 
     279           3 :     fd_vm_haddr_query_t * queries[] = { &result_query };
     280           3 :     FD_VM_TRANSLATE_MUT( vm, queries );
     281             : 
     282           3 :     fd_ristretto255_scalar_mul( r, inputL, p );
     283           3 :     fd_ristretto255_point_tobytes( result_query.haddr, r );
     284           3 :     ret = 0UL;
     285           3 :     break;
     286           3 :   }
     287             : 
     288           0 :   default:
     289             :     /* COV: this can never happen because of the previous switch */
     290           0 :     return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
     291          36 :   }
     292             : 
     293          12 : soft_error:
     294          12 :   *_ret = ret;
     295          12 :   return FD_VM_SUCCESS;
     296           0 : #undef MATCH_ID_OP
     297           0 : #undef EDWARDS
     298           0 : #undef RISTRETTO
     299             : 
     300           0 : invalid_error:
     301             :   /* https://github.com/anza-xyz/agave/blob/5b3390b99a6e7665439c623062c1a1dda2803524/programs/bpf_loader/src/syscalls/mod.rs#L1135-L1156 */
     302           0 :   if( FD_FEATURE_ACTIVE_BANK( vm->instr_ctx->txn_ctx->bank, abort_on_invalid_curve ) ) {
     303           0 :     FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE );
     304           0 :     return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
     305           0 :   }
     306           0 :   *_ret = 1UL;
     307           0 :   return FD_VM_SUCCESS;
     308           0 : }
     309             : 
     310             : /* multi_scalar_mul_edwards computes a MSM on curve25519.
     311             : 
     312             :    This function is equivalent to
     313             :    zk-token-sdk::edwards::multi_scalar_mul_edwards
     314             : 
     315             :    https://github.com/solana-labs/solana/blob/v1.17.7/zk-token-sdk/src/curve25519/edwards.rs#L116
     316             : 
     317             :    Specifically it takes as input byte arrays and takes care of scalars
     318             :    validation and points decompression.  It then invokes ballet MSM
     319             :    function fd_ed25519_multi_scalar_mul.  To avoid dynamic allocation,
     320             :    the full MSM is done in batches of FD_BALLET_CURVE25519_MSM_BATCH_SZ. */
     321             : 
     322             : static fd_ed25519_point_t *
     323             : multi_scalar_mul_edwards( fd_ed25519_point_t * r,
     324             :                           uchar const *        scalars,
     325             :                           uchar const *        points,
     326           3 :                           ulong                cnt ) {
     327             :   /* Validate all scalars first (fast) */
     328           9 :   for( ulong i=0UL; i<cnt; i++ ) {
     329           6 :     if( FD_UNLIKELY( !fd_curve25519_scalar_validate ( scalars + i*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ ) ) ) {
     330           0 :       return NULL;
     331           0 :     }
     332           6 :   }
     333             : 
     334             :   /* Static allocation of a batch of decompressed points */
     335           3 :   fd_ed25519_point_t tmp[1];
     336           3 :   fd_ed25519_point_t A[ FD_BALLET_CURVE25519_MSM_BATCH_SZ ];
     337             : 
     338           3 :   fd_ed25519_point_set_zero( r );
     339           6 :   for( ulong i=0UL; i<cnt; i+=FD_BALLET_CURVE25519_MSM_BATCH_SZ ) {
     340           3 :     ulong batch_cnt = fd_ulong_min( cnt-i, FD_BALLET_CURVE25519_MSM_BATCH_SZ );
     341             : 
     342             :     /* Decompress (and validate) points */
     343           9 :     for( ulong j=0UL; j<batch_cnt; j++ ) {
     344             :       //TODO: use fd_ed25519_point_frombytes_2x
     345           6 :       if( FD_UNLIKELY( !fd_ed25519_point_frombytes( &A[j], points + j*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ ) ) ) {
     346           0 :         return NULL;
     347           0 :       }
     348           6 :     }
     349             : 
     350           3 :     fd_ed25519_multi_scalar_mul( tmp, scalars, A, batch_cnt );
     351           3 :     fd_ed25519_point_add( r, r, tmp );
     352           3 :     points  += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ *batch_cnt;
     353           3 :     scalars += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ*batch_cnt;
     354           3 :   }
     355             : 
     356           3 :   return r;
     357           3 : }
     358             : 
     359             : /* multi_scalar_mul_ristretto computes a MSM on ristretto255.
     360             :    See multi_scalar_mul_edwards for details. */
     361             : 
     362             : static fd_ed25519_point_t *
     363             : multi_scalar_mul_ristretto( fd_ristretto255_point_t * r,
     364             :                             uchar const *             scalars,
     365             :                             uchar const *             points,
     366           3 :                             ulong                     cnt ) {
     367             :   /* Validate all scalars first (fast) */
     368           9 :   for( ulong i=0UL; i<cnt; i++ ) {
     369           6 :     if( FD_UNLIKELY( !fd_curve25519_scalar_validate ( scalars + i*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ ) ) ) {
     370           0 :       return NULL;
     371           0 :     }
     372           6 :   }
     373             : 
     374             :   /* Static allocation of a batch of decompressed points */
     375           3 :   fd_ristretto255_point_t tmp[1];
     376           3 :   fd_ristretto255_point_t A[ FD_BALLET_CURVE25519_MSM_BATCH_SZ ];
     377             : 
     378           3 :   fd_ristretto255_point_set_zero( r );
     379           6 :   for( ulong i=0UL; i<cnt; i+=FD_BALLET_CURVE25519_MSM_BATCH_SZ ) {
     380           3 :     ulong batch_cnt = fd_ulong_min( cnt-i, FD_BALLET_CURVE25519_MSM_BATCH_SZ );
     381             : 
     382             :     /* Decompress (and validate) points */
     383           9 :     for( ulong j=0UL; j<batch_cnt; j++ ) {
     384             :       //TODO: use fd_ristretto255_point_frombytes_2x
     385           6 :       if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( &A[j], points + j*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ ) ) ) {
     386           0 :         return NULL;
     387           0 :       }
     388           6 :     }
     389             : 
     390           3 :     fd_ristretto255_multi_scalar_mul( tmp, scalars, A, batch_cnt );
     391           3 :     fd_ristretto255_point_add( r, r, tmp );
     392           3 :     points  += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ *batch_cnt;
     393           3 :     scalars += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ*batch_cnt;
     394           3 :   }
     395             : 
     396           3 :   return r;
     397           3 : }
     398             : 
     399             : #undef BATCH_MAX
     400             : 
     401             : int
     402             : fd_vm_syscall_sol_curve_multiscalar_mul( void *  _vm,
     403             :                                          ulong   curve_id,
     404             :                                          ulong   scalars_addr,
     405             :                                          ulong   points_addr,
     406             :                                          ulong   points_len,
     407             :                                          ulong   result_point_addr,
     408          15 :                                          ulong * _ret ) {
     409             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1129 */
     410          15 :   fd_vm_t * vm = (fd_vm_t *)_vm;
     411          15 :   ulong     ret = 1UL; /* by default return Ok(1) == error */
     412             : 
     413             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1143-L1151 */
     414          15 :   if( FD_UNLIKELY( points_len > 512 ) ) {
     415           3 :     FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );
     416           3 :     return FD_VM_SYSCALL_ERR_INVALID_LENGTH; /* SyscallError::InvalidLength */
     417           3 :   }
     418             : 
     419             :   /* Note: we don't strictly follow the Rust implementation, but instead combine
     420             :      common code across switch cases. Similar to fd_vm_syscall_sol_alt_bn128_group_op. */
     421             : 
     422          12 :   ulong base_cost = 0UL;
     423          12 :   ulong incremental_cost = 0UL;
     424          12 :   switch( curve_id ) {
     425           6 :   case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS:
     426           6 :     base_cost = FD_VM_CURVE25519_EDWARDS_MSM_BASE_COST;
     427           6 :     incremental_cost = FD_VM_CURVE25519_EDWARDS_MSM_INCREMENTAL_COST;
     428           6 :     break;
     429             : 
     430           3 :   case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO:
     431           3 :     base_cost = FD_VM_CURVE25519_RISTRETTO_MSM_BASE_COST;
     432           3 :     incremental_cost = FD_VM_CURVE25519_RISTRETTO_MSM_INCREMENTAL_COST;
     433           3 :     break;
     434             : 
     435           3 :   default:
     436             :     /* https://github.com/anza-xyz/agave/blob/5b3390b99a6e7665439c623062c1a1dda2803524/programs/bpf_loader/src/syscalls/mod.rs#L1262-L1271 */
     437           3 :     if( FD_FEATURE_ACTIVE( vm->instr_ctx->txn_ctx->slot, &vm->instr_ctx->txn_ctx->features, abort_on_invalid_curve ) ) {
     438           3 :       FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE );
     439           3 :       return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
     440           3 :     }
     441           0 :     goto soft_error;
     442          12 :   }
     443             : 
     444             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1155-L1164 */
     445           9 :   ulong cost = fd_ulong_sat_add(
     446           9 :     base_cost,
     447           9 :     fd_ulong_sat_mul(
     448           9 :       incremental_cost,
     449           9 :       fd_ulong_sat_sub( points_len, 1 )
     450           9 :     )
     451           9 :   );
     452           9 :   FD_VM_CU_UPDATE( vm, cost );
     453             : 
     454             :   /* Edge case points_len==0.
     455             :      Agave computes the MSM, that returns the point at infinity, and stores the result.
     456             :      This means that we have to mem map result, and then set the point at infinity,
     457             :      that is 0x0100..00 for Edwards and 0x00..00 for Ristretto. */
     458           9 :   if ( FD_UNLIKELY( points_len==0 ) ) {
     459           3 :     uchar * result = FD_VM_MEM_HADDR_ST( vm, result_point_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
     460           0 :     memset( result, 0, 32 );
     461           0 :     result[0] = curve_id==FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS ? 1 : 0;
     462           0 :     *_ret = 0;
     463           0 :     return FD_VM_SUCCESS;
     464           3 :   }
     465             : 
     466             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1166-L1178 */
     467          18 :   uchar const * scalars = FD_VM_MEM_HADDR_LD( vm, scalars_addr, FD_VM_ALIGN_RUST_POD_U8_ARRAY, points_len*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ );
     468          18 :   uchar const * points  = FD_VM_MEM_HADDR_LD( vm, points_addr,  FD_VM_ALIGN_RUST_POD_U8_ARRAY, points_len*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ );
     469             : 
     470           0 :   switch( curve_id ) {
     471             : 
     472           3 :   case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS: {
     473             :     /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1180-L1189 */
     474           3 :     fd_ed25519_point_t _r[1];
     475           3 :     fd_ed25519_point_t * r = multi_scalar_mul_edwards( _r, scalars, points, points_len );
     476             : 
     477           3 :     if( FD_LIKELY( r ) ) {
     478             :       /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1339-L1343 */
     479           3 :       fd_vm_haddr_query_t result_query = {
     480           3 :         .vaddr    = result_point_addr,
     481           3 :         .align    = FD_VM_ALIGN_RUST_POD_U8_ARRAY,
     482           3 :         .sz       = FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ,
     483           3 :         .is_slice = 0,
     484           3 :       };
     485             : 
     486           3 :       fd_vm_haddr_query_t * queries[] = { &result_query };
     487           3 :       FD_VM_TRANSLATE_MUT( vm, queries );
     488             : 
     489           3 :       fd_ed25519_point_tobytes( result_query.haddr, r );
     490           3 :       ret = 0UL;
     491           3 :     }
     492           3 :     break;
     493           3 :   }
     494             : 
     495           3 :   case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO: {
     496           3 :     fd_ristretto255_point_t _r[1];
     497           3 :     fd_ristretto255_point_t * r = multi_scalar_mul_ristretto( _r, scalars, points, points_len );
     498             : 
     499           3 :     if( FD_LIKELY( r ) ) {
     500             :       /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1380-L1384 */
     501           3 :       fd_vm_haddr_query_t result_query = {
     502           3 :         .vaddr    = result_point_addr,
     503           3 :         .align    = FD_VM_ALIGN_RUST_POD_U8_ARRAY,
     504           3 :         .sz       = FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ,
     505           3 :         .is_slice = 0,
     506           3 :       };
     507             : 
     508           3 :       fd_vm_haddr_query_t * queries[] = { &result_query };
     509           3 :       FD_VM_TRANSLATE_MUT( vm, queries );
     510             : 
     511           3 :       fd_ristretto255_point_tobytes( result_query.haddr, r );
     512           3 :       ret = 0UL;
     513           3 :     }
     514           3 :     break;
     515           3 :   }
     516             : 
     517           3 :   default:
     518             :     /* COV: this can never happen because of the previous switch */
     519           0 :     return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
     520          18 :   }
     521             : 
     522           6 : soft_error:
     523           6 :   *_ret = ret;
     524           6 :   return FD_VM_SUCCESS;
     525          18 : }

Generated by: LCOV version 1.14