wormhole/cosmwasm/packages/wormhole-bindings/src/fake.rs

235 lines
7.3 KiB
Rust
Raw Normal View History

cosmwasm: Add wormchain-accounting contract (#1920) * sdk/rust: Move profile settings to workspace * sdk/rust: Add serde_wormhole crate The serde_wormhole crate implements the wormhole wire format as a serde data format. This will let us replace all the hand-rolled serialization with auto-generated code, which is less error-prone and easier to review. * sdk/rust: Add serde-based struct defintions Refactor the core crate to add serde-based struct definitions for the various messages used by the different wormhole smart contracts. This will also make it easier to use alternate data formats (like json) for client-side tooling. Co-authored-by: Reisen <reisen@morphism.org> * sdk/rust: Drop references to `de::Unexpected` The `de::Unexpected` enum from serde has a `Float(f64)` variant. Referencing this enum anywhere in the code will cause the compiler to emit its `fmt::Display` impl, which includes an `f64.load` instruction on wasm targets. Even if this instruction is never executed, its mere existence will cause cosmos chains to reject any cosmwasm contract that has it. Fix this by removing all references to `de::Unexpected`. * cosmwasm: Use cargo resolver version "2" Enable the new feature resolver for the entire workspace. This prevents features that are enabled only for dev builds from also being enabled in normal builds. * Move cosmwasm Dockerfile to root directory The cosmwasm contracts now also depend on the rust sdk so the docker build context needs to be set to the root directory rather than the cosmwasm/ directory. * cosmwasm: Add wormchain-accounting contract This contract implements tokenbridge accounting specifically for the wormchain environment. Fixes #1880. * cosmwasm/accounting: Drop references to `de::Unexpected` The `de::Unexpected` enum from serde has a `Float(f64)` variant. Referencing this enum anywhere in the code will cause the compiler to emit its `fmt::Display` impl, which includes an `f64.load` instruction on wasm targets. Even if this instruction is never executed, its mere existence will cause cosmos chains to reject any cosmwasm contracts that contain it. Fix this by removing references to `de::Unexpected`. Co-authored-by: Reisen <reisen@morphism.org>
2022-12-14 09:06:45 -08:00
use std::{cell::RefCell, collections::BTreeSet, fmt::Debug, rc::Rc};
use anyhow::{anyhow, bail, ensure};
use cosmwasm_std::{to_binary, Addr, Api, Binary, BlockInfo, CustomQuery, Empty, Querier, Storage};
use cw_multi_test::{AppResponse, CosmosRouter, Module};
use k256::ecdsa::{
self,
signature::{Signature as SigT, Signer, Verifier},
SigningKey,
};
use schemars::JsonSchema;
use serde::de::DeserializeOwned;
use crate::{Signature, WormholeQuery};
#[derive(Debug)]
struct Inner {
index: u32,
expiration: u64,
guardians: [SigningKey; 7],
}
#[derive(Clone, Debug)]
pub struct WormholeKeeper(Rc<RefCell<Inner>>);
impl WormholeKeeper {
pub fn new() -> WormholeKeeper {
let guardians = [
SigningKey::from_bytes(&[
93, 217, 189, 224, 168, 81, 157, 93, 238, 38, 143, 8, 182, 94, 69, 77, 232, 199,
238, 206, 15, 135, 221, 58, 43, 74, 0, 129, 54, 198, 62, 226,
])
.unwrap(),
SigningKey::from_bytes(&[
150, 48, 135, 223, 194, 186, 243, 139, 177, 8, 126, 32, 210, 57, 42, 28, 29, 102,
196, 201, 106, 136, 40, 149, 218, 150, 240, 213, 192, 128, 161, 245,
])
.unwrap(),
SigningKey::from_bytes(&[
121, 51, 199, 93, 237, 227, 62, 220, 128, 129, 195, 4, 190, 163, 254, 12, 212, 224,
188, 76, 141, 242, 229, 121, 192, 5, 161, 176, 136, 99, 83, 53,
])
.unwrap(),
SigningKey::from_bytes(&[
224, 180, 4, 114, 215, 161, 184, 12, 218, 96, 20, 141, 154, 242, 46, 230, 167, 165,
54, 141, 108, 64, 146, 27, 193, 89, 251, 139, 234, 132, 124, 30,
])
.unwrap(),
SigningKey::from_bytes(&[
69, 1, 17, 179, 19, 47, 56, 47, 255, 219, 143, 89, 115, 54, 242, 209, 163, 131,
225, 30, 59, 195, 217, 141, 167, 253, 6, 95, 252, 52, 7, 223,
])
.unwrap(),
SigningKey::from_bytes(&[
181, 3, 165, 125, 15, 200, 155, 56, 157, 204, 105, 221, 203, 149, 215, 175, 220,
228, 200, 37, 169, 39, 68, 127, 132, 196, 203, 232, 155, 55, 67, 253,
])
.unwrap(),
SigningKey::from_bytes(&[
72, 81, 175, 107, 23, 108, 178, 66, 32, 53, 14, 117, 233, 33, 114, 102, 68, 89, 83,
201, 129, 57, 56, 130, 214, 212, 172, 16, 23, 22, 234, 160,
])
.unwrap(),
];
WormholeKeeper(Rc::new(RefCell::new(Inner {
index: 0,
expiration: 0,
guardians,
})))
}
pub fn sign(&self, msg: &[u8]) -> Vec<Signature> {
self.0
.borrow()
.guardians
.iter()
.map(|g| {
<SigningKey as Signer<ecdsa::Signature>>::sign(g, msg)
.as_bytes()
.to_vec()
.into()
})
.enumerate()
.map(|(idx, sig)| Signature {
index: idx as u8,
signature: sig,
})
.collect()
}
pub fn verify_quorum(
&self,
data: &[u8],
index: u32,
signatures: &[Signature],
block_time: u64,
) -> anyhow::Result<Empty> {
let mut signers = BTreeSet::new();
for s in signatures {
self.verify_signature(data, index, s, block_time)?;
signers.insert(s.index);
}
if signers.len() as u32 >= self.calculate_quorum(index, block_time)? {
Ok(Empty {})
} else {
Err(anyhow!("no quorum"))
}
}
pub fn verify_signature(
&self,
data: &[u8],
index: u32,
sig: &Signature,
block_time: u64,
) -> anyhow::Result<Empty> {
let this = self.0.borrow();
ensure!(this.index == index, "invalid guardian set");
ensure!(
this.expiration == 0 || block_time < this.expiration,
"guardian set expired"
);
if let Some(g) = this.guardians.get(sig.index as usize) {
let s = ecdsa::Signature::try_from(&*sig.signature).unwrap();
g.verifying_key()
.verify(data, &s)
.map(|()| Empty {})
.map_err(From::from)
} else {
Err(anyhow!("invalid guardian index"))
}
}
pub fn calculate_quorum(&self, index: u32, block_time: u64) -> anyhow::Result<u32> {
let this = self.0.borrow();
ensure!(this.index == index, "invalid guardian set");
ensure!(
this.expiration == 0 || block_time < this.expiration,
"guardian set expired"
);
Ok(((this.guardians.len() as u32 * 10 / 3) * 2) / 10 + 1)
}
pub fn query(&self, request: WormholeQuery, block: &BlockInfo) -> anyhow::Result<Binary> {
match request {
WormholeQuery::VerifyQuorum {
data,
guardian_set_index,
signatures,
} => self
.verify_quorum(&data, guardian_set_index, &signatures, block.height)
.and_then(|e| to_binary(&e).map_err(From::from)),
WormholeQuery::VerifySignature {
data,
guardian_set_index,
signature,
} => self
.verify_signature(&data, guardian_set_index, &signature, block.height)
.and_then(|e| to_binary(&e).map_err(From::from)),
WormholeQuery::CalculateQuorum { guardian_set_index } => self
.calculate_quorum(guardian_set_index, block.height)
.and_then(|q| to_binary(&q).map_err(From::from)),
}
}
pub fn expiration(&self) -> u64 {
self.0.borrow().expiration
}
pub fn set_expiration(&self, expiration: u64) {
self.0.borrow_mut().expiration = expiration;
}
pub fn guardian_set_index(&self) -> u32 {
self.0.borrow().index
}
pub fn set_index(&self, index: u32) {
self.0.borrow_mut().index = index;
}
}
impl Default for WormholeKeeper {
fn default() -> Self {
Self::new()
}
}
impl Module for WormholeKeeper {
type ExecT = Empty;
type QueryT = WormholeQuery;
type SudoT = Empty;
fn execute<ExecC, QueryC>(
&self,
_api: &dyn Api,
_storage: &mut dyn Storage,
_router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
_block: &BlockInfo,
sender: Addr,
msg: Self::ExecT,
) -> anyhow::Result<AppResponse>
where
ExecC: Debug + Clone + PartialEq + JsonSchema + DeserializeOwned + 'static,
QueryC: CustomQuery + DeserializeOwned + 'static,
{
bail!("Unexpected exec msg {msg:?} from {sender}")
}
fn sudo<ExecC, QueryC>(
&self,
_api: &dyn Api,
_storage: &mut dyn Storage,
_router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
_block: &BlockInfo,
msg: Self::SudoT,
) -> anyhow::Result<AppResponse> {
bail!("Unexpected sudo msg {msg:?}")
}
fn query(
&self,
_api: &dyn Api,
_storage: &dyn Storage,
_querier: &dyn Querier,
block: &BlockInfo,
request: Self::QueryT,
) -> anyhow::Result<Binary> {
self.query(request, block)
}
}