Source file mockup_simulator.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
type block = {
rpc_context : Tezos_protocol_environment.rpc_context;
protocol_data : Protocol.Alpha_context.Block_header.protocol_data;
raw_protocol_data : Bytes.t;
operations : Mockup.M.Block_services.operation list list;
resulting_context_hash : Context_hash.t;
}
type chain = block list
(** As new blocks and operations are received they are pushed to an Lwt_pipe
wrapped into this type. *)
type broadcast =
| Broadcast_block of Block_hash.t * Block_header.t * Operation.t list list
| Broadcast_op of Operation_hash.t * Alpha_context.packed_operation
(** The state of a mockup node. *)
type state = {
instance_index : int;
(** Index of this node. Indices go from 0 to N-1 where N is the total
number of bakers in the simulation. *)
live_depth : int;
(** How many blocks (counting from the head into the past) are considered live? *)
mutable chain : chain; (** The chain as seen by this fake "node". *)
mutable mempool : (Operation_hash.t * Mockup.M.Protocol.operation) list;
(** Mempool of this fake "node". *)
chain_table : chain Block_hash.Table.t;
(** The chain table of this fake "node". It maps from block hashes to
blocks. *)
global_chain_table : block Block_hash.Table.t;
(** The global chain table that allows us to look up blocks that may be
missing in [chain_table], i.e. not known to this particular node. This
is used to find unknown predecessors. The real node can ask about an
unknown block and receive it on request, this is supposed to emulate
that functionality. *)
ctxt_table : Tezos_protocol_environment.rpc_context Context_hash.Table.t;
(** The context table allows us to look up rpc_context by its hash. *)
validated_blocks_pipe :
(Block_hash.t * Block_header.t * Operation.t list list) Lwt_pipe.Unbounded.t;
(** [validated_blocks_pipe] is used to implement the
[monitor_validated_blocks] RPC. *)
heads_pipe : (Block_hash.t * Block_header.t) Lwt_pipe.Unbounded.t;
(** [heads_pipe] is used to implement the [monitor_heads]
RPC. *)
mutable operations_stream :
(Operation_hash.t * Mockup.M.Protocol.operation) list Lwt_stream.t;
mutable operations_stream_push :
(Operation_hash.t * Mockup.M.Protocol.operation) list option -> unit;
(** [operations_pipe] is used to implement the [operations_pipe] RPC. *)
mutable streaming_operations : bool;
(** A helper flag used to implement the monitor operations RPC. *)
broadcast_pipes : broadcast Lwt_pipe.Unbounded.t list;
(** Broadcast pipes per node. *)
genesis_block_true_hash : Block_hash.t;
(** True hash of the genesis
block as calculated by the
[Block_header.hash] function. *)
}
let accounts = Mockup.Protocol_parameters.default_value.bootstrap_accounts
let chain_id = Chain_id.of_string_exn "main"
let genesis_block_hash =
Block_hash.of_b58check_exn
"BLockGenesisGenesisGenesisGenesisGenesisCCCCCeZiLHU"
let genesis_predecessor_block_hash = Block_hash.zero
type propagation = Block | Pass | Delay of float
type propagation_vector = propagation list
module type Hooks = sig
val on_inject_block :
level:int32 ->
round:int32 ->
block_hash:Block_hash.t ->
block_header:Block_header.t ->
operations:Operation.t list list ->
protocol_data:Alpha_context.Block_header.protocol_data ->
(Block_hash.t * Block_header.t * Operation.t list list * propagation_vector)
tzresult
Lwt.t
val on_inject_operation :
op_hash:Operation_hash.t ->
op:Alpha_context.packed_operation ->
(Operation_hash.t * Alpha_context.packed_operation * propagation_vector)
tzresult
Lwt.t
val on_new_validated_block :
block_hash:Block_hash.t ->
block_header:Block_header.t ->
operations:Operation.t list list ->
(Block_hash.t * Block_header.t * Operation.t list list) option Lwt.t
val on_new_head :
block_hash:Block_hash.t ->
block_header:Block_header.t ->
(Block_hash.t * Block_header.t) option Lwt.t
val on_new_operation :
Operation_hash.t * Alpha_context.packed_operation ->
(Operation_hash.t * Alpha_context.packed_operation) option Lwt.t
val check_block_before_processing :
level:int32 ->
round:int32 ->
block_hash:Block_hash.t ->
block_header:Block_header.t ->
protocol_data:Alpha_context.Block_header.protocol_data ->
unit tzresult Lwt.t
val check_chain_after_processing :
level:int32 -> round:int32 -> chain:chain -> unit tzresult Lwt.t
val check_mempool_after_processing :
mempool:(Operation_hash.t * Mockup.M.Protocol.operation) list ->
unit tzresult Lwt.t
val stop_on_event : Baking_state.event -> bool
val on_start_baker :
baker_position:int ->
delegates:Baking_state.consensus_key list ->
cctxt:Protocol_client_context.full ->
unit Lwt.t
val check_chain_on_success : chain:chain -> unit tzresult Lwt.t
end
(** Return a series of blocks starting from the block with the given
identifier. *)
let locate_blocks (state : state)
(block : Tezos_shell_services.Block_services.block) :
block list tzresult Lwt.t =
let open Lwt_result_syntax in
match block with
| `Hash (hash, rel) -> (
match Block_hash.Table.find state.chain_table hash with
| None ->
failwith "locate_blocks: can't find the block %a" Block_hash.pp hash
| Some chain0 ->
let _, chain = List.split_n rel chain0 in
return chain)
| `Head rel ->
let _, chain = List.split_n rel state.chain in
return chain
| `Level _ -> failwith "locate_blocks: `Level block spec not handled"
| `Genesis -> failwith "locate_blocks: `Genesis block spec net handled"
| `Alias _ -> failwith "locate_blocks: `Alias block spec not handled"
(** Similar to [locate_blocks], but only returns the first block. *)
let locate_block (state : state)
(block : Tezos_shell_services.Block_services.block) : block tzresult Lwt.t =
let open Lwt_result_syntax in
let* blocks = locate_blocks state block in
match blocks with
| [] -> failwith "locate_block: can't find the block"
| x :: _ -> return x
(** Return the collection of live blocks for a given block identifier. *)
let live_blocks (state : state) block =
let open Lwt_result_syntax in
let* chain = locate_blocks state block in
let segment, _ = List.split_n state.live_depth chain in
return
(List.fold_left
(fun set ({rpc_context; _} : block) ->
let hash = rpc_context.Tezos_protocol_environment.block_hash in
Block_hash.Set.add hash set)
(Block_hash.Set.of_list
[state.genesis_block_true_hash; genesis_predecessor_block_hash])
segment)
(** Extract the round number from raw fitness. *)
let round_from_raw_fitness raw_fitness =
let open Lwt_result_syntax in
match Protocol.Alpha_context.Fitness.from_raw raw_fitness with
| Ok fitness ->
return
(Alpha_context.Round.to_int32
(Protocol.Alpha_context.Fitness.round fitness))
| Error _ -> failwith "round_from_raw_fitness: cannot parse fitness"
(** Extract level from a block header. *)
let get_block_level ( : Block_header.t) =
let open Lwt_result_syntax in
return block_header.shell.level
(** Extract round from a block header. *)
let get_block_round ( : Block_header.t) =
round_from_raw_fitness block_header.shell.fitness
(** Parse protocol data. *)
let parse_protocol_data (protocol_data : Bytes.t) =
let open Lwt_result_syntax in
match
Data_encoding.Binary.of_bytes_opt
Protocol.Alpha_context.Block_header.protocol_data_encoding
protocol_data
with
| None -> failwith "can't parse protocol data of a block"
| Some parsed_protocol_data -> return parsed_protocol_data
(** Broadcast an operation or block according to the given propagation
vector. *)
let handle_propagation msg propagation_vector broadcast_pipes =
let open Lwt_result_syntax in
let*! () =
List.iter_s
(fun (propagation, pipe) ->
match propagation with
| Block -> Lwt.return_unit
| Pass ->
Lwt_pipe.Unbounded.push pipe msg ;
Lwt.return_unit
| Delay s ->
Lwt.dont_wait
(fun () ->
let*! () = Lwt_unix.sleep s in
Lwt_pipe.Unbounded.push pipe msg ;
Lwt.return_unit)
(fun _exn -> ()) ;
Lwt.return_unit)
(List.combine_drop propagation_vector broadcast_pipes)
in
return_unit
(** Use the [user_hooks] to produce a module of functions that will perform
the heavy lifting for the RPC implementations. *)
let make_mocked_services_hooks (state : state) (user_hooks : (module Hooks)) :
Faked_services.hooks =
let open Lwt_result_syntax in
let module User_hooks = (val user_hooks : Hooks) in
let module Impl : Faked_services.Mocked_services_hooks = struct
type mempool = Mockup.M.Block_services.Mempool.t
let monitor_validated_blocks () =
let next () =
let rec pop_until_ok () =
let*! block_hash, , operations =
Lwt_pipe.Unbounded.pop state.validated_blocks_pipe
in
let*! result =
User_hooks.on_new_validated_block
~block_hash
~block_header
~operations
in
match result with
| None -> pop_until_ok ()
| Some (hash, head, operations) ->
Lwt.return_some (chain_id, hash, head, operations)
in
pop_until_ok ()
in
let shutdown () = () in
Tezos_rpc.Answer.{next; shutdown}
let monitor_heads () =
let next () =
let rec pop_until_ok () =
let*! block_hash, =
Lwt_pipe.Unbounded.pop state.heads_pipe
in
let*! () = Lwt_unix.sleep 0.1 in
let*! head_opt = User_hooks.on_new_head ~block_hash ~block_header in
match head_opt with
| None -> pop_until_ok ()
| Some head -> Lwt.return_some head
in
pop_until_ok ()
in
let shutdown () = () in
Tezos_rpc.Answer.{next; shutdown}
let monitor_bootstrapped () =
let first_run = ref true in
let next () =
if !first_run then (
first_run := false ;
let b = match state.chain with [] -> assert false | b :: _ -> b in
let head_hash = b.rpc_context.block_hash in
let timestamp = b.rpc_context.block_header.timestamp in
Lwt.return_some (head_hash, timestamp))
else Lwt.return_none
in
let shutdown () = () in
Tezos_rpc.Answer.{next; shutdown}
let protocols (block : Tezos_shell_services.Block_services.block) =
let* x = locate_block state block in
let hash = x.rpc_context.block_hash in
let is_predecessor_of_genesis =
match block with
| `Hash (requested_hash, rel) ->
Int.equal rel 0
&& Block_hash.equal requested_hash genesis_predecessor_block_hash
| _ -> false
in
return
Tezos_shell_services.Block_services.
{
current_protocol =
(if
Block_hash.equal hash genesis_block_hash
|| is_predecessor_of_genesis
then Protocol_hash.zero
else Protocol.hash);
next_protocol =
(if is_predecessor_of_genesis then Protocol_hash.zero
else Protocol.hash);
}
let may_lie_on_proto_level block x =
let is_predecessor_of_genesis =
match block with
| `Hash (requested_hash, rel) ->
Int.equal rel 0
&& Block_hash.equal requested_hash genesis_predecessor_block_hash
| _ -> false
in
if is_predecessor_of_genesis then
{
x.rpc_context.block_header with
proto_level = pred x.rpc_context.block_header.proto_level;
}
else x.rpc_context.block_header
let (block : Tezos_shell_services.Block_services.block) :
bytes tzresult Lwt.t =
let* x = locate_block state block in
let shell = may_lie_on_proto_level block x in
let protocol_data =
Data_encoding.Binary.to_bytes_exn
Protocol.block_header_data_encoding
x.protocol_data
in
return
(Data_encoding.Binary.to_bytes_exn
Tezos_base.Block_header.encoding
{shell; protocol_data})
let (block : Tezos_shell_services.Block_services.block) :
Mockup.M.Block_services.block_header tzresult Lwt.t =
let* x = locate_block state block in
let shell = may_lie_on_proto_level block x in
return
{
Mockup.M.Block_services.hash = x.rpc_context.block_hash;
chain_id;
shell;
protocol_data = x.protocol_data;
}
let resulting_context_hash
(block : Tezos_shell_services.Block_services.block) :
Context_hash.t tzresult Lwt.t =
let* x = locate_block state block in
return x.resulting_context_hash
let operations block =
let* x = locate_block state block in
return x.operations
let inject_block block_hash ( : Block_header.t) operations =
let* protocol_data = parse_protocol_data block_header.protocol_data in
let* level = get_block_level block_header in
let* round = get_block_round block_header in
let* block_hash1, , operations1, propagation_vector =
User_hooks.on_inject_block
~level
~round
~block_hash
~block_header
~operations
~protocol_data
in
handle_propagation
(Broadcast_block (block_hash1, block_header1, operations1))
propagation_vector
state.broadcast_pipes
let all_pipes_or_select = function
| None -> return state.broadcast_pipes
| Some l ->
List.map_es
(fun n ->
match List.nth_opt state.broadcast_pipes n with
| None ->
failwith
"Node number %d is out of range (max is %d)"
n
(List.length state.broadcast_pipes - 1)
| Some pipe -> return pipe)
l
let broadcast_block ?dests block_hash ( : Block_header.t)
operations =
let* pipes = all_pipes_or_select dests in
let*! () =
List.iter_s
(fun pipe ->
Lwt_pipe.Unbounded.push
pipe
(Broadcast_block (block_hash, block_header, operations)) ;
Lwt.return_unit)
pipes
in
return_unit
let inject_operation (Operation.{shell; proto} as op) =
let op_hash = Operation.hash op in
let proto_op_opt =
Data_encoding.Binary.of_bytes Protocol.operation_data_encoding proto
in
match proto_op_opt with
| Error _ -> failwith "inject_operation: cannot parse operation"
| Ok protocol_data ->
let op : Protocol.Alpha_context.packed_operation =
{shell; protocol_data}
in
let* op_hash1, op1, propagation_vector =
User_hooks.on_inject_operation ~op_hash ~op
in
let* () =
handle_propagation
(Broadcast_op (op_hash1, op1))
propagation_vector
state.broadcast_pipes
in
return op_hash1
let broadcast_operation ?dests
(op : Protocol.Alpha_context.packed_operation) =
let* pipes = all_pipes_or_select dests in
let op_hash = Alpha_context.Operation.hash_packed op in
let*! () =
List.iter_s
(fun pipe ->
Lwt_pipe.Unbounded.push pipe (Broadcast_op (op_hash, op)) ;
Lwt.return_unit)
pipes
in
return_unit
let pending_operations () =
let ops = state.mempool in
Lwt.return
Mockup.M.Block_services.Mempool.
{
validated = ops;
refused = Operation_hash.Map.empty;
outdated = Operation_hash.Map.empty;
branch_refused = Operation_hash.Map.empty;
branch_delayed = Operation_hash.Map.empty;
unprocessed = Operation_hash.Map.empty;
}
let monitor_operations ~version ~validated ~branch_delayed ~branch_refused
~refused =
ignore validated ;
ignore branch_delayed ;
ignore branch_refused ;
ignore refused ;
let streamed = ref false in
state.streaming_operations <- true ;
let next () =
let rec loop () =
let*! ops_opt = Lwt_stream.get state.operations_stream in
match ops_opt with
| None when !streamed -> Lwt.return_none
| None ->
streamed := true ;
Lwt.return_some (version, [])
| Some ops -> (
let*! result =
List.filter_map_s User_hooks.on_new_operation ops
in
match result with
| [] -> loop ()
| l -> Lwt.return_some (version, List.map (fun x -> (x, None)) l))
in
loop ()
in
let shutdown () = () in
Tezos_rpc.Answer.{next; shutdown}
let rpc_context_callback block =
let* x = locate_block state block in
return x.rpc_context
let list_blocks ~heads ~length ~min_date:_ =
let compare_block_fitnesses block0 block1 =
Fitness.compare
block0.rpc_context.block_header.fitness
block1.rpc_context.block_header.fitness
in
let hash_of_block block = block.rpc_context.block_hash in
let lookup_head head =
let* xs = locate_blocks state (`Hash (head, 0)) in
let segment =
match length with None -> xs | Some n -> List.take_n n xs
in
return
(List.map hash_of_block (List.sort compare_block_fitnesses segment))
in
List.map_es lookup_head heads
let live_blocks block = live_blocks state block
let raw_protocol_data block =
let* x = locate_block state block in
return x.raw_protocol_data
end in
(module Impl)
(** Return the current head. *)
let head {chain; _} =
let open Lwt_result_syntax in
match List.hd chain with
| None -> failwith "mockup_simulator.ml: empty chain"
| Some hd -> return hd
(** Clear from the mempool operations whose branch does not point to
a live block with respect to the current head. *)
let clear_mempool state =
let open Lwt_result_syntax in
let* head = head state in
let included_ops_hashes =
List.map
(fun (op : Mockup.M.Block_services.operation) -> op.hash)
(List.flatten head.operations)
in
let* live_set = live_blocks state (`Head 0) in
let mempool =
List.filter
(fun (_oph, (op : Mockup.M.Protocol.operation)) ->
let included_in_head =
List.mem
~equal:Operation_hash.equal
(Alpha_context.Operation.hash_packed op)
included_ops_hashes
in
Block_hash.Set.mem op.shell.branch live_set && not included_in_head)
state.mempool
in
state.mempool <- mempool ;
return_unit
let begin_validation_and_application ctxt chain_id mode ~predecessor ~cache =
let open Lwt_result_syntax in
let* validation_state =
Mockup.M.Protocol.begin_validation ctxt chain_id mode ~predecessor ~cache
in
let* application_state =
Mockup.M.Protocol.begin_application ctxt chain_id mode ~predecessor ~cache
in
return (validation_state, application_state)
let validate_and_apply_operation (validation_state, application_state) oph op =
let open Lwt_result_syntax in
let* validation_state =
Mockup.M.Protocol.validate_operation validation_state oph op
in
let* application_state, receipt =
Mockup.M.Protocol.apply_operation application_state oph op
in
return ((validation_state, application_state), receipt)
let finalize_validation_and_application (validation_state, application_state)
=
let open Lwt_result_syntax in
let* () = Mockup.M.Protocol.finalize_validation validation_state in
Mockup.M.Protocol.finalize_application application_state shell_header
(** Apply a block to the given [rpc_context]. *)
let reconstruct_context (rpc_context : Tezos_protocol_environment.rpc_context)
(operations : Operation.t list list) ( : Block_header.t) =
let open Lwt_result_syntax in
let predecessor = rpc_context.block_header in
let predecessor_context = rpc_context.context in
let* protocol_data = parse_protocol_data block_header.protocol_data in
let* state =
begin_validation_and_application
predecessor_context
chain_id
(Application {shell = block_header.shell; protocol_data})
~predecessor
~cache:`Lazy
in
let i = ref 0 in
let* state, _ =
List.fold_left_es
(List.fold_left_es (fun (state, results) op ->
incr i ;
let oph = Operation.hash op in
let operation_data =
Data_encoding.Binary.of_bytes_exn
Mockup.M.Protocol.operation_data_encoding
op.Operation.proto
in
let op =
{
Mockup.M.Protocol.shell = op.shell;
protocol_data = operation_data;
}
in
let* state, receipt = validate_and_apply_operation state oph op in
return (state, receipt :: results)))
(state, [])
operations
in
finalize_validation_and_application state None
(** Process an incoming block. If validation succeeds:
- update the current head to this new block
- cleanup outdated operations
- cleanup listener table
Note that this implementation does not handle concurrent branches. *)
let rec process_block state block_hash ( : Block_header.t)
operations =
let open Lwt_result_syntax in
let get_predecessor () =
let predecessor_hash = block_header.Block_header.shell.predecessor in
let* head = head state in
match Block_hash.Table.find state.chain_table predecessor_hash with
| None | Some [] -> (
match
Block_hash.Table.find state.global_chain_table predecessor_hash
with
| None -> failwith "get_predecessor: unknown predecessor block"
| Some predecessor ->
let =
Block_header.
{
shell = predecessor.rpc_context.block_header;
protocol_data = predecessor.raw_protocol_data;
}
in
let predecessor_ops =
List.map
(fun xs ->
List.map
(fun (op : Mockup.M.Block_services.operation) ->
Operation.
{
shell = op.shell;
proto =
Data_encoding.Binary.to_bytes_exn
Protocol.operation_data_encoding
op.protocol_data;
})
xs)
predecessor.operations
in
let* () =
process_block
state
predecessor.rpc_context.block_hash
predecessor_block_header
predecessor_ops
in
return predecessor)
| Some (predecessor :: _) ->
if
Int32.sub
head.rpc_context.block_header.level
predecessor.rpc_context.block_header.level
<= 2l
then return predecessor
else failwith "get_predecessor: the predecessor block is too old"
in
match Block_hash.Table.find state.chain_table block_hash with
| Some _ ->
return_unit
| None ->
let* predecessor = get_predecessor () in
let* head = head state in
let* {context; message; _}, _ =
reconstruct_context predecessor.rpc_context operations block_header
in
let resulting_context_hash =
Tezos_context_ops.Context_ops.hash
~time:block_header.shell.timestamp
?message
context
in
let rpc_context =
Tezos_protocol_environment.
{context; block_hash; block_header = block_header.shell}
in
let operations =
List.map
(fun pass ->
List.map
(fun (Operation.{shell; proto} as op) ->
let hash : Operation_hash.t = Operation.hash op in
let protocol_data : Alpha_context.packed_protocol_data =
Data_encoding.Binary.of_bytes_exn
Protocol.operation_data_encoding
proto
in
{
Mockup.M.Block_services.chain_id;
hash;
shell;
protocol_data;
receipt = Empty;
})
pass)
operations
in
let* protocol_data = parse_protocol_data block_header.protocol_data in
let new_block =
{
rpc_context;
protocol_data;
raw_protocol_data = block_header.protocol_data;
operations;
resulting_context_hash;
}
in
let predecessor_hash = block_header.Block_header.shell.predecessor in
let tail =
Block_hash.Table.find state.chain_table predecessor_hash
|> WithExceptions.Option.get ~loc:__LOC__
in
let new_chain = new_block :: tail in
Block_hash.Table.replace state.chain_table block_hash new_chain ;
Block_hash.Table.replace state.global_chain_table block_hash new_block ;
Context_hash.Table.replace
state.ctxt_table
resulting_context_hash
rpc_context ;
if
Fitness.(
block_header.shell.fitness > head.rpc_context.block_header.fitness)
then (
state.chain <- new_chain ;
let* () = clear_mempool state in
state.operations_stream_push None ;
state.streaming_operations <- false ;
let operations_stream, operations_stream_push = Lwt_stream.create () in
state.operations_stream <- operations_stream ;
state.operations_stream_push <- operations_stream_push ;
state.operations_stream_push (Some state.mempool) ;
return_unit)
else return_unit
(** This process listens to broadcast block and operations and incorporates
them in the context of the fake node. *)
let rec listener ~(user_hooks : (module Hooks)) ~state ~broadcast_pipe =
let open Lwt_result_syntax in
let module User_hooks = (val user_hooks : Hooks) in
let*! result = Lwt_pipe.Unbounded.pop broadcast_pipe in
match result with
| Broadcast_op (operation_hash, packed_operation) ->
let* () =
if
List.mem_assoc
~equal:Operation_hash.equal
operation_hash
state.mempool
then return_unit
else (
state.mempool <- (operation_hash, packed_operation) :: state.mempool ;
state.operations_stream_push
(Some [(operation_hash, packed_operation)]) ;
User_hooks.check_mempool_after_processing ~mempool:state.mempool)
in
listener ~user_hooks ~state ~broadcast_pipe
| Broadcast_block (block_hash, , operations) ->
let* level = get_block_level block_header in
let* round = get_block_round block_header in
let* protocol_data = parse_protocol_data block_header.protocol_data in
let* () =
User_hooks.check_block_before_processing
~level
~round
~block_hash
~block_header
~protocol_data
in
let* () = process_block state block_hash block_header operations in
let* () =
User_hooks.check_chain_after_processing ~level ~round ~chain:state.chain
in
Lwt_pipe.Unbounded.push
state.validated_blocks_pipe
(block_hash, block_header, operations) ;
Lwt_pipe.Unbounded.push state.heads_pipe (block_hash, block_header) ;
listener ~user_hooks ~state ~broadcast_pipe
(** Create a fake node state. *)
let create_fake_node_state ~i ~live_depth
~(genesis_block : Block_header.t * Tezos_protocol_environment.rpc_context)
~global_chain_table ~broadcast_pipes =
let open Lwt_result_syntax in
let , rpc_context0 = genesis_block in
let* protocol_data = parse_protocol_data block_header0.protocol_data in
let genesis0 =
{
rpc_context = rpc_context0;
protocol_data;
raw_protocol_data = block_header0.protocol_data;
operations = [[]; []; []; []];
resulting_context_hash = block_header0.shell.context;
}
in
let chain0 = [genesis0] in
let validated_blocks_pipe = Lwt_pipe.Unbounded.create () in
let heads_pipe = Lwt_pipe.Unbounded.create () in
let operations_stream, operations_stream_push = Lwt_stream.create () in
let genesis_block_true_hash =
Block_header.hash
{
shell = rpc_context0.block_header;
protocol_data = block_header0.protocol_data;
}
in
Lwt_pipe.Unbounded.push heads_pipe (rpc_context0.block_hash, block_header0) ;
return
{
instance_index = i;
live_depth;
mempool = [];
chain = chain0;
chain_table =
Block_hash.Table.of_seq
(List.to_seq
[
(rpc_context0.block_hash, chain0);
(genesis_block_true_hash, chain0);
(genesis_predecessor_block_hash, chain0);
]);
global_chain_table;
ctxt_table =
Context_hash.Table.of_seq
(List.to_seq
[
( rpc_context0.Tezos_protocol_environment.block_header
.Block_header.context,
rpc_context0 );
]);
validated_blocks_pipe;
heads_pipe;
operations_stream;
operations_stream_push;
streaming_operations = false;
broadcast_pipes;
genesis_block_true_hash;
}
class tezt_printer : Tezos_client_base.Client_context.printer =
let open Tezos_client_base in
let open Client_context in
let wrap_tezt_log : (_ format4 -> _) -> _ format4 -> _ =
fun f x ->
Format.kasprintf
(fun msg ->
f "%s" msg ;
Lwt.return_unit)
x
in
object
method error : type a b. (a, b) lwt_format -> a =
Format.kasprintf (fun msg -> Lwt.fail (Failure msg))
method warning : type a. (a, unit) lwt_format -> a =
wrap_tezt_log Tezt_core.Log.warn
method message : type a. (a, unit) lwt_format -> a =
wrap_tezt_log (fun x -> Tezt_core.Log.info x)
method answer : type a. (a, unit) lwt_format -> a =
wrap_tezt_log (fun x -> Tezt_core.Log.info x)
method log : type a. string -> (a, unit) lwt_format -> a =
fun _log_output -> wrap_tezt_log (fun x -> Tezt_core.Log.info x)
end
(** Start baker process. *)
let baker_process ~(delegates : Baking_state.consensus_key list) ~base_dir
~(genesis_block : Block_header.t * Tezos_protocol_environment.rpc_context)
~i ~global_chain_table ~broadcast_pipes ~(user_hooks : (module Hooks)) =
let open Lwt_result_syntax in
let broadcast_pipe =
List.nth broadcast_pipes i |> WithExceptions.Option.get ~loc:__LOC__
in
let* state =
create_fake_node_state
~i
~live_depth:60
~genesis_block
~global_chain_table
~broadcast_pipes
in
let filesystem = String.Hashtbl.create 10 in
let wallet = new Faked_client_context.faked_io_wallet ~base_dir ~filesystem in
let cctxt =
let hooks = make_mocked_services_hooks state user_hooks in
new Protocol_client_context.wrap_full
(new Faked_client_context.unix_faked
~base_dir
~filesystem
~chain_id
~hooks)
in
let module User_hooks = (val user_hooks : Hooks) in
let*! () = User_hooks.on_start_baker ~baker_position:i ~delegates ~cctxt in
let* () =
List.iter_es
(fun ({alias; public_key; public_key_hash; secret_key_uri} :
Baking_state.consensus_key) ->
let open Tezos_client_base in
let name = alias |> WithExceptions.Option.get ~loc:__LOC__ in
let* public_key_uri = Client_keys.neuterize secret_key_uri in
Client_keys.register_key
wallet
~force:false
(public_key_hash, public_key_uri, secret_key_uri)
~public_key
name)
delegates
in
let context_index =
let open Abstract_context_index in
{
sync_fun = Lwt.return;
checkout_fun =
(fun hash ->
Context_hash.Table.find state.ctxt_table hash
|> Option.map (fun Tezos_protocol_environment.{context; _} -> context)
|> Lwt.return);
finalize_fun = Lwt.return;
}
in
let module User_hooks = (val user_hooks : Hooks) in
let listener_process () = listener ~user_hooks ~state ~broadcast_pipe in
let stop_on_event event = User_hooks.stop_on_event event in
let baker_process () =
Faked_daemon.Baker.run
~cctxt
~stop_on_event
~chain_id
~context_index
~delegates
in
let* () = Lwt.pick [listener_process (); baker_process ()] in
User_hooks.check_chain_on_success ~chain:state.chain
let genesis_protocol_data (baker_sk : Signature.secret_key)
(predecessor_hash : Block_hash.t) ( : Block_header.shell_header)
: Bytes.t =
let proof_of_work_nonce =
Bytes.create Protocol.Alpha_context.Constants.proof_of_work_nonce_size
in
let payload_hash =
Protocol.Alpha_context.Block_payload.hash
~predecessor_hash
~payload_round:Alpha_context.Round.zero
[]
in
let per_block_votes =
{
Protocol.Per_block_votes_repr.liquidity_baking_vote =
Baking_configuration.default_votes_config
.Baking_configuration.liquidity_baking_vote;
adaptive_issuance_vote =
Baking_configuration.default_votes_config
.Baking_configuration.adaptive_issuance_vote;
}
in
let contents =
Protocol.Alpha_context.Block_header.
{
payload_hash;
payload_round = Alpha_context.Round.zero;
proof_of_work_nonce;
seed_nonce_hash = None;
per_block_votes;
}
in
let =
Data_encoding.Binary.to_bytes_exn
Protocol.Alpha_context.Block_header.unsigned_encoding
(block_header, contents)
in
let signature =
Signature.sign
~watermark:
Alpha_context.Block_header.(to_watermark (Block_header chain_id))
baker_sk
unsigned_header
in
Data_encoding.Binary.to_bytes_exn
Protocol.Alpha_context.Block_header.protocol_data_encoding
{contents; signature}
(** Figure out who should be the signer for the genesis block. *)
let deduce_baker_sk
(accounts_with_secrets :
(Protocol.Alpha_context.Parameters.bootstrap_account
* Tezos_mockup_commands.Mockup_wallet.bootstrap_secret)
list) (total_accounts : int) (level : int) :
Signature.secret_key tzresult Lwt.t =
let open Lwt_result_syntax in
let* baker_index =
match (total_accounts, level) with
| _, 0 -> return 0
| _ ->
failwith
"cannot deduce baker for a genesis block, total accounts = %d, level \
= %d"
total_accounts
level
in
let _, secret =
List.nth accounts_with_secrets baker_index
|> WithExceptions.Option.get ~loc:__LOC__
in
let secret_key =
Signature.Secret_key.of_b58check_exn (Uri.path (secret.sk_uri :> Uri.t))
in
return secret_key
(** Generate the two initial genesis blocks. *)
let make_genesis_context ~delegate_selection ~initial_seed ~round0 ~round1
~consensus_committee_size ~consensus_threshold accounts_with_secrets
(total_accounts : int) =
let open Lwt_result_syntax in
let default_constants = Mockup.Protocol_parameters.default_value.constants in
let round_durations =
let open Alpha_context in
Stdlib.Option.get
(Round.Durations.create_opt
~first_round_duration:(Period.of_seconds_exn round0)
~delay_increment_per_round:
(Period.of_seconds_exn (Int64.sub round1 round0)))
in
let constants =
{
default_constants with
initial_seed;
consensus_committee_size;
consensus_threshold;
minimal_block_delay = Alpha_context.Period.of_seconds_exn (max 1L round0);
delay_increment_per_round =
Alpha_context.Period.of_seconds_exn Int64.(max 1L (sub round1 round0));
}
in
let from_bootstrap_account i
( (account : Protocol.Alpha_context.Parameters.bootstrap_account),
(secret : Tezos_mockup_commands.Mockup_wallet.bootstrap_secret) ) :
Mockup.Parsed_account.t =
{
name = Format.sprintf "bootstrap%d" (i + 1);
sk_uri = secret.sk_uri;
amount = account.amount;
}
in
let bootstrap_accounts =
Data_encoding.Json.construct
(Data_encoding.list Mockup.Parsed_account.encoding)
(List.mapi from_bootstrap_account accounts_with_secrets)
in
let*? delegate_selection =
let open Result_syntax in
List.map_e
(fun (level, round_delegates) ->
let* level = Raw_level_repr.of_int32 level in
let+ round_delegates =
List.map_e
(fun (round, delegate) ->
let+ round = Round_repr.of_int32 round in
(round, delegate))
round_delegates
in
(level, round_delegates))
delegate_selection
|> Environment.wrap_tzresult
in
let cctxt = new tezt_printer in
let* initial_seed =
match (delegate_selection, constants.initial_seed) with
| [], seed_opt -> return seed_opt
| selection, (Some _ as seed) -> (
let*! () = cctxt#message "Checking provided seed." in
let* result =
Tenderbrute.check_seed
~bootstrap_accounts_json:bootstrap_accounts
~parameters:
Mockup.Protocol_parameters.{default_value with constants}
~seed
selection
in
match result with
| true -> return seed
| false ->
failwith "Provided initial seed does not match delegate selection")
| _, None ->
let*! () = cctxt#message "No initial seed provided, bruteforcing." in
Tenderbrute.bruteforce
~max:100_000_000_000
~bootstrap_accounts_json:bootstrap_accounts
~parameters:Mockup.Protocol_parameters.{default_value with constants}
delegate_selection
in
let*! () =
match initial_seed with
| None -> Lwt.return_unit
| _ when initial_seed = constants.initial_seed -> Lwt.return_unit
| Some seed ->
cctxt#warning
"Bruteforced seed is %a, please save into your test."
State_hash.pp
seed
in
let constants = {constants with initial_seed} in
let common_parameters =
Mockup.Protocol_parameters.{default_value with constants}
in
let make_block0 initial_timestamp =
let parameters = {common_parameters with initial_timestamp} in
let reencoded_parameters =
Data_encoding.Binary.of_bytes_exn Mockup.M.parameters_encoding
@@ Data_encoding.Binary.to_bytes_exn
Mockup.Protocol_parameters.encoding
parameters
in
let* {chain = _; rpc_context = rpc_context0; protocol_data = _} =
Mockup.M.init
~cctxt
~parameters:reencoded_parameters
~constants_overrides_json:None
~bootstrap_accounts_json:(Some bootstrap_accounts)
in
let =
{
rpc_context0.block_header with
predecessor = genesis_predecessor_block_hash;
}
in
let rpc_context = {rpc_context0 with block_header = block_header0} in
let* baker_sk = deduce_baker_sk accounts_with_secrets total_accounts 0 in
let protocol_data =
genesis_protocol_data
baker_sk
genesis_predecessor_block_hash
rpc_context.block_header
in
let =
Block_header.{shell = rpc_context.block_header; protocol_data}
in
return (block_header, rpc_context)
in
let level0_round0_duration =
Protocol.Alpha_context.Round.round_duration
round_durations
Alpha_context.Round.zero
in
let timestamp0 =
Time.Protocol.of_seconds
Int64.(
sub
(of_float (Unix.time ()))
(Alpha_context.Period.to_seconds level0_round0_duration))
in
make_block0 timestamp0
(** By default, propagate every message everywhere. *)
let default_propagation_vector = List.repeat 5 Pass
module Default_hooks : Hooks = struct
let on_inject_block ~level:_ ~round:_ ~block_hash ~ ~operations
~protocol_data:_ =
let open Lwt_result_syntax in
return (block_hash, block_header, operations, default_propagation_vector)
let on_inject_operation ~op_hash ~op =
let open Lwt_result_syntax in
return (op_hash, op, default_propagation_vector)
let on_new_validated_block ~block_hash ~ ~operations =
Lwt.return_some (block_hash, block_header, operations)
let on_new_head ~block_hash ~ =
Lwt.return_some (block_hash, block_header)
let on_new_operation x = Lwt.return_some x
let check_block_before_processing ~level:_ ~round:_ ~block_hash:_
~block_header:_ ~protocol_data:_ =
Lwt_result_syntax.return_unit
let check_chain_after_processing ~level:_ ~round:_ ~chain:_ =
Lwt_result_syntax.return_unit
let check_mempool_after_processing ~mempool:_ = Lwt_result_syntax.return_unit
let stop_on_event _ = false
let on_start_baker ~baker_position:_ ~delegates:_ ~cctxt:_ = Lwt.return_unit
let check_chain_on_success ~chain:_ = Lwt_result_syntax.return_unit
end
type config = {
round0 : int64;
round1 : int64;
timeout : int;
delegate_selection : (int32 * (int32 * Signature.public_key_hash) list) list;
initial_seed : State_hash.t option;
consensus_committee_size : int;
consensus_threshold : int;
}
let default_config =
{
round0 = 2L;
round1 = 3L ;
timeout = 30;
delegate_selection = [];
initial_seed = None;
consensus_committee_size =
Default_parameters.constants_mainnet.consensus_committee_size;
consensus_threshold =
Default_parameters.constants_mainnet.consensus_threshold;
}
let make_baking_delegate
( (account : Alpha_context.Parameters.bootstrap_account),
(secret : Tezos_mockup_commands.Mockup_wallet.bootstrap_secret) ) :
Baking_state.consensus_key =
Baking_state.
{
alias = Some secret.name;
public_key = account.public_key |> WithExceptions.Option.get ~loc:__LOC__;
public_key_hash = account.public_key_hash;
secret_key_uri = secret.sk_uri;
}
let run ?(config = default_config) bakers_spec =
let open Lwt_result_syntax in
Tezos_client_base.Client_keys.register_signer
(module Tezos_signer_backends.Unencrypted) ;
let total_accounts =
List.fold_left (fun acc (n, _) -> acc + n) 0 bakers_spec
in
if total_accounts = 0 then
failwith "the simulation should use at least one delegate"
else if total_accounts > 5 then
failwith "only up to 5 bootstrap accounts are available"
else
let total_bakers = List.length bakers_spec in
let* broadcast_pipes =
List.init ~when_negative_length:() total_bakers (fun _ ->
Lwt_pipe.Unbounded.create ())
|> function
| Error () -> failwith "impossible: negative length of the baker spec"
| Ok xs -> return xs
in
let global_chain_table = Block_hash.Table.create 10 in
let* bootstrap_secrets =
Tezos_mockup_commands.Mockup_wallet.default_bootstrap_accounts
in
let accounts_with_secrets =
List.combine_drop (List.take_n total_accounts accounts) bootstrap_secrets
in
let all_delegates = List.map make_baking_delegate accounts_with_secrets in
let* genesis_block =
make_genesis_context
~delegate_selection:config.delegate_selection
~initial_seed:config.initial_seed
~round0:config.round0
~round1:config.round1
~consensus_committee_size:config.consensus_committee_size
~consensus_threshold:config.consensus_threshold
accounts_with_secrets
total_accounts
in
let take_third (_, _, x) = x in
let timeout_process () =
let*! () = Lwt_unix.sleep (Float.of_int config.timeout) in
failwith "the test is taking longer than %d seconds@." config.timeout
in
Lwt.pick
[
timeout_process ();
Lwt_result_syntax.tzjoin
(take_third
(List.fold_left
(fun (i, delegates_acc, ms) (n, user_hooks) ->
let delegates, leftover_delegates =
List.split_n n delegates_acc
in
let m =
baker_process
~delegates
~base_dir:"dummy"
~genesis_block
~i
~global_chain_table
~broadcast_pipes
~user_hooks
in
(i + 1, leftover_delegates, m :: ms))
(0, all_delegates, [])
bakers_spec));
]
let get_account_pk i =
match List.nth accounts i with
| None -> assert false
| Some acc -> acc.public_key |> WithExceptions.Option.get ~loc:__LOC__
let bootstrap1 = get_account_pk 0
let bootstrap2 = get_account_pk 1
let bootstrap3 = get_account_pk 2
let bootstrap4 = get_account_pk 3
let bootstrap5 = get_account_pk 4
let check_block_signature ~block_hash ~( : Block_header.t)
~public_key =
let open Lwt_result_syntax in
let (protocol_data : Protocol.Alpha_context.Block_header.protocol_data) =
Data_encoding.Binary.of_bytes_exn
Protocol.Alpha_context.Block_header.protocol_data_encoding
block_header.protocol_data
in
let =
Data_encoding.Binary.to_bytes_exn
Protocol.Alpha_context.Block_header.unsigned_encoding
(block_header.shell, protocol_data.contents)
in
if
Signature.check
~watermark:
Alpha_context.Block_header.(to_watermark (Block_header chain_id))
public_key
protocol_data.signature
unsigned_header
then return_unit
else
failwith
"unexpected signature for %a; tried with %a@."
Block_hash.pp
block_hash
Signature.Public_key.pp
public_key
type op_predicate =
Operation_hash.t -> Alpha_context.packed_operation -> bool tzresult Lwt.t
let mempool_count_ops ~mempool ~predicate =
let open Lwt_result_syntax in
let* results =
List.map_es (fun (op_hash, op) -> predicate op_hash op) mempool
in
return
(List.fold_left
(fun acc result -> if result then acc + 1 else acc)
0
results)
let mempool_has_op ~mempool ~predicate =
let open Lwt_result_syntax in
let* n = mempool_count_ops ~mempool ~predicate in
return (n > 0)
let mempool_has_op_ref ~mempool ~predicate ~var =
let open Lwt_result_syntax in
let* result = mempool_has_op ~mempool ~predicate in
if result then var := true ;
return_unit
let op_is_signed_by ~public_key (op_hash : Operation_hash.t)
(op : Alpha_context.packed_operation) =
let open Lwt_result_syntax in
match op.protocol_data with
| Operation_data d -> (
let* watermark =
match d.contents with
| Single op_contents ->
return
(match op_contents with
| Attestation _ ->
Alpha_context.Operation.to_watermark (Attestation chain_id)
| Preattestation _ ->
Alpha_context.Operation.to_watermark (Preattestation chain_id)
| _ -> Signature.Generic_operation)
| _ -> failwith "unexpected contents in %a@." Operation_hash.pp op_hash
in
match d.signature with
| None ->
failwith
"did not find a signature for op %a@."
Operation_hash.pp
op_hash
| Some signature ->
let unsigned_operation_bytes =
Data_encoding.Binary.to_bytes_exn
Protocol.Alpha_context.Operation.unsigned_encoding
(op.shell, Contents_list d.contents)
in
return
(Signature.check
~watermark
public_key
signature
unsigned_operation_bytes))
let op_is_preattestation ?level ?round (op_hash : Operation_hash.t)
(op : Alpha_context.packed_operation) =
let open Lwt_result_syntax in
match op.protocol_data with
| Operation_data d -> (
match d.contents with
| Single op_contents -> (
match op_contents with
| Preattestation consensus_content ->
let right_level =
match level with
| None -> true
| Some expected_level ->
Int32.equal
(Alpha_context.Raw_level.to_int32 consensus_content.level)
expected_level
in
let right_round =
match round with
| None -> true
| Some expected_round ->
Int32.equal
(Alpha_context.Round.to_int32 consensus_content.round)
expected_round
in
return (right_level && right_round)
| _ -> return_false)
| _ -> failwith "unexpected contents in %a@." Operation_hash.pp op_hash)
let op_is_attestation ?level ?round (op_hash : Operation_hash.t)
(op : Alpha_context.packed_operation) =
let open Lwt_result_syntax in
match op.protocol_data with
| Operation_data d -> (
match d.contents with
| Single op_contents -> (
match op_contents with
| Attestation {consensus_content; _} ->
let right_level =
match level with
| None -> true
| Some expected_level ->
Int32.equal
(Alpha_context.Raw_level.to_int32 consensus_content.level)
expected_level
in
let right_round =
match round with
| None -> true
| Some expected_round ->
Int32.equal
(Alpha_context.Round.to_int32 consensus_content.round)
expected_round
in
return (right_level && right_round)
| _ -> return_false)
| _ -> failwith "unexpected contents in %a@." Operation_hash.pp op_hash)
let op_is_both f g op_hash op =
let open Lwt_result_syntax in
let* f_result = f op_hash op in
if f_result then g op_hash op else return_false
let save_proposal_payload
~(protocol_data : Alpha_context.Block_header.protocol_data) ~var =
let open Lwt_result_syntax in
var :=
Some
(protocol_data.contents.payload_hash, protocol_data.contents.payload_round) ;
return_unit
let verify_payload_hash
~(protocol_data : Alpha_context.Block_header.protocol_data)
~original_proposal ~message =
let open Lwt_result_syntax in
match !original_proposal with
| None ->
failwith
"verify_payload_hash: expected to have observed a proposal by now"
| Some (original_hash, original_round) ->
if
Protocol.Block_payload_hash.equal
original_hash
protocol_data.contents.payload_hash
&& Protocol.Alpha_context.Round.equal
original_round
protocol_data.contents.payload_round
then return_unit
else failwith "verify_payload_hash: %s" message
let get_block_round block =
round_from_raw_fitness block.rpc_context.block_header.fitness