Skip to content

Commit

Permalink
feat(node): Pause programs after specific amount of blocks (#2507)
Browse files Browse the repository at this point in the history
gshep authored Apr 26, 2023
1 parent 5e05d46 commit cf98c94
Showing 42 changed files with 1,172 additions and 311 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -349,6 +349,7 @@ demo-signal-entry = { path = "examples/binaries/signal-entry" }
demo-value-sender = { path = "examples/binaries/value-sender" }
demo-waiter = { path = "examples/binaries/waiter" }
demo-wait-timeout = { path = "examples/binaries/wait-timeout" }
demo-wait-wake = { path = "examples/binaries/wait_wake" }
demo-waiting-proxy = { path = "examples/binaries/waiting-proxy" }

# Dependencies that only used in one package
4 changes: 2 additions & 2 deletions common/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -135,7 +135,7 @@ pub fn generate_wasm3(payload: Vec<u8>) -> Result<Vec<u8>, &'static str> {

pub fn set_program<
ProgramStorage: super::ProgramStorage<BlockNumber = BlockNumber>,
BlockNumber: Zero,
BlockNumber: Zero + Copy + Saturating,
>(
program_id: ProgramId,
code: Vec<u8>,
@@ -164,8 +164,8 @@ pub fn set_program<
static_pages,
state: ProgramState::Initialized,
gas_reservation_map: GasReservationMap::default(),
expiration_block: Zero::zero(),
},
Zero::zero(),
)
.expect("benchmarking; program duplicates should not exist");
}
31 changes: 18 additions & 13 deletions common/src/event.rs
Original file line number Diff line number Diff line change
@@ -26,7 +26,6 @@ use frame_support::{
scale_info::{self, TypeInfo},
};
use gear_core::{ids::MessageId, message::MessageWaitedType};
use primitive_types::H256;

/// Programs entry for messages.
///
@@ -219,28 +218,34 @@ pub type UserMessageReadReason = Reason<UserMessageReadRuntimeReason, UserMessag
pub enum ProgramChangeKind<BlockNumber> {
/// Active status achieved.
///
/// Occurs when new program created, paused program was resumed
/// or expiration block number updated.
/// Occurs when new program created or paused program was resumed.
///
/// Expiration block number presents block number when this program become
/// paused due to losing ability to pay rent for holding.
Active { expiration: BlockNumber },

// TODO: consider about addition expiration block number (issue #1014).
/// Program become inactive forever due to `gr_exit` call.
Inactive,

// TODO: consider about addition expiration block number (issue #1014).
/// Paused status.
///
/// Program is no longer available for interaction, but can be
/// resumed by paying rent and giving whole data related to it.
Paused {
/// Code hash the program relates to.
code_hash: H256,
/// Hash of memory pages of the program.
memory_hash: H256,
/// Waitlist hash addressed to the program.
waitlist_hash: H256,
},
Paused,

/// Program become inactive forever due to init failure.
Terminated,

/// Occurs when expiration block number of a program changed.
///
/// Expiration block number presents block number when this program become
/// paused due to losing ability to pay rent for holding.
ExpirationChanged { expiration: BlockNumber },

/// Occurs when new program set in the storage.
///
/// Expiration block number presents block number when this program become
/// paused due to losing ability to pay rent for holding or terminated in
/// case of didn't get initialised.
ProgramSet { expiration: BlockNumber },
}
53 changes: 38 additions & 15 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -31,6 +31,9 @@ pub use code_storage::{CodeStorage, Error as CodeStorageError};
pub mod program_storage;
pub use program_storage::{Error as ProgramStorageError, ProgramStorage};

pub mod paused_program_storage;
pub use paused_program_storage::PausedProgramStorage;

pub mod gas_provider;

#[cfg(feature = "runtime-benchmarks")]
@@ -56,7 +59,7 @@ use gear_core::{
reservation::GasReservationMap,
};
use primitive_types::H256;
use sp_arithmetic::traits::{BaseArithmetic, Unsigned};
use sp_arithmetic::traits::{BaseArithmetic, Saturating, Unsigned};
use sp_core::crypto::UncheckedFrom;
use sp_std::{
collections::{btree_map::BTreeMap, btree_set::BTreeSet},
@@ -152,6 +155,21 @@ pub trait GasPrice {
}
}

/// Trait defines basic parameters of programs rent charging.
pub trait ProgramRentConfig {
/// Type representing an index of a block.
type BlockNumber;
/// Type representing a quantity of value.
type Balance: BaseArithmetic + From<u32> + Copy + Unsigned;

/// The free of charge period of rent.
type FreePeriod: Get<Self::BlockNumber>;
/// The program rent cost per block.
type CostPerBlock: Get<Self::Balance>;
/// The minimal amount of blocks to resume.
type MinimalResumePeriod: Get<Self::BlockNumber>;
}

