Skip to content

Commit

Permalink
example + cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
longbowlu committed Sep 2, 2022
1 parent 31972fc commit 9adcc13
Show file tree
Hide file tree
Showing 21 changed files with 1,251 additions and 977 deletions.
4 changes: 0 additions & 4 deletions crates/sui-cluster-test/src/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,6 @@ impl ObjectChecker {
println!("getting object {object_id}, info :: {object_info:?}");

match object_info {
GetRawObjectDataResponse::SequenceNumberTooHigh { .. }
| GetRawObjectDataResponse::ExistsButPastNotFound(_, _) => {
panic!("Unexpected response: {:?}", object_info)
}
GetRawObjectDataResponse::NotExists(_) => {
panic!("Node can't find gas object {}", object_id)
}
Expand Down
92 changes: 62 additions & 30 deletions 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 @@ -1354,37 +1354,69 @@ impl AuthorityState {
self.database.get_sui_system_state_object()
}

pub async fn get_object_read(
pub async fn get_object_read(&self, object_id: &ObjectID) -> Result<ObjectRead, SuiError> {
match self.database.get_latest_parent_entry(*object_id)? {
None => Ok(ObjectRead::NotExists(*object_id)),
Some((obj_ref, _)) => {
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(ObjectRead::Exists(obj_ref, object, layout))
}
}
} else {
Ok(ObjectRead::Deleted(obj_ref))
}
}
}
}

