Skip to content

Commit ee1280f

Browse files
committed
Merge AccumulatorEvents in temporary_store so that transaction effects are accurate
1 parent 838a57c commit ee1280f

File tree

13 files changed

+709
-59
lines changed

13 files changed

+709
-59
lines changed

crates/sui-core/src/accumulators/mod.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ impl MergedValueIntermediate {
162162
match value {
163163
AccumulatorValue::Integer(_) => Self::SumU128(0),
164164
AccumulatorValue::IntegerTuple(_, _) => Self::SumU128U128(0, 0),
165-
AccumulatorValue::EventDigest(_, _) => Self::Events(vec![]),
165+
AccumulatorValue::EventDigest(_) => Self::Events(vec![]),
166166
}
167167
}
168168

@@ -178,13 +178,15 @@ impl MergedValueIntermediate {
178178
*v1 += w1 as u128;
179179
*v2 += w2 as u128;
180180
}
181-
(Self::Events(commitments), AccumulatorValue::EventDigest(event_idx, digest)) => {
182-
commitments.push(EventCommitment::new(
183-
checkpoint_seq,
184-
transaction_idx,
185-
event_idx,
186-
digest,
187-
));
181+
(Self::Events(commitments), AccumulatorValue::EventDigest(event_digests)) => {
182+
for (event_idx, digest) in event_digests {
183+
commitments.push(EventCommitment::new(
184+
checkpoint_seq,
185+
transaction_idx,
186+
event_idx,
187+
digest,
188+
));
189+
}
188190
}
189191
_ => {
190192
fatal!("invalid merge");

crates/sui-core/src/rpc_index.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -702,15 +702,17 @@ impl IndexStoreTables {
702702
for acc in acc_events {
703703
if let Some(stream_id) =
704704
sui_types::accumulator_root::stream_id_from_accumulator_event(&acc)
705-
&& let AccumulatorValue::EventDigest(idx, _d) = acc.write.value
705+
&& let AccumulatorValue::EventDigest(event_digests) = &acc.write.value
706706
{
707-
let key = EventIndexKey {
708-
stream_id,
709-
checkpoint_seq,
710-
transaction_idx: tx_idx,
711-
event_index: idx as u32,
712-
};
713-
entries.push((key, ()));
707+
for (idx, _d) in event_digests {
708+
let key = EventIndexKey {
709+
stream_id,
710+
checkpoint_seq,
711+
transaction_idx: tx_idx,
712+
event_index: *idx as u32,
713+
};
714+
entries.push((key, ()));
715+
}
714716
}
715717
}
716718

crates/sui-core/tests/staged/sui.yaml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@ AccumulatorValue:
2828
- U64
2929
2:
3030
EventDigest:
31-
TUPLE:
32-
- U64
33-
- TYPENAME: Digest
31+
NEWTYPE:
32+
SEQ:
33+
TUPLE:
34+
- U64
35+
- TYPENAME: Digest
3436
AccumulatorWriteV1:
3537
STRUCT:
3638
- address:

crates/sui-e2e-tests/tests/address_balance_tests.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2301,3 +2301,143 @@ async fn test_soft_bundle_different_gas_payers() {
23012301

23022302
test_cluster.trigger_reconfiguration().await;
23032303
}
2304+
2305+
#[sim_test]
2306+
async fn test_multiple_deposits_merged_in_effects() {
2307+
let _guard = ProtocolConfig::apply_overrides_for_testing(|_, mut cfg| {
2308+
cfg.create_root_accumulator_object_for_testing();
2309+
cfg.enable_accumulators_for_testing();
2310+
cfg
2311+
});
2312+
2313+
let mut test_cluster = TestClusterBuilder::new().build().await;
2314+
let rgp = test_cluster.get_reference_gas_price().await;
2315+
let context = &mut test_cluster.wallet;
2316+
2317+
let (sender, gas) = get_sender_and_gas(context).await;
2318+
let recipient = sender;
2319+
2320+
let initial_deposit = make_send_to_account_tx(10000, recipient, sender, gas, rgp);
2321+
let res = test_cluster
2322+
.sign_and_execute_transaction(&initial_deposit)
2323+
.await;
2324+
let gas = res.effects.unwrap().gas_object().reference.to_object_ref();
2325+
2326+
test_cluster.fullnode_handle.sui_node.with(|node| {
2327+
let state = node.state();
2328+
let child_object_resolver = state.get_child_object_resolver().as_ref();
2329+
verify_accumulator_exists(child_object_resolver, recipient, 10000);
2330+
});
2331+
2332+
let mut builder = ProgrammableTransactionBuilder::new();
2333+
2334+
let deposit_amounts = vec![1000u64, 2000u64, 3000u64];
2335+
for amount in &deposit_amounts {
2336+
let amount_arg = builder.pure(*amount).unwrap();
2337+
let recipient_arg = builder.pure(recipient).unwrap();
2338+
let coin = builder.command(Command::SplitCoins(Argument::GasCoin, vec![amount_arg]));
2339+
2340+
let Argument::Result(coin_idx) = coin else {
2341+
panic!("coin is not a result");
2342+
};
2343+
2344+
let coin = Argument::NestedResult(coin_idx, 0);
2345+
2346+
builder.programmable_move_call(
2347+
SUI_FRAMEWORK_PACKAGE_ID,
2348+
Identifier::new("coin").unwrap(),
2349+
Identifier::new("send_funds").unwrap(),
2350+
vec!["0x2::sui::SUI".parse().unwrap()],
2351+
vec![coin, recipient_arg],
2352+
);
2353+
}
2354+
2355+
let withdraw_amounts = vec![500u64, 1500u64];
2356+
for amount in &withdraw_amounts {
2357+
let withdraw_arg = sui_types::transaction::FundsWithdrawalArg::balance_from_sender(
2358+
*amount,
2359+
sui_types::type_input::TypeInput::from(sui_types::gas_coin::GAS::type_tag()),
2360+
);
2361+
let withdraw_arg = builder.funds_withdrawal(withdraw_arg).unwrap();
2362+
2363+
let amount_arg = builder
2364+
.pure(move_core_types::u256::U256::from(*amount))
2365+
.unwrap();
2366+
2367+
let split_withdraw_arg = builder.programmable_move_call(
2368+
SUI_FRAMEWORK_PACKAGE_ID,
2369+
Identifier::new("funds_accumulator").unwrap(),
2370+
Identifier::new("withdrawal_split").unwrap(),
2371+
vec!["0x2::balance::Balance<0x2::sui::SUI>".parse().unwrap()],
2372+
vec![withdraw_arg, amount_arg],
2373+
);
2374+
2375+
let coin = builder.programmable_move_call(
2376+
SUI_FRAMEWORK_PACKAGE_ID,
2377+
Identifier::new("coin").unwrap(),
2378+
Identifier::new("redeem_funds").unwrap(),
2379+
vec!["0x2::sui::SUI".parse().unwrap()],
2380+
vec![split_withdraw_arg],
2381+
);
2382+
2383+
builder.transfer_arg(sender, coin);
2384+
}
2385+
2386+
let tx = TransactionKind::ProgrammableTransaction(builder.finish());
2387+
let tx_data = TransactionData::new(tx, sender, gas, 10000000, rgp);
2388+
2389+
let signed_tx = test_cluster.wallet.sign_transaction(&tx_data).await;
2390+
let (effects, _) = test_cluster
2391+
.execute_transaction_return_raw_effects(signed_tx)
2392+
.await
2393+
.unwrap();
2394+
2395+
assert!(
2396+
effects.status().is_ok(),
2397+
"Transaction should succeed, got: {:?}",
2398+
effects.status()
2399+
);
2400+
2401+
let acc_events = effects.accumulator_events();
2402+
assert_eq!(
2403+
acc_events.len(),
2404+
1,
2405+
"Should have exactly 1 accumulator event (merged), got: {}",
2406+
acc_events.len()
2407+
);
2408+
2409+
let event = &acc_events[0];
2410+
assert_eq!(
2411+
event.write.address.address, recipient,
2412+
"Accumulator event should be for the recipient"
2413+
);
2414+
2415+
let deposit_total: u64 = deposit_amounts.iter().sum();
2416+
let withdraw_total: u64 = withdraw_amounts.iter().sum();
2417+
let expected_net = deposit_total - withdraw_total;
2418+
2419+
match &event.write.value {
2420+
sui_types::effects::AccumulatorValue::Integer(value) => {
2421+
assert_eq!(
2422+
*value, expected_net,
2423+
"Merged accumulator value should be {} (deposits {} - withdrawals {}), got {}",
2424+
expected_net, deposit_total, withdraw_total, value
2425+
);
2426+
}
2427+
_ => panic!("Expected Integer accumulator value"),
2428+
}
2429+
2430+
match &event.write.operation {
2431+
sui_types::effects::AccumulatorOperation::Merge => {}
2432+
_ => panic!("Expected Merge operation since deposits > withdrawals"),
2433+
}
2434+
2435+
let expected_final_balance = 10000 + expected_net;
2436+
test_cluster.fullnode_handle.sui_node.with(|node| {
2437+
let state = node.state();
2438+
let child_object_resolver = state.get_child_object_resolver().as_ref();
2439+
verify_accumulator_exists(child_object_resolver, recipient, expected_final_balance);
2440+
});
2441+
2442+
test_cluster.trigger_reconfiguration().await;
2443+
}

crates/sui-e2e-tests/tests/authenticated_events_api_tests.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,38 @@ async fn emit_test_event(
100100
test_cluster.sign_and_execute_transaction(&tx_data).await;
101101
}
102102

103+
async fn emit_multiple_test_events(
104+
test_cluster: &TestCluster,
105+
package_id: ObjectID,
106+
sender: SuiAddress,
107+
values: Vec<u64>,
108+
) -> sui_json_rpc_types::SuiTransactionBlockResponse {
109+
let rgp = test_cluster.get_reference_gas_price().await;
110+
let mut ptb = ProgrammableTransactionBuilder::new();
111+
let vals = ptb.pure(values).unwrap();
112+
ptb.programmable_move_call(
113+
package_id,
114+
move_core_types::identifier::Identifier::new("events").unwrap(),
115+
move_core_types::identifier::Identifier::new("emit_multiple").unwrap(),
116+
vec![],
117+
vec![vals],
118+
);
119+
let gas_object = test_cluster
120+
.wallet
121+
.get_one_gas_object_owned_by_address(sender)
122+
.await
123+
.unwrap()
124+
.unwrap();
125+
let tx_data = TransactionData::new(
126+
sui_types::transaction::TransactionKind::ProgrammableTransaction(ptb.finish()),
127+
sender,
128+
gas_object,
129+
50_000_000_000,
130+
rgp,
131+
);
132+
test_cluster.sign_and_execute_transaction(&tx_data).await
133+
}
134+
103135
async fn query_authenticated_events(
104136
rpc_url: &str,
105137
stream_id: &str,
@@ -810,3 +842,83 @@ async fn authenticated_events_backfill_test() {
810842
"expected 5 authenticated events after backfill, got {count}"
811843
);
812844
}
845+
846+
#[sim_test]
847+
async fn authenticated_events_multiple_events_per_transaction() {
848+
let _guard: sui_protocol_config::OverrideGuard =
849+
ProtocolConfig::apply_overrides_for_testing(|_, mut cfg| {
850+
cfg.enable_authenticated_event_streams_for_testing();
851+
cfg
852+
});
853+
854+
let rpc_config = create_rpc_config_with_authenticated_events();
855+
856+
let test_cluster = TestClusterBuilder::new()
857+
.disable_fullnode_pruning()
858+
.with_rpc_config(rpc_config)
859+
.build()
860+
.await;
861+
862+
let package_id = publish_test_package(&test_cluster).await;
863+
let sender = test_cluster.wallet.config.keystore.addresses()[0];
864+
865+
let _response =
866+
emit_multiple_test_events(&test_cluster, package_id, sender, vec![100, 200, 300]).await;
867+
868+
let mut event_client = EventServiceClient::connect(test_cluster.rpc_url().to_owned())
869+
.await
870+
.unwrap();
871+
872+
let mut req = ListAuthenticatedEventsRequest::default();
873+
req.stream_id = Some(package_id.to_string());
874+
req.start_checkpoint = Some(0);
875+
req.page_size = None;
876+
req.page_token = None;
877+
let response = event_client
878+
.list_authenticated_events(req)
879+
.await
880+
.unwrap()
881+
.into_inner();
882+
883+
let count = response.events.len();
884+
assert_eq!(
885+
count, 3,
886+
"expected 3 authenticated events (all from one transaction), got {count}"
887+
);
888+
889+
#[derive(serde::Deserialize)]
890+
struct E {
891+
value: u64,
892+
}
893+
894+
let values: Vec<u64> = response
895+
.events
896+
.iter()
897+
.filter_map(|event| {
898+
event.event.as_ref().and_then(|e| {
899+
e.contents.as_ref().and_then(|c| {
900+
c.value
901+
.as_ref()
902+
.and_then(|v| bcs::from_bytes::<E>(v).ok().map(|e| e.value))
903+
})
904+
})
905+
})
906+
.collect();
907+
908+
assert_eq!(values.len(), 3, "should extract 3 event values");
909+
assert!(values.contains(&100), "should contain event with value 100");
910+
assert!(values.contains(&200), "should contain event with value 200");
911+
assert!(values.contains(&300), "should contain event with value 300");
912+
913+
let tx_indices: std::collections::HashSet<u32> = response
914+
.events
915+
.iter()
916+
.filter_map(|event| event.transaction_idx)
917+
.collect();
918+
919+
assert_eq!(
920+
tx_indices.len(),
921+
1,
922+
"all events should be from the same transaction"
923+
);
924+
}

crates/sui-e2e-tests/tests/authenticated_events_end_to_end_test.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,12 @@ fn build_event_commitments_from_checkpoint(
101101

102102
let accumulator_events = effects.accumulator_events();
103103
for acc_event in accumulator_events.iter() {
104-
if let AccumulatorValue::EventDigest(event_idx, digest) = &acc_event.write.value {
105-
let event_commitment =
106-
EventCommitment::new(checkpoint_seq, idx as u64, *event_idx, *digest);
107-
event_commitments.push(event_commitment);
104+
if let AccumulatorValue::EventDigest(event_digests) = &acc_event.write.value {
105+
for (event_idx, digest) in event_digests {
106+
let event_commitment =
107+
EventCommitment::new(checkpoint_seq, idx as u64, *event_idx, *digest);
108+
event_commitments.push(event_commitment);
109+
}
108110
}
109111
}
110112
}
Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
// Copyright (c) Mysten Labs, Inc.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
module auth_event::events {
5-
use sui::event;
4+
module auth_event::events;
65

7-
public struct E has copy, drop { value: u64 }
6+
use sui::event;
87

9-
public entry fun emit(value: u64) {
10-
event::emit_authenticated(E { value });
11-
}
12-
}
8+
public struct E has copy, drop { value: u64 }
9+
10+
public entry fun emit(value: u64) {
11+
event::emit_authenticated(E { value });
12+
}
13+
14+
public entry fun emit_multiple(values: vector<u64>) {
15+
let mut i = 0;
16+
while (i < values.length()) {
17+
event::emit_authenticated(E { value: values[i] });
18+
i = i + 1;
19+
};
20+
}

0 commit comments

Comments
 (0)