Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
maltekliemann committed Oct 3, 2024
1 parent ed146bb commit e24e474
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 19 deletions.
122 changes: 112 additions & 10 deletions zrml/combo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ mod pallet {
use crate::{traits::IdManager, types::Hash};
use core::marker::PhantomData;
use frame_support::{
ensure,
pallet_prelude::{IsType, StorageVersion},
require_transactional, transactional,
};
Expand All @@ -40,7 +41,7 @@ mod pallet {
pallet_prelude::{BlockNumberFor, OriginFor},
};
use orml_traits::MultiCurrency;
use sp_runtime::DispatchResult;
use sp_runtime::{DispatchError, DispatchResult};
use zeitgeist_primitives::{traits::MarketCommonsPalletApi, types::Asset};

#[pallet::config]
Expand All @@ -58,7 +59,11 @@ mod pallet {
#[pallet::storage_version(STORAGE_VERSION)]
pub struct Pallet<T>(PhantomData<T>);

pub(crate) type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
pub(crate) type AssetOf<T> = Asset<MarketIdOf<T>>;
pub(crate) type BalanceOf<T> =
<<T as Config>::MultiCurrency as MultiCurrency<AccountIdOf<T>>>::Balance;
pub(crate) type CollectionIdOf<T> = <<T as Config>::CollectionIdManager as IdManager>::Id;
pub(crate) type MarketIdOf<T> =
<<T as Config>::MarketCommons as MarketCommonsPalletApi>::MarketId;

Expand All @@ -74,36 +79,133 @@ mod pallet {
T: Config, {}

#[pallet::error]
pub enum Error<T> {}
pub enum Error<T> {
/// The specified partition is empty, contains overlaps or is too long.
InvalidPartition,

/// The specified collection ID is invalid.
InvalidCollectionId,
}

#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(0)] // TODO
#[transactional]
pub fn split_position(origin: OriginFor<T>) -> DispatchResult {
let _ = ensure_signed(origin)?;
Self::do_split_position()
pub fn split_position(
origin: OriginFor<T>,
// TODO Abstract this into a separate type.
parent_collection_id: Option<CollectionIdOf<T>>,
market_id: MarketIdOf<T>,
partition: Vec<Vec<bool>>,
amount: BalanceOf<T>,
) -> DispatchResult {
let who = ensure_signed(origin)?;
Self::do_split_position(who, parent_collection_id, market_id, partition, amount)
}

#[pallet::call_index(1)]
#[pallet::weight(0)] // TODO
#[transactional]
pub fn merge_position(origin: OriginFor<T>) -> DispatchResult {
let _ = ensure_signed(origin)?;
Self::do_merge_position()
pub fn merge_position(
origin: OriginFor<T>,
parent_collection_id: Option<CollectionIdOf<T>>,
market_id: MarketIdOf<T>,
partition: Vec<Vec<bool>>,
amount: BalanceOf<T>,
) -> DispatchResult {
let who = ensure_signed(origin)?;
Self::do_merge_position(who, parent_collection_id, market_id, partition, amount)
}
}

impl<T: Config> Pallet<T> {
#[require_transactional]
fn do_split_position() -> DispatchResult {
fn do_split_position(
who: AccountIdOf<T>,
parent_collection_id: Option<CollectionIdOf<T>>,
market_id: MarketIdOf<T>,
partition: Vec<Vec<bool>>,
_amount: BalanceOf<T>,
) -> DispatchResult {
let free_index_set = Self::free_index_set(parent_collection_id, market_id, &partition)?;

Ok(())
}

#[require_transactional]
fn do_merge_position() -> DispatchResult {
fn do_merge_position(
who: AccountIdOf<T>,
parent_collection_id: Option<CollectionIdOf<T>>,
market_id: MarketIdOf<T>,
partition: Vec<Vec<bool>>,
_amount: BalanceOf<T>,
) -> DispatchResult {
let free_index_set = Self::free_index_set(parent_collection_id, market_id, &partition)?;
let position_ids = partition
.iter()
.cloned()
.map(|index_set| Self::position(parent_collection_id, market_id, index_set))
.collect::<Result<Vec<_>, _>>()?;

for position in position_ids.iter() {
// TODO
}

Ok(())
}

fn free_index_set(
parent_collection_id: Option<CollectionIdOf<T>>,
market_id: MarketIdOf<T>,
partition: &Vec<Vec<bool>>,
) -> Result<Vec<bool>, DispatchError> {
let market = T::MarketCommons::market(&market_id)?;
let asset_count = market.outcomes() as usize;
let mut free_index_set = vec![true; asset_count];

for index_set in partition.iter() {
// Ensure that the partition is not trivial.
let ones = index_set.iter().fold(0usize, |acc, &val| acc + (val as usize));
ensure!(ones > 0, Error::<T>::InvalidPartition);
ensure!(ones < asset_count, Error::<T>::InvalidPartition);

// Ensure that `index_set` is disjoint from the previously iterated elements of the
// partition.
ensure!(
free_index_set.iter().zip(index_set.iter()).all(|(i, j)| *i || !*j),
Error::<T>::InvalidPartition
);

// Remove indices of `index_set` from `free_index_set`.
free_index_set =
free_index_set.iter().zip(index_set.iter()).map(|(i, j)| *i && !*j).collect();
}

Ok(free_index_set)
}

fn position(
parent_collection_id: Option<CollectionIdOf<T>>,
market_id: MarketIdOf<T>,
index_set: Vec<bool>,
) -> Result<CollectionIdOf<T>, DispatchError> {
let market = T::MarketCommons::market(&market_id)?;
let collateral_token = market.base_asset;

let collection_id = T::CollectionIdManager::get_collection_id(
parent_collection_id,
market_id,
index_set,
false, // TODO Expose this parameter!
)
.ok_or(Error::<T>::InvalidCollectionId)?;

let position_id =
T::CollectionIdManager::get_position_id(collateral_token, collection_id);

Ok(position_id)
// TODO Return Asset::Combinatorial(position_id) instead.
}
}
}
2 changes: 1 addition & 1 deletion zrml/combo/src/traits/id_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ pub(crate) trait IdManager {
fn get_position_id(
collateral: Self::Asset,
collection_id: Self::Id,
) -> Option<Self::Id>;
) -> Self::Id;
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ fn decompress_hash(hash: Hash, force_max_work: bool) -> Option<G1Affine> {
}
}
}
std::mem::forget(dummy_x); // Ensure that the dummies are considered "read" by rustc.
std::mem::forget(dummy_y);
std::hint::black_box(dummy_x); // Ensure that the dummies are considered "read" by rustc.
std::hint::black_box(dummy_y);
let mut y = y_opt?; // This **should** be infallible.

// We have two options for the y-coordinate of the corresponding point: `y` and `P - y`. If
Expand Down
115 changes: 115 additions & 0 deletions zrml/combo/src/types/cryptographic_id_manager/hash_tuple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use crate::types::Hash;
use frame_support::{Blake2_256, StorageHasher};
use parity_scale_codec::Encode;
use zeitgeist_primitives::types::Asset;

pub trait ToBytes {
fn to_bytes(&self) -> Vec<u8>;
}

pub trait HashTuple {
fn hash_tuple<T1, T2>(tuple: (T1, T2)) -> Hash
where
T1: ToBytes,
T2: ToBytes;
}

impl HashTuple for Blake2_256 {
fn hash_tuple<T1, T2>(tuple: (T1, T2)) -> Hash
where
T1: ToBytes,
T2: ToBytes,
{
let mut bytes = Vec::new();

bytes.extend_from_slice(&tuple.0.to_bytes());
bytes.extend_from_slice(&tuple.1.to_bytes());

Blake2_256::hash(&bytes)
}
}

/// Implements `ToBytes` for any type implementing `to_be_bytes`.
macro_rules! impl_to_bytes {
($($t:ty),*) => {
$(
impl ToBytes for $t {
fn to_bytes(&self) -> Vec<u8> {
self.to_be_bytes().to_vec()
}
}
)*
};
}

impl_to_bytes!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);

impl ToBytes for bool {
fn to_bytes(&self) -> Vec<u8> {
vec![*self as u8]
}
}

impl ToBytes for Hash {
fn to_bytes(&self) -> Vec<u8> {
self.to_vec()
}
}

impl<T> ToBytes for Vec<T>
where
T: ToBytes,
{
fn to_bytes(&self) -> Vec<u8> {
let mut result = Vec::new();

for b in self.iter() {
result.extend_from_slice(&b.to_bytes());
}

result
}
}

/// Beware! All changes to this implementation need to be backwards compatible. Failure to follow this
/// restriction will result in assets changing hashes between versions, causing unreachable funds.
///
/// Of course, this is true of any modification of the collection ID manager, but this is the place
/// where it's most likely to happen. We're using tests below to ensure that unintentional changes
/// are caught.
impl<MarketId> ToBytes for Asset<MarketId>
where
MarketId: Encode,
{
fn to_bytes(&self) -> Vec<u8> {
self.encode()
}
}

#[cfg(test)]
mod tests {
use super::*;
use test_case::test_case;

type MarketId = u128;

// Beware! If you have to modify these tests, that means that you broke encoding of assets in a
// way that's not backwards compatible.
#[test_case(Asset::Ztg, vec![4])]
#[test_case(Asset::ForeignAsset(0), vec![5, 0, 0, 0, 0])]
#[test_case(Asset::ForeignAsset(1), vec![5, 1, 0, 0, 0])]
#[test_case(Asset::ForeignAsset(2), vec![5, 2, 0, 0, 0])]
#[test_case(Asset::ForeignAsset(3), vec![5, 3, 0, 0, 0])]
#[test_case(Asset::ForeignAsset(4), vec![5, 4, 0, 0, 0])]
#[test_case(Asset::ForeignAsset(5), vec![5, 5, 0, 0, 0])]
#[test_case(Asset::ForeignAsset(6), vec![5, 6, 0, 0, 0])]
#[test_case(Asset::ForeignAsset(7), vec![5, 7, 0, 0, 0])]
#[test_case(Asset::ForeignAsset(8), vec![5, 8, 0, 0, 0])]
#[test_case(Asset::ForeignAsset(9), vec![5, 9, 0, 0, 0])]
#[test_case(Asset::ForeignAsset(u32::MAX - 1), vec![5, 254, 255, 255, 255])]
#[test_case(Asset::ForeignAsset(u32::MAX), vec![5, 255, 255, 255, 255])]
fn asset_to_bytes_works(asset: Asset<MarketId>, expected: Vec<u8>) {
let actual = asset.to_bytes();
assert_eq!(actual, expected);
}
}
9 changes: 4 additions & 5 deletions zrml/combo/src/types/cryptographic_id_manager/mod.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
mod decompressor;
mod hash_tuple;

use crate::traits::IdManager;
use crate::{traits::IdManager, types::Hash};
use core::marker::PhantomData;
use ethnum::U256;
use frame_support::{Blake2_256, StorageHasher};
use hash_tuple::{HashTuple, ToBytes};
use parity_scale_codec::Encode;
use sp_runtime::DispatchError;
use crate::types::Hash;
use zeitgeist_primitives::types::Asset;

pub(crate) struct CryptographicIdManager<MarketId, Hasher>(PhantomData<(MarketId, Hasher)>);

impl<MarketId, Hasher> IdManager for CryptographicIdManager<MarketId, Hasher>
where
MarketId: ToBytes + Encode,
Hasher: HashTuple
Hasher: HashTuple,
{
type Asset = Asset<MarketId>;
type Id = Hash;
Expand All @@ -33,8 +32,8 @@ where
decompressor::get_collection_id(hash, parent_collection_id, force_max_work)
}

fn get_position_id(collateral: Self::Asset, collection_id: Self::Id) -> Option<Self::Id> {
fn get_position_id(collateral: Self::Asset, collection_id: Self::Id) -> Self::Id {
let input = (collateral, collection_id);
Some(Hasher::hash_tuple(input))
Hasher::hash_tuple(input)
}
}
1 change: 0 additions & 1 deletion zrml/combo/src/types/cryptographic_id_manager/typedefs.rs

This file was deleted.

0 comments on commit e24e474

Please sign in to comment.