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: 160 286 55.9 %
Date: 2025-03-20 12:08:36 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( vm->instr_ctx->txn_ctx->slot, vm->instr_ctx->txn_ctx->features, 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           0 :     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 );
     142           0 :     fd_ed25519_point_add( r, p0, p1 );
     143           0 :     fd_ed25519_point_tobytes( result, r );
     144           0 :     ret = 0UL;
     145           0 :     break;
     146           0 :   }
     147             : 
     148           0 :   case MATCH_ID_OP( EDWARDS, FD_VM_SYSCALL_SOL_CURVE_SUB ): {
     149           0 :     fd_ed25519_point_t p0[1], p1[1], r[1];
     150           0 :     if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p0, inputL ) ) ) {
     151           0 :       goto soft_error;
     152           0 :     }
     153           0 :     if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p1, inputR ) ) ) {
     154           0 :       goto soft_error;
     155           0 :     }
     156             : 
     157           0 :     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 );
     158           0 :     fd_ed25519_point_sub( r, p0, p1 );
     159           0 :     fd_ed25519_point_tobytes( result, r );
     160           0 :     ret = 0UL;
     161           0 :     break;
     162           0 :   }
     163             : 
     164           0 :   case MATCH_ID_OP( EDWARDS, FD_VM_SYSCALL_SOL_CURVE_MUL ): {
     165           0 :     fd_ed25519_point_t p[1], r[1];
     166           0 :     if( FD_UNLIKELY( !fd_curve25519_scalar_validate( inputL ) ) ) {
     167           0 :       goto soft_error;
     168           0 :     }
     169           0 :     if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p, inputR ) ) ) {
     170           0 :       goto soft_error;
     171           0 :     }
     172             : 
     173           0 :     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 );
     174           0 :     fd_ed25519_scalar_mul( r, inputL, p );
     175           0 :     fd_ed25519_point_tobytes( result, r );
     176           0 :     ret = 0UL;
     177           0 :     break;
     178           0 :   }
     179             : 
     180           6 :   case MATCH_ID_OP( RISTRETTO, FD_VM_SYSCALL_SOL_CURVE_ADD ): {
     181           6 :     fd_ristretto255_point_t p0[1], p1[1], r[1];
     182           6 :     if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p0, inputL ) ) ) {
     183           0 :       goto soft_error;
     184           0 :     }
     185           6 :     if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p1, inputR ) ) ) {
     186           0 :       goto soft_error;
     187           0 :     }
     188             : 
     189           6 :     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 );
     190           6 :     fd_ristretto255_point_add( r, p0, p1 );
     191           6 :     fd_ristretto255_point_tobytes( result, r );
     192           6 :     ret = 0UL;
     193           6 :     break;
     194           6 :   }
     195             : 
     196           3 :   case MATCH_ID_OP( RISTRETTO, FD_VM_SYSCALL_SOL_CURVE_SUB ): {
     197           3 :     fd_ristretto255_point_t p0[1], p1[1], r[1];
     198           3 :     if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p0, inputL ) ) ) {
     199           0 :       goto soft_error;
     200           0 :     }
     201           3 :     if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p1, inputR ) ) ) {
     202           0 :       goto soft_error;
     203           0 :     }
     204             : 
     205           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 );
     206           3 :     fd_ristretto255_point_sub( r, p0, p1 );
     207           3 :     fd_ristretto255_point_tobytes( result, r );
     208           3 :     ret = 0UL;
     209           3 :     break;
     210           3 :   }
     211             : 
     212           3 :   case MATCH_ID_OP( RISTRETTO, FD_VM_SYSCALL_SOL_CURVE_MUL ): {
     213           3 :     fd_ristretto255_point_t p[1], r[1];
     214           3 :     if( FD_UNLIKELY( !fd_curve25519_scalar_validate( inputL ) ) ) {
     215           0 :       goto soft_error;
     216           0 :     }
     217           3 :     if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p, inputR ) ) ) {
     218           0 :       goto soft_error;
     219           0 :     }
     220             : 
     221           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 );
     222           3 :     fd_ristretto255_scalar_mul( r, inputL, p );
     223           3 :     fd_ristretto255_point_tobytes( result, r );
     224           3 :     ret = 0UL;
     225           3 :     break;
     226           3 :   }
     227             : 
     228           0 :   default:
     229             :     /* COV: this can never happen because of the previous switch */
     230           0 :     return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
     231          36 :   }
     232             : 
     233          12 : soft_error:
     234          12 :   *_ret = ret;
     235          12 :   return FD_VM_SUCCESS;
     236           0 : #undef MATCH_ID_OP
     237           0 : #undef EDWARDS
     238           0 : #undef RISTRETTO
     239             : 
     240           0 : invalid_error:
     241             :   /* https://github.com/anza-xyz/agave/blob/5b3390b99a6e7665439c623062c1a1dda2803524/programs/bpf_loader/src/syscalls/mod.rs#L1135-L1156 */
     242           0 :   if( FD_FEATURE_ACTIVE( vm->instr_ctx->txn_ctx->slot, vm->instr_ctx->txn_ctx->features, abort_on_invalid_curve ) ) {
     243           0 :     FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE );
     244           0 :     return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
     245           0 :   }
     246           0 :   *_ret = 1UL;
     247           0 :   return FD_VM_SUCCESS;
     248           0 : }
     249             : 
     250             : /* multi_scalar_mul_edwards computes a MSM on curve25519.
     251             : 
     252             :    This function is equivalent to
     253             :    zk-token-sdk::edwards::multi_scalar_mul_edwards
     254             : 
     255             :    https://github.com/solana-labs/solana/blob/v1.17.7/zk-token-sdk/src/curve25519/edwards.rs#L116
     256             : 
     257             :    Specifically it takes as input byte arrays and takes care of scalars
     258             :    validation and points decompression.  It then invokes ballet MSM
     259             :    function fd_ed25519_multi_scalar_mul.  To avoid dynamic allocation,
     260             :    the full MSM is done in batches of FD_BALLET_CURVE25519_MSM_BATCH_SZ. */
     261             : 
     262             : static fd_ed25519_point_t *
     263             : multi_scalar_mul_edwards( fd_ed25519_point_t * r,
     264             :                           uchar const *        scalars,
     265             :                           uchar const *        points,
     266           3 :                           ulong                cnt ) {
     267             :   /* Validate all scalars first (fast) */
     268           9 :   for( ulong i=0UL; i<cnt; i++ ) {
     269           6 :     if( FD_UNLIKELY( !fd_curve25519_scalar_validate ( scalars + i*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ ) ) ) {
     270           0 :       return NULL;
     271           0 :     }
     272           6 :   }
     273             : 
     274             :   /* Static allocation of a batch of decompressed points */
     275           3 :   fd_ed25519_point_t tmp[1];
     276           3 :   fd_ed25519_point_t A[ FD_BALLET_CURVE25519_MSM_BATCH_SZ ];
     277             : 
     278           3 :   fd_ed25519_point_set_zero( r );
     279           6 :   for( ulong i=0UL; i<cnt; i+=FD_BALLET_CURVE25519_MSM_BATCH_SZ ) {
     280           3 :     ulong batch_cnt = fd_ulong_min( cnt-i, FD_BALLET_CURVE25519_MSM_BATCH_SZ );
     281             : 
     282             :     /* Decompress (and validate) points */
     283           9 :     for( ulong j=0UL; j<batch_cnt; j++ ) {
     284             :       //TODO: use fd_ed25519_point_frombytes_2x
     285           6 :       if( FD_UNLIKELY( !fd_ed25519_point_frombytes( &A[j], points + j*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ ) ) ) {
     286           0 :         return NULL;
     287           0 :       }
     288           6 :     }
     289             : 
     290           3 :     fd_ed25519_multi_scalar_mul( tmp, scalars, A, batch_cnt );
     291           3 :     fd_ed25519_point_add( r, r, tmp );
     292           3 :     points  += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ *batch_cnt;
     293           3 :     scalars += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ*batch_cnt;
     294           3 :   }
     295             : 
     296           3 :   return r;
     297           3 : }
     298             : 
     299             : /* multi_scalar_mul_ristretto computes a MSM on ristretto255.
     300             :    See multi_scalar_mul_edwards for details. */
     301             : 
     302             : static fd_ed25519_point_t *
     303             : multi_scalar_mul_ristretto( fd_ristretto255_point_t * r,
     304             :                             uchar const *             scalars,
     305             :                             uchar const *             points,
     306           3 :                             ulong                     cnt ) {
     307             :   /* Validate all scalars first (fast) */
     308           9 :   for( ulong i=0UL; i<cnt; i++ ) {
     309           6 :     if( FD_UNLIKELY( !fd_curve25519_scalar_validate ( scalars + i*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ ) ) ) {
     310           0 :       return NULL;
     311           0 :     }
     312           6 :   }
     313             : 
     314             :   /* Static allocation of a batch of decompressed points */
     315           3 :   fd_ristretto255_point_t tmp[1];
     316           3 :   fd_ristretto255_point_t A[ FD_BALLET_CURVE25519_MSM_BATCH_SZ ];
     317             : 
     318           3 :   fd_ristretto255_point_set_zero( r );
     319           6 :   for( ulong i=0UL; i<cnt; i+=FD_BALLET_CURVE25519_MSM_BATCH_SZ ) {
     320           3 :     ulong batch_cnt = fd_ulong_min( cnt-i, FD_BALLET_CURVE25519_MSM_BATCH_SZ );
     321             : 
     322             :     /* Decompress (and validate) points */
     323           9 :     for( ulong j=0UL; j<batch_cnt; j++ ) {
     324             :       //TODO: use fd_ristretto255_point_frombytes_2x
     325           6 :       if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( &A[j], points + j*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ ) ) ) {
     326           0 :         return NULL;
     327           0 :       }
     328           6 :     }
     329             : 
     330           3 :     fd_ristretto255_multi_scalar_mul( tmp, scalars, A, batch_cnt );
     331           3 :     fd_ristretto255_point_add( r, r, tmp );
     332           3 :     points  += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ *batch_cnt;
     333           3 :     scalars += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ*batch_cnt;
     334           3 :   }
     335             : 
     336           3 :   return r;
     337           3 : }
     338             : 
     339             : #undef BATCH_MAX
     340             : 
     341             : int
     342             : fd_vm_syscall_sol_curve_multiscalar_mul( void *  _vm,
     343             :                                          ulong   curve_id,
     344             :                                          ulong   scalars_addr,
     345             :                                          ulong   points_addr,
     346             :                                          ulong   points_len,
     347             :                                          ulong   result_point_addr,
     348          15 :                                          ulong * _ret ) {
     349             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1129 */
     350          15 :   fd_vm_t * vm = (fd_vm_t *)_vm;
     351          15 :   ulong     ret = 1UL; /* by default return Ok(1) == error */
     352             : 
     353             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1143-L1151 */
     354          15 :   if( FD_UNLIKELY( points_len > 512 ) ) {
     355           3 :     FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );
     356           3 :     return FD_VM_SYSCALL_ERR_INVALID_LENGTH; /* SyscallError::InvalidLength */
     357           3 :   }
     358             : 
     359             :   /* Note: we don't strictly follow the Rust implementation, but instead combine
     360             :      common code across switch cases. Similar to fd_vm_syscall_sol_alt_bn128_group_op. */
     361             : 
     362          12 :   ulong base_cost = 0UL;
     363          12 :   ulong incremental_cost = 0UL;
     364          12 :   switch( curve_id ) {
     365           6 :   case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS:
     366           6 :     base_cost = FD_VM_CURVE25519_EDWARDS_MSM_BASE_COST;
     367           6 :     incremental_cost = FD_VM_CURVE25519_EDWARDS_MSM_INCREMENTAL_COST;
     368           6 :     break;
     369             : 
     370           3 :   case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO:
     371           3 :     base_cost = FD_VM_CURVE25519_RISTRETTO_MSM_BASE_COST;
     372           3 :     incremental_cost = FD_VM_CURVE25519_RISTRETTO_MSM_INCREMENTAL_COST;
     373           3 :     break;
     374             : 
     375           3 :   default:
     376             :     /* https://github.com/anza-xyz/agave/blob/5b3390b99a6e7665439c623062c1a1dda2803524/programs/bpf_loader/src/syscalls/mod.rs#L1262-L1271 */
     377           3 :     if( FD_FEATURE_ACTIVE( vm->instr_ctx->txn_ctx->slot, vm->instr_ctx->txn_ctx->features, abort_on_invalid_curve ) ) {
     378           3 :       FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE );
     379           3 :       return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
     380           3 :     }
     381           0 :     goto soft_error;
     382          12 :   }
     383             : 
     384             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1155-L1164 */
     385           9 :   ulong cost = fd_ulong_sat_add(
     386           9 :     base_cost,
     387           9 :     fd_ulong_sat_mul(
     388           9 :       incremental_cost,
     389           9 :       fd_ulong_sat_sub( points_len, 1 )
     390           9 :     )
     391           9 :   );
     392           9 :   FD_VM_CU_UPDATE( vm, cost );
     393             : 
     394             :   /* Edge case points_len==0.
     395             :      Agave computes the MSM, that returns the point at infinity, and stores the result.
     396             :      This means that we have to mem map result, and then set the point at infinity,
     397             :      that is 0x0100..00 for Edwards and 0x00..00 for Ristretto. */
     398           9 :   if ( FD_UNLIKELY( points_len==0 ) ) {
     399           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 );
     400           0 :     memset( result, 0, 32 );
     401           0 :     result[0] = curve_id==FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS ? 1 : 0;
     402           0 :     *_ret = 0;
     403           0 :     return FD_VM_SUCCESS;
     404           3 :   }
     405             : 
     406             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1166-L1178 */
     407          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 );
     408          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 );
     409             : 
     410           0 :   switch( curve_id ) {
     411             : 
     412           3 :   case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS: {
     413             :     /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1180-L1189 */
     414           3 :     fd_ed25519_point_t _r[1];
     415           3 :     fd_ed25519_point_t * r = multi_scalar_mul_edwards( _r, scalars, points, points_len );
     416             : 
     417           3 :     if( FD_LIKELY( r ) ) {
     418           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 );
     419           0 :       fd_ed25519_point_tobytes( result, r );
     420           3 :       ret = 0UL;
     421           3 :     }
     422           3 :     break;
     423           3 :   }
     424             : 
     425           3 :   case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO: {
     426           3 :     fd_ristretto255_point_t _r[1];
     427           3 :     fd_ristretto255_point_t * r = multi_scalar_mul_ristretto( _r, scalars, points, points_len );
     428             : 
     429           3 :     if( FD_LIKELY( r ) ) {
     430           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 );
     431           0 :       fd_ristretto255_point_tobytes( result, r );
     432           3 :       ret = 0UL;
     433           3 :     }
     434           3 :     break;
     435           3 :   }
     436             : 
     437           3 :   default:
     438             :     /* COV: this can never happen because of the previous switch */
     439           0 :     return FD_VM_SYSCALL_ERR_INVALID_ATTRIBUTE; /* SyscallError::InvalidAttribute */
     440          18 :   }
     441             : 
     442           6 : soft_error:
     443           6 :   *_ret = ret;
     444           6 :   return FD_VM_SUCCESS;
     445          18 : }

Generated by: LCOV version 1.14