golana/
checker.rs

1use crate::errors::*;
2use anchor_lang::prelude::*;
3use borsh::{BorshDeserialize, BorshSerialize};
4use go_vm::{types::PackageObj, *};
5
6#[derive(BorshDeserialize, BorshSerialize, Debug, Clone, PartialEq, Eq)]
7pub enum AccessMode {
8    None,
9    Initialize(usize),
10    ReadOnly(usize),
11    Mutable(usize),
12}
13
14impl AccessMode {
15    pub fn get_data_index(&self) -> Option<usize> {
16        match self {
17            Self::Initialize(i) | Self::ReadOnly(i) | Self::Mutable(i) => Some(*i),
18            _ => None,
19        }
20    }
21}
22
23#[derive(BorshDeserialize, BorshSerialize, Debug, Clone)]
24pub struct AccMeta {
25    pub name: String,
26    pub is_signer: bool,
27    pub is_mut: bool,
28    pub data_meta: Option<types::Meta>,
29}
30
31#[derive(BorshDeserialize, BorshSerialize, Debug, Clone)]
32pub struct IxMeta {
33    pub name: String,
34    pub gos_meta: types::Meta,
35    pub process_method: types::FunctionKey,
36    pub process_method_index: usize,
37    pub accounts: Vec<AccMeta>,
38    pub args: Vec<(String, types::Meta)>,
39}
40
41impl IxMeta {
42    fn new(
43        name: &str,
44        gos_meta: types::Meta,
45        account: &types::Meta,
46        program: &types::Meta,
47        pkg: &types::PackageObj,
48        metas: &types::MetadataObjs,
49    ) -> Result<IxMeta> {
50        let (methods, inner_meta) = metas[gos_meta.key].as_named();
51        let process_method_index = methods
52            .mapping
53            .iter()
54            .find_map(|(name, index)| (name == "Process").then_some(*index as usize))
55            .ok_or(error!(GolError::MethodNotFound))?;
56        let method_desc = (&*methods.members[process_method_index]).borrow();
57        let process_method = method_desc
58            .pointer_recv
59            .then_some(method_desc.func.unwrap())
60            .ok_or(error!(GolError::NonPointerReceiver))?;
61
62        // Build struct fields
63        let fields = metas[inner_meta.key].as_struct().infos();
64        let mut i = 0;
65        let mut accounts: Vec<(AccMeta, &str)> = vec![];
66
67        // First, get all AccountInfo
68        while i < fields.len() {
69            let meta = &fields[i].meta;
70            let name = &fields[i].name;
71            let account_tag = &fields[i].lookup_tag("account");
72            let (is_signer, is_mut) = Self::is_signer_or_mut(account_tag);
73            let data_tag = &fields[i].lookup_tag("data");
74            let data_meta = Self::get_data_type(data_tag, pkg)?;
75            if meta.key == account.key || meta.key == program.key {
76                if meta.ptr_depth != 0 {
77                    return Err(error!(GolError::PointerAccount));
78                }
79                accounts.push((
80                    AccMeta {
81                        name: name.to_owned(),
82                        is_signer,
83                        is_mut,
84                        data_meta,
85                    },
86                    &fields[i].name,
87                ));
88                i += 1;
89            } else {
90                break;
91            }
92        }
93
94        // Then arguments
95        let mut args = vec![];
96        while i < fields.len() {
97            let meta = &fields[i].meta;
98            if meta.is_type || meta.ptr_depth != 0 {
99                return Err(error!(GolError::WrongArgType));
100            }
101            // todo: more checks
102            args.push((fields[i].name.clone(), meta.clone()));
103            i += 1;
104        }
105
106        Ok(IxMeta {
107            name: name.to_owned(),
108            gos_meta,
109            process_method,
110            process_method_index,
111            accounts: accounts.into_iter().map(|(acc, _)| acc).collect(),
112            args,
113        })
114    }
115
116    fn is_signer_or_mut(tag: &Option<String>) -> (bool, bool) {
117        if let Some(tag) = tag {
118            let tags: Vec<&str> = tag.split(',').map(|x| x.trim()).collect();
119            let is_signer = tags.contains(&"signer");
120            let is_mut = tags.contains(&"mut");
121            return (is_signer, is_mut);
122        }
123        (false, false)
124    }
125
126    fn get_data_type(tag: &Option<String>, pkg: &PackageObj) -> Result<Option<types::Meta>> {
127        match tag {
128            Some(t) => {
129                let index = pkg
130                    .member_index(&t)
131                    .ok_or(error!(GolError::DataTypeNotFound))?;
132                let meta = pkg.member(*index);
133                if meta.typ() != types::ValueType::Metadata {
134                    return Err(error!(GolError::DataTypeNotFound));
135                }
136                Ok(Some(meta.as_metadata().clone()))
137            }
138            None => Ok(None),
139        }
140    }
141}
142
143#[derive(BorshDeserialize, BorshSerialize, Debug, Clone)]
144pub struct TxMeta {
145    pub iface_meta: types::Meta,
146    pub pub_key_meta: types::Meta,
147    pub instructions: Vec<IxMeta>,
148}
149
150pub fn check(bc: &Bytecode) -> Result<TxMeta> {
151    let account_meta = get_solana_type_meta(bc, "Account").ok_or(error!(GolError::MetaNotFound))?;
152    let program_meta = get_solana_type_meta(bc, "Program").ok_or(error!(GolError::MetaNotFound))?;
153
154    let mut iface_meta = None;
155    let mut pub_key_meta = None;
156    let mut ix_details = Vec::new();
157    for pkg in bc.objects.packages.iter() {
158        if pkg.name() == "solana" {
159            // Find the interface metadata in solana package
160            for (name, index) in pkg.member_indices() {
161                if name == "Ix" && pkg.member(*index).typ() == types::ValueType::Metadata {
162                    iface_meta = Some(pkg.member(*index).as_metadata().clone());
163                } else if name == "PublicKey"
164                    && pkg.member(*index).typ() == types::ValueType::Metadata
165                {
166                    pub_key_meta = Some(pkg.member(*index).as_metadata().clone());
167                }
168            }
169        } else {
170            for (name, index) in pkg.member_indices() {
171                if name.starts_with("Ix") && pkg.member(*index).typ() == types::ValueType::Metadata
172                {
173                    let member = pkg.member(*index);
174                    let gmeta = member.as_metadata();
175                    ix_details.push((name, gmeta.clone(), pkg));
176                }
177            }
178        }
179    }
180
181    let instructions = ix_details
182        .into_iter()
183        .map(|(name, meta, pkg)| {
184            IxMeta::new(
185                name,
186                meta,
187                &account_meta,
188                &program_meta,
189                pkg,
190                &bc.objects.metas,
191            )
192        })
193        .collect::<Result<Vec<IxMeta>>>()?;
194    Ok(TxMeta {
195        iface_meta: iface_meta.unwrap(),
196        pub_key_meta: pub_key_meta.unwrap(),
197        instructions,
198    })
199}
200
201fn get_solana_type_meta(bc: &Bytecode, name: &str) -> Option<types::Meta> {
202    let key = bc
203        .objects
204        .packages
205        .iter()
206        .enumerate()
207        .find(|(_, pkg)| pkg.name() == "solana")
208        .map(|(i, _)| i.into())?;
209    let pkg = &bc.objects.packages[key];
210    let account = pkg.member(*pkg.member_index(name)?);
211    match account.typ() {
212        types::ValueType::Metadata => Some(account.as_metadata().clone()),
213        _ => None,
214    }
215}
216
217#[cfg(test)]
218mod tests {
219    use super::*;
220    use std::io::Read;
221    use std::path::Path;
222
223    fn read_bytecode(full_name: &Path) -> Bytecode {
224        let mut f = std::fs::OpenOptions::new()
225            .read(true)
226            .open(full_name)
227            .expect("no file found");
228        let mut buffer = Vec::new();
229
230        // read the whole file
231        f.read_to_end(&mut buffer).expect("read file error");
232        Bytecode::try_from_slice(&buffer).expect("deserialize error")
233    }
234
235    #[test]
236    fn it_works() {
237        let bc = read_bytecode(Path::new("../examples/escrow/target/escrow.gosb"));
238        let xx = check(&bc);
239        dbg!(&xx);
240    }
241}