accountant: use token bridge governance vaa to make modifications
This commit is contained in:
parent
3e05a027af
commit
a3a8e3ea87
|
@ -0,0 +1,3 @@
|
|||
[profile.release]
|
||||
lto = true
|
||||
strip = true
|
|
@ -2,7 +2,7 @@ use std::marker::PhantomData;
|
|||
|
||||
use accountant::{
|
||||
query_balance, query_modification,
|
||||
state::{account, transfer, Modification, TokenAddress, Transfer},
|
||||
state::{account, transfer, Kind, Modification, Reason, TokenAddress, Transfer},
|
||||
validate_transfer,
|
||||
};
|
||||
use anyhow::{ensure, Context};
|
||||
|
@ -17,7 +17,7 @@ use cw_storage_plus::Bound;
|
|||
use serde_wormhole::RawMessage;
|
||||
use tinyvec::{Array, TinyVec};
|
||||
use wormhole::{
|
||||
token::{Action, GovernancePacket, Message},
|
||||
token::{Action, GovernancePacket, Message, ModificationKind},
|
||||
vaa::{self, Body, Header, Signature},
|
||||
Chain,
|
||||
};
|
||||
|
@ -74,17 +74,17 @@ pub fn execute(
|
|||
guardian_set_index,
|
||||
signature,
|
||||
} => submit_observations(deps, info, observations, guardian_set_index, signature),
|
||||
ExecuteMsg::ModifyBalance {
|
||||
modification,
|
||||
guardian_set_index,
|
||||
signatures,
|
||||
} => modify_balance(deps, info, modification, guardian_set_index, signatures),
|
||||
// ExecuteMsg::ModifyBalance {
|
||||
// modification,
|
||||
// guardian_set_index,
|
||||
// signatures,
|
||||
// } => modify_balance(deps, info, modification, guardian_set_index, signatures),
|
||||
ExecuteMsg::UpgradeContract {
|
||||
upgrade,
|
||||
guardian_set_index,
|
||||
signatures,
|
||||
} => upgrade_contract(deps, env, info, upgrade, guardian_set_index, signatures),
|
||||
ExecuteMsg::SubmitVAAs { vaas } => submit_vaas(deps, info, vaas),
|
||||
ExecuteMsg::SubmitVaas { vaas } => submit_vaas(deps, info, vaas),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,31 +276,17 @@ fn handle_observation(
|
|||
|
||||
fn modify_balance(
|
||||
deps: DepsMut<WormholeQuery>,
|
||||
info: MessageInfo,
|
||||
modification: Binary,
|
||||
guardian_set_index: u32,
|
||||
signatures: Vec<Signature>,
|
||||
) -> Result<Response, AnyError> {
|
||||
deps.querier
|
||||
.query::<Empty>(
|
||||
&WormholeQuery::VerifyQuorum {
|
||||
data: modification.clone(),
|
||||
guardian_set_index,
|
||||
signatures: signatures.into_iter().map(From::from).collect(),
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
.context(ContractError::VerifyQuorum)?;
|
||||
info: &MessageInfo,
|
||||
modification: Modification,
|
||||
) -> Result<Event, AnyError> {
|
||||
let mut event = accountant::modify_balance(deps, modification)
|
||||
.context("failed to modify account balance")?;
|
||||
|
||||
let msg: Modification = from_binary(&modification).context("failed to parse `Modification`")?;
|
||||
|
||||
let event =
|
||||
accountant::modify_balance(deps, msg).context("failed to modify account balance")?;
|
||||
|
||||
Ok(Response::new()
|
||||
event = event
|
||||
.add_attribute("action", "modify_balance")
|
||||
.add_attribute("owner", info.sender)
|
||||
.add_event(event))
|
||||
.add_attribute("owner", info.sender.clone());
|
||||
|
||||
Ok(event)
|
||||
}
|
||||
|
||||
fn upgrade_contract(
|
||||
|
@ -345,7 +331,7 @@ fn submit_vaas(
|
|||
) -> Result<Response, AnyError> {
|
||||
let evts = vaas
|
||||
.into_iter()
|
||||
.map(|v| handle_vaa(deps.branch(), v))
|
||||
.map(|v| handle_vaa(deps.branch(), &info, v))
|
||||
.collect::<anyhow::Result<Vec<_>>>()?;
|
||||
Ok(Response::new()
|
||||
.add_attribute("action", "submit_vaas")
|
||||
|
@ -353,7 +339,11 @@ fn submit_vaas(
|
|||
.add_events(evts))
|
||||
}
|
||||
|
||||
fn handle_vaa(mut deps: DepsMut<WormholeQuery>, vaa: Binary) -> anyhow::Result<Event> {
|
||||
fn handle_vaa(
|
||||
mut deps: DepsMut<WormholeQuery>,
|
||||
info: &MessageInfo,
|
||||
vaa: Binary,
|
||||
) -> anyhow::Result<Event> {
|
||||
let (header, data) = serde_wormhole::from_slice::<(Header, &RawMessage)>(&vaa)
|
||||
.context("failed to parse VAA header")?;
|
||||
|
||||
|
@ -394,12 +384,12 @@ fn handle_vaa(mut deps: DepsMut<WormholeQuery>, vaa: Binary) -> anyhow::Result<E
|
|||
bail!(ContractError::DuplicateMessage);
|
||||
}
|
||||
|
||||
let evt = if body.emitter_chain == Chain::Solana
|
||||
let evt = if (body.emitter_chain == Chain::Solana || body.emitter_chain == Chain::Wormchain)
|
||||
&& body.emitter_address == wormhole::GOVERNANCE_EMITTER
|
||||
{
|
||||
let govpacket = serde_wormhole::from_slice(body.payload)
|
||||
.context("failed to parse governance packet")?;
|
||||
handle_governance_vaa(deps.branch(), body.with_payload(govpacket))?
|
||||
handle_governance_vaa(deps.branch(), info, body.with_payload(govpacket))?
|
||||
} else {
|
||||
let msg = serde_wormhole::from_slice(body.payload)
|
||||
.context("failed to parse tokenbridge message")?;
|
||||
|
@ -415,6 +405,7 @@ fn handle_vaa(mut deps: DepsMut<WormholeQuery>, vaa: Binary) -> anyhow::Result<E
|
|||
|
||||
fn handle_governance_vaa(
|
||||
deps: DepsMut<WormholeQuery>,
|
||||
info: &MessageInfo,
|
||||
body: Body<GovernancePacket>,
|
||||
) -> anyhow::Result<Event> {
|
||||
ensure!(
|
||||
|
@ -438,6 +429,34 @@ fn handle_governance_vaa(
|
|||
.add_attribute("chain", chain.to_string())
|
||||
.add_attribute("emitter_address", emitter_address.to_string()))
|
||||
}
|
||||
Action::ModifyBalance {
|
||||
sequence,
|
||||
chain_id,
|
||||
token_chain,
|
||||
token_address,
|
||||
kind,
|
||||
amount,
|
||||
reason,
|
||||
} => {
|
||||
let token_address = TokenAddress::new(token_address.0);
|
||||
let kind = match kind {
|
||||
ModificationKind::Add => Kind::Add,
|
||||
ModificationKind::Subtract => Kind::Sub,
|
||||
ModificationKind::Unknown => bail!("unsupported governance action"),
|
||||
};
|
||||
let amount = Uint256::from_be_bytes(amount.0);
|
||||
let reason = Reason::new(reason);
|
||||
let modification = Modification {
|
||||
sequence,
|
||||
chain_id,
|
||||
token_chain,
|
||||
token_address,
|
||||
kind,
|
||||
amount,
|
||||
reason,
|
||||
};
|
||||
modify_balance(deps, info, modification).map_err(|e| e.into())
|
||||
}
|
||||
_ => bail!("unsupported governance action"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,18 +106,17 @@ pub enum ExecuteMsg {
|
|||
signature: Signature,
|
||||
},
|
||||
|
||||
/// Modifies the balance of a single account. Used to manually override the balance.
|
||||
ModifyBalance {
|
||||
// A serialized `Modification` message.
|
||||
modification: Binary,
|
||||
// /// Modifies the balance of a single account. Used to manually override the balance.
|
||||
// ModifyBalance {
|
||||
// // A serialized governance vaa containing a serialized `Modification` message.
|
||||
// // modification_vaa: Binary,
|
||||
|
||||
// The index of the guardian set used to sign this modification.
|
||||
guardian_set_index: u32,
|
||||
|
||||
// A quorum of signatures for `modification`.
|
||||
signatures: Vec<Signature>,
|
||||
},
|
||||
// // The index of the guardian set used to sign this modification.
|
||||
// // guardian_set_index: u32,
|
||||
|
||||
// // A quorum of signatures for `modification`.
|
||||
// // signatures: Vec<Signature>,
|
||||
// },
|
||||
UpgradeContract {
|
||||
// A serialized `Upgrade` message.
|
||||
upgrade: Binary,
|
||||
|
@ -131,7 +130,7 @@ pub enum ExecuteMsg {
|
|||
|
||||
/// Submit one or more signed VAAs to update the on-chain state. If processing any of the VAAs
|
||||
/// returns an error, the entire transaction is aborted and none of the VAAs are committed.
|
||||
SubmitVAAs {
|
||||
SubmitVaas {
|
||||
/// One or more VAAs to be submitted. Each VAA should be encoded in the standard wormhole
|
||||
/// wire format.
|
||||
vaas: Vec<Binary>,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use accountant::state::{account, transfer, Modification};
|
||||
use accountant::state::{account, transfer, Kind, Modification};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::{
|
||||
testing::{MockApi, MockStorage},
|
||||
|
@ -19,9 +19,9 @@ use global_accountant::{
|
|||
};
|
||||
use serde::Serialize;
|
||||
use wormhole::{
|
||||
token::{Action, GovernancePacket},
|
||||
token::{Action, GovernancePacket, ModificationKind},
|
||||
vaa::{Body, Header, Signature},
|
||||
Address, Chain, Vaa,
|
||||
Address, Amount, Chain, Vaa,
|
||||
};
|
||||
use wormhole_bindings::{fake, WormholeQuery};
|
||||
|
||||
|
@ -34,6 +34,7 @@ pub struct TransferResponse {
|
|||
pub struct Contract {
|
||||
addr: Addr,
|
||||
app: FakeApp,
|
||||
pub sequence: u64,
|
||||
}
|
||||
|
||||
impl Contract {
|
||||
|
@ -67,23 +68,71 @@ impl Contract {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn modify_balance(
|
||||
pub fn modify_balance_with(
|
||||
&mut self,
|
||||
modification: Binary,
|
||||
guardian_set_index: u32,
|
||||
signatures: Vec<Signature>,
|
||||
modification: Modification,
|
||||
wh: &fake::WormholeKeeper,
|
||||
tamperer: impl Fn(Vaa<GovernancePacket>) -> Binary,
|
||||
) -> anyhow::Result<AppResponse> {
|
||||
let Modification {
|
||||
sequence,
|
||||
chain_id,
|
||||
token_chain,
|
||||
token_address,
|
||||
kind,
|
||||
amount,
|
||||
reason,
|
||||
} = modification;
|
||||
let token_address = Address(*token_address);
|
||||
let kind = match kind {
|
||||
Kind::Add => ModificationKind::Add,
|
||||
Kind::Sub => ModificationKind::Subtract,
|
||||
};
|
||||
let amount = Amount(amount.to_be_bytes());
|
||||
let reason = reason.0;
|
||||
let body = Body {
|
||||
timestamp: self.sequence as u32,
|
||||
nonce: self.sequence as u32,
|
||||
emitter_chain: Chain::Solana,
|
||||
emitter_address: wormhole::GOVERNANCE_EMITTER,
|
||||
sequence: self.sequence,
|
||||
consistency_level: 0,
|
||||
payload: GovernancePacket {
|
||||
chain: Chain::Any,
|
||||
action: Action::ModifyBalance {
|
||||
sequence,
|
||||
chain_id,
|
||||
token_chain,
|
||||
token_address,
|
||||
kind,
|
||||
amount,
|
||||
reason,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
self.sequence += 1;
|
||||
|
||||
let (vaa, _) = sign_vaa_body(wh, body);
|
||||
let data = tamperer(vaa);
|
||||
let vaas: Vec<Binary> = vec![data];
|
||||
|
||||
self.app.execute_contract(
|
||||
Addr::unchecked(USER),
|
||||
self.addr(),
|
||||
&ExecuteMsg::ModifyBalance {
|
||||
modification,
|
||||
guardian_set_index,
|
||||
signatures,
|
||||
},
|
||||
&ExecuteMsg::SubmitVaas { vaas },
|
||||
&[],
|
||||
)
|
||||
}
|
||||
pub fn modify_balance(
|
||||
&mut self,
|
||||
modification: Modification,
|
||||
wh: &fake::WormholeKeeper,
|
||||
) -> anyhow::Result<AppResponse> {
|
||||
self.modify_balance_with(modification, wh, |vaa| {
|
||||
serde_wormhole::to_vec(&vaa).map(From::from).unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn upgrade_contract(
|
||||
&mut self,
|
||||
|
@ -107,7 +156,7 @@ impl Contract {
|
|||
self.app.execute_contract(
|
||||
Addr::unchecked(ADMIN),
|
||||
self.addr(),
|
||||
&ExecuteMsg::SubmitVAAs { vaas },
|
||||
&ExecuteMsg::SubmitVaas { vaas },
|
||||
&[],
|
||||
)
|
||||
}
|
||||
|
@ -287,7 +336,16 @@ pub fn proper_instantiate() -> (fake::WormholeKeeper, Contract) {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
(wh, Contract { addr, app })
|
||||
let sequence = 0;
|
||||
|
||||
(
|
||||
wh,
|
||||
Contract {
|
||||
addr,
|
||||
app,
|
||||
sequence,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn sign_vaa_body<P: Serialize>(wh: &fake::WormholeKeeper, body: Body<P>) -> (Vaa<P>, Binary) {
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
mod helpers;
|
||||
|
||||
use accountant::state::{account, Kind, Modification};
|
||||
use cosmwasm_std::{to_binary, Event, Uint256};
|
||||
use cosmwasm_std::{Event, Uint256};
|
||||
use helpers::*;
|
||||
|
||||
#[test]
|
||||
fn simple_modify() {
|
||||
let (wh, mut contract) = proper_instantiate();
|
||||
|
||||
let index = wh.guardian_set_index();
|
||||
let m = Modification {
|
||||
sequence: 0,
|
||||
chain_id: 1,
|
||||
|
@ -16,14 +15,10 @@ fn simple_modify() {
|
|||
token_address: [0x7c; 32].into(),
|
||||
kind: Kind::Add,
|
||||
amount: Uint256::from(300u128),
|
||||
reason: "test".into(),
|
||||
reason: "test".try_into().unwrap(),
|
||||
};
|
||||
let modification = to_binary(&m).unwrap();
|
||||
|
||||
let signatures = wh.sign(&modification);
|
||||
let resp = contract
|
||||
.modify_balance(modification, index, signatures)
|
||||
.unwrap();
|
||||
let resp = contract.modify_balance(m.clone(), &wh).unwrap();
|
||||
|
||||
let evt = Event::new("wasm-Modification")
|
||||
.add_attribute("sequence", serde_json_wasm::to_string(&m.sequence).unwrap())
|
||||
|
@ -59,7 +54,6 @@ fn simple_modify() {
|
|||
fn duplicate_modify() {
|
||||
let (wh, mut contract) = proper_instantiate();
|
||||
|
||||
let index = wh.guardian_set_index();
|
||||
let m = Modification {
|
||||
sequence: 0,
|
||||
chain_id: 1,
|
||||
|
@ -67,17 +61,13 @@ fn duplicate_modify() {
|
|||
token_address: [0x7c; 32].into(),
|
||||
kind: Kind::Add,
|
||||
amount: Uint256::from(300u128),
|
||||
reason: "test".into(),
|
||||
reason: "test".try_into().unwrap(),
|
||||
};
|
||||
let modification = to_binary(&m).unwrap();
|
||||
|
||||
let signatures = wh.sign(&modification);
|
||||
contract
|
||||
.modify_balance(modification.clone(), index, signatures.clone())
|
||||
.unwrap();
|
||||
contract.modify_balance(m.clone(), &wh).unwrap();
|
||||
|
||||
contract
|
||||
.modify_balance(modification, index, signatures)
|
||||
.modify_balance(m, &wh)
|
||||
.expect_err("successfully submitted duplicate modification");
|
||||
}
|
||||
|
||||
|
@ -85,7 +75,6 @@ fn duplicate_modify() {
|
|||
fn round_trip() {
|
||||
let (wh, mut contract) = proper_instantiate();
|
||||
|
||||
let index = wh.guardian_set_index();
|
||||
let mut m = Modification {
|
||||
sequence: 0,
|
||||
chain_id: 1,
|
||||
|
@ -93,14 +82,10 @@ fn round_trip() {
|
|||
token_address: [0x7c; 32].into(),
|
||||
kind: Kind::Add,
|
||||
amount: Uint256::from(300u128),
|
||||
reason: "test".into(),
|
||||
reason: "test".try_into().unwrap(),
|
||||
};
|
||||
let modification = to_binary(&m).unwrap();
|
||||
|
||||
let signatures = wh.sign(&modification);
|
||||
contract
|
||||
.modify_balance(modification, index, signatures)
|
||||
.unwrap();
|
||||
contract.modify_balance(m.clone(), &wh).unwrap();
|
||||
|
||||
let actual = contract.query_modification(m.sequence).unwrap();
|
||||
assert_eq!(m, actual);
|
||||
|
@ -108,14 +93,9 @@ fn round_trip() {
|
|||
// Now reverse the modification.
|
||||
m.sequence += 1;
|
||||
m.kind = Kind::Sub;
|
||||
m.reason = "reverse".into();
|
||||
m.reason = "reverse".try_into().unwrap();
|
||||
|
||||
let modification = to_binary(&m).unwrap();
|
||||
|
||||
let signatures = wh.sign(&modification);
|
||||
contract
|
||||
.modify_balance(modification, index, signatures)
|
||||
.unwrap();
|
||||
contract.modify_balance(m.clone(), &wh).unwrap();
|
||||
|
||||
let actual = contract.query_modification(m.sequence).unwrap();
|
||||
assert_eq!(m, actual);
|
||||
|
@ -134,7 +114,6 @@ fn round_trip() {
|
|||
fn missing_guardian_set() {
|
||||
let (wh, mut contract) = proper_instantiate();
|
||||
|
||||
let index = wh.guardian_set_index();
|
||||
let m = Modification {
|
||||
sequence: 0,
|
||||
chain_id: 1,
|
||||
|
@ -142,13 +121,14 @@ fn missing_guardian_set() {
|
|||
token_address: [0x7c; 32].into(),
|
||||
kind: Kind::Add,
|
||||
amount: Uint256::from(300u128),
|
||||
reason: "test".into(),
|
||||
reason: "test".try_into().unwrap(),
|
||||
};
|
||||
let modification = to_binary(&m).unwrap();
|
||||
|
||||
let signatures = wh.sign(&modification);
|
||||
contract
|
||||
.modify_balance(modification, index + 1, signatures)
|
||||
.modify_balance_with(m, &wh, |mut vaa| {
|
||||
vaa.guardian_set_index += 1;
|
||||
serde_wormhole::to_vec(&vaa).map(From::from).unwrap()
|
||||
})
|
||||
.expect_err("successfully modified balance with invalid guardian set");
|
||||
}
|
||||
|
||||
|
@ -156,7 +136,6 @@ fn missing_guardian_set() {
|
|||
fn expired_guardian_set() {
|
||||
let (wh, mut contract) = proper_instantiate();
|
||||
|
||||
let index = wh.guardian_set_index();
|
||||
let mut block = contract.app().block_info();
|
||||
wh.set_expiration(block.height);
|
||||
block.height += 1;
|
||||
|
@ -169,13 +148,10 @@ fn expired_guardian_set() {
|
|||
token_address: [0x7c; 32].into(),
|
||||
kind: Kind::Add,
|
||||
amount: Uint256::from(300u128),
|
||||
reason: "test".into(),
|
||||
reason: "test".try_into().unwrap(),
|
||||
};
|
||||
let modification = to_binary(&m).unwrap();
|
||||
|
||||
let signatures = wh.sign(&modification);
|
||||
contract
|
||||
.modify_balance(modification, index, signatures)
|
||||
.modify_balance(m, &wh)
|
||||
.expect_err("successfully modified balance with expired guardian set");
|
||||
}
|
||||
|
||||
|
@ -183,7 +159,6 @@ fn expired_guardian_set() {
|
|||
fn no_quorum() {
|
||||
let (wh, mut contract) = proper_instantiate();
|
||||
|
||||
let index = wh.guardian_set_index();
|
||||
let m = Modification {
|
||||
sequence: 0,
|
||||
chain_id: 1,
|
||||
|
@ -191,19 +166,19 @@ fn no_quorum() {
|
|||
token_address: [0x7c; 32].into(),
|
||||
kind: Kind::Add,
|
||||
amount: Uint256::from(300u128),
|
||||
reason: "test".into(),
|
||||
reason: "test".try_into().unwrap(),
|
||||
};
|
||||
let modification = to_binary(&m).unwrap();
|
||||
|
||||
let mut signatures = wh.sign(&modification);
|
||||
let newlen = wh
|
||||
.calculate_quorum(0, contract.app().block_info().height)
|
||||
.map(|q| (q - 1) as usize)
|
||||
.unwrap();
|
||||
signatures.truncate(newlen);
|
||||
|
||||
contract
|
||||
.modify_balance(modification, index, signatures)
|
||||
.modify_balance_with(m, &wh, |mut vaa| {
|
||||
vaa.signatures.truncate(newlen);
|
||||
serde_wormhole::to_vec(&vaa).map(From::from).unwrap()
|
||||
})
|
||||
.expect_err("successfully submitted modification without quorum");
|
||||
}
|
||||
|
||||
|
@ -213,7 +188,6 @@ fn repeat() {
|
|||
|
||||
let (wh, mut contract) = proper_instantiate();
|
||||
|
||||
let index = wh.guardian_set_index();
|
||||
let mut m = Modification {
|
||||
sequence: 0,
|
||||
chain_id: 1,
|
||||
|
@ -221,18 +195,13 @@ fn repeat() {
|
|||
token_address: [0x7c; 32].into(),
|
||||
kind: Kind::Add,
|
||||
amount: Uint256::from(300u128),
|
||||
reason: "test".into(),
|
||||
reason: "test".try_into().unwrap(),
|
||||
};
|
||||
|
||||
for _ in 0..ITERATIONS {
|
||||
m.sequence += 1;
|
||||
|
||||
let modification = to_binary(&m).unwrap();
|
||||
|
||||
let signatures = wh.sign(&modification);
|
||||
contract
|
||||
.modify_balance(modification, index, signatures)
|
||||
.unwrap();
|
||||
contract.modify_balance(m.clone(), &wh).unwrap();
|
||||
|
||||
let actual = contract.query_modification(m.sequence).unwrap();
|
||||
assert_eq!(m, actual);
|
||||
|
|
|
@ -6,7 +6,7 @@ use accountant::state::{
|
|||
account::{self, Balance},
|
||||
transfer, Kind, Modification, Transfer,
|
||||
};
|
||||
use cosmwasm_std::{to_binary, Uint256};
|
||||
use cosmwasm_std::Uint256;
|
||||
use global_accountant::msg::TransferStatus;
|
||||
use helpers::*;
|
||||
use wormhole::{token::Message, vaa::Body, Address, Amount};
|
||||
|
@ -17,21 +17,17 @@ fn create_accounts(wh: &fake::WormholeKeeper, contract: &mut Contract, count: us
|
|||
for i in 0..count {
|
||||
for j in 0..count {
|
||||
s += 1;
|
||||
let m = to_binary(&Modification {
|
||||
let m = Modification {
|
||||
sequence: s,
|
||||
chain_id: i as u16,
|
||||
token_chain: j as u16,
|
||||
token_address: [i as u8; 32].into(),
|
||||
kind: Kind::Add,
|
||||
amount: Uint256::from(j as u128),
|
||||
reason: "create_accounts".into(),
|
||||
})
|
||||
.unwrap();
|
||||
reason: "create_accounts".try_into().unwrap(),
|
||||
};
|
||||
|
||||
let signatures = wh.sign(&m);
|
||||
contract
|
||||
.modify_balance(m, wh.guardian_set_index(), signatures)
|
||||
.unwrap();
|
||||
contract.modify_balance(m, wh).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,14 +97,10 @@ pub fn create_modifications(
|
|||
token_address: [i as u8; 32].into(),
|
||||
kind: Kind::Add,
|
||||
amount: Uint256::from(i as u128),
|
||||
reason: format!("{i}"),
|
||||
reason: format!("{i}").as_str().try_into().unwrap(),
|
||||
};
|
||||
|
||||
let msg = to_binary(&m).unwrap();
|
||||
let signatures = wh.sign(&msg);
|
||||
contract
|
||||
.modify_balance(msg, wh.guardian_set_index(), signatures)
|
||||
.unwrap();
|
||||
contract.modify_balance(m.clone(), wh).unwrap();
|
||||
|
||||
out.push(m);
|
||||
}
|
||||
|
|
|
@ -515,19 +515,20 @@ fn missing_native_account() {
|
|||
.calculate_quorum(index, contract.app().block_info().height)
|
||||
.unwrap() as usize;
|
||||
|
||||
// increase the sequence to be large than the vaa's used to register emitters.
|
||||
contract.sequence += 100;
|
||||
|
||||
// We need to set up a fake wrapped account so that the initial check succeeds.
|
||||
let m = to_binary(&Modification {
|
||||
let m = Modification {
|
||||
sequence: 0,
|
||||
chain_id: emitter_chain,
|
||||
token_chain,
|
||||
token_address: token_address.into(),
|
||||
kind: Kind::Add,
|
||||
amount: Uint256::new(amount.0),
|
||||
reason: "fake wrapped balance for testing".into(),
|
||||
})
|
||||
.unwrap();
|
||||
let signatures = wh.sign(&m);
|
||||
contract.modify_balance(m, index, signatures).unwrap();
|
||||
reason: "fake wrapped balance for testing".try_into().unwrap(),
|
||||
};
|
||||
contract.modify_balance(m, &wh).unwrap();
|
||||
|
||||
let key = transfer::Key::new(emitter_chain, [emitter_chain as u8; 32].into(), 37);
|
||||
let msg = Message::Transfer {
|
||||
|
@ -633,20 +634,20 @@ fn wrapped_to_wrapped() {
|
|||
register_emitters(&wh, &mut contract, 15);
|
||||
let index = wh.guardian_set_index();
|
||||
let num_guardians = wh.num_guardians();
|
||||
// increase the sequence to be large than the vaa's used to register emitters.
|
||||
contract.sequence += 100;
|
||||
|
||||
// We need an initial fake wrapped account.
|
||||
let m = to_binary(&Modification {
|
||||
let m = Modification {
|
||||
sequence: 0,
|
||||
chain_id: emitter_chain,
|
||||
token_chain,
|
||||
token_address: token_address.into(),
|
||||
kind: Kind::Add,
|
||||
amount: Uint256::new(amount.0),
|
||||
reason: "fake wrapped balance for testing".into(),
|
||||
})
|
||||
.unwrap();
|
||||
let signatures = wh.sign(&m);
|
||||
contract.modify_balance(m, index, signatures).unwrap();
|
||||
reason: "fake wrapped balance for testing".try_into().unwrap(),
|
||||
};
|
||||
contract.modify_balance(m, &wh).unwrap();
|
||||
|
||||
let key = transfer::Key::new(emitter_chain, [emitter_chain as u8; 32].into(), 37);
|
||||
let msg = Message::Transfer {
|
||||
|
|
|
@ -227,7 +227,7 @@ pub enum ModifyBalanceError {
|
|||
/// token_address: [3u8; 32].into(),
|
||||
/// kind: Kind::Sub,
|
||||
/// amount: Uint256::from(4u128),
|
||||
/// reason: "test".into(),
|
||||
/// reason: "test".try_into().unwrap(),
|
||||
/// };
|
||||
///
|
||||
/// let err = modify_balance(deps.as_mut(), m)
|
||||
|
@ -400,7 +400,7 @@ mod tests {
|
|||
token_address: [i as u8; 32].into(),
|
||||
kind: if i % 2 == 0 { Kind::Add } else { Kind::Sub },
|
||||
amount: Uint256::from(i as u128),
|
||||
reason: format!("{i}"),
|
||||
reason: [0x20u8; 32].into(),
|
||||
};
|
||||
out.push(m);
|
||||
}
|
||||
|
@ -547,7 +547,7 @@ mod tests {
|
|||
token_address: tx.data.token_address,
|
||||
kind: Kind::Add,
|
||||
amount: wrapped,
|
||||
reason: "test".into(),
|
||||
reason: [0x20u8; 32].into(),
|
||||
};
|
||||
modify_balance(deps.as_mut(), m).unwrap();
|
||||
|
||||
|
@ -960,7 +960,7 @@ mod tests {
|
|||
token_address: [3u8; 32].into(),
|
||||
kind: Kind::Add,
|
||||
amount: Uint256::from(4u128),
|
||||
reason: "test".into(),
|
||||
reason: [0x20u8; 32].into(),
|
||||
};
|
||||
|
||||
let evt = modify_balance(deps.as_mut(), m.clone()).unwrap();
|
||||
|
@ -997,7 +997,7 @@ mod tests {
|
|||
token_address: [3u8; 32].into(),
|
||||
kind: Kind::Add,
|
||||
amount: Uint256::from(4u128),
|
||||
reason: "test".into(),
|
||||
reason: [0x20u8; 32].into(),
|
||||
};
|
||||
|
||||
modify_balance(deps.as_mut(), m.clone()).unwrap();
|
||||
|
@ -1020,7 +1020,7 @@ mod tests {
|
|||
token_address: [3u8; 32].into(),
|
||||
kind: Kind::Add,
|
||||
amount: Uint256::from(4u128),
|
||||
reason: "test".into(),
|
||||
reason: [0x20u8; 32].into(),
|
||||
};
|
||||
|
||||
modify_balance(deps.as_mut(), m.clone()).unwrap();
|
||||
|
@ -1044,7 +1044,7 @@ mod tests {
|
|||
token_address: [3u8; 32].into(),
|
||||
kind: Kind::Add,
|
||||
amount: Uint256::from(4u128),
|
||||
reason: "test".into(),
|
||||
reason: [0x20u8; 32].into(),
|
||||
};
|
||||
|
||||
for i in 0..ITERATIONS {
|
||||
|
@ -1069,7 +1069,7 @@ mod tests {
|
|||
token_address: [3u8; 32].into(),
|
||||
kind: Kind::Sub,
|
||||
amount: Uint256::from(4u128),
|
||||
reason: "test".into(),
|
||||
reason: [0x20u8; 32].into(),
|
||||
};
|
||||
|
||||
let e = modify_balance(deps.as_mut(), m)
|
||||
|
@ -1251,7 +1251,7 @@ mod tests {
|
|||
token_address: [i as u8; 32].into(),
|
||||
kind: if i % 2 == 0 { Kind::Add } else { Kind::Sub },
|
||||
amount: Uint256::from(i as u128),
|
||||
reason: format!("{i}"),
|
||||
reason: [0x20u8; 32].into(),
|
||||
};
|
||||
|
||||
let key = i as u64;
|
||||
|
|
|
@ -19,8 +19,8 @@ pub const TRANSFERS: Map<transfer::Key, transfer::Data> = Map::new("accountant/t
|
|||
#[cw_serde]
|
||||
#[derive(Eq, PartialOrd, Ord)]
|
||||
pub enum Kind {
|
||||
Add,
|
||||
Sub,
|
||||
Add = 1,
|
||||
Sub = 2,
|
||||
}
|
||||
|
||||
impl fmt::Display for Kind {
|
||||
|
@ -32,6 +32,46 @@ impl fmt::Display for Kind {
|
|||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Copy, Default, Eq, Hash, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
pub struct Reason(
|
||||
#[serde(with = "fixed_bytes_as_string_serde")]
|
||||
#[schemars(with = "String")]
|
||||
pub [u8; 32],
|
||||
);
|
||||
|
||||
impl Reason {
|
||||
pub const fn new(reason: [u8; 32]) -> Reason {
|
||||
Reason(reason)
|
||||
}
|
||||
// pub fn to_string(&self) -> String {
|
||||
// unsafe { std::str::from_utf8_unchecked(&self.0).to_string() }
|
||||
// }
|
||||
}
|
||||
|
||||
impl From<[u8; 32]> for Reason {
|
||||
fn from(reason: [u8; 32]) -> Self {
|
||||
Reason(reason)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Reason {
|
||||
type Error = &'static str;
|
||||
fn try_from(reason: &str) -> Result<Self, Self::Error> {
|
||||
// default all spaces
|
||||
let mut bz = [b' '; 32];
|
||||
if reason.len() <= 32 {
|
||||
for (i, byte) in reason.as_bytes().iter().enumerate() {
|
||||
bz[i] = *byte;
|
||||
}
|
||||
Ok(Reason(bz))
|
||||
} else {
|
||||
Err("reason cannot be longer than 32 bytes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Eq, PartialOrd, Ord)]
|
||||
pub struct Modification {
|
||||
|
@ -49,7 +89,30 @@ pub struct Modification {
|
|||
// The amount to be modified.
|
||||
pub amount: Uint256,
|
||||
// A human-readable reason for the modification.
|
||||
pub reason: String,
|
||||
pub reason: Reason,
|
||||
}
|
||||
|
||||
pub const MODIFICATIONS: Map<u64, Modification> = Map::new("accountant/modifications");
|
||||
|
||||
pub mod fixed_bytes_as_string_serde {
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::{Deserializer, Serializer};
|
||||
|
||||
pub fn serialize<S: Serializer>(v: &[u8; 32], s: S) -> Result<S::Ok, S::Error> {
|
||||
let str = String::from_utf8((*v).into())
|
||||
.map_err(|_| serde::ser::Error::custom("invalid utf8"))?;
|
||||
String::serialize(&str, s)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<[u8; 32], D::Error> {
|
||||
static EXPECTING: &str = "reason string of 32 bytes";
|
||||
let str = String::deserialize(d)?;
|
||||
let bz = str.as_bytes();
|
||||
if bz.len() != 32 {
|
||||
return Err(serde::de::Error::invalid_length(32, &EXPECTING));
|
||||
}
|
||||
let mut bz32 = [0u8; 32];
|
||||
bz32.clone_from_slice(bz);
|
||||
Ok(bz32)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_wormhole::RawMessage;
|
||||
|
||||
use crate::{Address, Amount, Chain};
|
||||
use serde::{Deserializer, Serializer};
|
||||
|
||||
/// Represents a non-governance action targeted at the token bridge.
|
||||
///
|
||||
|
@ -201,6 +202,52 @@ pub enum Message<P = Box<RawMessage>> {
|
|||
},
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
|
||||
pub enum ModificationKind {
|
||||
Unknown = 0,
|
||||
Add = 1,
|
||||
Subtract = 2,
|
||||
}
|
||||
|
||||
impl From<u8> for ModificationKind {
|
||||
fn from(other: u8) -> ModificationKind {
|
||||
match other {
|
||||
1 => ModificationKind::Add,
|
||||
2 => ModificationKind::Subtract,
|
||||
_ => ModificationKind::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ModificationKind> for u8 {
|
||||
fn from(other: ModificationKind) -> u8 {
|
||||
match other {
|
||||
ModificationKind::Unknown => 0,
|
||||
ModificationKind::Add => 1,
|
||||
ModificationKind::Subtract => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ModificationKind {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_u8((*self).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ModificationKind {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
<u8 as Deserialize>::deserialize(deserializer).map(Self::from)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a governance action targeted at the token bridge.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Action {
|
||||
|
@ -216,6 +263,28 @@ pub enum Action {
|
|||
/// Upgrades the token bridge contract to a new address.
|
||||
#[serde(rename = "2")]
|
||||
ContractUpgrade { new_contract: Address },
|
||||
|
||||
// Modify balance for tokenbridge
|
||||
#[serde(rename = "3")]
|
||||
ModifyBalance {
|
||||
// module: \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00TokenBridge (32)
|
||||
// chainId: \x03\x0c (2)
|
||||
// action: \x03 (1)
|
||||
|
||||
// \x00\x00\x00\x00\x00\x00\x00$
|
||||
sequence: u64, // 8
|
||||
|
||||
// \x00\x01
|
||||
chain_id: u16, // 10
|
||||
// \x00\x01
|
||||
token_chain: u16, // 12
|
||||
// ||||||||||||||||||||||||||||||||
|
||||
token_address: Address, // 44
|
||||
// \x01
|
||||
kind: ModificationKind, // 45
|
||||
amount: Amount, // 78
|
||||
reason: [u8; 32], // 110
|
||||
},
|
||||
}
|
||||
|
||||
/// Represents the payload for a governance VAA targeted at the token bridge.
|
||||
|
@ -241,7 +310,7 @@ mod governance_packet_impl {
|
|||
|
||||
use crate::{
|
||||
token::{Action, GovernancePacket},
|
||||
Address, Chain,
|
||||
Address, Amount, Chain,
|
||||
};
|
||||
|
||||
// MODULE = "TokenBridge"
|
||||
|
@ -290,6 +359,17 @@ mod governance_packet_impl {
|
|||
emitter_address: Address,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct ModifyBalance {
|
||||
sequence: u64,
|
||||
chain_id: u16,
|
||||
token_chain: u16,
|
||||
token_address: Address,
|
||||
kind: super::ModificationKind,
|
||||
amount: Amount,
|
||||
reason: [u8; 32],
|
||||
}
|
||||
|
||||
impl Serialize for GovernancePacket {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
|
@ -320,6 +400,30 @@ mod governance_packet_impl {
|
|||
seq.serialize_field("chain", &self.chain)?;
|
||||
seq.serialize_field("payload", &ContractUpgrade { new_contract })?;
|
||||
}
|
||||
Action::ModifyBalance {
|
||||
sequence,
|
||||
chain_id,
|
||||
token_chain,
|
||||
token_address,
|
||||
kind,
|
||||
amount,
|
||||
reason,
|
||||
} => {
|
||||
seq.serialize_field("action", &3u8)?;
|
||||
seq.serialize_field("chain", &self.chain)?;
|
||||
seq.serialize_field(
|
||||
"payload",
|
||||
&ModifyBalance {
|
||||
sequence,
|
||||
chain_id,
|
||||
token_chain,
|
||||
token_address,
|
||||
kind,
|
||||
amount,
|
||||
reason,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
seq.end()
|
||||
|
@ -373,6 +477,28 @@ mod governance_packet_impl {
|
|||
|
||||
Action::ContractUpgrade { new_contract }
|
||||
}
|
||||
3 => {
|
||||
let ModifyBalance {
|
||||
sequence,
|
||||
chain_id,
|
||||
token_chain,
|
||||
token_address,
|
||||
kind,
|
||||
amount,
|
||||
reason,
|
||||
} = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| Error::invalid_length(3, &EXPECTING))?;
|
||||
Action::ModifyBalance {
|
||||
sequence,
|
||||
chain_id,
|
||||
token_chain,
|
||||
token_address,
|
||||
kind,
|
||||
amount,
|
||||
reason,
|
||||
}
|
||||
}
|
||||
v => {
|
||||
return Err(Error::custom(format_args!(
|
||||
"invalid value: {v}, expected one of 1, 2"
|
||||
|
@ -450,6 +576,26 @@ mod governance_packet_impl {
|
|||
|
||||
Action::ContractUpgrade { new_contract }
|
||||
}
|
||||
3 => {
|
||||
let ModifyBalance {
|
||||
sequence,
|
||||
chain_id,
|
||||
token_chain,
|
||||
token_address,
|
||||
kind,
|
||||
amount,
|
||||
reason,
|
||||
} = map.next_value()?;
|
||||
Action::ModifyBalance {
|
||||
sequence,
|
||||
chain_id,
|
||||
token_chain,
|
||||
token_address,
|
||||
kind,
|
||||
amount,
|
||||
reason,
|
||||
}
|
||||
}
|
||||
v => {
|
||||
return Err(Error::custom(format_args!(
|
||||
"invalid action: {v}, expected one of: 1, 2"
|
||||
|
|
Loading…
Reference in New Issue