Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Raffle Demo #1583

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
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
Loading