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 let fields = metas[inner_meta.key].as_struct().infos();
64 let mut i = 0;
65 let mut accounts: Vec<(AccMeta, &str)> = vec![];
66
67 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 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 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 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 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}