1use serde::Deserialize;
4
5use super::rpc::{AccessKeyDetails, ValidatorStakeView};
6use super::{AccountId, CryptoHash, NearToken, PublicKey, Signature};
7
8#[derive(Debug, Clone, Deserialize)]
14pub struct EpochValidatorInfo {
15 pub current_validators: Vec<CurrentEpochValidatorInfo>,
17 pub next_validators: Vec<NextEpochValidatorInfo>,
19 #[serde(default)]
21 pub current_fishermen: Vec<ValidatorStakeView>,
22 #[serde(default)]
24 pub next_fishermen: Vec<ValidatorStakeView>,
25 #[serde(default)]
27 pub current_proposals: Vec<ValidatorStakeView>,
28 #[serde(default)]
30 pub prev_epoch_kickout: Vec<ValidatorKickoutView>,
31 pub epoch_start_height: u64,
33 pub epoch_height: u64,
35}
36
37#[derive(Debug, Clone, Deserialize)]
39pub struct CurrentEpochValidatorInfo {
40 pub account_id: AccountId,
42 pub public_key: PublicKey,
44 pub is_slashed: bool,
46 pub stake: NearToken,
48 #[serde(default)]
50 pub shards_produced: Vec<u64>,
51 pub num_produced_blocks: u64,
53 pub num_expected_blocks: u64,
55 #[serde(default)]
57 pub num_produced_chunks: u64,
58 #[serde(default)]
60 pub num_expected_chunks: u64,
61 #[serde(default)]
63 pub num_produced_chunks_per_shard: Vec<u64>,
64 #[serde(default)]
66 pub num_expected_chunks_per_shard: Vec<u64>,
67 #[serde(default)]
69 pub num_produced_endorsements: u64,
70 #[serde(default)]
72 pub num_expected_endorsements: u64,
73 #[serde(default)]
75 pub num_produced_endorsements_per_shard: Vec<u64>,
76 #[serde(default)]
78 pub num_expected_endorsements_per_shard: Vec<u64>,
79 #[serde(default)]
81 pub shards_endorsed: Vec<u64>,
82}
83
84#[derive(Debug, Clone, Deserialize)]
86pub struct NextEpochValidatorInfo {
87 pub account_id: AccountId,
89 pub public_key: PublicKey,
91 pub stake: NearToken,
93 #[serde(default)]
95 pub shards: Vec<u64>,
96}
97
98#[derive(Debug, Clone, Deserialize)]
100pub struct ValidatorKickoutView {
101 pub account_id: AccountId,
103 pub reason: ValidatorKickoutReason,
105}
106
107#[derive(Debug, Clone, Deserialize)]
109pub enum ValidatorKickoutReason {
110 #[serde(rename = "Slashed")]
112 Slashed,
113 NotEnoughBlocks {
115 produced: u64,
117 expected: u64,
119 },
120 NotEnoughChunks {
122 produced: u64,
124 expected: u64,
126 },
127 Unstaked,
129 NotEnoughStake {
131 stake: NearToken,
133 threshold: NearToken,
135 },
136 DidNotGetASeat,
138 NotEnoughChunkEndorsements {
140 produced: u64,
142 expected: u64,
144 },
145 ProtocolVersionTooOld {
147 version: u32,
149 network_version: u32,
151 },
152}
153
154#[derive(Debug, Clone, Deserialize)]
160pub struct LightClientBlockView {
161 pub prev_block_hash: CryptoHash,
163 pub next_block_inner_hash: CryptoHash,
165 pub inner_lite: BlockHeaderInnerLiteView,
167 pub inner_rest_hash: CryptoHash,
169 #[serde(default)]
171 pub next_bps: Option<Vec<ValidatorStakeView>>,
172 #[serde(default)]
174 pub approvals_after_next: Vec<Option<Signature>>,
175}
176
177#[derive(Debug, Clone, Deserialize)]
179pub struct LightClientBlockLiteView {
180 pub prev_block_hash: CryptoHash,
182 pub inner_rest_hash: CryptoHash,
184 pub inner_lite: BlockHeaderInnerLiteView,
186}
187
188#[derive(Debug, Clone, Deserialize)]
190pub struct BlockHeaderInnerLiteView {
191 pub height: u64,
193 pub epoch_id: CryptoHash,
195 pub next_epoch_id: CryptoHash,
197 pub prev_state_root: CryptoHash,
199 pub outcome_root: CryptoHash,
201 pub timestamp: u64,
203 pub timestamp_nanosec: String,
205 pub next_bp_hash: CryptoHash,
207 pub block_merkle_root: CryptoHash,
209}
210
211#[derive(Debug, Clone, Deserialize)]
217pub struct StateChangeWithCauseView {
218 pub cause: StateChangeCauseView,
220 pub value: StateChangeValueView,
222}
223
224#[derive(Debug, Clone, Deserialize)]
226#[serde(rename_all = "snake_case", tag = "type")]
227pub enum StateChangeCauseView {
228 NotWritableToDisk,
230 InitialState,
232 TransactionProcessing {
234 tx_hash: CryptoHash,
236 },
237 ActionReceiptProcessingStarted {
239 receipt_hash: CryptoHash,
241 },
242 ActionReceiptGasReward {
244 receipt_hash: CryptoHash,
246 },
247 ReceiptProcessing {
249 receipt_hash: CryptoHash,
251 },
252 PostponedReceipt {
254 receipt_hash: CryptoHash,
256 },
257 UpdatedDelayedReceipts,
259 ValidatorAccountsUpdate,
261 Migration,
263 BandwidthSchedulerStateUpdate,
265}
266
267#[derive(Debug, Clone, Deserialize)]
269#[serde(rename_all = "snake_case", tag = "type", content = "change")]
270pub enum StateChangeValueView {
271 AccountUpdate {
273 account_id: AccountId,
275 #[serde(flatten)]
277 account: serde_json::Value,
278 },
279 AccountDeletion {
281 account_id: AccountId,
283 },
284 AccessKeyUpdate {
286 account_id: AccountId,
288 public_key: PublicKey,
290 access_key: AccessKeyDetails,
292 },
293 AccessKeyDeletion {
295 account_id: AccountId,
297 public_key: PublicKey,
299 },
300 GasKeyNonceUpdate {
302 account_id: AccountId,
304 public_key: PublicKey,
306 index: u16,
308 nonce: u64,
310 },
311 DataUpdate {
313 account_id: AccountId,
315 #[serde(rename = "key_base64")]
317 key: String,
318 #[serde(rename = "value_base64")]
320 value: String,
321 },
322 DataDeletion {
324 account_id: AccountId,
326 #[serde(rename = "key_base64")]
328 key: String,
329 },
330 ContractCodeUpdate {
332 account_id: AccountId,
334 #[serde(rename = "code_base64")]
336 code: String,
337 },
338 ContractCodeDeletion {
340 account_id: AccountId,
342 },
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348
349 #[test]
350 fn test_gas_key_nonce_update_deserialization() {
351 let json = serde_json::json!({
352 "type": "gas_key_nonce_update",
353 "change": {
354 "account_id": "alice.near",
355 "public_key": "ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp",
356 "index": 3,
357 "nonce": 42
358 }
359 });
360 let change: StateChangeValueView = serde_json::from_value(json).unwrap();
361 match change {
362 StateChangeValueView::GasKeyNonceUpdate {
363 account_id,
364 public_key,
365 index,
366 nonce,
367 } => {
368 assert_eq!(account_id.as_str(), "alice.near");
369 assert!(public_key.to_string().starts_with("ed25519:"));
370 assert_eq!(index, 3);
371 assert_eq!(nonce, 42);
372 }
373 _ => panic!("Expected GasKeyNonceUpdate"),
374 }
375 }
376
377 #[test]
378 fn test_state_change_with_cause_deserialization() {
379 let json = serde_json::json!({
380 "cause": {
381 "type": "transaction_processing",
382 "tx_hash": "9FtHUFBQsZ2MG77K3x3MJ9wjX3UT8zE1TczCrhZEcG8U"
383 },
384 "value": {
385 "type": "gas_key_nonce_update",
386 "change": {
387 "account_id": "alice.near",
388 "public_key": "ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp",
389 "index": 0,
390 "nonce": 100
391 }
392 }
393 });
394 let change: StateChangeWithCauseView = serde_json::from_value(json).unwrap();
395 assert!(matches!(
396 change.value,
397 StateChangeValueView::GasKeyNonceUpdate { .. }
398 ));
399 }
400
401 #[test]
402 fn test_state_change_cause_deserialization() {
403 let json = serde_json::json!({
404 "type": "transaction_processing",
405 "tx_hash": "9FtHUFBQsZ2MG77K3x3MJ9wjX3UT8zE1TczCrhZEcG8U"
406 });
407 let cause: StateChangeCauseView = serde_json::from_value(json).unwrap();
408 assert!(matches!(
409 cause,
410 StateChangeCauseView::TransactionProcessing { .. }
411 ));
412 }
413
414 #[test]
415 fn test_account_update_flattened_deserialization() {
416 let json = serde_json::json!({
417 "type": "account_update",
418 "change": {
419 "account_id": "alice.near",
420 "amount": "1000000000000000000000000",
421 "locked": "0",
422 "code_hash": "11111111111111111111111111111111",
423 "storage_usage": 100,
424 "storage_paid_at": 0
425 }
426 });
427 let change: StateChangeValueView = serde_json::from_value(json).unwrap();
428 match change {
429 StateChangeValueView::AccountUpdate {
430 account_id,
431 account,
432 } => {
433 assert_eq!(account_id.as_str(), "alice.near");
434 assert_eq!(account["amount"], "1000000000000000000000000");
435 assert_eq!(account["storage_usage"], 100);
436 }
437 _ => panic!("expected AccountUpdate"),
438 }
439 }
440
441 #[test]
442 fn test_data_update_base64_field_names() {
443 let json = serde_json::json!({
444 "type": "data_update",
445 "change": {
446 "account_id": "alice.near",
447 "key_base64": "c3RhdGU=",
448 "value_base64": "dGVzdA=="
449 }
450 });
451 let change: StateChangeValueView = serde_json::from_value(json).unwrap();
452 match change {
453 StateChangeValueView::DataUpdate {
454 account_id,
455 key,
456 value,
457 } => {
458 assert_eq!(account_id.as_str(), "alice.near");
459 assert_eq!(key, "c3RhdGU=");
460 assert_eq!(value, "dGVzdA==");
461 }
462 _ => panic!("expected DataUpdate"),
463 }
464 }
465
466 #[test]
467 fn test_data_deletion_base64_field_name() {
468 let json = serde_json::json!({
469 "type": "data_deletion",
470 "change": {
471 "account_id": "alice.near",
472 "key_base64": "c3RhdGU="
473 }
474 });
475 let change: StateChangeValueView = serde_json::from_value(json).unwrap();
476 assert!(matches!(change, StateChangeValueView::DataDeletion { .. }));
477 }
478
479 #[test]
480 fn test_contract_code_update_base64_field_name() {
481 let json = serde_json::json!({
482 "type": "contract_code_update",
483 "change": {
484 "account_id": "alice.near",
485 "code_base64": "AGFzbQEAAAA="
486 }
487 });
488 let change: StateChangeValueView = serde_json::from_value(json).unwrap();
489 match change {
490 StateChangeValueView::ContractCodeUpdate {
491 account_id, code, ..
492 } => {
493 assert_eq!(account_id.as_str(), "alice.near");
494 assert_eq!(code, "AGFzbQEAAAA=");
495 }
496 _ => panic!("expected ContractCodeUpdate"),
497 }
498 }
499}