wormhole/cosmwasm/contracts/wormchain-accounting/tests/missing_observations.rs

228 lines
7.8 KiB
Rust

mod helpers;
use accounting::state::transfer;
use cosmwasm_std::{to_binary, Uint256};
use helpers::*;
use wormchain_accounting::msg::Observation;
use wormhole::{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 = wh.sign(&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 mut first_signatures = wh.sign(&first_data);
first_signatures.sort_by_key(|s| s.index);
// 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 mut second_signatures = wh.sign(&second_data);
second_signatures.sort_by_key(|s| s.index);
// 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 = wh.sign(&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());
}
}
}