Skip to content

Commit

Permalink
rpc-alt: tryGetPastObject showContent, showBcs
Browse files Browse the repository at this point in the history
  • Loading branch information
amnn committed Jan 28, 2025
1 parent 842fc9e commit 62e5074
Show file tree
Hide file tree
Showing 3 changed files with 257 additions and 9 deletions.
141 changes: 141 additions & 0 deletions crates/sui-indexer-alt-e2e-tests/tests/jsonrpc/objects/contents.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
processed 8 tasks

init:
A: object(0,0)

task 1, lines 10-19:
//# publish
created: object(1,0)
mutated: object(0,1)
gas summary: computation_cost: 1000000, storage_cost: 4620800, storage_rebate: 0, non_refundable_storage_fee: 0

task 2, lines 21-23:
//# programmable --sender A --inputs @A
//> 0: test::mod::new();
//> 1: TransferObjects([Result(0)], Input(0))
created: object(2,0)
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 2226800, storage_rebate: 0, non_refundable_storage_fee: 0

task 3, lines 25-27:
//# programmable --sender A --inputs 42 @A
//> 0: SplitCoins(Gas, [Input(0)]);
//> 1: TransferObjects([Result(0)], Input(1))
created: object(3,0)
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 1976000, storage_rebate: 978120, non_refundable_storage_fee: 9880

task 4, line 29:
//# create-checkpoint
Checkpoint created: 1

task 5, lines 31-35:
//# run-jsonrpc
Response: {
"jsonrpc": "2.0",
"id": 0,
"result": {
"status": "VersionFound",
"details": {
"objectId": "0xb1d114770bfc9968a2ad3da9c6d5bcbf32e4bcf3d0bf3eba674df5d907a83e73",
"version": "1",
"digest": "Sgp59rDKZRKoSZ2ZJKKcc5YQ7MrhEHDxbZLqqFjywcq",
"content": {
"dataType": "package",
"disassembled": {
"mod": "// Move bytecode v6\nmodule b1d114770bfc9968a2ad3da9c6d5bcbf32e4bcf3d0bf3eba674df5d907a83e73.mod {\nuse 0000000000000000000000000000000000000000000000000000000000000002::object;\nuse 0000000000000000000000000000000000000000000000000000000000000002::tx_context;\n\nstruct Foo has store, key {\n\tid: UID\n}\n\npublic new(Arg0: &mut TxContext): Foo {\nB0:\n\t0: MoveLoc[0](Arg0: &mut TxContext)\n\t1: Call object::new(&mut TxContext): UID\n\t2: Pack[0](Foo)\n\t3: Ret\n}\n\n}\n"
}
},
"bcs": {
"dataType": "package",
"id": "0xb1d114770bfc9968a2ad3da9c6d5bcbf32e4bcf3d0bf3eba674df5d907a83e73",
"version": 1,
"moduleMap": {
"mod": "oRzrCwYAAAAIAQAGAgYMAxIKBRwLBycvCFZACpYBBgycAQ0ABAEGAQcAAAwAAQIEAAIBAgAABQABAAEFAAMAAQcIAgEIAAABCAEDRm9vCVR4Q29udGV4dANVSUQCaWQDbW9kA25ldwZvYmplY3QKdHhfY29udGV4dLHRFHcL/Jlooq09qcbVvL8y5Lzz0L8+umdN9dkHqD5zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAgEDCAEAAQAAAgQLABEBEgACAA=="
},
"typeOriginTable": [
{
"module_name": "mod",
"datatype_name": "Foo",
"package": "0xb1d114770bfc9968a2ad3da9c6d5bcbf32e4bcf3d0bf3eba674df5d907a83e73"
}
],
"linkageTable": {
"0x0000000000000000000000000000000000000000000000000000000000000001": {
"upgraded_id": "0x0000000000000000000000000000000000000000000000000000000000000001",
"upgraded_version": 1
},
"0x0000000000000000000000000000000000000000000000000000000000000002": {
"upgraded_id": "0x0000000000000000000000000000000000000000000000000000000000000002",
"upgraded_version": 1
}
}
}
}
}
}

task 6, lines 37-41:
//# run-jsonrpc
Response: {
"jsonrpc": "2.0",
"id": 1,
"result": {
"status": "VersionFound",
"details": {
"objectId": "0x5736caa914301b5f6bc2734fdd6ef4c0097ebf6f0b346ec0ce1119f3a86c8c37",
"version": "2",
"digest": "6pd5G7cgCu3ShFTxpyo7xJTsq5EwzbAnrmfNeR55roUH",
"content": {
"dataType": "moveObject",
"type": "0xb1d114770bfc9968a2ad3da9c6d5bcbf32e4bcf3d0bf3eba674df5d907a83e73::mod::Foo",
"hasPublicTransfer": true,
"fields": {
"id": {
"id": "0x5736caa914301b5f6bc2734fdd6ef4c0097ebf6f0b346ec0ce1119f3a86c8c37"
}
}
},
"bcs": {
"dataType": "moveObject",
"type": "0xb1d114770bfc9968a2ad3da9c6d5bcbf32e4bcf3d0bf3eba674df5d907a83e73::mod::Foo",
"hasPublicTransfer": true,
"version": 2,
"bcsBytes": "VzbKqRQwG19rwnNP3W70wAl+v28LNG7AzhEZ86hsjDc="
}
}
}
}

