use serde::Deserialize;
use super::rpc::{AccessKeyDetails, ValidatorStakeView};
use super::{AccountId, CryptoHash, NearToken, PublicKey, Signature};
#[derive(Debug, Clone, Deserialize)]
pub struct EpochValidatorInfo {
pub current_validators: Vec<CurrentEpochValidatorInfo>,
pub next_validators: Vec<NextEpochValidatorInfo>,
#[serde(default)]
pub current_fishermen: Vec<ValidatorStakeView>,
#[serde(default)]
pub next_fishermen: Vec<ValidatorStakeView>,
#[serde(default)]
pub current_proposals: Vec<ValidatorStakeView>,
#[serde(default)]
pub prev_epoch_kickout: Vec<ValidatorKickoutView>,
pub epoch_start_height: u64,
pub epoch_height: u64,
}
#[derive(Debug, Clone, Deserialize)]
pub struct CurrentEpochValidatorInfo {
pub account_id: AccountId,
pub public_key: PublicKey,
pub is_slashed: bool,
pub stake: NearToken,
#[serde(default)]
pub shards_produced: Vec<u64>,
pub num_produced_blocks: u64,
pub num_expected_blocks: u64,
#[serde(default)]
pub num_produced_chunks: u64,
#[serde(default)]
pub num_expected_chunks: u64,
#[serde(default)]
pub num_produced_chunks_per_shard: Vec<u64>,
#[serde(default)]
pub num_expected_chunks_per_shard: Vec<u64>,
#[serde(default)]
pub num_produced_endorsements: u64,
#[serde(default)]
pub num_expected_endorsements: u64,
#[serde(default)]
pub num_produced_endorsements_per_shard: Vec<u64>,
#[serde(default)]
pub num_expected_endorsements_per_shard: Vec<u64>,
#[serde(default)]
pub shards_endorsed: Vec<u64>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct NextEpochValidatorInfo {
pub account_id: AccountId,
pub public_key: PublicKey,
pub stake: NearToken,
#[serde(default)]
pub shards: Vec<u64>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ValidatorKickoutView {
pub account_id: AccountId,
pub reason: ValidatorKickoutReason,
}
#[derive(Debug, Clone, Deserialize)]
pub enum ValidatorKickoutReason {
#[serde(rename = "Slashed")]
Slashed,
NotEnoughBlocks {
produced: u64,
expected: u64,
},
NotEnoughChunks {
produced: u64,
expected: u64,
},
Unstaked,
NotEnoughStake {
stake: NearToken,
threshold: NearToken,
},
DidNotGetASeat,
NotEnoughChunkEndorsements {
produced: u64,
expected: u64,
},
ProtocolVersionTooOld {
version: u32,
network_version: u32,
},
}
#[derive(Debug, Clone, Deserialize)]
pub struct LightClientBlockView {
pub prev_block_hash: CryptoHash,
pub next_block_inner_hash: CryptoHash,
pub inner_lite: BlockHeaderInnerLiteView,
pub inner_rest_hash: CryptoHash,
#[serde(default)]
pub next_bps: Option<Vec<ValidatorStakeView>>,
#[serde(default)]
pub approvals_after_next: Vec<Option<Signature>>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct LightClientBlockLiteView {
pub prev_block_hash: CryptoHash,
pub inner_rest_hash: CryptoHash,
pub inner_lite: BlockHeaderInnerLiteView,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BlockHeaderInnerLiteView {
pub height: u64,
pub epoch_id: CryptoHash,
pub next_epoch_id: CryptoHash,
pub prev_state_root: CryptoHash,
pub outcome_root: CryptoHash,
pub timestamp: u64,
pub timestamp_nanosec: String,
pub next_bp_hash: CryptoHash,
pub block_merkle_root: CryptoHash,
}
#[derive(Debug, Clone, Deserialize)]
pub struct StateChangeWithCauseView {
pub cause: StateChangeCauseView,
pub value: StateChangeValueView,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum StateChangeCauseView {
NotWritableToDisk,
InitialState,
TransactionProcessing {
tx_hash: CryptoHash,
},
ActionReceiptProcessingStarted {
receipt_hash: CryptoHash,
},
ActionReceiptGasReward {
receipt_hash: CryptoHash,
},
ReceiptProcessing {
receipt_hash: CryptoHash,
},
PostponedReceipt {
receipt_hash: CryptoHash,
},
UpdatedDelayedReceipts,
ValidatorAccountsUpdate,
Migration,
BandwidthSchedulerStateUpdate,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type", content = "change")]
pub enum StateChangeValueView {
AccountUpdate {
account_id: AccountId,
#[serde(flatten)]
account: serde_json::Value,
},
AccountDeletion {
account_id: AccountId,
},
AccessKeyUpdate {
account_id: AccountId,
public_key: PublicKey,
access_key: AccessKeyDetails,
},
AccessKeyDeletion {
account_id: AccountId,
public_key: PublicKey,
},
GasKeyNonceUpdate {
account_id: AccountId,
public_key: PublicKey,
index: u16,
nonce: u64,
},
DataUpdate {
account_id: AccountId,
#[serde(rename = "key_base64")]
key: String,
#[serde(rename = "value_base64")]
value: String,
},
DataDeletion {
account_id: AccountId,
#[serde(rename = "key_base64")]
key: String,
},
ContractCodeUpdate {
account_id: AccountId,
#[serde(rename = "code_base64")]
code: String,
},
ContractCodeDeletion {
account_id: AccountId,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gas_key_nonce_update_deserialization() {
let json = serde_json::json!({
"type": "gas_key_nonce_update",
"change": {
"account_id": "alice.near",
"public_key": "ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp",
"index": 3,
"nonce": 42
}
});
let change: StateChangeValueView = serde_json::from_value(json).unwrap();
match change {
StateChangeValueView::GasKeyNonceUpdate {
account_id,
public_key,
index,
nonce,
} => {
assert_eq!(account_id.as_str(), "alice.near");
assert!(public_key.to_string().starts_with("ed25519:"));
assert_eq!(index, 3);
assert_eq!(nonce, 42);
}
_ => panic!("Expected GasKeyNonceUpdate"),
}
}
#[test]
fn test_state_change_with_cause_deserialization() {
let json = serde_json::json!({
"cause": {
"type": "transaction_processing",
"tx_hash": "9FtHUFBQsZ2MG77K3x3MJ9wjX3UT8zE1TczCrhZEcG8U"
},
"value": {
"type": "gas_key_nonce_update",
"change": {
"account_id": "alice.near",
"public_key": "ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp",
"index": 0,
"nonce": 100
}
}
});
let change: StateChangeWithCauseView = serde_json::from_value(json).unwrap();
assert!(matches!(
change.value,
StateChangeValueView::GasKeyNonceUpdate { .. }
));
}
#[test]
fn test_state_change_cause_deserialization() {
let json = serde_json::json!({
"type": "transaction_processing",
"tx_hash": "9FtHUFBQsZ2MG77K3x3MJ9wjX3UT8zE1TczCrhZEcG8U"
});
let cause: StateChangeCauseView = serde_json::from_value(json).unwrap();
assert!(matches!(
cause,
StateChangeCauseView::TransactionProcessing { .. }
));
}
#[test]
fn test_account_update_flattened_deserialization() {
let json = serde_json::json!({
"type": "account_update",
"change": {
"account_id": "alice.near",
"amount": "1000000000000000000000000",
"locked": "0",
"code_hash": "11111111111111111111111111111111",
"storage_usage": 100,
"storage_paid_at": 0
}
});
let change: StateChangeValueView = serde_json::from_value(json).unwrap();
match change {
StateChangeValueView::AccountUpdate {
account_id,
account,
} => {
assert_eq!(account_id.as_str(), "alice.near");
assert_eq!(account["amount"], "1000000000000000000000000");
assert_eq!(account["storage_usage"], 100);
}
_ => panic!("expected AccountUpdate"),
}
}
#[test]
fn test_data_update_base64_field_names() {
let json = serde_json::json!({
"type": "data_update",
"change": {
"account_id": "alice.near",
"key_base64": "c3RhdGU=",
"value_base64": "dGVzdA=="
}
});
let change: StateChangeValueView = serde_json::from_value(json).unwrap();
match change {
StateChangeValueView::DataUpdate {
account_id,
key,
value,
} => {
assert_eq!(account_id.as_str(), "alice.near");
assert_eq!(key, "c3RhdGU=");
assert_eq!(value, "dGVzdA==");
}
_ => panic!("expected DataUpdate"),
}
}
#[test]
fn test_data_deletion_base64_field_name() {
let json = serde_json::json!({
"type": "data_deletion",
"change": {
"account_id": "alice.near",
"key_base64": "c3RhdGU="
}
});
let change: StateChangeValueView = serde_json::from_value(json).unwrap();
assert!(matches!(change, StateChangeValueView::DataDeletion { .. }));
}
#[test]
fn test_contract_code_update_base64_field_name() {
let json = serde_json::json!({
"type": "contract_code_update",
"change": {
"account_id": "alice.near",
"code_base64": "AGFzbQEAAAA="
}
});
let change: StateChangeValueView = serde_json::from_value(json).unwrap();
match change {
StateChangeValueView::ContractCodeUpdate {
account_id, code, ..
} => {
assert_eq!(account_id.as_str(), "alice.near");
assert_eq!(code, "AGFzbQEAAAA=");
}
_ => panic!("expected ContractCodeUpdate"),
}
}
}