Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fetch past objects #4367

Merged
merged 4 commits into from
Sep 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 60 additions & 1 deletion crates/sui-core/src/authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ use sui_types::crypto::AuthorityKeyPair;
use sui_types::messages_checkpoint::{
CheckpointRequest, CheckpointRequestType, CheckpointResponse, CheckpointSequenceNumber,
};
use sui_types::object::Owner;
use sui_types::object::{Owner, PastObjectRead};
use sui_types::sui_system_state::SuiSystemState;

pub mod authority_notifier;
Expand Down Expand Up @@ -1390,6 +1390,65 @@ impl AuthorityState {
}
}

/// This function aims to serve rpc reads on past objects and
/// we don't expect it to be called for other purposes.
/// Depending on the object pruning policies that will be enforced in the
/// future there is no software-level guarantee/SLA to retrieve an object
/// with an old version even if it exists/existed.
pub async fn get_past_object_read(
&self,
object_id: &ObjectID,
version: SequenceNumber,
) -> Result<PastObjectRead, SuiError> {
// Firstly we see if the object ever exists by getting its latest data
match self.database.get_latest_parent_entry(*object_id)? {
None => Ok(PastObjectRead::ObjectNotExists(*object_id)),
Some((obj_ref, _)) => {
if version > obj_ref.1 {
return Ok(PastObjectRead::VersionTooHigh {
object_id: *object_id,
asked_version: version,
latest_version: obj_ref.1,
});
}
if version < obj_ref.1 {
// Read past objects
return Ok(match self.database.get_object_by_key(object_id, version)? {
None => PastObjectRead::VersionNotFound(*object_id, version),
Some(object) => {
let layout = object.get_layout(
ObjectFormatOptions::default(),
self.module_cache.as_ref(),
)?;
let obj_ref = object.compute_object_reference();
PastObjectRead::VersionFound(obj_ref, object, layout)
}
});
}
// version is equal to the latest seq number this node knows
if obj_ref.2.is_alive() {
match self.database.get_object_by_key(object_id, obj_ref.1)? {
None => {
error!("Object with in parent_entry is missing from object store, datastore is inconsistent");
Err(SuiError::ObjectNotFound {
object_id: *object_id,
})
}
Some(object) => {
let layout = object.get_layout(
ObjectFormatOptions::default(),
self.module_cache.as_ref(),
)?;
Ok(PastObjectRead::VersionFound(obj_ref, object, layout))
}
}
} else {
Ok(PastObjectRead::ObjectDeleted(obj_ref))
}
}
}
}

pub fn get_owner_objects(&self, owner: Owner) -> SuiResult<Vec<ObjectInfo>> {
self.database.get_owner_objects(owner)
}
Expand Down
102 changes: 101 additions & 1 deletion crates/sui-json-rpc-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ use sui_types::messages::{
};
use sui_types::messages_checkpoint::CheckpointSequenceNumber;
use sui_types::move_package::{disassemble_modules, MovePackage};
use sui_types::object::{Data, MoveObject, Object, ObjectFormatOptions, ObjectRead, Owner};
use sui_types::object::{
Data, MoveObject, Object, ObjectFormatOptions, ObjectRead, Owner, PastObjectRead,
};
use sui_types::sui_serde::{Base64, Encoding};

#[cfg(test)]
Expand Down Expand Up @@ -993,6 +995,104 @@ impl<T: SuiData> TryFrom<ObjectRead> for SuiObjectRead<T> {
}
}

pub type GetPastObjectDataResponse = SuiPastObjectRead<SuiParsedData>;

#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
#[serde(tag = "status", content = "details", rename = "ObjectRead")]
pub enum SuiPastObjectRead<T: SuiData> {
/// The object exists and is found with this version
VersionFound(SuiObject<T>),
/// The object does not exist
ObjectNotExists(ObjectID),
/// The object is found to be deleted with this version
longbowlu marked this conversation as resolved.
Show resolved Hide resolved
ObjectDeleted(SuiObjectRef),
/// The object exists but not found with this version
VersionNotFound(ObjectID, SequenceNumber),
/// The asked object version is higher than the latest
VersionTooHigh {
longbowlu marked this conversation as resolved.
Show resolved Hide resolved
object_id: ObjectID,
asked_version: SequenceNumber,
latest_version: SequenceNumber,
},
}

