call.rs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. // SPDX-License-Identifier: Apache-2.0
  2. use crate::{
  3. build_solidity, create_program_address, AccountMeta, AccountState, BorshToken, Instruction,
  4. Pubkey, VirtualMachine,
  5. };
  6. use base58::FromBase58;
  7. use num_bigint::BigInt;
  8. use num_traits::One;
  9. #[test]
  10. fn simple_external_call() {
  11. let mut vm = build_solidity(
  12. r#"
  13. contract bar0 {
  14. function test_bar(string v) public {
  15. print("bar0 says: " + v);
  16. }
  17. function test_other(address x) public {
  18. bar1.test_bar{program_id: x}("cross contract call");
  19. }
  20. }
  21. contract bar1 {
  22. function test_bar(string v) public {
  23. print("bar1 says: " + v);
  24. }
  25. }"#,
  26. );
  27. let bar1_account = vm.initialize_data_account();
  28. let bar1_program_id = vm.stack[0].id;
  29. vm.function("new")
  30. .accounts(vec![("dataAccount", bar1_account)])
  31. .call();
  32. vm.function("test_bar")
  33. .arguments(&[BorshToken::String(String::from("yo"))])
  34. .call();
  35. assert_eq!(vm.logs, "bar1 says: yo");
  36. vm.logs.truncate(0);
  37. vm.set_program(0);
  38. let bar0_account = vm.initialize_data_account();
  39. vm.function("new")
  40. .accounts(vec![("dataAccount", bar0_account)])
  41. .call();
  42. vm.function("test_bar")
  43. .arguments(&[BorshToken::String(String::from("uncle beau"))])
  44. .call();
  45. assert_eq!(vm.logs, "bar0 says: uncle beau");
  46. vm.logs.truncate(0);
  47. vm.function("test_other")
  48. .accounts(vec![
  49. ("bar1_programId", bar1_program_id),
  50. ("systemProgram", [0; 32]),
  51. ])
  52. .remaining_accounts(&[
  53. AccountMeta {
  54. pubkey: Pubkey(bar1_account),
  55. is_writable: false,
  56. is_signer: false,
  57. },
  58. AccountMeta {
  59. pubkey: Pubkey(bar1_program_id),
  60. is_signer: false,
  61. is_writable: false,
  62. },
  63. ])
  64. .arguments(&[BorshToken::Address(bar1_program_id)])
  65. .call();
  66. assert_eq!(vm.logs, "bar1 says: cross contract call");
  67. }
  68. #[test]
  69. fn external_call_with_returns() {
  70. let mut vm = build_solidity(
  71. r#"
  72. contract bar0 {
  73. function test_other(address x) public returns (int64) {
  74. return bar1.test_bar{program_id: x}(7) + 5;
  75. }
  76. }
  77. contract bar1 {
  78. function test_bar(int64 y) public returns (int64) {
  79. return 3 + y;
  80. }
  81. }"#,
  82. );
  83. let bar1_account = vm.initialize_data_account();
  84. let bar1_program_id = vm.stack[0].id;
  85. vm.function("new")
  86. .accounts(vec![("dataAccount", bar1_account)])
  87. .call();
  88. let res = vm
  89. .function("test_bar")
  90. .arguments(&[BorshToken::Int {
  91. width: 64,
  92. value: BigInt::from(21),
  93. }])
  94. .call()
  95. .unwrap();
  96. assert_eq!(
  97. res,
  98. BorshToken::Int {
  99. width: 64,
  100. value: BigInt::from(24u8)
  101. }
  102. );
  103. vm.set_program(0);
  104. let bar0_account = vm.initialize_data_account();
  105. vm.function("new")
  106. .accounts(vec![("dataAccount", bar0_account)])
  107. .call();
  108. let res = vm
  109. .function("test_other")
  110. .arguments(&[BorshToken::Address(bar1_program_id)])
  111. .accounts(vec![
  112. ("bar1_programId", bar1_program_id),
  113. ("systemProgram", [0; 32]),
  114. ])
  115. .remaining_accounts(&[
  116. AccountMeta {
  117. pubkey: Pubkey(bar1_account),
  118. is_writable: false,
  119. is_signer: false,
  120. },
  121. AccountMeta {
  122. pubkey: Pubkey(bar1_program_id),
  123. is_signer: false,
  124. is_writable: false,
  125. },
  126. ])
  127. .call()
  128. .unwrap();
  129. assert_eq!(
  130. res,
  131. BorshToken::Int {
  132. width: 64,
  133. value: BigInt::from(15u8)
  134. }
  135. );
  136. }
  137. #[test]
  138. fn external_raw_call_with_returns() {
  139. let mut vm = build_solidity(
  140. r#"
  141. contract bar0 {
  142. bytes8 private constant SELECTOR = bytes8(sha256(bytes('global:test_bar')));
  143. function test_other(address x) public returns (int64) {
  144. bytes select = abi.encodeWithSelector(SELECTOR, int64(7));
  145. bytes signature = abi.encodeWithSignature("global:test_bar", int64(7));
  146. require(select == signature, "must be the same");
  147. (, bytes raw) = address(x).call(signature);
  148. (int64 v) = abi.decode(raw, (int64));
  149. return v + 5;
  150. }
  151. }
  152. contract bar1 {
  153. function test_bar(int64 y) public returns (int64) {
  154. return 3 + y;
  155. }
  156. }"#,
  157. );
  158. let bar1_account = vm.initialize_data_account();
  159. let bar1_program_id = vm.stack[0].id;
  160. vm.function("new")
  161. .accounts(vec![("dataAccount", bar1_account)])
  162. .call();
  163. let res = vm
  164. .function("test_bar")
  165. .arguments(&[BorshToken::Int {
  166. width: 64,
  167. value: BigInt::from(21u8),
  168. }])
  169. .call()
  170. .unwrap();
  171. assert_eq!(
  172. res,
  173. BorshToken::Int {
  174. width: 64,
  175. value: BigInt::from(24u8),
  176. }
  177. );
  178. vm.set_program(0);
  179. let bar0_account = vm.initialize_data_account();
  180. vm.function("new")
  181. .accounts(vec![("dataAccount", bar0_account)])
  182. .call();
  183. let res = vm
  184. .function("test_other")
  185. .arguments(&[BorshToken::Address(bar1_program_id)])
  186. .accounts(vec![("systemProgram", [0; 32])])
  187. .remaining_accounts(&[
  188. AccountMeta {
  189. pubkey: Pubkey(bar1_account),
  190. is_writable: false,
  191. is_signer: false,
  192. },
  193. AccountMeta {
  194. pubkey: Pubkey(bar1_program_id),
  195. is_signer: false,
  196. is_writable: false,
  197. },
  198. ])
  199. .call()
  200. .unwrap();
  201. assert_eq!(
  202. res,
  203. BorshToken::Int {
  204. width: 64,
  205. value: BigInt::from(15u8),
  206. }
  207. );
  208. }
  209. #[test]
  210. fn call_external_func_type() {
  211. let mut vm = build_solidity(
  212. r#"
  213. contract testing {
  214. function testPtr(int a) public pure returns (int, int) {
  215. return (a/2, 3);
  216. }
  217. function doTest() public view returns (int, int) {
  218. function(int) external pure returns (int, int) sfPtr = this.testPtr;
  219. (int a, int b) = sfPtr(2);
  220. return (a, b);
  221. }
  222. }
  223. "#,
  224. );
  225. let data_account = vm.initialize_data_account();
  226. vm.function("new")
  227. .accounts(vec![("dataAccount", data_account)])
  228. .call();
  229. let res = vm
  230. .function("doTest")
  231. .accounts(vec![("systemProgram", [0; 32])])
  232. .call()
  233. .unwrap()
  234. .unwrap_tuple();
  235. assert_eq!(
  236. res,
  237. vec![
  238. BorshToken::Int {
  239. width: 256,
  240. value: BigInt::one(),
  241. },
  242. BorshToken::Int {
  243. width: 256,
  244. value: BigInt::from(3u8)
  245. }
  246. ]
  247. );
  248. }
  249. #[test]
  250. fn external_call_with_string_returns() {
  251. let mut vm = build_solidity(
  252. r#"
  253. contract bar0 {
  254. function test_other(address x) public returns (string) {
  255. string y = bar1.test_bar{program_id: x}(7);
  256. print(y);
  257. return y;
  258. }
  259. function test_this(address x) public {
  260. address a = bar1.who_am_i{program_id: x}();
  261. assert(a == address(x));
  262. }
  263. }
  264. contract bar1 {
  265. function test_bar(int64 y) public returns (string) {
  266. return "foo:{}".format(y);
  267. }
  268. function who_am_i() public returns (address) {
  269. return address(this);
  270. }
  271. }"#,
  272. );
  273. let bar1_account = vm.initialize_data_account();
  274. let bar1_program_id = vm.stack[0].id;
  275. vm.function("new")
  276. .accounts(vec![("dataAccount", bar1_account)])
  277. .call();
  278. let res = vm
  279. .function("test_bar")
  280. .arguments(&[BorshToken::Int {
  281. width: 64,
  282. value: BigInt::from(22u8),
  283. }])
  284. .call()
  285. .unwrap();
  286. assert_eq!(res, BorshToken::String(String::from("foo:22")));
  287. vm.set_program(0);
  288. let bar0_account = vm.initialize_data_account();
  289. vm.function("new")
  290. .accounts(vec![("dataAccount", bar0_account)])
  291. .call();
  292. let res = vm
  293. .function("test_other")
  294. .arguments(&[BorshToken::Address(bar1_program_id)])
  295. .accounts(vec![
  296. ("bar1_programId", bar1_program_id),
  297. ("systemProgram", [0; 32]),
  298. ])
  299. .remaining_accounts(&[
  300. AccountMeta {
  301. pubkey: Pubkey(bar1_account),
  302. is_writable: false,
  303. is_signer: false,
  304. },
  305. AccountMeta {
  306. pubkey: Pubkey(bar1_program_id),
  307. is_signer: false,
  308. is_writable: false,
  309. },
  310. ])
  311. .call()
  312. .unwrap();
  313. assert_eq!(res, BorshToken::String(String::from("foo:7")));
  314. vm.function("test_this")
  315. .arguments(&[BorshToken::Address(bar1_program_id)])
  316. .accounts(vec![
  317. ("bar1_programId", bar1_program_id),
  318. ("systemProgram", [0; 32]),
  319. ])
  320. .remaining_accounts(&[
  321. AccountMeta {
  322. pubkey: Pubkey(bar1_account),
  323. is_writable: false,
  324. is_signer: false,
  325. },
  326. AccountMeta {
  327. pubkey: Pubkey(bar1_program_id),
  328. is_signer: false,
  329. is_writable: false,
  330. },
  331. ])
  332. .call();
  333. }
  334. #[test]
  335. fn encode_call() {
  336. let mut vm = build_solidity(
  337. r#"
  338. contract bar0 {
  339. bytes8 private constant SELECTOR = bytes8(sha256(bytes('global:test_bar')));
  340. function test_other(address x) public returns (int64) {
  341. bytes select = abi.encodeWithSelector(SELECTOR, int64(7));
  342. bytes signature = abi.encodeCall(bar1.test_bar, 7);
  343. require(select == signature, "must be the same");
  344. (, bytes raw) = address(x).call(signature);
  345. (int64 v) = abi.decode(raw, (int64));
  346. return v + 5;
  347. }
  348. }
  349. contract bar1 {
  350. function test_bar(int64 y) public pure returns (int64) {
  351. return 3 + y;
  352. }
  353. }"#,
  354. );
  355. let bar1_account = vm.initialize_data_account();
  356. let bar1_program_id = vm.stack[0].id;
  357. vm.function("new")
  358. .accounts(vec![("dataAccount", bar1_account)])
  359. .call();
  360. let res = vm
  361. .function("test_bar")
  362. .arguments(&[BorshToken::Int {
  363. width: 64,
  364. value: BigInt::from(21u8),
  365. }])
  366. .call()
  367. .unwrap();
  368. assert_eq!(
  369. res,
  370. BorshToken::Int {
  371. width: 64,
  372. value: BigInt::from(24u8)
  373. }
  374. );
  375. vm.set_program(0);
  376. let bar0_account = vm.initialize_data_account();
  377. vm.function("new")
  378. .accounts(vec![("dataAccount", bar0_account)])
  379. .call();
  380. let res = vm
  381. .function("test_other")
  382. .arguments(&[BorshToken::Address(bar1_program_id)])
  383. .accounts(vec![("systemProgram", [0; 32])])
  384. .remaining_accounts(&[
  385. AccountMeta {
  386. pubkey: Pubkey(bar1_account),
  387. is_writable: false,
  388. is_signer: false,
  389. },
  390. AccountMeta {
  391. pubkey: Pubkey(bar1_program_id),
  392. is_signer: false,
  393. is_writable: false,
  394. },
  395. ])
  396. .call()
  397. .unwrap();
  398. assert_eq!(
  399. res,
  400. BorshToken::Int {
  401. width: 64,
  402. value: BigInt::from(15u8)
  403. }
  404. );
  405. }
  406. #[test]
  407. fn internal_function_storage() {
  408. let mut vm = build_solidity(
  409. r#"
  410. contract ft {
  411. function(int32,int32) internal returns (int32) func;
  412. function mul(int32 a, int32 b) internal returns (int32) {
  413. return a * b;
  414. }
  415. function add(int32 a, int32 b) internal returns (int32) {
  416. return a + b;
  417. }
  418. function set_op(bool action) public {
  419. if (action) {
  420. func = mul;
  421. } else {
  422. func = add;
  423. }
  424. }
  425. function test(int32 a, int32 b) public returns (int32) {
  426. return func(a, b);
  427. }
  428. }"#,
  429. );
  430. let data_account = vm.initialize_data_account();
  431. vm.function("new")
  432. .accounts(vec![("dataAccount", data_account)])
  433. .call();
  434. let res = vm
  435. .function("set_op")
  436. .arguments(&[BorshToken::Bool(true)])
  437. .accounts(vec![("dataAccount", data_account)])
  438. .call();
  439. assert!(res.is_none());
  440. let res = vm
  441. .function("test")
  442. .arguments(&[
  443. BorshToken::Int {
  444. width: 32,
  445. value: BigInt::from(3u8),
  446. },
  447. BorshToken::Int {
  448. width: 32,
  449. value: BigInt::from(5u8),
  450. },
  451. ])
  452. .accounts(vec![("dataAccount", data_account)])
  453. .call()
  454. .unwrap();
  455. assert_eq!(
  456. res,
  457. BorshToken::Int {
  458. width: 32,
  459. value: BigInt::from(15u8)
  460. }
  461. );
  462. let res = vm
  463. .function("set_op")
  464. .arguments(&[BorshToken::Bool(false)])
  465. .accounts(vec![("dataAccount", data_account)])
  466. .call();
  467. assert!(res.is_none());
  468. let res = vm
  469. .function("test")
  470. .arguments(&[
  471. BorshToken::Int {
  472. width: 32,
  473. value: BigInt::from(3u8),
  474. },
  475. BorshToken::Int {
  476. width: 32,
  477. value: BigInt::from(5u8),
  478. },
  479. ])
  480. .accounts(vec![("dataAccount", data_account)])
  481. .call()
  482. .unwrap();
  483. assert_eq!(
  484. res,
  485. BorshToken::Int {
  486. width: 32,
  487. value: BigInt::from(8u8)
  488. }
  489. );
  490. }
  491. #[test]
  492. fn raw_call_accounts() {
  493. let mut vm = build_solidity(
  494. r#"
  495. import {AccountMeta} from 'solana';
  496. contract SplToken {
  497. address constant tokenProgramId = address"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
  498. address constant SYSVAR_RENT_PUBKEY = address"SysvarRent111111111111111111111111111111111";
  499. struct InitializeMintInstruction {
  500. uint8 instruction;
  501. uint8 decimals;
  502. address mintAuthority;
  503. uint8 freezeAuthorityOption;
  504. address freezeAuthority;
  505. }
  506. function create_mint_with_freezeauthority(uint8 decimals, address mintAuthority, address freezeAuthority) public {
  507. InitializeMintInstruction instr = InitializeMintInstruction({
  508. instruction: 0,
  509. decimals: decimals,
  510. mintAuthority: mintAuthority,
  511. freezeAuthorityOption: 1,
  512. freezeAuthority: freezeAuthority
  513. });
  514. AccountMeta[2] metas = [
  515. AccountMeta({pubkey: instr.mintAuthority, is_writable: true, is_signer: false}),
  516. AccountMeta({pubkey: SYSVAR_RENT_PUBKEY, is_writable: false, is_signer: false})
  517. ];
  518. tokenProgramId.call{accounts: metas}(instr);
  519. }
  520. }"#,
  521. );
  522. let data_account = vm.initialize_data_account();
  523. vm.function("new")
  524. .accounts(vec![("dataAccount", data_account)])
  525. .call();
  526. let token = Pubkey(
  527. "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
  528. .from_base58()
  529. .unwrap()
  530. .try_into()
  531. .unwrap(),
  532. );
  533. vm.account_data.insert(token.0, AccountState::default());
  534. let test_args = |_vm: &VirtualMachine, instr: &Instruction, _signers: &[Pubkey]| {
  535. let sysvar_rent = Pubkey(
  536. "SysvarRent111111111111111111111111111111111"
  537. .from_base58()
  538. .unwrap()
  539. .try_into()
  540. .unwrap(),
  541. );
  542. assert_eq!(
  543. &instr.data,
  544. &[
  545. 0, 11, 113, 117, 105, 110, 113, 117, 97, 103, 105, 110, 116, 97, 113, 117, 97, 100,
  546. 114, 105, 110, 103, 101, 110, 116, 105, 108, 108, 105, 97, 114, 100, 116, 104, 1,
  547. 113, 117, 105, 110, 113, 117, 97, 103, 105, 110, 116, 97, 113, 117, 97, 100, 114,
  548. 105, 110, 103, 101, 110, 116, 105, 108, 108, 105, 111, 110, 116, 104, 115,
  549. ]
  550. );
  551. assert!(instr.accounts[0].is_writable);
  552. assert!(!instr.accounts[0].is_signer);
  553. assert_eq!(
  554. instr.accounts[0].pubkey,
  555. Pubkey([
  556. 113, 117, 105, 110, 113, 117, 97, 103, 105, 110, 116, 97, 113, 117, 97, 100, 114,
  557. 105, 110, 103, 101, 110, 116, 105, 108, 108, 105, 97, 114, 100, 116, 104
  558. ])
  559. );
  560. assert!(!instr.accounts[1].is_writable);
  561. assert!(!instr.accounts[1].is_signer);
  562. assert_eq!(instr.accounts[1].pubkey, sysvar_rent);
  563. };
  564. vm.call_params_check.insert(token.clone(), test_args);
  565. vm.function("create_mint_with_freezeauthority")
  566. .arguments(&[
  567. BorshToken::Uint {
  568. width: 8,
  569. value: BigInt::from(11u8),
  570. },
  571. BorshToken::Address(b"quinquagintaquadringentilliardth".to_owned()),
  572. BorshToken::Address(b"quinquagintaquadringentillionths".to_owned()),
  573. ])
  574. .accounts(vec![("tokenProgram", token.0), ("systemProgram", [0; 32])])
  575. .call();
  576. }
  577. #[test]
  578. fn pda() {
  579. let mut vm = build_solidity(
  580. r#"
  581. import {AccountMeta} from 'solana';
  582. contract pda {
  583. address constant tokenProgramId = address"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
  584. address constant SYSVAR_RENT_PUBKEY = address"SysvarRent111111111111111111111111111111111";
  585. function test() public {
  586. bytes instr = new bytes(1);
  587. AccountMeta[1] metas = [
  588. AccountMeta({pubkey: SYSVAR_RENT_PUBKEY, is_writable: false, is_signer: false})
  589. ];
  590. bytes3 foo = "foo";
  591. address addr = address"8dtukUTHTZoVQTA5i4UdC2z6A2b5yvnJhkzhYnwAk3Fm";
  592. tokenProgramId.call{seeds: [ [ foo ] , ["b", "a", "r"], [addr], [foo, addr, "meh"] ], accounts: metas}(instr);
  593. }
  594. }"#,
  595. );
  596. let data_account = vm.initialize_data_account();
  597. vm.function("new")
  598. .accounts(vec![("dataAccount", data_account)])
  599. .call();
  600. let test_args = |vm: &VirtualMachine, _instr: &Instruction, signers: &[Pubkey]| {
  601. assert_eq!(
  602. signers[0],
  603. create_program_address(&vm.stack[0].id, &[b"foo"])
  604. );
  605. assert_eq!(
  606. signers[1],
  607. create_program_address(&vm.stack[0].id, &[b"bar"])
  608. );
  609. assert_eq!(
  610. signers[2],
  611. create_program_address(&vm.stack[0].id, &[b"quinquagintaquadringentilliardth"])
  612. );
  613. assert_eq!(
  614. signers[3],
  615. create_program_address(
  616. &vm.stack[0].id,
  617. &[b"fooquinquagintaquadringentilliardthmeh"]
  618. )
  619. );
  620. };
  621. let token = Pubkey(
  622. "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
  623. .from_base58()
  624. .unwrap()
  625. .try_into()
  626. .unwrap(),
  627. );
  628. vm.account_data.insert(token.0, AccountState::default());
  629. vm.call_params_check.insert(token.clone(), test_args);
  630. vm.function("test")
  631. .accounts(vec![("tokenProgram", token.0), ("systemProgram", [0; 32])])
  632. .call();
  633. }