solana_deploy.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. // SPDX-License-Identifier: Apache-2.0
  2. use super::{
  3. cfg::ReturnCode, expression, Builtin, ControlFlowGraph, Expression, Instr, Options, Type,
  4. Vartable,
  5. };
  6. use crate::codegen::solana_accounts::account_management::{
  7. account_meta_literal, retrieve_key_from_account_info,
  8. };
  9. use crate::sema::ast::{
  10. self, ArrayLength, CallTy, ConstructorAnnotation, Function, FunctionAttributes, Namespace,
  11. StructType,
  12. };
  13. use base58::ToBase58;
  14. use num_bigint::{BigInt, Sign};
  15. use num_traits::{ToPrimitive, Zero};
  16. use solang_parser::pt::Loc;
  17. /// Special code for Solana constructors like creating the account
  18. ///
  19. /// On Solana, prepare the data account after deploy; ensure the account is
  20. /// large enough and write magic to it to show the account has been deployed.
  21. pub(super) fn solana_deploy(
  22. func: &Function,
  23. constructor_args: &[Expression],
  24. contract_no: usize,
  25. vartab: &mut Vartable,
  26. cfg: &mut ControlFlowGraph,
  27. ns: &Namespace,
  28. opt: &Options,
  29. ) {
  30. let contract = &ns.contracts[contract_no];
  31. let program_id = contract.program_id.as_ref();
  32. if let Some(program_id) = program_id {
  33. // emit code to check program_id == program_id
  34. let cond = Expression::Equal {
  35. loc: Loc::Codegen,
  36. left: Box::new(Expression::NumberLiteral {
  37. loc: Loc::Codegen,
  38. ty: Type::Address(false),
  39. value: BigInt::from_bytes_be(Sign::Plus, program_id),
  40. }),
  41. right: Box::new(Expression::Builtin {
  42. loc: Loc::Codegen,
  43. tys: vec![Type::Address(false)],
  44. kind: Builtin::ProgramId,
  45. args: Vec::new(),
  46. }),
  47. };
  48. let id_fail = cfg.new_basic_block("program_id_fail".to_string());
  49. let id_ok = cfg.new_basic_block("program_id_ok".to_string());
  50. cfg.add(
  51. vartab,
  52. Instr::BranchCond {
  53. cond,
  54. true_block: id_ok,
  55. false_block: id_fail,
  56. },
  57. );
  58. cfg.set_basic_block(id_fail);
  59. let message = format!("program_id should be {}", program_id.to_base58()).into_bytes();
  60. let expr = Expression::AllocDynamicBytes {
  61. loc: Loc::Codegen,
  62. ty: Type::String,
  63. size: Box::new(Expression::NumberLiteral {
  64. loc: Loc::Codegen,
  65. ty: Type::Uint(32),
  66. value: BigInt::from(message.len()),
  67. }),
  68. initializer: Some(message),
  69. };
  70. cfg.add(vartab, Instr::Print { expr });
  71. cfg.add(
  72. vartab,
  73. Instr::ReturnCode {
  74. code: ReturnCode::InvalidProgramId,
  75. },
  76. );
  77. cfg.set_basic_block(id_ok);
  78. }
  79. // Make sure that the data account is large enough. Read the size of the
  80. // account via `tx.accounts[0].data.length`.
  81. // tx.accounts[0]
  82. let tx_account_0 = Expression::Subscript {
  83. loc: Loc::Codegen,
  84. ty: Type::Struct(StructType::AccountInfo),
  85. array_ty: Type::Array(
  86. Type::Struct(StructType::AccountInfo).into(),
  87. vec![ArrayLength::Dynamic],
  88. ),
  89. expr: Expression::Builtin {
  90. loc: Loc::Codegen,
  91. tys: vec![Type::Array(
  92. Type::Struct(StructType::AccountInfo).into(),
  93. vec![ArrayLength::Dynamic],
  94. )],
  95. kind: Builtin::Accounts,
  96. args: vec![],
  97. }
  98. .into(),
  99. index: Expression::NumberLiteral {
  100. loc: Loc::Codegen,
  101. ty: Type::Uint(32),
  102. value: BigInt::zero(),
  103. }
  104. .into(),
  105. };
  106. let loaded_item = Expression::Load {
  107. loc: Loc::Codegen,
  108. ty: Type::Slice(Box::new(Type::Bytes(1))),
  109. expr: Expression::StructMember {
  110. loc: Loc::Codegen,
  111. ty: Type::Ref(Box::new(Type::Slice(Box::new(Type::Bytes(1))))),
  112. expr: tx_account_0.into(),
  113. member: 2,
  114. }
  115. .into(),
  116. };
  117. // .data.length
  118. let account_length = Expression::Builtin {
  119. loc: Loc::Codegen,
  120. tys: vec![Type::Uint(32)],
  121. kind: Builtin::ArrayLength,
  122. args: vec![loaded_item],
  123. };
  124. let account_data_var = vartab.temp_name("data_length", &Type::Uint(32));
  125. cfg.add(
  126. vartab,
  127. Instr::Set {
  128. loc: Loc::Codegen,
  129. res: account_data_var,
  130. expr: account_length,
  131. },
  132. );
  133. let account_length = Expression::Variable {
  134. loc: Loc::Codegen,
  135. ty: Type::Uint(32),
  136. var_no: account_data_var,
  137. };
  138. let account_no_data = Expression::Equal {
  139. loc: Loc::Codegen,
  140. left: account_length.clone().into(),
  141. right: Expression::NumberLiteral {
  142. loc: Loc::Codegen,
  143. ty: Type::Uint(32),
  144. value: 0.into(),
  145. }
  146. .into(),
  147. };
  148. let account_exists = cfg.new_basic_block("account_exists".into());
  149. let create_account = cfg.new_basic_block("create_account".into());
  150. cfg.add(
  151. vartab,
  152. Instr::BranchCond {
  153. cond: account_no_data,
  154. true_block: create_account,
  155. false_block: account_exists,
  156. },
  157. );
  158. cfg.set_basic_block(account_exists);
  159. let is_enough = Expression::MoreEqual {
  160. loc: Loc::Codegen,
  161. signed: false,
  162. left: account_length.into(),
  163. right: Expression::NumberLiteral {
  164. loc: Loc::Codegen,
  165. ty: Type::Uint(32),
  166. value: contract.fixed_layout_size.clone(),
  167. }
  168. .into(),
  169. };
  170. let account_ok = cfg.new_basic_block("account_ok".into());
  171. let not_enough = cfg.new_basic_block("not_enough".into());
  172. cfg.add(
  173. vartab,
  174. Instr::BranchCond {
  175. cond: is_enough,
  176. true_block: account_ok,
  177. false_block: not_enough,
  178. },
  179. );
  180. cfg.set_basic_block(not_enough);
  181. cfg.add(
  182. vartab,
  183. Instr::ReturnCode {
  184. code: ReturnCode::AccountDataTooSmall,
  185. },
  186. );
  187. cfg.set_basic_block(create_account);
  188. // The expressions in the @payer, @seed, @bump, and @space have been resolved in the constructors
  189. // context, so any variables will be bound to that vartable. Only the parameters are visible
  190. // when these were resolved; simply copy the decoded constructor arguments into the right variables.
  191. for (i, arg) in func.get_symbol_table().arguments.iter().enumerate() {
  192. if let Some(arg) = arg {
  193. let param = &func.params[i];
  194. vartab.add_known(*arg, param.id.as_ref().unwrap(), &param.ty);
  195. cfg.add(
  196. vartab,
  197. Instr::Set {
  198. loc: Loc::Codegen,
  199. res: *arg,
  200. expr: constructor_args[i].clone(),
  201. },
  202. );
  203. }
  204. }
  205. if let Some(ConstructorAnnotation::Payer(_, name)) = func
  206. .annotations
  207. .iter()
  208. .find(|tag| matches!(tag, ConstructorAnnotation::Payer(..)))
  209. {
  210. let metas_ty = Type::Array(
  211. Box::new(Type::Struct(StructType::AccountMeta)),
  212. vec![ArrayLength::Fixed(BigInt::from(2))],
  213. );
  214. let metas = vartab.temp_name("metas", &metas_ty);
  215. let account_info_ty = Type::Ref(Box::new(Type::Struct(StructType::AccountInfo)));
  216. let account_info_var = vartab.temp_anonymous(&account_info_ty);
  217. cfg.add(
  218. vartab,
  219. Instr::AccountAccess {
  220. loc: Loc::Codegen,
  221. name: name.clone(),
  222. var_no: account_info_var,
  223. },
  224. );
  225. let account_var = Expression::Variable {
  226. loc: Loc::Codegen,
  227. ty: account_info_ty,
  228. var_no: account_info_var,
  229. };
  230. let ptr_to_address = retrieve_key_from_account_info(account_var);
  231. cfg.add(
  232. vartab,
  233. Instr::Set {
  234. loc: Loc::Codegen,
  235. res: metas,
  236. expr: Expression::ArrayLiteral {
  237. loc: Loc::Codegen,
  238. ty: metas_ty.clone(),
  239. dimensions: vec![2],
  240. values: vec![
  241. account_meta_literal(ptr_to_address, true, true),
  242. account_meta_literal(
  243. Expression::Builtin {
  244. loc: Loc::Codegen,
  245. tys: vec![Type::Ref(Box::new(Type::Address(false)))],
  246. kind: Builtin::GetAddress,
  247. args: vec![],
  248. },
  249. true,
  250. true,
  251. ),
  252. ],
  253. },
  254. },
  255. );
  256. // Calculate minimum balance for rent-exempt
  257. let (space, lamports) = if let Some(ConstructorAnnotation::Space(space_expr)) = func
  258. .annotations
  259. .iter()
  260. .find(|tag| matches!(tag, ConstructorAnnotation::Space(..)))
  261. {
  262. let space_var = vartab.temp_name("space", &Type::Uint(64));
  263. let expr = expression(space_expr, cfg, contract_no, None, ns, vartab, opt);
  264. cfg.add(
  265. vartab,
  266. Instr::Set {
  267. loc: Loc::Codegen,
  268. res: space_var,
  269. expr,
  270. },
  271. );
  272. let space = Expression::Variable {
  273. loc: Loc::Codegen,
  274. ty: Type::Uint(64),
  275. var_no: space_var,
  276. };
  277. // https://github.com/solana-labs/solana/blob/718f433206c124da85a8aa2476c0753f351f9a28/sdk/program/src/rent.rs#L78-L82
  278. let lamports = Expression::Multiply {
  279. loc: Loc::Codegen,
  280. ty: Type::Uint(64),
  281. overflowing: false,
  282. left: Expression::Add {
  283. loc: Loc::Codegen,
  284. ty: Type::Uint(64),
  285. overflowing: false,
  286. left: space.clone().into(),
  287. right: Expression::NumberLiteral {
  288. loc: Loc::Codegen,
  289. ty: Type::Uint(64),
  290. value: 128.into(),
  291. }
  292. .into(),
  293. }
  294. .into(),
  295. right: Expression::NumberLiteral {
  296. loc: Loc::Codegen,
  297. ty: Type::Uint(64),
  298. value: BigInt::from(3480 * 2),
  299. }
  300. .into(),
  301. };
  302. (space, lamports)
  303. } else {
  304. let space_runtime_constant = contract.fixed_layout_size.to_u64().unwrap();
  305. // https://github.com/solana-labs/solana/blob/718f433206c124da85a8aa2476c0753f351f9a28/sdk/program/src/rent.rs#L78-L82
  306. let lamports_runtime_constant = (128 + space_runtime_constant) * 3480 * 2;
  307. (
  308. Expression::NumberLiteral {
  309. loc: Loc::Codegen,
  310. ty: Type::Uint(64),
  311. value: space_runtime_constant.into(),
  312. },
  313. Expression::NumberLiteral {
  314. loc: Loc::Codegen,
  315. ty: Type::Uint(64),
  316. value: lamports_runtime_constant.into(),
  317. },
  318. )
  319. };
  320. let instruction_var = vartab.temp_name("instruction", &Type::DynamicBytes);
  321. let instruction = Expression::Variable {
  322. loc: Loc::Codegen,
  323. ty: Type::DynamicBytes,
  324. var_no: instruction_var,
  325. };
  326. // The CreateAccount instruction is 52 bytes (4 + 8 + 8 + 32)
  327. let instruction_size = 52;
  328. cfg.add(
  329. vartab,
  330. Instr::Set {
  331. loc: Loc::Codegen,
  332. res: instruction_var,
  333. expr: Expression::AllocDynamicBytes {
  334. loc: Loc::Codegen,
  335. ty: Type::DynamicBytes,
  336. size: Expression::NumberLiteral {
  337. loc: Loc::Codegen,
  338. ty: Type::Uint(32),
  339. value: instruction_size.into(),
  340. }
  341. .into(),
  342. initializer: None,
  343. },
  344. },
  345. );
  346. // instruction CreateAccount
  347. cfg.add(
  348. vartab,
  349. Instr::WriteBuffer {
  350. buf: instruction.clone(),
  351. value: Expression::NumberLiteral {
  352. loc: Loc::Codegen,
  353. ty: Type::Uint(32),
  354. value: BigInt::from(0),
  355. },
  356. offset: Expression::NumberLiteral {
  357. loc: Loc::Codegen,
  358. ty: Type::Uint(32),
  359. value: BigInt::from(0),
  360. },
  361. },
  362. );
  363. // lamports
  364. cfg.add(
  365. vartab,
  366. Instr::WriteBuffer {
  367. buf: instruction.clone(),
  368. value: lamports,
  369. offset: Expression::NumberLiteral {
  370. loc: Loc::Codegen,
  371. ty: Type::Uint(32),
  372. value: BigInt::from(4),
  373. },
  374. },
  375. );
  376. // space
  377. cfg.add(
  378. vartab,
  379. Instr::WriteBuffer {
  380. buf: instruction.clone(),
  381. value: space,
  382. offset: Expression::NumberLiteral {
  383. loc: Loc::Codegen,
  384. ty: Type::Uint(32),
  385. value: BigInt::from(12),
  386. },
  387. },
  388. );
  389. // owner
  390. cfg.add(
  391. vartab,
  392. Instr::WriteBuffer {
  393. buf: instruction.clone(),
  394. value: if let Some(program_id) = program_id {
  395. Expression::NumberLiteral {
  396. loc: Loc::Codegen,
  397. ty: Type::Address(false),
  398. value: BigInt::from_bytes_be(Sign::Plus, program_id),
  399. }
  400. } else {
  401. Expression::Builtin {
  402. loc: Loc::Codegen,
  403. tys: vec![Type::Address(false)],
  404. kind: Builtin::ProgramId,
  405. args: vec![],
  406. }
  407. },
  408. offset: Expression::NumberLiteral {
  409. loc: Loc::Codegen,
  410. ty: Type::Uint(32),
  411. value: BigInt::from(20),
  412. },
  413. },
  414. );
  415. // seeds
  416. let mut seeds = Vec::new();
  417. let mut declared_bump = None;
  418. for note in &func.annotations {
  419. match note {
  420. ConstructorAnnotation::Seed(seed) => {
  421. seeds.push(expression(seed, cfg, contract_no, None, ns, vartab, opt));
  422. }
  423. ConstructorAnnotation::Bump(bump) => {
  424. let expr = ast::Expression::Cast {
  425. loc: Loc::Codegen,
  426. to: Type::Slice(Type::Bytes(1).into()),
  427. expr: ast::Expression::BytesCast {
  428. loc: Loc::Codegen,
  429. to: Type::DynamicBytes,
  430. from: Type::Bytes(1),
  431. expr: bump.clone().into(),
  432. }
  433. .into(),
  434. };
  435. declared_bump = Some(expr);
  436. }
  437. _ => (),
  438. }
  439. }
  440. if let Some(bump) = declared_bump {
  441. seeds.push(expression(&bump, cfg, contract_no, None, ns, vartab, opt));
  442. }
  443. let seeds = if !seeds.is_empty() {
  444. let ty = Type::Array(
  445. Box::new(Type::Slice(Box::new(Type::Bytes(1)))),
  446. vec![ArrayLength::Fixed(seeds.len().into())],
  447. );
  448. let address_seeds = Expression::ArrayLiteral {
  449. loc: Loc::Codegen,
  450. ty,
  451. dimensions: vec![seeds.len() as u32],
  452. values: seeds,
  453. };
  454. let ty = Type::Array(
  455. Box::new(Type::Slice(Box::new(Type::Slice(Box::new(Type::Bytes(1)))))),
  456. vec![ArrayLength::Fixed(1.into())],
  457. );
  458. Some(Expression::ArrayLiteral {
  459. loc: Loc::Codegen,
  460. ty,
  461. dimensions: vec![1],
  462. values: vec![address_seeds],
  463. })
  464. } else {
  465. None
  466. };
  467. cfg.add(
  468. vartab,
  469. Instr::ExternalCall {
  470. success: None,
  471. seeds,
  472. address: Some(Expression::NumberLiteral {
  473. loc: Loc::Codegen,
  474. ty: Type::Address(false),
  475. value: BigInt::from(0),
  476. }), // SystemProgram 11111111111111111111111111111111
  477. accounts: Some(Expression::Variable {
  478. loc: Loc::Codegen,
  479. ty: metas_ty,
  480. var_no: metas,
  481. }),
  482. payload: instruction,
  483. value: Expression::NumberLiteral {
  484. loc: Loc::Codegen,
  485. ty: Type::Uint(64),
  486. value: BigInt::from(0),
  487. },
  488. gas: Expression::NumberLiteral {
  489. loc: Loc::Codegen,
  490. ty: Type::Uint(64),
  491. value: BigInt::from(0),
  492. },
  493. callty: CallTy::Regular,
  494. contract_function_no: None,
  495. flags: None,
  496. },
  497. );
  498. cfg.add(vartab, Instr::Branch { block: account_ok });
  499. } else {
  500. cfg.add(
  501. vartab,
  502. Instr::ReturnCode {
  503. code: ReturnCode::AccountDataTooSmall,
  504. },
  505. );
  506. }
  507. cfg.set_basic_block(account_ok);
  508. // Write contract magic number to offset 0
  509. cfg.add(
  510. vartab,
  511. Instr::SetStorage {
  512. ty: Type::Uint(32),
  513. value: Expression::NumberLiteral {
  514. loc: Loc::Codegen,
  515. ty: Type::Uint(64),
  516. value: BigInt::from(contract.selector()),
  517. },
  518. storage: Expression::NumberLiteral {
  519. loc: Loc::Codegen,
  520. ty: Type::Uint(64),
  521. value: BigInt::zero(),
  522. },
  523. },
  524. );
  525. // Calculate heap offset
  526. let fixed_fields_size = contract.fixed_layout_size.to_u64().unwrap();
  527. // align on 8 byte boundary (round up to nearest multiple of 8)
  528. let heap_offset = (fixed_fields_size + 7) & !7;
  529. // Write heap offset to 12
  530. cfg.add(
  531. vartab,
  532. Instr::SetStorage {
  533. ty: Type::Uint(32),
  534. value: Expression::NumberLiteral {
  535. loc: Loc::Codegen,
  536. ty: Type::Uint(64),
  537. value: BigInt::from(heap_offset),
  538. },
  539. storage: Expression::NumberLiteral {
  540. loc: Loc::Codegen,
  541. ty: Type::Uint(64),
  542. value: BigInt::from(12),
  543. },
  544. },
  545. );
  546. }