Skip to content

Commit

Permalink
Implement the /object/attached-modules/royalty endpoint.
Browse files Browse the repository at this point in the history
  • Loading branch information
jakrawcz-rdx committed Feb 15, 2024
1 parent 6d4f9de commit 55d576f
Show file tree
Hide file tree
Showing 17 changed files with 998 additions and 6 deletions.
83 changes: 82 additions & 1 deletion core-rust/node-http-apis/engine-state-api-schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,39 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"/object/attached-modules/royalty":
post:
summary: Get Royalty Configuration
description: |
Reads the currently configured Package and Component Royalty amounts of particular Object's
methods.
tags:
- AttachedModules
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/ObjectRoyaltyRequest"
responses:
'200':
description: Current assignment
content:
application/json:
schema:
$ref: "#/components/schemas/ObjectRoyaltyResponse"
'400':
description: Client error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
'500':
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"

#############################################################################################
components:
Expand Down Expand Up @@ -2493,7 +2526,7 @@ components:
properties:
key:
type: string
description: A summarized state of the ledger at which the query was performed.
description: Role key.
assignment:
$ref: "#/components/schemas/Assignment"
AssignmentType:
Expand Down Expand Up @@ -2553,6 +2586,54 @@ components:
- Owner: Owner role may only be updated by the owner themselves.
- Object: Owner role may be updated by the object containing the access rules. This is currently primarily used for Presecurified objects.
#########################################################
# REQUEST: /object/attached-modules/royalty #
#########################################################
ObjectRoyaltyRequest:
type: object
required:
- entity_address
- key
properties:
entity_address:
$ref: "#/components/schemas/EntityAddress"
ObjectRoyaltyResponse:
type: object
required:
- at_ledger_state
- method_royalties
properties:
at_ledger_state:
$ref: "#/components/schemas/LedgerStateSummary"
description: A summarized state of the ledger at which the query was performed.
method_royalties:
type: array
items:
$ref: "#/components/schemas/ObjectMethodRoyalty"
ObjectMethodRoyalty:
type: object
required:
- name
properties:
name:
type: string
description: Method name.
component_royalty_amount:
description: |
An amount to be paid in royalties to the Component's owner for calling the method.
When not present, then no Component royalty is due (either because of explicit
configuration, or because of disabled Component royalties).
Please note that on top of this amount, a `package_royalty_amount` may also be due.
$ref: "#/components/schemas/RoyaltyAmount"
package_royalty_amount:
description: |
An amount to be paid in royalties to the Package's owner for calling the method (on
top of the `component_royalty_amount`).
When not present, then no Package royalty is due (either because of explicit
configuration, or because of disabled Package royalties).
Note: this field is returned here only for convenience; since this is a Blueprint-level
configuration, the same information can be obtained from the `/blueprint/info` endpoint.
$ref: "#/components/schemas/RoyaltyAmount"
#########################################################
# REQUEST: /kv-store/iterator #
#########################################################
KeyValueStoreIteratorRequest:
Expand Down
76 changes: 76 additions & 0 deletions core-rust/node-http-apis/src/engine_state_api/attached_modules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,82 @@ lazy_static::lazy_static! {

/// A statically-known type information of the [`RoleAssignmentCollection::AccessRuleKeyValue`].
static ref ROLE_ASSIGNMENT_COLLECTION_META: ObjectCollectionMeta = create_object_collection_meta::<ModuleRoleKey, VersionedRoleAssignmentAccessRule>(RoleAssignmentCollection::AccessRuleKeyValue);

/// A statically-known type information of the [`ComponentRoyaltyCollection::MethodAmountKeyValue`].
static ref METHOD_ROYALTY_COLLECTION_META: ObjectCollectionMeta = create_object_collection_meta::<String, VersionedComponentRoyaltyMethodAmount>(ComponentRoyaltyCollection::MethodAmountKeyValue);
}

/// A loader of Object's attached Royalty information.
///
/// Note: as evident by its sole [`EngineStateDataLoader`] dependency, this loader operates at an
/// abstraction layer higher than the rest of the Engine State API (i.e. it interprets the data that
/// can be read using other, lower-level means).
pub struct ObjectRoyaltyLoader<'s, S: SubstateDatabase> {
meta_loader: EngineStateMetaLoader<'s, S>,
data_loader: EngineStateDataLoader<'s, S>,
}

