From 27b3db0bdca2376085431de1e827d0eb60237c41 Mon Sep 17 00:00:00 2001 From: Malte Kliemann Date: Wed, 16 Oct 2024 20:20:46 +0200 Subject: [PATCH] Scaffold futarchy pallet (#1378) * Scaffold futarchy pallet * Scaffold test suite * . * . --- Cargo.lock | 25 +++++ Cargo.toml | 3 + runtime/battery-station/Cargo.toml | 4 + runtime/common/src/lib.rs | 8 ++ runtime/zeitgeist/Cargo.toml | 4 + zrml/futarchy/Cargo.toml | 58 ++++++++++++ zrml/futarchy/README.md | 1 + zrml/futarchy/src/lib.rs | 99 +++++++++++++++++++ zrml/futarchy/src/mock/ext_builder.rs | 72 ++++++++++++++ zrml/futarchy/src/mock/mod.rs | 21 +++++ zrml/futarchy/src/mock/runtime.rs | 131 ++++++++++++++++++++++++++ zrml/futarchy/src/tests/mod.rs | 18 ++++ zrml/futarchy/src/traits/mod.rs | 16 ++++ zrml/futarchy/src/types/mod.rs | 16 ++++ 14 files changed, 476 insertions(+) create mode 100644 zrml/futarchy/Cargo.toml create mode 100644 zrml/futarchy/README.md create mode 100644 zrml/futarchy/src/lib.rs create mode 100644 zrml/futarchy/src/mock/ext_builder.rs create mode 100644 zrml/futarchy/src/mock/mod.rs create mode 100644 zrml/futarchy/src/mock/runtime.rs create mode 100644 zrml/futarchy/src/tests/mod.rs create mode 100644 zrml/futarchy/src/traits/mod.rs create mode 100644 zrml/futarchy/src/types/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 3b4ac9ba8..81586cbd9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -894,6 +894,7 @@ dependencies = [ "zrml-authorized", "zrml-combinatorial-tokens", "zrml-court", + "zrml-futarchy", "zrml-global-disputes", "zrml-hybrid-router", "zrml-market-commons", @@ -15117,6 +15118,7 @@ dependencies = [ "zrml-authorized", "zrml-combinatorial-tokens", "zrml-court", + "zrml-futarchy", "zrml-global-disputes", "zrml-hybrid-router", "zrml-market-commons", @@ -15240,6 +15242,29 @@ dependencies = [ "zrml-market-commons", ] +[[package]] +name = "zrml-futarchy" +version = "0.5.5" +dependencies = [ + "env_logger 0.10.2", + "frame-benchmarking", + "frame-support", + "frame-system", + "orml-currencies", + "orml-tokens", + "orml-traits", + "pallet-balances", + "pallet-preimage", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "test-case", + "zeitgeist-primitives", + "zrml-futarchy", +] + [[package]] name = "zrml-global-disputes" version = "0.5.5" diff --git a/Cargo.toml b/Cargo.toml index 70a221993..bd41ad2d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ default-members = [ "zrml/authorized", "zrml/combinatorial-tokens", "zrml/court", + "zrml/futarchy", "zrml/hybrid-router", "zrml/global-disputes", "zrml/market-commons", @@ -39,6 +40,7 @@ members = [ "zrml/authorized", "zrml/combinatorial-tokens", "zrml/court", + "zrml/futarchy", "zrml/hybrid-router", "zrml/global-disputes", "zrml/market-commons", @@ -247,6 +249,7 @@ zeitgeist-macros = { path = "macros", default-features = false } zeitgeist-primitives = { path = "primitives", default-features = false } zrml-authorized = { path = "zrml/authorized", default-features = false } zrml-combinatorial-tokens = { path = "zrml/combinatorial-tokens", default-features = false } +zrml-futarchy = { path = "zrml/futarchy", default-features = false } zrml-court = { path = "zrml/court", default-features = false } zrml-global-disputes = { path = "zrml/global-disputes", default-features = false } zrml-hybrid-router = { path = "zrml/hybrid-router", default-features = false } diff --git a/runtime/battery-station/Cargo.toml b/runtime/battery-station/Cargo.toml index 01359557e..43f71abcb 100644 --- a/runtime/battery-station/Cargo.toml +++ b/runtime/battery-station/Cargo.toml @@ -111,6 +111,7 @@ zeitgeist-primitives = { workspace = true } zrml-authorized = { workspace = true } zrml-combinatorial-tokens = { workspace = true } zrml-court = { workspace = true } +zrml-futarchy = { workspace = true } zrml-global-disputes = { workspace = true, optional = true } zrml-hybrid-router = { workspace = true } zrml-market-commons = { workspace = true } @@ -217,6 +218,7 @@ runtime-benchmarks = [ "zrml-authorized/runtime-benchmarks", "zrml-combinatorial-tokens/runtime-benchmarks", "zrml-court/runtime-benchmarks", + "zrml-futarchy/runtime-benchmarks", "zrml-hybrid-router/runtime-benchmarks", "zrml-neo-swaps/runtime-benchmarks", "zrml-parimutuel/runtime-benchmarks", @@ -331,6 +333,7 @@ std = [ "zrml-authorized/std", "zrml-combinatorial-tokens/std", "zrml-court/std", + "zrml-futarchy/std", "zrml-hybrid-router/std", "zrml-market-commons/std", "zrml-neo-swaps/std", @@ -386,6 +389,7 @@ try-runtime = [ "zrml-authorized/try-runtime", "zrml-combinatorial-tokens/try-runtime", "zrml-court/try-runtime", + "zrml-futarchy/try-runtime", "zrml-hybrid-router/try-runtime", "zrml-market-commons/try-runtime", "zrml-neo-swaps/try-runtime", diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index e47db9fe5..20a3fcecc 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -360,6 +360,7 @@ macro_rules! create_runtime { Parimutuel: zrml_parimutuel::{Call, Event, Pallet, Storage} = 62, HybridRouter: zrml_hybrid_router::{Call, Event, Pallet, Storage} = 64, CombinatorialTokens: zrml_combinatorial_tokens::{Call, Event, Pallet, Storage} = 65, + Futarchy: zrml_futarchy::{Call, Event, Pallet, Storage} = 66, $($additional_pallets)* } @@ -1205,6 +1206,13 @@ macro_rules! impl_config_traits { type WeightInfo = zrml_court::weights::WeightInfo; } + impl zrml_futarchy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type MultiCurrency = AssetManager; + type Preimages = Preimage; + type SubmitOrigin = EnsureRoot; + } + impl zrml_market_commons::Config for Runtime { type Balance = Balance; type MarketId = MarketId; diff --git a/runtime/zeitgeist/Cargo.toml b/runtime/zeitgeist/Cargo.toml index 1a43e9b51..49a01c3b5 100644 --- a/runtime/zeitgeist/Cargo.toml +++ b/runtime/zeitgeist/Cargo.toml @@ -110,6 +110,7 @@ zeitgeist-primitives = { workspace = true } zrml-authorized = { workspace = true } zrml-combinatorial-tokens = { workspace = true } zrml-court = { workspace = true } +zrml-futarchy = { workspace = true } zrml-global-disputes = { workspace = true, optional = true } zrml-hybrid-router = { workspace = true } zrml-market-commons = { workspace = true } @@ -214,6 +215,7 @@ runtime-benchmarks = [ "zrml-authorized/runtime-benchmarks", "zrml-combinatorial-tokens/runtime-benchmarks", "zrml-court/runtime-benchmarks", + "zrml-futarchy/runtime-benchmarks", "zrml-hybrid-router/runtime-benchmarks", "zrml-neo-swaps/runtime-benchmarks", "zrml-parimutuel/runtime-benchmarks", @@ -320,6 +322,7 @@ std = [ "zrml-authorized/std", "zrml-combinatorial-tokens/std", "zrml-court/std", + "zrml-futarchy/std", "zrml-hybrid-router/std", "zrml-market-commons/std", "zrml-neo-swaps/std", @@ -374,6 +377,7 @@ try-runtime = [ "zrml-authorized/try-runtime", "zrml-combinatorial-tokens/try-runtime", "zrml-court/try-runtime", + "zrml-futarchy/try-runtime", "zrml-hybrid-router/try-runtime", "zrml-market-commons/try-runtime", "zrml-neo-swaps/try-runtime", diff --git a/zrml/futarchy/Cargo.toml b/zrml/futarchy/Cargo.toml new file mode 100644 index 000000000..d8495e45f --- /dev/null +++ b/zrml/futarchy/Cargo.toml @@ -0,0 +1,58 @@ +[dependencies] +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +orml-traits = { workspace = true } +pallet-preimage = { workspace = true } +parity-scale-codec = { workspace = true, features = ["derive", "max-encoded-len"] } +scale-info = { workspace = true, features = ["derive"] } +sp-runtime = { workspace = true } +zeitgeist-primitives = { workspace = true } + +# mock + +env_logger = { workspace = true, optional = true } +orml-currencies = { workspace = true, optional = true } +orml-tokens = { workspace = true, optional = true } +pallet-balances = { workspace = true, optional = true } +pallet-timestamp = { workspace = true, optional = true } +sp-io = { workspace = true, optional = true } + +[dev-dependencies] +test-case = { workspace = true } +zrml-futarchy = { workspace = true, features = ["default", "mock"] } + +[features] +default = ["std"] +mock = [ + "env_logger/default", + "orml-currencies/default", + "orml-tokens/default", + "sp-io/default", + "pallet-balances/default", + "pallet-timestamp/default", + "zeitgeist-primitives/mock", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] +std = [ + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "orml-traits/std", + "parity-scale-codec/std", + "sp-runtime/std", + "zeitgeist-primitives/std", +] +try-runtime = [ + "frame-support/try-runtime", +] + +[package] +authors = ["Zeitgeist PM "] +edition.workspace = true +name = "zrml-futarchy" +version = "0.5.5" diff --git a/zrml/futarchy/README.md b/zrml/futarchy/README.md new file mode 100644 index 000000000..19f6917fb --- /dev/null +++ b/zrml/futarchy/README.md @@ -0,0 +1 @@ +# Futarchy Module diff --git a/zrml/futarchy/src/lib.rs b/zrml/futarchy/src/lib.rs new file mode 100644 index 000000000..e8ea5c08b --- /dev/null +++ b/zrml/futarchy/src/lib.rs @@ -0,0 +1,99 @@ +// Copyright 2024 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +#![doc = include_str!("../README.md")] +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +pub mod mock; +mod tests; +mod traits; +pub mod types; + +pub use pallet::*; + +#[frame_support::pallet] +mod pallet { + use core::marker::PhantomData; + use frame_support::{ + pallet_prelude::{EnsureOrigin, IsType, StorageVersion}, + require_transactional, + traits::{QueryPreimage, StorePreimage}, + transactional, + }; + use frame_system::pallet_prelude::OriginFor; + use orml_traits::MultiCurrency; + use sp_runtime::DispatchResult; + + #[pallet::config] + pub trait Config: frame_system::Config { + type MultiCurrency: MultiCurrency; + + // Preimage interface for acquiring call data. + type Preimages: QueryPreimage + StorePreimage; + + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + type SubmitOrigin: EnsureOrigin; + + // // TODO + // // The origin from which proposals may be whitelisted. + // type WhitelistOrigin: EnsureOrigin; + + // TODO Scheduler, EnactmentPeriod + } + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(PhantomData); + + pub(crate) type AccountIdOf = ::AccountId; + pub(crate) type BalanceOf = + <::MultiCurrency as MultiCurrency>>::Balance; + + pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + + // TODO Storage Items + + #[pallet::event] + #[pallet::generate_deposit(fn deposit_event)] + pub enum Event + where + T: Config, {} + + #[pallet::error] + pub enum Error {} + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[transactional] + #[pallet::weight({0})] + pub fn submit(origin: OriginFor) -> DispatchResult { + T::SubmitOrigin::ensure_origin(origin)?; + Self::do_submit() + } + } + + impl Pallet { + #[require_transactional] + fn do_submit() -> DispatchResult { + Ok(()) + } + } +} diff --git a/zrml/futarchy/src/mock/ext_builder.rs b/zrml/futarchy/src/mock/ext_builder.rs new file mode 100644 index 000000000..ddd2d2e10 --- /dev/null +++ b/zrml/futarchy/src/mock/ext_builder.rs @@ -0,0 +1,72 @@ +// Copyright 2024 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +use crate::mock::runtime::{Runtime, System}; +use sp_io::TestExternalities; +use sp_runtime::BuildStorage; + +#[cfg(feature = "parachain")] +use {crate::mock::consts::FOREIGN_ASSET, zeitgeist_primitives::types::CustomMetadata}; + +pub struct ExtBuilder; + +impl ExtBuilder { + pub fn build() -> TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + // See the logs in tests when using `RUST_LOG=debug cargo test -- --nocapture` + let _ = env_logger::builder().is_test(true).try_init(); + + pallet_balances::GenesisConfig:: { balances: vec![] } + .assimilate_storage(&mut t) + .unwrap(); + + #[cfg(feature = "parachain")] + { + orml_tokens::GenesisConfig:: { balances: vec![] } + .assimilate_storage(&mut t) + .unwrap(); + + let custom_metadata = + CustomMetadata { allow_as_base_asset: true, ..Default::default() }; + + orml_asset_registry::GenesisConfig:: { + assets: vec![( + FOREIGN_ASSET, + AssetMetadata { + decimals: 18, + name: "MKL".as_bytes().to_vec().try_into().unwrap(), + symbol: "MKL".as_bytes().to_vec().try_into().unwrap(), + existential_deposit: 0, + location: None, + additional: custom_metadata, + } + .encode(), + )], + last_asset_id: FOREIGN_ASSET, + } + .assimilate_storage(&mut t) + .unwrap(); + } + + let mut test_ext: sp_io::TestExternalities = t.into(); + + test_ext.execute_with(|| System::set_block_number(1)); + + test_ext + } +} diff --git a/zrml/futarchy/src/mock/mod.rs b/zrml/futarchy/src/mock/mod.rs new file mode 100644 index 000000000..762e0a01a --- /dev/null +++ b/zrml/futarchy/src/mock/mod.rs @@ -0,0 +1,21 @@ +// Copyright 2024 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +#![cfg(feature = "mock")] + +pub mod ext_builder; +pub(crate) mod runtime; diff --git a/zrml/futarchy/src/mock/runtime.rs b/zrml/futarchy/src/mock/runtime.rs new file mode 100644 index 000000000..3ff62020d --- /dev/null +++ b/zrml/futarchy/src/mock/runtime.rs @@ -0,0 +1,131 @@ +// Copyright 2024 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +use crate as zrml_futarchy; +use frame_support::{construct_runtime, parameter_types, traits::Everything}; +use frame_system::{mocking::MockBlock, EnsureRoot}; +use sp_runtime::traits::{BlakeTwo256, ConstU32, IdentityLookup}; +use zeitgeist_primitives::{ + constants::mock::{ + BlockHashCount, ExistentialDeposit, ExistentialDeposits, GetNativeCurrencyId, MaxLocks, + MaxReserves, MinimumPeriod, + }, + types::{AccountIdTest, Amount, Balance, BasicCurrencyAdapter, CurrencyId, Hash, Moment}, +}; + +parameter_types! { + pub const PreimageBaseDeposit: Balance = 0; + pub const PreimageByteDeposit: Balance = 0; +} + +construct_runtime! { + pub enum Runtime { + Futarchy: zrml_futarchy, + Balances: pallet_balances, + Currencies: orml_currencies, + Preimage: pallet_preimage, + System: frame_system, + Timestamp: pallet_timestamp, + Tokens: orml_tokens, + } +} + +impl zrml_futarchy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type MultiCurrency = Currencies; + type Preimages = Preimage; + type SubmitOrigin = EnsureRoot<::AccountId>; +} + +impl orml_currencies::Config for Runtime { + type GetNativeCurrencyId = GetNativeCurrencyId; + type MultiCurrency = Tokens; + type NativeCurrency = BasicCurrencyAdapter; + type WeightInfo = (); +} + +impl pallet_balances::Config for Runtime { + type AccountStore = System; + type Balance = Balance; + type DustRemoval = (); + type FreezeIdentifier = (); + type RuntimeHoldReason = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ExistentialDeposit; + type MaxHolds = (); + type MaxFreezes = (); + type MaxLocks = MaxLocks; + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); +} + +impl pallet_preimage::Config for Runtime { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type ManagerOrigin = EnsureRoot; + type BaseDeposit = PreimageBaseDeposit; + type ByteDeposit = PreimageByteDeposit; +} + +impl frame_system::Config for Runtime { + type AccountData = pallet_balances::AccountData; + type AccountId = AccountIdTest; + type BaseCallFilter = Everything; + type Block = MockBlock; + type BlockHashCount = BlockHashCount; + type BlockLength = (); + type BlockWeights = (); + type RuntimeCall = RuntimeCall; + type DbWeight = (); + type RuntimeEvent = RuntimeEvent; + type Hash = Hash; + type Hashing = BlakeTwo256; + type Lookup = IdentityLookup; + type Nonce = u64; + type MaxConsumers = ConstU32<16>; + type OnKilledAccount = (); + type OnNewAccount = (); + type RuntimeOrigin = RuntimeOrigin; + type PalletInfo = PalletInfo; + type SS58Prefix = (); + type SystemWeightInfo = (); + type Version = (); + type OnSetCode = (); +} + +impl pallet_timestamp::Config for Runtime { + type MinimumPeriod = MinimumPeriod; + type Moment = Moment; + type OnTimestampSet = (); + type WeightInfo = (); +} + +impl orml_tokens::Config for Runtime { + type Amount = Amount; + type Balance = Balance; + type CurrencyId = CurrencyId; + type DustRemovalWhitelist = Everything; + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposits = ExistentialDeposits; + type MaxLocks = MaxLocks; + type MaxReserves = MaxReserves; + type CurrencyHooks = (); + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); +} diff --git a/zrml/futarchy/src/tests/mod.rs b/zrml/futarchy/src/tests/mod.rs new file mode 100644 index 000000000..3c2c4444b --- /dev/null +++ b/zrml/futarchy/src/tests/mod.rs @@ -0,0 +1,18 @@ +// Copyright 2024 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . + +#![cfg(all(feature = "mock", test))] diff --git a/zrml/futarchy/src/traits/mod.rs b/zrml/futarchy/src/traits/mod.rs new file mode 100644 index 000000000..1032ee726 --- /dev/null +++ b/zrml/futarchy/src/traits/mod.rs @@ -0,0 +1,16 @@ +// Copyright 2024 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see . diff --git a/zrml/futarchy/src/types/mod.rs b/zrml/futarchy/src/types/mod.rs new file mode 100644 index 000000000..1032ee726 --- /dev/null +++ b/zrml/futarchy/src/types/mod.rs @@ -0,0 +1,16 @@ +// Copyright 2024 Forecasting Technologies LTD. +// +// This file is part of Zeitgeist. +// +// Zeitgeist is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. +// +// Zeitgeist 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zeitgeist. If not, see .