mod helpers; use accountant::state::transfer; use cosmwasm_std::{to_binary, Uint256}; use global_accountant::msg::Observation; use helpers::*; use wormhole_sdk::{token::Message, Address, Amount, Chain}; fn create_observation() -> Observation { let msg: Message = Message::Transfer { amount: Amount(Uint256::from(500u128).to_be_bytes()), token_address: Address([0x02; 32]), token_chain: Chain::Ethereum, recipient: Address([0x1c; 32]), recipient_chain: Chain::Solana, fee: Amount([0u8; 32]), }; Observation { tx_hash: vec![ 0x60, 0x5a, 0x56, 0x46, 0x3a, 0x0d, 0x71, 0x8b, 0x92, 0xf7, 0xe0, 0x00, 0x31, 0x1b, 0x63, 0xde, 0xb1, 0x50, 0xd6, 0x36, 0x66, 0x47, 0xef, 0x0b, 0x38, 0xd7, 0x7d, 0x60, 0xf5, 0xc6, 0xc4, 0x32, ] .into(), timestamp: 0xcf863e0c, nonce: 0x7058f400, emitter_chain: 2, emitter_address: [2; 32], sequence: 0xcc0b5753769752a3, consistency_level: 200, payload: serde_wormhole::to_vec(&msg).map(From::from).unwrap(), } } #[test] fn missing_observations() { let (wh, mut contract) = proper_instantiate(); register_emitters(&wh, &mut contract, 3); let index = wh.guardian_set_index(); let quorum = wh .calculate_quorum(index, contract.app().block_info().height) .unwrap() as usize; let o = create_observation(); let digest = o.digest().unwrap(); let data = to_binary(&[o.clone()]).unwrap(); let signatures = sign_observations(&wh, &data); // Don't submit enough signatures for the transfer to reach quorum. for s in &signatures[..quorum - 1] { contract .submit_observations(data.clone(), index, *s) .unwrap(); } // The transfer should still be pending. let key = transfer::Key::new(o.emitter_chain, o.emitter_address.into(), o.sequence); let pending = contract.query_pending_transfer(key).unwrap(); assert_eq!(&digest, pending[0].digest()); for (i, s) in signatures.iter().enumerate() { let resp = contract.query_missing_observations(index, s.index).unwrap(); if i < quorum - 1 { assert!(resp.missing.is_empty()); } else { assert_eq!(resp.missing.len(), 1); let missing = resp.missing.first().unwrap(); assert_eq!(missing.chain_id, o.emitter_chain); assert_eq!(missing.tx_hash, o.tx_hash); } } // Now submit one more signature so the transfer is committed. contract .submit_observations(data, index, signatures[quorum]) .unwrap(); // There should be no more missing observations. for s in signatures { let resp = contract.query_missing_observations(index, s.index).unwrap(); assert!(resp.missing.is_empty()); } } #[test] fn different_observations() { let (wh, mut contract) = proper_instantiate(); register_emitters(&wh, &mut contract, 3); let index = wh.guardian_set_index(); let quorum = wh .calculate_quorum(index, contract.app().block_info().height) .unwrap() as usize; let first = create_observation(); let first_data = to_binary(&[first.clone()]).unwrap(); let first_signatures = sign_observations(&wh, &first_data); // Don't submit enough signatures for the transfer to reach quorum. for s in &first_signatures[..quorum - 1] { contract .submit_observations(first_data.clone(), index, *s) .unwrap(); } // Create a new observation with a different tx hash and payload. let msg: Message = Message::Transfer { amount: Amount(Uint256::from(900u128).to_be_bytes()), token_address: Address([0x02; 32]), token_chain: Chain::Ethereum, recipient: Address([0x1c; 32]), recipient_chain: Chain::Algorand, fee: Amount([0u8; 32]), }; let mut second = create_observation(); second.tx_hash = vec![ 0x8e, 0x29, 0xe6, 0xbc, 0xc0, 0x48, 0x88, 0x75, 0x57, 0xeb, 0x50, 0x9e, 0xb8, 0x5a, 0x4d, 0x96, 0x53, 0xc0, 0xd7, 0x0b, 0x2c, 0xcb, 0xf1, 0x7b, 0x4d, 0x7c, 0x6e, 0x9a, 0xa6, 0x5e, 0x2f, 0x1f, ] .into(); second.payload = serde_wormhole::to_vec(&msg).map(From::from).unwrap(); let second_data = to_binary(&[second.clone()]).unwrap(); let second_signatures = sign_observations(&wh, &second_data); // Submit a different set of signatures for the second observation. for s in second_signatures.iter().rev().take(quorum - 1) { contract .submit_observations(second_data.clone(), index, *s) .unwrap(); } let num_signatures = second_signatures.len(); for (i, s) in second_signatures.into_iter().enumerate() { let resp = contract.query_missing_observations(index, s.index).unwrap(); if i < num_signatures - quorum + 1 { // We should be missing the second observation. assert_eq!(resp.missing.len(), 1); let missing = resp.missing.first().unwrap(); assert_eq!(second.emitter_chain, missing.chain_id); assert_eq!(second.tx_hash, missing.tx_hash); } else if i >= quorum - 1 { // We should be missing the first observation. assert_eq!(resp.missing.len(), 1); let missing = resp.missing.first().unwrap(); assert_eq!(first.emitter_chain, missing.chain_id); assert_eq!(first.tx_hash, missing.tx_hash); } else { // We shouldn't be missing any observations. assert!(resp.missing.is_empty()); } } } #[test] fn guardian_set_change() { let (wh, mut contract) = proper_instantiate(); register_emitters(&wh, &mut contract, 3); let first_set = wh.guardian_set_index(); let quorum = wh .calculate_quorum(first_set, contract.app().block_info().height) .unwrap() as usize; let o = create_observation(); let data = to_binary(&[o.clone()]).unwrap(); let signatures = sign_observations(&wh, &data); // Don't submit enough signatures for the transfer to reach quorum. for s in &signatures[..quorum - 1] { contract .submit_observations(data.clone(), first_set, *s) .unwrap(); } // Update the guardian set index and submit a different set of signatures. let second_set = first_set + 1; wh.set_index(second_set); for s in signatures.iter().rev().take(quorum - 1) { contract .submit_observations(data.clone(), second_set, *s) .unwrap(); } let num_signatures = signatures.len(); for (i, s) in signatures.into_iter().enumerate() { let first_missing = contract .query_missing_observations(first_set, s.index) .unwrap() .missing; let second_missing = contract .query_missing_observations(second_set, s.index) .unwrap() .missing; if i < num_signatures - quorum + 1 { // We should be missing signatures for the new guardian set. assert!(first_missing.is_empty()); assert_eq!(second_missing.len(), 1); let missing = second_missing.first().unwrap(); assert_eq!(o.emitter_chain, missing.chain_id); assert_eq!(o.tx_hash, missing.tx_hash); } else if i >= quorum - 1 { // We should be missing signatures for the old guardian set. assert!(second_missing.is_empty()); assert_eq!(first_missing.len(), 1); let missing = first_missing.first().unwrap(); assert_eq!(o.emitter_chain, missing.chain_id); assert_eq!(o.tx_hash, missing.tx_hash); } else { // We shouldn't be missing signatures for either set. assert!(first_missing.is_empty()); assert!(second_missing.is_empty()); } } }