2022-07-18 14:24:13 -07:00
|
|
|
use schemars::JsonSchema;
|
2022-09-30 00:01:09 -07:00
|
|
|
use serde::{Deserialize, Serialize};
|
2022-06-16 09:48:01 -07:00
|
|
|
|
2022-09-30 00:01:09 -07:00
|
|
|
use cosmwasm_std::{Binary, CanonicalAddr, Coin, StdResult, Storage, Uint128};
|
2022-06-16 09:48:01 -07:00
|
|
|
use cosmwasm_storage::{
|
2022-09-30 00:01:09 -07:00
|
|
|
bucket, bucket_read, singleton, singleton_read, Bucket, ReadonlyBucket, ReadonlySingleton,
|
2022-06-16 09:48:01 -07:00
|
|
|
Singleton,
|
|
|
|
};
|
|
|
|
|
2022-09-30 00:01:09 -07:00
|
|
|
use crate::{byte_utils::ByteUtils, error::ContractError};
|
2022-06-16 09:48:01 -07:00
|
|
|
|
2022-09-30 00:01:09 -07:00
|
|
|
use sha3::{Digest, Keccak256};
|
2022-06-16 09:48:01 -07:00
|
|
|
|
|
|
|
type HumanAddr = String;
|
|
|
|
|
|
|
|
pub static CONFIG_KEY: &[u8] = b"config";
|
|
|
|
pub static GUARDIAN_SET_KEY: &[u8] = b"guardian_set";
|
|
|
|
pub static SEQUENCE_KEY: &[u8] = b"sequence";
|
|
|
|
pub static WRAPPED_ASSET_KEY: &[u8] = b"wrapped_asset";
|
|
|
|
pub static WRAPPED_ASSET_ADDRESS_KEY: &[u8] = b"wrapped_asset_address";
|
|
|
|
|
2022-07-18 14:24:13 -07:00
|
|
|
/// Legacy version of [`ConfigInfo`]. Required for the migration. In
|
|
|
|
/// particular, the last two fields of [`ConfigInfo`] have been added after the
|
|
|
|
/// Terra2 contract's deployment, which means that Terra2 needs to be migrated.
|
|
|
|
/// See [`crate::contract::migrate`] for details on why this is necessary.
|
|
|
|
/// Once the migration has been executed, this struct (and the corresponding
|
|
|
|
/// migration logic) can be deleted.
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
|
|
|
pub struct ConfigInfoLegacy {
|
|
|
|
/// Current active guardian set
|
|
|
|
pub guardian_set_index: u32,
|
|
|
|
|
|
|
|
/// Period for which a guardian set stays active after it has been replaced.
|
|
|
|
/// The typo is an easter egg.
|
|
|
|
pub guardian_set_expirity: u64,
|
|
|
|
|
|
|
|
/// Governance chain (typically Solana, i.e. chain id 1)
|
|
|
|
pub gov_chain: u16,
|
|
|
|
|
|
|
|
/// Address of governance contract (typically 0x0000000000000000000000000000000000000000000000000000000000000004)
|
|
|
|
pub gov_address: Vec<u8>,
|
|
|
|
|
|
|
|
// Message sending fee
|
|
|
|
pub fee: Coin,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Information about this contract's general parameters.
|
2022-06-16 09:48:01 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
|
|
|
pub struct ConfigInfo {
|
2022-07-18 14:24:13 -07:00
|
|
|
/// Current active guardian set
|
2022-06-16 09:48:01 -07:00
|
|
|
pub guardian_set_index: u32,
|
|
|
|
|
2022-07-18 14:24:13 -07:00
|
|
|
/// Period for which a guardian set stays active after it has been replaced.
|
|
|
|
/// The typo is an easter egg.
|
2022-06-16 09:48:01 -07:00
|
|
|
pub guardian_set_expirity: u64,
|
|
|
|
|
2022-07-18 14:24:13 -07:00
|
|
|
/// Governance chain (typically Solana, i.e. chain id 1)
|
2022-06-16 09:48:01 -07:00
|
|
|
pub gov_chain: u16,
|
2022-07-18 14:24:13 -07:00
|
|
|
|
|
|
|
/// Address of governance contract (typically 0x0000000000000000000000000000000000000000000000000000000000000004)
|
2022-06-16 09:48:01 -07:00
|
|
|
pub gov_address: Vec<u8>,
|
|
|
|
|
|
|
|
// Message sending fee
|
|
|
|
pub fee: Coin,
|
2022-07-18 14:24:13 -07:00
|
|
|
|
|
|
|
/// The wormhole id of the current chain.
|
|
|
|
pub chain_id: u16,
|
|
|
|
|
|
|
|
/// The denom in which transaction fees and wormhole fees are paid.
|
|
|
|
/// Typically the native denom of the current chain.
|
|
|
|
pub fee_denom: String,
|
2022-06-16 09:48:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Validator Action Approval(VAA) data
|
2022-09-30 02:18:54 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
2022-06-16 09:48:01 -07:00
|
|
|
pub struct ParsedVAA {
|
|
|
|
pub version: u8,
|
|
|
|
pub guardian_set_index: u32,
|
|
|
|
pub timestamp: u32,
|
|
|
|
pub nonce: u32,
|
|
|
|
pub len_signers: u8,
|
|
|
|
|
|
|
|
pub emitter_chain: u16,
|
|
|
|
pub emitter_address: Vec<u8>,
|
|
|
|
pub sequence: u64,
|
|
|
|
pub consistency_level: u8,
|
|
|
|
pub payload: Vec<u8>,
|
|
|
|
|
|
|
|
pub hash: Vec<u8>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ParsedVAA {
|
|
|
|
/* VAA format:
|
|
|
|
|
|
|
|
header (length 6):
|
|
|
|
0 uint8 version (0x01)
|
|
|
|
1 uint32 guardian set index
|
|
|
|
5 uint8 len signatures
|
|
|
|
|
|
|
|
per signature (length 66):
|
|
|
|
0 uint8 index of the signer (in guardian keys)
|
|
|
|
1 [65]uint8 signature
|
|
|
|
|
|
|
|
body:
|
|
|
|
0 uint32 timestamp (unix in seconds)
|
|
|
|
4 uint32 nonce
|
|
|
|
8 uint16 emitter_chain
|
|
|
|
10 [32]uint8 emitter_address
|
|
|
|
42 uint64 sequence
|
|
|
|
50 uint8 consistency_level
|
|
|
|
51 []uint8 payload
|
|
|
|
*/
|
|
|
|
|
|
|
|
pub const HEADER_LEN: usize = 6;
|
|
|
|
pub const SIGNATURE_LEN: usize = 66;
|
|
|
|
|
|
|
|
pub const GUARDIAN_SET_INDEX_POS: usize = 1;
|
|
|
|
pub const LEN_SIGNER_POS: usize = 5;
|
|
|
|
|
|
|
|
pub const VAA_NONCE_POS: usize = 4;
|
|
|
|
pub const VAA_EMITTER_CHAIN_POS: usize = 8;
|
|
|
|
pub const VAA_EMITTER_ADDRESS_POS: usize = 10;
|
|
|
|
pub const VAA_SEQUENCE_POS: usize = 42;
|
|
|
|
pub const VAA_CONSISTENCY_LEVEL_POS: usize = 50;
|
|
|
|
pub const VAA_PAYLOAD_POS: usize = 51;
|
|
|
|
|
|
|
|
// Signature data offsets in the signature block
|
|
|
|
pub const SIG_DATA_POS: usize = 1;
|
|
|
|
// Signature length minus recovery id at the end
|
|
|
|
pub const SIG_DATA_LEN: usize = 64;
|
|
|
|
// Recovery byte is last after the main signature
|
|
|
|
pub const SIG_RECOVERY_POS: usize = Self::SIG_DATA_POS + Self::SIG_DATA_LEN;
|
|
|
|
|
|
|
|
pub fn deserialize(data: &[u8]) -> StdResult<Self> {
|
|
|
|
let version = data.get_u8(0);
|
|
|
|
|
|
|
|
// Load 4 bytes starting from index 1
|
|
|
|
let guardian_set_index: u32 = data.get_u32(Self::GUARDIAN_SET_INDEX_POS);
|
|
|
|
let len_signers = data.get_u8(Self::LEN_SIGNER_POS) as usize;
|
|
|
|
let body_offset: usize = Self::HEADER_LEN + Self::SIGNATURE_LEN * len_signers as usize;
|
|
|
|
|
|
|
|
// Hash the body
|
|
|
|
if body_offset >= data.len() {
|
|
|
|
return ContractError::InvalidVAA.std_err();
|
|
|
|
}
|
|
|
|
let body = &data[body_offset..];
|
|
|
|
let mut hasher = Keccak256::new();
|
|
|
|
hasher.update(body);
|
|
|
|
let hash = hasher.finalize().to_vec();
|
|
|
|
|
|
|
|
// Rehash the hash
|
|
|
|
let mut hasher = Keccak256::new();
|
|
|
|
hasher.update(hash);
|
|
|
|
let hash = hasher.finalize().to_vec();
|
|
|
|
|
|
|
|
// Signatures valid, apply VAA
|
|
|
|
if body_offset + Self::VAA_PAYLOAD_POS > data.len() {
|
|
|
|
return ContractError::InvalidVAA.std_err();
|
|
|
|
}
|
|
|
|
|
|
|
|
let timestamp = data.get_u32(body_offset);
|
|
|
|
let nonce = data.get_u32(body_offset + Self::VAA_NONCE_POS);
|
|
|
|
let emitter_chain = data.get_u16(body_offset + Self::VAA_EMITTER_CHAIN_POS);
|
|
|
|
let emitter_address = data
|
|
|
|
.get_bytes32(body_offset + Self::VAA_EMITTER_ADDRESS_POS)
|
|
|
|
.to_vec();
|
|
|
|
let sequence = data.get_u64(body_offset + Self::VAA_SEQUENCE_POS);
|
|
|
|
let consistency_level = data.get_u8(body_offset + Self::VAA_CONSISTENCY_LEVEL_POS);
|
|
|
|
let payload = data[body_offset + Self::VAA_PAYLOAD_POS..].to_vec();
|
|
|
|
|
|
|
|
Ok(ParsedVAA {
|
|
|
|
version,
|
|
|
|
guardian_set_index,
|
|
|
|
timestamp,
|
|
|
|
nonce,
|
|
|
|
len_signers: len_signers as u8,
|
|
|
|
emitter_chain,
|
|
|
|
emitter_address,
|
|
|
|
sequence,
|
|
|
|
consistency_level,
|
|
|
|
payload,
|
|
|
|
hash,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Guardian address
|
2022-09-30 02:18:54 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
2022-06-16 09:48:01 -07:00
|
|
|
pub struct GuardianAddress {
|
|
|
|
pub bytes: Binary, // 20-byte addresses
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
use hex;
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
impl GuardianAddress {
|
|
|
|
pub fn from(string: &str) -> GuardianAddress {
|
|
|
|
GuardianAddress {
|
|
|
|
bytes: hex::decode(string).expect("Decoding failed").into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Guardian set information
|
2022-09-30 02:18:54 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
2022-06-16 09:48:01 -07:00
|
|
|
pub struct GuardianSetInfo {
|
|
|
|
pub addresses: Vec<GuardianAddress>,
|
|
|
|
// List of guardian addresses
|
|
|
|
pub expiration_time: u64, // Guardian set expiration time
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GuardianSetInfo {
|
|
|
|
pub fn quorum(&self) -> usize {
|
|
|
|
// allow quorum of 0 for testing purposes...
|
|
|
|
if self.addresses.is_empty() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
((self.addresses.len() * 10 / 3) * 2) / 10 + 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wormhole contract generic information
|
2022-09-30 02:18:54 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
2022-06-16 09:48:01 -07:00
|
|
|
pub struct WormholeInfo {
|
|
|
|
// Period for which a guardian set stays active after it has been replaced
|
|
|
|
pub guardian_set_expirity: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn config(storage: &mut dyn Storage) -> Singleton<ConfigInfo> {
|
|
|
|
singleton(storage, CONFIG_KEY)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn config_read(storage: &dyn Storage) -> ReadonlySingleton<ConfigInfo> {
|
|
|
|
singleton_read(storage, CONFIG_KEY)
|
|
|
|
}
|
|
|
|
|
2022-07-18 14:24:13 -07:00
|
|
|
pub fn config_read_legacy(storage: &dyn Storage) -> ReadonlySingleton<ConfigInfoLegacy> {
|
|
|
|
singleton_read(storage, CONFIG_KEY)
|
|
|
|
}
|
|
|
|
|
2022-06-16 09:48:01 -07:00
|
|
|
pub fn guardian_set_set(
|
|
|
|
storage: &mut dyn Storage,
|
|
|
|
index: u32,
|
|
|
|
data: &GuardianSetInfo,
|
|
|
|
) -> StdResult<()> {
|
|
|
|
bucket(storage, GUARDIAN_SET_KEY).save(&index.to_be_bytes(), data)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn guardian_set_get(storage: &dyn Storage, index: u32) -> StdResult<GuardianSetInfo> {
|
|
|
|
bucket_read(storage, GUARDIAN_SET_KEY).load(&index.to_be_bytes())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn sequence_set(storage: &mut dyn Storage, emitter: &[u8], sequence: u64) -> StdResult<()> {
|
|
|
|
bucket(storage, SEQUENCE_KEY).save(emitter, &sequence)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn sequence_read(storage: &dyn Storage, emitter: &[u8]) -> u64 {
|
|
|
|
bucket_read(storage, SEQUENCE_KEY)
|
|
|
|
.load(emitter)
|
2022-09-30 02:18:54 -07:00
|
|
|
.unwrap_or(0)
|
2022-06-16 09:48:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn vaa_archive_add(storage: &mut dyn Storage, hash: &[u8]) -> StdResult<()> {
|
|
|
|
bucket(storage, GUARDIAN_SET_KEY).save(hash, &true)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn vaa_archive_check(storage: &dyn Storage, hash: &[u8]) -> bool {
|
|
|
|
bucket_read(storage, GUARDIAN_SET_KEY)
|
|
|
|
.load(hash)
|
2022-09-30 02:18:54 -07:00
|
|
|
.unwrap_or(false)
|
2022-06-16 09:48:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn wrapped_asset(storage: &mut dyn Storage) -> Bucket<HumanAddr> {
|
|
|
|
bucket(storage, WRAPPED_ASSET_KEY)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn wrapped_asset_read(storage: &dyn Storage) -> ReadonlyBucket<HumanAddr> {
|
|
|
|
bucket_read(storage, WRAPPED_ASSET_KEY)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn wrapped_asset_address(storage: &mut dyn Storage) -> Bucket<Vec<u8>> {
|
|
|
|
bucket(storage, WRAPPED_ASSET_ADDRESS_KEY)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn wrapped_asset_address_read(storage: &dyn Storage) -> ReadonlyBucket<Vec<u8>> {
|
|
|
|
bucket_read(storage, WRAPPED_ASSET_ADDRESS_KEY)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct GovernancePacket {
|
|
|
|
pub module: Vec<u8>,
|
|
|
|
pub action: u8,
|
|
|
|
pub chain: u16,
|
|
|
|
pub payload: Vec<u8>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GovernancePacket {
|
|
|
|
pub fn deserialize(data: &[u8]) -> StdResult<Self> {
|
|
|
|
let module = data.get_bytes32(0).to_vec();
|
|
|
|
let action = data.get_u8(32);
|
|
|
|
let chain = data.get_u16(33);
|
|
|
|
let payload = data[35..].to_vec();
|
|
|
|
|
|
|
|
Ok(GovernancePacket {
|
|
|
|
module,
|
|
|
|
action,
|
|
|
|
chain,
|
|
|
|
payload,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// action 1
|
2022-09-30 00:01:09 -07:00
|
|
|
pub struct ContractUpgrade {
|
2022-06-16 09:48:01 -07:00
|
|
|
pub new_contract: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
// action 2
|
|
|
|
pub struct GuardianSetUpgrade {
|
|
|
|
pub new_guardian_set_index: u32,
|
|
|
|
pub new_guardian_set: GuardianSetInfo,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ContractUpgrade {
|
|
|
|
pub fn deserialize(data: &[u8]) -> StdResult<Self> {
|
|
|
|
let new_contract = data.get_u64(24);
|
2022-09-30 00:01:09 -07:00
|
|
|
Ok(ContractUpgrade { new_contract })
|
2022-06-16 09:48:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GuardianSetUpgrade {
|
|
|
|
pub fn deserialize(data: &[u8]) -> StdResult<Self> {
|
|
|
|
const ADDRESS_LEN: usize = 20;
|
|
|
|
|
|
|
|
let new_guardian_set_index = data.get_u32(0);
|
|
|
|
|
|
|
|
let n_guardians = data.get_u8(4);
|
|
|
|
|
|
|
|
let mut addresses = vec![];
|
|
|
|
|
|
|
|
for i in 0..n_guardians {
|
|
|
|
let pos = 5 + (i as usize) * ADDRESS_LEN;
|
|
|
|
if pos + ADDRESS_LEN > data.len() {
|
|
|
|
return ContractError::InvalidVAA.std_err();
|
|
|
|
}
|
|
|
|
|
|
|
|
addresses.push(GuardianAddress {
|
|
|
|
bytes: data[pos..pos + ADDRESS_LEN].to_vec().into(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let new_guardian_set = GuardianSetInfo {
|
|
|
|
addresses,
|
|
|
|
expiration_time: 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(GuardianSetUpgrade {
|
|
|
|
new_guardian_set_index,
|
|
|
|
new_guardian_set,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// action 3
|
|
|
|
pub struct SetFee {
|
|
|
|
pub fee: Coin,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SetFee {
|
2022-07-18 14:24:13 -07:00
|
|
|
pub fn deserialize(data: &[u8], fee_denom: String) -> StdResult<Self> {
|
2022-06-16 09:48:01 -07:00
|
|
|
let (_, amount) = data.get_u256(0);
|
|
|
|
let fee = Coin {
|
2022-07-18 14:24:13 -07:00
|
|
|
denom: fee_denom,
|
2022-06-16 09:48:01 -07:00
|
|
|
amount: Uint128::new(amount),
|
|
|
|
};
|
|
|
|
Ok(SetFee { fee })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// action 4
|
|
|
|
pub struct TransferFee {
|
|
|
|
pub amount: Coin,
|
|
|
|
pub recipient: CanonicalAddr,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TransferFee {
|
2022-07-18 14:24:13 -07:00
|
|
|
pub fn deserialize(data: &[u8], fee_denom: String) -> StdResult<Self> {
|
2022-06-16 09:48:01 -07:00
|
|
|
let recipient = data.get_address(0);
|
|
|
|
|
|
|
|
let (_, amount) = data.get_u256(32);
|
|
|
|
let amount = Coin {
|
2022-07-18 14:24:13 -07:00
|
|
|
denom: fee_denom,
|
2022-06-16 09:48:01 -07:00
|
|
|
amount: Uint128::new(amount),
|
|
|
|
};
|
|
|
|
Ok(TransferFee { amount, recipient })
|
|
|
|
}
|
|
|
|
}
|