mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-10 05:09:22 +00:00
Partial work on correcting pallet calls
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -7419,6 +7419,7 @@ name = "serai-consensus"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"frame-support",
|
||||||
"futures",
|
"futures",
|
||||||
"log",
|
"log",
|
||||||
"pallet-session",
|
"pallet-session",
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ sc-service = { git = "https://github.com/serai-dex/substrate" }
|
|||||||
sc-client-api = { git = "https://github.com/serai-dex/substrate" }
|
sc-client-api = { git = "https://github.com/serai-dex/substrate" }
|
||||||
sc-consensus = { git = "https://github.com/serai-dex/substrate" }
|
sc-consensus = { git = "https://github.com/serai-dex/substrate" }
|
||||||
|
|
||||||
|
frame-support = { git = "https://github.com/serai-dex/substrate" }
|
||||||
pallet-session = { git = "https://github.com/serai-dex/substrate" }
|
pallet-session = { git = "https://github.com/serai-dex/substrate" }
|
||||||
|
|
||||||
substrate-prometheus-endpoint = { git = "https://github.com/serai-dex/substrate" }
|
substrate-prometheus-endpoint = { git = "https://github.com/serai-dex/substrate" }
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use std::{sync::Arc, collections::HashMap};
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use sp_core::sr25519::Public;
|
||||||
use sp_inherents::CreateInherentDataProviders;
|
use sp_inherents::CreateInherentDataProviders;
|
||||||
use sp_runtime::traits::Block;
|
use sp_runtime::traits::Block;
|
||||||
use sp_api::TransactionFor;
|
use sp_api::TransactionFor;
|
||||||
@@ -11,6 +12,8 @@ use sc_consensus::{BlockCheckParams, BlockImportParams, ImportResult, BlockImpor
|
|||||||
|
|
||||||
use sc_client_api::Backend;
|
use sc_client_api::Backend;
|
||||||
|
|
||||||
|
use frame_support::traits::ValidatorSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
tendermint::{TendermintClient, TendermintImport},
|
tendermint::{TendermintClient, TendermintImport},
|
||||||
Announce,
|
Announce,
|
||||||
@@ -29,6 +32,7 @@ where
|
|||||||
TransactionFor<C, B>: Send + Sync + 'static,
|
TransactionFor<C, B>: Send + Sync + 'static,
|
||||||
Arc<C>: BlockImport<B, Transaction = TransactionFor<C, B>>,
|
Arc<C>: BlockImport<B, Transaction = TransactionFor<C, B>>,
|
||||||
<Arc<C> as BlockImport<B>>::Error: Into<Error>,
|
<Arc<C> as BlockImport<B>>::Error: Into<Error>,
|
||||||
|
C::Api: ValidatorSet<Public>,
|
||||||
{
|
{
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Transaction = TransactionFor<C, B>;
|
type Transaction = TransactionFor<C, B>;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use std::{
|
|||||||
time::{UNIX_EPOCH, SystemTime},
|
time::{UNIX_EPOCH, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
use sp_core::Decode;
|
use sp_core::{Decode, sr25519::Public};
|
||||||
use sp_inherents::CreateInherentDataProviders;
|
use sp_inherents::CreateInherentDataProviders;
|
||||||
use sp_runtime::traits::{Header, Block};
|
use sp_runtime::traits::{Header, Block};
|
||||||
use sp_api::{BlockId, TransactionFor};
|
use sp_api::{BlockId, TransactionFor};
|
||||||
@@ -19,6 +19,8 @@ use sc_client_api::Backend;
|
|||||||
|
|
||||||
use substrate_prometheus_endpoint::Registry;
|
use substrate_prometheus_endpoint::Registry;
|
||||||
|
|
||||||
|
use frame_support::traits::ValidatorSet;
|
||||||
|
|
||||||
use tendermint_machine::{
|
use tendermint_machine::{
|
||||||
ext::{BlockNumber, Commit},
|
ext::{BlockNumber, Commit},
|
||||||
TendermintMachine,
|
TendermintMachine,
|
||||||
@@ -98,6 +100,7 @@ where
|
|||||||
TransactionFor<C, B>: Send + Sync + 'static,
|
TransactionFor<C, B>: Send + Sync + 'static,
|
||||||
Arc<C>: BlockImport<B, Transaction = TransactionFor<C, B>>,
|
Arc<C>: BlockImport<B, Transaction = TransactionFor<C, B>>,
|
||||||
<Arc<C> as BlockImport<B>>::Error: Into<Error>,
|
<Arc<C> as BlockImport<B>>::Error: Into<Error>,
|
||||||
|
C::Api: ValidatorSet<Public>,
|
||||||
{
|
{
|
||||||
let import = TendermintImport::new(client, announce, providers, env);
|
let import = TendermintImport::new(client, announce, providers, env);
|
||||||
|
|
||||||
@@ -116,7 +119,7 @@ where
|
|||||||
Ok(best) => BlockNumber(best),
|
Ok(best) => BlockNumber(best),
|
||||||
Err(_) => panic!("BlockNumber exceeded u64"),
|
Err(_) => panic!("BlockNumber exceeded u64"),
|
||||||
},
|
},
|
||||||
Commit::<TendermintValidators>::decode(
|
Commit::<TendermintValidators<B, C>>::decode(
|
||||||
&mut import_clone
|
&mut import_clone
|
||||||
.client
|
.client
|
||||||
.justifications(&BlockId::Number(best))
|
.justifications(&BlockId::Number(best))
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ use log::warn;
|
|||||||
|
|
||||||
use tokio::sync::RwLock as AsyncRwLock;
|
use tokio::sync::RwLock as AsyncRwLock;
|
||||||
|
|
||||||
use sp_core::{Encode, Decode};
|
use sp_core::{
|
||||||
use sp_application_crypto::sr25519::Signature;
|
Encode, Decode,
|
||||||
|
sr25519::{Public, Signature},
|
||||||
|
};
|
||||||
use sp_inherents::{InherentData, InherentDataProvider, CreateInherentDataProviders};
|
use sp_inherents::{InherentData, InherentDataProvider, CreateInherentDataProviders};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
traits::{Header, Block},
|
traits::{Header, Block},
|
||||||
@@ -26,6 +28,8 @@ use sc_consensus::{ForkChoiceStrategy, BlockImportParams, BlockImport, import_qu
|
|||||||
use sc_service::ImportQueue;
|
use sc_service::ImportQueue;
|
||||||
use sc_client_api::{BlockBackend, Backend, Finalizer};
|
use sc_client_api::{BlockBackend, Backend, Finalizer};
|
||||||
|
|
||||||
|
use frame_support::traits::ValidatorSet;
|
||||||
|
|
||||||
use tendermint_machine::{
|
use tendermint_machine::{
|
||||||
ext::{BlockError, Commit, Network},
|
ext::{BlockError, Commit, Network},
|
||||||
SignedMessage, TendermintHandle,
|
SignedMessage, TendermintHandle,
|
||||||
@@ -47,6 +51,9 @@ pub trait TendermintClient<B: Block, Be: Backend<B> + 'static>:
|
|||||||
+ Finalizer<B, Be>
|
+ Finalizer<B, Be>
|
||||||
+ ProvideRuntimeApi<B>
|
+ ProvideRuntimeApi<B>
|
||||||
+ 'static
|
+ 'static
|
||||||
|
where
|
||||||
|
TransactionFor<Self, B>: Send + Sync + 'static,
|
||||||
|
Self::Api: ValidatorSet<Public>,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
impl<
|
impl<
|
||||||
@@ -61,6 +68,9 @@ impl<
|
|||||||
+ ProvideRuntimeApi<B>
|
+ ProvideRuntimeApi<B>
|
||||||
+ 'static,
|
+ 'static,
|
||||||
> TendermintClient<B, Be> for C
|
> TendermintClient<B, Be> for C
|
||||||
|
where
|
||||||
|
TransactionFor<C, B>: Send + Sync + 'static,
|
||||||
|
C::Api: ValidatorSet<Public>,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,10 +83,13 @@ pub(crate) struct TendermintImport<
|
|||||||
A: Announce<B>,
|
A: Announce<B>,
|
||||||
> where
|
> where
|
||||||
TransactionFor<C, B>: Send + Sync + 'static,
|
TransactionFor<C, B>: Send + Sync + 'static,
|
||||||
|
C::Api: ValidatorSet<Public>,
|
||||||
{
|
{
|
||||||
_block: PhantomData<B>,
|
_block: PhantomData<B>,
|
||||||
_backend: PhantomData<Be>,
|
_backend: PhantomData<Be>,
|
||||||
|
|
||||||
|
validators: Arc<TendermintValidators<B, C>>,
|
||||||
|
|
||||||
importing_block: Arc<RwLock<Option<B::Hash>>>,
|
importing_block: Arc<RwLock<Option<B::Hash>>>,
|
||||||
pub(crate) machine: Arc<RwLock<Option<TendermintHandle<Self>>>>,
|
pub(crate) machine: Arc<RwLock<Option<TendermintHandle<Self>>>>,
|
||||||
|
|
||||||
@@ -98,12 +111,15 @@ impl<
|
|||||||
> Clone for TendermintImport<B, Be, C, CIDP, E, A>
|
> Clone for TendermintImport<B, Be, C, CIDP, E, A>
|
||||||
where
|
where
|
||||||
TransactionFor<C, B>: Send + Sync + 'static,
|
TransactionFor<C, B>: Send + Sync + 'static,
|
||||||
|
C::Api: ValidatorSet<Public>,
|
||||||
{
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
TendermintImport {
|
TendermintImport {
|
||||||
_block: PhantomData,
|
_block: PhantomData,
|
||||||
_backend: PhantomData,
|
_backend: PhantomData,
|
||||||
|
|
||||||
|
validators: self.validators.clone(),
|
||||||
|
|
||||||
importing_block: self.importing_block.clone(),
|
importing_block: self.importing_block.clone(),
|
||||||
machine: self.machine.clone(),
|
machine: self.machine.clone(),
|
||||||
|
|
||||||
@@ -127,6 +143,7 @@ impl<
|
|||||||
> TendermintImport<B, Be, C, CIDP, E, A>
|
> TendermintImport<B, Be, C, CIDP, E, A>
|
||||||
where
|
where
|
||||||
TransactionFor<C, B>: Send + Sync + 'static,
|
TransactionFor<C, B>: Send + Sync + 'static,
|
||||||
|
C::Api: ValidatorSet<Public>,
|
||||||
{
|
{
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
client: Arc<C>,
|
client: Arc<C>,
|
||||||
@@ -138,6 +155,8 @@ where
|
|||||||
_block: PhantomData,
|
_block: PhantomData,
|
||||||
_backend: PhantomData,
|
_backend: PhantomData,
|
||||||
|
|
||||||
|
validators: TendermintValidators::new(client),
|
||||||
|
|
||||||
importing_block: Arc::new(RwLock::new(None)),
|
importing_block: Arc::new(RwLock::new(None)),
|
||||||
machine: Arc::new(RwLock::new(None)),
|
machine: Arc::new(RwLock::new(None)),
|
||||||
|
|
||||||
@@ -196,7 +215,7 @@ where
|
|||||||
Err(Error::InvalidJustification)?;
|
Err(Error::InvalidJustification)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let commit: Commit<TendermintValidators> =
|
let commit: Commit<TendermintValidators<B, C>> =
|
||||||
Commit::decode(&mut justification.1.as_ref()).map_err(|_| Error::InvalidJustification)?;
|
Commit::decode(&mut justification.1.as_ref()).map_err(|_| Error::InvalidJustification)?;
|
||||||
if !self.verify_commit(hash, &commit) {
|
if !self.verify_commit(hash, &commit) {
|
||||||
Err(Error::InvalidJustification)?;
|
Err(Error::InvalidJustification)?;
|
||||||
@@ -309,20 +328,21 @@ impl<
|
|||||||
> Network for TendermintImport<B, Be, C, CIDP, E, A>
|
> Network for TendermintImport<B, Be, C, CIDP, E, A>
|
||||||
where
|
where
|
||||||
TransactionFor<C, B>: Send + Sync + 'static,
|
TransactionFor<C, B>: Send + Sync + 'static,
|
||||||
|
C::Api: ValidatorSet<Public>,
|
||||||
{
|
{
|
||||||
type ValidatorId = u16;
|
type ValidatorId = u16;
|
||||||
type SignatureScheme = TendermintValidators;
|
type SignatureScheme = TendermintValidators<B, C>;
|
||||||
type Weights = TendermintValidators;
|
type Weights = TendermintValidators<B, C>;
|
||||||
type Block = B;
|
type Block = B;
|
||||||
|
|
||||||
const BLOCK_TIME: u32 = { (serai_runtime::MILLISECS_PER_BLOCK / 1000) as u32 };
|
const BLOCK_TIME: u32 = { (serai_runtime::MILLISECS_PER_BLOCK / 1000) as u32 };
|
||||||
|
|
||||||
fn signature_scheme(&self) -> Arc<TendermintValidators> {
|
fn signature_scheme(&self) -> Arc<TendermintValidators<B, C>> {
|
||||||
Arc::new(TendermintValidators::new())
|
self.validators.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn weights(&self) -> Arc<TendermintValidators> {
|
fn weights(&self) -> Arc<TendermintValidators<B, C>> {
|
||||||
Arc::new(TendermintValidators::new())
|
self.validators.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn broadcast(&mut self, msg: SignedMessage<u16, Self::Block, Signature>) {
|
async fn broadcast(&mut self, msg: SignedMessage<u16, Self::Block, Signature>) {
|
||||||
@@ -390,7 +410,7 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_block(&mut self, block: B, commit: Commit<TendermintValidators>) -> B {
|
async fn add_block(&mut self, block: B, commit: Commit<TendermintValidators<B, C>>) -> B {
|
||||||
let hash = block.hash();
|
let hash = block.hash();
|
||||||
let justification = (CONSENSUS_ID, commit.encode());
|
let justification = (CONSENSUS_ID, commit.encode());
|
||||||
debug_assert!(self.verify_justification(hash, &justification).is_ok());
|
debug_assert!(self.verify_justification(hash, &justification).is_ok());
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use core::ops::Deref;
|
use core::{marker::PhantomData, ops::Deref};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use sp_application_crypto::{
|
use sp_application_crypto::{
|
||||||
@@ -6,8 +6,11 @@ use sp_application_crypto::{
|
|||||||
sr25519::{Public, Pair, Signature},
|
sr25519::{Public, Pair, Signature},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use sp_runtime::traits::Block;
|
||||||
use sp_staking::SessionIndex;
|
use sp_staking::SessionIndex;
|
||||||
use pallet_session::Pallet as Session;
|
use sp_api::ProvideRuntimeApi;
|
||||||
|
|
||||||
|
use frame_support::traits::ValidatorSet;
|
||||||
|
|
||||||
use tendermint_machine::ext::{BlockNumber, Round, Weights, SignatureScheme};
|
use tendermint_machine::ext::{BlockNumber, Round, Weights, SignatureScheme};
|
||||||
|
|
||||||
@@ -22,12 +25,17 @@ struct TendermintValidatorsStruct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TendermintValidatorsStruct {
|
impl TendermintValidatorsStruct {
|
||||||
fn from_module() -> TendermintValidatorsStruct {
|
fn from_module<B: Block, C: Send + Sync + ProvideRuntimeApi<B>>(
|
||||||
let validators = Session::<serai_runtime::Runtime>::validators();
|
client: C,
|
||||||
|
) -> TendermintValidatorsStruct
|
||||||
|
where
|
||||||
|
C::Api: ValidatorSet<Public>,
|
||||||
|
{
|
||||||
|
let validators = client.runtime_api().validators();
|
||||||
assert_eq!(validators.len(), 1);
|
assert_eq!(validators.len(), 1);
|
||||||
let keys = Pair::from_string("//Alice", None).unwrap();
|
let keys = Pair::from_string("//Alice", None).unwrap();
|
||||||
TendermintValidatorsStruct {
|
TendermintValidatorsStruct {
|
||||||
session: Session::<serai_runtime::Runtime>::current_index(),
|
session: client.runtime_api().session_index(),
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
total_weight: validators.len().try_into().unwrap(),
|
total_weight: validators.len().try_into().unwrap(),
|
||||||
@@ -40,20 +48,28 @@ impl TendermintValidatorsStruct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrap every access of the validators struct in something which forces calling refresh
|
// Wrap every access of the validators struct in something which forces calling refresh
|
||||||
struct Refresh {
|
struct Refresh<B: Block, C: Send + Sync + ProvideRuntimeApi<B>> {
|
||||||
|
_block: PhantomData<B>,
|
||||||
|
client: C,
|
||||||
_refresh: Arc<RwLock<TendermintValidatorsStruct>>,
|
_refresh: Arc<RwLock<TendermintValidatorsStruct>>,
|
||||||
}
|
}
|
||||||
impl Refresh {
|
impl<B: Block, C: Send + Sync + ProvideRuntimeApi<B>> Refresh<B, C>
|
||||||
|
where
|
||||||
|
C::Api: ValidatorSet<Public>,
|
||||||
|
{
|
||||||
// If the session has changed, re-create the struct with the data on it
|
// If the session has changed, re-create the struct with the data on it
|
||||||
fn refresh(&self) {
|
fn refresh(&self) {
|
||||||
let session = self._refresh.read().unwrap().session;
|
let session = self._refresh.read().unwrap().session;
|
||||||
if session != Session::<serai_runtime::Runtime>::current_index() {
|
if session != self.client.runtime_api().session_index() {
|
||||||
*self._refresh.write().unwrap() = TendermintValidatorsStruct::from_module();
|
*self._refresh.write().unwrap() = TendermintValidatorsStruct::from_module(self.client);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Refresh {
|
impl<B: Block, C: Send + Sync + ProvideRuntimeApi<B>> Deref for Refresh<B, C>
|
||||||
|
where
|
||||||
|
C::Api: ValidatorSet<Public>,
|
||||||
|
{
|
||||||
type Target = RwLock<TendermintValidatorsStruct>;
|
type Target = RwLock<TendermintValidatorsStruct>;
|
||||||
fn deref(&self) -> &RwLock<TendermintValidatorsStruct> {
|
fn deref(&self) -> &RwLock<TendermintValidatorsStruct> {
|
||||||
self.refresh();
|
self.refresh();
|
||||||
@@ -61,16 +77,26 @@ impl Deref for Refresh {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct TendermintValidators(Refresh);
|
pub(crate) struct TendermintValidators<B: Block, C: Send + Sync + ProvideRuntimeApi<B>>(
|
||||||
impl TendermintValidators {
|
Refresh<B, C>,
|
||||||
pub(crate) fn new() -> TendermintValidators {
|
);
|
||||||
|
impl<B: Block, C: Send + Sync + ProvideRuntimeApi<B>> TendermintValidators<B, C>
|
||||||
|
where
|
||||||
|
C::Api: ValidatorSet<Public>,
|
||||||
|
{
|
||||||
|
pub(crate) fn new(client: C) -> TendermintValidators<B, C> {
|
||||||
TendermintValidators(Refresh {
|
TendermintValidators(Refresh {
|
||||||
|
_block: PhantomData,
|
||||||
|
client,
|
||||||
_refresh: Arc::new(RwLock::new(TendermintValidatorsStruct::from_module())),
|
_refresh: Arc::new(RwLock::new(TendermintValidatorsStruct::from_module())),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SignatureScheme for TendermintValidators {
|
impl<B: Block, C: Send + Sync + ProvideRuntimeApi<B>> SignatureScheme for TendermintValidators<B, C>
|
||||||
|
where
|
||||||
|
C::Api: ValidatorSet<Public>,
|
||||||
|
{
|
||||||
type ValidatorId = u16;
|
type ValidatorId = u16;
|
||||||
type Signature = Signature;
|
type Signature = Signature;
|
||||||
type AggregateSignature = Vec<Signature>;
|
type AggregateSignature = Vec<Signature>;
|
||||||
@@ -100,7 +126,10 @@ impl SignatureScheme for TendermintValidators {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Weights for TendermintValidators {
|
impl<B: Block, C: Send + Sync + ProvideRuntimeApi<B>> Weights for TendermintValidators<B, C>
|
||||||
|
where
|
||||||
|
C::Api: ValidatorSet<Public>,
|
||||||
|
{
|
||||||
type ValidatorId = u16;
|
type ValidatorId = u16;
|
||||||
|
|
||||||
fn total_weight(&self) -> u64 {
|
fn total_weight(&self) -> u64 {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use sp_core::sr25519::Public;
|
||||||
use sp_inherents::CreateInherentDataProviders;
|
use sp_inherents::CreateInherentDataProviders;
|
||||||
use sp_runtime::traits::Block;
|
use sp_runtime::traits::Block;
|
||||||
use sp_api::TransactionFor;
|
use sp_api::TransactionFor;
|
||||||
@@ -11,6 +12,8 @@ use sc_consensus::{BlockImportParams, BlockImport, Verifier};
|
|||||||
|
|
||||||
use sc_client_api::Backend;
|
use sc_client_api::Backend;
|
||||||
|
|
||||||
|
use frame_support::traits::ValidatorSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
tendermint::{TendermintClient, TendermintImport},
|
tendermint::{TendermintClient, TendermintImport},
|
||||||
Announce,
|
Announce,
|
||||||
@@ -29,6 +32,7 @@ where
|
|||||||
TransactionFor<C, B>: Send + Sync + 'static,
|
TransactionFor<C, B>: Send + Sync + 'static,
|
||||||
Arc<C>: BlockImport<B, Transaction = TransactionFor<C, B>>,
|
Arc<C>: BlockImport<B, Transaction = TransactionFor<C, B>>,
|
||||||
<Arc<C> as BlockImport<B>>::Error: Into<Error>,
|
<Arc<C> as BlockImport<B>>::Error: Into<Error>,
|
||||||
|
C::Api: ValidatorSet<Public>,
|
||||||
{
|
{
|
||||||
async fn verify(
|
async fn verify(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
Reference in New Issue
Block a user