near_primitives/stateless_validation/
contract_distribution.rs

1use std::collections::HashSet;
2use std::sync::Arc;
3
4use borsh::{BorshDeserialize, BorshSerialize};
5use bytesize::ByteSize;
6use near_crypto::{PublicKey, Signature};
7use near_primitives_core::code::ContractCode;
8use near_primitives_core::hash::{CryptoHash, hash};
9use near_primitives_core::types::{AccountId, ShardId};
10use near_schema_checker_lib::ProtocolSchema;
11
12use super::ChunkProductionKey;
13#[cfg(feature = "solomon")]
14use crate::reed_solomon::{ReedSolomonEncoderDeserialize, ReedSolomonEncoderSerialize};
15use crate::types::SignatureDifferentiator;
16use crate::{utils::compression::CompressedData, validator_signer::ValidatorSigner};
17
18// Data structures for chunk producers to send accessed contracts to chunk validators.
19
20/// Contains contracts (as code-hashes) accessed during the application of a chunk.
21/// This is used by the chunk producer to let the chunk validators know about which contracts
22/// are needed for validating a witness, so that the chunk validators can request missing code.
23#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
24pub enum ChunkContractAccesses {
25    V1(ChunkContractAccessesV1),
26}
27
28/// Contains information necessary to identify StateTransitionData in the storage.
29#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
30pub struct MainTransitionKey {
31    pub block_hash: CryptoHash,
32    pub shard_id: ShardId,
33}
34
35impl ChunkContractAccesses {
36    pub fn new(
37        next_chunk: ChunkProductionKey,
38        contracts: HashSet<CodeHash>,
39        main_transition: MainTransitionKey,
40        signer: &ValidatorSigner,
41    ) -> Self {
42        Self::V1(ChunkContractAccessesV1::new(next_chunk, contracts, main_transition, signer))
43    }
44
45    pub fn contracts(&self) -> &[CodeHash] {
46        match self {
47            Self::V1(accesses) => &accesses.inner.contracts,
48        }
49    }
50
51    pub fn chunk_production_key(&self) -> &ChunkProductionKey {
52        match self {
53            Self::V1(accesses) => &accesses.inner.next_chunk,
54        }
55    }
56
57    pub fn main_transition(&self) -> &MainTransitionKey {
58        match self {
59            Self::V1(accesses) => &accesses.inner.main_transition,
60        }
61    }
62
63    pub fn verify_signature(&self, public_key: &PublicKey) -> bool {
64        match self {
65            Self::V1(accesses) => accesses.verify_signature(public_key),
66        }
67    }
68}
69
70#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
71pub struct ChunkContractAccessesV1 {
72    inner: ChunkContractAccessesInner,
73    /// Signature of the inner, signed by the chunk producer of the next chunk.
74    signature: Signature,
75}
76
77impl ChunkContractAccessesV1 {
78    fn new(
79        next_chunk: ChunkProductionKey,
80        contracts: HashSet<CodeHash>,
81        main_transition: MainTransitionKey,
82        signer: &ValidatorSigner,
83    ) -> Self {
84        let inner = ChunkContractAccessesInner::new(next_chunk, contracts, main_transition);
85        let signature = signer.sign_bytes(&borsh::to_vec(&inner).unwrap());
86        Self { inner, signature }
87    }
88
89    fn verify_signature(&self, public_key: &PublicKey) -> bool {
90        self.signature.verify(&borsh::to_vec(&self.inner).unwrap(), public_key)
91    }
92}
93
94#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
95pub struct ChunkContractAccessesInner {
96    /// Production metadata of the chunk created after the chunk the accesses belong to.
97    /// We associate this message with the next-chunk info because this message is generated
98    /// and distributed while generating the state-witness of the next chunk
99    /// (by the chunk producer of the next chunk).
100    next_chunk: ChunkProductionKey,
101    /// List of code-hashes for the contracts accessed.
102    contracts: Vec<CodeHash>,
103    /// Corresponds to the StateTransitionData where the contracts were accessed.
104    main_transition: MainTransitionKey,
105    signature_differentiator: SignatureDifferentiator,
106}
107
108impl ChunkContractAccessesInner {
109    fn new(
110        next_chunk: ChunkProductionKey,
111        contracts: HashSet<CodeHash>,
112        main_transition: MainTransitionKey,
113    ) -> Self {
114        Self {
115            next_chunk,
116            contracts: contracts.into_iter().collect(),
117            main_transition,
118            signature_differentiator: "ChunkContractAccessesInner".to_owned(),
119        }
120    }
121}
122
123// Data structures for chunk validators to request contract code from chunk producers.
124
125/// Message to request missing code for a set of contracts.
126/// The contracts are identified by the hash of their code.
127#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
128pub enum ContractCodeRequest {
129    V1(ContractCodeRequestV1),
130}
131
132impl ContractCodeRequest {
133    pub fn new(
134        next_chunk: ChunkProductionKey,
135        contracts: HashSet<CodeHash>,
136        main_transition: MainTransitionKey,
137        signer: &ValidatorSigner,
138    ) -> Self {
139        Self::V1(ContractCodeRequestV1::new(next_chunk, contracts, main_transition, signer))
140    }
141
142    pub fn requester(&self) -> &AccountId {
143        match self {
144            Self::V1(request) => &request.inner.requester,
145        }
146    }
147
148    pub fn contracts(&self) -> &[CodeHash] {
149        match self {
150            Self::V1(request) => &request.inner.contracts,
151        }
152    }
153
154    pub fn chunk_production_key(&self) -> &ChunkProductionKey {
155        match self {
156            Self::V1(request) => &request.inner.next_chunk,
157        }
158    }
159
160    pub fn main_transition(&self) -> &MainTransitionKey {
161        match self {
162            Self::V1(request) => &request.inner.main_transition,
163        }
164    }
165
166    pub fn verify_signature(&self, public_key: &PublicKey) -> bool {
167        match self {
168            Self::V1(v1) => v1.verify_signature(public_key),
169        }
170    }
171}
172
173#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
174pub struct ContractCodeRequestV1 {
175    inner: ContractCodeRequestInner,
176    /// Signature of the inner.
177    signature: Signature,
178}
179
180impl ContractCodeRequestV1 {
181    fn new(
182        next_chunk: ChunkProductionKey,
183        contracts: HashSet<CodeHash>,
184        main_transition: MainTransitionKey,
185        signer: &ValidatorSigner,
186    ) -> Self {
187        let inner = ContractCodeRequestInner::new(
188            signer.validator_id().clone(),
189            next_chunk,
190            contracts,
191            main_transition,
192        );
193        let signature = signer.sign_bytes(&borsh::to_vec(&inner).unwrap());
194        Self { inner, signature }
195    }
196
197    pub fn verify_signature(&self, public_key: &PublicKey) -> bool {
198        self.signature.verify(&borsh::to_vec(&self.inner).unwrap(), public_key)
199    }
200}
201
202#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
203pub struct ContractCodeRequestInner {
204    /// Account of the node requesting the contracts. Used for signature verification and
205    /// to identify the node to send the response to.
206    requester: AccountId,
207    /// Production metadata of the chunk created after the chunk the accesses belong to.
208    /// We associate this message with the next-chunk info because this message is generated
209    /// and distributed while generating the state-witness of the next chunk
210    /// (by the chunk producer of the next chunk).
211    next_chunk: ChunkProductionKey,
212    /// List of code-hashes for the contracts accessed.
213    contracts: Vec<CodeHash>,
214    main_transition: MainTransitionKey,
215    signature_differentiator: SignatureDifferentiator,
216}
217
218impl ContractCodeRequestInner {
219    fn new(
220        requester: AccountId,
221        next_chunk: ChunkProductionKey,
222        contracts: HashSet<CodeHash>,
223        main_transition: MainTransitionKey,
224    ) -> Self {
225        Self {
226            requester,
227            next_chunk,
228            contracts: contracts.into_iter().collect(),
229            main_transition,
230            signature_differentiator: "ContractCodeRequestInner".to_owned(),
231        }
232    }
233}
234
235// Data structures for chunk producers to send contract code to chunk validators as response to ContractCodeRequest.
236
237#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
238pub enum ContractCodeResponse {
239    V1(ContractCodeResponseV1),
240}
241
242impl ContractCodeResponse {
243    pub fn encode(
244        next_chunk: ChunkProductionKey,
245        contracts: &Vec<CodeBytes>,
246    ) -> std::io::Result<Self> {
247        ContractCodeResponseV1::encode(next_chunk, contracts).map(|v1| Self::V1(v1))
248    }
249
250    pub fn chunk_production_key(&self) -> &ChunkProductionKey {
251        match self {
252            Self::V1(v1) => &v1.next_chunk,
253        }
254    }
255
256    pub fn decompress_contracts(&self) -> std::io::Result<Vec<CodeBytes>> {
257        let compressed_contracts = match self {
258            Self::V1(v1) => &v1.compressed_contracts,
259        };
260        compressed_contracts.decode().map(|(data, _size)| data)
261    }
262}
263
264#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
265pub struct ContractCodeResponseV1 {
266    // The same as `next_chunk` in `ContractCodeRequest`
267    next_chunk: ChunkProductionKey,
268    /// Code for the contracts.
269    compressed_contracts: CompressedContractCode,
270}
271
272impl ContractCodeResponseV1 {
273    pub fn encode(
274        next_chunk: ChunkProductionKey,
275        contracts: &Vec<CodeBytes>,
276    ) -> std::io::Result<Self> {
277        let (compressed_contracts, _size) = CompressedContractCode::encode(&contracts)?;
278        Ok(Self { next_chunk, compressed_contracts })
279    }
280}
281
282/// Represents max allowed size of the raw (not compressed) contract code response,
283/// corresponds to the size of borsh-serialized ContractCodeResponse.
284const MAX_UNCOMPRESSED_CONTRACT_CODE_RESPONSE_SIZE: u64 =
285    ByteSize::mib(if cfg!(feature = "test_features") { 512 } else { 64 }).0;
286const CONTRACT_CODE_RESPONSE_COMPRESSION_LEVEL: i32 = 3;
287
288/// This is the compressed version of a list of borsh-serialized contract code.
289#[derive(
290    Debug,
291    Clone,
292    PartialEq,
293    Eq,
294    BorshSerialize,
295    BorshDeserialize,
296    ProtocolSchema,
297    derive_more::From,
298    derive_more::AsRef,
299)]
300struct CompressedContractCode(Box<[u8]>);
301
302impl
303    CompressedData<
304        Vec<CodeBytes>,
305        MAX_UNCOMPRESSED_CONTRACT_CODE_RESPONSE_SIZE,
306        CONTRACT_CODE_RESPONSE_COMPRESSION_LEVEL,
307    > for CompressedContractCode
308{
309}
310
311/// Hash of some (uncompiled) contract code.
312#[derive(
313    Debug,
314    Clone,
315    Hash,
316    PartialEq,
317    Eq,
318    Ord,
319    PartialOrd,
320    BorshSerialize,
321    BorshDeserialize,
322    serde::Serialize,
323    serde::Deserialize,
324    ProtocolSchema,
325)]
326pub struct CodeHash(pub CryptoHash);
327
328impl From<CryptoHash> for CodeHash {
329    fn from(crypto_hash: CryptoHash) -> Self {
330        Self(crypto_hash)
331    }
332}
333
334impl Into<CryptoHash> for CodeHash {
335    fn into(self) -> CryptoHash {
336        self.0
337    }
338}
339
340/// Raw bytes of the (uncompiled) contract code.
341#[derive(
342    Debug,
343    Clone,
344    PartialEq,
345    Eq,
346    BorshSerialize,
347    BorshDeserialize,
348    serde::Serialize,
349    serde::Deserialize,
350    ProtocolSchema,
351)]
352pub struct CodeBytes(pub Arc<[u8]>);
353
354impl CodeBytes {
355    pub fn hash(&self) -> CodeHash {
356        hash(self.0.as_ref()).into()
357    }
358}
359
360impl From<ContractCode> for CodeBytes {
361    fn from(code: ContractCode) -> Self {
362        Self(code.take_code().into())
363    }
364}
365
366impl Into<ContractCode> for CodeBytes {
367    fn into(self) -> ContractCode {
368        ContractCode::new(self.0.to_vec(), None)
369    }
370}
371
372/// Contains the accesses and changes (eg. deployments) to the contracts while applying a chunk.
373#[derive(Debug, Default, Clone)]
374pub struct ContractUpdates {
375    /// Code-hashes of the contracts accessed (called) while applying the chunk.
376    pub contract_accesses: HashSet<CodeHash>,
377    /// Contracts deployed while applying the chunk.
378    pub contract_deploys: Vec<ContractCode>,
379}
380
381impl ContractUpdates {
382    /// Returns the code-hashes of the contracts deployed.
383    pub fn contract_deploy_hashes(&self) -> HashSet<CodeHash> {
384        self.contract_deploys.iter().map(|contract| (*contract.hash()).into()).collect()
385    }
386}
387
388// Data structures for chunk producers to send deployed contracts to chunk validators.
389
390#[derive(Clone, BorshSerialize, BorshDeserialize, ProtocolSchema)]
391pub struct ChunkContractDeploys {
392    compressed_contracts: CompressedContractCode,
393}
394
395impl ChunkContractDeploys {
396    pub fn compress_contracts(contracts: &Vec<CodeBytes>) -> std::io::Result<Self> {
397        CompressedContractCode::encode(contracts)
398            .map(|(compressed_contracts, _size)| Self { compressed_contracts })
399    }
400
401    pub fn decompress_contracts(&self) -> std::io::Result<Vec<CodeBytes>> {
402        self.compressed_contracts.decode().map(|(data, _size)| data)
403    }
404}
405
406#[cfg(feature = "solomon")]
407impl ReedSolomonEncoderSerialize for ChunkContractDeploys {}
408#[cfg(feature = "solomon")]
409impl ReedSolomonEncoderDeserialize for ChunkContractDeploys {}
410
411#[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
412pub enum PartialEncodedContractDeploys {
413    V1(PartialEncodedContractDeploysV1),
414}
415
416impl PartialEncodedContractDeploys {
417    pub fn new(
418        key: ChunkProductionKey,
419        part: PartialEncodedContractDeploysPart,
420        signer: &ValidatorSigner,
421    ) -> Self {
422        Self::V1(PartialEncodedContractDeploysV1::new(key, part, signer))
423    }
424
425    pub fn chunk_production_key(&self) -> &ChunkProductionKey {
426        match &self {
427            Self::V1(v1) => &v1.inner.next_chunk,
428        }
429    }
430
431    pub fn part(&self) -> &PartialEncodedContractDeploysPart {
432        match &self {
433            Self::V1(v1) => &v1.inner.part,
434        }
435    }
436
437    pub fn verify_signature(&self, public_key: &PublicKey) -> bool {
438        match self {
439            Self::V1(accesses) => accesses.verify_signature(public_key),
440        }
441    }
442}
443
444impl Into<(ChunkProductionKey, PartialEncodedContractDeploysPart)>
445    for PartialEncodedContractDeploys
446{
447    fn into(self) -> (ChunkProductionKey, PartialEncodedContractDeploysPart) {
448        match self {
449            Self::V1(PartialEncodedContractDeploysV1 { inner, .. }) => {
450                (inner.next_chunk, inner.part)
451            }
452        }
453    }
454}
455
456#[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
457pub struct PartialEncodedContractDeploysV1 {
458    inner: PartialEncodedContractDeploysInner,
459    signature: Signature,
460}
461
462impl PartialEncodedContractDeploysV1 {
463    pub fn new(
464        key: ChunkProductionKey,
465        part: PartialEncodedContractDeploysPart,
466        signer: &ValidatorSigner,
467    ) -> Self {
468        let inner = PartialEncodedContractDeploysInner::new(key, part);
469        let signature = signer.sign_bytes(&borsh::to_vec(&inner).unwrap());
470        Self { inner, signature }
471    }
472
473    pub fn verify_signature(&self, public_key: &PublicKey) -> bool {
474        self.signature.verify(&borsh::to_vec(&self.inner).unwrap(), public_key)
475    }
476}
477
478#[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
479pub struct PartialEncodedContractDeploysPart {
480    pub part_ord: usize,
481    pub data: Box<[u8]>,
482    pub encoded_length: usize,
483}
484
485impl std::fmt::Debug for PartialEncodedContractDeploysPart {
486    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
487        f.debug_struct("PartialEncodedContractDeploysPart")
488            .field("part_ord", &self.part_ord)
489            .field("data_size", &self.data.len())
490            .field("encoded_length", &self.encoded_length)
491            .finish()
492    }
493}
494
495#[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
496pub struct PartialEncodedContractDeploysInner {
497    next_chunk: ChunkProductionKey,
498    part: PartialEncodedContractDeploysPart,
499    signature_differentiator: SignatureDifferentiator,
500}
501
502impl PartialEncodedContractDeploysInner {
503    fn new(next_chunk: ChunkProductionKey, part: PartialEncodedContractDeploysPart) -> Self {
504        Self {
505            next_chunk,
506            part,
507            signature_differentiator: "PartialEncodedContractDeploysInner".to_owned(),
508        }
509    }
510}