/// 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,
seq_num: Option<SequenceNumber>,
) -> Result<ObjectRead, SuiError> {
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)?, seq_num) {
(None, _) => Ok(ObjectRead::NotExists(*object_id)),
(Some((obj_ref, _)), Some(seq_num)) if seq_num > obj_ref.1 => {
Ok(ObjectRead::SequenceNumberTooHigh {
object_id: *object_id,
asked_seq_num: seq_num,
latest_seq_num: obj_ref.1,
})
}
(Some((obj_ref, _)), Some(seq_num)) if seq_num < obj_ref.1 => {
// Read past objects
match self.database.get_object_by_key(object_id, seq_num)? {
None => Ok(ObjectRead::ExistsButPastNotFound(*object_id, seq_num)),
Some(object) => {
let layout = object.get_layout(
ObjectFormatOptions::default(),
self.module_cache.as_ref(),
)?;
let obj_ref = object.compute_object_reference();
Ok(ObjectRead::Exists(obj_ref, object, layout))
}
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,
});
}
}
// seq_num not provided or equal to the latest seq number this node knows
(Some((obj_ref, _)), _) => {
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 => {
Expand All @@ -1398,11 +1430,11 @@ impl AuthorityState {
ObjectFormatOptions::default(),
self.module_cache.as_ref(),
)?;
Ok(ObjectRead::Exists(obj_ref, object, layout))
Ok(PastObjectRead::VersionFound(obj_ref, object, layout))
}
}
} else {
Ok(ObjectRead::Deleted(obj_ref))
Ok(PastObjectRead::ObjectDeleted(obj_ref))
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/sui-gateway/src/unit_tests/rpc_server_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ async fn test_get_object_info() -> Result<(), anyhow::Error> {
let objects = http_client.get_objects_owned_by_address(*address).await?;

for oref in objects {
let result: GetObjectDataResponse = http_client.get_object(oref.object_id, None).await?;
let result: GetObjectDataResponse = http_client.get_object(oref.object_id).await?;
assert!(
matches!(result, GetObjectDataResponse::Exists(object) if oref.object_id == object.id() && &object.owner.get_owner_address()? == address)
);
Expand Down
138 changes: 97 additions & 41 deletions 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 @@ -951,12 +953,6 @@ pub enum SuiObjectRead<T: SuiData> {
Exists(SuiObject<T>),
NotExists(ObjectID),
Deleted(SuiObjectRef),
ExistsButPastNotFound(ObjectID, SequenceNumber),
SequenceNumberTooHigh {
object_id: ObjectID,
asked_seq_num: SequenceNumber,
latest_seq_num: SequenceNumber,
},
}

impl<T: SuiData> SuiObjectRead<T> {
Expand All @@ -969,19 +965,6 @@ impl<T: SuiData> SuiObjectRead<T> {
}),
Self::NotExists(id) => Err(SuiError::ObjectNotFound { object_id: *id }),
Self::Exists(o) => Ok(o),
Self::ExistsButPastNotFound(id, seq_num) => Err(SuiError::PastObjectNotFound {
object_id: *id,
seq_num: *seq_num,
}),
Self::SequenceNumberTooHigh {
object_id,
asked_seq_num,
latest_seq_num,
} => Err(SuiError::ObjectSequenceNumberTooHigh {
object_id: *object_id,
asked_seq_num: *asked_seq_num,
latest_seq_num: *latest_seq_num,
}),
}
}

Expand All @@ -994,19 +977,6 @@ impl<T: SuiData> SuiObjectRead<T> {
}),
Self::NotExists(id) => Err(SuiError::ObjectNotFound { object_id: id }),
Self::Exists(o) => Ok(o),
Self::ExistsButPastNotFound(id, seq_num) => Err(SuiError::PastObjectNotFound {
object_id: id,
seq_num,
}),
Self::SequenceNumberTooHigh {
object_id,
asked_seq_num,
latest_seq_num,
} => Err(SuiError::ObjectSequenceNumberTooHigh {
object_id,
asked_seq_num,
latest_seq_num,
}),
}
}
}
Expand All @@ -1021,17 +991,103 @@ impl<T: SuiData> TryFrom<ObjectRead> for SuiObjectRead<T> {
Ok(SuiObjectRead::Exists(SuiObject::try_from(o, layout)?))
}
ObjectRead::Deleted(oref) => Ok(SuiObjectRead::Deleted(oref.into())),
ObjectRead::ExistsButPastNotFound(id, seq_num) => {
Ok(SuiObjectRead::ExistsButPastNotFound(id, seq_num))
}
}
}

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
ObjectDeleted(SuiObjectRef),
/// The object exists but not found with this version
VersionNotFound(ObjectID, SequenceNumber),
/// The asked object version is higher than the latest
VersionTooHigh {
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::PastObjectNotFound {
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::PastObjectNotFound { 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))
}
ObjectRead::SequenceNumberTooHigh {
PastObjectRead::VersionTooHigh {
object_id,
asked_seq_num,
latest_seq_num,
} => Ok(SuiObjectRead::SequenceNumberTooHigh {
asked_version,
latest_version,
} => Ok(SuiPastObjectRead::VersionTooHigh {
object_id,
asked_seq_num,
latest_seq_num,
asked_version,
latest_version,
}),
}
}
Expand Down
24 changes: 18 additions & 6 deletions crates/sui-json-rpc/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ 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, SequenceNumber, SuiAddress, TransactionDigest};
Expand Down Expand Up @@ -110,9 +111,20 @@ pub trait RpcReadApi {
&self,
/// the ID of the queried object
object_id: ObjectID,
/// the sequence number of the queried object, If None, use the latest sequence number
seq_num: Option<SequenceNumber>,
) -> RpcResult<GetObjectDataResponse>;

/// Note there is no software-level guarantee/SLA that objects with past versions
/// can be retrieved with 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 = "getPastObjectMaybe")]
async fn get_past_object_maybe(
&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 = "Full Node API")]
Expand Down
2 changes: 1 addition & 1 deletion crates/sui-json-rpc/src/bcs_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl ClientStateAdaptor {
match self {
ClientStateAdaptor::Gateway(client) => client.get_raw_object(object_id).await,
ClientStateAdaptor::FullNode(client) => client
.get_object_read(&object_id, None)
.get_object_read(&object_id)
.await
.map_err(|e| anyhow!("{e}"))?
.try_into(),
Expand Down
Loading

0 comments on commit 9adcc13

Please sign in to comment.