1
use std::fs;
2
use std::io::{self, Read};
3
use std::path::Path;
4

            
5
use atty::Stream;
6
use bonsol_sdk::BonsolClient;
7
use clap::Parser;
8
use env_logger::Env;
9
use risc0_circuit_rv32im::prove::emu::exec::DEFAULT_SEGMENT_LIMIT_PO2;
10
use risc0_circuit_rv32im::prove::emu::testutil::DEFAULT_SESSION_LIMIT;
11
use risc0_zkvm::ExecutorEnv;
12
use solana_sdk::signer::Signer;
13

            
14
use crate::command::{BonsolCli, Command};
15
use crate::common::{execute_get_inputs, load_solana_config, sol_check, ZkProgramManifest};
16
use crate::error::{BonsolCliError, ZkManifestError};
17

            
18
mod build;
19
mod deploy;
20
mod estimate;
21
mod execute;
22
mod init;
23
mod prove;
24

            
25
#[cfg(all(test, feature = "integration-tests"))]
26
mod tests;
27

            
28
pub mod command;
29
pub mod common;
30
pub(crate) mod error;
31

            
32
#[tokio::main]
33
async fn main() -> anyhow::Result<()> {
34
    // Initialize env_logger to enable debug! log statements
35
    env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
36

            
37
    let BonsolCli {
38
        config,
39
        keypair,
40
        rpc_url,
41
        command,
42
    } = BonsolCli::parse();
43

            
44
    match command {
45
        Command::Build { zk_program_path } => build::build(
46
            &load_solana_config(config, rpc_url, keypair)?.1,
47
            zk_program_path,
48
        ),
49
        Command::Deploy { deploy_args } => {
50
            let (rpc_url, keypair) = load_solana_config(config, rpc_url, keypair)?;
51
            if !sol_check(rpc_url.clone(), keypair.pubkey()).await {
52
                return Err(BonsolCliError::InsufficientFunds(keypair.pubkey().to_string()).into());
53
            }
54

            
55
            deploy::deploy(rpc_url, keypair, deploy_args).await
56
        }
57
        Command::Estimate {
58
            manifest_path,
59
            input_file,
60
            max_cycles,
61
        } => {
62
            let manifest_file = fs::File::open(Path::new(&manifest_path)).map_err(|err| {
63
                BonsolCliError::ZkManifestError(ZkManifestError::FailedToOpen {
64
                    manifest_path: manifest_path.clone(),
65
                    err,
66
                })
67
            })?;
68
            let manifest: ZkProgramManifest =
69
                serde_json::from_reader(manifest_file).map_err(|err| {
70
                    BonsolCliError::ZkManifestError(ZkManifestError::FailedDeserialization {
71
                        manifest_path,
72
                        err,
73
                    })
74
                })?;
75
            let elf = fs::read(&manifest.binary_path).map_err(|err| {
76
                BonsolCliError::ZkManifestError(ZkManifestError::FailedToLoadBinary {
77
                    binary_path: manifest.binary_path.clone(),
78
                    err,
79
                })
80
            })?;
81

            
82
            let mut env = &mut ExecutorEnv::builder();
83
            env = env
84
                .segment_limit_po2(DEFAULT_SEGMENT_LIMIT_PO2 as u32)
85
                .session_limit(max_cycles.or(DEFAULT_SESSION_LIMIT));
86
            if input_file.is_some() {
87
                let inputs = execute_get_inputs(input_file, None)?;
88
                let inputs: Vec<&str> = inputs.iter().map(|i| i.data.as_str()).collect();
89
                env = env.write(&inputs.as_slice())?;
90
            }
91

            
92
            estimate::estimate(elf.as_slice(), env.build()?)
93
        }
94
        Command::Execute {
95
            execution_request_file,
96
            program_id,
97
            execution_id,
98
            expiry,
99
            input_file,
100
            wait,
101
            tip,
102
            timeout,
103
        } => {
104
            let (rpc_url, keypair) = load_solana_config(config, rpc_url, keypair)?;
105
            if !sol_check(rpc_url.clone(), keypair.pubkey()).await {
106
                return Err(BonsolCliError::InsufficientFunds(keypair.pubkey().to_string()).into());
107
            }
108
            let stdin = atty::isnt(Stream::Stdin)
109
                .then(|| {
110
                    let mut buffer = String::new();
111
                    io::stdin().read_to_string(&mut buffer).ok()?;
112
                    (!buffer.trim().is_empty()).then_some(buffer)
113
                })
114
                .flatten();
115
            let sdk = BonsolClient::new(rpc_url.clone());
116

            
117
            execute::execute(
118
                &sdk,
119
                rpc_url,
120
                &keypair,
121
                execution_request_file,
122
                program_id,
123
                execution_id,
124
                timeout,
125
                input_file,
126
                tip,
127
                expiry,
128
                stdin,
129
                wait,
130
            )
131
            .await
132
        }
133
        Command::Prove {
134
            manifest_path,
135
            program_id,
136
            input_file,
137
            execution_id,
138
            output_location,
139
        } => {
140
            let rpc_url = load_solana_config(config, rpc_url, keypair)?.0;
141
            let stdin = atty::isnt(Stream::Stdin)
142
                .then(|| {
143
                    let mut buffer = String::new();
144
                    io::stdin().read_to_string(&mut buffer).ok()?;
145
                    (!buffer.trim().is_empty()).then_some(buffer)
146
                })
147
                .flatten();
148
            let sdk = BonsolClient::new(rpc_url.clone());
149

            
150
            prove::prove(
151
                &sdk,
152
                execution_id,
153
                manifest_path,
154
                program_id,
155
                input_file,
156
                output_location,
157
                stdin,
158
            )
159
            .await
160
        }
161
        Command::Init { project_name, dir } => init::init_project(&project_name, dir),
162
    }
163
}