impl<T: SuiData> SuiPastObjectRead<T> {
/// Returns a reference to the object if there is any, otherwise an Err
pub fn object(&self) -> Result<&SuiObject<T>, SuiError> {
match &self {
Self::ObjectDeleted(oref) => Err(SuiError::ObjectDeleted {
object_ref: oref.to_object_ref(),
}),
Self::ObjectNotExists(id) => Err(SuiError::ObjectNotFound { object_id: *id }),
Self::VersionFound(o) => Ok(o),
Self::VersionNotFound(id, seq_num) => Err(SuiError::ObjectVersionNotFound {
object_id: *id,
version: *seq_num,
}),
Self::VersionTooHigh {
object_id,
asked_version,
latest_version,
} => Err(SuiError::ObjectSequenceNumberTooHigh {
object_id: *object_id,
asked_version: *asked_version,
latest_version: *latest_version,
}),
}
}

/// Returns the object value if there is any, otherwise an Err
pub fn into_object(self) -> Result<SuiObject<T>, SuiError> {
match self {
Self::ObjectDeleted(oref) => Err(SuiError::ObjectDeleted {
object_ref: oref.to_object_ref(),
}),
Self::ObjectNotExists(id) => Err(SuiError::ObjectNotFound { object_id: id }),
Self::VersionFound(o) => Ok(o),
Self::VersionNotFound(object_id, version) => {
Err(SuiError::ObjectVersionNotFound { object_id, version })
}
Self::VersionTooHigh {
object_id,
asked_version,
latest_version,
} => Err(SuiError::ObjectSequenceNumberTooHigh {
object_id,
asked_version,
latest_version,
}),
}
}
}

impl<T: SuiData> TryFrom<PastObjectRead> for SuiPastObjectRead<T> {
type Error = anyhow::Error;

fn try_from(value: PastObjectRead) -> Result<Self, Self::Error> {
match value {
PastObjectRead::ObjectNotExists(id) => Ok(SuiPastObjectRead::ObjectNotExists(id)),
PastObjectRead::VersionFound(_, o, layout) => Ok(SuiPastObjectRead::VersionFound(
SuiObject::try_from(o, layout)?,
)),
PastObjectRead::ObjectDeleted(oref) => {
Ok(SuiPastObjectRead::ObjectDeleted(oref.into()))
}
PastObjectRead::VersionNotFound(id, seq_num) => {
Ok(SuiPastObjectRead::VersionNotFound(id, seq_num))
}
PastObjectRead::VersionTooHigh {
object_id,
asked_version,
latest_version,
} => Ok(SuiPastObjectRead::VersionTooHigh {
object_id,
asked_version,
latest_version,
}),
}
}
}

#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Eq, PartialEq)]
#[serde(untagged, rename = "MoveValue")]
pub enum SuiMoveValue {
Expand Down
24 changes: 19 additions & 5 deletions crates/sui-json-rpc/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ use jsonrpsee::core::RpcResult;
use jsonrpsee_proc_macros::rpc;
use sui_json::SuiJsonValue;
use sui_json_rpc_types::{
GatewayTxSeqNumber, GetObjectDataResponse, GetRawObjectDataResponse, MoveFunctionArgType,
RPCTransactionRequestParams, SuiEventEnvelope, SuiEventFilter, SuiExecuteTransactionResponse,
SuiGasCostSummary, SuiMoveNormalizedFunction, SuiMoveNormalizedModule, SuiMoveNormalizedStruct,
SuiObjectInfo, SuiTransactionFilter, SuiTransactionResponse, SuiTypeTag, TransactionBytes,
GatewayTxSeqNumber, GetObjectDataResponse, GetPastObjectDataResponse, GetRawObjectDataResponse,
MoveFunctionArgType, RPCTransactionRequestParams, SuiEventEnvelope, SuiEventFilter,
SuiExecuteTransactionResponse, SuiGasCostSummary, SuiMoveNormalizedFunction,
SuiMoveNormalizedModule, SuiMoveNormalizedStruct, SuiObjectInfo, SuiTransactionFilter,
SuiTransactionResponse, SuiTypeTag, TransactionBytes,
};
use sui_open_rpc_macros::open_rpc;
use sui_types::base_types::{ObjectID, SuiAddress, TransactionDigest};
use sui_types::base_types::{ObjectID, SequenceNumber, SuiAddress, TransactionDigest};
use sui_types::crypto::SignatureScheme;
use sui_types::messages::ExecuteTransactionRequestType;
use sui_types::object::Owner;
Expand Down Expand Up @@ -202,6 +203,19 @@ pub trait RpcFullNodeReadApi {
/// the recipient's Sui address
addr: SuiAddress,
) -> RpcResult<Vec<(GatewayTxSeqNumber, TransactionDigest)>>;

/// Note there is no software-level guarantee/SLA that objects with past versions
/// can be retrieved by this API, even if the object and version exists/existed.
/// The result may vary across nodes depending on their pruning policies.
/// Return the object information for a specified version
#[method(name = "tryGetPastObject")]
async fn try_get_past_object(
&self,
/// the ID of the queried object
object_id: ObjectID,
/// the version of the queried object. If None, default to the latest known version
version: SequenceNumber,
) -> RpcResult<GetPastObjectDataResponse>;
}

