errors.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. // SPDX-License-Identifier: Apache-2.0
  2. use crate::build_solidity;
  3. use parity_scale_codec::{Decode, Encode};
  4. use primitive_types::U256;
  5. use solang::{
  6. codegen::{
  7. revert::{PanicCode, PanicCode::*, SolidityError},
  8. Expression,
  9. },
  10. sema::ast::Namespace,
  11. Target,
  12. };
  13. #[derive(Encode, Decode)]
  14. struct PanicData {
  15. selector: [u8; 4],
  16. data: U256,
  17. }
  18. impl From<PanicCode> for PanicData {
  19. fn from(value: PanicCode) -> Self {
  20. Self {
  21. selector: SolidityError::Panic(value)
  22. .selector(&Namespace::new(Target::default_polkadot())),
  23. data: U256::from(value as u8),
  24. }
  25. }
  26. }
  27. #[derive(Encode, Decode)]
  28. struct ErrorData {
  29. selector: [u8; 4],
  30. msg: String,
  31. }
  32. impl From<String> for ErrorData {
  33. fn from(msg: String) -> Self {
  34. Self {
  35. selector: SolidityError::String(Expression::Poison)
  36. .selector(&Namespace::new(Target::default_polkadot())),
  37. msg,
  38. }
  39. }
  40. }
  41. #[test]
  42. fn constructor_buf_too_small() {
  43. let mut runtime = build_solidity(
  44. r#"contract RuntimeErrors {
  45. function write_bytes_failure(uint8 buf_size) public pure {
  46. bytes data = new bytes(10);
  47. bytes smol_buf = new bytes(buf_size);
  48. smol_buf.writeBytes(data, 0);
  49. }
  50. }"#,
  51. );
  52. runtime.function_expect_failure("write_bytes_failure", 9u8.encode());
  53. assert!(runtime
  54. .debug_buffer()
  55. .contains("runtime_error: data does not fit into buffer in test.sol"));
  56. assert_eq!(runtime.output(), PanicData::from(ArrayIndexOob).encode());
  57. }
  58. #[test]
  59. fn math_overflow() {
  60. let mut runtime = build_solidity(
  61. r#"contract RuntimeErrors {
  62. function math_overflow(int8 num) public pure returns (int8) {
  63. int8 ovf = num + 120;
  64. return ovf;
  65. }
  66. }"#,
  67. );
  68. runtime.function_expect_failure("math_overflow", 10u8.encode());
  69. assert!(runtime
  70. .debug_buffer()
  71. .contains("runtime_error: math overflow in test.sol"));
  72. assert_eq!(runtime.output(), PanicData::from(MathOverflow).encode());
  73. }
  74. #[test]
  75. fn require() {
  76. let mut runtime = build_solidity(
  77. r#"contract RuntimeErrors {
  78. function require_test(int8 num) public pure returns (int8) {
  79. require(num > 10, "sesa");
  80. return 0;
  81. }
  82. }"#,
  83. );
  84. runtime.function_expect_failure("require_test", 9u8.encode());
  85. assert!(runtime
  86. .debug_buffer()
  87. .contains("runtime_error: sesa require condition failed in test.sol"));
  88. assert_eq!(
  89. runtime.output(),
  90. ErrorData::from("sesa".to_string()).encode()
  91. );
  92. }
  93. #[test]
  94. fn require_without_message() {
  95. let mut runtime = build_solidity(
  96. r#"contract RuntimeErrors {
  97. function require_test(int8 num) public pure returns (int8) {
  98. require(num > 10);
  99. return 0;
  100. }
  101. }"#,
  102. );
  103. runtime.function_expect_failure("require_test", 9u8.encode());
  104. assert!(runtime
  105. .debug_buffer()
  106. .contains("runtime_error: require condition failed in test.sol"));
  107. assert!(runtime.output().is_empty());
  108. }
  109. #[test]
  110. fn assert() {
  111. let mut runtime = build_solidity(
  112. r#"contract RuntimeErrors {
  113. function assert_test(int8 num) public returns (int8) {
  114. assert(num > 10);
  115. return 0;
  116. }
  117. }"#,
  118. );
  119. runtime.function_expect_failure("assert_test", 9u8.encode());
  120. assert!(runtime
  121. .debug_buffer()
  122. .contains("runtime_error: assert failure in test.sol"));
  123. assert_eq!(runtime.output(), PanicData::from(Assertion).encode());
  124. }
  125. #[test]
  126. fn set_storage_bytes_oob() {
  127. let mut runtime = build_solidity(
  128. r#"contract RuntimeErrors {
  129. bytes public b = hex"0000_00fa";
  130. function set_storage_bytes() public returns (bytes) {
  131. bytes sesa = new bytes(1);
  132. b[5] = sesa[0];
  133. return sesa;
  134. }
  135. }"#,
  136. );
  137. runtime.constructor(0, vec![]);
  138. runtime.function_expect_failure("set_storage_bytes", Vec::new());
  139. assert!(runtime
  140. .debug_buffer()
  141. .contains("runtime_error: storage index out of bounds in test.sol"));
  142. assert_eq!(runtime.output(), PanicData::from(ArrayIndexOob).encode());
  143. }
  144. #[test]
  145. fn get_storage_bytes_oob() {
  146. let mut runtime = build_solidity(
  147. r#"contract RuntimeErrors {
  148. bytes public b = hex"0000_00fa";
  149. function get_storage_bytes() public returns (bytes) {
  150. bytes sesa = new bytes(1);
  151. sesa[0] = b[5];
  152. return sesa;
  153. }
  154. }"#,
  155. );
  156. runtime.constructor(0, vec![]);
  157. runtime.function_expect_failure("get_storage_bytes", Vec::new());
  158. assert!(runtime
  159. .debug_buffer()
  160. .contains("runtime_error: storage array index out of bounds in test.sol"));
  161. assert_eq!(runtime.output(), PanicData::from(ArrayIndexOob).encode());
  162. }
  163. #[test]
  164. fn transfer_fails() {
  165. let mut runtime = build_solidity(
  166. r#"contract RuntimeErrors {
  167. function transfer_abort() public {
  168. address a = address(0);
  169. payable(a).transfer(10);
  170. }
  171. }"#,
  172. );
  173. runtime.function_expect_failure("transfer_abort", Vec::new());
  174. assert!(runtime
  175. .debug_buffer()
  176. .contains("runtime_error: value transfer failure in test.sol"));
  177. assert!(runtime.output().is_empty());
  178. }
  179. #[test]
  180. fn empty_storage_array_pop() {
  181. let mut runtime = build_solidity(
  182. r#"contract RuntimeErrors {
  183. uint256[] public arr;
  184. function pop_empty_storage() public {
  185. arr.pop();
  186. }
  187. }"#,
  188. );
  189. runtime.constructor(0, vec![]);
  190. runtime.function_expect_failure("pop_empty_storage", Vec::new());
  191. assert!(runtime
  192. .debug_buffer()
  193. .contains("runtime_error: pop from empty storage array in test.sol"));
  194. assert_eq!(runtime.output(), PanicData::from(EmptyArrayPop).encode());
  195. }
  196. #[test]
  197. fn contract_instantiatoin_fail() {
  198. let mut runtime = build_solidity(
  199. r#"contract RuntimeErrors {
  200. child public c;
  201. child public c2;
  202. constructor() payable {}
  203. function create_child() public {
  204. c = new child{value: 900e15, salt: hex"02"}();
  205. c2 = new child{value: 900e15, salt: hex"02"}();
  206. uint128 x = address(this).balance;
  207. print("x = {}".format(x));
  208. }
  209. }
  210. contract child {
  211. function say_my_name() public pure returns (string memory) {
  212. print("say_my_name");
  213. return "child";
  214. }
  215. }"#,
  216. );
  217. runtime.set_transferred_value(3500);
  218. runtime.constructor(0, Vec::new());
  219. runtime.set_transferred_value(0);
  220. runtime.function_expect_failure("create_child", Vec::new());
  221. assert!(runtime
  222. .debug_buffer()
  223. .contains("runtime_error: contract creation failed in test.sol:6"));
  224. assert!(runtime.output().is_empty());
  225. }
  226. #[test]
  227. fn revert() {
  228. let mut runtime = build_solidity(
  229. r#"contract RuntimeErrors {
  230. function i_will_revert() public pure {
  231. revert();
  232. }
  233. function revert_dyn(string s) public pure {
  234. revert(s);
  235. }
  236. function revert_static() public pure {
  237. revert("hi");
  238. }
  239. }"#,
  240. );
  241. runtime.function_expect_failure("i_will_revert", Vec::new());
  242. assert!(runtime
  243. .debug_buffer()
  244. .contains("runtime_error: unspecified revert encountered"));
  245. assert!(runtime.output().is_empty());
  246. let msg = "hello \"\n\0world!".to_string();
  247. runtime.function_expect_failure("revert_dyn", msg.encode());
  248. dbg!(runtime.debug_buffer());
  249. assert!(runtime
  250. .debug_buffer()
  251. .contains("hello \"\n\0world! revert encountered"));
  252. runtime.function_expect_failure("revert_static", Vec::new());
  253. assert!(runtime.debug_buffer().contains("hi revert encountered"));
  254. assert_eq!(runtime.output(), ErrorData::from("hi".to_string()).encode());
  255. }
  256. #[test]
  257. fn int_too_large_for_bytes() {
  258. let mut runtime = build_solidity(
  259. r#"contract RuntimeErrors {
  260. function write_integer_failure(uint8 buf_size) public {
  261. bytes smol_buf = new bytes(buf_size);
  262. smol_buf.writeUint32LE(350, 20);
  263. }
  264. }"#,
  265. );
  266. runtime.function_expect_failure("write_integer_failure", 1u8.encode());
  267. assert!(runtime
  268. .debug_buffer()
  269. .contains("runtime_error: integer too large to write in buffer in test.sol"));
  270. assert_eq!(runtime.output(), PanicData::from(ArrayIndexOob).encode());
  271. }
  272. #[test]
  273. fn invalid_instruction() {
  274. let mut runtime = build_solidity(
  275. r#"contract RuntimeErrors {
  276. function invalid_instruction() public pure {
  277. assembly {
  278. invalid()
  279. }
  280. }
  281. }"#,
  282. );
  283. runtime.function_expect_failure("invalid_instruction", Vec::new());
  284. assert!(runtime
  285. .debug_buffer()
  286. .contains("runtime_error: reached invalid instruction in test.sol"));
  287. assert_eq!(runtime.output(), PanicData::from(Generic).encode());
  288. }
  289. #[test]
  290. fn array_index_oob() {
  291. let mut runtime = build_solidity(
  292. r#"contract RuntimeErrors {
  293. function out_of_bounds(uint8 input) public pure returns (uint256) {
  294. uint256[] a = new uint256[](input);
  295. return a[20];
  296. }
  297. }"#,
  298. );
  299. runtime.function_expect_failure("out_of_bounds", 19u8.encode());
  300. assert!(runtime
  301. .debug_buffer()
  302. .contains("runtime_error: array index out of bounds in test.sol"));
  303. assert_eq!(runtime.output(), PanicData::from(ArrayIndexOob).encode());
  304. }
  305. #[test]
  306. fn truncated_type_overflow() {
  307. let mut runtime = build_solidity(
  308. r#"contract RuntimeErrors {
  309. function trunc_failure(uint128 input) public returns (uint256) {
  310. uint256[] a = new uint256[](input);
  311. return a[0];
  312. }
  313. }"#,
  314. );
  315. runtime.function_expect_failure("trunc_failure", u128::MAX.encode());
  316. assert!(runtime
  317. .debug_buffer()
  318. .contains("runtime_error: truncated type overflows in test.sol"));
  319. assert_eq!(runtime.output(), PanicData::from(MathOverflow).encode());
  320. }
  321. #[test]
  322. fn byte_cast_fail() {
  323. let mut runtime = build_solidity(
  324. r#"contract RuntimeErrors {
  325. function byte_cast_failure(uint8 num) public pure returns (bytes) {
  326. bytes smol_buf = new bytes(num);
  327. bytes32 b32 = bytes32(smol_buf);
  328. return b32;
  329. }
  330. }"#,
  331. );
  332. runtime.function_expect_failure("byte_cast_failure", 33u8.encode());
  333. assert!(runtime
  334. .debug_buffer()
  335. .contains("runtime_error: bytes cast error in test.sol"));
  336. assert_eq!(
  337. runtime.output(),
  338. PanicData::from(PanicCode::Generic).encode()
  339. )
  340. }
  341. #[test]
  342. fn int_read_oob() {
  343. let mut runtime = build_solidity(
  344. r#"contract RuntimeErrors {
  345. function read_integer_failure(uint32 offset) public {
  346. bytes smol_buf = new bytes(1);
  347. smol_buf.readUint16LE(offset);
  348. }
  349. }"#,
  350. );
  351. runtime.function_expect_failure("read_integer_failure", 2u32.encode());
  352. assert!(runtime
  353. .debug_buffer()
  354. .contains("runtime_error: read integer out of bounds in test.sol"));
  355. assert_eq!(runtime.output(), PanicData::from(ArrayIndexOob).encode());
  356. }
  357. #[test]
  358. fn external_call() {
  359. let mut runtime = build_solidity(
  360. r#"contract RuntimeErrors {
  361. callee public cal;
  362. constructor() payable {}
  363. function call_ext() public {
  364. cal = new callee();
  365. cal.callee_func{gas: 1e15}();
  366. }
  367. }
  368. contract callee {
  369. function callee_func() public {
  370. revert();
  371. }
  372. }"#,
  373. );
  374. runtime.set_transferred_value(3500);
  375. runtime.constructor(0, vec![]);
  376. runtime.function_expect_failure("call_ext", Vec::new());
  377. assert!(runtime
  378. .debug_buffer()
  379. .contains("runtime_error: external call failed in test.sol"));
  380. assert!(runtime.output().is_empty());
  381. }
  382. #[test]
  383. fn non_payable_function_with_value() {
  384. let mut runtime =
  385. build_solidity(r#"contract RuntimeErrors { function dont_pay_me() public {} }"#);
  386. runtime.set_transferred_value(1);
  387. runtime.function_expect_failure("dont_pay_me", Vec::new());
  388. assert!(runtime
  389. .debug_buffer()
  390. .contains("runtime_error: non payable function dont_pay_me received value"));
  391. assert!(runtime.output().is_empty());
  392. }
  393. #[test]
  394. fn multiplication_overflow_big_u256() {
  395. let mut runtime = build_solidity(
  396. r#"contract RuntimeErrors {
  397. function pow(uint256 bar) public pure returns(uint256) {
  398. return bar ** 2;
  399. }
  400. function mul(uint256 bar) public pure returns(uint256) {
  401. return bar * 2;
  402. }
  403. }"#,
  404. );
  405. let expected_debug_output = "runtime_error: multiplication overflow";
  406. let expected_output = PanicData::from(MathOverflow).encode();
  407. runtime.function_expect_failure("pow", U256::MAX.encode());
  408. assert!(runtime.debug_buffer().contains(expected_debug_output));
  409. assert_eq!(runtime.output(), expected_output);
  410. runtime.function_expect_failure("mul", U256::MAX.encode());
  411. assert!(runtime.debug_buffer().contains(expected_debug_output));
  412. assert_eq!(runtime.output(), expected_output)
  413. }
  414. #[test]
  415. fn multiplication_overflow_u8() {
  416. let mut runtime = build_solidity(
  417. r#"contract RuntimeErrors {
  418. function pow(uint8 bar) public pure returns(uint8) {
  419. return bar ** 2;
  420. }
  421. function mul(uint8 bar) public pure returns(uint8) {
  422. return bar * 2;
  423. }
  424. }"#,
  425. );
  426. let expected_debug_output = "runtime_error: math overflow";
  427. let expected_output = PanicData::from(MathOverflow).encode();
  428. runtime.function_expect_failure("pow", u8::MAX.encode());
  429. assert!(runtime.debug_buffer().contains(expected_debug_output));
  430. assert_eq!(runtime.output(), expected_output);
  431. runtime.function_expect_failure("mul", u8::MAX.encode());
  432. assert!(runtime.debug_buffer().contains(expected_debug_output));
  433. assert_eq!(runtime.output(), expected_output)
  434. }
  435. #[test]
  436. fn empty_array_pop() {
  437. let mut runtime = build_solidity(
  438. r#"contract RuntimeErrors {
  439. function pop_empty_array() public pure returns(uint256[] arr) {
  440. arr.pop();
  441. }
  442. }"#,
  443. );
  444. runtime.function_expect_failure("pop_empty_array", vec![]);
  445. assert!(runtime.debug_buffer().contains("pop from empty array"));
  446. assert_eq!(runtime.output(), PanicData::from(EmptyArrayPop).encode());
  447. }
  448. #[test]
  449. fn uint256_div_by_zero() {
  450. let mut runtime = build_solidity(
  451. r#"contract RuntimeErrors {
  452. function div_by_zero(uint256 div) public pure returns(uint256 ret) {
  453. ret = ret / div;
  454. }
  455. function mod_zero(uint256 div) public pure returns(uint256 ret) {
  456. ret = ret % div;
  457. }
  458. }"#,
  459. );
  460. runtime.function_expect_failure("div_by_zero", U256::zero().encode());
  461. assert!(runtime.debug_buffer().contains("division by zero"));
  462. assert_eq!(runtime.output(), PanicData::from(DivisionByZero).encode());
  463. runtime.function_expect_failure("mod_zero", U256::zero().encode());
  464. assert!(runtime.debug_buffer().contains("division by zero"));
  465. assert_eq!(runtime.output(), PanicData::from(DivisionByZero).encode());
  466. }
  467. #[test]
  468. fn int256_div_by_zero() {
  469. let mut runtime = build_solidity(
  470. r#"contract RuntimeErrors {
  471. function div_by_zero(int256 div) public pure returns(int256 ret) {
  472. ret = ret / div;
  473. }
  474. function mod_zero(int256 div) public pure returns(int256 ret) {
  475. ret = ret % div;
  476. }
  477. }"#,
  478. );
  479. runtime.function_expect_failure("div_by_zero", U256::zero().encode());
  480. assert!(runtime.debug_buffer().contains("division by zero"));
  481. assert_eq!(runtime.output(), PanicData::from(DivisionByZero).encode());
  482. runtime.function_expect_failure("mod_zero", U256::zero().encode());
  483. assert!(runtime.debug_buffer().contains("division by zero"));
  484. assert_eq!(runtime.output(), PanicData::from(DivisionByZero).encode());
  485. }
  486. #[test]
  487. fn internal_dyn_call_on_nil() {
  488. let mut runtime = build_solidity(
  489. r#"contract RuntimeErrors {
  490. function() internal x;
  491. function f() public returns (uint r) {
  492. x();
  493. return 2;
  494. }
  495. }"#,
  496. );
  497. runtime.function_expect_failure("f", vec![]);
  498. assert!(runtime
  499. .debug_buffer()
  500. .contains("internal function uninitialized"));
  501. assert_eq!(
  502. runtime.output(),
  503. PanicData::from(InternalFunctionUninitialized).encode()
  504. );
  505. }