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: 268 286 93.7 %
Date: 2024-11-13 11:58:15 Functions: 5 5 100.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          30 :                                         /**/            ulong * _ret ) {
      14             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L871 */
      15          30 :   fd_vm_t * vm = (fd_vm_t *)_vm;
      16          30 :   ulong     ret = 1UL; /* by default return Ok(1) == error */
      17             : 
      18          30 :   uchar const * point = NULL;
      19          30 :   switch( curve_id ) {
      20             : 
      21          12 :   case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS:
      22             : 
      23          12 :     FD_VM_CU_UPDATE( vm, FD_VM_CURVE25519_EDWARDS_VALIDATE_POINT_COST );
      24           9 :     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           6 :     break;
      27             : 
      28          12 :   case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO:
      29             : 
      30          12 :     FD_VM_CU_UPDATE( vm, FD_VM_CURVE25519_RISTRETTO_VALIDATE_POINT_COST );
      31           9 :     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           6 :     break;
      34             : 
      35           6 :   default:
      36             :     /* https://github.com/anza-xyz/agave/blob/5b3390b99a6e7665439c623062c1a1dda2803524/programs/bpf_loader/src/syscalls/mod.rs#L919-L928 */
      37           6 :     if( FD_FEATURE_ACTIVE( (vm->instr_ctx->slot_ctx), abort_on_invalid_curve ) ) {
      38           3 :       FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_ERR_SYSCALL_INVALID_ATTRIBUTE );
      39           3 :       return FD_VM_ERR_INVAL; /* SyscallError::InvalidAttribute */
      40           3 :     }
      41          30 :   }
      42             : 
      43          15 :   *_ret = ret;
      44          15 :   return FD_VM_SUCCESS;
      45          30 : }
      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         252 :                                   ulong * _ret ) {
      55             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L928 */
      56         252 :   fd_vm_t * vm = (fd_vm_t *)_vm;
      57         252 :   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         360 : #define MATCH_ID_OP(crv_id,grp_op) ((crv_id << 4) | grp_op)
      70         252 : #define EDWARDS   FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS
      71         252 : #define RISTRETTO FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO
      72             : 
      73         252 :   ulong cost = 0UL;
      74         252 :   switch( curve_id ) {
      75             : 
      76         117 :   case EDWARDS:
      77         117 :     switch( group_op ) {
      78             : 
      79          21 :     case FD_VM_SYSCALL_SOL_CURVE_ADD:
      80          21 :       cost = FD_VM_CURVE25519_EDWARDS_ADD_COST;
      81          21 :       break;
      82             : 
      83          21 :     case FD_VM_SYSCALL_SOL_CURVE_SUB:
      84          21 :       cost = FD_VM_CURVE25519_EDWARDS_SUBTRACT_COST;
      85          21 :       break;
      86             : 
      87          69 :     case FD_VM_SYSCALL_SOL_CURVE_MUL:
      88          69 :       cost = FD_VM_CURVE25519_EDWARDS_MULTIPLY_COST;
      89          69 :       break;
      90             : 
      91           6 :     default:
      92           6 :       goto invalid_error;
      93         117 :     }
      94         111 :     break;
      95             : 
      96         129 :   case RISTRETTO:
      97         129 :     switch( group_op ) {
      98             : 
      99          27 :     case FD_VM_SYSCALL_SOL_CURVE_ADD:
     100          27 :       cost = FD_VM_CURVE25519_RISTRETTO_ADD_COST;
     101          27 :       break;
     102             : 
     103          24 :     case FD_VM_SYSCALL_SOL_CURVE_SUB:
     104          24 :       cost = FD_VM_CURVE25519_RISTRETTO_SUBTRACT_COST;
     105          24 :       break;
     106             : 
     107          72 :     case FD_VM_SYSCALL_SOL_CURVE_MUL:
     108          72 :       cost = FD_VM_CURVE25519_RISTRETTO_MULTIPLY_COST;
     109          72 :       break;
     110             : 
     111           6 :     default:
     112           6 :       goto invalid_error;
     113         129 :     }
     114         123 :     break;
     115             : 
     116         123 :   default:
     117           6 :     goto invalid_error;
     118         252 :   }
     119             : 
     120             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L944-L947 */
     121         450 :   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         612 :   uchar const * inputL = FD_VM_MEM_HADDR_LD( vm, left_input_addr,   FD_VM_ALIGN_RUST_POD_U8_ARRAY, 32UL );
     128         558 :   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         180 :   switch( MATCH_ID_OP( curve_id, group_op ) ) {
     131             : 
     132          12 :   case MATCH_ID_OP( EDWARDS, FD_VM_SYSCALL_SOL_CURVE_ADD ): {
     133          12 :     fd_ed25519_point_t p0[1], p1[1], r[1];
     134          12 :     if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p0, inputL ) ) ) {
     135           3 :       goto soft_error;
     136           3 :     }
     137           9 :     if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p1, inputR ) ) ) {
     138           3 :       goto soft_error;
     139           3 :     }
     140             : 
     141           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 );
     142           0 :     fd_ed25519_point_add( r, p0, p1 );
     143           3 :     fd_ed25519_point_tobytes( result, r );
     144           3 :     ret = 0UL;
     145           3 :     break;
     146           9 :   }
     147             : 
     148          12 :   case MATCH_ID_OP( EDWARDS, FD_VM_SYSCALL_SOL_CURVE_SUB ): {
     149          12 :     fd_ed25519_point_t p0[1], p1[1], r[1];
     150          12 :     if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p0, inputL ) ) ) {
     151           3 :       goto soft_error;
     152           3 :     }
     153           9 :     if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p1, inputR ) ) ) {
     154           3 :       goto soft_error;
     155           3 :     }
     156             : 
     157           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 );
     158           0 :     fd_ed25519_point_sub( r, p0, p1 );
     159           3 :     fd_ed25519_point_tobytes( result, r );
     160           3 :     ret = 0UL;
     161           3 :     break;
     162           9 :   }
     163             : 
     164          60 :   case MATCH_ID_OP( EDWARDS, FD_VM_SYSCALL_SOL_CURVE_MUL ): {
     165          60 :     fd_ed25519_point_t p[1], r[1];
     166          60 :     if( FD_UNLIKELY( !fd_curve25519_scalar_validate( inputL ) ) ) {
     167          12 :       goto soft_error;
     168          12 :     }
     169          48 :     if( FD_UNLIKELY( !fd_ed25519_point_frombytes( p, inputR ) ) ) {
     170           6 :       goto soft_error;
     171           6 :     }
     172             : 
     173          42 :     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          39 :     fd_ed25519_point_tobytes( result, r );
     176          39 :     ret = 0UL;
     177          39 :     break;
     178          81 :   }
     179             : 
     180          18 :   case MATCH_ID_OP( RISTRETTO, FD_VM_SYSCALL_SOL_CURVE_ADD ): {
     181          18 :     fd_ristretto255_point_t p0[1], p1[1], r[1];
     182          18 :     if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p0, inputL ) ) ) {
     183           3 :       goto soft_error;
     184           3 :     }
     185          15 :     if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p1, inputR ) ) ) {
     186           3 :       goto soft_error;
     187           3 :     }
     188             : 
     189          12 :     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           9 :     fd_ristretto255_point_add( r, p0, p1 );
     191           9 :     fd_ristretto255_point_tobytes( result, r );
     192           9 :     ret = 0UL;
     193           9 :     break;
     194          21 :   }
     195             : 
     196          15 :   case MATCH_ID_OP( RISTRETTO, FD_VM_SYSCALL_SOL_CURVE_SUB ): {
     197          15 :     fd_ristretto255_point_t p0[1], p1[1], r[1];
     198          15 :     if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p0, inputL ) ) ) {
     199           3 :       goto soft_error;
     200           3 :     }
     201          12 :     if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p1, inputR ) ) ) {
     202           3 :       goto soft_error;
     203           3 :     }
     204             : 
     205           9 :     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           6 :     fd_ristretto255_point_sub( r, p0, p1 );
     207           6 :     fd_ristretto255_point_tobytes( result, r );
     208           6 :     ret = 0UL;
     209           6 :     break;
     210          15 :   }
     211             : 
     212          63 :   case MATCH_ID_OP( RISTRETTO, FD_VM_SYSCALL_SOL_CURVE_MUL ): {
     213          63 :     fd_ristretto255_point_t p[1], r[1];
     214          63 :     if( FD_UNLIKELY( !fd_curve25519_scalar_validate( inputL ) ) ) {
     215          15 :       goto soft_error;
     216          15 :     }
     217          48 :     if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( p, inputR ) ) ) {
     218          30 :       goto soft_error;
     219          30 :     }
     220             : 
     221          18 :     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          15 :     fd_ristretto255_scalar_mul( r, inputL, p );
     223          15 :     fd_ristretto255_point_tobytes( result, r );
     224          15 :     ret = 0UL;
     225          15 :     break;
     226          33 :   }
     227             : 
     228           0 :   default:
     229             :     /* COV: this can never happen because of the previous switch */
     230           0 :     return FD_VM_ERR_INVAL; /* SyscallError::InvalidAttribute */
     231         558 :   }
     232             : 
     233         162 : soft_error:
     234         162 :   *_ret = ret;
     235         162 :   return FD_VM_SUCCESS;
     236           0 : #undef MATCH_ID_OP
     237           0 : #undef EDWARDS
     238           0 : #undef RISTRETTO
     239             : 
     240          18 : invalid_error:
     241             :   /* https://github.com/anza-xyz/agave/blob/5b3390b99a6e7665439c623062c1a1dda2803524/programs/bpf_loader/src/syscalls/mod.rs#L1135-L1156 */
     242          18 :   if( FD_FEATURE_ACTIVE( (vm->instr_ctx->slot_ctx), abort_on_invalid_curve ) ) {
     243           9 :     FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_ERR_SYSCALL_INVALID_ATTRIBUTE );
     244           9 :     return FD_VM_ERR_INVAL; /* SyscallError::InvalidAttribute */
     245           9 :   }
     246           9 :   *_ret = 1UL;
     247           9 :   return FD_VM_SUCCESS;
     248          18 : }
     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          12 :                           ulong                cnt ) {
     267             :   /* Validate all scalars first (fast) */
     268          33 :   for( ulong i=0UL; i<cnt; i++ ) {
     269          24 :     if( FD_UNLIKELY( !fd_curve25519_scalar_validate ( scalars + i*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ ) ) ) {
     270           3 :       return NULL;
     271           3 :     }
     272          24 :   }
     273             : 
     274             :   /* Static allocation of a batch of decompressed points */
     275           9 :   fd_ed25519_point_t tmp[1];
     276           9 :   fd_ed25519_point_t A[ FD_BALLET_CURVE25519_MSM_BATCH_SZ ];
     277             : 
     278           9 :   fd_ed25519_point_set_zero( r );
     279          15 :   for( ulong i=0UL; i<cnt; i+=FD_BALLET_CURVE25519_MSM_BATCH_SZ ) {
     280           9 :     ulong batch_cnt = fd_ulong_min( cnt-i, FD_BALLET_CURVE25519_MSM_BATCH_SZ );
     281             : 
     282             :     /* Decompress (and validate) points */
     283          21 :     for( ulong j=0UL; j<batch_cnt; j++ ) {
     284             :       //TODO: use fd_ed25519_point_frombytes_2x
     285          15 :       if( FD_UNLIKELY( !fd_ed25519_point_frombytes( &A[j], points + j*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ ) ) ) {
     286           3 :         return NULL;
     287           3 :       }
     288          15 :     }
     289             : 
     290           6 :     fd_ed25519_multi_scalar_mul( tmp, scalars, A, batch_cnt );
     291           6 :     fd_ed25519_point_add( r, r, tmp );
     292           6 :     points  += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ *batch_cnt;
     293           6 :     scalars += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ*batch_cnt;
     294           6 :   }
     295             : 
     296           6 :   return r;
     297           9 : }
     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          12 :                             ulong                     cnt ) {
     307             :   /* Validate all scalars first (fast) */
     308          33 :   for( ulong i=0UL; i<cnt; i++ ) {
     309          24 :     if( FD_UNLIKELY( !fd_curve25519_scalar_validate ( scalars + i*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ ) ) ) {
     310           3 :       return NULL;
     311           3 :     }
     312          24 :   }
     313             : 
     314             :   /* Static allocation of a batch of decompressed points */
     315           9 :   fd_ristretto255_point_t tmp[1];
     316           9 :   fd_ristretto255_point_t A[ FD_BALLET_CURVE25519_MSM_BATCH_SZ ];
     317             : 
     318           9 :   fd_ristretto255_point_set_zero( r );
     319          15 :   for( ulong i=0UL; i<cnt; i+=FD_BALLET_CURVE25519_MSM_BATCH_SZ ) {
     320           9 :     ulong batch_cnt = fd_ulong_min( cnt-i, FD_BALLET_CURVE25519_MSM_BATCH_SZ );
     321             : 
     322             :     /* Decompress (and validate) points */
     323          21 :     for( ulong j=0UL; j<batch_cnt; j++ ) {
     324             :       //TODO: use fd_ristretto255_point_frombytes_2x
     325          15 :       if( FD_UNLIKELY( !fd_ristretto255_point_frombytes( &A[j], points + j*FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ ) ) ) {
     326           3 :         return NULL;
     327           3 :       }
     328          15 :     }
     329             : 
     330           6 :     fd_ristretto255_multi_scalar_mul( tmp, scalars, A, batch_cnt );
     331           6 :     fd_ristretto255_point_add( r, r, tmp );
     332           6 :     points  += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_POINT_SZ *batch_cnt;
     333           6 :     scalars += FD_VM_SYSCALL_SOL_CURVE_CURVE25519_SCALAR_SZ*batch_cnt;
     334           6 :   }
     335             : 
     336           6 :   return r;
     337           9 : }
     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          63 :                                          ulong * _ret ) {
     349             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1129 */
     350          63 :   fd_vm_t * vm = (fd_vm_t *)_vm;
     351          63 :   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          63 :   if( FD_UNLIKELY( points_len > 512 ) ) {
     355           9 :     FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_ERR_SYSCALL_INVALID_LENGTH );
     356           9 :     return FD_VM_ERR_INVAL; /* SyscallError::InvalidLength */
     357           9 :   }
     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          54 :   ulong base_cost = 0UL;
     363          54 :   ulong incremental_cost = 0UL;
     364          54 :   switch( curve_id ) {
     365          24 :   case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_EDWARDS:
     366          24 :     base_cost = FD_VM_CURVE25519_EDWARDS_MSM_BASE_COST;
     367          24 :     incremental_cost = FD_VM_CURVE25519_EDWARDS_MSM_INCREMENTAL_COST;
     368          24 :     break;
     369             : 
     370          21 :   case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO:
     371          21 :     base_cost = FD_VM_CURVE25519_RISTRETTO_MSM_BASE_COST;
     372          21 :     incremental_cost = FD_VM_CURVE25519_RISTRETTO_MSM_INCREMENTAL_COST;
     373          21 :     break;
     374             : 
     375           9 :   default:
     376             :     /* https://github.com/anza-xyz/agave/blob/5b3390b99a6e7665439c623062c1a1dda2803524/programs/bpf_loader/src/syscalls/mod.rs#L1262-L1271 */
     377           9 :     if( FD_FEATURE_ACTIVE( (vm->instr_ctx->slot_ctx), abort_on_invalid_curve ) ) {
     378           6 :       FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_ERR_SYSCALL_INVALID_ATTRIBUTE );
     379           6 :       return FD_VM_ERR_INVAL; /* SyscallError::InvalidAttribute */
     380           6 :     }
     381           3 :     goto soft_error;
     382          54 :   }
     383             : 
     384             :   /* https://github.com/anza-xyz/agave/blob/v1.18.8/programs/bpf_loader/src/syscalls/mod.rs#L1155-L1164 */
     385          45 :   ulong cost = fd_ulong_sat_add(
     386          45 :     base_cost,
     387          45 :     fd_ulong_sat_mul(
     388          45 :       incremental_cost,
     389          45 :       fd_ulong_sat_sub( points_len, 1 )
     390          45 :     )
     391          45 :   );
     392          45 :   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          39 :   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          96 :   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          78 :   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          12 :   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          12 :     fd_ed25519_point_t _r[1];
     415          12 :     fd_ed25519_point_t * r = multi_scalar_mul_edwards( _r, scalars, points, points_len );
     416             : 
     417          12 :     if( FD_LIKELY( r ) ) {
     418          18 :       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          18 :       ret = 0UL;
     421          18 :     }
     422          12 :     break;
     423          12 :   }
     424             : 
     425          12 :   case FD_VM_SYSCALL_SOL_CURVE_CURVE25519_RISTRETTO: {
     426          12 :     fd_ristretto255_point_t _r[1];
     427          12 :     fd_ristretto255_point_t * r = multi_scalar_mul_ristretto( _r, scalars, points, points_len );
     428             : 
     429          12 :     if( FD_LIKELY( r ) ) {
     430          18 :       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          18 :       ret = 0UL;
     433          18 :     }
     434          12 :     break;
     435          12 :   }
     436             : 
     437          12 :   default:
     438             :     /* COV: this can never happen because of the previous switch */
     439           0 :     return FD_VM_ERR_INVAL; /* SyscallError::InvalidAttribute */
     440          78 :   }
     441             : 
     442          27 : soft_error:
     443          27 :   *_ret = ret;
     444          27 :   return FD_VM_SUCCESS;
     445          78 : }

Generated by: LCOV version 1.14