Skip to content

Commit

Permalink
Raffle Demo
Browse files Browse the repository at this point in the history
  • Loading branch information
clangenb committed Mar 11, 2024
1 parent 47a6de8 commit bc3b4b9
Show file tree
Hide file tree
Showing 29 changed files with 1,917 additions and 6 deletions.
56 changes: 56 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2792,6 +2792,39 @@ dependencies = [
"substrate-api-client",
]

[[package]]
name = "ita-raffle-stf"
version = "0.9.0"
dependencies = [
"frame-support",
"frame-system",
"ita-sgx-runtime",
"itp-hashing",
"itp-node-api",
"itp-node-api-metadata",
"itp-sgx-externalities",
"itp-sgx-runtime-primitives",
"itp-stf-interface",
"itp-stf-primitives",
"itp-storage",
"itp-types",
"itp-utils",
"log 0.4.20",
"pallet-balances",
"pallet-parentchain",
"pallet-raffles",
"pallet-sudo",
"parity-scale-codec",
"rlp",
"sgx_tstd",
"sha3",
"sp-core",
"sp-io 7.0.0",
"sp-keyring",
"sp-runtime",
"sp-std",
]

[[package]]
name = "ita-sgx-runtime"
version = "0.9.0"
Expand All @@ -2803,11 +2836,13 @@ dependencies = [
"pallet-balances",
"pallet-evm",
"pallet-parentchain",
"pallet-raffles",
"pallet-sudo",
"pallet-timestamp",
"pallet-transaction-payment",
"parity-scale-codec",
"scale-info",
"sgx_rand",
"sp-api",
"sp-core",
"sp-runtime",
Expand All @@ -2821,6 +2856,7 @@ version = "0.9.0"
dependencies = [
"frame-support",
"frame-system",
"ita-raffle-stf",
"ita-sgx-runtime",
"itp-hashing",
"itp-node-api",
Expand Down Expand Up @@ -5177,6 +5213,26 @@ dependencies = [
"sp-std",
]

[[package]]
name = "pallet-raffles"
version = "0.11.0"
dependencies = [
"env_logger 0.9.3",
"frame-support",
"frame-system",
"itp-binary-merkle-tree",
"log 0.4.20",
"pallet-balances",
"parity-scale-codec",
"scale-info",
"serde 1.0.193",
"sp-core",
"sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)",
"sp-keyring",
"sp-runtime",
"sp-std",
]

[[package]]
name = "pallet-sidechain"
version = "0.11.0"
Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ resolver = "2"
members = [
"app-libs/oracle",
"app-libs/parentchain-interface",
"app-libs/raffle/stf",
"app-libs/raffle/pallet-raffle",
"app-libs/sgx-runtime",
"app-libs/stf",
"cli",
Expand Down
48 changes: 48 additions & 0 deletions app-libs/raffle/pallet-raffle/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[package]
name = "pallet-raffles"
description = "The remote attestation registry and verification pallet for integritee blockchains and parachains"
version = "0.11.0"
authors = ["Integritee AG <[email protected]>"]
homepage = "https://integritee.network/"
repository = "https://github.com/integritee-network/pallets/"
license = "Apache-2.0"
edition = "2021"

[dependencies]
log = { version = "0.4", default-features = false }
parity-scale-codec = { version = "3.0.0", default-features = false, features = ["derive"] }
scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }

itp-binary-merkle-tree = { default-features = false, path = "../../../core-primitives/binary-merkle-tree" }

# substrate dependencies
frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }
frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }
sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }
sp-io = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }
sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }
sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }

[dev-dependencies]
env_logger = "0.9.0"
pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }
sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }

[features]
default = ["std"]
std = [
"frame-support/std",
"frame-system/std",
"log/std",
"itp-binary-merkle-tree/std",
"parity-scale-codec/std",
"scale-info/std",
"serde/std",
"sp-core/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
]

try-runtime = ["frame-support/try-runtime"]
232 changes: 232 additions & 0 deletions app-libs/raffle/pallet-raffle/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::{dispatch::DispatchResult, ensure};
use itp_binary_merkle_tree::{merkle_proof, merkle_root, MerkleProofWithCodec};
use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
use sp_core::{MaxEncodedLen, H256};
use sp_runtime::traits::Keccak256;
use sp_std::{fmt::Debug, vec::Vec};

pub use pallet::*;

pub mod merkle_tree {
pub use itp_binary_merkle_tree::{
merkle_proof, merkle_root, verify_proof, MerkleProofWithCodec,
};
pub use sp_runtime::traits::Keccak256;
}

#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
pub mod weights;

pub type RaffleIndex = u32;
pub type WinnerCount = u32;

#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq, TypeInfo, MaxEncodedLen)]
pub struct RaffleMetadata<AccountId: Debug> {
index: RaffleIndex,
raffle: Raffle<AccountId>,
}

#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq, TypeInfo, MaxEncodedLen)]
pub struct Raffle<AccountId: Debug> {
owner: AccountId,
winner_count: WinnerCount,
registration_open: bool,
}

#[frame_support::pallet]
pub mod pallet {
use crate::{weights::WeightInfo, Raffle, RaffleIndex, Shuffle, WinnerCount};
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use sp_core::H256;
use sp_std::vec::Vec;

const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
#[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
#[pallet::without_storage_info]
pub struct Pallet<T>(PhantomData<T>);

/// Configuration trait.
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type WeightInfo: WeightInfo;

/// Implements random shuffling of values.
///
/// If you use this on-chain you need to make sure to have a deterministic seed based
/// on-chain values. If you use this in SGX, we want to make sure that the randomness
/// is as secure as possible, hence we use the SGX's randomness, which uses a hardware
/// secured randomness source: https://en.wikipedia.org/wiki/RDRAND.
type Shuffle: Shuffle;
}

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// A new raffle has been registered
RaffleAdded { index: RaffleIndex, raffle: Raffle<T::AccountId> },

