Skip to main content

near_primitives/spice/
partial_data.rs

1use crate::merkle::MerklePath;
2use crate::types::StaticSignatureDifferentiator;
3use crate::validator_signer::ValidatorSigner;
4use near_crypto::{PublicKey, Signature};
5use near_primitives_core::hash::CryptoHash;
6use near_primitives_core::types::{AccountId, MerkleHash, ShardId};
7
8#[derive(borsh::BorshSerialize, borsh::BorshDeserialize, Debug, Clone, PartialEq, Eq, Hash)]
9pub enum SpiceDataIdentifier {
10    ReceiptProof { block_hash: CryptoHash, from_shard_id: ShardId, to_shard_id: ShardId },
11    Witness { block_hash: CryptoHash, shard_id: ShardId },
12}
13
14impl SpiceDataIdentifier {
15    pub fn block_hash(&self) -> &CryptoHash {
16        match self {
17            SpiceDataIdentifier::ReceiptProof { block_hash, .. } => block_hash,
18            SpiceDataIdentifier::Witness { block_hash, .. } => block_hash,
19        }
20    }
21}
22
23#[derive(borsh::BorshSerialize, borsh::BorshDeserialize, Debug, Clone, PartialEq, Eq, Hash)]
24pub struct SpiceDataCommitment {
25    pub hash: CryptoHash,
26    pub root: MerkleHash,
27    pub encoded_length: u64,
28}
29
30#[derive(borsh::BorshSerialize, borsh::BorshDeserialize, Debug, Clone, PartialEq, Eq)]
31pub struct SpiceDataPart {
32    pub part_ord: u64,
33    pub part: Box<[u8]>,
34    pub merkle_proof: MerklePath,
35}
36
37// TODO(spice): Version this struct since it is sent over the network.
38/// Partial data for spice with unverified signature.
39#[derive(borsh::BorshSerialize, borsh::BorshDeserialize, Debug, Clone, PartialEq, Eq)]
40pub struct SpicePartialData {
41    inner: SpicePartialDataInner,
42    sender: AccountId,
43    signature: Signature,
44}
45
46impl SpicePartialData {
47    pub fn new(
48        id: SpiceDataIdentifier,
49        commitment: SpiceDataCommitment,
50        parts: Vec<SpiceDataPart>,
51        signer: &ValidatorSigner,
52    ) -> Self {
53        let inner = SpicePartialDataInner { id, commitment, parts };
54        let signature = signer.sign_bytes(&inner.serialize_for_signing());
55        Self { inner, signature, sender: signer.validator_id().clone() }
56    }
57
58    pub fn block_hash(&self) -> &CryptoHash {
59        self.inner.id.block_hash()
60    }
61
62    pub fn sender(&self) -> &AccountId {
63        &self.sender
64    }
65
66    pub fn into_verified(self, public_key: &PublicKey) -> Option<SpiceVerifiedPartialData> {
67        let data = self.inner.serialize_for_signing();
68        if !self.signature.verify(&data, public_key) {
69            return None;
70        }
71        Some(SpiceVerifiedPartialData {
72            id: self.inner.id,
73            commitment: self.inner.commitment,
74            parts: self.inner.parts,
75            sender: self.sender,
76        })
77    }
78}
79
80#[derive(borsh::BorshSerialize, borsh::BorshDeserialize, Debug, Clone, PartialEq, Eq)]
81struct SpicePartialDataInner {
82    // We include id to allow finding recipients and producers when receiving the data.
83    id: SpiceDataIdentifier,
84    commitment: SpiceDataCommitment,
85    parts: Vec<SpiceDataPart>,
86}
87
88impl SpicePartialDataInner {
89    fn serialize_for_signing(&self) -> Vec<u8> {
90        static SIGNATURE_DIFFERENTIATOR: StaticSignatureDifferentiator = "SpicePartialData";
91        let data = (self, SIGNATURE_DIFFERENTIATOR);
92        borsh::to_vec(&data).unwrap()
93    }
94}
95
96/// Spice partial data with verified signature.
97#[derive(borsh::BorshSerialize, borsh::BorshDeserialize, Debug, Clone, PartialEq, Eq)]
98pub struct SpiceVerifiedPartialData {
99    pub id: SpiceDataIdentifier,
100    pub commitment: SpiceDataCommitment,
101    pub parts: Vec<SpiceDataPart>,
102    pub sender: AccountId,
103}
104
105// Outside of tests it should be impossible to create spice partial data with invalid signature.
106pub fn testonly_create_spice_partial_data(
107    id: SpiceDataIdentifier,
108    commitment: SpiceDataCommitment,
109    parts: Vec<SpiceDataPart>,
110    signature: Signature,
111    sender: AccountId,
112) -> SpicePartialData {
113    SpicePartialData { inner: SpicePartialDataInner { id, commitment, parts }, signature, sender }
114}