Skip to content

Commit bc3b4b9

Browse files
committed
Raffle Demo
1 parent 47a6de8 commit bc3b4b9

29 files changed

+1917
-6
lines changed

Cargo.lock

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2792,6 +2792,39 @@ dependencies = [
27922792
"substrate-api-client",
27932793
]
27942794

2795+
[[package]]
2796+
name = "ita-raffle-stf"
2797+
version = "0.9.0"
2798+
dependencies = [
2799+
"frame-support",
2800+
"frame-system",
2801+
"ita-sgx-runtime",
2802+
"itp-hashing",
2803+
"itp-node-api",
2804+
"itp-node-api-metadata",
2805+
"itp-sgx-externalities",
2806+
"itp-sgx-runtime-primitives",
2807+
"itp-stf-interface",
2808+
"itp-stf-primitives",
2809+
"itp-storage",
2810+
"itp-types",
2811+
"itp-utils",
2812+
"log 0.4.20",
2813+
"pallet-balances",
2814+
"pallet-parentchain",
2815+
"pallet-raffles",
2816+
"pallet-sudo",
2817+
"parity-scale-codec",
2818+
"rlp",
2819+
"sgx_tstd",
2820+
"sha3",
2821+
"sp-core",
2822+
"sp-io 7.0.0",
2823+
"sp-keyring",
2824+
"sp-runtime",
2825+
"sp-std",
2826+
]
2827+
27952828
[[package]]
27962829
name = "ita-sgx-runtime"
27972830
version = "0.9.0"
@@ -2803,11 +2836,13 @@ dependencies = [
28032836
"pallet-balances",
28042837
"pallet-evm",
28052838
"pallet-parentchain",
2839+
"pallet-raffles",
28062840
"pallet-sudo",
28072841
"pallet-timestamp",
28082842
"pallet-transaction-payment",
28092843
"parity-scale-codec",
28102844
"scale-info",
2845+
"sgx_rand",
28112846
"sp-api",
28122847
"sp-core",
28132848
"sp-runtime",
@@ -2821,6 +2856,7 @@ version = "0.9.0"
28212856
dependencies = [
28222857
"frame-support",
28232858
"frame-system",
2859+
"ita-raffle-stf",
28242860
"ita-sgx-runtime",
28252861
"itp-hashing",
28262862
"itp-node-api",
@@ -5177,6 +5213,26 @@ dependencies = [
51775213
"sp-std",
51785214
]
51795215

5216+
[[package]]
5217+
name = "pallet-raffles"
5218+
version = "0.11.0"
5219+
dependencies = [
5220+
"env_logger 0.9.3",
5221+
"frame-support",
5222+
"frame-system",
5223+
"itp-binary-merkle-tree",
5224+
"log 0.4.20",
5225+
"pallet-balances",
5226+
"parity-scale-codec",
5227+
"scale-info",
5228+
"serde 1.0.193",
5229+
"sp-core",
5230+
"sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)",
5231+
"sp-keyring",
5232+
"sp-runtime",
5233+
"sp-std",
5234+
]
5235+
51805236
[[package]]
51815237
name = "pallet-sidechain"
51825238
version = "0.11.0"

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ resolver = "2"
33
members = [
44
"app-libs/oracle",
55
"app-libs/parentchain-interface",
6+
"app-libs/raffle/stf",
7+
"app-libs/raffle/pallet-raffle",
68
"app-libs/sgx-runtime",
79
"app-libs/stf",
810
"cli",
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
[package]
2+
name = "pallet-raffles"
3+
description = "The remote attestation registry and verification pallet for integritee blockchains and parachains"
4+
version = "0.11.0"
5+
authors = ["Integritee AG <[email protected]>"]
6+
homepage = "https://integritee.network/"
7+
repository = "https://github.com/integritee-network/pallets/"
8+
license = "Apache-2.0"
9+
edition = "2021"
10+
11+
[dependencies]
12+
log = { version = "0.4", default-features = false }
13+
parity-scale-codec = { version = "3.0.0", default-features = false, features = ["derive"] }
14+
scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }
15+
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
16+
17+
itp-binary-merkle-tree = { default-features = false, path = "../../../core-primitives/binary-merkle-tree" }
18+
19+
# substrate dependencies
20+
frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }
21+
frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }
22+
sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }
23+
sp-io = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }
24+
sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }
25+
sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }
26+
27+
[dev-dependencies]
28+
env_logger = "0.9.0"
29+
pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }
30+
sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }
31+
32+
[features]
33+
default = ["std"]
34+
std = [
35+
"frame-support/std",
36+
"frame-system/std",
37+
"log/std",
38+
"itp-binary-merkle-tree/std",
39+
"parity-scale-codec/std",
40+
"scale-info/std",
41+
"serde/std",
42+
"sp-core/std",
43+
"sp-io/std",
44+
"sp-runtime/std",
45+
"sp-std/std",
46+
]
47+
48+
try-runtime = ["frame-support/try-runtime"]
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
#![cfg_attr(not(feature = "std"), no_std)]
2+
3+
use frame_support::{dispatch::DispatchResult, ensure};
4+
use itp_binary_merkle_tree::{merkle_proof, merkle_root, MerkleProofWithCodec};
5+
use parity_scale_codec::{Decode, Encode};
6+
use scale_info::TypeInfo;
7+
use sp_core::{MaxEncodedLen, H256};
8+
use sp_runtime::traits::Keccak256;
9+
use sp_std::{fmt::Debug, vec::Vec};
10+
11+
pub use pallet::*;
12+
13+
pub mod merkle_tree {
14+
pub use itp_binary_merkle_tree::{
15+
merkle_proof, merkle_root, verify_proof, MerkleProofWithCodec,
16+
};
17+
pub use sp_runtime::traits::Keccak256;
18+
}
19+
20+
#[cfg(test)]
21+
mod mock;
22+
#[cfg(test)]
23+
mod tests;
24+
pub mod weights;
25+
26+
pub type RaffleIndex = u32;
27+
pub type WinnerCount = u32;
28+
29+
#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq, TypeInfo, MaxEncodedLen)]
30+
pub struct RaffleMetadata<AccountId: Debug> {
31+
index: RaffleIndex,
32+
raffle: Raffle<AccountId>,
33+
}
34+
35+
#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq, TypeInfo, MaxEncodedLen)]
36+
pub struct Raffle<AccountId: Debug> {
37+
owner: AccountId,
38+
winner_count: WinnerCount,
39+
registration_open: bool,
40+
}
41+
42+
#[frame_support::pallet]
43+
pub mod pallet {
44+
use crate::{weights::WeightInfo, Raffle, RaffleIndex, Shuffle, WinnerCount};
45+
use frame_support::pallet_prelude::*;
46+
use frame_system::pallet_prelude::*;
47+
use sp_core::H256;
48+
use sp_std::vec::Vec;
49+
50+
const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
51+
#[pallet::pallet]
52+
#[pallet::storage_version(STORAGE_VERSION)]
53+
#[pallet::without_storage_info]
54+
pub struct Pallet<T>(PhantomData<T>);
55+
56+
/// Configuration trait.
57+
#[pallet::config]
58+
pub trait Config: frame_system::Config {
59+
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
60+
type WeightInfo: WeightInfo;
61+
62+
/// Implements random shuffling of values.
63+
///
64+
/// If you use this on-chain you need to make sure to have a deterministic seed based
65+
/// on-chain values. If you use this in SGX, we want to make sure that the randomness
66+
/// is as secure as possible, hence we use the SGX's randomness, which uses a hardware
67+
/// secured randomness source: https://en.wikipedia.org/wiki/RDRAND.
68+
type Shuffle: Shuffle;
69+
}
70+
71+
#[pallet::event]
72+
#[pallet::generate_deposit(pub(super) fn deposit_event)]
73+
pub enum Event<T: Config> {
74+
/// A new raffle has been registered
75+
RaffleAdded { index: RaffleIndex, raffle: Raffle<T::AccountId> },
76+
77+
/// Someone has registered for a raffle
78+
RaffleRegistration { who: T::AccountId, index: RaffleIndex },
79+
80+
/// Winners were drawn of a raffle
81+
WinnersDrawn { index: RaffleIndex, winners: Vec<T::AccountId>, registrations_root: H256 },
82+
}
83+
84+
#[pallet::error]
85+
pub enum Error<T> {
86+
/// The raffle does not exist
87+
NonexistentRaffle,
88+
/// The registrations for that raffles are closed
89+
RegistrationsClosed,
90+
/// Only the raffle owner can draw the winners
91+
OnlyRaffleOwnerCanDrawWinners,
92+
}
93+
94+
/// Ongoing raffles.
95+
#[pallet::storage]
96+
#[pallet::getter(fn ongoing_raffles)]
97+
pub type OnGoingRaffles<T: Config> =
98+
StorageMap<_, Blake2_128Concat, RaffleIndex, Raffle<T::AccountId>, OptionQuery>;
99+
100+
/// Ongoing raffles.
101+
#[pallet::storage]
102+
#[pallet::getter(fn winners)]
103+
pub type Winners<T: Config> =
104+
StorageMap<_, Blake2_128Concat, RaffleIndex, Vec<T::AccountId>, OptionQuery>;
105+
106+
#[pallet::storage]
107+
#[pallet::getter(fn registrations)]
108+
pub type Registrations<T: Config> = StorageDoubleMap<
109+
_,
110+
Blake2_128Concat,
111+
RaffleIndex,
112+
Blake2_128Concat,
113+
T::AccountId,
114+
(),
115+
OptionQuery,
116+
>;
117+
118+
#[pallet::storage]
119+
#[pallet::getter(fn raffle_count)]
120+
pub type RaffleCount<T> = StorageValue<_, RaffleIndex, ValueQuery>;
121+
122+
#[pallet::hooks]
123+
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
124+
125+
#[pallet::call]
126+
impl<T: Config> Pallet<T> {
127+
#[pallet::call_index(0)]
128+
#[pallet::weight(T::WeightInfo::add_raffle())]
129+
pub fn add_raffle(origin: OriginFor<T>, winner_count: WinnerCount) -> DispatchResult {
130+
let sender = ensure_signed(origin)?;
131+
let index = Self::raffle_count();
132+
133+
let raffle = Raffle { owner: sender, winner_count, registration_open: true };
134+
135+
OnGoingRaffles::<T>::insert(index, &raffle);
136+
RaffleCount::<T>::put(index + 1);
137+
138+
Self::deposit_event(Event::RaffleAdded { index, raffle });
139+
Ok(())
140+
}
141+
142+
#[pallet::call_index(1)]
143+
#[pallet::weight(T::WeightInfo::register_for_raffle())]
144+
pub fn register_for_raffle(origin: OriginFor<T>, index: RaffleIndex) -> DispatchResult {
145+
let sender = ensure_signed(origin)?;
146+
147+
ensure!(OnGoingRaffles::<T>::contains_key(index), Error::<T>::NonexistentRaffle);
148+
ensure!(
149+
OnGoingRaffles::<T>::get(index)
150+
.expect("Asserted above that the key exists; qed")
151+
.registration_open,
152+
Error::<T>::RegistrationsClosed
153+
);
154+
155+
Registrations::<T>::insert(index, &sender, ());
156+
157+
Self::deposit_event(Event::RaffleRegistration { who: sender, index });
158+
Ok(())
159+
}
160+
161+
#[pallet::call_index(2)]
162+
#[pallet::weight(T::WeightInfo::draw_winners())]
163+
pub fn draw_winners(origin: OriginFor<T>, index: RaffleIndex) -> DispatchResult {
164+
let sender = ensure_signed(origin)?;
165+
Self::try_draw_winners(sender, index)
166+
}
167+
}
168+
}
169+
170+
impl<T: Config> Pallet<T> {
171+
pub fn all_ongoing_raffles() -> Vec<RaffleMetadata<T::AccountId>> {
172+
OnGoingRaffles::<T>::iter()
173+
.map(|kv| RaffleMetadata { index: kv.0, raffle: kv.1 })
174+
.collect()
175+
}
176+
177+
pub fn raffle_registrations(index: RaffleIndex) -> Vec<T::AccountId> {
178+
Registrations::<T>::iter_prefix(index).map(|kv| kv.0).collect()
179+
}
180+
181+
fn try_draw_winners(owner: T::AccountId, index: RaffleIndex) -> DispatchResult {
182+
let raffle = OnGoingRaffles::<T>::get(index).ok_or(Error::<T>::NonexistentRaffle)?;
183+
ensure!(raffle.registration_open, Error::<T>::RegistrationsClosed);
184+
ensure!(raffle.owner == owner, Error::<T>::OnlyRaffleOwnerCanDrawWinners);
185+
186+
let mut registrations = Self::raffle_registrations(index);
187+
let registrations_root = Self::merkle_root(&registrations);
188+
<T as Config>::Shuffle::shuffle(&mut registrations);
189+
190+
let count = core::cmp::min(registrations.len(), raffle.winner_count as usize);
191+
let winners = registrations[..count].to_vec();
192+
193+
OnGoingRaffles::<T>::mutate(index, |r| r.as_mut().map(|r| r.registration_open = false));
194+
Winners::<T>::insert(index, &winners);
195+
196+
Self::deposit_event(Event::WinnersDrawn { index, winners, registrations_root });
197+
Ok(())
198+
}
199+
200+
pub fn merkle_proof_for_registration(
201+
index: RaffleIndex,
202+
account: &T::AccountId,
203+
) -> Option<MerkleProofWithCodec<H256, Vec<u8>>> {
204+
let registrations = Self::raffle_registrations(index);
205+
Self::merkle_proof(account, &registrations)
206+
}
207+
208+
pub(crate) fn merkle_root(accounts: &[T::AccountId]) -> H256 {
209+
merkle_root::<Keccak256, _>(accounts.iter().map(Encode::encode))
210+
}
211+
212+
pub(crate) fn merkle_proof(
213+
account: &T::AccountId,
214+
registrations: &[T::AccountId],
215+
) -> Option<MerkleProofWithCodec<H256, Vec<u8>>> {
216+
let leaf_index = Self::merkle_leaf_index_for_registration(account, registrations)?;
217+
let p =
218+
merkle_proof::<Keccak256, _, _>(registrations.iter().map(Encode::encode), leaf_index);
219+
Some(p.into())
220+
}
221+
222+
pub(crate) fn merkle_leaf_index_for_registration(
223+
account: &T::AccountId,
224+
registrations: &[T::AccountId],
225+
) -> Option<usize> {
226+
registrations.iter().position(|a| a == account)
227+
}
228+
}
229+
230+
pub trait Shuffle {
231+
fn shuffle<T>(values: &mut [T]);
232+
}

0 commit comments

Comments
 (0)