/// Someone has registered for a raffle
RaffleRegistration { who: T::AccountId, index: RaffleIndex },

/// Winners were drawn of a raffle
WinnersDrawn { index: RaffleIndex, winners: Vec<T::AccountId>, registrations_root: H256 },
}

#[pallet::error]
pub enum Error<T> {
/// The raffle does not exist
NonexistentRaffle,
/// The registrations for that raffles are closed
RegistrationsClosed,
/// Only the raffle owner can draw the winners
OnlyRaffleOwnerCanDrawWinners,
}

/// Ongoing raffles.
#[pallet::storage]
#[pallet::getter(fn ongoing_raffles)]
pub type OnGoingRaffles<T: Config> =
StorageMap<_, Blake2_128Concat, RaffleIndex, Raffle<T::AccountId>, OptionQuery>;

/// Ongoing raffles.
#[pallet::storage]
#[pallet::getter(fn winners)]
pub type Winners<T: Config> =
StorageMap<_, Blake2_128Concat, RaffleIndex, Vec<T::AccountId>, OptionQuery>;

#[pallet::storage]
#[pallet::getter(fn registrations)]
pub type Registrations<T: Config> = StorageDoubleMap<
_,
Blake2_128Concat,
RaffleIndex,
Blake2_128Concat,
T::AccountId,
(),
OptionQuery,
>;

#[pallet::storage]
#[pallet::getter(fn raffle_count)]
pub type RaffleCount<T> = StorageValue<_, RaffleIndex, ValueQuery>;

#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}

#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::add_raffle())]
pub fn add_raffle(origin: OriginFor<T>, winner_count: WinnerCount) -> DispatchResult {
let sender = ensure_signed(origin)?;
let index = Self::raffle_count();

let raffle = Raffle { owner: sender, winner_count, registration_open: true };

OnGoingRaffles::<T>::insert(index, &raffle);
RaffleCount::<T>::put(index + 1);

Self::deposit_event(Event::RaffleAdded { index, raffle });
Ok(())
}

#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::register_for_raffle())]
pub fn register_for_raffle(origin: OriginFor<T>, index: RaffleIndex) -> DispatchResult {
let sender = ensure_signed(origin)?;

ensure!(OnGoingRaffles::<T>::contains_key(index), Error::<T>::NonexistentRaffle);
ensure!(
OnGoingRaffles::<T>::get(index)
.expect("Asserted above that the key exists; qed")
.registration_open,
Error::<T>::RegistrationsClosed
);

Registrations::<T>::insert(index, &sender, ());

Self::deposit_event(Event::RaffleRegistration { who: sender, index });
Ok(())
}

#[pallet::call_index(2)]
#[pallet::weight(T::WeightInfo::draw_winners())]
pub fn draw_winners(origin: OriginFor<T>, index: RaffleIndex) -> DispatchResult {
let sender = ensure_signed(origin)?;
Self::try_draw_winners(sender, index)
}
}
}

impl<T: Config> Pallet<T> {
pub fn all_ongoing_raffles() -> Vec<RaffleMetadata<T::AccountId>> {
OnGoingRaffles::<T>::iter()
.map(|kv| RaffleMetadata { index: kv.0, raffle: kv.1 })
.collect()
}

pub fn raffle_registrations(index: RaffleIndex) -> Vec<T::AccountId> {
Registrations::<T>::iter_prefix(index).map(|kv| kv.0).collect()
}

fn try_draw_winners(owner: T::AccountId, index: RaffleIndex) -> DispatchResult {
let raffle = OnGoingRaffles::<T>::get(index).ok_or(Error::<T>::NonexistentRaffle)?;
ensure!(raffle.registration_open, Error::<T>::RegistrationsClosed);
ensure!(raffle.owner == owner, Error::<T>::OnlyRaffleOwnerCanDrawWinners);

let mut registrations = Self::raffle_registrations(index);
let registrations_root = Self::merkle_root(&registrations);
<T as Config>::Shuffle::shuffle(&mut registrations);

let count = core::cmp::min(registrations.len(), raffle.winner_count as usize);
let winners = registrations[..count].to_vec();

OnGoingRaffles::<T>::mutate(index, |r| r.as_mut().map(|r| r.registration_open = false));
Winners::<T>::insert(index, &winners);

Self::deposit_event(Event::WinnersDrawn { index, winners, registrations_root });
Ok(())
}

pub fn merkle_proof_for_registration(
index: RaffleIndex,
account: &T::AccountId,
) -> Option<MerkleProofWithCodec<H256, Vec<u8>>> {
let registrations = Self::raffle_registrations(index);
Self::merkle_proof(account, &registrations)
}

pub(crate) fn merkle_root(accounts: &[T::AccountId]) -> H256 {
merkle_root::<Keccak256, _>(accounts.iter().map(Encode::encode))
}

pub(crate) fn merkle_proof(
account: &T::AccountId,
registrations: &[T::AccountId],
) -> Option<MerkleProofWithCodec<H256, Vec<u8>>> {
let leaf_index = Self::merkle_leaf_index_for_registration(account, registrations)?;
let p =
merkle_proof::<Keccak256, _, _>(registrations.iter().map(Encode::encode), leaf_index);
Some(p.into())
}

pub(crate) fn merkle_leaf_index_for_registration(
account: &T::AccountId,
registrations: &[T::AccountId],
) -> Option<usize> {
registrations.iter().position(|a| a == account)
}
}

pub trait Shuffle {
fn shuffle<T>(values: &mut [T]);
}
Loading

0 comments on commit bc3b4b9

Please sign in to comment.