impl<'s, S: SubstateDatabase> ObjectRoyaltyLoader<'s, S> {
/// Creates an instance reading from the given database.
pub fn new(database: &'s S) -> Self {
Self {
meta_loader: EngineStateMetaLoader::new(database),
data_loader: EngineStateDataLoader::new(database),
}
}

/// Returns Package and Component royalty amounts for all methods of the given object.
pub fn load_method_amounts(
&self,
node_id: &NodeId,
) -> Result<Vec<MethodRoyaltyAmount>, AttachedModuleBrowsingError> {
let EntityMeta::Object(object_meta) = self.meta_loader.load_entity_meta(node_id)? else {
return Err(AttachedModuleBrowsingError::NotAnObject);
};

let mut component_royalties = if object_meta
.attached_module_states
.contains_key(&AttachedModuleId::Royalty)
{
self.data_loader
.iter_object_collection(
node_id,
ModuleId::Royalty,
METHOD_ROYALTY_COLLECTION_META.deref(),
None,
)?
.map(|(key, value)| {
Ok::<_, AttachedModuleBrowsingError>((
decode_kv_collection_key::<String>(key),
decode_latest::<VersionedComponentRoyaltyMethodAmount>(value)?,
))
})
.collect::<Result<NonIterMap<_, _>, _>>()?
} else {
NonIterMap::new()
};

Ok(self
.meta_loader
.load_blueprint_meta(&object_meta.blueprint_reference)?
.methods
.into_iter()
.map(|method| MethodRoyaltyAmount {
for_component: component_royalties
.remove(&method.name)
.unwrap_or(RoyaltyAmount::Free),
for_package: method.royalty,
name: method.name,
})
.collect())
}
}

/// Resolved Package and Component royalty amounts of a specific method.
pub struct MethodRoyaltyAmount {
pub name: String,
pub for_component: RoyaltyAmount,
pub for_package: RoyaltyAmount,
}

/// A loader of Object's attached Role Assignments.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,8 @@ pub mod object_metadata_iterator_request;
pub use self::object_metadata_iterator_request::ObjectMetadataIteratorRequest;
pub mod object_metadata_iterator_response;
pub use self::object_metadata_iterator_response::ObjectMetadataIteratorResponse;
pub mod object_method_royalty;
pub use self::object_method_royalty::ObjectMethodRoyalty;
pub mod object_module_state_info;
pub use self::object_module_state_info::ObjectModuleStateInfo;
pub mod object_role_assignment_request;
Expand All @@ -316,6 +318,10 @@ pub mod object_role_assignment_response;
pub use self::object_role_assignment_response::ObjectRoleAssignmentResponse;
pub mod object_role_assignment_response_attached_modules_inner;
pub use self::object_role_assignment_response_attached_modules_inner::ObjectRoleAssignmentResponseAttachedModulesInner;
pub mod object_royalty_request;
pub use self::object_royalty_request::ObjectRoyaltyRequest;
pub mod object_royalty_response;
pub use self::object_royalty_response::ObjectRoyaltyResponse;
pub mod origin_array_metadata_value;
pub use self::origin_array_metadata_value::OriginArrayMetadataValue;
pub mod origin_metadata_value;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Engine State API
*
* This API provides a complete view of the current ledger state, operating at a relatively low level (i.e. returning Entities' data and type information in a generic way, without interpreting specifics of different native or custom components). It mirrors how the Radix Engine views the ledger state in its \"System\" layer, and thus can be useful for Scrypto developers, who need to inspect how the Engine models and stores their application's state, or how an interface / authentication scheme of another component looks like.
*
* The version of the OpenAPI document: v0.0.1
*
* Generated by: https://openapi-generator.tech
*/




#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize)]
pub struct ObjectMethodRoyalty {
/// Method name.
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "component_royalty_amount", skip_serializing_if = "Option::is_none")]
pub component_royalty_amount: Option<Box<crate::engine_state_api::generated::models::RoyaltyAmount>>,
#[serde(rename = "package_royalty_amount", skip_serializing_if = "Option::is_none")]
pub package_royalty_amount: Option<Box<crate::engine_state_api::generated::models::RoyaltyAmount>>,
}

