mod.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. #![allow(dead_code)]
  2. use accountant::state::{account, transfer, Kind, Modification};
  3. use cosmwasm_schema::cw_serde;
  4. use cosmwasm_std::{
  5. testing::{MockApi, MockStorage},
  6. Addr, Binary, Coin, Empty, StdError, StdResult, Uint128,
  7. };
  8. use cw_multi_test::{
  9. App, AppBuilder, AppResponse, BankKeeper, ContractWrapper, Executor, WasmKeeper,
  10. };
  11. use global_accountant::{
  12. msg::{
  13. AllAccountsResponse, AllModificationsResponse, AllPendingTransfersResponse,
  14. AllTransfersResponse, BatchTransferStatusResponse, ChainRegistrationResponse, ExecuteMsg,
  15. MissingObservationsResponse, QueryMsg, TransferStatus, SUBMITTED_OBSERVATIONS_PREFIX,
  16. },
  17. state,
  18. };
  19. use serde::Serialize;
  20. use wormhole::{
  21. token::{Action, GovernancePacket, ModificationKind},
  22. vaa::{Body, Header, Signature},
  23. Address, Amount, Chain, Vaa,
  24. };
  25. use wormhole_bindings::{fake, WormholeQuery};
  26. #[cw_serde]
  27. pub struct TransferResponse {
  28. pub data: transfer::Data,
  29. pub digest: Binary,
  30. }
  31. pub struct Contract {
  32. addr: Addr,
  33. app: FakeApp,
  34. pub sequence: u64,
  35. }
  36. impl Contract {
  37. pub fn addr(&self) -> Addr {
  38. self.addr.clone()
  39. }
  40. pub fn app(&self) -> &FakeApp {
  41. &self.app
  42. }
  43. pub fn app_mut(&mut self) -> &mut FakeApp {
  44. &mut self.app
  45. }
  46. pub fn submit_observations(
  47. &mut self,
  48. observations: Binary,
  49. guardian_set_index: u32,
  50. signature: Signature,
  51. ) -> anyhow::Result<AppResponse> {
  52. self.app.execute_contract(
  53. Addr::unchecked(USER),
  54. self.addr(),
  55. &ExecuteMsg::SubmitObservations {
  56. observations,
  57. guardian_set_index,
  58. signature,
  59. },
  60. &[],
  61. )
  62. }
  63. pub fn modify_balance_with(
  64. &mut self,
  65. modification: Modification,
  66. wh: &fake::WormholeKeeper,
  67. tamperer: impl Fn(Vaa<GovernancePacket>) -> Binary,
  68. ) -> anyhow::Result<AppResponse> {
  69. let Modification {
  70. sequence,
  71. chain_id,
  72. token_chain,
  73. token_address,
  74. kind,
  75. amount,
  76. reason,
  77. } = modification;
  78. let token_address = Address(*token_address);
  79. let kind = match kind {
  80. Kind::Add => ModificationKind::Add,
  81. Kind::Sub => ModificationKind::Subtract,
  82. };
  83. let amount = Amount(amount.to_be_bytes());
  84. let body = Body {
  85. timestamp: self.sequence as u32,
  86. nonce: self.sequence as u32,
  87. emitter_chain: Chain::Solana,
  88. emitter_address: wormhole::GOVERNANCE_EMITTER,
  89. sequence: self.sequence,
  90. consistency_level: 0,
  91. payload: GovernancePacket {
  92. chain: Chain::Any,
  93. action: Action::ModifyBalance {
  94. sequence,
  95. chain_id,
  96. token_chain,
  97. token_address,
  98. kind,
  99. amount,
  100. reason: reason.into(),
  101. },
  102. },
  103. };
  104. self.sequence += 1;
  105. let (vaa, _) = sign_vaa_body(wh, body);
  106. let data = tamperer(vaa);
  107. let vaas: Vec<Binary> = vec![data];
  108. self.app.execute_contract(
  109. Addr::unchecked(USER),
  110. self.addr(),
  111. &ExecuteMsg::SubmitVaas { vaas },
  112. &[],
  113. )
  114. }
  115. pub fn modify_balance(
  116. &mut self,
  117. modification: Modification,
  118. wh: &fake::WormholeKeeper,
  119. ) -> anyhow::Result<AppResponse> {
  120. self.modify_balance_with(modification, wh, |vaa| {
  121. serde_wormhole::to_vec(&vaa).map(From::from).unwrap()
  122. })
  123. }
  124. pub fn submit_vaas(&mut self, vaas: Vec<Binary>) -> anyhow::Result<AppResponse> {
  125. self.app.execute_contract(
  126. Addr::unchecked(ADMIN),
  127. self.addr(),
  128. &ExecuteMsg::SubmitVaas { vaas },
  129. &[],
  130. )
  131. }
  132. pub fn query_balance(&self, key: account::Key) -> StdResult<account::Balance> {
  133. self.app
  134. .wrap()
  135. .query_wasm_smart(self.addr(), &QueryMsg::Balance(key))
  136. }
  137. pub fn query_all_accounts(
  138. &self,
  139. start_after: Option<account::Key>,
  140. limit: Option<u32>,
  141. ) -> StdResult<AllAccountsResponse> {
  142. self.app
  143. .wrap()
  144. .query_wasm_smart(self.addr(), &QueryMsg::AllAccounts { start_after, limit })
  145. }
  146. pub fn query_transfer_status(&self, key: transfer::Key) -> StdResult<TransferStatus> {
  147. self.app
  148. .wrap()
  149. .query_wasm_smart(self.addr(), &QueryMsg::TransferStatus(key))
  150. }
  151. pub fn query_batch_transfer_status(
  152. &self,
  153. keys: Vec<transfer::Key>,
  154. ) -> StdResult<BatchTransferStatusResponse> {
  155. self.app
  156. .wrap()
  157. .query_wasm_smart(self.addr(), &QueryMsg::BatchTransferStatus(keys))
  158. }
  159. pub fn query_transfer(&self, key: transfer::Key) -> StdResult<TransferResponse> {
  160. self.query_transfer_status(key.clone()).and_then(|status| {
  161. if let TransferStatus::Committed { data, digest } = status {
  162. Ok(TransferResponse { data, digest })
  163. } else {
  164. Err(StdError::not_found(format!("transfer for key {key}")))
  165. }
  166. })
  167. }
  168. pub fn query_all_transfers(
  169. &self,
  170. start_after: Option<transfer::Key>,
  171. limit: Option<u32>,
  172. ) -> StdResult<AllTransfersResponse> {
  173. self.app
  174. .wrap()
  175. .query_wasm_smart(self.addr(), &QueryMsg::AllTransfers { start_after, limit })
  176. }
  177. pub fn query_pending_transfer(&self, key: transfer::Key) -> StdResult<Vec<state::Data>> {
  178. self.query_transfer_status(key.clone()).and_then(|status| {
  179. if let TransferStatus::Pending(state) = status {
  180. Ok(state)
  181. } else {
  182. Err(StdError::not_found(format!(
  183. "pending transfer for key {key}"
  184. )))
  185. }
  186. })
  187. }
  188. pub fn query_all_pending_transfers(
  189. &self,
  190. start_after: Option<transfer::Key>,
  191. limit: Option<u32>,
  192. ) -> StdResult<AllPendingTransfersResponse> {
  193. self.app.wrap().query_wasm_smart(
  194. self.addr(),
  195. &QueryMsg::AllPendingTransfers { start_after, limit },
  196. )
  197. }
  198. pub fn query_modification(&self, sequence: u64) -> StdResult<Modification> {
  199. self.app
  200. .wrap()
  201. .query_wasm_smart(self.addr(), &QueryMsg::Modification { sequence })
  202. }
  203. pub fn query_all_modifications(
  204. &self,
  205. start_after: Option<u64>,
  206. limit: Option<u32>,
  207. ) -> StdResult<AllModificationsResponse> {
  208. self.app.wrap().query_wasm_smart(
  209. self.addr(),
  210. &QueryMsg::AllModifications { start_after, limit },
  211. )
  212. }
  213. pub fn query_chain_registration(&self, chain: u16) -> StdResult<ChainRegistrationResponse> {
  214. self.app
  215. .wrap()
  216. .query_wasm_smart(self.addr(), &QueryMsg::ChainRegistration { chain })
  217. }
  218. pub fn query_missing_observations(
  219. &self,
  220. guardian_set: u32,
  221. index: u8,
  222. ) -> StdResult<MissingObservationsResponse> {
  223. self.app.wrap().query_wasm_smart(
  224. self.addr(),
  225. &QueryMsg::MissingObservations {
  226. guardian_set,
  227. index,
  228. },
  229. )
  230. }
  231. }
  232. const USER: &str = "USER";
  233. const ADMIN: &str = "ADMIN";
  234. const NATIVE_DENOM: &str = "denom";
  235. pub type FakeApp =
  236. App<BankKeeper, MockApi, MockStorage, fake::WormholeKeeper, WasmKeeper<Empty, WormholeQuery>>;
  237. fn fake_app(wh: fake::WormholeKeeper) -> FakeApp {
  238. AppBuilder::new_custom()
  239. .with_custom(wh)
  240. .build(|router, _, storage| {
  241. router
  242. .bank
  243. .init_balance(
  244. storage,
  245. &Addr::unchecked(USER),
  246. vec![Coin {
  247. denom: NATIVE_DENOM.to_string(),
  248. amount: Uint128::new(1),
  249. }],
  250. )
  251. .unwrap();
  252. })
  253. }
  254. pub fn proper_instantiate() -> (fake::WormholeKeeper, Contract) {
  255. let wh = fake::WormholeKeeper::new();
  256. let mut app = fake_app(wh.clone());
  257. let accountant_id = app.store_code(Box::new(ContractWrapper::new(
  258. global_accountant::contract::execute,
  259. global_accountant::contract::instantiate,
  260. global_accountant::contract::query,
  261. )));
  262. // We want the contract to be able to upgrade itself, which means we have to set the contract
  263. // as its own admin. So we have a bit of a catch-22 where we need to know the contract
  264. // address to register it but we need to register it to get its address. The hacky solution
  265. // here is to rely on the internal details of the test framework to figure out what the
  266. // address of the contract is going to be and then use that.
  267. //
  268. // TODO: Figure out a better way to do this. One option is to do something like:
  269. //
  270. // ```
  271. // let mut data = app.contract_data(&addr).unwrap();
  272. // data.admin = Some(addr.clone());
  273. // app.init_modules(|router, _, storage| router.wasm.save_contract(storage, &addr, &data))
  274. // .unwrap();
  275. // ```
  276. //
  277. // Unfortunately, the `wasm` field of `router` is private to the `cw-multi-test` crate so we
  278. // can't use it here. Maybe something to bring up with upstream.
  279. let addr = app
  280. .instantiate_contract(
  281. accountant_id,
  282. Addr::unchecked(ADMIN),
  283. &Empty {},
  284. &[],
  285. "accountant",
  286. Some("contract0".into()),
  287. )
  288. .unwrap();
  289. let sequence = 0;
  290. (
  291. wh,
  292. Contract {
  293. addr,
  294. app,
  295. sequence,
  296. },
  297. )
  298. }
  299. pub fn sign_vaa_body<P: Serialize>(wh: &fake::WormholeKeeper, body: Body<P>) -> (Vaa<P>, Binary) {
  300. let data = serde_wormhole::to_vec(&body).unwrap();
  301. let signatures = wh.sign(&data);
  302. let header = Header {
  303. version: 1,
  304. guardian_set_index: wh.guardian_set_index(),
  305. signatures,
  306. };
  307. let v = (header, body).into();
  308. let data = serde_wormhole::to_vec(&v).map(From::from).unwrap();
  309. (v, data)
  310. }
  311. pub fn sign_observations(wh: &fake::WormholeKeeper, observations: &[u8]) -> Vec<Signature> {
  312. let mut prepended =
  313. Vec::with_capacity(SUBMITTED_OBSERVATIONS_PREFIX.len() + observations.len());
  314. prepended.extend_from_slice(SUBMITTED_OBSERVATIONS_PREFIX);
  315. prepended.extend_from_slice(observations);
  316. let mut signatures = wh.sign(&prepended);
  317. signatures.sort_by_key(|s| s.index);
  318. signatures
  319. }
  320. pub fn register_emitters(wh: &fake::WormholeKeeper, contract: &mut Contract, count: usize) {
  321. for i in 0..count {
  322. let body = Body {
  323. timestamp: i as u32,
  324. nonce: i as u32,
  325. emitter_chain: Chain::Solana,
  326. emitter_address: wormhole::GOVERNANCE_EMITTER,
  327. sequence: i as u64,
  328. consistency_level: 0,
  329. payload: GovernancePacket {
  330. chain: Chain::Any,
  331. action: Action::RegisterChain {
  332. chain: (i as u16).into(),
  333. emitter_address: Address([i as u8; 32]),
  334. },
  335. },
  336. };
  337. let (_, data) = sign_vaa_body(wh, body);
  338. contract.submit_vaas(vec![data]).unwrap();
  339. }
  340. }