1
use std::str::FromStr;
2

            
3
use anyhow::Result;
4

            
5
use rand::distributions::Alphanumeric;
6
use rand::Rng;
7

            
8
use solana_rpc_client::nonblocking::rpc_client::RpcClient;
9
use solana_rpc_client_api::config::RpcSendTransactionConfig;
10
use solana_sdk::commitment_config::CommitmentConfig;
11
use solana_sdk::hash::hashv;
12
use solana_sdk::instruction::{AccountMeta, Instruction};
13
use solana_sdk::message::{v0, VersionedMessage};
14
use solana_sdk::pubkey::Pubkey;
15
use solana_sdk::signature::Keypair;
16
use solana_sdk::signer::Signer;
17
use solana_sdk::system_program;
18
use solana_sdk::transaction::VersionedTransaction;
19

            
20
use bonsol_sdk::instructions::{CallbackConfig, ExecutionConfig, InputRef};
21
use bonsol_sdk::{deployment_address, execution_address, BonsolClient, ExitCode, InputType};
22
use std::env;
23

            
24
#[tokio::main]
25
async fn main() -> Result<()> {
26
    let rpc_url = "http://127.0.0.1:8899".to_string();
27
    let rpc_client = RpcClient::new(rpc_url.clone());
28
    let bonsol_client = BonsolClient::new(rpc_url);
29
    let signer = Keypair::new();
30
    let args: Vec<String> = env::args().collect();
31
    rpc_client
32
        .request_airdrop(&signer.pubkey(), 100_000_000_000)
33
        .await?;
34
    let timeout = args.get(1).map(|s| s.parse::<u64>().unwrap()).unwrap_or(60);
35
    example_bonsol_program_test(&bonsol_client, &rpc_client, &signer, timeout).await?;
36
    example_sdk_test(&bonsol_client, &rpc_client, &signer, timeout).await?;
37
    example_sdk_no_callback_test(&bonsol_client, &rpc_client, &signer, timeout).await?;
38
    Ok(())
39
}
40

            
41
const SIMPLE_IMAGE_ID: &str = "68f4b0c5f9ce034aa60ceb264a18d6c410a3af68fafd931bcfd9ebe7c1e42960";
42

            
43
async fn example_sdk_no_callback_test(
44
    bonsol_client: &BonsolClient,
45
    client: &RpcClient,
46
    signer: &dyn Signer,
47
    timeout: u64,
48
) -> Result<()> {
49
    println!("Running sdk test, no callback");
50
    let expiration: u64 = 10000000000;
51
    let execution_id = rand_id(16);
52
    let input_1 = "{\"attestation\":\"test\"}";
53
    let input_2 = "https://echoserver.dev/server?response=N4IgFgpghgJhBOBnEAuA2mkBjA9gOwBcJCBaAgTwAcIQAaEIgDwIHpKAbKASzxAF0+9AEY4Y5VKArVUDCMzogYUAlBlFEBEAF96G5QFdkKAEwAGU1qA";
54
    let input_hash = hashv(&[input_1.as_bytes(), input_2.as_bytes()]);
55
    println!("Execution expiry {}", expiration);
56
    let slot = bonsol_client.get_current_slot().await?;
57
    let ixs = bonsol_client
58
        .execute_v1(
59
            &signer.pubkey(),
60
            SIMPLE_IMAGE_ID,
61
            &execution_id,
62
            vec![
63
                InputRef::new(InputType::PublicData, input_1.as_bytes()),
64
                InputRef::new(InputType::Private, input_2.as_bytes()),
65
            ],
66
            10000,
67
            slot + expiration,
68
            ExecutionConfig {
69
                verify_input_hash: true,
70
                input_hash: Some(input_hash.as_ref()),
71
                forward_output: true,
72
            },
73
            None,
74
            None,
75
        )
76
        .await?;
77
    let bh = client.get_latest_blockhash().await?;
78
    let tsx = v0::Message::try_compile(
79
        &signer.pubkey(),
80
        &ixs,
81
        &[],
82
        client.get_latest_blockhash().await?,
83
    )?;
84
    let tx = VersionedTransaction::try_new(VersionedMessage::V0(tsx), &[signer])?;
85
    let signature = client
86
        .send_transaction_with_config(
87
            &tx,
88
            RpcSendTransactionConfig {
89
                skip_preflight: true,
90
                ..Default::default()
91
            },
92
        )
93
        .await?;
94
    client
95
        .confirm_transaction_with_spinner(&signature, &bh, CommitmentConfig::confirmed())
96
        .await?;
97
    bonsol_client
98
        .wait_for_claim(signer.pubkey(), &execution_id, Some(timeout))
99
        .await?;
100
    let status = bonsol_client
101
        .wait_for_proof(signer.pubkey(), &execution_id, Some(timeout))
102
        .await?;
103
    if status != ExitCode::Success {
104
        return Err(anyhow::anyhow!("Execution failed"));
105
    }
106
    println!("SDK Execution succeeded");
107
    Ok(())
108
}
109

            
110
async fn example_sdk_test(
111
    bonsol_client: &BonsolClient,
112
    client: &RpcClient,
113
    signer: &dyn Signer,
114
    timeout: u64,
115
) -> Result<()> {
116
    println!("Running sdk test");
117
    let ea1 = Pubkey::from_str("3b6DR2gbTJwrrX27VLEZ2FJcHrDvTSLKEcTLVhdxCoaf")?;
118
    let ea2 = Pubkey::from_str("g7dD1FHSemkUQrX1Eak37wzvDjscgBW2pFCENwjLdMX")?;
119
    let ea3 = Pubkey::from_str("FHab8zDcP1DooZqXHWQowikqtXJb1eNHc46FEh1KejmX")?;
120
    let example_program = Pubkey::from_str("exay1T7QqsJPNcwzMiWubR6vZnqrgM16jZRraHgqBGG")?;
121
    let expiration: u64 = 10000000000;
122
    let execution_id = rand_id(16);
123
    let input_1 = "{\"attestation\":\"test\"}";
124
    let input_2 = "https://echoserver.dev/server?response=N4IgFgpghgJhBOBnEAuA2mkBjA9gOwBcJCBaAgTwAcIQAaEIgDwIHpKAbKASzxAF0+9AEY4Y5VKArVUDCMzogYUAlBlFEBEAF96G5QFdkKAEwAGU1qA";
125
    let input_hash = hashv(&[input_1.as_bytes(), input_2.as_bytes()]);
126
    println!("Execution expiry {}", expiration);
127
    let slot = bonsol_client.get_current_slot().await?;
128
    let ixs = bonsol_client
129
        .execute_v1(
130
            &signer.pubkey(),
131
            SIMPLE_IMAGE_ID,
132
            &execution_id,
133
            vec![
134
                InputRef::new(InputType::PublicData, input_1.as_bytes()),
135
                InputRef::new(InputType::Private, input_2.as_bytes()),
136
            ],
137
            10000,
138
            slot + expiration,
139
            ExecutionConfig {
140
                verify_input_hash: true,
141
                input_hash: Some(input_hash.as_ref()),
142
                forward_output: true,
143
            },
144
            Some(CallbackConfig {
145
                program_id: example_program,
146
                instruction_prefix: vec![2],
147
                extra_accounts: vec![
148
                    AccountMeta::new(ea1, false),
149
                    AccountMeta::new_readonly(ea2, false),
150
                    AccountMeta::new_readonly(ea3, false),
151
                ],
152
            }),
153
            None,
154
        )
155
        .await?;
156
    let bh = client.get_latest_blockhash().await?;
157
    let tsx = v0::Message::try_compile(
158
        &signer.pubkey(),
159
        &ixs,
160
        &[],
161
        client.get_latest_blockhash().await?,
162
    )?;
163
    let tx = VersionedTransaction::try_new(VersionedMessage::V0(tsx), &[signer])?;
164
    let signature = client
165
        .send_transaction_with_config(
166
            &tx,
167
            RpcSendTransactionConfig {
168
                skip_preflight: true,
169
                ..Default::default()
170
            },
171
        )
172
        .await?;
173
    client
174
        .confirm_transaction_with_spinner(&signature, &bh, CommitmentConfig::confirmed())
175
        .await?;
176
    bonsol_client
177
        .wait_for_claim(signer.pubkey(), &execution_id, Some(timeout))
178
        .await?;
179
    let status = bonsol_client
180
        .wait_for_proof(signer.pubkey(), &execution_id, Some(timeout))
181
        .await?;
182
    if status != ExitCode::Success {
183
        return Err(anyhow::anyhow!("Execution failed"));
184
    }
185
    println!("SDK Execution succeeded");
186
    Ok(())
187
}
188

            
189
async fn example_bonsol_program_test(
190
    bonsol_client: &BonsolClient,
191
    client: &RpcClient,
192
    signer: &dyn Signer,
193
    timeout: u64,
194
) -> Result<()> {
195
    println!("Running Bonsol program test");
196
    let example_program = Pubkey::from_str("exay1T7QqsJPNcwzMiWubR6vZnqrgM16jZRraHgqBGG")?;
197
    let bonsol_program = Pubkey::from_str("BoNsHRcyLLNdtnoDf8hiCNZpyehMC4FDMxs6NTxFi3ew")?;
198
    let expiration: u64 = 10000000000;
199
    let execution_id = rand_id(16);
200
    let (requester, bump) =
201
        Pubkey::find_program_address(&[execution_id.as_bytes()], &example_program);
202
    let input_1 = "{\"attestation\":\"test\"}";
203
    let input_2 = "https://echoserver.dev/server?response=N4IgFgpghgJhBOBnEAuA2mkBjA9gOwBcJCBaAgTwAcIQAaEIgDwIHpKAbKASzxAF0+9AEY4Y5VKArVUDCMzogYUAlBlFEBEAF96G5QFdkKAEwAGU1qA";
204
    let input_hash = hashv(&[input_1.as_bytes(), input_2.as_bytes()]);
205
    let execution_account = execution_address(&requester, execution_id.as_bytes()).0;
206
    let deployment_account = deployment_address(SIMPLE_IMAGE_ID).0;
207
    let ix = Instruction {
208
        program_id: example_program,
209
        accounts: vec![
210
            AccountMeta::new_readonly(signer.pubkey(), true),
211
            AccountMeta::new(requester, false),
212
            AccountMeta::new_readonly(system_program::id(), false),
213
            AccountMeta::new(execution_account, false),
214
            AccountMeta::new_readonly(deployment_account, false),
215
            AccountMeta::new_readonly(example_program, false),
216
            AccountMeta::new_readonly(bonsol_program, false),
217
        ],
218
        data: [
219
            &[0],
220
            execution_id.as_bytes(),
221
            input_hash.as_ref(),
222
            &expiration.to_le_bytes(),
223
            &[bump],
224
            input_2.as_bytes(),
225
        ]
226
        .concat(),
227
    };
228
    let bh = client.get_latest_blockhash().await?;
229
    let tsx = v0::Message::try_compile(
230
        &signer.pubkey(),
231
        &[ix],
232
        &[],
233
        client.get_latest_blockhash().await?,
234
    )?;
235
    let tx = VersionedTransaction::try_new(VersionedMessage::V0(tsx), &[signer])?;
236
    let signature = client
237
        .send_transaction_with_config(
238
            &tx,
239
            RpcSendTransactionConfig {
240
                skip_preflight: true,
241
                ..Default::default()
242
            },
243
        )
244
        .await?;
245
    client
246
        .confirm_transaction_with_spinner(&signature, &bh, CommitmentConfig::confirmed())
247
        .await?;
248
    bonsol_client
249
        .wait_for_claim(requester, &execution_id, Some(timeout))
250
        .await?;
251
    let status = bonsol_client
252
        .wait_for_proof(requester, &execution_id, Some(timeout))
253
        .await?;
254
    if status != ExitCode::Success {
255
        return Err(anyhow::anyhow!("Execution failed"));
256
    }
257
    println!("Bonsol Program Execution succeeded");
258
    Ok(())
259
}
260

            
261
pub fn rand_id(chars: usize) -> String {
262
    let mut rng = rand::thread_rng();
263
    (&mut rng)
264
        .sample_iter(Alphanumeric)
265
        .take(chars)
266
        .map(char::from)
267
        .collect()
268
}