Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -601,4 +601,4 @@ wasmi-validation = { version = "0.5.0", git = "https://github.com/gear-tech/wasm
wasm-instrument = { version = "0.4.0", git = "https://github.com/gear-tech/wasm-instrument", branch = "v0.4.0-sign-ext" }

# there are patches to disable `memory.grow` and to add offset of reserved memory
wasm-smith = { version = "0.230", git = "https://github.com/gear-tech/wasm-tools", branch = "gear-stable-1.230" }
wasm-smith = { version = "0.230", git = "https://github.com/gear-tech/wasm-tools", branch = "gear-stable-1.230" }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\n

70 changes: 69 additions & 1 deletion ethexe/common/src/crypto/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@
//! Ethereum address.

use super::keys::PublicKey;
use alloc::string::String;
use alloc::{string::String, vec::Vec};
use core::str::FromStr;
use derive_more::{Debug, Display, Error};
use gprimitives::{ActorId, H160};
use hex::FromHexError;
use nonempty::NonEmpty;
use parity_scale_codec::{Decode, Encode};
use sha3::Digest as _;

Expand Down Expand Up @@ -120,6 +121,73 @@ impl From<Address> for ActorId {
}
}

/// [`ValidatorsVec`] is a wrapper over non-empty vector of [`Address`].
/// It is needed because `NonEmpty` does not implement `Encode` and `Decode`.
#[derive(
Debug,
Clone,
Default,
PartialEq,
Eq,
Hash,
derive_more::Deref,
derive_more::DerefMut,
derive_more::IntoIterator,
)]
pub struct ValidatorsVec(NonEmpty<Address>);

// Encode / Decode implementations
impl Encode for ValidatorsVec {
fn encode(&self) -> Vec<u8> {
Into::<Vec<_>>::into(self.0.clone()).encode()
}
}

impl Decode for ValidatorsVec {
fn decode<I: parity_scale_codec::Input>(
input: &mut I,
) -> Result<Self, parity_scale_codec::Error> {
let inner: Vec<Address> = Decode::decode(input)?;
NonEmpty::from_vec(inner)
.map(Self)
.ok_or(parity_scale_codec::Error::from(
"Failed to decode ValidatorsVec: empty vector",
))
}
}

#[derive(Debug, Display, Error)]
#[display("{:?}", self)]
#[debug("ValidatorsVec must be non-empty")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[debug("ValidatorsVec must be non-empty")]
#[debug("Vec must be non-empty")]

pub struct TryFromVecError;

// Usefull conversions from / to `Vec<Address>`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useful

impl TryFrom<Vec<Address>> for ValidatorsVec {
type Error = TryFromVecError;

fn try_from(value: Vec<Address>) -> Result<Self, Self::Error> {
NonEmpty::from_vec(value).map(Self).ok_or(TryFromVecError)
}
}

impl From<NonEmpty<Address>> for ValidatorsVec {
fn from(value: NonEmpty<Address>) -> Self {
Self(value)
}
}

impl From<ValidatorsVec> for Vec<Address> {
fn from(value: ValidatorsVec) -> Self {
value.0.into()
}
}

