terra/contracts: columbus-5 migration for wormhole

Change-Id: I6772a49d3d667633b27d74eb7f349e01c849f505
This commit is contained in:
Reisen 2021-09-06 08:27:27 +00:00 committed by David Paryente
parent c92442e3ce
commit c04fe95859
6 changed files with 119 additions and 177 deletions

View File

@ -14,15 +14,15 @@ backtraces = ["cosmwasm-std/backtraces"]
library = []
[dependencies]
cosmwasm-std = { version = "0.10.0" }
cosmwasm-storage = { version = "0.10.0" }
schemars = "0.7"
cosmwasm-std = { version = "0.16.0" }
cosmwasm-storage = { version = "0.16.0" }
schemars = "0.8.1"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
cw20 = "0.2.2"
cw20-base = { version = "0.2.2", features = ["library"] }
cw20 = "0.8.0"
cw20-base = { version = "0.8.0", features = ["library"] }
cw20-wrapped = { path = "../cw20-wrapped", features = ["library"] }
thiserror = { version = "1.0.20" }
k256 = { version = "0.5.9", default-features = false, features = ["ecdsa"] }
k256 = { version = "0.9.4", default-features = false, features = ["ecdsa"] }
sha3 = { version = "0.9.1", default-features = false }
generic-array = { version = "0.14.4" }
hex = "0.4.2"
@ -30,4 +30,4 @@ lazy_static = "1.4.0"
[dev-dependencies]
cosmwasm-vm = { version = "0.10.0", default-features = false, features = ["default-cranelift"] }
serde_json = "1.0"
serde_json = "1.0"

View File

