deep_space/client/
types.rs

1use crate::address::Address;
2use crate::error::CosmosGrpcError;
3use bytes::BytesMut;
4use cosmos_sdk_proto::cosmos::auth::v1beta1::{BaseAccount as ProtoBaseAccount, ModuleAccount};
5use cosmos_sdk_proto::cosmos::vesting::v1beta1::{
6    ContinuousVestingAccount, DelayedVestingAccount, PeriodicVestingAccount, PermanentLockedAccount,
7};
8use cosmos_sdk_proto::tendermint::types::Block;
9use prost::Message;
10use prost_types::Any;
11
12/// This struct represents the status of a Cosmos chain, instead of just getting the
13/// latest block height we mandate that chain status is used, this allows callers to
14/// handle the possibility of a halted chain explicitly since essentially all requests
15/// about block height come with assumptions about the chains status
16#[derive(Debug, Clone)]
17pub enum ChainStatus {
18    /// The chain is operating correctly and blocks are being produced
19    Moving { block_height: u64 },
20    /// The chain is operating correctly, but the node we made this request
21    /// to is catching up and we should not trust it's responses to be
22    /// up to date
23    Syncing,
24    /// The chain is halted, this node is waiting for the chain to start again
25    /// the caller should take appropriate action to await the chain start
26    WaitingToStart,
27}
28
29/// This struct represents potential responses from the latest block endpoint
30/// we can either be syncing, waiting for the chain to start, or have the the
31/// actual latest block to the best of the nodes knowledge, which isn't at all
32/// a guarantee
33#[derive(Debug, Clone)]
34pub enum LatestBlock {
35    /// The chain is operating correctly and blocks are being produced, this is
36    /// the latest one this node has access to
37    Latest { block: Block },
38    /// The chain is operating correctly, but the node we made this request
39    /// to is catching up and we should not trust it's responses to be
40    /// up to date
41    Syncing { block: Block },
42    /// The chain is halted, this node is waiting for the chain to start again
43    /// the caller should take appropriate action to await the chain start
44    WaitingToStart,
45}
46
47/// Wrapper representing the various account types and their metadata data, easier to use than traits
48/// when you want to pass data around
49#[derive(Debug, Clone)]
50pub enum AccountType {
51    ProtoBaseAccount(ProtoBaseAccount),
52    PeriodicVestingAccount(PeriodicVestingAccount),
53    ContinuousVestingAccount(ContinuousVestingAccount),
54    DelayedVestingAccount(DelayedVestingAccount),
55    ModuleAccount(ModuleAccount),
56    PermenantLockedAccount(PermanentLockedAccount),
57}
58
59impl AccountType {
60    pub fn get_base_account(&self) -> BaseAccount {
61        match self {
62            AccountType::ProtoBaseAccount(a) => a.get_base_account(),
63            AccountType::PeriodicVestingAccount(a) => a.get_base_account(),
64            AccountType::ContinuousVestingAccount(a) => a.get_base_account(),
65            AccountType::DelayedVestingAccount(a) => a.get_base_account(),
66            AccountType::ModuleAccount(a) => a.get_base_account(),
67            AccountType::PermenantLockedAccount(a) => a.get_base_account(),
68        }
69    }
70
71    pub fn decode_from_any(value: prost_types::Any) -> Result<Self, CosmosGrpcError> {
72        let mut buf = BytesMut::with_capacity(value.value.len());
73        buf.extend_from_slice(&value.value);
74        match (
75            ProtoBaseAccount::decode(buf.clone()),
76            ContinuousVestingAccount::decode(buf.clone()),
77            PeriodicVestingAccount::decode(buf.clone()),
78            DelayedVestingAccount::decode(buf.clone()),
79            ModuleAccount::decode(buf.clone()),
80            PermanentLockedAccount::decode(buf.clone()),
81        ) {
82            (Ok(d), _, _, _, _, _) => Ok(AccountType::ProtoBaseAccount(d)),
83            // delayed and continuous can be parsed incorrectly
84            (_, Ok(c), Ok(p), _, _, _) => {
85                if value.type_url.contains("Continuous") {
86                    Ok(AccountType::ContinuousVestingAccount(c))
87                } else {
88                    Ok(AccountType::PeriodicVestingAccount(p))
89                }
90            }
91            (_, Ok(d), _, _, _, _) => Ok(AccountType::ContinuousVestingAccount(d)),
92            (_, _, Ok(d), _, _, _) => Ok(AccountType::PeriodicVestingAccount(d)),
93            (_, _, _, Ok(d), _, _) => Ok(AccountType::DelayedVestingAccount(d)),
94            (_, _, _, _, Ok(d), _) => Ok(AccountType::ModuleAccount(d)),
95            (_, _, _, _, _, Ok(d)) => Ok(AccountType::PermenantLockedAccount(d)),
96            (Err(e), _, _, _, _, _) => Err(CosmosGrpcError::DecodeError { error: e }),
97        }
98    }
99}
100
101/// This is a parsed and validated version of the Cosmos base account proto
102/// struct
103#[derive(Serialize, Deserialize, Debug, Clone)]
104pub struct BaseAccount {
105    pub address: Address,
106    /// an unprocessed proto struct containing a pubkey type
107    #[serde(skip_serializing, skip_deserializing)]
108    pub pubkey: Option<Any>,
109    pub account_number: u64,
110    pub sequence: u64,
111}
112
113impl From<ProtoBaseAccount> for BaseAccount {
114    fn from(value: ProtoBaseAccount) -> Self {
115        BaseAccount {
116            address: value.address.parse().unwrap(),
117            pubkey: value.pub_key,
118            account_number: value.account_number,
119            sequence: value.sequence,
120        }
121    }
122}
123
124/// A trait for all Cosmos account types that requires
125/// all types be sized and implement Clone
126pub trait CosmosAccount {
127    fn get_base_account(&self) -> BaseAccount;
128}
129
130// note that the vesting account nested uses gogoproto's embed tag
131// https://github.com/cosmos/cosmos-sdk/blob/master/proto/cosmos/vesting/v1beta1/vesting.proto#L16
132// As noted in the gogoproto docs this enforces that the values are not null https://pkg.go.dev/github.com/gogo/protobuf/gogoproto#pkg-types
133// therefore we unwrap() the options used to represent Go pointers here
134
135impl CosmosAccount for BaseAccount {
136    fn get_base_account(&self) -> BaseAccount {
137        self.clone()
138    }
139}
140
141impl CosmosAccount for ProtoBaseAccount {
142    fn get_base_account(&self) -> BaseAccount {
143        self.clone().into()
144    }
145}
146
147impl CosmosAccount for ContinuousVestingAccount {
148    fn get_base_account(&self) -> BaseAccount {
149        self.base_vesting_account
150            .clone()
151            .unwrap()
152            .base_account
153            .unwrap()
154            .into()
155    }
156}
157
158impl CosmosAccount for DelayedVestingAccount {
159    fn get_base_account(&self) -> BaseAccount {
160        self.base_vesting_account
161            .clone()
162            .unwrap()
163            .base_account
164            .unwrap()
165            .into()
166    }
167}
168
169impl CosmosAccount for PeriodicVestingAccount {
170    fn get_base_account(&self) -> BaseAccount {
171        self.base_vesting_account
172            .clone()
173            .unwrap()
174            .base_account
175            .unwrap()
176            .into()
177    }
178}
179
180impl CosmosAccount for ModuleAccount {
181    fn get_base_account(&self) -> BaseAccount {
182        self.base_account.clone().unwrap().into()
183    }
184}
185
186impl CosmosAccount for PermanentLockedAccount {
187    fn get_base_account(&self) -> BaseAccount {
188        self.base_vesting_account
189            .clone()
190            .unwrap()
191            .base_account
192            .unwrap()
193            .into()
194    }
195}
196
197/// A mirror of the BlockParams struct represents the maximum gas and bytes a block is allowed in the chain
198/// None represents unlimited
199#[derive(Debug, Clone)]
200pub struct BlockParams {
201    pub max_bytes: u64,
202    pub max_gas: Option<u64>,
203}
204
205#[cfg(test)]
206mod tests {}