Skip to content

Commit

Permalink
vouches pallet MVP (#355)
Browse files Browse the repository at this point in the history
* initial commit for new pallet. not compiling yet

* builds and test passes

* simplify: only one quality per vouch

* cleanup warnings

* more cleanup

* undisputed review comment fixes

* enum encoding hints and more specific docs to enums

* fix benchmarks

* remove hard-coding of enum encoding
  • Loading branch information
brenzi committed Oct 1, 2023
1 parent 7be60a1 commit 774e0be
Show file tree
Hide file tree
Showing 11 changed files with 470 additions and 3 deletions.
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ members = [
'rpc',
'scheduler',
'test-utils',
'vouches',
]

#[patch."https://github.com/encointer/substrate-fixed"]
Expand Down
4 changes: 1 addition & 3 deletions balances/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
[package]
name = "pallet-encointer-balances"
version = "1.2.0"
authors = [
"encointer.org <alain@encointer.org> and Parity Technologies <admin@parity.io>",
]
authors = ["encointer.org <alain@encointer.org>"]
edition = "2021"

[dependencies]
Expand Down
1 change: 1 addition & 0 deletions primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub mod faucet;
pub mod reputation_commitments;
pub mod scheduler;
pub mod storage;
pub mod vouches;

pub use ep_core::*;

Expand Down
91 changes: 91 additions & 0 deletions primitives/src/vouches.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) 2023 Encointer Association
// This file is part of Encointer
//
// Encointer 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.
//
// Encointer 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 Encointer. If not, see <http://www.gnu.org/licenses/>.

use crate::common::BoundedIpfsCid;
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::RuntimeDebug;
use scale_info::TypeInfo;
#[cfg(feature = "serde_derive")]
use serde::{Deserialize, Serialize};

/// Did the attester meet the attestee physically, virtually or through asynchronous messages?
#[derive(Default, Encode, Decode, Copy, Clone, PartialEq, Eq, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
pub enum PresenceType {
/// could be "I have exchanged messages with the person I vouch for"
/// could be "I have watched the replay of the Event or talk I vouch for"
#[default]
Asynchronous,
/// could be "I have attended that online conference remotely"
/// could be "I have visited that place in the metaverse"
/// could be "I have met this person on an video call and they presented this account to me"
LiveVirtual,
/// could be "I met the human I vouch for in-person and scanned the account they presented at the occasion of this physical encounter"
/// could be "I was standing in front of this monument and scanned the QR code on its plate"
/// could be "I ate at this restaurant and scanned the QR code presented at their entrance in order to submit a rating"
LivePhysical,
}

/// The nature of a vouch
/// this set is most likely incomplete. we leave gaps in the encoding to have room for more kinds
/// which still results in meaningful order
#[derive(Default, Encode, Decode, Copy, Clone, PartialEq, Eq, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
pub enum VouchKind {
/// Unspecified. This should generally be handeled as an invalid vouch or an alien use case
#[default]
Unspecified,
/// This person is know to me and I have verified their account with specified presence type
KnownHuman(PresenceType),
/// I do not claim to know this person, but I encountered a human being providing me with the account I vouch for
EncounteredHuman(PresenceType),
/// I encountered an object showing the account I vouch for
EncounteredObject(PresenceType),
/// I have visited a place labeled with the account I vouch for
VisitedPlace(PresenceType),
/// I have attended an event which identifies with the account I vouch for
AttendedEvent(PresenceType),
}

/// a scalar expression of quality. Interpretation left to client side per use case
/// could be a 0-5 star rating or a high-resolution 0..255
pub type Rating = u8;

/// additional information about the attestee's qualities
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
pub enum VouchQuality {
/// Don't want to submit additional information
#[default]
Unspecified,
/// a generic badge for qualitative attestation stored as a json file on IPFS (json schema TBD)
Badge(BoundedIpfsCid),
/// a quantitative expression of the attestee's quality (i.e. number of stars)
Rating(Rating),
}

#[derive(Default, Encode, Decode, PartialEq, Eq, RuntimeDebug, Clone, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
pub struct Vouch<Moment> {
/// protected vouches can't be purged. unprotected ones can be lazily purged after a time-to-live. (future feature)
pub protected: bool,
/// the timestamp of the block which registers this Vouch
pub timestamp: Moment,
/// what is the nature of this vouch?
pub vouch_kind: VouchKind,
/// additional information about the attestee's qualities
pub quality: VouchQuality,
}
52 changes: 52 additions & 0 deletions vouches/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
[package]
name = "pallet-encointer-vouches"
version = "1.2.0"
authors = ["encointer.org <alain@encointer.org> and Parity Technologies <admin@parity.io>"]
edition = "2021"

[dependencies]
approx = { version = "0.5.1", optional = true }
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] }
log = { version = "0.4.14", default-features = false }
scale-info = { version = "2.5.0", default-features = false }

# local deps
encointer-primitives = { path = "../primitives", default-features = false, features = ["serde_derive"] }

