1- use anyhow:: { anyhow, Error , Result } ;
1+ use anyhow:: { anyhow, bail , Context , Error , Result } ;
22use cargo_toml:: Manifest ;
33use chrono:: { TimeZone , Utc } ;
44use reqwest:: header:: USER_AGENT ;
@@ -201,7 +201,9 @@ pub fn use_version(opt_version: Option<Version>) -> Result<()> {
201201 . next ( )
202202 . expect ( "Expected input" ) ?;
203203 match input. as_str ( ) {
204- "y" | "yes" => return install_version ( InstallTarget :: Version ( version) , false , false ) ,
204+ "y" | "yes" => {
205+ return install_version ( InstallTarget :: Version ( version) , false , false , false )
206+ }
205207 _ => return Err ( anyhow ! ( "Installation rejected." ) ) ,
206208 } ;
207209 }
@@ -222,7 +224,7 @@ pub enum InstallTarget {
222224/// Update to the latest version
223225pub fn update ( ) -> Result < ( ) > {
224226 let latest_version = get_latest_version ( ) ?;
225- install_version ( InstallTarget :: Version ( latest_version) , false , false )
227+ install_version ( InstallTarget :: Version ( latest_version) , false , false , false )
226228}
227229
228230/// The commit sha provided can be shortened,
@@ -284,6 +286,7 @@ pub fn install_version(
284286 install_target : InstallTarget ,
285287 force : bool ,
286288 from_source : bool ,
289+ with_solana_verify : bool ,
287290) -> Result < ( ) > {
288291 let ( version, from_source) = match & install_target {
289292 InstallTarget :: Version ( version) => ( version. to_owned ( ) , from_source) ,
@@ -308,7 +311,7 @@ pub fn install_version(
308311 }
309312
310313 let is_commit = matches ! ( install_target, InstallTarget :: Commit ( _) ) ;
311- let is_older_than_v0_31_0 = version < Version :: parse ( "0.31.0" ) ? ;
314+ let is_older_than_v0_31_0 = version < Version :: new ( 0 , 31 , 0 ) ;
312315 if from_source || is_commit || is_older_than_v0_31_0 {
313316 // Build from source using `cargo install`
314317 let mut args: Vec < String > = vec ! [
@@ -430,37 +433,108 @@ pub fn install_version(
430433 ) ?;
431434 }
432435
436+ let is_at_least_0_32 = version >= Version :: new ( 0 , 32 , 0 ) ;
437+ if with_solana_verify {
438+ if is_at_least_0_32 {
439+ if !solana_verify_installed ( ) . is_ok_and ( |v| v) {
440+ #[ cfg( any( target_os = "linux" , target_os = "macos" ) ) ]
441+ install_solana_verify ( ) ?;
442+ #[ cfg( not( any( target_os = "linux" , target_os = "macos" ) ) ) ]
443+ install_solana_verify_from_source ( ) ?;
444+ println ! ( "solana-verify successfully installed" ) ;
445+ } else {
446+ println ! ( "solana-verify already installed" ) ;
447+ }
448+ } else {
449+ println ! ( "Not installing solana-verify for anchor < 0.32" ) ;
450+ }
451+ }
452+
453+ // If .version file is empty or not parseable, write the newly installed version to it
454+ if current_version ( ) . is_err ( ) {
455+ let mut current_version_file = fs:: File :: create ( current_version_file_path ( ) ) ?;
456+ current_version_file. write_all ( version. to_string ( ) . as_bytes ( ) ) ?;
457+ }
458+
459+ use_version ( Some ( version) )
460+ }
461+
462+ const SOLANA_VERIFY_VERSION : Version = Version :: new ( 0 , 4 , 11 ) ;
463+
464+ /// Check if `solana-verify` is both installed and >= [`SOLANA_VERIFY_VERSION`].
465+ fn solana_verify_installed ( ) -> Result < bool > {
466+ let bin_path = get_bin_dir_path ( ) . join ( "solana-verify" ) ;
467+ if !bin_path. exists ( ) {
468+ return Ok ( false ) ;
469+ }
470+ let output = Command :: new ( bin_path)
471+ . arg ( "-V" )
472+ . output ( )
473+ . context ( "executing `solana-verify` to check version" ) ?;
474+ let stdout =
475+ String :: from_utf8 ( output. stdout ) . context ( "expected `solana-verify` to output utf8" ) ?;
476+ let Some ( ( "solana-verify" , version) ) = stdout. trim ( ) . split_once ( " " ) else {
477+ bail ! ( "invalid `solana-verify` output: `{stdout}`" ) ;
478+ } ;
479+ if Version :: parse ( version) . with_context ( || "parsing solana-verify version `{version}" ) ?
480+ >= SOLANA_VERIFY_VERSION
481+ {
482+ Ok ( true )
483+ } else {
484+ Ok ( false )
485+ }
486+ }
487+
488+ /// Install `solana-verify` from binary releases. Only available on Linux and Mac
489+ #[ cfg( any( target_os = "linux" , target_os = "macos" ) ) ]
490+ fn install_solana_verify ( ) -> Result < ( ) > {
433491 println ! ( "Installing solana-verify..." ) ;
434- let solana_verify_install_output = Command :: new ( "cargo" )
492+ let os = std:: env:: consts:: OS ;
493+ let url = format ! (
494+ "https://github.com/Ellipsis-Labs/solana-verifiable-build/releases/download/v{SOLANA_VERIFY_VERSION}/solana-verify-{os}"
495+ ) ;
496+ let res = reqwest:: blocking:: get ( url) ?;
497+ if !res. status ( ) . is_success ( ) {
498+ bail ! (
499+ "Failed to download `solana-verify-{os} v{SOLANA_VERIFY_VERSION} (status code: {})" ,
500+ res. status( )
501+ ) ;
502+ } else {
503+ let bin_path = get_bin_dir_path ( ) . join ( "solana-verify" ) ;
504+ fs:: write ( & bin_path, res. bytes ( ) ?) ?;
505+ #[ cfg( unix) ]
506+ fs:: set_permissions (
507+ bin_path,
508+ <fs:: Permissions as std:: os:: unix:: fs:: PermissionsExt >:: from_mode ( 0o775 ) ,
509+ ) ?;
510+ Ok ( ( ) )
511+ }
512+ }
513+
514+ /// Install `solana-verify` by building from Git sources
515+ #[ cfg( not( any( target_os = "linux" , target_os = "macos" ) ) ) ]
516+ fn install_solana_verify_from_source ( ) -> Result < ( ) > {
517+ println ! ( "Installing solana-verify from source..." ) ;
518+ let status = Command :: new ( "cargo" )
435519 . args ( [
436520 "install" ,
437521 "solana-verify" ,
438522 "--git" ,
439523 "https://github.com/Ellipsis-Labs/solana-verifiable-build" ,
440524 "--rev" ,
441- "568cb334709e88b9b45fc24f1f440eecacf5db54" ,
525+ & format ! ( "v{SOLANA_VERIFY_VERSION}" ) ,
442526 "--root" ,
443527 AVM_HOME . to_str ( ) . unwrap ( ) ,
444528 "--force" ,
445529 "--locked" ,
446530 ] )
447- . stdout ( Stdio :: inherit ( ) )
448- . stderr ( Stdio :: inherit ( ) )
449- . output ( )
450- . map_err ( |e| anyhow ! ( "`cargo install` for `solana-verify` failed: {e}" ) ) ?;
451-
452- if !solana_verify_install_output. status . success ( ) {
453- return Err ( anyhow ! ( "Failed to install `solana-verify`" ) ) ;
454- }
455- println ! ( "solana-verify successfully installed." ) ;
456-
457- // If .version file is empty or not parseable, write the newly installed version to it
458- if current_version ( ) . is_err ( ) {
459- let mut current_version_file = fs:: File :: create ( current_version_file_path ( ) ) ?;
460- current_version_file. write_all ( version. to_string ( ) . as_bytes ( ) ) ?;
531+ . status ( )
532+ . context ( "executing `cargo install solana-verify`" ) ?;
533+ if status. success ( ) {
534+ Ok ( ( ) )
535+ } else {
536+ bail ! ( "failed to install `solana-verify`" ) ;
461537 }
462-
463- use_version ( Some ( version) )
464538}
465539
466540/// Remove an installed version of anchor-cli
0 commit comments