create_contract.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  1. // SPDX-License-Identifier: Apache-2.0
  2. use crate::{
  3. account_new, build_solidity, create_program_address, Account, AccountMeta, AccountState,
  4. BorshToken, Pubkey,
  5. };
  6. use base58::{FromBase58, ToBase58};
  7. #[test]
  8. fn simple_create_contract_no_seed() {
  9. let mut vm = build_solidity(
  10. r#"
  11. contract bar0 {
  12. function test_other() external {
  13. bar1.new("yo from bar0");
  14. }
  15. function call_bar1_at_address(string x) public {
  16. bar1.say_hello(x);
  17. }
  18. }
  19. @program_id("CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT")
  20. contract bar1 {
  21. @payer(payer)
  22. constructor(string v) {
  23. print("bar1 says: " + v);
  24. }
  25. function say_hello(string v) public {
  26. print("Hello {}".format(v));
  27. }
  28. }"#,
  29. );
  30. vm.set_program(0);
  31. let data_account = vm.initialize_data_account();
  32. vm.function("new")
  33. .accounts(vec![("dataAccount", data_account)])
  34. .call();
  35. let program_id: Account = "CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT"
  36. .from_base58()
  37. .unwrap()
  38. .try_into()
  39. .unwrap();
  40. let acc = account_new();
  41. let payer = account_new();
  42. println!("new account: {}", acc.to_base58());
  43. vm.account_data.insert(payer, AccountState::default());
  44. vm.account_data.insert(
  45. acc,
  46. AccountState {
  47. data: Vec::new(),
  48. owner: Some(program_id),
  49. lamports: 0,
  50. },
  51. );
  52. vm.function("test_other")
  53. .accounts(vec![
  54. ("bar1_dataAccount", acc),
  55. ("bar1_programId", program_id),
  56. ("payer", payer),
  57. ("systemProgram", [0; 32]),
  58. ])
  59. .remaining_accounts(&[AccountMeta {
  60. pubkey: Pubkey(acc),
  61. is_writable: true,
  62. is_signer: true,
  63. }])
  64. .call();
  65. assert_eq!(vm.logs, "bar1 says: yo from bar0");
  66. assert_eq!(vm.account_data[&acc].data.len(), 16);
  67. vm.logs.truncate(0);
  68. vm.function("call_bar1_at_address")
  69. .arguments(&[BorshToken::String(String::from("xywoleh"))])
  70. .accounts(vec![
  71. ("bar1_programId", program_id),
  72. ("systemProgram", [0; 32]),
  73. ])
  74. .call();
  75. assert_eq!(vm.logs, "Hello xywoleh");
  76. }
  77. #[test]
  78. fn simple_create_contract() {
  79. let mut vm = build_solidity(
  80. r#"
  81. contract bar0 {
  82. function test_other() external {
  83. bar1.new("yo from bar0");
  84. }
  85. function call_bar1_at_address(string x) public {
  86. bar1.say_hello(x);
  87. }
  88. }
  89. @program_id("CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT")
  90. contract bar1 {
  91. @payer(pay)
  92. constructor(string v) {
  93. print("bar1 says: " + v);
  94. }
  95. function say_hello(string v) public {
  96. print("Hello {}".format(v));
  97. }
  98. }"#,
  99. );
  100. vm.set_program(0);
  101. let data_account = vm.initialize_data_account();
  102. vm.function("new")
  103. .accounts(vec![("dataAccount", data_account)])
  104. .call();
  105. let program_id: Account = "CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT"
  106. .from_base58()
  107. .unwrap()
  108. .try_into()
  109. .unwrap();
  110. let seed = vm.create_pda(&program_id, 7);
  111. let payer = account_new();
  112. vm.account_data.insert(payer, AccountState::default());
  113. vm.function("test_other")
  114. .accounts(vec![
  115. ("bar1_dataAccount", seed.0),
  116. ("bar1_programId", program_id),
  117. ("pay", payer),
  118. ("systemProgram", [0; 32]),
  119. ])
  120. .call();
  121. assert_eq!(vm.logs, "bar1 says: yo from bar0");
  122. vm.logs.truncate(0);
  123. vm.function("call_bar1_at_address")
  124. .arguments(&[BorshToken::String(String::from("xywoleh"))])
  125. .accounts(vec![
  126. ("bar1_programId", program_id),
  127. ("systemProgram", [0; 32]),
  128. ])
  129. .call();
  130. assert_eq!(vm.logs, "Hello xywoleh");
  131. }
  132. #[test]
  133. fn create_contract_wrong_program_id() {
  134. let mut vm = build_solidity(
  135. r#"
  136. @program_id("CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT")
  137. contract bar0 {}
  138. "#,
  139. );
  140. let data_account = vm.initialize_data_account();
  141. vm.function("new")
  142. .accounts(vec![("dataAccount", data_account)])
  143. .call();
  144. let program = &vm.programs[0].id;
  145. let code = vm.account_data[program].data.clone();
  146. let mut vm = build_solidity(
  147. r#"
  148. @program_id("25UGQeMKp1YH8dR1WBtaj26iqfc49xjwfvLnUKavcz8E")
  149. contract bar0 {}
  150. "#,
  151. );
  152. let program = &vm.programs[0].id;
  153. vm.account_data.get_mut(program).unwrap().data = code;
  154. let data_account = vm.initialize_data_account();
  155. vm.function("new")
  156. .accounts(vec![("dataAccount", data_account)])
  157. .expected(7 << 32)
  158. .call();
  159. assert_eq!(
  160. vm.logs,
  161. "program_id should be CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT"
  162. );
  163. }
  164. #[test]
  165. fn call_constructor_twice() {
  166. let mut vm = build_solidity(
  167. r#"
  168. @program_id("CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT")
  169. contract bar0 {}
  170. "#,
  171. );
  172. let data_account = vm.initialize_data_account();
  173. vm.function("new")
  174. .accounts(vec![("dataAccount", data_account)])
  175. .call();
  176. vm.function("new")
  177. .accounts(vec![("dataAccount", data_account)])
  178. .expected(2)
  179. .call();
  180. }
  181. #[test]
  182. fn create_contract_with_payer() {
  183. let mut vm = build_solidity(
  184. r#"
  185. contract x {
  186. uint64 v;
  187. @payer(p)
  188. constructor() {
  189. v = 102;
  190. }
  191. function f() public returns (uint64) {
  192. return v;
  193. }
  194. }"#,
  195. );
  196. let payer = account_new();
  197. vm.account_data.insert(payer, AccountState::default());
  198. let data_account = vm.initialize_data_account();
  199. vm.function("new")
  200. .accounts(vec![
  201. ("dataAccount", data_account),
  202. ("p", payer),
  203. ("systemProgram", [0; 32]),
  204. ])
  205. .call();
  206. let ret = vm
  207. .function("f")
  208. .accounts(vec![("dataAccount", data_account)])
  209. .call()
  210. .unwrap();
  211. assert_eq!(
  212. ret,
  213. BorshToken::Uint {
  214. width: 64,
  215. value: 102.into()
  216. }
  217. );
  218. }
  219. #[test]
  220. #[should_panic(expected = "external call failed")]
  221. // 64424509440 = 15 << 32 (ERROR_NEW_ACCOUNT_NEEDED)
  222. fn missing_contract() {
  223. let mut vm = build_solidity(
  224. r#"
  225. contract bar0 {
  226. function test_other() external {
  227. bar1.new("yo from bar0");
  228. }
  229. function call_bar1_at_address(string x) public {
  230. bar1.say_hello(x);
  231. }
  232. }
  233. @program_id("7vJKRaKLGCNUPuHWdeHCTknkYf3dHXXEZ6ri7dc6ngeV")
  234. contract bar1 {
  235. constructor(string v) {
  236. print("bar1 says: " + v);
  237. }
  238. function say_hello(string v) public {
  239. print("Hello {}".format(v));
  240. }
  241. }"#,
  242. );
  243. vm.set_program(0);
  244. let data_account = vm.initialize_data_account();
  245. vm.function("new")
  246. .accounts(vec![("dataAccount", data_account)])
  247. .call();
  248. let missing = account_new();
  249. vm.logs.clear();
  250. vm.account_data.insert(missing, AccountState::default());
  251. let program_id: Account = "7vJKRaKLGCNUPuHWdeHCTknkYf3dHXXEZ6ri7dc6ngeV"
  252. .from_base58()
  253. .unwrap()
  254. .try_into()
  255. .unwrap();
  256. // There is no payer account, so the external call fails.
  257. let _ = vm
  258. .function("test_other")
  259. .accounts(vec![
  260. ("bar1_programId", program_id),
  261. ("bar1_dataAccount", missing),
  262. ("systemProgram", [0; 32]),
  263. ])
  264. .must_fail();
  265. }
  266. #[test]
  267. fn two_contracts() {
  268. let mut vm = build_solidity(
  269. r#"
  270. import 'solana';
  271. contract bar0 {
  272. function test_other(address a, address b, address payer) external {
  273. AccountMeta[2] bar1_metas = [
  274. AccountMeta({pubkey: a, is_writable: true, is_signer: true}),
  275. AccountMeta({pubkey: payer, is_writable: true, is_signer: true})
  276. ];
  277. AccountMeta[2] bar2_metas = [
  278. AccountMeta({pubkey: b, is_writable: true, is_signer: true}),
  279. AccountMeta({pubkey: payer, is_writable: true, is_signer: true})
  280. ];
  281. bar1.new{accounts: bar1_metas}("yo from bar0");
  282. bar1.new{accounts: bar2_metas}("hi from bar0");
  283. }
  284. }
  285. @program_id("CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT")
  286. contract bar1 {
  287. @payer(payer_account)
  288. constructor(string v) {
  289. print("bar1 says: " + v);
  290. }
  291. }"#,
  292. );
  293. vm.set_program(0);
  294. let data_account = vm.initialize_data_account();
  295. vm.function("new")
  296. .accounts(vec![("dataAccount", data_account)])
  297. .call();
  298. let program_id: Account = "CPDgqnhHDCsjFkJKMturRQ1QeM9EXZg3EYCeDoRP8pdT"
  299. .from_base58()
  300. .unwrap()
  301. .try_into()
  302. .unwrap();
  303. let seed1 = vm.create_pda(&program_id, 5);
  304. let seed2 = vm.create_pda(&program_id, 5);
  305. let payer = account_new();
  306. vm.account_data.insert(seed1.0, AccountState::default());
  307. vm.account_data.insert(seed2.0, AccountState::default());
  308. vm.account_data.insert(payer, AccountState::default());
  309. vm.function("test_other")
  310. .arguments(&[
  311. BorshToken::Address(seed1.0),
  312. BorshToken::Address(seed2.0),
  313. BorshToken::Address(payer),
  314. ])
  315. .accounts(vec![
  316. ("systemProgram", [0; 32]),
  317. ("bar1_programId", program_id),
  318. ])
  319. .remaining_accounts(&[
  320. AccountMeta {
  321. pubkey: Pubkey(seed1.0),
  322. is_signer: true,
  323. is_writable: true,
  324. },
  325. AccountMeta {
  326. pubkey: Pubkey(seed2.0),
  327. is_signer: true,
  328. is_writable: true,
  329. },
  330. AccountMeta {
  331. pubkey: Pubkey(payer),
  332. is_signer: true,
  333. is_writable: true,
  334. },
  335. ])
  336. .call();
  337. assert_eq!(vm.logs, "bar1 says: yo from bar0bar1 says: hi from bar0");
  338. vm.logs.truncate(0);
  339. }
  340. #[test]
  341. fn account_too_small() {
  342. let mut vm = build_solidity(
  343. r#"
  344. contract bar {
  345. int[200] foo1;
  346. }"#,
  347. );
  348. let data_account = vm.initialize_data_account();
  349. vm.account_data
  350. .get_mut(&data_account)
  351. .unwrap()
  352. .data
  353. .truncate(100);
  354. vm.function("new")
  355. .accounts(vec![("dataAccount", data_account)])
  356. .expected(5 << 32)
  357. .call();
  358. }
  359. #[test]
  360. fn account_with_space() {
  361. let mut vm = build_solidity(
  362. r#"
  363. contract bar {
  364. @payer(payer)
  365. constructor(@space uint64 x) {}
  366. function hello() public returns (bool) {
  367. return true;
  368. }
  369. }
  370. "#,
  371. );
  372. let data_account = vm.initialize_data_account();
  373. vm.account_data
  374. .get_mut(&data_account)
  375. .unwrap()
  376. .data
  377. .truncate(0);
  378. let payer = account_new();
  379. vm.account_data.insert(payer, AccountState::default());
  380. vm.function("new")
  381. .accounts(vec![
  382. ("dataAccount", data_account),
  383. ("payer", payer),
  384. ("systemProgram", [0; 32]),
  385. ])
  386. .arguments(&[BorshToken::Uint {
  387. width: 64,
  388. value: 306.into(),
  389. }])
  390. .call();
  391. assert_eq!(
  392. vm.account_data.get_mut(&data_account).unwrap().data.len(),
  393. 306
  394. );
  395. let ret = vm.function("hello").call().unwrap();
  396. assert_eq!(ret, BorshToken::Bool(true));
  397. }
  398. #[test]
  399. fn account_with_seed() {
  400. let mut vm = build_solidity(
  401. r#"
  402. contract bar {
  403. @space(511 + 102)
  404. @payer(payer)
  405. constructor(@seed bytes seed) {}
  406. function hello() public returns (bool) {
  407. return true;
  408. }
  409. }
  410. "#,
  411. );
  412. let program_id = vm.stack[0].id;
  413. let seed = vm.create_pda(&program_id, 7);
  414. let payer = account_new();
  415. vm.account_data.insert(payer, AccountState::default());
  416. vm.function("new")
  417. .accounts(vec![
  418. ("dataAccount", seed.0),
  419. ("payer", payer),
  420. ("systemProgram", [0; 32]),
  421. ])
  422. .arguments(&[BorshToken::Bytes(seed.1)])
  423. .call();
  424. assert_eq!(
  425. vm.account_data.get_mut(&seed.0).unwrap().data.len(),
  426. 511 + 102
  427. );
  428. let ret = vm.function("hello").call().unwrap();
  429. assert_eq!(ret, BorshToken::Bool(true));
  430. }
  431. #[test]
  432. fn account_with_seed_bump() {
  433. let mut vm = build_solidity(
  434. r#"
  435. contract bar {
  436. @space(511 + 102)
  437. @payer(payer)
  438. constructor(@seed address seed, @seed bytes2 seed2, @bump byte b) {}
  439. function hello() public returns (bool) {
  440. return true;
  441. }
  442. }
  443. "#,
  444. );
  445. let program_id = vm.stack[0].id;
  446. let (address, full_seed) = vm.create_pda(&program_id, 35);
  447. let bump = full_seed[34];
  448. let seed_addr = &full_seed[0..32];
  449. let seed2 = &full_seed[32..34];
  450. let payer = account_new();
  451. vm.account_data.insert(payer, AccountState::default());
  452. vm.function("new")
  453. .arguments(&[
  454. BorshToken::Address(seed_addr.try_into().unwrap()),
  455. BorshToken::FixedBytes(seed2.to_vec()),
  456. BorshToken::Uint {
  457. width: 8,
  458. value: bump.into(),
  459. },
  460. ])
  461. .accounts(vec![
  462. ("dataAccount", address),
  463. ("payer", payer),
  464. ("systemProgram", [0; 32]),
  465. ])
  466. .call();
  467. assert_eq!(
  468. vm.account_data.get_mut(&address).unwrap().data.len(),
  469. 511 + 102
  470. );
  471. let ret = vm.function("hello").call().unwrap();
  472. assert_eq!(ret, BorshToken::Bool(true));
  473. }
  474. #[test]
  475. fn account_with_seed_bump_literals() {
  476. let mut vm = build_solidity(
  477. r#"
  478. @program_id("vS5Tf8mnHGbUCMLQWrnvsFvwHLfA5p3yQM3ozxPckn8")
  479. contract bar {
  480. @space(2 << 8 + 4)
  481. @seed("meh")
  482. @bump(33) // 33 = ascii !
  483. @payer(my_account)
  484. constructor() {}
  485. function hello() public returns (bool) {
  486. return true;
  487. }
  488. }
  489. "#,
  490. );
  491. let program_id = vm.stack[0].id;
  492. let account = create_program_address(&program_id, &[b"meh!"]);
  493. let payer = account_new();
  494. vm.create_empty_account(&account.0, &program_id);
  495. vm.account_data.insert(payer, AccountState::default());
  496. vm.function("new")
  497. .accounts(vec![
  498. ("dataAccount", account.0),
  499. ("my_account", payer),
  500. ("systemProgram", [0; 32]),
  501. ])
  502. .call();
  503. assert_eq!(
  504. vm.account_data.get_mut(&account.0).unwrap().data.len(),
  505. 8192
  506. );
  507. let ret = vm.function("hello").call().unwrap();
  508. assert_eq!(ret, BorshToken::Bool(true));
  509. }
  510. #[test]
  511. fn create_child() {
  512. let mut vm = build_solidity(
  513. r#"
  514. contract creator {
  515. function create_child() external {
  516. print("Going to create child");
  517. Child.new();
  518. Child.say_hello();
  519. }
  520. }
  521. @program_id("Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT")
  522. contract Child {
  523. @payer(payer)
  524. @space(511 + 7)
  525. constructor() {
  526. print("In child constructor");
  527. }
  528. function say_hello() pure public {
  529. print("Hello there");
  530. }
  531. }"#,
  532. );
  533. vm.set_program(0);
  534. let data_account = vm.initialize_data_account();
  535. vm.function("new")
  536. .accounts(vec![("dataAccount", data_account)])
  537. .call();
  538. let child_program_id: Account = "Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT"
  539. .from_base58()
  540. .unwrap()
  541. .try_into()
  542. .unwrap();
  543. let payer = account_new();
  544. let program_id = vm.stack[0].id;
  545. let seed = vm.create_pda(&program_id, 7);
  546. vm.account_data.insert(payer, AccountState::default());
  547. vm.account_data.insert(seed.0, AccountState::default());
  548. vm.function("create_child")
  549. .accounts(vec![
  550. ("Child_dataAccount", seed.0),
  551. ("Child_programId", child_program_id),
  552. ("payer", payer),
  553. ("systemProgram", [0; 32]),
  554. ])
  555. .remaining_accounts(&[AccountMeta {
  556. pubkey: Pubkey(seed.0),
  557. is_signer: true,
  558. is_writable: true,
  559. }])
  560. .call();
  561. assert_eq!(
  562. vm.logs,
  563. "Going to create childIn child constructorHello there"
  564. );
  565. }
  566. #[test]
  567. fn create_child_with_meta() {
  568. let mut vm = build_solidity(
  569. r#"
  570. import 'solana';
  571. contract creator {
  572. function create_child_with_meta(address child, address payer) public {
  573. print("Going to create child");
  574. AccountMeta[2] metas = [
  575. AccountMeta({pubkey: child, is_signer: true, is_writable: true}),
  576. AccountMeta({pubkey: payer, is_signer: true, is_writable: true})
  577. // Passing the system account here crashes the VM, even if I add it to vm.account_data
  578. // AccountMeta({pubkey: address"11111111111111111111111111111111", is_writable: false, is_signer: false})
  579. ];
  580. Child.new{accounts: metas}();
  581. Child.say_hello();
  582. }
  583. }
  584. @program_id("Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT")
  585. contract Child {
  586. @payer(payer)
  587. @space(511 + 7)
  588. constructor() {
  589. print("In child constructor");
  590. }
  591. function say_hello() pure public {
  592. print("Hello there");
  593. }
  594. }
  595. "#,
  596. );
  597. vm.set_program(0);
  598. let data_account = vm.initialize_data_account();
  599. vm.function("new")
  600. .accounts(vec![("dataAccount", data_account)])
  601. .call();
  602. let payer = account_new();
  603. let program_id = vm.stack[0].id;
  604. let seed = vm.create_pda(&program_id, 7);
  605. vm.account_data.insert(seed.0, AccountState::default());
  606. vm.account_data.insert(payer, AccountState::default());
  607. let child_program_id: Account = "Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT"
  608. .from_base58()
  609. .unwrap()
  610. .try_into()
  611. .unwrap();
  612. vm.function("create_child_with_meta")
  613. .arguments(&[BorshToken::Address(seed.0), BorshToken::Address(payer)])
  614. .accounts(vec![
  615. ("Child_programId", child_program_id),
  616. ("systemProgram", [0; 32]),
  617. ])
  618. .remaining_accounts(&[
  619. AccountMeta {
  620. pubkey: Pubkey(seed.0),
  621. is_signer: false,
  622. is_writable: false,
  623. },
  624. AccountMeta {
  625. pubkey: Pubkey(payer),
  626. is_signer: true,
  627. is_writable: false,
  628. },
  629. ])
  630. .call();
  631. assert_eq!(
  632. vm.logs,
  633. "Going to create childIn child constructorHello there"
  634. );
  635. }