#[open_rpc(namespace = "sui", tag = "Transaction Builder API")]
Expand Down
11 changes: 5 additions & 6 deletions crates/sui-json-rpc/src/gateway_api.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use crate::api::{
RpcGatewayApiServer, RpcReadApiServer, RpcTransactionBuilderServer, WalletSyncApiServer,
};
use crate::SuiRpcModule;
use anyhow::anyhow;
use async_trait::async_trait;
use jsonrpsee::core::RpcResult;
use jsonrpsee_core::server::rpc_module::RpcModule;
use signature::Signature;
use tracing::debug;

use crate::api::{
RpcGatewayApiServer, RpcReadApiServer, RpcTransactionBuilderServer, WalletSyncApiServer,
};
use crate::SuiRpcModule;
use sui_core::gateway_state::{GatewayClient, GatewayTxSeqNumber};
use sui_json::SuiJsonValue;
use sui_json_rpc_types::{
Expand All @@ -27,6 +25,7 @@ use sui_types::{
crypto::SignableBytes,
messages::{Transaction, TransactionData},
};
use tracing::debug;

pub struct RpcGatewayImpl {
client: GatewayClient,
Expand Down
20 changes: 17 additions & 3 deletions crates/sui-json-rpc/src/read_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ use std::sync::Arc;
use sui_core::authority::AuthorityState;
use sui_core::gateway_state::GatewayTxSeqNumber;
use sui_json_rpc_types::{
GetObjectDataResponse, MoveFunctionArgType, ObjectValueKind, SuiMoveNormalizedFunction,
SuiMoveNormalizedModule, SuiMoveNormalizedStruct, SuiObjectInfo, SuiTransactionEffects,
SuiTransactionResponse,
GetObjectDataResponse, GetPastObjectDataResponse, MoveFunctionArgType, ObjectValueKind,
SuiMoveNormalizedFunction, SuiMoveNormalizedModule, SuiMoveNormalizedStruct, SuiObjectInfo,
SuiTransactionEffects, SuiTransactionResponse,
};
use sui_open_rpc::Module;
use sui_types::base_types::SequenceNumber;
use sui_types::base_types::{ObjectID, SuiAddress, TransactionDigest};
use sui_types::move_package::normalize_modules;
use sui_types::object::{Data, ObjectRead, Owner};
Expand Down Expand Up @@ -277,6 +278,19 @@ impl RpcFullNodeReadApiServer for FullNodeApi {
) -> RpcResult<Vec<(GatewayTxSeqNumber, TransactionDigest)>> {
Ok(self.state.get_transactions_to_addr(addr).await?)
}

async fn try_get_past_object(
&self,
object_id: ObjectID,
version: SequenceNumber,
) -> RpcResult<GetPastObjectDataResponse> {
Ok(self
.state
.get_past_object_read(&object_id, version)
.await
.map_err(|e| anyhow!("{e}"))?
.try_into()?)
}
}

impl SuiRpcModule for FullNodeApi {
Expand Down
Loading