@@ -123,8 +123,13 @@ struct Background<TPlat: PlatformRef> {
123
123
/// List of all active `state_subscribeRuntimeVersion` subscriptions, indexed by the
124
124
/// subscription ID.
125
125
runtime_version_subscriptions: hashbrown::HashSet<String, fnv::FnvBuildHasher>,
126
- /// List of all active `author_submitAndWatchExtrinsic` and
126
+ /// List of all active `author_submitAndWatchExtrinsic`, `transaction_v1_broadcast`, and
127
127
/// `transactionWatch_unstable_submitAndWatch` subscriptions, indexed by the subscription ID.
128
+ /// When it comes to `author_submitAndWatchExtrinsic` and
129
+ /// `transactionWatch_unstable_submitAndWatch`, transactions are removed from this list when
130
+ /// they are dropped from the transactions service. When it comes
131
+ /// to `transaction_v1_broadcast`, transactions are left forever until the API user
132
+ /// unsubscribes.
128
133
transactions_subscriptions: hashbrown::HashMap<String, TransactionWatch, fnv::FnvBuildHasher>,
129
134
130
135
/// List of all active `state_subscribeStorage` subscriptions, indexed by the subscription ID.
@@ -419,8 +424,14 @@ struct TransactionWatch {
419
424
enum TransactionWatchTy {
420
425
/// `author_submitAndWatchExtrinsic`.
421
426
Legacy,
427
+ /// `transaction_v1_broadcast`.
428
+ NewApi {
429
+ /// A copy of the body of the transaction is kept, as it might be necessary to re-insert
430
+ /// it in the transactions service later, for example if it reports having crashed.
431
+ transaction_bytes: Vec<u8>,
432
+ },
422
433
/// `transactionWatch_unstable_submitAndWatch`.
423
- NewApi ,
434
+ NewApiWatch ,
424
435
}
425
436
426
437
/// See [`Background::state_get_keys_paged_cache`].
@@ -771,6 +782,8 @@ pub(super) async fn run<TPlat: PlatformRef>(
771
782
| methods::MethodCall::rpc_methods { .. }
772
783
| methods::MethodCall::sudo_unstable_p2pDiscover { .. }
773
784
| methods::MethodCall::sudo_unstable_version { .. }
785
+ | methods::MethodCall::transaction_v1_broadcast { .. }
786
+ | methods::MethodCall::transaction_v1_stop { .. }
774
787
| methods::MethodCall::transactionWatch_unstable_submitAndWatch { .. }
775
788
| methods::MethodCall::transactionWatch_unstable_unwatch { .. }
776
789
| methods::MethodCall::sudo_network_unstable_watch { .. }
@@ -837,7 +850,7 @@ pub(super) async fn run<TPlat: PlatformRef>(
837
850
838
851
let mut transaction_updates = Box::pin(
839
852
me.transactions_service
840
- .submit_and_watch_transaction(transaction.0, 16)
853
+ .submit_and_watch_transaction(transaction.0, 16, true )
841
854
.await,
842
855
);
843
856
@@ -2504,43 +2517,62 @@ pub(super) async fn run<TPlat: PlatformRef>(
2504
2517
.await;
2505
2518
}
2506
2519
2507
- methods::MethodCall::transactionWatch_unstable_submitAndWatch {
2508
- transaction: methods::HexString(transaction),
2509
- } => {
2520
+ request_parsed @ (methods::MethodCall::transaction_v1_broadcast { .. }
2521
+ | methods::MethodCall::transactionWatch_unstable_submitAndWatch { .. }) => {
2522
+ let (transaction, watched) = match request_parsed {
2523
+ methods::MethodCall::transaction_v1_broadcast {
2524
+ transaction: methods::HexString(transaction),
2525
+ } => (transaction, false),
2526
+ methods::MethodCall::transactionWatch_unstable_submitAndWatch {
2527
+ transaction: methods::HexString(transaction),
2528
+ } => (transaction, true),
2529
+ _ => unreachable!()
2530
+ };
2531
+
2510
2532
let subscription_id = {
2511
2533
let mut subscription_id = [0u8; 32];
2512
2534
me.randomness.fill_bytes(&mut subscription_id);
2513
2535
bs58::encode(subscription_id).into_string()
2514
2536
};
2515
2537
2516
- let mut transaction_updates = Box::pin(
2517
- me.transactions_service
2518
- .submit_and_watch_transaction(transaction, 16)
2519
- .await,
2520
- );
2521
-
2522
2538
let _prev_value = me.transactions_subscriptions.insert(
2523
2539
subscription_id.clone(),
2524
2540
TransactionWatch {
2525
2541
included_block: None,
2526
2542
num_broadcasted_peers: 0,
2527
- ty: TransactionWatchTy::NewApi,
2543
+ ty: if watched { TransactionWatchTy::NewApiWatch } else {
2544
+ TransactionWatchTy::NewApi { transaction_bytes: transaction.clone() }
2545
+ },
2528
2546
},
2529
2547
);
2530
2548
debug_assert!(_prev_value.is_none());
2531
2549
2550
+ let mut transaction_updates = Box::pin(
2551
+ me.transactions_service
2552
+ .submit_and_watch_transaction(transaction, 16, watched)
2553
+ .await,
2554
+ );
2555
+
2532
2556
let _ = me
2533
2557
.responses_tx
2534
- .send(
2558
+ .send(if watched {
2535
2559
methods::Response::transactionWatch_unstable_submitAndWatch(
2536
2560
Cow::Borrowed(&subscription_id),
2537
2561
)
2562
+ } else {
2563
+ methods::Response::transaction_v1_broadcast(
2564
+ Cow::Borrowed(&subscription_id),
2565
+ )
2566
+ }
2538
2567
.to_json_response(request_id_json),
2539
2568
)
2540
2569
.await;
2541
2570
2542
2571
// A task is started that will yield when the transactions service
2543
2572
// generates a notification.
2573
+ // Note that we do that even for `transaction_v1_broadcast`, as it is
2574
+ // important to pull notifications from the channel in order to not
2575
+ // clog it.
2544
2576
me.background_tasks.push(Box::pin(async move {
2545
2577
let Some(status) = transaction_updates.as_mut().next().await else {
2546
2578
unreachable!()
@@ -2553,11 +2585,41 @@ pub(super) async fn run<TPlat: PlatformRef>(
2553
2585
}));
2554
2586
}
2555
2587
2588
+ methods::MethodCall::transaction_v1_stop { operation_id } => {
2589
+ let exists = me
2590
+ .transactions_subscriptions
2591
+ .get(&*operation_id)
2592
+ .map_or(false, |sub| {
2593
+ matches!(sub.ty, TransactionWatchTy::NewApi { .. })
2594
+ });
2595
+ if exists {
2596
+ me.transactions_subscriptions.remove(&*operation_id);
2597
+ let _ = me
2598
+ .responses_tx
2599
+ .send(
2600
+ methods::Response::transaction_v1_stop(())
2601
+ .to_json_response(request_id_json),
2602
+ )
2603
+ .await;
2604
+ } else {
2605
+ let _ = me
2606
+ .responses_tx
2607
+ .send(parse::build_error_response(
2608
+ request_id_json,
2609
+ json_rpc::parse::ErrorResponse::InvalidParams,
2610
+ None,
2611
+ ))
2612
+ .await;
2613
+ }
2614
+ }
2615
+
2556
2616
methods::MethodCall::transactionWatch_unstable_unwatch { subscription } => {
2557
2617
let exists = me
2558
2618
.transactions_subscriptions
2559
2619
.get(&*subscription)
2560
- .map_or(false, |sub| matches!(sub.ty, TransactionWatchTy::NewApi));
2620
+ .map_or(false, |sub| {
2621
+ matches!(sub.ty, TransactionWatchTy::NewApiWatch)
2622
+ });
2561
2623
if exists {
2562
2624
me.transactions_subscriptions.remove(&*subscription);
2563
2625
}
@@ -4785,7 +4847,55 @@ pub(super) async fn run<TPlat: PlatformRef>(
4785
4847
continue;
4786
4848
};
4787
4849
4788
- match (drop_reason, transaction_watch.ty) {
4850
+ match (drop_reason, &transaction_watch.ty) {
4851
+ (
4852
+ transactions_service::DropReason::GapInChain
4853
+ | transactions_service::DropReason::Crashed,
4854
+ TransactionWatchTy::NewApi { transaction_bytes },
4855
+ ) => {
4856
+ // In case of `transaction_v1_broadcast`, we re-submit the transaction
4857
+ // if it was dropped for a temporary reasons.
4858
+ let mut new_watcher = Box::pin(
4859
+ me.transactions_service
4860
+ .submit_and_watch_transaction(transaction_bytes.clone(), 16, false)
4861
+ .await,
4862
+ );
4863
+
4864
+ let _prev_value = me
4865
+ .transactions_subscriptions
4866
+ .insert(subscription_id.clone(), transaction_watch);
4867
+ debug_assert!(_prev_value.is_none());
4868
+
4869
+ // Push a new background task that waits for the next notification.
4870
+ me.background_tasks.push(Box::pin(async move {
4871
+ let Some(status) = new_watcher.as_mut().next().await else {
4872
+ unreachable!()
4873
+ };
4874
+ Event::TransactionEvent {
4875
+ subscription_id,
4876
+ event: status,
4877
+ watcher: new_watcher,
4878
+ }
4879
+ }));
4880
+ }
4881
+
4882
+ (
4883
+ transactions_service::DropReason::Finalized { .. }
4884
+ | transactions_service::DropReason::Invalid(_)
4885
+ | transactions_service::DropReason::MaxPendingTransactionsReached
4886
+ | transactions_service::DropReason::ValidateError(_),
4887
+ TransactionWatchTy::NewApi { .. },
4888
+ ) => {
4889
+ // In case of `transaction_v1_broadcast`, the transaction is re-inserted
4890
+ // in the list, but no new notification-generating task is pushed, making
4891
+ // the transaction effectively dead and waiting for `transaction_v1_stop`
4892
+ // to be called to remove it.
4893
+ let _prev_value = me
4894
+ .transactions_subscriptions
4895
+ .insert(subscription_id.clone(), transaction_watch);
4896
+ debug_assert!(_prev_value.is_none());
4897
+ }
4898
+
4789
4899
(transactions_service::DropReason::GapInChain, TransactionWatchTy::Legacy)
4790
4900
| (
4791
4901
transactions_service::DropReason::MaxPendingTransactionsReached,
@@ -4808,7 +4918,10 @@ pub(super) async fn run<TPlat: PlatformRef>(
4808
4918
)
4809
4919
.await;
4810
4920
}
4811
- (transactions_service::DropReason::GapInChain, TransactionWatchTy::NewApi) => {
4921
+ (
4922
+ transactions_service::DropReason::GapInChain,
4923
+ TransactionWatchTy::NewApiWatch,
4924
+ ) => {
4812
4925
let _ = me
4813
4926
.responses_tx
4814
4927
.send(
@@ -4825,7 +4938,7 @@ pub(super) async fn run<TPlat: PlatformRef>(
4825
4938
}
4826
4939
(
4827
4940
transactions_service::DropReason::MaxPendingTransactionsReached,
4828
- TransactionWatchTy::NewApi ,
4941
+ TransactionWatchTy::NewApiWatch ,
4829
4942
) => {
4830
4943
let _ = me
4831
4944
.responses_tx
@@ -4843,7 +4956,7 @@ pub(super) async fn run<TPlat: PlatformRef>(
4843
4956
}
4844
4957
(
4845
4958
transactions_service::DropReason::Invalid(error),
4846
- TransactionWatchTy::NewApi ,
4959
+ TransactionWatchTy::NewApiWatch ,
4847
4960
) => {
4848
4961
let _ = me
4849
4962
.responses_tx
@@ -4860,7 +4973,7 @@ pub(super) async fn run<TPlat: PlatformRef>(
4860
4973
}
4861
4974
(
4862
4975
transactions_service::DropReason::ValidateError(error),
4863
- TransactionWatchTy::NewApi ,
4976
+ TransactionWatchTy::NewApiWatch ,
4864
4977
) => {
4865
4978
let _ = me
4866
4979
.responses_tx
@@ -4875,7 +4988,10 @@ pub(super) async fn run<TPlat: PlatformRef>(
4875
4988
)
4876
4989
.await;
4877
4990
}
4878
- (transactions_service::DropReason::Crashed, TransactionWatchTy::NewApi) => {
4991
+ (
4992
+ transactions_service::DropReason::Crashed,
4993
+ TransactionWatchTy::NewApiWatch,
4994
+ ) => {
4879
4995
let _ = me
4880
4996
.responses_tx
4881
4997
.send(
@@ -4909,7 +5025,7 @@ pub(super) async fn run<TPlat: PlatformRef>(
4909
5025
}
4910
5026
(
4911
5027
transactions_service::DropReason::Finalized { block_hash, index },
4912
- TransactionWatchTy::NewApi ,
5028
+ TransactionWatchTy::NewApiWatch ,
4913
5029
) => {
4914
5030
let _ = me
4915
5031
.responses_tx
@@ -4946,6 +5062,10 @@ pub(super) async fn run<TPlat: PlatformRef>(
4946
5062
};
4947
5063
4948
5064
match (event, &transaction_watch.ty) {
5065
+ (_, TransactionWatchTy::NewApi { .. }) => {
5066
+ // Events are ignored when it comes to `transaction_v1_broadcast`.
5067
+ }
5068
+
4949
5069
(
4950
5070
transactions_service::TransactionStatus::Broadcast(peers),
4951
5071
TransactionWatchTy::Legacy,
@@ -4965,7 +5085,7 @@ pub(super) async fn run<TPlat: PlatformRef>(
4965
5085
}
4966
5086
(
4967
5087
transactions_service::TransactionStatus::Broadcast(peers),
4968
- TransactionWatchTy::NewApi ,
5088
+ TransactionWatchTy::NewApiWatch ,
4969
5089
) => {
4970
5090
transaction_watch.num_broadcasted_peers += peers.len();
4971
5091
let _ = me
@@ -4993,7 +5113,7 @@ pub(super) async fn run<TPlat: PlatformRef>(
4993
5113
}
4994
5114
(
4995
5115
transactions_service::TransactionStatus::Validated,
4996
- TransactionWatchTy::NewApi ,
5116
+ TransactionWatchTy::NewApiWatch ,
4997
5117
) => {
4998
5118
let _ = me
4999
5119
.responses_tx
@@ -5052,7 +5172,7 @@ pub(super) async fn run<TPlat: PlatformRef>(
5052
5172
transactions_service::TransactionStatus::IncludedBlockUpdate {
5053
5173
block_hash: Some((block_hash, index)),
5054
5174
},
5055
- TransactionWatchTy::NewApi ,
5175
+ TransactionWatchTy::NewApiWatch ,
5056
5176
) => {
5057
5177
transaction_watch.included_block = Some(block_hash);
5058
5178
let _ = me
@@ -5076,7 +5196,7 @@ pub(super) async fn run<TPlat: PlatformRef>(
5076
5196
transactions_service::TransactionStatus::IncludedBlockUpdate {
5077
5197
block_hash: None,
5078
5198
},
5079
- TransactionWatchTy::NewApi ,
5199
+ TransactionWatchTy::NewApiWatch ,
5080
5200
) => {
5081
5201
let _ = me
5082
5202
.responses_tx
0 commit comments