Skip to content

Commit

Permalink
Merge pull request #1510 from oasisprotocol/kostko/feature/consensus-…
Browse files Browse the repository at this point in the history
…staking-receipts

runtime-sdk: Add support for (un)delegation receipts
  • Loading branch information
kostko authored Sep 26, 2023
2 parents c35d047 + 1a46fa5 commit 9e8c6e9
Show file tree
Hide file tree
Showing 18 changed files with 956 additions and 18 deletions.
40 changes: 37 additions & 3 deletions runtime-sdk-macros/src/module_derive/method_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,29 @@ impl super::Deriver for DeriveMethodHandler {
};

let dispatch_call_impl = {
let (handler_names, handler_idents) = filter_by_kind(handlers, HandlerKind::Call);
let (handler_names, handler_fns): (Vec<_>, Vec<_>) = handlers
.iter()
.filter_map(|h| h.handler.as_ref())
.filter(|h| h.attrs.kind == HandlerKind::Call)
.map(|h| {
(h.attrs.rpc_name.clone(), {
let ident = &h.ident;

if h.attrs.is_internal {
quote! {
|ctx, body| {
if !ctx.is_internal() {
return Err(sdk::modules::core::Error::Forbidden.into());
}
Self::#ident(ctx, body)
}
}
} else {
quote! { Self::#ident }
}
})
})
.unzip();

if handler_names.is_empty() {
quote! {}
Expand All @@ -113,7 +135,7 @@ impl super::Deriver for DeriveMethodHandler {
) -> DispatchResult<cbor::Value, CallResult> {
match method {
#(
#handler_names => module::dispatch_call(ctx, body, Self::#handler_idents),
#handler_names => module::dispatch_call(ctx, body, #handler_fns),
)*
_ => DispatchResult::Unhandled(body),
}
Expand Down Expand Up @@ -347,6 +369,8 @@ struct MethodHandlerAttr {
allow_private_km: bool,
/// Whether this handler is tagged as allowing interactive calls. Only applies to call handlers.
allow_interactive: bool,
/// Whether this handler is tagged as internal.
is_internal: bool,
}
impl syn::parse::Parse for MethodHandlerAttr {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
Expand All @@ -365,6 +389,7 @@ impl syn::parse::Parse for MethodHandlerAttr {
let mut is_expensive = false;
let mut allow_private_km = false;
let mut allow_interactive = false;
let mut is_internal = false;
while input.peek(syn::token::Comma) {
let _: syn::token::Comma = input.parse()?;
let tag: syn::Ident = input.parse()?;
Expand Down Expand Up @@ -393,10 +418,18 @@ impl syn::parse::Parse for MethodHandlerAttr {
));
}
allow_interactive = true;
} else if tag == "internal" {
if kind != HandlerKind::Call {
return Err(syn::Error::new(
tag.span(),
"`internal` tag is only allowed on `call` handlers",
));
}
is_internal = true;
} else {
return Err(syn::Error::new(
tag.span(),
"invalid handler tag; supported: `expensive`, `allow_private_km`, `allow_interactive`",
"invalid handler tag; supported: `expensive`, `allow_private_km`, `allow_interactive`, `internal`",
));
}
}
Expand All @@ -410,6 +443,7 @@ impl syn::parse::Parse for MethodHandlerAttr {
is_expensive,
allow_private_km,
allow_interactive,
is_internal,
})
}
}
Expand Down
27 changes: 27 additions & 0 deletions runtime-sdk-macros/src/module_derive/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ mod tests {
fn my_call(foo2: Bar2) -> Baz2 {}
#[handler(call = "my_module.MyOtherCall")]
fn my_other_call(foo3: Bar3) -> Baz3 {}
#[handler(call = "my_module.MyInternalCall", internal)]
fn my_internal_call(foo4: Bar4) -> Baz4 {}
}
);

Expand All @@ -184,6 +186,7 @@ mod tests {
Self::prefetch_for_my_call(&mut add_prefix, body, auth_info),
),
"my_module.MyOtherCall" => module::DispatchResult::Handled(Ok(())),
"my_module.MyInternalCall" => module::DispatchResult::Handled(Ok(())),
_ => module::DispatchResult::Unhandled(body),
}
}
Expand All @@ -197,6 +200,12 @@ mod tests {
"my_module.MyOtherCall" => {
module::dispatch_call(ctx, body, Self::my_other_call)
}
"my_module.MyInternalCall" => module::dispatch_call(ctx, body, |ctx, body| {
if !ctx.is_internal() {
return Err(sdk::modules::core::Error::Forbidden.into());
}
Self::my_internal_call(ctx, body)
}),
_ => DispatchResult::Unhandled(body),
}
}
Expand All @@ -223,6 +232,10 @@ mod tests {
kind: core_types::MethodHandlerKind::Call,
name: "my_module.MyOtherCall".to_string(),
},
core_types::MethodHandlerInfo {
kind: core_types::MethodHandlerKind::Call,
name: "my_module.MyInternalCall".to_string(),
},
]
}
}
Expand All @@ -237,6 +250,8 @@ mod tests {
fn my_call(foo2: Bar2) -> Baz2 {}
#[handler(call = "my_module.MyOtherCall")]
fn my_other_call(foo3: Bar3) -> Baz3 {}
#[handler(call = "my_module.MyInternalCall", internal)]
fn my_internal_call(foo4: Bar4) -> Baz4 {}
}
};
)
Expand Down Expand Up @@ -537,6 +552,18 @@ mod tests {
super::derive_module(input);
}

