Skip to content

Commit 10662f5

Browse files
committed
grpc: add support for json rendering of Events
1 parent abace30 commit 10662f5

File tree

5 files changed

+127
-82
lines changed

5 files changed

+127
-82
lines changed

crates/sui-rpc-api/proto/sui/rpc/v2beta/event.proto

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ syntax = "proto3";
55

66
package sui.rpc.v2beta;
77

8+
import "google/protobuf/struct.proto";
89
import "sui/rpc/v2beta/bcs.proto";
910

1011
// Events emitted during the successful execution of a transaction.
@@ -37,4 +38,7 @@ message Event {
3738

3839
// BCS serialized bytes of the event.
3940
optional Bcs contents = 5;
41+
42+
// JSON rendering of the event.
43+
optional google.protobuf.Value json = 100;
4044
}

crates/sui-rpc-api/src/grpc/v2beta/ledger_service/get_transaction.rs

Lines changed: 105 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33

44
use crate::field_mask::FieldMaskTree;
55
use crate::field_mask::FieldMaskUtil;
6-
use crate::message::MessageMerge;
76
use crate::message::MessageMergeFrom;
87
use crate::proto::google::rpc::bad_request::FieldViolation;
98
use crate::proto::rpc::v2beta::BatchGetTransactionsRequest;
109
use crate::proto::rpc::v2beta::BatchGetTransactionsResponse;
10+
use crate::proto::rpc::v2beta::Event;
1111
use crate::proto::rpc::v2beta::ExecutedTransaction;
1212
use crate::proto::rpc::v2beta::GetTransactionRequest;
1313
use crate::proto::rpc::v2beta::Transaction;
@@ -21,6 +21,7 @@ use crate::RpcService;
2121
use prost_types::FieldMask;
2222
use sui_sdk_types::TransactionDigest;
2323
use sui_types::base_types::ObjectID;
24+
use sui_types::sui_sdk_types_conversions::struct_tag_sdk_to_core;
2425

