casper_storage/data_access_layer/
key_prefix.rs

1use casper_types::{
2    account::AccountHash,
3    bytesrepr::{self, FromBytes, ToBytes, U8_SERIALIZED_LENGTH},
4    contract_messages::TopicNameHash,
5    system::{auction::BidAddrTag, mint::BalanceHoldAddrTag},
6    EntityAddr, KeyTag, URefAddr,
7};
8
9/// Key prefixes used for querying the global state.
10#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
11pub enum KeyPrefix {
12    /// Retrieves all delegator bid addresses for a given validator.
13    DelegatorBidAddrsByValidator(AccountHash),
14    /// Retrieves all entries for a given hash addr.
15    MessageEntriesByEntity(EntityAddr),
16    /// Retrieves all messages for a given hash addr and topic.
17    MessagesByEntityAndTopic(EntityAddr, TopicNameHash),
18    /// Retrieves all named keys for a given entity.
19    NamedKeysByEntity(EntityAddr),
20    /// Retrieves all gas balance holds for a given purse.
21    GasBalanceHoldsByPurse(URefAddr),
22    /// Retrieves all processing balance holds for a given purse.
23    ProcessingBalanceHoldsByPurse(URefAddr),
24    /// Retrieves all V1 entry points for a given entity.
25    EntryPointsV1ByEntity(EntityAddr),
26    /// Retrieves all V2 entry points for a given entity.
27    EntryPointsV2ByEntity(EntityAddr),
28}
29
30impl ToBytes for KeyPrefix {
31    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
32        let mut result = bytesrepr::unchecked_allocate_buffer(self);
33        self.write_bytes(&mut result)?;
34        Ok(result)
35    }
36
37    fn serialized_length(&self) -> usize {
38        U8_SERIALIZED_LENGTH
39            + match self {
40                KeyPrefix::DelegatorBidAddrsByValidator(validator) => {
41                    U8_SERIALIZED_LENGTH + validator.serialized_length()
42                }
43                KeyPrefix::MessageEntriesByEntity(hash_addr) => hash_addr.serialized_length(),
44                KeyPrefix::MessagesByEntityAndTopic(hash_addr, topic) => {
45                    hash_addr.serialized_length() + topic.serialized_length()
46                }
47                KeyPrefix::NamedKeysByEntity(entity) => entity.serialized_length(),
48                KeyPrefix::GasBalanceHoldsByPurse(uref) => {
49                    U8_SERIALIZED_LENGTH + uref.serialized_length()
50                }
51                KeyPrefix::ProcessingBalanceHoldsByPurse(uref) => {
52                    U8_SERIALIZED_LENGTH + uref.serialized_length()
53                }
54                KeyPrefix::EntryPointsV1ByEntity(entity) => {
55                    U8_SERIALIZED_LENGTH + entity.serialized_length()
56                }
57                KeyPrefix::EntryPointsV2ByEntity(entity) => {
58                    U8_SERIALIZED_LENGTH + entity.serialized_length()
59                }
60            }
61    }
62
63    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
64        match self {
65            KeyPrefix::DelegatorBidAddrsByValidator(validator) => {
66                writer.push(KeyTag::BidAddr as u8);
67                writer.push(BidAddrTag::DelegatedAccount as u8);
68                validator.write_bytes(writer)?;
69            }
70            KeyPrefix::MessageEntriesByEntity(hash_addr) => {
71                writer.push(KeyTag::Message as u8);
72                hash_addr.write_bytes(writer)?;
73            }
74            KeyPrefix::MessagesByEntityAndTopic(hash_addr, topic) => {
75                writer.push(KeyTag::Message as u8);
76                hash_addr.write_bytes(writer)?;
77                topic.write_bytes(writer)?;
78            }
79            KeyPrefix::NamedKeysByEntity(entity) => {
80                writer.push(KeyTag::NamedKey as u8);
81                entity.write_bytes(writer)?;
82            }
83            KeyPrefix::GasBalanceHoldsByPurse(uref) => {
84                writer.push(KeyTag::BalanceHold as u8);
85                writer.push(BalanceHoldAddrTag::Gas as u8);
86                uref.write_bytes(writer)?;
87            }
88            KeyPrefix::ProcessingBalanceHoldsByPurse(uref) => {
89                writer.push(KeyTag::BalanceHold as u8);
90                writer.push(BalanceHoldAddrTag::Processing as u8);
91                uref.write_bytes(writer)?;
92            }
93            KeyPrefix::EntryPointsV1ByEntity(entity) => {
94                writer.push(KeyTag::EntryPoint as u8);
95                writer.push(0);
96                entity.write_bytes(writer)?;
97            }
98            KeyPrefix::EntryPointsV2ByEntity(entity) => {
99                writer.push(KeyTag::EntryPoint as u8);
100                writer.push(1);
101                entity.write_bytes(writer)?;
102            }
103        }
104        Ok(())
105    }
106}
107
108impl FromBytes for KeyPrefix {
109    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
110        let (tag, remainder) = u8::from_bytes(bytes)?;
111        let result = match tag {
112            tag if tag == KeyTag::BidAddr as u8 => {
113                let (bid_addr_tag, remainder) = u8::from_bytes(remainder)?;
114                match bid_addr_tag {
115                    tag if tag == BidAddrTag::DelegatedAccount as u8 => {
116                        let (validator, remainder) = AccountHash::from_bytes(remainder)?;
117                        (
118                            KeyPrefix::DelegatorBidAddrsByValidator(validator),
119                            remainder,
120                        )
121                    }
122                    _ => return Err(bytesrepr::Error::Formatting),
123                }
124            }
125            tag if tag == KeyTag::Message as u8 => {
126                let (hash_addr, remainder) = EntityAddr::from_bytes(remainder)?;
127                if remainder.is_empty() {
128                    (KeyPrefix::MessageEntriesByEntity(hash_addr), remainder)
129                } else {
130                    let (topic, remainder) = TopicNameHash::from_bytes(remainder)?;
131                    (
132                        KeyPrefix::MessagesByEntityAndTopic(hash_addr, topic),
133                        remainder,
134                    )
135                }
136            }
137            tag if tag == KeyTag::NamedKey as u8 => {
138                let (entity, remainder) = EntityAddr::from_bytes(remainder)?;
139                (KeyPrefix::NamedKeysByEntity(entity), remainder)
140            }
141            tag if tag == KeyTag::BalanceHold as u8 => {
142                let (balance_hold_addr_tag, remainder) = u8::from_bytes(remainder)?;
143                let (uref, remainder) = URefAddr::from_bytes(remainder)?;
144                match balance_hold_addr_tag {
145                    tag if tag == BalanceHoldAddrTag::Gas as u8 => {
146                        (KeyPrefix::GasBalanceHoldsByPurse(uref), remainder)
147                    }
148                    tag if tag == BalanceHoldAddrTag::Processing as u8 => {
149                        (KeyPrefix::ProcessingBalanceHoldsByPurse(uref), remainder)
150                    }
151                    _ => return Err(bytesrepr::Error::Formatting),
152                }
153            }
154            tag if tag == KeyTag::EntryPoint as u8 => {
155                let (entry_point_type, remainder) = u8::from_bytes(remainder)?;
156                let (entity, remainder) = EntityAddr::from_bytes(remainder)?;
157                match entry_point_type {
158                    0 => (KeyPrefix::EntryPointsV1ByEntity(entity), remainder),
159                    1 => (KeyPrefix::EntryPointsV2ByEntity(entity), remainder),
160                    _ => return Err(bytesrepr::Error::Formatting),
161                }
162            }
163            _ => return Err(bytesrepr::Error::Formatting),
164        };
165        Ok(result)
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use casper_types::testing::TestRng;
172    use rand::Rng;
173
174    use casper_types::{
175        addressable_entity::NamedKeyAddr,
176        contract_messages::MessageAddr,
177        gens::{account_hash_arb, entity_addr_arb, topic_name_hash_arb, u8_slice_32},
178        system::{auction::BidAddr, mint::BalanceHoldAddr},
179        BlockTime, EntryPointAddr, Key,
180    };
181
182    use super::*;
183    use proptest::prelude::*;
184
185    pub fn key_prefix_arb() -> impl Strategy<Value = KeyPrefix> {
186        prop_oneof![
187            account_hash_arb().prop_map(KeyPrefix::DelegatorBidAddrsByValidator),
188            entity_addr_arb().prop_map(KeyPrefix::MessageEntriesByEntity),
189            (entity_addr_arb(), topic_name_hash_arb()).prop_map(|(entity_addr, topic)| {
190                KeyPrefix::MessagesByEntityAndTopic(entity_addr, topic)
191            }),
192            entity_addr_arb().prop_map(KeyPrefix::NamedKeysByEntity),
193            u8_slice_32().prop_map(KeyPrefix::GasBalanceHoldsByPurse),
194            u8_slice_32().prop_map(KeyPrefix::ProcessingBalanceHoldsByPurse),
195            entity_addr_arb().prop_map(KeyPrefix::EntryPointsV1ByEntity),
196            entity_addr_arb().prop_map(KeyPrefix::EntryPointsV2ByEntity),
197        ]
198    }
199
200    proptest! {
201        #[test]
202        fn bytesrepr_roundtrip(key_prefix in key_prefix_arb()) {
203            bytesrepr::test_serialization_roundtrip(&key_prefix);
204        }
205    }
206
207    #[test]
208    fn key_serializer_compat() {
209        // This test ensures that the `KeyPrefix` deserializer is compatible with the `Key`
210        // serializer. Combined with the `bytesrepr_roundtrip` test, this ensures that
211        // `KeyPrefix` is binary compatible with `Key`.
212
213        let rng = &mut TestRng::new();
214
215        let hash1 = rng.gen();
216        let hash2 = rng.gen();
217
218        for (key, prefix) in [
219            (
220                Key::BidAddr(BidAddr::new_delegator_account_addr((hash1, hash2))),
221                KeyPrefix::DelegatorBidAddrsByValidator(AccountHash::new(hash1)),
222            ),
223            (
224                Key::Message(MessageAddr::new_message_addr(
225                    EntityAddr::SmartContract(hash1),
226                    TopicNameHash::new(hash2),
227                    0,
228                )),
229                KeyPrefix::MessagesByEntityAndTopic(
230                    EntityAddr::SmartContract(hash1),
231                    TopicNameHash::new(hash2),
232                ),
233            ),
234            (
235                Key::NamedKey(NamedKeyAddr::new_named_key_entry(
236                    EntityAddr::Account(hash1),
237                    hash2,
238                )),
239                KeyPrefix::NamedKeysByEntity(EntityAddr::Account(hash1)),
240            ),
241            (
242                Key::BalanceHold(BalanceHoldAddr::new_gas(hash1, BlockTime::new(0))),
243                KeyPrefix::GasBalanceHoldsByPurse(hash1),
244            ),
245            (
246                Key::BalanceHold(BalanceHoldAddr::new_processing(hash1, BlockTime::new(0))),
247                KeyPrefix::ProcessingBalanceHoldsByPurse(hash1),
248            ),
249            (
250                Key::EntryPoint(
251                    EntryPointAddr::new_v1_entry_point_addr(EntityAddr::Account(hash1), "name")
252                        .expect("should create entry point"),
253                ),
254                KeyPrefix::EntryPointsV1ByEntity(EntityAddr::Account(hash1)),
255            ),
256        ] {
257            let key_bytes = key.to_bytes().expect("should serialize key");
258            let (parsed_key_prefix, remainder) =
259                KeyPrefix::from_bytes(&key_bytes).expect("should deserialize key prefix");
260            assert_eq!(parsed_key_prefix, prefix, "key: {:?}", key);
261            assert!(!remainder.is_empty(), "key: {:?}", key);
262        }
263    }
264}