surfpool_core/
runloop.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use chrono::Utc;
use jsonrpc_core::MetaIoHandler;
use jsonrpc_http_server::{DomainsValidation, ServerBuilder};
use litesvm::LiteSVM;
use solana_rpc_client::rpc_client::RpcClient;
use solana_sdk::{clock::Clock, epoch_info::EpochInfo};
use std::{
    sync::{Arc, RwLock},
    thread::sleep,
    time::Duration,
};
use tokio::sync::broadcast;

use crate::rpc::{self, full::Full, minimal::Minimal, Config, SurfpoolMiddleware};

pub struct GlobalState {
    pub svm: LiteSVM,
    pub transactions_processed: u64,
    pub epoch_info: EpochInfo,
}

pub async fn start(svm: LiteSVM) {
    // Todo: should check config first
    let rpc_client = RpcClient::new("https://api.mainnet-beta.solana.com");
    let epoch_info = rpc_client.get_epoch_info().unwrap();
    // Question: can the value `slots_in_epoch` fluctuate over time?
    let slots_in_epoch = epoch_info.slots_in_epoch;

    let context = GlobalState {
        svm,
        transactions_processed: 0,
        epoch_info,
    };

    let context = Arc::new(RwLock::new(context));
    let (mempool_tx, mut mempool_rx) = broadcast::channel(1024);
    let middleware = SurfpoolMiddleware {
        context: context.clone(),
        mempool_tx,
        config: Config {},
    };

    let _handle = hiro_system_kit::thread_named("rpc handler").spawn(move || {
        let mut io = MetaIoHandler::with_middleware(middleware);
        io.extend_with(rpc::minimal::SurfpoolMinimalRpc.to_delegate());
        io.extend_with(rpc::full::SurfpoolFullRpc.to_delegate());
        let server = ServerBuilder::new(io)
            .cors(DomainsValidation::Disabled)
            .start_http(&"127.0.0.1:8899".parse().unwrap())
            .expect("Unable to start RPC server");
        server.wait();
    });

    loop {
        sleep(Duration::from_millis(400));
        let unix_timestamp: i64 = Utc::now().timestamp();

        let Ok(mut ctx) = context.try_write() else {
            println!("unable to lock svm");
            continue;
        };

        while let Ok(tx) = mempool_rx.try_recv() {
            tx.verify_with_results();
            let tx = tx.into_legacy_transaction().unwrap();
            let message = &tx.message;
            for instruction in &message.instructions {
                // The Transaction may not be sanitized at this point
                if instruction.program_id_index as usize >= message.account_keys.len() {
                    unreachable!();
                }
                let program_id = &message.account_keys[instruction.program_id_index as usize];

                // let mut pt = solana_program_test::ProgramTest::default();
                // add_program(&read_counter_program(), program_id, &mut pt);
                // let mut ctx = pt.start_with_context().await;

                if ctx.svm.get_account(&program_id).is_none() {
                    println!("Retrieving account from Mainnet: {:?}", program_id);
                    // solana_rpc_client::rpc_client::RpcClient::new(url)
                    let mainnet_account = rpc_client.get_account(&program_id).unwrap();
                    let _ = ctx.svm.set_account(*program_id, mainnet_account);
                    println!("Injecting {:?}", program_id);
                }
            }
            let res = ctx.svm.send_transaction(tx);
            println!("{:?}", res);
        }
        ctx.epoch_info.slot_index += 1;
        ctx.epoch_info.absolute_slot += 1;
        if ctx.epoch_info.slot_index > slots_in_epoch {
            ctx.epoch_info.slot_index = 0;
            ctx.epoch_info.epoch += 1;
        }
        ctx.svm.expire_blockhash();
        let clock: Clock = Clock {
            slot: ctx.epoch_info.slot_index,
            epoch: ctx.epoch_info.epoch,
            unix_timestamp,
            epoch_start_timestamp: 0, // todo
            leader_schedule_epoch: 0, // todo
        };
        ctx.svm.set_sysvar(&clock);
        println!("{:?} / {:?}", clock, ctx.svm.latest_blockhash());
    }
}