task 7, lines 43-47:
//# run-jsonrpc
Response: {
"jsonrpc": "2.0",
"id": 2,
"result": {
"status": "VersionFound",
"details": {
"objectId": "0xf2a6833ec5d2dd77e656a3fe62bdd4e4609b23fa0739312e384fdbb06080155e",
"version": "3",
"digest": "AeAb1PukmXSZNcUMrQmqqWeEwSwyoKXhSGogxN37Wdym",
"content": {
"dataType": "moveObject",
"type": "0x2::coin::Coin<0x2::sui::SUI>",
"hasPublicTransfer": true,
"fields": {
"balance": "42",
"id": {
"id": "0xf2a6833ec5d2dd77e656a3fe62bdd4e4609b23fa0739312e384fdbb06080155e"
}
}
},
"bcs": {
"dataType": "moveObject",
"type": "0x2::coin::Coin<0x2::sui::SUI>",
"hasPublicTransfer": true,
"version": 3,
"bcsBytes": "8qaDPsXS3XfmVqP+Yr3U5GCbI/oHOTEuOE/bsGCAFV4qAAAAAAAAAA=="
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//# init --protocol-version 70 --accounts A --addresses test=0x0 --simulator

// 1. View the contents of a package
// 2. View the contents of an arbitrary object
// 3. View the contents of a coin

//# publish
module test::mod {
public struct Foo has key, store {
id: UID,
}

public fun new(ctx: &mut TxContext): Foo {
Foo { id: object::new(ctx) }
}
}

//# programmable --sender A --inputs @A
//> 0: test::mod::new();
//> 1: TransferObjects([Result(0)], Input(0))

//# programmable --sender A --inputs 42 @A
//> 0: SplitCoins(Gas, [Input(0)]);
//> 1: TransferObjects([Result(0)], Input(1))

//# create-checkpoint

//# run-jsonrpc
{
"method": "sui_tryGetPastObject",
"params": ["@{obj_1_0}", 1, { "showContent": true, "showBcs": true }]
}

//# run-jsonrpc
{
"method": "sui_tryGetPastObject",
"params": ["@{obj_2_0}", 2, { "showContent": true, "showBcs": true }]
}

//# run-jsonrpc
{
"method": "sui_tryGetPastObject",
"params": ["@{obj_3_0}", 3, { "showContent": true, "showBcs": true }]
}
78 changes: 69 additions & 9 deletions crates/sui-indexer-alt-jsonrpc/src/api/objects/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,25 @@
// SPDX-License-Identifier: Apache-2.0

use anyhow::Context as _;
use futures::future::OptionFuture;
use move_core_types::annotated_value::MoveTypeLayout;
use sui_json_rpc_types::{
SuiObjectData, SuiObjectDataOptions, SuiObjectRef, SuiPastObjectResponse,
SuiData, SuiObjectData, SuiObjectDataOptions, SuiObjectRef, SuiParsedData,
SuiPastObjectResponse, SuiRawData,
};
use sui_types::{
base_types::{ObjectID, ObjectType, SequenceNumber},
digests::ObjectDigest,
object::Object,
object::{Data, Object},
TypeTag,
};
use tokio::join;

use crate::{context::Context, data::objects::VersionedObjectKey, error::RpcError};
use crate::{
context::Context,
data::objects::VersionedObjectKey,
error::{rpc_bail, RpcError},
};

/// Fetch the necessary data from the stores in `ctx` and transform it to build a response for a
/// past object identified by its ID and version, according to the response `options`.
Expand All @@ -38,13 +47,14 @@ pub(super) async fn past_object(
}));
};

Ok(SuiPastObjectResponse::VersionFound(object(
object_id, version, bytes, options,
)?))
Ok(SuiPastObjectResponse::VersionFound(
object(ctx, object_id, version, bytes, options).await?,
))
}

/// Extract a representation of the object from its stored form, according to its response options.
fn object(
async fn object(
ctx: &Context,
object_id: ObjectID,
version: SequenceNumber,
bytes: &[u8],
Expand All @@ -59,6 +69,26 @@ fn object(
.then(|| object.previous_transaction.clone());
let storage_rebate = options.show_storage_rebate.then(|| object.storage_rebate);

let content: OptionFuture<_> = options
.show_content
.then(|| object_data::<SuiParsedData>(ctx, &object))
.into();

let bcs: OptionFuture<_> = options
.show_bcs
.then(|| object_data::<SuiRawData>(ctx, &object))
.into();

let (content, bcs) = join!(content, bcs);

let content = content
.transpose()
.context("Failed to deserialize object content")?;

let bcs = bcs
.transpose()
.context("Failed to deserialize object to BCS")?;

Ok(SuiObjectData {
object_id,
version,
Expand All @@ -68,7 +98,37 @@ fn object(
previous_transaction,
storage_rebate,
display: None,
content: None,
bcs: None,
content,
bcs,
})
}

/// Extract the contents of an object, in a format chosen by the `D` type parameter.
/// This operaton can fail if it's not possible to get the type layout for the object's type.
async fn object_data<D: SuiData>(ctx: &Context, object: &Object) -> Result<D, RpcError> {
Ok(match object.data.clone() {
Data::Package(move_package) => D::try_from_package(move_package)?,

Data::Move(move_object) => {
let type_: TypeTag = move_object.type_().clone().into();
let MoveTypeLayout::Struct(layout) = ctx
.package_resolver()
.type_layout(type_.clone())
.await
.with_context(|| {
format!(
"Failed to resolve type layout for {}",
type_.to_canonical_display(/*with_prefix */ true)
)
})?
else {
rpc_bail!(
"Type {} is not a struct",
type_.to_canonical_display(/*with_prefix */ true)
);
};

D::try_from_object(move_object, *layout)?
}
})
}

0 comments on commit 62e5074

Please sign in to comment.