sui_rpc_api/grpc/v2/ledger_service/
get_object.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::ErrorReason;
5use crate::RpcError;
6use crate::RpcService;
7use crate::error::ObjectNotFoundError;
8use prost_types::FieldMask;
9use sui_rpc::field::FieldMaskTree;
10use sui_rpc::field::FieldMaskUtil;
11use sui_rpc::proto::google::rpc::bad_request::FieldViolation;
12use sui_rpc::proto::sui::rpc::v2::BatchGetObjectsRequest;
13use sui_rpc::proto::sui::rpc::v2::BatchGetObjectsResponse;
14use sui_rpc::proto::sui::rpc::v2::GetObjectRequest;
15use sui_rpc::proto::sui::rpc::v2::GetObjectResponse;
16use sui_rpc::proto::sui::rpc::v2::GetObjectResult;
17use sui_rpc::proto::sui::rpc::v2::Object;
18use sui_sdk_types::Address;
19
20pub const READ_MASK_DEFAULT: &str = "object_id,version,digest";
21
22type ValidationResult = Result<(Vec<(Address, Option<u64>)>, FieldMaskTree), RpcError>;
23
24pub fn validate_get_object_requests(
25    requests: Vec<(Option<String>, Option<u64>)>,
26    read_mask: Option<FieldMask>,
27) -> ValidationResult {
28    let read_mask = {
29        let read_mask = read_mask.unwrap_or_else(|| FieldMask::from_str(READ_MASK_DEFAULT));
30        read_mask.validate::<Object>().map_err(|path| {
31            FieldViolation::new("read_mask")
32                .with_description(format!("invalid read_mask path: {path}"))
33                .with_reason(ErrorReason::FieldInvalid)
34        })?;
35        FieldMaskTree::from(read_mask)
36    };
37    let requests = requests
38        .into_iter()
39        .enumerate()
40        .map(|(idx, (object_id, version))| {
41            let object_id = object_id
42                .as_ref()
43                .ok_or_else(|| {
44                    FieldViolation::new("object_id")
45                        .with_reason(ErrorReason::FieldMissing)
46                        .nested_at("requests", idx)
47                })?
48                .parse()
49                .map_err(|e| {
50                    FieldViolation::new("object_id")
51                        .with_description(format!("invalid object_id: {e}"))
52                        .with_reason(ErrorReason::FieldInvalid)
53                        .nested_at("requests", idx)
54                })?;
55            Ok((object_id, version))
56        })
57        .collect::<Result<_, RpcError>>()?;
58    Ok((requests, read_mask))
59}
60
61#[tracing::instrument(skip(service))]
62pub fn get_object(
63    service: &RpcService,
64    GetObjectRequest {
65        object_id,
66        version,
67        read_mask,
68        ..
69    }: GetObjectRequest,
70) -> Result<GetObjectResponse, RpcError> {
71    let (requests, read_mask) =
72        validate_get_object_requests(vec![(object_id, version)], read_mask)?;
73    let (object_id, version) = requests[0];
74    get_object_impl(service, object_id, version, &read_mask).map(GetObjectResponse::new)
75}
76
77#[tracing::instrument(skip(service))]
78pub fn batch_get_objects(
79    service: &RpcService,
80    BatchGetObjectsRequest {
81        requests,
82        read_mask,
83        ..
84    }: BatchGetObjectsRequest,
85) -> Result<BatchGetObjectsResponse, RpcError> {
86    let requests = requests
87        .into_iter()
88        .map(|req| (req.object_id, req.version))
89        .collect();
90    let (requests, read_mask) = validate_get_object_requests(requests, read_mask)?;
91    let objects = requests
92        .into_iter()
93        .map(|(object_id, version)| get_object_impl(service, object_id, version, &read_mask))
94        .map(|result| match result {
95            Ok(object) => GetObjectResult::new_object(object),
96            Err(error) => GetObjectResult::new_error(error.into_status_proto()),
97        })
98        .collect();
99    Ok(BatchGetObjectsResponse::new(objects))
100}
101
102#[tracing::instrument(skip(service))]
103fn get_object_impl(
104    service: &RpcService,
105    object_id: Address,
106    version: Option<u64>,
107    read_mask: &FieldMaskTree,
108) -> Result<Object, RpcError> {
109    let object = if let Some(version) = version {
110        service
111            .reader
112            .inner()
113            .get_object_by_key(&object_id.into(), version.into())
114            .ok_or_else(|| ObjectNotFoundError::new_with_version(object_id, version))?
115    } else {
116        service
117            .reader
118            .inner()
119            .get_object(&object_id.into())
120            .ok_or_else(|| ObjectNotFoundError::new(object_id))?
121    };
122
123    Ok(service.render_object_to_proto(&object, read_mask))
124}