2526
#[tracing::instrument(skip(service))]
2627
pub fn get_transaction(
@@ -57,7 +58,8 @@ pub fn get_transaction(
5758

5859
let transaction_read = service.reader.get_transaction_read(transaction_digest)?;
5960

60-
Ok(ExecutedTransaction::merge_from(
61+
Ok(transaction_to_response(
62+
service,
6163
transaction_read,
6264
&read_mask,
6365
))
@@ -95,100 +97,129 @@ pub fn batch_get_transactions(
9597
.reader
9698
.get_transaction_read(digest)
9799
.map(|transaction_read| {
98-
ExecutedTransaction::merge_from(transaction_read, &read_mask)
100+
transaction_to_response(service, transaction_read, &read_mask)
99101
})
100102
})
101103
.collect::<Result<_, _>>()?;
102104

103105
Ok(BatchGetTransactionsResponse { transactions })
104106
}
105107

106-
impl MessageMerge<crate::reader::TransactionRead> for ExecutedTransaction {
107-
fn merge(
108-
&mut self,
109-
source: crate::reader::TransactionRead,
110-
mask: &crate::field_mask::FieldMaskTree,
111-
) {
112-
if mask.contains(Self::DIGEST_FIELD.name) {
113-
self.digest = Some(source.digest.to_string());
114-
}
108+
fn transaction_to_response(
109+
service: &RpcService,
110+
source: crate::reader::TransactionRead,
111+
mask: &crate::field_mask::FieldMaskTree,
112+
) -> ExecutedTransaction {
113+
let mut message = ExecutedTransaction::default();
115114

116-
if let Some(submask) = mask.subtree(Self::TRANSACTION_FIELD.name) {
117-
self.transaction = Some(Transaction::merge_from(source.transaction, &submask));
118-
}
115+
if mask.contains(ExecutedTransaction::DIGEST_FIELD.name) {
116+
message.digest = Some(source.digest.to_string());
117+
}
119118

120-
if let Some(submask) = mask.subtree(Self::SIGNATURES_FIELD.name) {
121-
self.signatures = source
122-
.signatures
123-
.into_iter()
124-
.map(|s| UserSignature::merge_from(s, &submask))
125-
.collect();
126-
}
119+
if let Some(submask) = mask.subtree(ExecutedTransaction::TRANSACTION_FIELD.name) {
120+
message.transaction = Some(Transaction::merge_from(source.transaction, &submask));
121+
}
127122

128-
if let Some(submask) = mask.subtree(Self::EFFECTS_FIELD.name) {
129-
let mut effects = TransactionEffects::merge_from(&source.effects, &submask);
130-
131-
if let Some(object_types) = source.object_types {
132-
if submask.contains(TransactionEffects::CHANGED_OBJECTS_FIELD.name) {
133-
for changed_object in effects.changed_objects.iter_mut() {
134-
let Ok(object_id) = changed_object.object_id().parse::<ObjectID>() else {
135-
continue;
136-
};
137-
138-
if let Some(ty) = object_types.get(&object_id) {
139-
changed_object.object_type = Some(match ty {
140-
sui_types::base_types::ObjectType::Package => "package".to_owned(),
141-
sui_types::base_types::ObjectType::Struct(struct_tag) => {
142-
struct_tag.to_canonical_string(true)
143-
}
144-
});
145-
}
123+
if let Some(submask) = mask.subtree(ExecutedTransaction::SIGNATURES_FIELD.name) {
124+
message.signatures = source
125+
.signatures
126+
.into_iter()
127+
.map(|s| UserSignature::merge_from(s, &submask))
128+
.collect();
129+
}
130+
131+
if let Some(submask) = mask.subtree(ExecutedTransaction::EFFECTS_FIELD.name) {
132+
let mut effects = TransactionEffects::merge_from(&source.effects, &submask);
133+
134+
if let Some(object_types) = source.object_types {
135+
if submask.contains(TransactionEffects::CHANGED_OBJECTS_FIELD.name) {
136+
for changed_object in effects.changed_objects.iter_mut() {
137+
let Ok(object_id) = changed_object.object_id().parse::<ObjectID>() else {
138+
continue;
139+
};
140+
141+
if let Some(ty) = object_types.get(&object_id) {
142+
changed_object.object_type = Some(match ty {
143+
sui_types::base_types::ObjectType::Package => "package".to_owned(),
144+
sui_types::base_types::ObjectType::Struct(struct_tag) => {
145+
struct_tag.to_canonical_string(true)
146+
}
147+
});
146148
}
147149
}
150+
}
148151

149-
if submask.contains(TransactionEffects::UNCHANGED_SHARED_OBJECTS_FIELD.name) {
150-
for unchanged_shared_object in effects.unchanged_shared_objects.iter_mut() {
151-
let Ok(object_id) = unchanged_shared_object.object_id().parse::<ObjectID>()
152-
else {
153-
continue;
154-
};
155-
156-
if let Some(ty) = object_types.get(&object_id) {
157-
unchanged_shared_object.object_type = Some(match ty {
158-
sui_types::base_types::ObjectType::Package => "package".to_owned(),
159-
sui_types::base_types::ObjectType::Struct(struct_tag) => {
160-
struct_tag.to_canonical_string(true)
161-
}
162-
});
163-
}
152+
if submask.contains(TransactionEffects::UNCHANGED_SHARED_OBJECTS_FIELD.name) {
153+
for unchanged_shared_object in effects.unchanged_shared_objects.iter_mut() {
154+
let Ok(object_id) = unchanged_shared_object.object_id().parse::<ObjectID>()
155+
else {
156+
continue;
157+
};
158+
159+
if let Some(ty) = object_types.get(&object_id) {
160+
unchanged_shared_object.object_type = Some(match ty {
161+
sui_types::base_types::ObjectType::Package => "package".to_owned(),
162+
sui_types::base_types::ObjectType::Struct(struct_tag) => {
163+
struct_tag.to_canonical_string(true)
164+
}
165+
});
164166
}
165167
}
166168
}
167-
168-
self.effects = Some(effects);
169169
}
170170

171-
if let Some(submask) = mask.subtree(Self::EVENTS_FIELD.name) {
172-
self.events = source
173-
.events
174-
.map(|events| TransactionEvents::merge_from(events, &submask));
175-
}
171+
message.effects = Some(effects);
172+
}
176173

177-
if mask.contains(Self::CHECKPOINT_FIELD.name) {
178-
self.checkpoint = source.checkpoint;
179-
}
174+
if let Some(submask) = mask.subtree(ExecutedTransaction::EVENTS_FIELD.name) {
175+
message.events = source.events.map(|events| {
176+
let mut message = TransactionEvents::merge_from(events.clone(), &submask);
177+
178+
if let Some(event_mask) = submask.subtree(TransactionEvents::EVENTS_FIELD.name) {
179+
if event_mask.contains(Event::JSON_FIELD.name) {
180+
for (message, event) in message.events.iter_mut().zip(&events.0) {
181+
message.json = struct_tag_sdk_to_core(event.type_.clone())
182+
.ok()
183+
.and_then(|struct_tag| {
184+
let layout = service
185+
.reader
186+
.inner()
187+
.get_struct_layout(&struct_tag)
188+
.ok()
189+
.flatten()?;
190+
Some((layout, &event.contents))
191+
})
192+
.and_then(|(layout, contents)| {
193+
sui_types::proto_value::ProtoVisitor::default()
194+
.deserialize_value(contents, &layout)
195+
.map_err(|e| tracing::debug!("unable to convert to JSON: {e}"))
196+
.ok()
197+
.map(Box::new)
198+
});
199+
}
200+
}
201+
}
180202

181-
if mask.contains(Self::TIMESTAMP_FIELD.name) {
182-
self.timestamp = source.timestamp_ms.map(timestamp_ms_to_proto);
183-
}
203+
message
204+
});
205+
}
184206

185-
if mask.contains(Self::BALANCE_CHANGES_FIELD.name) {
186-
self.balance_changes = source
187-
.balance_changes
188-
.map(|balance_changes| balance_changes.into_iter().map(Into::into).collect())
189-
.unwrap_or_default();
190-
}
207+
if mask.contains(ExecutedTransaction::CHECKPOINT_FIELD.name) {
208+
message.checkpoint = source.checkpoint;
209+
}
210+
211+
if mask.contains(ExecutedTransaction::TIMESTAMP_FIELD.name) {
212+
message.timestamp = source.timestamp_ms.map(timestamp_ms_to_proto);
191213
}
214+
215+
if mask.contains(ExecutedTransaction::BALANCE_CHANGES_FIELD.name) {
216+
message.balance_changes = source
217+
.balance_changes
218+
.map(|balance_changes| balance_changes.into_iter().map(Into::into).collect())
219+
.unwrap_or_default();
220+
}
221+
222+
message
192223
}
193224

194225
impl From<sui_types::balance_change::BalanceChange> for crate::proto::rpc::v2beta::BalanceChange {
Binary file not shown.

crates/sui-rpc-api/src/proto/generated/sui.rpc.v2beta.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,9 @@ pub struct Event {
619619
/// BCS serialized bytes of the event.
620620
#[prost(message, optional, tag = "5")]
621621
pub contents: ::core::option::Option<Bcs>,
622+
/// JSON rendering of the event.
623+
#[prost(message, optional, boxed, tag = "100")]
624+
pub json: ::core::option::Option<::prost::alloc::boxed::Box<::prost_types::Value>>,
622625
}
623626
#[derive(Clone, PartialEq, ::prost::Message)]
624627
pub struct ExecutedTransaction {

crates/sui-rpc-api/src/proto/rpc/v2beta/events.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ use crate::proto::TryFromProtoError;
1313
//
1414

1515
impl Event {
16-
const PACKAGE_ID_FIELD: &'static MessageField = &MessageField::new("package_id");
17-
const MODULE_FIELD: &'static MessageField = &MessageField::new("module");
18-
const SENDER_FIELD: &'static MessageField = &MessageField::new("sender");
19-
const EVENT_TYPE_FIELD: &'static MessageField = &MessageField::new("event_type");
20-
const CONTENTS_FIELD: &'static MessageField = &MessageField::new("contents");
16+
pub const PACKAGE_ID_FIELD: &'static MessageField = &MessageField::new("package_id");
17+
pub const MODULE_FIELD: &'static MessageField = &MessageField::new("module");
18+
pub const SENDER_FIELD: &'static MessageField = &MessageField::new("sender");
19+
pub const EVENT_TYPE_FIELD: &'static MessageField = &MessageField::new("event_type");
20+
pub const CONTENTS_FIELD: &'static MessageField = &MessageField::new("contents");
21+
pub const JSON_FIELD: &'static MessageField = &MessageField::new("json");
2122
}
2223

2324
impl MessageFields for Event {
@@ -27,6 +28,7 @@ impl MessageFields for Event {
2728
Self::SENDER_FIELD,
2829
Self::EVENT_TYPE_FIELD,
2930
Self::CONTENTS_FIELD,
31+
Self::JSON_FIELD,
3032
];
3133
}
3234

@@ -73,6 +75,7 @@ impl MessageMerge<&Event> for Event {
7375
sender,
7476
event_type,
7577
contents,
78+
json,
7679
} = source;
7780

7881
if mask.contains(Self::PACKAGE_ID_FIELD.name) {
@@ -94,6 +97,10 @@ impl MessageMerge<&Event> for Event {
9497
if mask.contains(Self::CONTENTS_FIELD.name) {
9598
self.contents = contents.clone();
9699
}
100+
101+
if mask.contains(Self::JSON_FIELD.name) {
102+
self.json = json.clone();
103+
}
97104
}
98105
}
99106

@@ -151,10 +158,10 @@ impl TryFrom<&Event> for sui_sdk_types::Event {
151158
//
152159

153160
impl TransactionEvents {
154-
const BCS_FIELD: &'static MessageField =
161+
pub const BCS_FIELD: &'static MessageField =
155162
&MessageField::new("bcs").with_message_fields(super::Bcs::FIELDS);
156-
const DIGEST_FIELD: &'static MessageField = &MessageField::new("digest");
157-
const EVENTS_FIELD: &'static MessageField =
163+
pub const DIGEST_FIELD: &'static MessageField = &MessageField::new("digest");
164+
pub const EVENTS_FIELD: &'static MessageField =
158165
&MessageField::new("events").with_message_fields(Event::FIELDS);
159166
}
160167

0 commit comments

Comments
 (0)