1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
use std::{
collections::HashMap,
fmt::Write,
sync::{Arc, Mutex},
};
use solana_idl::Idl;
use crate::{
errors::{ChainsawError, ChainsawResult},
json::{
JsonIdlTypeDefinitionDeserializer, JsonSerializationOpts,
JsonTypeDefinitionDeserializerMap,
},
utils::{account_discriminator, DiscriminatorBytes},
IdlProvider,
};
/// Setup to deserialize accounts for a given program. The accounts are expected to have been
/// serialized using the [borsh] format.
///
/// Uses deserializers defined inside [deserializer] modules under the hood in order to resolve the
/// appropriate [borsh] deserializers for each field.
pub struct JsonAccountsDeserializer<'opts> {
/// The parsed [Idl] of the program
pub idl: Idl,
/// The [IdlProvider] of the program
pub provider: IdlProvider,
/// The deserializers for accounts of this program keyed by the discriminator of each account
/// type.
pub account_deserializers:
HashMap<DiscriminatorBytes, JsonIdlTypeDefinitionDeserializer<'opts>>,
/// Allows looking up a discriminator for an account by name.
pub account_discriminators: HashMap<String, DiscriminatorBytes>,
/// Allows looking up a account names by discriminator.
pub account_names: HashMap<DiscriminatorBytes, String>,
/// Map of defined type deserializers. Defined types are be nested inside accounts.
pub type_map: JsonTypeDefinitionDeserializerMap<'opts>,
/// The [JsonSerializationOpts] specifying how specific data types should be deserialized.
pub serialization_opts: &'opts JsonSerializationOpts,
}
impl<'opts> JsonAccountsDeserializer<'opts> {
/// Tries to create an [AccounbtDeserializer] by parsing the [Idl].
/// Fails if the IDL could not be parsed.
///
/// - [json} the IDL definition in JSON format
/// - [provider] the provider used to create the IDL
/// - [serialization_opts] specifying how specific data types should be deserialized.
pub fn try_from_idl(
json: &str,
provider: IdlProvider,
serialization_opts: &'opts JsonSerializationOpts,
) -> ChainsawResult<Self> {
let idl: Idl = serde_json::from_str(json)?;
Ok(Self::from_idl(idl, provider, serialization_opts))
}
/// Creates an [AccounbtDeserializer] from the provided [Idl]
/// Fails if the IDL could not be parsed.
///
/// - [idl} the IDL definition
/// - [provider] the provider used to create the IDL
/// - [serialization_opts] specifying how specific data types should be deserialized.
pub fn from_idl(
idl: Idl,
provider: IdlProvider,
serialization_opts: &'opts JsonSerializationOpts,
) -> Self {
let mut account_deserializers = HashMap::new();
let mut account_discriminators = HashMap::new();
let type_map = Arc::new(Mutex::new(HashMap::new()));
for type_definition in &idl.types {
let instance = JsonIdlTypeDefinitionDeserializer::new(
type_definition,
type_map.clone(),
serialization_opts,
);
type_map
.lock()
.unwrap()
.insert(instance.name.clone(), instance);
}
for type_definition in &idl.accounts {
let type_deserializer =
JsonIdlTypeDefinitionDeserializer::<'opts>::new(
type_definition,
type_map.clone(),
serialization_opts,
);
// NOTE: for now we assume that one account doesn't reference another
// thus we don't include it in the lookup map for nested types
// Similarly for instruction args once we support them
let discriminator = account_discriminator(&type_definition.name);
account_deserializers.insert(discriminator, type_deserializer);
// We expect less accounts to be looked up by name and are fine with an extra
// lookup instead of keeping another copy of the deserializers by name directly
account_discriminators
.insert(type_definition.name.clone(), discriminator);
}
let account_names = account_discriminators
.iter()
.map(|(name, discriminator)| (*discriminator, name.clone()))
.collect();
Self {
idl,
provider,
type_map,
serialization_opts,
account_deserializers,
account_discriminators,
account_names,
}
}
/// Deserializes an account from the provided data.
///
/// This is the common way of resolving an account json and using it to deserialize
/// account data.
/// It expects the first 8 bytes of data to hold the account discriminator as is the case for
/// anchor accounts.
/// For all other accounts use [deserialize_account_data_by_name] instead.
pub fn deserialize_account_data<W: Write>(
&self,
account_data: &mut &[u8],
f: &mut W,
) -> ChainsawResult<()> {
let discriminator = &account_data[..8];
let deserializer = self
.account_deserializers
.get(discriminator)
.ok_or_else(|| {
ChainsawError::UnknownDiscriminatedAccount(format!(
"disciminator: {:?}",
discriminator
))
})?;
let data = &mut &account_data[8..];
deserializer.deserialize(f, data)
}
/// Deserializes an account from the provided data.
///
/// This method expects account data to **not** be prefixed with 8 bytes of discriminator data.
/// Instead it derives that discriminator from the provided account name and then looks up the
/// json.
pub fn deserialize_account_data_by_name<W: Write>(
&self,
account_data: &mut &[u8],
account_name: &str,
f: &mut W,
) -> ChainsawResult<()> {
let discriminator = account_discriminator(account_name);
let deserializer =
self.account_deserializers.get(&discriminator).ok_or_else(
|| ChainsawError::UnknownAccount(account_name.to_string()),
)?;
deserializer.deserialize(f, account_data)
}
/// Resolves the account name for the provided discriminator.
pub fn account_name(
&self,
discriminator: &DiscriminatorBytes,
) -> Option<&str> {
self.account_names.get(discriminator).map(|s| s.as_str())
}
}