#[test]
#[should_panic(expected = "only allowed on `call` handlers")]
fn generate_method_handler_malformed_internal_noncall() {
let input: syn::ItemImpl = syn::parse_quote!(
impl<C: Cfg> MyModule<C> {
#[handler(query = "foo", internal)]
fn my_method_call() -> () {}
}
);
super::derive_module(input);
}

#[test]
#[should_panic]
fn generate_method_handler_malformed_multiple_metas() {
Expand Down
15 changes: 15 additions & 0 deletions runtime-sdk/src/modules/consensus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,20 @@ const MODULE_NAME: &str = "consensus";
pub struct Parameters {
pub consensus_denomination: token::Denomination,
pub consensus_scaling_factor: u64,

/// Minimum amount that is allowed to be delegated. This should be greater than or equal to what
/// is configured in the consensus layer as the consensus layer will do its own checks.
///
/// The amount is in consensus units.
pub min_delegate_amount: u128,
}

impl Default for Parameters {
fn default() -> Self {
Self {
consensus_denomination: token::Denomination::from_str("TEST").unwrap(),
consensus_scaling_factor: 1,
min_delegate_amount: 0,
}
}
}
Expand Down Expand Up @@ -119,6 +126,10 @@ pub enum Error {
#[sdk_error(code = 5)]
AmountNotRepresentable,

#[error("amount is lower than the minimum delegation amount")]
#[sdk_error(code = 6)]
UnderMinDelegationAmount,

#[error("history: {0}")]
#[sdk_error(transparent)]
History(#[from] history::Error),
Expand Down Expand Up @@ -278,6 +289,10 @@ impl API for Module {
Self::ensure_consensus_denomination(ctx, amount.denomination())?;
let amount = Self::amount_to_consensus(ctx, amount.amount())?;

if amount < Self::params().min_delegate_amount {
return Err(Error::UnderMinDelegationAmount);
}

ctx.emit_message(
Message::Staking(Versioned::new(
0,
Expand Down
42 changes: 41 additions & 1 deletion runtime-sdk/src/modules/consensus/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
},
};

use super::{Genesis, Parameters, API as _};
use super::{Error, Genesis, Parameters, API as _};

#[test]
fn test_api_transfer_invalid_denomination() {
Expand Down Expand Up @@ -276,6 +276,43 @@ fn test_api_escrow() {
});
}

#[test]
fn test_api_escrow_min_delegate_amount() {
let mut mock = mock::Mock::default();
let mut ctx = mock.create_ctx();

Consensus::set_params(Parameters {
min_delegate_amount: 10,
..Default::default()
});

ctx.with_tx(mock::transaction().into(), |mut tx_ctx, _call| {
let hook_name = "test_event_handler";
let amount = BaseUnits::new(5, Denomination::from_str("TEST").unwrap());
let result = Consensus::escrow(
&mut tx_ctx,
keys::alice::address(),
&amount,
MessageEventHookInvocation::new(hook_name.to_string(), 0),
);

assert!(matches!(result, Err(Error::UnderMinDelegationAmount)));
});

ctx.with_tx(mock::transaction().into(), |mut tx_ctx, _call| {
let hook_name = "test_event_handler";
let amount = BaseUnits::new(15, Denomination::from_str("TEST").unwrap());
let result = Consensus::escrow(
&mut tx_ctx,
keys::alice::address(),
&amount,
MessageEventHookInvocation::new(hook_name.to_string(), 0),
);

assert!(result.is_ok());
});
}

#[test]
fn test_api_escrow_scaling() {
let mut mock = mock::Mock::default();
Expand Down Expand Up @@ -430,6 +467,7 @@ fn test_query_parameters() {
let params = Parameters {
consensus_denomination: Denomination::NATIVE,
consensus_scaling_factor: 1_000,
min_delegate_amount: 10,
};
Consensus::set_params(params.clone());

Expand All @@ -445,6 +483,7 @@ fn test_init_bad_scaling_factor_1() {
consensus_denomination: Denomination::NATIVE,
// Zero scaling factor is invalid.
consensus_scaling_factor: 0,
min_delegate_amount: 0,
},
});
}
Expand All @@ -457,6 +496,7 @@ fn test_init_bad_scaling_factor_2() {
consensus_denomination: Denomination::NATIVE,
// Scaling factor that is not a power of 10 is invalid.
consensus_scaling_factor: 1230,
min_delegate_amount: 0,
},
});
}
Loading

0 comments on commit 9e8c6e9

Please sign in to comment.