evm.rs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. // SPDX-License-Identifier: Apache-2.0
  2. use rayon::prelude::*;
  3. use solang::{file_resolver::FileResolver, parse_and_resolve, sema::ast, Target};
  4. use std::{
  5. ffi::OsStr,
  6. fs,
  7. path::{Path, PathBuf},
  8. };
  9. use walkdir::WalkDir;
  10. mod evm_tests;
  11. fn test_solidity(src: &str) -> ast::Namespace {
  12. let mut cache = FileResolver::default();
  13. cache.set_file_contents("test.sol", src.to_string());
  14. let ns = parse_and_resolve(OsStr::new("test.sol"), &mut cache, Target::EVM);
  15. ns.print_diagnostics_in_plain(&cache, false);
  16. ns
  17. }
  18. #[test]
  19. fn address() {
  20. let ns = test_solidity(
  21. "
  22. contract address_tester {
  23. function encode_const() public returns (address) {
  24. return 0x52908400098527886E0F7030069857D2E4169EE7;
  25. }
  26. function test_arg(address foo) public {
  27. assert(foo == 0x27b1fdb04752bbc536007a920d24acb045561c26);
  28. // this literal is a number
  29. int x = 0x27b1fdb047_52bbc536007a920d24acb045561C26;
  30. assert(int(foo) == x);
  31. }
  32. function allones() public returns (address) {
  33. return address(1);
  34. }
  35. }",
  36. );
  37. assert!(!ns.diagnostics.any_errors());
  38. }
  39. #[test]
  40. fn try_catch() {
  41. let ns = test_solidity(
  42. r#"
  43. contract b {
  44. int32 x;
  45. constructor(int32 a) public {
  46. x = a;
  47. }
  48. function get_x(int32 t) public returns (int32) {
  49. if (t == 0) {
  50. revert("cannot be zero");
  51. }
  52. return x * t;
  53. }
  54. }
  55. contract c {
  56. b x;
  57. constructor() public {
  58. x = new b(102);
  59. }
  60. function test() public returns (int32) {
  61. int32 state = 0;
  62. try x.get_x(0) returns (int32 l) {
  63. state = 1;
  64. } catch Error(string err) {
  65. if (err == "cannot be zero") {
  66. state = 2;
  67. } else {
  68. state = 3;
  69. }
  70. } catch (bytes ) {
  71. state = 4;
  72. }
  73. return state;
  74. }
  75. }"#,
  76. );
  77. assert!(!ns.diagnostics.any_errors());
  78. }
  79. #[test]
  80. fn selfdestruct() {
  81. let ns = test_solidity(
  82. r##"
  83. contract other {
  84. function goaway(address payable recipient) public returns (bool) {
  85. selfdestruct(recipient);
  86. }
  87. }
  88. contract c {
  89. other o;
  90. function step1() public {
  91. o = new other{value: 511}();
  92. }
  93. function step2() public {
  94. bool foo = o.goaway(payable(address(this)));
  95. }
  96. }"##,
  97. );
  98. assert!(!ns.diagnostics.any_errors());
  99. }
  100. #[test]
  101. fn eth_builtins() {
  102. let ns = test_solidity(
  103. r#"
  104. contract testing {
  105. function test_address() public view returns (uint256 ret) {
  106. assembly {
  107. let a := address()
  108. ret := a
  109. }
  110. }
  111. function test_balance() public view returns (uint256 ret) {
  112. assembly {
  113. let a := address()
  114. ret := balance(a)
  115. }
  116. }
  117. function test_selfbalance() public view returns (uint256 ret) {
  118. assembly {
  119. let a := selfbalance()
  120. ret := a
  121. }
  122. }
  123. function test_caller() public view returns (uint256 ret) {
  124. assembly {
  125. let a := caller()
  126. ret := a
  127. }
  128. }
  129. function test_callvalue() public view returns (uint256 ret) {
  130. assembly {
  131. let a := callvalue()
  132. ret := a
  133. }
  134. }
  135. function test_extcodesize() public view returns (uint256 ret) {
  136. assembly {
  137. let a := address()
  138. ret := extcodesize(a)
  139. }
  140. }
  141. }
  142. "#,
  143. );
  144. assert!(!ns.diagnostics.any_errors());
  145. }
  146. #[test]
  147. fn ethereum_solidity_tests() {
  148. let error_matcher =
  149. regex::Regex::new(r"// ----\r?\n(//\s+Warning \d+: .*\n)*//\s+\w+Error( \d+)?: (.*)")
  150. .unwrap();
  151. let entries = WalkDir::new(
  152. Path::new(env!("CARGO_MANIFEST_DIR"))
  153. .join("testdata/solidity/test/libsolidity/semanticTests"),
  154. )
  155. .into_iter()
  156. .chain(WalkDir::new(
  157. Path::new(env!("CARGO_MANIFEST_DIR"))
  158. .join("testdata/solidity/test/libsolidity/syntaxTests"),
  159. ));
  160. let errors: usize = entries
  161. .par_bridge()
  162. .filter_map(|e| {
  163. let entry = e.unwrap();
  164. let file_name = entry.file_name().to_string_lossy();
  165. // FIXME: max_depth_reached_4.sol causes a stack overflow in resolve_expression.rs
  166. // FIXME: others listed explicitly cause panics and need fixing
  167. if !file_name.ends_with("max_depth_reached_4.sol")
  168. && !file_name.ends_with("invalid_utf8_sequence.sol")
  169. // Bug in solc: https://github.com/ethereum/solidity/issues/11573
  170. && !file_name
  171. .ends_with("internal_library_function_attached_to_string_accepting_storage.sol")
  172. && file_name.ends_with(".sol")
  173. {
  174. Some(entry)
  175. } else {
  176. None
  177. }
  178. })
  179. .map(|entry| {
  180. let path = entry.path().parent().unwrap();
  181. let source = fs::read_to_string(entry.path()).unwrap();
  182. let expect_error = error_matcher
  183. .captures(&source)
  184. .map(|captures| captures.get(3).unwrap().as_str());
  185. let (mut cache, names) = set_file_contents(&source, entry.path());
  186. cache.add_import_path(path);
  187. let errors: usize = names
  188. .iter()
  189. .map(|name| {
  190. let ns = parse_and_resolve(OsStr::new(&name), &mut cache, Target::EVM);
  191. if ns.diagnostics.any_errors() {
  192. if expect_error.is_none() {
  193. println!("file: {} name:{}", entry.path().display(), name);
  194. ns.print_diagnostics_in_plain(&cache, false);
  195. 1
  196. } else {
  197. 0
  198. }
  199. } else if let Some(error) = expect_error {
  200. println!("file: {} name:{}", entry.path().display(), name);
  201. println!("expecting error {error}");
  202. 1
  203. } else {
  204. 0
  205. }
  206. })
  207. .sum();
  208. errors
  209. })
  210. .sum();
  211. assert_eq!(errors, 897);
  212. }
  213. fn set_file_contents(source: &str, path: &Path) -> (FileResolver, Vec<String>) {
  214. let mut cache = FileResolver::default();
  215. let mut name = path.to_string_lossy().to_string();
  216. let mut names = Vec::new();
  217. let mut contents = String::new();
  218. let source_delimiter = regex::Regex::new(r"==== Source: (.*) ====").unwrap();
  219. let external_source_delimiter = regex::Regex::new(r"==== ExternalSource: (.*) ====").unwrap();
  220. let equals = regex::Regex::new("([a-zA-Z0-9_]+)=(.*)").unwrap();
  221. for line in source.lines() {
  222. if let Some(cap) = source_delimiter.captures(line) {
  223. if !contents.is_empty() {
  224. cache.set_file_contents(&name, contents);
  225. names.push(name);
  226. name = String::new();
  227. }
  228. cap.get(1).unwrap().as_str().clone_into(&mut name);
  229. if name == "////" {
  230. "test.sol".clone_into(&mut name);
  231. }
  232. contents = String::new();
  233. } else if let Some(cap) = external_source_delimiter.captures(line) {
  234. let filename = cap.get(1).unwrap().as_str();
  235. let mut name = filename.to_owned();
  236. if let Some(cap) = equals.captures(filename) {
  237. let mut ext = path.parent().unwrap().to_path_buf();
  238. ext.push(cap.get(2).unwrap().as_str());
  239. cap.get(1).unwrap().as_str().clone_into(&mut name);
  240. let source = fs::read_to_string(ext).unwrap();
  241. cache.set_file_contents(&name, source);
  242. }
  243. // else rely on file resolver to import stuff
  244. } else {
  245. contents.push_str(line);
  246. contents.push('\n');
  247. }
  248. }
  249. if !contents.is_empty() {
  250. cache.set_file_contents(&name, contents);
  251. names.push(name);
  252. }
  253. cache.add_import_path(&PathBuf::from(""));
  254. (cache, names)
  255. }