Skip to content

Commit

Permalink
feat: add get_class endpoint to API
Browse files Browse the repository at this point in the history
  • Loading branch information
ftupas authored and AbdelStark committed Dec 23, 2022
1 parent 7c0a97a commit fb211b9
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 4 deletions.
55 changes: 52 additions & 3 deletions beerus_rest_api/src/api/starknet/endpoints.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::resp::{
QueryBlockHashAndNumberResponse, QueryBlockNumberResponse, QueryChainIdResponse,
QueryContractViewResponse, QueryGetStorageAtResponse, QueryL1ToL2MessageCancellationsResponse,
QueryL1ToL2MessageNonceResponse, QueryL1ToL2MessagesResponse, QueryNonceResponse,
QueryStateRootResponse,
QueryContractViewResponse, QueryGetClassResponse, QueryGetStorageAtResponse,
QueryL1ToL2MessageCancellationsResponse, QueryL1ToL2MessageNonceResponse,
QueryL1ToL2MessagesResponse, QueryNonceResponse, QueryStateRootResponse,
};
use crate::api::ApiResponse;

Expand Down Expand Up @@ -125,6 +125,30 @@ pub async fn query_l1_to_l2_message_nonce(
ApiResponse::from_result(query_l1_to_l2_message_nonce_inner(beerus).await)
}

/// Query the contract class definition in the given block associated with the given hash.
/// The contract class definition.
///
/// # Arguments
///
/// * `block_id_type` - Type of block identifier. eg. hash, number, tag
/// * `block_id` - The block identifier. eg. 0x123, 123, pending, or latest
/// * `class_hash` - The class hash.
///
/// # Returns
///
/// `Ok(ContractClass)` if the operation was successful.
/// `Err(eyre::Report)` if the operation failed.
#[openapi]
#[get("/starknet/contract/class/<class_hash>?<block_id>&<block_id_type>")]
pub async fn get_class(
beerus: &State<BeerusLightClient>,
block_id_type: String,
block_id: String,
class_hash: String,
) -> ApiResponse<QueryGetClassResponse> {
ApiResponse::from_result(get_class_inner(beerus, block_id_type, block_id, class_hash).await)
}

/// Query the state root of StarkNet.
///
/// # Arguments
Expand Down Expand Up @@ -383,3 +407,28 @@ pub async fn query_l1_to_l2_message_nonce_inner(
result: beerus.starknet_l1_to_l2_message_nonce().await?.to_string(),
})
}

/// Query the contract class
/// # Returns
/// `ContractClass` - The contract class definition.
/// # Examples
pub async fn get_class_inner(
beerus: &State<BeerusLightClient>,
block_id_type: String,
block_id: String,
class_hash: String,
) -> Result<QueryGetClassResponse> {
let block_id =
beerus_core::starknet_helper::block_id_string_to_block_id_type(&block_id_type, &block_id)?;
let class_hash = FieldElement::from_str(&class_hash)?;
debug!("Querying Contract Class");
let result = beerus
.starknet_lightclient
.get_class(&block_id, class_hash)
.await?;
Ok(QueryGetClassResponse {
program: base64::encode(&result.program),
entry_points_by_type: serde_json::value::to_value(&result.entry_points_by_type).unwrap(),
abi: serde_json::value::to_value(result.abi.unwrap()).unwrap(),
})
}
10 changes: 9 additions & 1 deletion beerus_rest_api/src/api/starknet/resp/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use rocket::serde::Serialize;
use schemars::JsonSchema;

use serde_json::Value;
#[derive(Serialize, JsonSchema)]
#[serde(crate = "rocket::serde")]
pub struct QueryStateRootResponse {
Expand Down Expand Up @@ -66,3 +66,11 @@ pub struct QueryBlockHashAndNumberResponse {
pub block_hash: String,
pub block_number: String,
}

#[derive(Serialize, JsonSchema)]
#[serde(crate = "rocket::serde")]
pub struct QueryGetClassResponse {
pub abi: Value,
pub entry_points_by_type: Value,
pub program: String,
}
1 change: 1 addition & 0 deletions beerus_rest_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub async fn build_rocket_server(beerus: BeerusLightClient) -> Rocket<Build> {
starknet::endpoints::query_starknet_chain_id,
starknet::endpoints::query_starknet_block_number,
starknet::endpoints::query_starknet_block_hash_and_number,
starknet::endpoints::get_class,
],
)
}
Expand Down
86 changes: 86 additions & 0 deletions beerus_rest_api/tests/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1350,6 +1350,92 @@ mod test {
);
}

/// Test the `get_class` endpoint.
/// `/starknet/contract/class/<class_hash>?<block_id>&<block_id_type>`
/// Given normal conditions, when query starknet get_class, then ok.
#[tokio::test]
async fn given_normal_conditions_when_get_class_then_ok() {
// Build mocks.
let (config, ethereum_lightclient, mut starknet_lightclient) = config_and_mocks();

// Given
let (expected_result, expected_result_value) =
beerus_core::starknet_helper::create_mock_contract_class();

// Set the expected return value for the StarkNet light client mock.
starknet_lightclient
.expect_get_class()
.return_once(move |_block_id, _class_hash| Ok(expected_result));

let beerus = BeerusLightClient::new(
config,
Box::new(ethereum_lightclient),
Box::new(starknet_lightclient),
);
let client = Client::tracked(build_rocket_server(beerus).await)
.await
.expect("valid rocket instance");

// When
let response = client
.get(uri!(
"/starknet/contract/class/0x123?block_id=123&block_id_type=number"
))
.dispatch()
.await;

// Then
assert_eq!(response.status(), Status::Ok);
assert_eq!(
response.into_string().await.unwrap(),
serde_json::to_string(&expected_result_value).unwrap()
);
}

/// Test the `get_class` endpoint.
/// `/starknet/contract/class/<class_hash>?<block_id>&<block_id_type>`
/// Given StarkNet light client returns error when query starknet get_class, then error is propagated.
#[tokio::test]
async fn given_starknet_ligthclient_returns_error_when_get_class_then_error_is_propagated() {
// Build mocks.
let (config, ethereum_lightclient, mut starknet_lightclient) = config_and_mocks();

// Given

// Set the expected return value for the StarkNet light client mock.
starknet_lightclient
.expect_get_class()
.return_once(move |_block_id, _class_hash| {
Err(eyre::eyre!("cannot query starknet address block number"))
});

let beerus = BeerusLightClient::new(
config,
Box::new(ethereum_lightclient),
Box::new(starknet_lightclient),
);

// Build the Rocket instance.
let client = Client::tracked(build_rocket_server(beerus).await)
.await
.expect("valid rocket instance");

// When
let response = client
.get(uri!(
"/starknet/contract/class/0x123?block_id=123&block_id_type=number"
))
.dispatch()
.await;

// Then
assert_eq!(response.status(), Status::InternalServerError);
assert_eq!(
response.into_string().await.unwrap(),
"{\"error_message\":\"cannot query starknet address block number\"}"
);
}

fn config_and_mocks() -> (Config, MockEthereumLightClient, MockStarkNetLightClient) {
let config = Config {
ethereum_network: "mainnet".to_string(),
Expand Down

0 comments on commit fb211b9

Please sign in to comment.