Skip to content

Commit

Permalink
fetch past objects (#4367)
Browse files Browse the repository at this point in the history
* fetch past objects

* example + cleanup

* move to fullnode api and rename maybe -> try

* rebase
  • Loading branch information
longbowlu authored Sep 9, 2022
1 parent cf5584f commit 2bf1304
Show file tree
Hide file tree
Showing 17 changed files with 1,533 additions and 934 deletions.
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
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::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

0 comments on commit 2bf1304

Please sign in to comment.