diff --git a/Cargo.toml b/Cargo.toml index ae1ad8f3f..f84ff4b46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,9 @@ members = [ "substrate/in-instructions/primitives", "substrate/in-instructions/pallet", + "substrate/staking/primitives", + "substrate/staking/pallet", + "substrate/validator-sets/primitives", "substrate/validator-sets/pallet", diff --git a/common/std-shims/Cargo.toml b/common/std-shims/Cargo.toml index 0f20c3335..b7d3abbe1 100644 --- a/common/std-shims/Cargo.toml +++ b/common/std-shims/Cargo.toml @@ -13,7 +13,7 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] -spin = "0.9" +spin = { version = "0.9", features = ["mutex", "once"] } hashbrown = "0.14" [features] diff --git a/common/std-shims/src/sync.rs b/common/std-shims/src/sync.rs index 75cfe39a1..6b8070bc2 100644 --- a/common/std-shims/src/sync.rs +++ b/common/std-shims/src/sync.rs @@ -29,42 +29,22 @@ pub use mutex_shim::{ShimMutex as Mutex, MutexGuard}; pub use std::sync::OnceLock; #[cfg(not(feature = "std"))] mod oncelock_shim { - use super::Mutex; + use spin::Once; - pub struct OnceLock(Mutex, Option); + pub struct OnceLock(Once); impl OnceLock { pub const fn new() -> OnceLock { - OnceLock(Mutex::new(false), None) + OnceLock(Once::new()) } - - // These return a distinct Option in case of None so another caller using get_or_init doesn't - // transform it from None to Some pub fn get(&self) -> Option<&T> { - if !*self.0.lock() { - None - } else { - self.1.as_ref() - } + self.0.poll() } pub fn get_mut(&mut self) -> Option<&mut T> { - if !*self.0.lock() { - None - } else { - self.1.as_mut() - } + self.0.get_mut() } pub fn get_or_init T>(&self, f: F) -> &T { - let mut lock = self.0.lock(); - if !*lock { - unsafe { - core::ptr::addr_of!(self.1).cast_mut().write_unaligned(Some(f())); - } - } - *lock = true; - drop(lock); - - self.get().unwrap() + self.0.call_once(f) } } } diff --git a/common/zalloc/Cargo.toml b/common/zalloc/Cargo.toml index 2a18cf3cd..c639e341d 100644 --- a/common/zalloc/Cargo.toml +++ b/common/zalloc/Cargo.toml @@ -13,7 +13,7 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] -zeroize = "^1.5" +zeroize = { version = "^1.5", default-features = false } [features] # Commented for now as it requires nightly and we don't use nightly diff --git a/coordinator/tributary/Cargo.toml b/coordinator/tributary/Cargo.toml index 824288c3c..cc3bd24f8 100644 --- a/coordinator/tributary/Cargo.toml +++ b/coordinator/tributary/Cargo.toml @@ -32,7 +32,7 @@ scale = { package = "parity-scale-codec", version = "3", features = ["derive"] } futures = "0.3" tendermint = { package = "tendermint-machine", path = "./tendermint" } -tokio = { version = "1", features = ["macros", "sync", "time", "rt"] } +tokio = { version = "1", features = ["sync", "time", "rt"] } [features] tests = [] diff --git a/coordinator/tributary/tendermint/Cargo.toml b/coordinator/tributary/tendermint/Cargo.toml index 91153e8a5..865034de8 100644 --- a/coordinator/tributary/tendermint/Cargo.toml +++ b/coordinator/tributary/tendermint/Cargo.toml @@ -16,4 +16,7 @@ log = "0.4" parity-scale-codec = { version = "3", features = ["derive"] } futures = "0.3" -tokio = { version = "1", features = ["macros", "sync", "time", "rt"] } +tokio = { version = "1", features = ["sync", "time"] } + +[dev-dependencies] +tokio = { version = "1", features = ["rt-multi-thread", "macros"] } diff --git a/deny.toml b/deny.toml index 79ce59898..ffff49a4c 100644 --- a/deny.toml +++ b/deny.toml @@ -61,6 +61,9 @@ exceptions = [ { allow = ["AGPL-3.0"], name = "serai-validator-sets-pallet" }, + { allow = ["AGPL-3.0"], name = "serai-staking-primitives" }, + { allow = ["AGPL-3.0"], name = "serai-staking-pallet" }, + { allow = ["AGPL-3.0"], name = "serai-runtime" }, { allow = ["AGPL-3.0"], name = "serai-node" }, diff --git a/deploy/coins/bitcoin/Dockerfile b/deploy/coins/bitcoin/Dockerfile index 755b0c3ee..a3b215ff4 100644 --- a/deploy/coins/bitcoin/Dockerfile +++ b/deploy/coins/bitcoin/Dockerfile @@ -26,12 +26,15 @@ RUN grep bitcoin-${BITCOIN_VERSION}-x86_64-linux-gnu.tar.gz SHA256SUMS | sha256s # Prepare Image RUN tar xzvf bitcoin-${BITCOIN_VERSION}-x86_64-linux-gnu.tar.gz -FROM debian:bullseye-slim as image +FROM debian:bookworm-slim as image WORKDIR /home/bitcoin COPY --from=builder /home/bitcoin/* . RUN mv bin/* /bin && mv lib/* /lib COPY ./scripts /scripts +# Upgrade packages +RUN apt update && apt upgrade -y + EXPOSE 8332 8333 18332 18333 18443 18444 VOLUME ["/home/bitcoin/.bitcoin"] diff --git a/deploy/coins/monero/Dockerfile b/deploy/coins/monero/Dockerfile index 6e6dfe628..842f9146e 100644 --- a/deploy/coins/monero/Dockerfile +++ b/deploy/coins/monero/Dockerfile @@ -27,12 +27,15 @@ RUN gpg --keyserver hkp://keyserver.ubuntu.com:80 --keyserver-options no-self-si RUN tar -xvjf monero-linux-x64-v${MONERO_VERSION}.tar.bz2 --strip-components=1 # Prepare Image -FROM debian:bullseye-slim as image +FROM debian:bookworm-slim as image WORKDIR /home/monero COPY --from=builder /home/monero/* . RUN mv * /bin/ COPY ./scripts /scripts +# Upgrade packages +RUN apt update && apt upgrade -y + EXPOSE 18080 18081 VOLUME /home/monero/.bitmonero diff --git a/deploy/message-queue/Dockerfile b/deploy/message-queue/Dockerfile index 3f9ba8155..4471b0834 100644 --- a/deploy/message-queue/Dockerfile +++ b/deploy/message-queue/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.71 as builder +FROM rust:1.71-slim-bookworm as builder LABEL description="STAGE 1: Build" # Add files for build @@ -29,7 +29,7 @@ RUN --mount=type=cache,target=/root/.cargo \ mv /serai/target/release/serai-message-queue /serai/bin # Prepare Image -FROM debian:bullseye-slim as image +FROM debian:bookworm-slim as image LABEL description="STAGE 2: Copy and Run" WORKDIR /home/serai @@ -38,6 +38,9 @@ WORKDIR /home/serai COPY --from=builder /serai/bin/* /bin/ COPY --from=builder /serai/AGPL-3.0 . +# Upgrade packages +RUN apt update && apt upgrade -y + # Run message-queue EXPOSE 2287 CMD ["serai-message-queue"] diff --git a/deploy/processor/Dockerfile b/deploy/processor/Dockerfile index 33cfef8e0..6537a887b 100644 --- a/deploy/processor/Dockerfile +++ b/deploy/processor/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/paritytech/ci-linux:production as builder +FROM rust:1.71-slim-bookworm as builder LABEL description="STAGE 1: Build" # Add files for build @@ -16,6 +16,11 @@ ADD AGPL-3.0 /serai WORKDIR /serai +RUN apt update && apt upgrade -y && apt install -y clang libssl-dev + +# Add the wasm toolchain +RUN rustup target add wasm32-unknown-unknown + # Mount the caches and build RUN --mount=type=cache,target=/root/.cargo \ --mount=type=cache,target=/usr/local/cargo/registry \ @@ -27,7 +32,7 @@ RUN --mount=type=cache,target=/root/.cargo \ mv /serai/target/release/serai-processor /serai/bin # Prepare Image -FROM debian:bullseye-slim as image +FROM debian:bookworm-slim as image LABEL description="STAGE 2: Copy and Run" WORKDIR /home/serai @@ -36,8 +41,8 @@ WORKDIR /home/serai COPY --from=builder /serai/bin/* /bin/ COPY --from=builder /serai/AGPL-3.0 . -# Install openssl -RUN apt update && apt upgrade -y && apt install -y libssl +# Upgrade packages and install openssl +RUN apt update && apt upgrade -y && apt install -y libssl-dev # Run processor CMD ["serai-processor"] diff --git a/deploy/serai/Dockerfile b/deploy/serai/Dockerfile index 467f7b9ad..2bc63f40c 100644 --- a/deploy/serai/Dockerfile +++ b/deploy/serai/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/paritytech/ci-linux:production as builder +FROM rust:1.71-slim-bookworm as builder LABEL description="STAGE 1: Build" # Add files for build @@ -16,34 +16,20 @@ ADD AGPL-3.0 /serai WORKDIR /serai -# Update Rust -RUN rustup update - -# Install Solc @ 0.8.16 -RUN --mount=type=cache,target=/root/.cache \ - --mount=type=cache,target=/root/.local \ - --mount=type=cache,target=/root/.solc-select \ - pip3 install solc-select==0.2.1 -RUN --mount=type=cache,target=/root/.cache \ - --mount=type=cache,target=/root/.local \ - --mount=type=cache,target=/root/.solc-select \ - solc-select install 0.8.16 -RUN --mount=type=cache,target=/root/.cache \ - --mount=type=cache,target=/root/.local \ - --mount=type=cache,target=/root/.solc-select \ - solc-select use 0.8.16 - -# Mount cargo and the Serai cache -RUN --mount=type=cache,target=/root/.local \ - --mount=type=cache,target=/root/.solc-select \ - --mount=type=cache,target=/root/.cache \ - --mount=type=cache,target=/usr/local/cargo/git \ +RUN apt update && apt upgrade -y && apt install -y make clang libssl-dev protobuf-compiler + +# Add the wasm toolchain +RUN rustup target add wasm32-unknown-unknown + +# Mount the caches and build +RUN --mount=type=cache,target=/root/.cargo \ --mount=type=cache,target=/usr/local/cargo/registry \ + --mount=type=cache,target=/usr/local/cargo/git \ --mount=type=cache,target=/serai/target \ cd substrate/node && cargo build --release # Prepare Image -FROM ubuntu:latest as image +FROM debian:bookworm-slim as image LABEL description="STAGE 2: Copy and Run" WORKDIR /home/serai @@ -52,6 +38,9 @@ WORKDIR /home/serai COPY --from=builder /serai/target/release/ /bin/ COPY --from=builder /serai/AGPL-3.0 . +# Upgrade packages +RUN apt update && apt upgrade -y + # Run node EXPOSE 30333 9615 9933 9944 CMD ["serai-node"] diff --git a/substrate/runtime/Cargo.toml b/substrate/runtime/Cargo.toml index 4bac075d5..6792db977 100644 --- a/substrate/runtime/Cargo.toml +++ b/substrate/runtime/Cargo.toml @@ -57,6 +57,9 @@ pallet-grandpa = { git = "https://github.com/serai-dex/substrate", default-featu pallet-authority-discovery = { git = "https://github.com/serai-dex/substrate", default-features = false } +pallet-session = { git = "https://github.com/serai-dex/substrate", default-features = false } +staking-pallet = { package = "serai-staking-pallet", path = "../staking/pallet", default-features = false } + frame-system-rpc-runtime-api = { git = "https://github.com/serai-dex/substrate", default-features = false } pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/serai-dex/substrate", default-features = false } @@ -114,6 +117,8 @@ std = [ ] runtime-benchmarks = [ + "hex-literal", + "sp-runtime/runtime-benchmarks", "frame-system/runtime-benchmarks", @@ -125,8 +130,8 @@ runtime-benchmarks = [ "pallet-balances/runtime-benchmarks", "pallet-assets/runtime-benchmarks", - "pallet-babe/runtime-benchmarks", - "pallet-grandpa/runtime-benchmarks", + "pallet-babe/runtime-benchmarks", + "pallet-grandpa/runtime-benchmarks", ] default = ["std"] diff --git a/substrate/runtime/src/lib.rs b/substrate/runtime/src/lib.rs index a57b29061..94c979e01 100644 --- a/substrate/runtime/src/lib.rs +++ b/substrate/runtime/src/lib.rs @@ -61,6 +61,8 @@ use babe::AuthorityId as BabeId; use grandpa::AuthorityId as GrandpaId; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; +use pallet_session::PeriodicSessions; + /// An index to a block. pub type BlockNumber = u64; @@ -354,6 +356,32 @@ impl authority_discovery::Config for Runtime { type MaxAuthorities = MaxAuthorities; } +const SESSION_LENGTH: BlockNumber = 5 * DAYS; +type Sessions = PeriodicSessions, ConstU32<{ SESSION_LENGTH }>>; + +pub struct IdentityValidatorIdOf; +impl Convert> for IdentityValidatorIdOf { + fn convert(key: Public) -> Option { + Some(key) + } +} + +impl pallet_session::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ValidatorId = AccountId; + type ValidatorIdOf = IdentityValidatorIdOf; + type ShouldEndSession = Sessions; + type NextSessionRotation = Sessions; + type SessionManager = (); + type SessionHandler = ::KeyTypeIdProviders; + type Keys = SessionKeys; + type WeightInfo = pallet_session::weights::SubstrateWeight; +} + +impl staking_pallet::Config for Runtime { + type Currency = Balances; +} + pub type Header = generic::Header; pub type Block = generic::Block; pub type SignedExtra = ( @@ -393,6 +421,7 @@ construct_runtime!( ValidatorSets: validator_sets, Session: session, + Staking: staking, Babe: babe, Grandpa: grandpa, diff --git a/substrate/staking/pallet/Cargo.toml b/substrate/staking/pallet/Cargo.toml new file mode 100644 index 000000000..784087347 --- /dev/null +++ b/substrate/staking/pallet/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "serai-staking-pallet" +version = "0.1.0" +description = "Staking pallet for Serai" +license = "AGPL-3.0-only" +repository = "https://github.com/serai-dex/serai/tree/develop/substrate/staking/pallet" +authors = ["Luke Parker "] +edition = "2021" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[dependencies] +parity-scale-codec = { version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2", default-features = false, features = ["derive"] } + +sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false } + +frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false } +frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false } + +staking-primitives = { package = "serai-staking-primitives", path = "../primitives", default-features = false } + +[features] +std = [ + "frame-system/std", + "frame-support/std", +] + +runtime-benchmarks = [ + "frame-system/runtime-benchmarks", + "frame-support/runtime-benchmarks", +] + +default = ["std"] diff --git a/substrate/staking/pallet/LICENSE b/substrate/staking/pallet/LICENSE new file mode 100644 index 000000000..d6e1814a2 --- /dev/null +++ b/substrate/staking/pallet/LICENSE @@ -0,0 +1,15 @@ +AGPL-3.0-only license + +Copyright (c) 2022 Luke Parker + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License Version 3 as +published by the Free Software Foundation. + +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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . diff --git a/substrate/staking/pallet/src/lib.rs b/substrate/staking/pallet/src/lib.rs new file mode 100644 index 000000000..06e947bd7 --- /dev/null +++ b/substrate/staking/pallet/src/lib.rs @@ -0,0 +1,214 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +#[frame_support::pallet] +pub mod pallet { + use sp_runtime::traits::TrailingZeroInput; + + use frame_system::pallet_prelude::*; + use frame_support::{ + pallet_prelude::*, + traits::{Currency, tokens::ExistenceRequirement}, + }; + + use staking_primitives::AllocatedStaking; + + #[pallet::error] + pub enum Error { + BondUnavailable, + InsufficientDelegation, + } + + // TODO: Event + + #[pallet::config] + pub trait Config: frame_system::Config { + type Currency: Currency; + } + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(PhantomData); + + // There's an argument some of the following should be in AccountData + // AccountData is also premised on enabling reaping, when reaping is more of a pain than a gain + + /// The amount of funds this account has staked. + #[pallet::storage] + #[pallet::getter(fn staked)] + pub type Staked = StorageMap<_, Blake2_128Concat, T::AccountId, u64, ValueQuery>; + + /// The amount of funds a staking account has delegated to another. + #[pallet::storage] + #[pallet::getter(fn delegated)] + pub type Delegations = StorageDoubleMap< + _, + Blake2_128Concat, + T::AccountId, + Blake2_128Concat, + T::AccountId, + u64, + ValueQuery, + >; + + /// The amount of bond this account has been delegated. + #[pallet::storage] + #[pallet::getter(fn bond)] + pub type Bond = StorageMap<_, Blake2_128Concat, T::AccountId, u64, ValueQuery>; + + /// The amount of bond this account has allocated to validator sets. + #[pallet::storage] + #[pallet::getter(fn allocated)] + pub type Allocated = StorageMap<_, Blake2_128Concat, T::AccountId, u64, ValueQuery>; + + impl Pallet { + fn account() -> T::AccountId { + // Substrate has a pattern of using simply using 8-bytes (as a PalletId) directly as an + // AccountId. This replicates its internals to remove the 8-byte limit + T::AccountId::decode(&mut TrailingZeroInput::new(b"staking")).unwrap() + } + + fn add_delegation(from: &T::AccountId, to: &T::AccountId, amount: u64) { + Delegations::::mutate(from, to, |delegated| *delegated += amount); + Bond::::mutate(to, |bond| *bond += amount); + } + + fn remove_delegation(from: &T::AccountId, to: &T::AccountId, amount: u64) -> DispatchResult { + // This was cleaner when it was the if statements, then mutation, which also followed the + // checks, effects, interactions pattern + // Doing so resulted in two DB lookups however, which was inefficient, and interactions + // before all checks are fine + // They used to not be, and require `transactional` to achieve that behavior, yet that's + // been deprecated for it to be the default behavior + Bond::::try_mutate(to, |bond| { + let available = *bond - Self::allocated(to); + if available < amount { + Err(Error::::BondUnavailable)?; + } + *bond -= amount; + Ok::<_, DispatchError>(()) + })?; + + Delegations::::try_mutate(from, to, |delegated| { + if *delegated < amount { + Err(Error::::InsufficientDelegation)?; + } + *delegated -= amount; + Ok(()) + }) + } + + fn add_stake(account: &T::AccountId, delegate: &T::AccountId, amount: u64) { + Staked::::mutate(account, |staked| *staked += amount); + Self::add_delegation(account, delegate, amount); + } + + fn remove_stake( + account: &T::AccountId, + delegate: &T::AccountId, + amount: u64, + ) -> DispatchResult { + Self::remove_delegation(account, delegate, amount)?; + Staked::::mutate(account, |staked| { + // Check this invariant in the name of safety + if *staked < amount { + panic!("delegations exceeded staked funds"); + } + *staked -= amount; + }); + Ok(()) + } + + fn move_delegation( + account: &T::AccountId, + from: &T::AccountId, + to: &T::AccountId, + amount: u64, + ) -> DispatchResult { + Self::remove_delegation(account, from, amount)?; + Self::add_delegation(account, to, amount); + Ok(()) + } + + fn allocate_internal(account: &T::AccountId, amount: u64) -> Result<(), Error> { + Allocated::::try_mutate(account, |allocated| { + let available = Self::bond(account) - *allocated; + if available < amount { + Err(Error::::BondUnavailable)?; + } + *allocated += amount; + Ok(()) + }) + } + + fn deallocate_internal(account: &T::AccountId, amount: u64) -> Result<(), Error> { + Allocated::::try_mutate(account, |allocated| { + if *allocated < amount { + panic!("deallocating more than allocated"); + } + *allocated -= amount; + Ok(()) + }) + } + } + + #[pallet::call] + impl Pallet { + /// Stake funds from this account, delegating them to the specified account. + #[pallet::weight(0)] // TODO + pub fn stake( + origin: OriginFor, + delegate: T::AccountId, + #[pallet::compact] amount: u64, + ) -> DispatchResult { + let signer = ensure_signed(origin)?; + // Serai accounts are solely public keys. Accordingly, there's no harm to letting accounts + // die. They'll simply be re-instantiated later + // AllowDeath accordingly to not add additional requirements (and therefore annoyances) + T::Currency::transfer(&signer, &Self::account(), amount, ExistenceRequirement::AllowDeath)?; + Self::add_stake(&signer, &delegate, amount); + Ok(()) + } + + /// Undelegate funds from one account, re-delegating to another. Only unallocated funds may be + /// redelegated. + #[pallet::weight(0)] // TODO + pub fn redelegate( + origin: OriginFor, + from: T::AccountId, + to: T::AccountId, + amount: u64, + ) -> DispatchResult { + let signer = ensure_signed(origin)?; + Self::move_delegation(&signer, &from, &to, amount)?; + Ok(()) + } + + /// Unstake funds currently delegated to the specified account. Only unallocated funds may be + /// unstaked. + #[pallet::weight(0)] // TODO + pub fn unstake( + origin: OriginFor, + delegate: T::AccountId, + #[pallet::compact] amount: u64, + ) -> DispatchResult { + let signer = ensure_signed(origin)?; + Self::remove_stake(&signer, &delegate, amount)?; + // This should never be out of funds as there should always be stakers. Accordingly... + T::Currency::transfer(&Self::account(), &signer, amount, ExistenceRequirement::KeepAlive)?; + Ok(()) + } + } + + impl AllocatedStaking for Pallet { + type Error = Error; + + fn allocate(account: &T::AccountId, amount: u64) -> Result<(), Error> { + Self::allocate_internal(account, amount) + } + fn deallocate(account: &T::AccountId, amount: u64) -> Result<(), Error> { + Self::deallocate_internal(account, amount) + } + } +} + +pub use pallet::*; diff --git a/substrate/staking/primitives/Cargo.toml b/substrate/staking/primitives/Cargo.toml new file mode 100644 index 000000000..3653f8111 --- /dev/null +++ b/substrate/staking/primitives/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "serai-staking-primitives" +version = "0.1.0" +description = "Staking primitives for Serai" +license = "AGPL-3.0-only" +repository = "https://github.com/serai-dex/serai/tree/develop/substrate/staking/pallet" +authors = ["Luke Parker "] +edition = "2021" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[dependencies] +frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false } + +[features] +std = [] +default = ["std"] diff --git a/substrate/staking/primitives/LICENSE b/substrate/staking/primitives/LICENSE new file mode 100644 index 000000000..d6e1814a2 --- /dev/null +++ b/substrate/staking/primitives/LICENSE @@ -0,0 +1,15 @@ +AGPL-3.0-only license + +Copyright (c) 2022 Luke Parker + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License Version 3 as +published by the Free Software Foundation. + +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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . diff --git a/substrate/staking/primitives/src/lib.rs b/substrate/staking/primitives/src/lib.rs new file mode 100644 index 000000000..b37f8e315 --- /dev/null +++ b/substrate/staking/primitives/src/lib.rs @@ -0,0 +1,12 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_system::pallet::Config; + +pub trait AllocatedStaking { + type Error; + + // TODO: Should this specify the allocator, handling tracking that as well? + // Or should each allocator have a map of AccountId -> u64 on their end? + fn allocate(account: &T::AccountId, amount: u64) -> Result<(), Self::Error>; + fn deallocate(account: &T::AccountId, amount: u64) -> Result<(), Self::Error>; +}