1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the Apache 2.0 License.
3
+
4
+ #include " ccf/historical_queries_utils.h"
5
+
6
+ #include " ccf/rpc_context.h"
7
+ #include " ccf/service/tables/service.h"
8
+ #include " kv/kv_types.h"
9
+ #include " node/tx_receipt_impl.h"
10
+
11
+ namespace ccf
12
+ {
13
+ static std::map<crypto::Pem, std::vector<crypto::Pem>>
14
+ service_endorsement_cache;
15
+
16
+ namespace historical
17
+ {
18
+ std::optional<ServiceInfo> find_previous_service_identity (
19
+ kv::ReadOnlyTx& tx,
20
+ ccf::historical::StatePtr& state,
21
+ AbstractStateCache& state_cache)
22
+ {
23
+ SeqNo target_seqno = state->transaction_id .seqno ;
24
+
25
+ // We start at the previous write to the latest (current) service info.
26
+ auto service = tx.template ro <Service>(Tables::SERVICE);
27
+
28
+ // Iterate until we find the most recent write to the service info that
29
+ // precedes the target seqno.
30
+ std::optional<ServiceInfo> hservice_info = service->get ();
31
+ SeqNo i = -1 ;
32
+ do
33
+ {
34
+ if (!hservice_info->previous_service_identity_version )
35
+ {
36
+ // Pre 2.0 we did not record the versions of previous identities in
37
+ // the service table.
38
+ throw std::runtime_error (
39
+ " The service identity that signed the receipt cannot be found "
40
+ " because it is in a pre-2.0 part of the ledger." );
41
+ }
42
+ i = hservice_info->previous_service_identity_version .value_or (i - 1 );
43
+ LOG_TRACE_FMT (" historical service identity search at: {}" , i);
44
+ auto hstate = state_cache.get_state_at (i, i);
45
+ if (!hstate)
46
+ {
47
+ return std::nullopt; // Not available yet - retry later.
48
+ }
49
+ auto htx = hstate->store ->create_read_only_tx ();
50
+ auto hservice = htx.ro <Service>(Tables::SERVICE);
51
+ hservice_info = hservice->get ();
52
+ } while (i > target_seqno || (i > 1 && !hservice_info));
53
+
54
+ if (!hservice_info)
55
+ {
56
+ throw std::runtime_error (" Failed to locate previous service identity" );
57
+ }
58
+
59
+ return hservice_info;
60
+ }
61
+
62
+ bool populate_service_endorsements (
63
+ kv::ReadOnlyTx& tx,
64
+ ccf::historical::StatePtr& state,
65
+ AbstractStateCache& state_cache,
66
+ std::shared_ptr<NetworkIdentitySubsystemInterface>
67
+ network_identity_subsystem)
68
+ {
69
+ try
70
+ {
71
+ if (!network_identity_subsystem)
72
+ {
73
+ throw std::runtime_error (
74
+ " The service identity endorsement for this receipt cannot be "
75
+ " created "
76
+ " because the current network identity is not available." );
77
+ }
78
+
79
+ const auto & network_identity = network_identity_subsystem->get ();
80
+
81
+ if (state && state->receipt && state->receipt ->node_cert )
82
+ {
83
+ auto & receipt = *state->receipt ;
84
+
85
+ if (receipt.node_cert ->empty ())
86
+ {
87
+ // Pre 2.0 receipts did not contain node certs.
88
+ throw std::runtime_error (
89
+ " Node certificate in receipt is empty, likely because the "
90
+ " transaction is in a pre-2.0 part of the ledger." );
91
+ }
92
+
93
+ auto v = crypto::make_unique_verifier (*receipt.node_cert );
94
+ if (!v->verify_certificate (
95
+ {&network_identity->cert }, {}, /* ignore_time */ true ))
96
+ {
97
+ // The current service identity does not endorse the node
98
+ // certificate in the receipt, so we search for the the most recent
99
+ // write to the service info table before the historical transaction
100
+ // ID to get the historical service identity.
101
+
102
+ auto opt_psi =
103
+ find_previous_service_identity (tx, state, state_cache);
104
+ if (!opt_psi)
105
+ {
106
+ return false ;
107
+ }
108
+
109
+ auto hpubkey = crypto::public_key_pem_from_cert (
110
+ crypto::cert_pem_to_der (opt_psi->cert ));
111
+
112
+ auto eit = service_endorsement_cache.find (hpubkey);
113
+ if (eit != service_endorsement_cache.end ())
114
+ {
115
+ // Note: validity period of service certificate may have changed
116
+ // since we created the cached endorsements.
117
+ receipt.service_endorsements = eit->second ;
118
+ }
119
+ else
120
+ {
121
+ auto ncv = crypto::make_unique_verifier (network_identity->cert );
122
+ auto endorsement = create_endorsed_cert (
123
+ hpubkey,
124
+ ReplicatedNetworkIdentity::subject_name,
125
+ {},
126
+ ncv->validity_period (),
127
+ network_identity->priv_key ,
128
+ network_identity->cert ,
129
+ true );
130
+ service_endorsement_cache[hpubkey] = {endorsement};
131
+ receipt.service_endorsements = {endorsement};
132
+ }
133
+ }
134
+ }
135
+ }
136
+ catch (std::exception & ex)
137
+ {
138
+ LOG_DEBUG_FMT (
139
+ " Exception while extracting previous service identities: {}" ,
140
+ ex.what ());
141
+ // (We keep the incomplete receipt, no further error reporting)
142
+ }
143
+
144
+ return true ;
145
+ }
146
+ }
147
+ }
0 commit comments