# substrate deps
frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
pallet-timestamp = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
sp-core = { version = "21.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
sp-runtime = { version = "24.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
sp-std = { version = "8.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }

[dev-dependencies]
approx = "0.5.1"
sp-io = { version = "23.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
test-utils = { path = "../test-utils" }

[features]
default = ["std"]
std = [
"codec/std",
"log/std",
"scale-info/std",
"approx/std",
# local deps
"encointer-primitives/std",
# substrate deps
"frame-benchmarking/std",
"frame-support/std",
"frame-system/std",
"sp-std/std",
"sp-runtime/std",
"sp-core/std",
]

runtime-benchmarks = ["frame-benchmarking", "approx"]

try-runtime = [
"frame-system/try-runtime",
]
20 changes: 20 additions & 0 deletions vouches/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use crate::*;
use encointer_primitives::vouches::{PresenceType, VouchKind, VouchQuality};
use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite};
use frame_system::RawOrigin;

benchmarks! {
vouch_for {
let zoran = account("zoran", 1, 1);
let goran = account("goran", 1, 2);
let vouch_kind = VouchKind::EncounteredHuman(PresenceType::LivePhysical);
let quality = VouchQuality::default();
assert!(<Vouches<T>>::iter().next().is_none());
}: _(RawOrigin::Signed(zoran), goran, vouch_kind, quality)
verify {
assert!(<Vouches<T>>::iter().next().is_some());
}

}

impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime);
105 changes: 105 additions & 0 deletions vouches/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright (c) 2019 Encointer Association
// This file is part of Encointer
//
// Encointer 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.
//
// Encointer 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 Encointer. If not, see <http://www.gnu.org/licenses/>.

#![cfg_attr(not(feature = "std"), no_std)]

use core::marker::PhantomData;
use encointer_primitives::vouches::{Vouch, VouchKind, VouchQuality};
use frame_system::{self as frame_system, ensure_signed, pallet_prelude::OriginFor};
use log::info;
pub use pallet::*;
use sp_std::convert::TryInto;
pub use weights::WeightInfo;
// Logger target
const LOG: &str = "encointer::vouches";

#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;

mod weights;
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;

#[pallet::pallet]
pub struct Pallet<T>(PhantomData<T>);

#[pallet::config]
pub trait Config: frame_system::Config + pallet_timestamp::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type WeightInfo: WeightInfo;
#[pallet::constant]
type MaxVouchesPerAttester: Get<u32>;
}

#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight((<T as Config>::WeightInfo::vouch_for(), DispatchClass::Normal, Pays::Yes))]
pub fn vouch_for(
origin: OriginFor<T>,
attestee: T::AccountId,
vouch_kind: VouchKind,
quality: VouchQuality,
) -> DispatchResultWithPostInfo {
let attester = ensure_signed(origin)?;
let now = <pallet_timestamp::Pallet<T>>::get();
let vouch =
Vouch { protected: false, timestamp: now, vouch_kind, quality: quality.clone() };
<Vouches<T>>::try_mutate(
&attestee,
&attester,
|vouches| -> DispatchResultWithPostInfo {
vouches.try_push(vouch).map_err(|_| Error::<T>::TooManyVouchesForAttestee)?;
Ok(().into())
},
)?;
info!(target: LOG, "vouching: {:?} for {:?}, vouch type: {:?}, quality: {:?}", attester, attestee, vouch_kind, quality);
Self::deposit_event(Event::VouchedFor { attestee, attester, vouch_kind });
Ok(().into())
}
}

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// someone or something has vouched for someone or something
VouchedFor { attestee: T::AccountId, attester: T::AccountId, vouch_kind: VouchKind },
}

#[pallet::error]
pub enum Error<T> {
/// The calling attester has too many vouches for this attestee
TooManyVouchesForAttestee,
}

#[pallet::storage]
#[pallet::getter(fn vouches)]
pub(super) type Vouches<T: Config> = StorageDoubleMap<
_,
Blake2_128Concat,
T::AccountId,
Blake2_128Concat,
T::AccountId,
BoundedVec<Vouch<T::Moment>, T::MaxVouchesPerAttester>,
ValueQuery,
>;
}
51 changes: 51 additions & 0 deletions vouches/src/mock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) 2019 Alain Brenzikofer
// This file is part of Encointer
//
// Encointer 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.
//
// Encointer 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 Encointer. If not, see <http://www.gnu.org/licenses/>.

//! Mock runtime for the encointer_balances module

use crate as dut;
use frame_support::{parameter_types, traits::ConstU32};
use sp_runtime::BuildStorage;
use test_utils::*;

type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<TestRuntime>;

frame_support::construct_runtime!(
pub enum TestRuntime
{
System: frame_system::{Pallet, Call, Config<T>, Storage, Event<T>},
Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent},
EncointerVouches: dut::{Pallet, Call, Storage, Event<T>},
}
);

parameter_types! {}

impl dut::Config for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type MaxVouchesPerAttester = ConstU32<4>;
}

// boilerplate
impl_frame_system!(TestRuntime);
impl_timestamp!(TestRuntime);

// genesis values
pub fn new_test_ext() -> sp_io::TestExternalities {
let t = frame_system::GenesisConfig::<TestRuntime>::default().build_storage().unwrap();
t.into()
}
Loading

0 comments on commit 774e0be

Please sign in to comment.