Skip to content

Commit

Permalink
Add code to sign the messages.
Browse files Browse the repository at this point in the history
  • Loading branch information
tayfunelmas committed Oct 15, 2024
1 parent 4c64fd1 commit d2a9caa
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 41 deletions.
2 changes: 2 additions & 0 deletions chain/chain/src/chain_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ impl<'a> ChainUpdate<'a> {
shard_id,
apply_result.proof,
apply_result.applied_receipts_hash,
apply_result.contract_accesses,
);
}
}
Expand Down Expand Up @@ -184,6 +185,7 @@ impl<'a> ChainUpdate<'a> {
shard_uid.shard_id(),
apply_result.proof,
apply_result.applied_receipts_hash,
apply_result.contract_accesses,
);
}
}
Expand Down
3 changes: 3 additions & 0 deletions chain/chain/src/resharding/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ impl ReshardingManager {
new_shard_uid.shard_id(),
Some(partial_storage),
CryptoHash::default(),
// No contract code is accessed during resharding.
// TODO(#11099): Confirm if sending no contracts is ok here.
vec![],
);

// Commit `TrieChanges` directly. They are needed to serve reads of
Expand Down
5 changes: 3 additions & 2 deletions chain/chain/src/store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use near_primitives::sharding::{
use near_primitives::state_sync::{
ReceiptProofResponse, ShardStateSyncResponseHeader, StateHeaderKey, StateSyncDumpProgress,
};
use near_primitives::stateless_validation::contract_distribution::CodeHash;
use near_primitives::stateless_validation::stored_chunk_state_transition_data::StoredChunkStateTransitionData;
use near_primitives::transaction::{
ExecutionOutcomeWithId, ExecutionOutcomeWithIdAndProof, ExecutionOutcomeWithProof,
Expand Down Expand Up @@ -2013,16 +2014,16 @@ impl<'a> ChainStoreUpdate<'a> {
shard_id: ShardId,
partial_storage: Option<PartialStorage>,
applied_receipts_hash: CryptoHash,
_contract_accesses: Vec<CodeHash>,
) {
if let Some(partial_storage) = partial_storage {
self.state_transition_data.insert(
(block_hash, shard_id),
StoredChunkStateTransitionData {
base_state: partial_storage.nodes,
receipts_hash: applied_receipts_hash,
// TODO(#11099): Revisit this.
#[cfg(feature = "contract_distribution")]
contract_accesses: vec![],
contract_accesses: _contract_accesses,
},
);
}
Expand Down
22 changes: 12 additions & 10 deletions chain/client/src/stateless_validation/state_witness_producer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl Client {

let my_signer =
validator_signer.as_ref().ok_or(Error::NotAValidator(format!("send state witness")))?;
let (state_witness, contract_accesses) = self.create_state_witness(
let (state_witness, _contract_accesses) = self.create_state_witness(
my_signer.validator_id().clone(),
prev_block_header,
prev_chunk_header,
Expand Down Expand Up @@ -87,12 +87,14 @@ impl Client {
}

#[cfg(feature = "contract_distribution")]
self.send_contract_accesses_to_chunk_validators(
epoch_id,
&chunk_header,
contract_accesses,
my_signer.as_ref(),
);
if !_contract_accesses.is_empty() {
self.send_contract_accesses_to_chunk_validators(
epoch_id,
&chunk_header,
_contract_accesses,
my_signer.as_ref(),
);
}

self.partial_witness_adapter.send(DistributeStateWitnessRequest {
epoch_id: *epoch_id,
Expand Down Expand Up @@ -318,13 +320,13 @@ impl Client {
}

/// Sends the contract access to the same chunk validators that will receive the state witness for the chunk.
#[allow(unused)]
fn send_contract_accesses_to_chunk_validators(
&self,
epoch_id: &EpochId,
chunk_header: &ShardChunkHeader,
contract_accesses: Vec<CodeHash>,
// TODO(#11099): Sign the message using this signer.
_signer: &ValidatorSigner,
my_signer: &ValidatorSigner,
) {
let chunk_validators = self
.epoch_manager
Expand All @@ -345,7 +347,7 @@ impl Client {
self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests(
NetworkRequests::ChunkContractAccesses(
chunk_validators,
ChunkContractAccesses::new(chunk_production_key, contract_accesses),
ChunkContractAccesses::new(chunk_production_key, contract_accesses, my_signer),
),
));
}
Expand Down
7 changes: 6 additions & 1 deletion chain/network/src/state_witness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,20 @@ pub struct PartialEncodedStateWitnessMessage(pub PartialEncodedStateWitness);
#[rtype(result = "()")]
pub struct PartialEncodedStateWitnessForwardMessage(pub PartialEncodedStateWitness);

/// Message to partial witness actor (on a chunk validator) that contains code-hashes of
/// the contracts that are accessed when applying the previous chunk.
#[derive(actix::Message, Clone, Debug, PartialEq, Eq)]
#[rtype(result = "()")]
pub struct ChunkContractAccessesMessage(pub ChunkContractAccesses);

/// Message to partial witness actor (on a chunk producer) that requests contract code
/// by their code hashes.
#[derive(actix::Message, Clone, Debug, PartialEq, Eq)]
#[rtype(result = "()")]
pub struct ContractCodeRequestMessage(pub ContractCodeRequest);

// TODO(#11099): Use the compressed form of the message.
/// Message to partial witness actor (on a chunk validator) that provides contract code
/// requested beforehand.
#[derive(actix::Message, Clone, Debug, PartialEq, Eq)]
#[rtype(result = "()")]
pub struct ContractCodeResponseMessage(pub ContractCodeResponse);
Expand Down
93 changes: 66 additions & 27 deletions core/primitives/src/stateless_validation/contract_distribution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,27 @@ use near_primitives_core::{
};
use near_schema_checker_lib::ProtocolSchema;

use crate::{types::EpochId, utils::compression::CompressedData};
use crate::{
types::EpochId, utils::compression::CompressedData, validator_signer::ValidatorSigner,
};

use super::{ChunkProductionKey, SignatureDifferentiator};

/// Contains contracts (as code-hashes) accessed during the application of a chunk.
/// This is used by the chunk producer to let the chunk validators know about which contracts
/// are needed for validating a witness, so that the chunk validators can request missing code.
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
pub enum ChunkContractAccesses {
V1(ChunkContractAccessesV1),
}

impl ChunkContractAccesses {
pub fn new(next_chunk: ChunkProductionKey, contracts: Vec<CodeHash>) -> Self {
Self::V1(ChunkContractAccessesV1::new(next_chunk, contracts))
pub fn new(
next_chunk: ChunkProductionKey,
contracts: Vec<CodeHash>,
signer: &ValidatorSigner,
) -> Self {
Self::V1(ChunkContractAccessesV1::new(next_chunk, contracts, signer))
}

pub fn contracts(&self) -> &Vec<CodeHash> {
Expand All @@ -30,24 +39,26 @@ impl ChunkContractAccesses {

#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
pub struct ChunkContractAccessesV1 {
pub inner: ChunkContractAccessesInner,
inner: ChunkContractAccessesInner,
/// Signature of the inner, signed by the chunk producer of the next chunk.
pub signature: Signature,
signature: Signature,
}

impl ChunkContractAccessesV1 {
fn new(next_chunk: ChunkProductionKey, contracts: Vec<CodeHash>) -> Self {
Self {
inner: ChunkContractAccessesInner::new(next_chunk, contracts),
// TODO(#11099): Sign the inner message.
signature: Signature::default(),
}
fn new(
next_chunk: ChunkProductionKey,
contracts: Vec<CodeHash>,
signer: &ValidatorSigner,
) -> Self {
let inner = ChunkContractAccessesInner::new(next_chunk, contracts);
let signature = signer.sign_chunk_contract_accesses(&inner);
Self { inner, signature }
}
}

/// Identifies a chunk by the epoch, block, and shard in which it was produced.
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
pub struct ChunkMetadata {
struct ChunkMetadata {
epoch_id: EpochId,
height_created: BlockHeight,
shard_id: ShardId,
Expand Down Expand Up @@ -102,6 +113,14 @@ pub enum ContractCodeRequest {
}

impl ContractCodeRequest {
pub fn new(
next_chunk: ChunkProductionKey,
contracts: Vec<CodeHash>,
signer: &ValidatorSigner,
) -> Self {
Self::V1(ContractCodeRequestV1::new(next_chunk, contracts, signer))
}

pub fn contracts(&self) -> &Vec<CodeHash> {
match self {
Self::V1(request) => &request.inner.contracts,
Expand All @@ -111,9 +130,21 @@ impl ContractCodeRequest {

#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
pub struct ContractCodeRequestV1 {
pub inner: ContractCodeRequestInner,
inner: ContractCodeRequestInner,
/// Signature of the inner.
pub signature: Signature,
signature: Signature,
}

impl ContractCodeRequestV1 {
fn new(
next_chunk: ChunkProductionKey,
contracts: Vec<CodeHash>,
signer: &ValidatorSigner,
) -> Self {
let inner = ContractCodeRequestInner::new(next_chunk, contracts);
let signature = signer.sign_contract_code_request(&inner);
Self { inner, signature }
}
}

#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
Expand All @@ -129,6 +160,16 @@ pub struct ContractCodeRequestInner {
signature_differentiator: SignatureDifferentiator,
}

impl ContractCodeRequestInner {
fn new(next_chunk: ChunkProductionKey, contracts: Vec<CodeHash>) -> Self {
Self {
next_chunk: next_chunk.into(),
contracts,
signature_differentiator: "ContractCodeRequestInner".to_owned(),
}
}
}

// Data structures for chunk producers to send contract code to chunk validators as response to ContractCodeRequest.

#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
Expand All @@ -137,25 +178,23 @@ pub enum ContractCodeResponse {
}

impl ContractCodeResponse {
pub fn new(contracts: &Vec<CodeBytes>) -> Self {
Self::V1(ContractCodeResponseV1::new(contracts))
pub fn new(contracts: &Vec<CodeBytes>, signer: &ValidatorSigner) -> Self {
Self::V1(ContractCodeResponseV1::new(contracts, signer))
}
}

#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
pub struct ContractCodeResponseV1 {
pub inner: ContractCodeResponseInner,
inner: ContractCodeResponseInner,
/// Signature of the inner.
pub signature: Signature,
signature: Signature,
}

impl ContractCodeResponseV1 {
fn new(contracts: &Vec<CodeBytes>) -> Self {
Self {
inner: ContractCodeResponseInner::new(contracts),
// TODO(#11099): Sign the inner message.
signature: Signature::default(),
}
fn new(contracts: &Vec<CodeBytes>, signer: &ValidatorSigner) -> Self {
let inner = ContractCodeResponseInner::new(contracts);
let signature = signer.sign_contract_code_response(&inner);
Self { inner, signature }
}
}

Expand Down Expand Up @@ -183,9 +222,9 @@ impl ContractCodeResponseInner {

/// Represents max allowed size of the raw (not compressed) contract code response,
/// corresponds to the size of borsh-serialized ContractCodeResponse.
pub const MAX_UNCOMPRESSED_CONTRACT_CODE_RESPONSE_SIZE: u64 =
const MAX_UNCOMPRESSED_CONTRACT_CODE_RESPONSE_SIZE: u64 =
ByteSize::mib(if cfg!(feature = "test_features") { 512 } else { 64 }).0;
pub const CONTRACT_CODE_RESPONSE_COMPRESSION_LEVEL: i32 = 3;
const CONTRACT_CODE_RESPONSE_COMPRESSION_LEVEL: i32 = 3;

/// This is the compressed version of a list of borsh-serialized contract code.
#[derive(
Expand All @@ -199,7 +238,7 @@ pub const CONTRACT_CODE_RESPONSE_COMPRESSION_LEVEL: i32 = 3;
derive_more::From,
derive_more::AsRef,
)]
pub struct CompressedContractCode(Box<[u8]>);
struct CompressedContractCode(Box<[u8]>);

impl
CompressedData<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use near_schema_checker_lib::ProtocolSchema;

/// Stored on disk for each chunk, including missing chunks, in order to
/// produce a chunk state witness when needed.
// TODO(#11099): Make this a versioned structure.
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
pub struct StoredChunkStateTransitionData {
/// The partial state that is needed to apply the state transition,
Expand Down
51 changes: 51 additions & 0 deletions core/primitives/src/validator_signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ use crate::sharding::ChunkHash;
use crate::stateless_validation::chunk_endorsement::{
ChunkEndorsementInner, ChunkEndorsementMetadata,
};
use crate::stateless_validation::contract_distribution::{
ChunkContractAccessesInner, ContractCodeRequestInner, ContractCodeResponseInner,
};
use crate::stateless_validation::partial_witness::PartialEncodedStateWitnessInner;
use crate::stateless_validation::state_witness::EncodedChunkStateWitness;
use crate::telemetry::TelemetryInfo;
Expand Down Expand Up @@ -146,6 +149,30 @@ impl ValidatorSigner {
}
}

/// Signs the inner contents of a ChunkContractAccesses message.
pub fn sign_chunk_contract_accesses(&self, inner: &ChunkContractAccessesInner) -> Signature {
match self {
ValidatorSigner::Empty(signer) => signer.sign_chunk_contract_accesses(inner),
ValidatorSigner::InMemory(signer) => signer.sign_chunk_contract_accesses(inner),
}
}

/// Signs the inner contents of a ContractCodeRequest message.
pub fn sign_contract_code_request(&self, inner: &ContractCodeRequestInner) -> Signature {
match self {
ValidatorSigner::Empty(signer) => signer.sign_contract_code_request(inner),
ValidatorSigner::InMemory(signer) => signer.sign_contract_code_request(inner),
}
}

/// Signs the inner contents of a ContractCodeResponse message.
pub fn sign_contract_code_response(&self, inner: &ContractCodeResponseInner) -> Signature {
match self {
ValidatorSigner::Empty(signer) => signer.sign_contract_code_response(inner),
ValidatorSigner::InMemory(signer) => signer.sign_contract_code_response(inner),
}
}

/// Signs a proto-serialized AccountKeyPayload (see
/// chain/network/src/network_protocol/network.proto).
/// Making it typesafe would require moving the definition of
Expand Down Expand Up @@ -273,6 +300,18 @@ impl EmptyValidatorSigner {
fn sign_account_key_payload(&self, _proto_bytes: &[u8]) -> Signature {
Signature::default()
}

fn sign_chunk_contract_accesses(&self, _inner: &ChunkContractAccessesInner) -> Signature {
Signature::default()
}

fn sign_contract_code_request(&self, _inner: &ContractCodeRequestInner) -> Signature {
Signature::default()
}

fn sign_contract_code_response(&self, _inner: &ContractCodeResponseInner) -> Signature {
Signature::default()
}
}

/// Signer that keeps secret key in memory and signs locally.
Expand Down Expand Up @@ -378,6 +417,18 @@ impl InMemoryValidatorSigner {
self.signer.sign(proto_bytes)
}

fn sign_chunk_contract_accesses(&self, inner: &ChunkContractAccessesInner) -> Signature {
self.signer.sign(&borsh::to_vec(inner).unwrap())
}

fn sign_contract_code_request(&self, inner: &ContractCodeRequestInner) -> Signature {
self.signer.sign(&borsh::to_vec(inner).unwrap())
}

fn sign_contract_code_response(&self, inner: &ContractCodeResponseInner) -> Signature {
self.signer.sign(&borsh::to_vec(inner).unwrap())
}

fn compute_vrf_with_proof(
&self,
data: &[u8],
Expand Down
Loading

0 comments on commit d2a9caa

Please sign in to comment.