impl From<ValidatorsVec> for Vec<ActorId> {
fn from(value: ValidatorsVec) -> Self {
value.into_iter().map(Into::into).collect()
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
21 changes: 16 additions & 5 deletions ethexe/common/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
// TODO #4547: move types to another module(s)

use crate::{
Address, BlockHeader, BlockMeta, CodeBlobInfo, ProgramStates, Schedule, events::BlockEvent,
gear::StateTransition,
BlockHeader, BlockMeta, CodeBlobInfo, GearExeTimelines, ProgramStates, Schedule,
events::BlockEvent, gear::StateTransition, primitives::ValidatorsInfo,
};
use alloc::{
collections::{BTreeSet, VecDeque},
Expand All @@ -33,7 +33,6 @@ use gear_core::{
ids::{ActorId, CodeId},
};
use gprimitives::H256;
use nonempty::NonEmpty;
use parity_scale_codec::{Decode, Encode};

#[derive(
Expand Down Expand Up @@ -61,6 +60,14 @@ impl BlockOutcome {
}
}

/// Static data stored in the database.
/// Expected to be unmutable and set only once.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Encode, Decode)]
pub struct StaticData {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use LatestData

pub gear_exe_timelines: Option<GearExeTimelines>,
// Maybe add more static fields in the future.
}

#[auto_impl::auto_impl(&, Box)]
pub trait BlockMetaStorageRead {
/// NOTE: if `BlockMeta` doesn't exist in the database, it will return the default value.
Expand Down Expand Up @@ -111,18 +118,22 @@ pub trait CodesStorageWrite {

#[auto_impl::auto_impl(&, Box)]
pub trait OnChainStorageRead {
fn gear_exe_timelines(&self) -> Option<GearExeTimelines>;

fn block_header(&self, block_hash: H256) -> Option<BlockHeader>;
fn block_events(&self, block_hash: H256) -> Option<Vec<BlockEvent>>;
fn code_blob_info(&self, code_id: CodeId) -> Option<CodeBlobInfo>;
fn latest_synced_block_height(&self) -> Option<u32>;
fn validators(&self, block_hash: H256) -> Option<NonEmpty<Address>>;
fn validators_info(&self, block_hash: H256) -> Option<ValidatorsInfo>;
}

#[auto_impl::auto_impl(&)]
pub trait OnChainStorageWrite {
fn set_gear_exe_timelines(&self, timelines: GearExeTimelines);

fn set_block_header(&self, block_hash: H256, header: BlockHeader);
fn set_block_events(&self, block_hash: H256, events: &[BlockEvent]);
fn set_code_blob_info(&self, code_id: CodeId, code_info: CodeBlobInfo);
fn set_latest_synced_block_height(&self, height: u32);
fn set_validators(&self, block_hash: H256, validator_set: NonEmpty<Address>);
fn set_validators_info(&self, block_hash: H256, validators_info: ValidatorsInfo);
}
8 changes: 4 additions & 4 deletions ethexe/common/src/events/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub enum Event {
},
StorageSlotChanged,
NextEraValidatorsCommitted {
next_era_start: u64,
era_index: u64,
},
}

Expand All @@ -72,8 +72,8 @@ impl Event {
RequestEvent::ProgramCreated { actor_id, code_id }
}
Self::StorageSlotChanged => RequestEvent::StorageSlotChanged,
Self::NextEraValidatorsCommitted { next_era_start } => {
RequestEvent::NextEraValidatorsCommitted { next_era_start }
Self::NextEraValidatorsCommitted { era_index } => {
RequestEvent::NextEraValidatorsCommitted { era_index }
}
Self::CodeGotValidated { .. }
| Self::HeadCommitted(_)
Expand Down Expand Up @@ -101,6 +101,6 @@ pub enum RequestEvent {
},
StorageSlotChanged,
NextEraValidatorsCommitted {
next_era_start: u64,
era_index: u64,
},
}
42 changes: 41 additions & 1 deletion ethexe/common/src/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use crate::{Digest, ToDigest, events::BlockEvent};
use crate::{Digest, ToDigest, ValidatorsVec, events::BlockEvent};
use alloc::{
collections::{btree_map::BTreeMap, btree_set::BTreeSet},
vec::Vec,
Expand Down Expand Up @@ -167,6 +167,46 @@ impl CodeAndId {
}
}

/// [`NextEraValidators`] represents the all possible states of the next era validators.
/// The majority of the era time, the next era validators are not known yet.
/// The state switches to [`NextEraValidators::Elected`] at the election checkpoint by calling `makeElectionAt` in the middleware.
/// After the commitment is included in a block, the state switches to [`NextEraValidators::Committed`].
#[derive(Debug, Clone, Default, PartialEq, Eq, Encode, Decode)]
pub enum NextEraValidators {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like idea of storing next era validators in DB. Why:

  1. Observer must make election - this is ok right now, but should be moved to consensus service in future (consensus call rpc by self)
  2. Elected, but not committed validators could be stored in consensus service memory, which is much simpler implementation
  3. In propagate_validators_info - is the only one case where you use it. It removes rpc access once per era, but increase code complexity all over gear exe code, I don't think is fare trade

/// Validators are not known yet.
#[default]
Unknown,
/// Validators are elected, but not yet committed.
Elected(ValidatorsVec),
// Committed in the Router.
Committed(ValidatorsVec),
}

/// [`ValidatorsInfo`] stores the current and state of next set of validators.
/// The next set of validators will be applied at the beginning of the next era.
///
/// NOTE: [`Default`] implementation creates a non-empty set with a single zero address.
/// DO NOT use default in production code, it is only for tests purposes.
#[derive(Debug, Clone, Default, PartialEq, Eq, Encode, Decode)]
pub struct ValidatorsInfo {
pub current: ValidatorsVec,
pub next: NextEraValidators,
}

/// GearExe network timelines configuration. Parameters fetched the Router contract.
/// This struct stores in the database, because of using in the multiple places.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
pub struct GearExeTimelines {
// The genesis timestamp of the GearExe network.
pub genesis_ts: u64,
// The duration of an era in seconds.
pub era: u64,
// The election duration in seconds before the end of an era when the next set of validators elected.
/// (start of era)[ - - - - - - - - - - - + - - - - ] (end of era)
/// ^ election
pub election: u64,
}

/// RemoveFromMailbox key; (msgs sources program (mailbox and queue provider), destination user id)
pub type Rfm = (ActorId, ActorId);

Expand Down
22 changes: 22 additions & 0 deletions ethexe/common/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,25 @@ pub const fn u64_into_uint48_be_bytes_lossy(val: u64) -> [u8; 6] {

[b1, b2, b3, b4, b5, b6]
}

// Eras management
// Era: (gensis + era_index * era_duration, end_ts]

/// Returns the era index for the given timestamp.
/// The eras starts from 0.
#[inline(always)]
pub fn era_from_ts(ts: u64, genesis_ts: u64, era_duration: u64) -> u64 {
(ts - genesis_ts) / era_duration
}

/// Returns the timestamp since which the given era started.
#[inline(always)]
pub fn start_of_era_timestamp(era_index: u64, genesis_ts: u64, era_duration: u64) -> u64 {
genesis_ts + era_index * era_duration + 1
}

/// Returns the timestamp at which the given era ended.
#[inline(always)]
pub fn end_of_era_timestamp(era_index: u64, genesis_ts: u64, era_duration: u64) -> u64 {
genesis_ts + (era_index + 1) * era_duration
}
Loading
Loading