Skip to content

Commit 7ae4447

Browse files
authored
[graphql-alt] Load just-published packages within Object::with_address (#23928)
## Description Currently, package queries that stem from simulated transactions produce null outputs because they do not currently pay attention to the new objects produced by the transaction. Support for reading from these new objects has been added to the Object load paths, but although MovePackages are objects, they are typically fetched from the kv_packages table, which also needs to consult the transaction's output objects. This PR fixes that problem by adding a logic to `MovePackage::with_address` to load the latest object version for a given address. ## Test plan How did you test the new or updated feature? ``` cargo nextest run -p sui-indexer-alt-e2e-tests cargo nextest run -p sui-indexer-alt-framework ``` --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] gRPC: - [ ] JSON-RPC: - [x] GraphQL: Removed `events` field from `SimulationResult`. Events are now only accessible via `effects.events()` to eliminate redundancy. - [ ] CLI: - [ ] Rust SDK:
1 parent ad898db commit 7ae4447

15 files changed

+194
-185
lines changed

crates/sui-indexer-alt-e2e-tests/tests/graphql_simulate_transaction_tests.rs

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ enum ArgumentKind {
8686
#[serde(rename_all = "camelCase")]
8787
struct SimulationResult {
8888
effects: Option<TransactionEffects>,
89-
events: Option<Events>,
9089
outputs: Option<Vec<CommandResult>>,
9190
error: Option<String>,
9291
}
@@ -118,16 +117,6 @@ struct GasInput {
118117
gas_budget: String,
119118
}
120119

121-
// Events is now a Vec<EventNode> directly, not wrapped in nodes
122-
type Events = Vec<EventNode>;
123-
124-
#[derive(Debug, Deserialize)]
125-
struct EventNode {
126-
#[serde(rename = "eventBcs")]
127-
event_bcs: String,
128-
sender: Sender,
129-
}
130-
131120
#[derive(Debug, Deserialize)]
132121
#[serde(rename_all = "camelCase")]
133122
struct ObjectChangeNode {
@@ -348,12 +337,25 @@ async fn test_simulate_transaction_with_events() {
348337
query($txData: JSON!) {
349338
simulateTransaction(transaction: $txData) {
350339
effects {
351-
digest
352340
status
353-
}
354-
events {
355-
eventBcs
356-
sender { address }
341+
events {
342+
nodes {
343+
contents {
344+
json
345+
}
346+
transactionModule {
347+
package {
348+
version
349+
modules {
350+
nodes {
351+
name
352+
}
353+
}
354+
}
355+
name
356+
}
357+
}
358+
}
357359
}
358360
error
359361
}
@@ -370,19 +372,40 @@ async fn test_simulate_transaction_with_events() {
370372
.await
371373
.expect("GraphQL request failed");
372374

373-
let simulation_result: SimulationResult =
374-
serde_json::from_value(result.pointer("/data/simulateTransaction").unwrap().clone())
375-
.unwrap();
376-
377-
// Verify events were simulated
378-
let events = simulation_result.events.unwrap();
379-
assert!(!events.is_empty());
380-
381-
let sender_address = validator_cluster.get_address_0();
382-
for event_node in &events {
383-
assert!(!event_node.event_bcs.is_empty());
384-
assert_eq!(event_node.sender.address, sender_address.to_string());
375+
// Verify package version and digest are populated correctly from execution context
376+
insta::assert_json_snapshot!(result.pointer("/data/simulateTransaction"), @r#"
377+
{
378+
"effects": {
379+
"status": "SUCCESS",
380+
"events": {
381+
"nodes": [
382+
{
383+
"contents": {
384+
"json": {
385+
"message": "Package published successfully!",
386+
"value": "42"
387+
}
388+
},
389+
"transactionModule": {
390+
"package": {
391+
"version": 1,
392+
"modules": {
393+
"nodes": [
394+
{
395+
"name": "emit_event"
396+
}
397+
]
398+
}
399+
},
400+
"name": "emit_event"
401+
}
402+
}
403+
]
404+
}
405+
},
406+
"error": null
385407
}
408+
"#);
386409

387410
graphql_cluster.stopped().await;
388411
}

crates/sui-indexer-alt-framework/src/ingestion/rpc_client.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ use crate::ingestion::ingestion_client::{
1717
#[async_trait]
1818
impl IngestionClientTrait for RpcClient {
1919
async fn fetch(&self, checkpoint: u64) -> FetchResult {
20-
let request = GetCheckpointRequest::by_sequence_number(checkpoint).with_read_mask(
21-
FieldMask::from_paths([
20+
let request: GetCheckpointRequest = GetCheckpointRequest::by_sequence_number(checkpoint)
21+
.with_read_mask(FieldMask::from_paths([
2222
"summary.bcs",
2323
"signature",
2424
"contents.bcs",
@@ -27,8 +27,7 @@ impl IngestionClientTrait for RpcClient {
2727
"transactions.effects.unchanged_loaded_runtime_objects",
2828
"transactions.events.bcs",
2929
"objects.objects.bcs",
30-
]),
31-
);
30+
]));
3231

3332
let response = self
3433
.clone()

crates/sui-indexer-alt-graphql/schema.graphql

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3544,7 +3544,7 @@ type SharedInput {
35443544
}
35453545

35463546
"""
3547-
The result of simulating a transaction, including the predicted effects, events, and any errors.
3547+
The result of simulating a transaction, including the predicted effects and any errors.
35483548
"""
35493549
type SimulationResult {
35503550
"""
@@ -3560,12 +3560,6 @@ type SimulationResult {
35603560
"""
35613561
error: String
35623562
"""
3563-
The events that would be emitted if the transaction were executed.
3564-
3565-
`None` if the simulation failed or no events would be emitted.
3566-
"""
3567-
events: [Event!]
3568-
"""
35693563
The intermediate outputs for each command of the transaction simulation, including contents of mutated references and return values.
35703564
"""
35713565
outputs: [CommandResult!]

crates/sui-indexer-alt-graphql/src/api/mutation.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,17 @@ impl Mutation {
7777
.await
7878
{
7979
Ok(response) => {
80-
let scope = Scope::new(ctx)?;
81-
let effects = TransactionEffects::from_execution_response(
80+
let executed_transaction = response
81+
.transaction
82+
.as_ref()
83+
.ok_or_else(|| anyhow!("ExecuteTransactionResponse should have transaction"))?;
84+
85+
let scope = Scope::new(ctx)?
86+
.with_executed_transaction(executed_transaction)
87+
.map_err(crate::error::upcast)?;
88+
let effects = TransactionEffects::from_executed_transaction(
8289
scope,
83-
response,
90+
executed_transaction,
8491
tx_data,
8592
parsed_signatures,
8693
)

crates/sui-indexer-alt-graphql/src/api/query.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,6 @@ impl Query {
600600
}
601601
Err(GrpcExecutionError(status)) => Ok(SimulationResult {
602602
effects: None,
603-
events: None,
604603
outputs: None,
605604
error: Some(status.to_string()),
606605
}),

crates/sui-indexer-alt-graphql/src/api/types/move_package.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,6 @@ impl MovePackage {
522522
/// user input. When the package's contents are fetched from the latest version of that object
523523
/// as of the current checkpoint.
524524
pub(crate) fn with_address(scope: Scope, address: NativeSuiAddress) -> Self {
525-
// TODO: Look for the package in the scope (just-published packages).
526525
let super_ = Object::with_address(scope, address);
527526
Self {
528527
super_,

crates/sui-indexer-alt-graphql/src/api/types/object.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -627,11 +627,23 @@ impl Object {
627627
}
628628
}
629629

630-
/// Construct an object that is represented by just its address. This does not check that the
631-
/// object exists, so should not be used to "fetch" an address provided as user input. When the
632-
/// object's contents are fetched from the latest version of that object as of the current
633-
/// checkpoint.
630+
/// Construct an object that is represented by just its address.
631+
///
632+
/// This function first checks the execution context for freshly created/modified objects.
633+
/// If found there, returns an Object with its contents already loaded. Otherwise, returns
634+
/// an Object that will lazy-load its contents from the database when accessed.
635+
///
636+
/// This does not verify that the object exists in the database (for the lazy-loading case),
637+
/// so should not be used to "fetch" an address provided as user input. Contents from the
638+
/// database are fetched from the latest version of that object available in the database,
639+
/// which may not be as current as the checkpoint specified in scope.
634640
pub(crate) fn with_address(scope: Scope, address: NativeSuiAddress) -> Self {
641+
// Check execution context first for freshly created/modified objects
642+
if let Some(native_obj) = scope.execution_output_object_latest(address.into()) {
643+
return Self::from_contents(scope.clone(), native_obj.clone());
644+
}
645+
646+
// Fallback to lazy loading from database
635647
Self {
636648
super_: Address::with_address(scope, address),
637649
version_digest: None,

crates/sui-indexer-alt-graphql/src/api/types/simulation_result.rs

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,16 @@ use sui_types::transaction::TransactionData;
88

99
use crate::{error::RpcError, scope::Scope};
1010

11-
use super::{command_result::CommandResult, event::Event, transaction_effects::TransactionEffects};
11+
use super::{command_result::CommandResult, transaction_effects::TransactionEffects};
1212

13-
/// The result of simulating a transaction, including the predicted effects, events, and any errors.
13+
/// The result of simulating a transaction, including the predicted effects and any errors.
1414
#[derive(Clone, SimpleObject)]
1515
pub struct SimulationResult {
1616
/// The predicted effects of the transaction if it were executed.
1717
///
1818
/// `None` if the simulation failed due to an error.
1919
pub effects: Option<TransactionEffects>,
2020

21-
/// The events that would be emitted if the transaction were executed.
22-
///
23-
/// `None` if the simulation failed or no events would be emitted.
24-
pub events: Option<Vec<Event>>,
25-
2621
/// The intermediate outputs for each command of the transaction simulation, including contents of mutated references and return values.
2722
pub outputs: Option<Vec<CommandResult>>,
2823

@@ -39,42 +34,20 @@ impl SimulationResult {
3934
response: proto::SimulateTransactionResponse,
4035
transaction_data: TransactionData,
4136
) -> Result<Self, RpcError> {
42-
let effects = Some(TransactionEffects::from_simulation_response(
43-
scope.clone(),
44-
response.clone(),
45-
transaction_data.clone(),
46-
)?);
47-
48-
// Parse events - break into clear steps
4937
let executed_transaction = response
5038
.transaction
5139
.as_ref()
52-
.context("No transaction in simulation response")?;
40+
.context("SimulateTransactionResponse should have transaction")?;
5341

54-
let events_bcs = executed_transaction
55-
.events
56-
.as_ref()
57-
.and_then(|events| events.bcs.as_ref());
58-
59-
let transaction_events = events_bcs
60-
.map(|bcs| bcs.deserialize())
61-
.transpose()
62-
.context("Failed to deserialize events BCS")?;
42+
// Create scope with execution objects
43+
let scope = scope.with_executed_transaction(executed_transaction)?;
6344

64-
let events = transaction_events.map(|events: sui_types::effects::TransactionEvents| {
65-
events
66-
.data
67-
.into_iter()
68-
.enumerate()
69-
.map(|(sequence, native_event)| Event {
70-
scope: scope.clone(),
71-
native: native_event,
72-
transaction_digest: transaction_data.digest(),
73-
sequence_number: sequence as u64,
74-
timestamp_ms: 0, // No timestamp for simulation
75-
})
76-
.collect()
77-
});
45+
let effects = TransactionEffects::from_executed_transaction(
46+
scope.clone(),
47+
executed_transaction,
48+
transaction_data.clone(),
49+
vec![], // No signatures for simulated transactions
50+
)?;
7851

7952
// Extract command results from the response
8053
let outputs = Some(
@@ -86,8 +59,7 @@ impl SimulationResult {
8659
);
8760

8861
Ok(Self {
89-
effects,
90-
events,
62+
effects: Some(effects),
9163
outputs,
9264
error: None,
9365
})

crates/sui-indexer-alt-graphql/src/api/types/snapshots/sui_indexer_alt_graphql__api__types__available_range__field_piplines_tests__registry_collect_pipelines_snapshot.snap

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,9 +1433,6 @@ SharedInput.mutable
14331433
SimulationResult.effects
14341434
=> {}
14351435

1436-
SimulationResult.events
1437-
=> {}
1438-
14391436
SimulationResult.outputs
14401437
=> {}
14411438

crates/sui-indexer-alt-graphql/src/api/types/transaction_effects.rs

Lines changed: 1 addition & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use sui_types::{
1717
digests::TransactionDigest,
1818
effects::TransactionEffectsAPI,
1919
execution_status::ExecutionStatus as NativeExecutionStatus,
20-
object::Object as NativeObject,
2120
signature::GenericSignature,
2221
transaction::{TransactionData, TransactionDataAPI},
2322
};
@@ -362,7 +361,7 @@ impl EffectsContents {
362361

363362
impl TransactionEffects {
364363
/// Create a new TransactionEffects from an ExecutedTransaction.
365-
fn from_executed_transaction(
364+
pub(crate) fn from_executed_transaction(
366365
scope: Scope,
367366
executed_transaction: &ExecutedTransaction,
368367
transaction_data: TransactionData,
@@ -375,11 +374,9 @@ impl TransactionEffects {
375374
)
376375
.context("Failed to create TransactionContents from ExecutedTransaction")?;
377376

378-
let objects = extract_objects_from_executed_transaction(executed_transaction);
379377
let digest = contents
380378
.digest()
381379
.context("Failed to get digest from transaction contents")?;
382-
let scope = scope.with_execution_output(objects);
383380

384381
Ok(Self {
385382
digest,
@@ -390,35 +387,6 @@ impl TransactionEffects {
390387
})
391388
}
392389

393-
/// Create a new TransactionEffects from a gRPC ExecuteTransactionResponse.
394-
pub(crate) fn from_execution_response(
395-
scope: Scope,
396-
response: sui_rpc::proto::sui::rpc::v2::ExecuteTransactionResponse,
397-
transaction_data: TransactionData,
398-
signatures: Vec<GenericSignature>,
399-
) -> Result<Self, RpcError> {
400-
let executed_transaction = response
401-
.transaction
402-
.as_ref()
403-
.context("ExecuteTransactionResponse should have transaction")?;
404-
405-
Self::from_executed_transaction(scope, executed_transaction, transaction_data, signatures)
406-
}
407-
408-
/// Create a new TransactionEffects from a gRPC SimulateTransactionResponse.
409-
pub(crate) fn from_simulation_response(
410-
scope: Scope,
411-
response: sui_rpc::proto::sui::rpc::v2::SimulateTransactionResponse,
412-
transaction_data: TransactionData,
413-
) -> Result<Self, RpcError> {
414-
let executed_transaction = response
415-
.transaction
416-
.as_ref()
417-
.context("SimulateTransactionResponse should have transaction")?;
418-
419-
Self::from_executed_transaction(scope, executed_transaction, transaction_data, vec![])
420-
}
421-
422390
/// Load the effects from the store, and return it fully inflated (with contents already
423391
/// fetched). Returns `None` if the effects do not exist (either never existed or were pruned
424392
/// from the store).
@@ -499,16 +467,3 @@ impl From<Transaction> for TransactionEffects {
499467
}
500468
}
501469
}
502-
503-
/// Extract input and output object contents from an ExecutedTransaction.
504-
fn extract_objects_from_executed_transaction(
505-
executed_transaction: &ExecutedTransaction,
506-
) -> Vec<NativeObject> {
507-
executed_transaction
508-
.objects()
509-
.objects()
510-
.iter()
511-
.filter_map(|obj| obj.bcs.as_ref())
512-
.map(|bcs| bcs.deserialize().expect("Object BCS should be valid"))
513-
.collect()
514-
}

0 commit comments

Comments
 (0)