impl ObjectMethodRoyalty {
pub fn new(name: String) -> ObjectMethodRoyalty {
ObjectMethodRoyalty {
name,
component_royalty_amount: None,
package_royalty_amount: None,
}
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Engine State API
*
* This API provides a complete view of the current ledger state, operating at a relatively low level (i.e. returning Entities' data and type information in a generic way, without interpreting specifics of different native or custom components). It mirrors how the Radix Engine views the ledger state in its \"System\" layer, and thus can be useful for Scrypto developers, who need to inspect how the Engine models and stores their application's state, or how an interface / authentication scheme of another component looks like.
*
* The version of the OpenAPI document: v0.0.1
*
* Generated by: https://openapi-generator.tech
*/




#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize)]
pub struct ObjectRoyaltyRequest {
/// A Bech32m-encoded, human readable rendering of an arbitrary Entity's address.
#[serde(rename = "entity_address")]
pub entity_address: String,
}

impl ObjectRoyaltyRequest {
pub fn new(entity_address: String) -> ObjectRoyaltyRequest {
ObjectRoyaltyRequest {
entity_address,
}
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Engine State API
*
* This API provides a complete view of the current ledger state, operating at a relatively low level (i.e. returning Entities' data and type information in a generic way, without interpreting specifics of different native or custom components). It mirrors how the Radix Engine views the ledger state in its \"System\" layer, and thus can be useful for Scrypto developers, who need to inspect how the Engine models and stores their application's state, or how an interface / authentication scheme of another component looks like.
*
* The version of the OpenAPI document: v0.0.1
*
* Generated by: https://openapi-generator.tech
*/




#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize)]
pub struct ObjectRoyaltyResponse {
#[serde(rename = "at_ledger_state")]
pub at_ledger_state: Box<crate::engine_state_api::generated::models::LedgerStateSummary>,
#[serde(rename = "method_royalties")]
pub method_royalties: Vec<crate::engine_state_api::generated::models::ObjectMethodRoyalty>,
}

impl ObjectRoyaltyResponse {
pub fn new(at_ledger_state: crate::engine_state_api::generated::models::LedgerStateSummary, method_royalties: Vec<crate::engine_state_api::generated::models::ObjectMethodRoyalty>) -> ObjectRoyaltyResponse {
ObjectRoyaltyResponse {
at_ledger_state: Box::new(at_ledger_state),
method_royalties,
}
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize)]
pub struct RoleAssignmentEntry {
/// A summarized state of the ledger at which the query was performed.
/// Role key.
#[serde(rename = "key")]
pub key: String,
#[serde(rename = "assignment")]
Expand Down
2 changes: 2 additions & 0 deletions core-rust/node-http-apis/src/engine_state_api/handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod object_field;
mod object_metadata_entry;
mod object_metadata_iterator;
mod object_role_assignment;
mod object_royalty;

use super::{HasKey, Page, ResponseError};
use crate::engine_prelude::*;
Expand All @@ -30,6 +31,7 @@ pub(crate) use object_field::*;
pub(crate) use object_metadata_entry::*;
pub(crate) use object_metadata_iterator::*;
pub(crate) use object_role_assignment::*;
pub(crate) use object_royalty::*;

/// A paging support for handlers.
/// This is technically a convenience facade on top of [`NextKeyPager`], adding HTTP-level handling of
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use crate::engine_state_api::*;

use crate::engine_prelude::*;

use std::ops::Deref;

pub(crate) async fn handle_object_royalty(
state: State<EngineStateApiState>,
Json(request): Json<models::ObjectRoyaltyRequest>,
) -> Result<Json<models::ObjectRoyaltyResponse>, ResponseError> {
let mapping_context = MappingContext::new(&state.network);
let extraction_context = ExtractionContext::new(&state.network);

let node_id = extract_address_as_node_id(&extraction_context, &request.entity_address)
.map_err(|err| err.into_response_error("entity_address"))?;

let database = state.state_manager.database.read_current();
let loader = ObjectRoyaltyLoader::new(database.deref());

let method_amounts = loader.load_method_amounts(&node_id)?;

let header = read_current_ledger_header(database.deref());

Ok(Json(models::ObjectRoyaltyResponse {
at_ledger_state: Box::new(to_api_ledger_state_summary(&mapping_context, &header)?),
method_royalties: method_amounts
.into_iter()
.map(|method_amount| to_api_method_royalty(&mapping_context, method_amount))
.collect::<Result<Vec<_>, _>>()?,
}))
}

fn to_api_method_royalty(
_context: &MappingContext,
method_amount: MethodRoyaltyAmount,
) -> Result<models::ObjectMethodRoyalty, MappingError> {
let MethodRoyaltyAmount {
name,
for_component,
for_package,
} = method_amount;
Ok(models::ObjectMethodRoyalty {
name,
component_royalty_amount: to_api_royalty_amount(&for_component).map(Box::new),
package_royalty_amount: to_api_royalty_amount(&for_package).map(Box::new),
})
}
4 changes: 4 additions & 0 deletions core-rust/node-http-apis/src/engine_state_api/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ pub async fn create_server<F>(
"/object/attached-modules/role-assignment",
post(handle_object_role_assignment),
)
.route(
"/object/attached-modules/royalty",
post(handle_object_royalty),
)
.route("/kv-store/iterator", post(handle_kv_store_iterator))
.route("/kv-store/entry", post(handle_kv_store_entry))
.route("/entity/schema/entry", post(handle_entity_schema_entry))
Expand Down
Loading

0 comments on commit 55d576f

Please sign in to comment.