events.rs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. // SPDX-License-Identifier: Apache-2.0
  2. use crate::build_solidity;
  3. use ink::env::{
  4. hash::{Blake2x256, CryptoHash},
  5. topics::PrefixedValue,
  6. };
  7. use ink::primitives::AccountId;
  8. use parity_scale_codec::Encode;
  9. use solang::{file_resolver::FileResolver, Target};
  10. use std::ffi::OsStr;
  11. fn topic_hash(encoded: &[u8]) -> Vec<u8> {
  12. let mut buf = [0; 32];
  13. if encoded.len() <= 32 {
  14. buf[..encoded.len()].copy_from_slice(encoded);
  15. } else {
  16. <Blake2x256 as CryptoHash>::hash(encoded, &mut buf);
  17. };
  18. buf.into()
  19. }
  20. #[test]
  21. fn anonymous() {
  22. let mut runtime = build_solidity(
  23. r##"
  24. contract a {
  25. event foo(bool b) anonymous;
  26. function emit_event() public {
  27. emit foo(true);
  28. }
  29. }"##,
  30. );
  31. runtime.constructor(0, Vec::new());
  32. runtime.function("emit_event", Vec::new());
  33. assert_eq!(runtime.events.len(), 1);
  34. let event = &runtime.events[0];
  35. assert_eq!(event.topics.len(), 0);
  36. assert_eq!(event.data, (0u8, true).encode());
  37. }
  38. #[test]
  39. fn emit() {
  40. #[derive(Encode)]
  41. enum Event {
  42. Foo(bool, u32, i64),
  43. Bar(u32, u64, String),
  44. }
  45. let mut runtime = build_solidity(
  46. r##"
  47. contract a {
  48. event foo(bool,uint32,int64 indexed i);
  49. event bar(uint32,uint64,string indexed s);
  50. function emit_event() public {
  51. emit foo(true, 102, 1);
  52. emit bar(0xdeadcafe, 102, "foobar");
  53. }
  54. }"##,
  55. );
  56. runtime.constructor(0, Vec::new());
  57. runtime.function("emit_event", Vec::new());
  58. assert_eq!(runtime.events.len(), 2);
  59. let event = &runtime.events[0];
  60. assert_eq!(event.topics.len(), 2);
  61. assert_eq!(event.topics[0], topic_hash(b"\0a::foo")[..]);
  62. let topic = PrefixedValue {
  63. prefix: b"a::foo::i",
  64. value: &1i64,
  65. }
  66. .encode();
  67. assert_eq!(event.topics[1], topic_hash(&topic[..])[..]);
  68. assert_eq!(event.data, Event::Foo(true, 102, 1).encode());
  69. let event = &runtime.events[1];
  70. assert_eq!(event.topics.len(), 2);
  71. println!(
  72. "topic hash: {}",
  73. std::str::from_utf8(&event.topics[0]).unwrap()
  74. );
  75. println!(
  76. "topic hash: {}",
  77. std::str::from_utf8(&event.topics[0]).unwrap()
  78. );
  79. assert_eq!(event.topics[0], topic_hash(b"\0a::bar")[..]);
  80. let topic = PrefixedValue {
  81. prefix: b"a::bar::s",
  82. value: &String::from("foobar"),
  83. }
  84. .encode();
  85. assert_eq!(event.topics[1].to_vec(), topic_hash(&topic[..]));
  86. assert_eq!(
  87. event.data,
  88. Event::Bar(0xdeadcafe, 102, "foobar".into()).encode()
  89. );
  90. }
  91. #[test]
  92. fn event_imported() {
  93. let mut cache = FileResolver::new();
  94. cache.set_file_contents(
  95. "a.sol",
  96. r#"
  97. import "b.sol";
  98. contract foo {
  99. function emit_event() public {
  100. emit bar(102, true);
  101. }
  102. }
  103. "#
  104. .to_string(),
  105. );
  106. cache.set_file_contents(
  107. "b.sol",
  108. r#"
  109. event bar (uint32 indexed f1, bool x);
  110. "#
  111. .to_string(),
  112. );
  113. let ns =
  114. solang::parse_and_resolve(OsStr::new("a.sol"), &mut cache, Target::default_substrate());
  115. assert!(!ns.diagnostics.any_errors());
  116. let mut cache = FileResolver::new();
  117. cache.set_file_contents(
  118. "a.sol",
  119. r#"
  120. import "b.sol";
  121. contract foo {
  122. function emit_event() public {
  123. emit baz.bar(102, true);
  124. }
  125. }
  126. "#
  127. .to_string(),
  128. );
  129. cache.set_file_contents(
  130. "b.sol",
  131. r#"
  132. abstract contract baz {
  133. event bar (uint32 indexed f1, bool x);
  134. }
  135. "#
  136. .to_string(),
  137. );
  138. let ns =
  139. solang::parse_and_resolve(OsStr::new("a.sol"), &mut cache, Target::default_substrate());
  140. assert!(!ns.diagnostics.any_errors());
  141. let mut cache = FileResolver::new();
  142. cache.set_file_contents(
  143. "a.sol",
  144. r#"
  145. import "b.sol" as X;
  146. contract foo {
  147. function emit_event() public {
  148. emit X.baz.bar(102, true);
  149. }
  150. }
  151. "#
  152. .to_string(),
  153. );
  154. cache.set_file_contents(
  155. "b.sol",
  156. r#"
  157. abstract contract baz {
  158. event bar (uint32 indexed f1, bool x);
  159. }
  160. "#
  161. .to_string(),
  162. );
  163. let ns =
  164. solang::parse_and_resolve(OsStr::new("a.sol"), &mut cache, Target::default_substrate());
  165. assert!(!ns.diagnostics.any_errors());
  166. let mut cache = FileResolver::new();
  167. cache.set_file_contents(
  168. "a.sol",
  169. r#"
  170. import "b.sol" as X;
  171. contract foo {
  172. function emit_event() public {
  173. emit X.bar(102, true);
  174. }
  175. }
  176. "#
  177. .to_string(),
  178. );
  179. cache.set_file_contents(
  180. "b.sol",
  181. r#"
  182. event bar (uint32 indexed f1, bool x);
  183. "#
  184. .to_string(),
  185. );
  186. let ns =
  187. solang::parse_and_resolve(OsStr::new("a.sol"), &mut cache, Target::default_substrate());
  188. assert!(!ns.diagnostics.any_errors());
  189. }
  190. /// FIXME: Use the exact same event structure once the `Option<T>` type is available
  191. #[test]
  192. fn erc20_ink_example() {
  193. #[derive(Encode)]
  194. enum Event {
  195. Transfer(AccountId, AccountId, u128),
  196. }
  197. #[derive(Encode)]
  198. struct Transfer {
  199. from: AccountId,
  200. to: AccountId,
  201. value: u128,
  202. }
  203. let mut runtime = build_solidity(
  204. r##"
  205. contract Erc20 {
  206. event Transfer(
  207. address indexed from,
  208. address indexed to,
  209. uint128 value
  210. );
  211. function emit_event(address from, address to, uint128 value) public {
  212. emit Transfer(from, to, value);
  213. }
  214. }"##,
  215. );
  216. runtime.constructor(0, Vec::new());
  217. let from = AccountId::from([1; 32]);
  218. let to = AccountId::from([2; 32]);
  219. let value = 10;
  220. runtime.function("emit_event", Transfer { from, to, value }.encode());
  221. assert_eq!(runtime.events.len(), 1);
  222. let event = &runtime.events[0];
  223. assert_eq!(event.data, Event::Transfer(from, to, value).encode());
  224. assert_eq!(event.topics.len(), 3);
  225. assert_eq!(event.topics[0], topic_hash(b"\0Erc20::Transfer")[..]);
  226. let expected_topic = PrefixedValue {
  227. prefix: b"Erc20::Transfer::from",
  228. value: &from,
  229. };
  230. assert_eq!(event.topics[1], topic_hash(&expected_topic.encode())[..]);
  231. let expected_topic = PrefixedValue {
  232. prefix: b"Erc20::Transfer::to",
  233. value: &to,
  234. };
  235. assert_eq!(event.topics[2], topic_hash(&expected_topic.encode())[..]);
  236. }
  237. #[test]
  238. fn freestanding() {
  239. let mut runtime = build_solidity(
  240. r##"
  241. event A(bool indexed b);
  242. function foo() {
  243. emit A(true);
  244. }
  245. contract a {
  246. function emit_event() public {
  247. foo();
  248. }
  249. }"##,
  250. );
  251. runtime.constructor(0, Vec::new());
  252. runtime.function("emit_event", Vec::new());
  253. assert_eq!(runtime.events.len(), 1);
  254. let event = &runtime.events[0];
  255. assert_eq!(event.data, (0u8, true).encode());
  256. assert_eq!(event.topics[0], topic_hash(b"\0a::A")[..]);
  257. let expected_topic = PrefixedValue {
  258. prefix: b"a::A::b",
  259. value: &true,
  260. };
  261. assert_eq!(event.topics[1], topic_hash(&expected_topic.encode())[..]);
  262. }
  263. #[test]
  264. fn different_contract() {
  265. let mut runtime = build_solidity(
  266. r##"abstract contract A { event X(bool indexed foo); }
  267. contract B { function emit_event() public { emit A.X(true); } }"##,
  268. );
  269. runtime.constructor(0, Vec::new());
  270. runtime.function("emit_event", Vec::new());
  271. assert_eq!(runtime.events.len(), 1);
  272. let event = &runtime.events[0];
  273. assert_eq!(event.data, (0u8, true).encode());
  274. assert_eq!(event.topics[0], topic_hash(b"\0A::X")[..]);
  275. let expected_topic = PrefixedValue {
  276. prefix: b"A::X::foo",
  277. value: &true,
  278. };
  279. assert_eq!(event.topics[1], topic_hash(&expected_topic.encode())[..]);
  280. }