@ -1,18 +1,16 @@
use cosmwasm_std::{
entry_point,
has_coins,
log,
to_binary,
Api,
BankMsg,
Binary,
Coin,
CosmosMsg,
Deps,
DepsMut,
Env,
Extern,
HandleResponse,
HumanAddr,
InitResponse,
Querier,
MessageInfo,
Response,
StdError,
StdResult,
Storage,
@ -25,11 +23,11 @@ use crate::{
},
error::ContractError,
msg::{
ExecuteMsg,
GetAddressHexResponse,
GetStateResponse,
GuardianSetInfoResponse,
HandleMsg,
InitMsg,
InstantiateMsg,
QueryMsg,
},
state::{
@ -59,7 +57,7 @@ use k256::{
Signature as RecoverableSignature,
},
Signature,
VerifyKey,
VerifyingKey,
},
EncodedPoint,
};
@ -71,6 +69,8 @@ use sha3::{
use generic_array::GenericArray;
use std::convert::TryFrom;
type HumanAddr = String;
// Chain ID of Terra
const CHAIN_ID: u16 = 3;
@ -78,11 +78,8 @@ const CHAIN_ID: u16 = 3;
const FEE_AMOUNT: u128 = 10000;
pub const FEE_DENOMINATION: &str = "uluna";
pub fn init<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
_env: Env,
msg: InitMsg,
) -> StdResult<InitResponse> {
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(deps: DepsMut, _env: Env, msg: InstantiateMsg) -> StdResult<Response> {
// Save general wormhole info
let state = ConfigInfo {
gov_chain: msg.gov_chain,
@ -91,41 +88,39 @@ pub fn init<S: Storage, A: Api, Q: Querier>(
guardian_set_expirity: msg.guardian_set_expirity,
fee: Coin::new(FEE_AMOUNT, FEE_DENOMINATION), // 0.01 Luna (or 10000 uluna) fee by default
};
config(&mut deps.storage).save(&state)?;
config(deps.storage).save(&state)?;
// Add initial guardian set to storage
guardian_set_set(
&mut deps.storage,
deps.storage,
state.guardian_set_index,
&msg.initial_guardian_set,
)?;
Ok(InitResponse::default())
Ok(Response::default())
}
pub fn handle<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
env: Env,
msg: HandleMsg,
) -> StdResult<HandleResponse> {
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult<Response> {
match msg {
HandleMsg::PostMessage { message, nonce } => {
handle_post_message(deps, env, &message.as_slice(), nonce)
ExecuteMsg::PostMessage { message, nonce } => {
handle_post_message(deps, env, info, &message.as_slice(), nonce)
}
HandleMsg::SubmitVAA { vaa } => handle_submit_vaa(deps, env, vaa.as_slice()),
ExecuteMsg::SubmitVAA { vaa } => handle_submit_vaa(deps, env, info, vaa.as_slice()),
}
}
/// Process VAA message signed by quardians
fn handle_submit_vaa<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
fn handle_submit_vaa(
deps: DepsMut,
env: Env,
_info: MessageInfo,
data: &[u8],
) -> StdResult<HandleResponse> {
let state = config_read(&deps.storage).load()?;
) -> StdResult<Response> {
let state = config_read(deps.storage).load()?;
let vaa = parse_and_verify_vaa(&deps.storage, data, env.block.time)?;
vaa_archive_add(&mut deps.storage, vaa.hash.as_slice())?;
let vaa = parse_and_verify_vaa(deps.storage, data, env.block.time.seconds())?;
vaa_archive_add(deps.storage, vaa.hash.as_slice())?;
if state.gov_chain == vaa.emitter_chain && state.gov_address == vaa.emitter_address {
if state.guardian_set_index != vaa.guardian_set_index {
@ -139,11 +134,7 @@ fn handle_submit_vaa<S: Storage, A: Api, Q: Querier>(
ContractError::InvalidVAAAction.std_err()
}
fn handle_governance_payload<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
env: Env,
data: &Vec<u8>,
) -> StdResult<HandleResponse> {
fn handle_governance_payload(deps: DepsMut, env: Env, data: &Vec<u8>) -> StdResult<Response> {
let gov_packet = GovernancePacket::deserialize(&data)?;
let module = String::from_utf8(gov_packet.module).unwrap();
@ -170,8 +161,8 @@ fn handle_governance_payload<S: Storage, A: Api, Q: Querier>(
/// Parses raw VAA data into a struct and verifies whether it contains sufficient signatures of an
/// active guardian set i.e. is valid according to Wormhole consensus rules
fn parse_and_verify_vaa<S: Storage>(
storage: &S,
fn parse_and_verify_vaa(
storage: &dyn Storage,
data: &[u8],
block_time: u64,
) -> StdResult<ParsedVAA> {
@ -239,18 +230,14 @@ fn parse_and_verify_vaa<S: Storage>(
Ok(vaa)
}
fn vaa_update_guardian_set<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
env: Env,
data: &Vec<u8>,
) -> StdResult<HandleResponse> {
fn vaa_update_guardian_set(deps: DepsMut, env: Env, data: &Vec<u8>) -> StdResult<Response> {
/* Payload format
0 uint32 new_index
4 uint8 len(keys)
5 [][20]uint8 guardian addresses
*/
let mut state = config_read(&deps.storage).load()?;
let mut state = config_read(deps.storage).load()?;
let GuardianSetUpgrade {
new_guardian_set_index,
@ -265,107 +252,72 @@ fn vaa_update_guardian_set<S: Storage, A: Api, Q: Querier>(
state.guardian_set_index = new_guardian_set_index;
guardian_set_set(
&mut deps.storage,
state.guardian_set_index,
&new_guardian_set,
)?;
guardian_set_set(deps.storage, state.guardian_set_index, &new_guardian_set)?;
config(&mut deps.storage).save(&state)?;
config(deps.storage).save(&state)?;
let mut old_guardian_set = guardian_set_get(&deps.storage, old_guardian_set_index)?;
old_guardian_set.expiration_time = env.block.time + state.guardian_set_expirity;
guardian_set_set(&mut deps.storage, old_guardian_set_index, &old_guardian_set)?;
let mut old_guardian_set = guardian_set_get(deps.storage, old_guardian_set_index)?;
old_guardian_set.expiration_time = env.block.time.seconds() + state.guardian_set_expirity;
guardian_set_set(deps.storage, old_guardian_set_index, &old_guardian_set)?;
Ok(HandleResponse {
messages: vec![],
log: vec![
log("action", "guardian_set_change"),
log("old", old_guardian_set_index),
log("new", state.guardian_set_index),
],
data: None,
})
Ok(Response::new()
.add_attribute("action", "guardian_set_change")
.add_attribute("old", old_guardian_set_index.to_string())
.add_attribute("new", state.guardian_set_index.to_string()))
}
pub fn handle_set_fee<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
env: Env,
data: &Vec<u8>,
) -> StdResult<HandleResponse> {
pub fn handle_set_fee(deps: DepsMut, _env: Env, data: &Vec<u8>) -> StdResult<Response> {
let set_fee_msg = SetFee::deserialize(&data)?;
// Save new fees
let mut state = config_read(&mut deps.storage).load()?;
let mut state = config_read(deps.storage).load()?;
state.fee = set_fee_msg.fee;
config(&mut deps.storage).save(&state)?;
config(deps.storage).save(&state)?;
Ok(HandleResponse {
messages: vec![],
log: vec![
log("action", "fee_change"),
log("new_fee.amount", state.fee.amount),
log("new_fee.denom", state.fee.denom),
],
data: None,
})
Ok(Response::new()
.add_attribute("action", "fee_change")
.add_attribute("new_fee.amount", state.fee.amount.to_string())
.add_attribute("new_fee.denom", state.fee.denom.to_string()))
}
pub fn handle_transfer_fee<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
env: Env,
data: &Vec<u8>,
) -> StdResult<HandleResponse> {
pub fn handle_transfer_fee(deps: DepsMut, _env: Env, data: &Vec<u8>) -> StdResult<Response> {
let transfer_msg = TransferFee::deserialize(&data)?;
Ok(HandleResponse {
messages: vec![CosmosMsg::Bank(BankMsg::Send {
from_address: env.contract.address,
to_address: deps.api.human_address(&transfer_msg.recipient)?,
amount: vec![transfer_msg.amount],
})],
log: vec![],
data: None,
})
Ok(Response::new().add_message(CosmosMsg::Bank(BankMsg::Send {
to_address: deps.api.addr_humanize(&transfer_msg.recipient)?.to_string(),
amount: vec![transfer_msg.amount],
})))
}
fn handle_post_message<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
fn handle_post_message(
deps: DepsMut,
env: Env,
info: MessageInfo,
message: &[u8],
nonce: u32,
) -> StdResult<HandleResponse> {
let state = config_read(&deps.storage).load()?;
) -> StdResult<Response> {
let state = config_read(deps.storage).load()?;
let fee = state.fee;
// Check fee
if !has_coins(env.message.sent_funds.as_ref(), &fee) {
if !has_coins(info.funds.as_ref(), &fee) {
return ContractError::FeeTooLow.std_err();
}
let emitter = extend_address_to_32(&deps.api.canonical_address(&env.message.sender)?);
let emitter = extend_address_to_32(&deps.api.addr_canonicalize(&info.sender.as_str())?);
let sequence = sequence_read(deps.storage, emitter.as_slice());
sequence_set(deps.storage, emitter.as_slice(), sequence + 1)?;
let sequence = sequence_read(&deps.storage, emitter.as_slice());
sequence_set(&mut deps.storage, emitter.as_slice(), sequence + 1)?;
Ok(HandleResponse {
messages: vec![],
log: vec![
log("message.message", hex::encode(message)),
log("message.sender", hex::encode(emitter)),
log("message.chain_id", CHAIN_ID),
log("message.nonce", nonce),
log("message.sequence", sequence),
log("message.block_time", env.block.time),
],
data: None,
})
Ok(Response::new()
.add_attribute("message.message", hex::encode(message))
.add_attribute("message.sender", hex::encode(emitter))
.add_attribute("message.chain_id", CHAIN_ID.to_string())
.add_attribute("message.nonce", nonce.to_string())
.add_attribute("message.sequence", sequence.to_string())
.add_attribute("message.block_time", env.block.time.seconds().to_string()))
}
pub fn query<S: Storage, A: Api, Q: Querier>(
deps: &Extern<S, A, Q>,
msg: QueryMsg,
) -> StdResult<Binary> {
pub fn query(deps: Deps, msg: QueryMsg) -> StdResult<Binary> {
match msg {
QueryMsg::GuardianSetInfo {} => to_binary(&query_guardian_set_info(deps)?),
QueryMsg::VerifyVAA { vaa, block_time } => to_binary(&query_parse_and_verify_vaa(
@ -378,11 +330,9 @@ pub fn query<S: Storage, A: Api, Q: Querier>(
}
}
pub fn query_guardian_set_info<S: Storage, A: Api, Q: Querier>(
deps: &Extern<S, A, Q>,
) -> StdResult<GuardianSetInfoResponse> {
let state = config_read(&deps.storage).load()?;
let guardian_set = guardian_set_get(&deps.storage, state.guardian_set_index)?;
pub fn query_guardian_set_info(deps: Deps) -> StdResult<GuardianSetInfoResponse> {
let state = config_read(deps.storage).load()?;
let guardian_set = guardian_set_get(deps.storage, state.guardian_set_index)?;
let res = GuardianSetInfoResponse {
guardian_set_index: state.guardian_set_index,
addresses: guardian_set.addresses,
@ -390,33 +340,28 @@ pub fn query_guardian_set_info<S: Storage, A: Api, Q: Querier>(
Ok(res)
}
pub fn query_parse_and_verify_vaa<S: Storage, A: Api, Q: Querier>(
deps: &Extern<S, A, Q>,
pub fn query_parse_and_verify_vaa(
deps: Deps,
data: &[u8],
block_time: u64,
) -> StdResult<ParsedVAA> {
parse_and_verify_vaa(&deps.storage, data, block_time)
parse_and_verify_vaa(deps.storage, data, block_time)
}
// returns the hex of the 32 byte address we use for some address on this chain
pub fn query_address_hex<S: Storage, A: Api, Q: Querier>(
deps: &Extern<S, A, Q>,
address: &HumanAddr,
) -> StdResult<GetAddressHexResponse> {
pub fn query_address_hex(deps: Deps, address: &HumanAddr) -> StdResult<GetAddressHexResponse> {
Ok(GetAddressHexResponse {
hex: hex::encode(extend_address_to_32(&deps.api.canonical_address(&address)?)),
hex: hex::encode(extend_address_to_32(&deps.api.addr_canonicalize(&address)?)),
})
}
pub fn query_state<S: Storage, A: Api, Q: Querier>(
deps: &Extern<S, A, Q>,
) -> StdResult<GetStateResponse> {
let state = config_read(&deps.storage).load()?;
pub fn query_state(deps: Deps) -> StdResult<GetStateResponse> {
let state = config_read(deps.storage).load()?;
let res = GetStateResponse { fee: state.fee };
Ok(res)
}
fn keys_equal(a: &VerifyKey, b: &GuardianAddress) -> bool {
fn keys_equal(a: &VerifyingKey, b: &GuardianAddress) -> bool {
let mut hasher = Keccak256::new();
let point: EncodedPoint = EncodedPoint::from(a);

View File

@ -104,7 +104,6 @@ impl ContractError {
pub fn std(&self) -> StdError {
StdError::GenericErr {
msg: format!("{}", self),
backtrace: None,
}
}

View File

@ -5,6 +5,3 @@ pub mod msg;
pub mod state;
pub use crate::error::ContractError;
#[cfg(all(target_arch = "wasm32", not(feature = "library")))]
cosmwasm_std::create_entry_points!(contract);

View File

@ -1,7 +1,6 @@
use cosmwasm_std::{
Binary,
Coin,
HumanAddr,
};
use schemars::JsonSchema;
use serde::{
@ -14,8 +13,10 @@ use crate::state::{
GuardianSetInfo,
};
type HumanAddr = String;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InitMsg {
pub struct InstantiateMsg {
pub gov_chain: u16,
pub gov_address: Binary,
@ -25,7 +26,7 @@ pub struct InitMsg {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum HandleMsg {
pub enum ExecuteMsg {
SubmitVAA { vaa: Binary },
PostMessage { message: Binary, nonce: u32 },
}

View File

@ -1,6 +1,5 @@
use schemars::{
JsonSchema,
Set,
};
use serde::{
Deserialize,
@ -11,7 +10,6 @@ use cosmwasm_std::{
Binary,
CanonicalAddr,
Coin,
HumanAddr,
StdResult,
Storage,
Uint128,
@ -37,6 +35,8 @@ use sha3::{
Keccak256,
};
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";
@ -217,62 +217,62 @@ pub struct WormholeInfo {
pub guardian_set_expirity: u64,
}
pub fn config<S: Storage>(storage: &mut S) -> Singleton<S, ConfigInfo> {
pub fn config(storage: &mut dyn Storage) -> Singleton<ConfigInfo> {
singleton(storage, CONFIG_KEY)
}
pub fn config_read<S: Storage>(storage: &S) -> ReadonlySingleton<S, ConfigInfo> {
pub fn config_read(storage: &dyn Storage) -> ReadonlySingleton<ConfigInfo> {
singleton_read(storage, CONFIG_KEY)
}
pub fn guardian_set_set<S: Storage>(
storage: &mut S,
pub fn guardian_set_set(
storage: &mut dyn Storage,
index: u32,
data: &GuardianSetInfo,
) -> StdResult<()> {
bucket(GUARDIAN_SET_KEY, storage).save(&index.to_be_bytes(), data)
bucket(storage, GUARDIAN_SET_KEY).save(&index.to_be_bytes(), data)
}
pub fn guardian_set_get<S: Storage>(storage: &S, index: u32) -> StdResult<GuardianSetInfo> {
bucket_read(GUARDIAN_SET_KEY, storage).load(&index.to_be_bytes())
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<S: Storage>(storage: &mut S, emitter: &[u8], sequence: u64) -> StdResult<()> {
bucket(SEQUENCE_KEY, storage).save(emitter, &sequence)
pub fn sequence_set(storage: &mut dyn Storage, emitter: &[u8], sequence: u64) -> StdResult<()> {
bucket(storage, SEQUENCE_KEY).save(emitter, &sequence)
}
pub fn sequence_read<S: Storage>(storage: &S, emitter: &[u8]) -> u64 {
bucket_read(SEQUENCE_KEY, storage)
pub fn sequence_read(storage: &dyn Storage, emitter: &[u8]) -> u64 {
bucket_read(storage, SEQUENCE_KEY)
.load(&emitter)
.or::<u64>(Ok(0))
.unwrap()
}
pub fn vaa_archive_add<S: Storage>(storage: &mut S, hash: &[u8]) -> StdResult<()> {
bucket(GUARDIAN_SET_KEY, storage).save(hash, &true)
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<S: Storage>(storage: &S, hash: &[u8]) -> bool {
bucket_read(GUARDIAN_SET_KEY, storage)
pub fn vaa_archive_check(storage: &dyn Storage, hash: &[u8]) -> bool {
bucket_read(storage, GUARDIAN_SET_KEY)
.load(&hash)
.or::<bool>(Ok(false))
.unwrap()
}
pub fn wrapped_asset<S: Storage>(storage: &mut S) -> Bucket<S, HumanAddr> {
bucket(WRAPPED_ASSET_KEY, storage)
pub fn wrapped_asset(storage: &mut dyn Storage) -> Bucket<HumanAddr> {
bucket(storage, WRAPPED_ASSET_KEY)
}
pub fn wrapped_asset_read<S: Storage>(storage: &S) -> ReadonlyBucket<S, HumanAddr> {
bucket_read(WRAPPED_ASSET_KEY, storage)
pub fn wrapped_asset_read(storage: &dyn Storage) -> ReadonlyBucket<HumanAddr> {
bucket_read(storage, WRAPPED_ASSET_KEY)
}
pub fn wrapped_asset_address<S: Storage>(storage: &mut S) -> Bucket<S, Vec<u8>> {
bucket(WRAPPED_ASSET_ADDRESS_KEY, storage)
pub fn wrapped_asset_address(storage: &mut dyn Storage) -> Bucket<Vec<u8>> {
bucket(storage, WRAPPED_ASSET_ADDRESS_KEY)
}
pub fn wrapped_asset_address_read<S: Storage>(storage: &S) -> ReadonlyBucket<S, Vec<u8>> {
bucket_read(WRAPPED_ASSET_ADDRESS_KEY, storage)
pub fn wrapped_asset_address_read(storage: &dyn Storage) -> ReadonlyBucket<Vec<u8>> {
bucket_read(storage, WRAPPED_ASSET_ADDRESS_KEY)
}
pub struct GovernancePacket {
@ -351,7 +351,7 @@ impl SetFee {
let (_, amount) = data.get_u256(0);
let fee = Coin {
denom: String::from(FEE_DENOMINATION),
amount: Uint128(amount),
amount: Uint128::new(amount),
};
Ok(SetFee { fee })
}
@ -371,7 +371,7 @@ impl TransferFee {
let (_, amount) = data.get_u256(32);
let amount = Coin {
denom: String::from(FEE_DENOMINATION),
amount: Uint128(amount),
amount: Uint128::new(amount),
};
Ok(TransferFee { amount, recipient })
}