Skip to content

Commit c9263cf

Browse files
author
Jean-François Morcillo
committed
Add operation execution status to get_operations command
Signed-off-by: Jean-François Morcillo <[email protected]>
1 parent 27c2795 commit c9263cf

File tree

15 files changed

+463
-97
lines changed

15 files changed

+463
-97
lines changed

massa-api-exports/src/lib.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,25 @@ fn display_if_true(value: bool, text: &str) -> String {
4747
}
4848
}
4949

50+
fn display_option_bool(
51+
value: Option<bool>,
52+
text_true: &str,
53+
text_false: &str,
54+
text_none: &str,
55+
) -> String {
56+
match value {
57+
Some(true) => {
58+
format!("[{}]", text_true)
59+
}
60+
Some(false) => {
61+
format!("[{}]", text_false)
62+
}
63+
None => {
64+
format!("[{}]", text_none)
65+
}
66+
}
67+
}
68+
5069
/// Just a wrapper with a optional beginning and end
5170
#[derive(Debug, Deserialize, Clone, Copy, Serialize)]
5271
pub struct TimeInterval {

massa-api-exports/src/operation.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use massa_models::{
88
use massa_signature::{PublicKey, Signature};
99
use serde::{Deserialize, Serialize};
1010

11-
use crate::display_if_true;
11+
use crate::{display_if_true, display_option_bool};
1212

1313
/// operation input
1414
#[derive(Serialize, Deserialize, Debug)]
@@ -32,21 +32,34 @@ pub struct OperationInfo {
3232
/// if it appears in multiple blocks, these blocks are in different cliques
3333
pub in_blocks: Vec<BlockId>,
3434
/// true if the operation is final (for example in a final block)
35-
pub is_final: bool,
35+
pub is_operation_final: Option<bool>,
3636
/// Thread in which the operation can be included
3737
pub thread: u8,
3838
/// the operation itself
3939
pub operation: SecureShareOperation,
40+
/// true if the operation execution succeeded, false if failed, None means unknown
41+
pub op_exec_status: Option<bool>,
4042
}
4143

4244
impl std::fmt::Display for OperationInfo {
4345
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4446
writeln!(
4547
f,
46-
"Operation {}{}{}",
48+
"Operation {}{}{}{}",
4749
self.id,
4850
display_if_true(self.in_pool, " (in pool)"),
49-
display_if_true(self.is_final, " (final)")
51+
display_option_bool(
52+
self.is_operation_final,
53+
" (operation is final)",
54+
" (operation is final)",
55+
" (finality unkown)"
56+
),
57+
display_option_bool(
58+
self.op_exec_status,
59+
" ( succes)",
60+
" ( failed)",
61+
" (unkown status)"
62+
)
5063
)?;
5164
writeln!(f, "In blocks:")?;
5265
for block_id in &self.in_blocks {

massa-api/src/public.rs

Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -495,60 +495,64 @@ impl MassaRpcServer for API<Public> {
495495
.collect()
496496
};
497497

498-
// keep only the ops found in storage
498+
// keep only the ops id (found in storage)
499499
let ops: Vec<OperationId> = storage_info.iter().map(|(op, _)| op.id).collect();
500500

501-
// ask pool whether it carries the operations
502-
let in_pool = self.0.pool_command_sender.contains_operations(&ops);
503-
504501
let api_cfg = self.0.api_settings.clone();
505-
let consensus_controller = self.0.consensus_controller.clone();
506502
if ops.len() as u64 > api_cfg.max_arguments {
507503
return Err(ApiError::BadRequest("too many arguments".into()).into());
508504
}
509505

510-
// check finality by cross-referencing Consensus and looking for final blocks that contain the op
511-
let is_final: Vec<bool> = {
512-
let involved_blocks: Vec<BlockId> = storage_info
513-
.iter()
514-
.flat_map(|(_op, bs)| bs.iter())
515-
.unique()
516-
.cloned()
517-
.collect();
506+
// ask pool whether it carries the operations
507+
let in_pool = self.0.pool_command_sender.contains_operations(&ops);
518508

519-
let involved_block_statuses = consensus_controller.get_block_statuses(&involved_blocks);
509+
let (speculative_op_exec_statuses, final_op_exec_statuses) =
510+
self.0.execution_controller.get_op_exec_status();
520511

521-
let block_statuses: PreHashMap<BlockId, BlockGraphStatus> = involved_blocks
522-
.into_iter()
523-
.zip(involved_block_statuses.into_iter())
524-
.collect();
525-
storage_info
526-
.iter()
527-
.map(|(_op, bs)| {
528-
bs.iter()
529-
.any(|b| block_statuses.get(b) == Some(&BlockGraphStatus::Final))
530-
})
531-
.collect()
532-
};
512+
// compute operation finality and operation execution status from *_op_exec_statuses
513+
let (is_operation_final, statuses): (Vec<Option<bool>>, Vec<Option<bool>>) = ops
514+
.iter()
515+
.map(|op| {
516+
let is_final = final_op_exec_statuses
517+
.get(op)
518+
.and_then(|status| status.clone());
519+
let is_speculative = speculative_op_exec_statuses
520+
.get(op)
521+
.and_then(|status| status.clone());
522+
match (is_final, is_speculative) {
523+
(Some(true), _) => (Some(true), Some(true)),
524+
(Some(false), _) => (Some(true), Some(false)),
525+
(None, Some(true)) => (Some(false), Some(true)),
526+
(None, Some(false)) => (Some(false), Some(false)),
527+
(None, None) => (None, None),
528+
}
529+
})
530+
.collect::<Vec<(Option<bool>, Option<bool>)>>()
531+
.into_iter()
532+
.unzip();
533533

534534
// gather all values into a vector of OperationInfo instances
535535
let mut res: Vec<OperationInfo> = Vec::with_capacity(ops.len());
536536
let zipped_iterator = izip!(
537537
ops.into_iter(),
538538
storage_info.into_iter(),
539539
in_pool.into_iter(),
540-
is_final.into_iter()
540+
is_operation_final.into_iter(),
541+
statuses.into_iter(),
541542
);
542-
for (id, (operation, in_blocks), in_pool, is_final) in zipped_iterator {
543+
for (id, (operation, in_blocks), in_pool, is_operation_final, op_exec_status) in
544+
zipped_iterator
545+
{
543546
res.push(OperationInfo {
544547
id,
545548
in_pool,
546-
is_final,
549+
is_operation_final,
547550
thread: operation
548551
.content_creator_address
549552
.get_thread(api_cfg.thread_count),
550553
operation,
551554
in_blocks: in_blocks.into_iter().collect(),
555+
op_exec_status,
552556
});
553557
}
554558

@@ -1052,3 +1056,34 @@ impl MassaRpcServer for API<Public> {
10521056
openrpc
10531057
}
10541058
}
1059+
1060+
// check finality by cross-referencing Consensus and looking for final blocks that contain the op
1061+
fn check_consensus_finality(
1062+
storage_info: &Vec<(SecureShareOperation, PreHashSet<BlockId>)>,
1063+
consensus_controller: Box<dyn ConsensusController>,
1064+
) -> Vec<(OperationId, bool)> {
1065+
let involved_blocks: Vec<BlockId> = storage_info
1066+
.iter()
1067+
.flat_map(|(_op, bs)| bs.iter())
1068+
.unique()
1069+
.cloned()
1070+
.collect();
1071+
1072+
let involved_block_statuses = consensus_controller.get_block_statuses(&involved_blocks);
1073+
1074+
let block_statuses: PreHashMap<BlockId, BlockGraphStatus> = involved_blocks
1075+
.into_iter()
1076+
.zip(involved_block_statuses.into_iter())
1077+
.collect();
1078+
1079+
storage_info
1080+
.iter()
1081+
.map(|(op, bs)| {
1082+
(
1083+
op.id,
1084+
bs.iter()
1085+
.any(|b| block_statuses.get(b) == Some(&BlockGraphStatus::Final)),
1086+
)
1087+
})
1088+
.collect()
1089+
}

massa-executed-ops/src/executed_ops.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use nom::{
2222
IResult, Parser,
2323
};
2424
use std::{
25-
collections::BTreeMap,
25+
collections::{BTreeMap, HashMap},
2626
ops::Bound::{Excluded, Included, Unbounded},
2727
};
2828

@@ -39,6 +39,8 @@ pub struct ExecutedOps {
3939
pub ops: PreHashSet<OperationId>,
4040
/// Accumulated hash of the executed operations
4141
pub hash: Hash,
42+
/// successful operations
43+
pub op_exec_status: HashMap<OperationId, bool>,
4244
}
4345

4446
impl ExecutedOps {
@@ -49,6 +51,7 @@ impl ExecutedOps {
4951
sorted_ops: BTreeMap::new(),
5052
ops: PreHashSet::default(),
5153
hash: Hash::from_bytes(EXECUTED_OPS_HASH_INITIAL_BYTES),
54+
op_exec_status: HashMap::new(),
5255
}
5356
}
5457

@@ -86,7 +89,7 @@ impl ExecutedOps {
8689
/// Apply speculative operations changes to the final executed operations state
8790
pub fn apply_changes(&mut self, changes: ExecutedOpsChanges, slot: Slot) {
8891
self.extend_and_compute_hash(changes.keys());
89-
for (op_id, slot) in changes {
92+
for (op_id, (op_exec_success, slot)) in changes {
9093
self.sorted_ops
9194
.entry(slot)
9295
.and_modify(|ids| {
@@ -97,7 +100,11 @@ impl ExecutedOps {
97100
new.insert(op_id);
98101
new
99102
});
103+
if let Some(status) = op_exec_success {
104+
self.op_exec_status.insert(op_id, status);
105+
}
100106
}
107+
dbg!(&self.op_exec_status);
101108
self.prune(slot);
102109
}
103110

@@ -113,6 +120,7 @@ impl ExecutedOps {
113120
for (_, ids) in removed {
114121
for op_id in ids {
115122
self.ops.remove(&op_id);
123+
self.op_exec_status.remove(&op_id);
116124
self.hash ^= *op_id.get_hash();
117125
}
118126
}

massa-executed-ops/src/ops_changes.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
//! Copyright (c) 2022 MASSA LABS <[email protected]>
22
33
use massa_models::{
4-
operation::{OperationId, OperationIdDeserializer, OperationIdSerializer},
4+
operation::{
5+
BoolDeserializer, BoolSerializer, OperationId, OperationIdDeserializer,
6+
OperationIdSerializer,
7+
},
58
prehash::PreHashMap,
69
slot::{Slot, SlotDeserializer, SlotSerializer},
710
};
811
use massa_serialization::{
9-
Deserializer, SerializeError, Serializer, U64VarIntDeserializer, U64VarIntSerializer,
12+
Deserializer, OptionDeserializer, OptionSerializer, SerializeError, Serializer,
13+
U64VarIntDeserializer, U64VarIntSerializer,
1014
};
1115
use nom::{
1216
error::{context, ContextError, ParseError},
@@ -16,13 +20,14 @@ use nom::{
1620
};
1721
use std::ops::Bound::{Excluded, Included};
1822

19-
/// Speculatives changes for ExecutedOps
20-
pub type ExecutedOpsChanges = PreHashMap<OperationId, Slot>;
23+
/// Speculative changes for ExecutedOps
24+
pub type ExecutedOpsChanges = PreHashMap<OperationId, (Option<bool>, Slot)>;
2125

2226
/// `ExecutedOps` Serializer
2327
pub struct ExecutedOpsChangesSerializer {
2428
u64_serializer: U64VarIntSerializer,
2529
operation_id_serializer: OperationIdSerializer,
30+
op_execution: OptionSerializer<bool, BoolSerializer>,
2631
slot_serializer: SlotSerializer,
2732
}
2833

@@ -38,6 +43,7 @@ impl ExecutedOpsChangesSerializer {
3843
ExecutedOpsChangesSerializer {
3944
u64_serializer: U64VarIntSerializer::new(),
4045
operation_id_serializer: OperationIdSerializer::new(),
46+
op_execution: OptionSerializer::new(BoolSerializer::new()),
4147
slot_serializer: SlotSerializer::new(),
4248
}
4349
}
@@ -51,8 +57,10 @@ impl Serializer<ExecutedOpsChanges> for ExecutedOpsChangesSerializer {
5157
) -> Result<(), SerializeError> {
5258
self.u64_serializer
5359
.serialize(&(value.len() as u64), buffer)?;
54-
for (op_id, slot) in value {
60+
for (op_id, (op_execution_succeeded, slot)) in value {
5561
self.operation_id_serializer.serialize(op_id, buffer)?;
62+
self.op_execution
63+
.serialize(op_execution_succeeded, buffer)?;
5664
self.slot_serializer.serialize(slot, buffer)?;
5765
}
5866
Ok(())
@@ -63,6 +71,7 @@ impl Serializer<ExecutedOpsChanges> for ExecutedOpsChangesSerializer {
6371
pub struct ExecutedOpsChangesDeserializer {
6472
u64_deserializer: U64VarIntDeserializer,
6573
operation_id_deserializer: OperationIdDeserializer,
74+
op_execution_deserializer: OptionDeserializer<bool, BoolDeserializer>,
6675
slot_deserializer: SlotDeserializer,
6776
}
6877

@@ -75,6 +84,7 @@ impl ExecutedOpsChangesDeserializer {
7584
Included(max_ops_changes_length),
7685
),
7786
operation_id_deserializer: OperationIdDeserializer::new(),
87+
op_execution_deserializer: OptionDeserializer::new(BoolDeserializer::new()),
7888
slot_deserializer: SlotDeserializer::new(
7989
(Included(u64::MIN), Included(u64::MAX)),
8090
(Included(0), Excluded(thread_count)),
@@ -98,13 +108,20 @@ impl Deserializer<ExecutedOpsChanges> for ExecutedOpsChangesDeserializer {
98108
context("operation id", |input| {
99109
self.operation_id_deserializer.deserialize(input)
100110
}),
111+
context("operation execution succeeded", |input| {
112+
self.op_execution_deserializer.deserialize(input)
113+
}),
101114
context("expiration slot", |input| {
102115
self.slot_deserializer.deserialize(input)
103116
}),
104117
)),
105118
),
106119
)
107-
.map(|ids| ids.into_iter().collect())
120+
.map(|ids| {
121+
ids.into_iter()
122+
.map(|(id, op_exec_status, slot)| (id, (op_exec_status, slot)))
123+
.collect()
124+
})
108125
.parse(buffer)
109126
}
110127
}

massa-execution-exports/src/controller_traits.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ pub trait ExecutionController: Send + Sync {
5151
addresses: &[Address],
5252
) -> Vec<(Option<Amount>, Option<Amount>)>;
5353

54+
fn get_op_exec_status(
55+
&self,
56+
) -> (
57+
HashMap<OperationId, Option<bool>>,
58+
HashMap<OperationId, Option<bool>>,
59+
);
60+
5461
/// Get a copy of a single datastore entry with its final and active values
5562
///
5663
/// # Return value

massa-execution-exports/src/test_exports/mock.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,4 +222,13 @@ impl ExecutionController for MockExecutionController {
222222
fn clone_box(&self) -> Box<dyn ExecutionController> {
223223
Box::new(self.clone())
224224
}
225+
226+
fn get_op_exec_status(
227+
&self,
228+
) -> (
229+
HashMap<OperationId, Option<bool>>,
230+
HashMap<OperationId, Option<bool>>,
231+
) {
232+
(HashMap::new(), HashMap::new())
233+
}
225234
}

0 commit comments

Comments
 (0)