pub trait QueueRunner {
type Gas;

@@ -183,13 +201,13 @@ pub trait BlockLimiter {
#[derive(Clone, Debug, Decode, Encode, PartialEq, Eq, TypeInfo)]
#[codec(crate = codec)]
#[scale_info(crate = scale_info)]
pub enum Program {
Active(ActiveProgram),
pub enum Program<BlockNumber: Copy + Saturating> {
Active(ActiveProgram<BlockNumber>),
Exited(ProgramId),
Terminated(ProgramId),
}

impl Program {
impl<BlockNumber: Copy + Saturating> Program<BlockNumber> {
pub fn is_active(&self) -> bool {
matches!(self, Program::Active(_))
}
@@ -212,25 +230,29 @@ impl Program {
)
}

pub fn is_uninitialized(&self) -> bool {
matches!(
self,
Program::Active(ActiveProgram {
state: ProgramState::Uninitialized { .. },
..
})
)
pub fn is_uninitialized(&self) -> Option<MessageId> {
if let Program::Active(ActiveProgram {
state: ProgramState::Uninitialized { message_id },
..
}) = self
{
Some(*message_id)
} else {
None
}
}
}

#[derive(Clone, Debug, derive_more::Display)]
#[display(fmt = "Program is not an active one")]
pub struct InactiveProgramError;

impl core::convert::TryFrom<Program> for ActiveProgram {
impl<BlockNumber: Copy + Saturating> core::convert::TryFrom<Program<BlockNumber>>
for ActiveProgram<BlockNumber>
{
type Error = InactiveProgramError;

fn try_from(prog_with_status: Program) -> Result<ActiveProgram, Self::Error> {
fn try_from(prog_with_status: Program<BlockNumber>) -> Result<Self, Self::Error> {
match prog_with_status {
Program::Active(p) => Ok(p),
_ => Err(InactiveProgramError),
@@ -241,7 +263,7 @@ impl core::convert::TryFrom<Program> for ActiveProgram {
#[derive(Clone, Debug, Decode, Encode, PartialEq, Eq, TypeInfo)]
#[codec(crate = codec)]
#[scale_info(crate = scale_info)]
pub struct ActiveProgram {
pub struct ActiveProgram<BlockNumber: Copy + Saturating> {
/// Set of dynamic wasm page numbers, which are allocated by the program.
pub allocations: BTreeSet<WasmPage>,
/// Set of gear pages numbers, which has data in storage.
@@ -251,6 +273,7 @@ pub struct ActiveProgram {
pub code_exports: BTreeSet<DispatchKind>,
pub static_pages: WasmPage,
pub state: ProgramState,
pub expiration_block: BlockNumber,
}

/// Enumeration contains variants for program state.
83 changes: 83 additions & 0 deletions common/src/paused_program_storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// This file is part of Gear.

// Copyright (C) 2023 Gear Technologies Inc.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// 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 super::*;
use crate::storage::MapStorage;
use core::fmt::Debug;

#[derive(Clone, Debug, PartialEq, Eq, Decode, Encode, TypeInfo)]
#[codec(crate = codec)]
#[scale_info(crate = scale_info)]
struct Item {
allocations: BTreeSet<WasmPage>,
memory_pages: BTreeMap<GearPage, PageBuf>,
code_hash: H256,
}

impl<BlockNumber: Copy + Saturating> From<(ActiveProgram<BlockNumber>, BTreeMap<GearPage, PageBuf>)>
for Item
{
fn from(
(program, memory_pages): (ActiveProgram<BlockNumber>, BTreeMap<GearPage, PageBuf>),
) -> Self {
Self {
allocations: program.allocations,
memory_pages,
code_hash: program.code_hash,
}
}
}

impl Item {
fn hash(&self) -> H256 {
self.using_encoded(sp_io::hashing::blake2_256).into()
}
}

/// Trait to pause/resume programs.
pub trait PausedProgramStorage: super::ProgramStorage {
type PausedProgramMap: MapStorage<Key = ProgramId, Value = (Self::BlockNumber, H256)>;

/// Attempt to remove all items from all the associated maps.
fn reset() {
Self::PausedProgramMap::clear();
}

/// Does the paused program (explicitly) exist in storage?
fn paused_program_exists(program_id: &ProgramId) -> bool {
Self::PausedProgramMap::contains_key(program_id)
}

/// Pause an active program with the given key `program_id`.
///
/// Return corresponding map with gas reservations if the program was paused.
fn pause_program(
program_id: ProgramId,
block_number: Self::BlockNumber,
) -> Result<GasReservationMap, <Self as super::ProgramStorage>::Error> {
let (mut program, memory_pages) = Self::remove_active_program(program_id)?;
let gas_reservations = core::mem::take(&mut program.gas_reservation_map);

Self::PausedProgramMap::insert(
program_id,
(block_number, Item::from((program, memory_pages)).hash()),
);

Ok(gas_reservations)
}
}
Loading

0 comments on commit cf98c94

Please sign in to comment.