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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
use crate::SbError;
use serde::Deserialize;

fn default_cluster() -> String {
    "devnet".to_string()
}

const DEFAULT_PUBKEY: &str = "11111111111111111111111111111111";

/// The expected environment variables when a solana function is being executed
#[derive(Clone, Debug, Default, Deserialize)]
pub struct SolanaFunctionEnvironment {
    /// FUNCTION_KEY: the pubkey of the function being executed
    pub function_key: String,
    /// PAYER: The gas payer for this transaction
    pub payer: String,
    /// VERIFIER: the pubey of the oracle veriying this call
    pub verifier: String,
    /// REWARD_RECEIVER: The escrow to send the reward the oracle will receive
    /// for executing this function
    pub reward_receiver: String,

    // can be manually populated from client if missing
    /// FUNCTION_DATA: The preloaded data of the `FUNCTION_KEY` account
    #[serde(default)]
    pub function_data: String,
    /// VERIFIER_ENCLAVE_SIGNER: The keypair the verifying oracle is using to
    /// sign this transaction.
    #[serde(default)]
    pub verifier_enclave_signer: String,
    /// QUEUE_AUTHORITY: The authority of the oracle queue this function is
    /// executing on.
    #[serde(default)]
    pub queue_authority: String,

    // only used for routines
    /// FUNCTION_ROUTINE_KEY: If this function is being called with parameters,
    /// this variable will hold the pubkey of the request account
    #[serde(default)]
    pub function_routine_key: String,

    /// FUNCTION_ROUTINE_DATA: The preloaded data of the `FUNCTION_ROUTINE_KEY`
    /// account
    #[serde(default)]
    pub function_routine_data: String,

    // only used for requests
    /// FUNCTION_REQUEST_KEY: If this function is being called with parameters,
    /// this ariable will hold the pubkey of the request account
    #[serde(default)]
    pub function_request_key: String,
    /// FUNCTION_REQUEST_DATA: The preloaded data of the `FUNCTION_REQUEST_KEY`
    /// account
    #[serde(default)]
    pub function_request_data: String,

    // helpers
    #[serde(default = "default_cluster")]
    pub cluster: String,

    // optional, we can use this to wait for a slot if the public RPCs are behind
    pub minimum_context_slot: Option<u64>,
}
impl SolanaFunctionEnvironment {
    pub fn parse() -> Result<Self, SbError> {
        match envy::from_env::<SolanaFunctionEnvironment>() {
            Ok(env) => Ok(env),
            Err(error) => Err(SbError::CustomMessage(format!(
                "failed to decode environment variables: {}",
                error
            ))),
        }
    }

    /**
     * Returns the vec! of environment variable key-value pairs used by bollard
     */
    pub fn to_env(&self) -> Vec<String> {
        let mut env = vec![];
        env.push(format!("CLUSTER={}", self.cluster));
        env.push(format!("FUNCTION_KEY={}", self.function_key));
        env.push(format!("PAYER={}", self.payer));
        env.push(format!("VERIFIER={}", self.verifier));
        env.push(format!("REWARD_RECEIVER={}", self.reward_receiver));

        if !self.verifier_enclave_signer.is_empty()
            && &self.verifier_enclave_signer != &DEFAULT_PUBKEY.to_string()
        {
            env.push(format!(
                "VERIFIER_ENCLAVE_SIGNER={}",
                self.verifier_enclave_signer
            ));
        }

        if !self.queue_authority.is_empty() && &self.queue_authority != &DEFAULT_PUBKEY.to_string()
        {
            env.push(format!("QUEUE_AUTHORITY={}", self.queue_authority));
        }

        if !self.function_data.is_empty() && !self.function_data.chars().all(|c| c == '0') {
            env.push(format!("FUNCTION_DATA={}", self.function_data));
        }

        if !self.function_routine_key.is_empty()
            && &self.function_routine_key != &DEFAULT_PUBKEY.to_string()
        {
            env.push(format!(
                "FUNCTION_ROUTINE_KEY={}",
                self.function_routine_key
            ));
        }

        if !self.function_routine_data.is_empty()
            && !self.function_routine_data.chars().all(|c| c == '0')
        {
            env.push(format!(
                "FUNCTION_ROUTINE_DATA={}",
                self.function_routine_data
            ));
        }

        if !self.function_request_key.is_empty()
            && &self.function_request_key != &DEFAULT_PUBKEY.to_string()
        {
            env.push(format!(
                "FUNCTION_REQUEST_KEY={}",
                self.function_request_key
            ));
        }

        if !self.function_request_data.is_empty()
            && !self.function_request_data.chars().all(|c| c == '0')
        {
            env.push(format!(
                "FUNCTION_REQUEST_DATA={}",
                self.function_request_data
            ));
        }

        if let Some(minimum_context_slot) = self.minimum_context_slot {
            env.push(format!("MINIMUM_CONTEXT_SLOT={}", minimum_context_slot));
        }

        env
    }
}