Line data Source code
1 : // Copyright (c) 2018 The Dash Core developers
2 : // Copyright (c) 2021 The PIVX Core developers
3 : // Distributed under the MIT/X11 software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #ifndef PIVX_BLS_BLS_WORKER_H
7 : #define PIVX_BLS_BLS_WORKER_H
8 :
9 : #include "bls/bls_wrapper.h"
10 : #include "ctpl_stl.h"
11 :
12 : #include <future>
13 : #include <mutex>
14 :
15 : // Low level BLS/DKG stuff. All very compute intensive and optimized for parallelization
16 : // The worker tries to parallelize as much as possible and utilizes a few properties of BLS aggregation to speed up things
17 : // For example, public key vectors can be aggregated in parallel if they are split into batches and the batched aggregations are
18 : // aggregated to a final public key. This utilizes that when aggregating keys (a+b+c+d) gives the same result as (a+b)+(c+d)
19 : class CBLSWorker
20 : {
21 : public:
22 : typedef std::function<void(const CBLSSignature&)> SignDoneCallback;
23 : typedef std::function<void(bool)> SigVerifyDoneCallback;
24 : typedef std::function<bool()> CancelCond;
25 :
26 : private:
27 : ctpl::thread_pool workerPool;
28 :
29 : static const int SIG_VERIFY_BATCH_SIZE = 8;
30 : struct SigVerifyJob {
31 : SigVerifyDoneCallback doneCallback;
32 : CancelCond cancelCond;
33 : CBLSSignature sig;
34 : CBLSPublicKey pubKey;
35 : uint256 msgHash;
36 0 : SigVerifyJob(SigVerifyDoneCallback&& _doneCallback, CancelCond&& _cancelCond, const CBLSSignature& _sig, const CBLSPublicKey& _pubKey, const uint256& _msgHash) :
37 : doneCallback(_doneCallback),
38 : cancelCond(_cancelCond),
39 : sig(_sig),
40 : pubKey(_pubKey),
41 0 : msgHash(_msgHash)
42 : {
43 0 : }
44 : };
45 :
46 : std::mutex sigVerifyMutex;
47 : int sigVerifyBatchesInProgress{0};
48 : std::vector<SigVerifyJob> sigVerifyQueue;
49 :
50 : public:
51 : CBLSWorker();
52 : ~CBLSWorker();
53 :
54 : void Start();
55 : void Stop();
56 :
57 : bool GenerateContributions(int threshold, const BLSIdVector& ids, BLSVerificationVectorPtr& vvecRet, BLSSecretKeyVector& skShares);
58 :
59 : // The following functions are all used to aggregate verification (public key) vectors
60 : // Inputs are in the following form:
61 : // [
62 : // [a1, b1, c1, d1],
63 : // [a2, b2, c2, d2],
64 : // [a3, b3, c3, d3],
65 : // [a4, b4, c4, d4],
66 : // ]
67 : // The result is in the following form:
68 : // [ a1+a2+a3+a4, b1+b2+b3+b4, c1+c2+c3+c4, d1+d2+d3+d4]
69 : // Multiple things can be parallelized here. For example, all 4 entries in the result vector can be calculated in parallel
70 : // Also, each individual vector can be split into multiple batches and aggregating the batches can also be paralellized.
71 : void AsyncBuildQuorumVerificationVector(const std::vector<BLSVerificationVectorPtr>& vvecs,
72 : size_t start, size_t count, bool parallel,
73 : std::function<void(const BLSVerificationVectorPtr&)> doneCallback);
74 : std::future<BLSVerificationVectorPtr> AsyncBuildQuorumVerificationVector(const std::vector<BLSVerificationVectorPtr>& vvecs,
75 : size_t start, size_t count, bool parallel);
76 : BLSVerificationVectorPtr BuildQuorumVerificationVector(const std::vector<BLSVerificationVectorPtr>& vvecs,
77 : size_t start = 0, size_t count = 0, bool parallel = true);
78 :
79 : // The following functions are all used to aggregate single vectors
80 : // Inputs are in the following form:
81 : // [a, b, c, d],
82 : // The result is simply a+b+c+d
83 : // Aggregation is paralellized by splitting up the input vector into multiple batches and then aggregating the individual batch results
84 : void AsyncAggregateSecretKeys(const BLSSecretKeyVector& secKeys,
85 : size_t start, size_t count, bool parallel,
86 : std::function<void(const CBLSSecretKey&)> doneCallback);
87 : std::future<CBLSSecretKey> AsyncAggregateSecretKeys(const BLSSecretKeyVector& secKeys,
88 : size_t start, size_t count, bool parallel);
89 : CBLSSecretKey AggregateSecretKeys(const BLSSecretKeyVector& secKeys, size_t start = 0, size_t count = 0, bool parallel = true);
90 :
91 : void AsyncAggregatePublicKeys(const BLSPublicKeyVector& pubKeys,
92 : size_t start, size_t count, bool parallel,
93 : std::function<void(const CBLSPublicKey&)> doneCallback);
94 : std::future<CBLSPublicKey> AsyncAggregatePublicKeys(const BLSPublicKeyVector& pubKeys,
95 : size_t start, size_t count, bool parallel);
96 : CBLSPublicKey AggregatePublicKeys(const BLSPublicKeyVector& pubKeys, size_t start = 0, size_t count = 0, bool parallel = true);
97 :
98 : void AsyncAggregateSigs(const BLSSignatureVector& sigs,
99 : size_t start, size_t count, bool parallel,
100 : std::function<void(const CBLSSignature&)> doneCallback);
101 : std::future<CBLSSignature> AsyncAggregateSigs(const BLSSignatureVector& sigs,
102 : size_t start, size_t count, bool parallel);
103 : CBLSSignature AggregateSigs(const BLSSignatureVector& sigs, size_t start = 0, size_t count = 0, bool parallel = true);
104 :
105 :
106 : // Calculate public key share from public key vector and id. Not parallelized
107 : CBLSPublicKey BuildPubKeyShare(const BLSVerificationVectorPtr& vvec, const CBLSId& id);
108 :
109 : // The following functions verify multiple verification vectors and contributions for the same id
110 : // This is parallelized by performing batched verification. The verification vectors and the contributions of
111 : // a batch are aggregated (in parallel, see AsyncBuildQuorumVerificationVector and AsyncBuildSecretKeyShare). The
112 : // result per batch is a single aggregated verification vector and a single aggregated contribution, which are then
113 : // verified with VerifyContributionShare. If verification of the aggregated inputs is successful, the whole batch
114 : // is marked as valid. If the batch verification fails, the individual entries are verified in a non-aggregated manner
115 : void AsyncVerifyContributionShares(const CBLSId& forId, const std::vector<BLSVerificationVectorPtr>& vvecs, const BLSSecretKeyVector& skShares,
116 : bool parallel, bool aggregated, std::function<void(const std::vector<bool>&)> doneCallback);
117 : std::future<std::vector<bool> > AsyncVerifyContributionShares(const CBLSId& forId, const std::vector<BLSVerificationVectorPtr>& vvecs, const BLSSecretKeyVector& skShares,
118 : bool parallel, bool aggregated);
119 : std::vector<bool> VerifyContributionShares(const CBLSId& forId, const std::vector<BLSVerificationVectorPtr>& vvecs, const BLSSecretKeyVector& skShares,
120 : bool parallel = true, bool aggregated = true);
121 :
122 : std::future<bool> AsyncVerifyContributionShare(const CBLSId& forId, const BLSVerificationVectorPtr& vvec, const CBLSSecretKey& skContribution);
123 :
124 : // Non paralellized verification of a single contribution
125 : bool VerifyContributionShare(const CBLSId& forId, const BLSVerificationVectorPtr& vvec, const CBLSSecretKey& skContribution);
126 :
127 : // Simple verification of vectors. Checks x.IsValid() for every entry and checks for duplicate entries
128 : bool VerifyVerificationVector(const BLSVerificationVector& vvec, size_t start = 0, size_t count = 0);
129 : bool VerifyVerificationVectors(const std::vector<BLSVerificationVectorPtr>& vvecs, size_t start = 0, size_t count = 0);
130 : bool VerifySecretKeyVector(const BLSSecretKeyVector& secKeys, size_t start = 0, size_t count = 0);
131 : bool VerifySignatureVector(const BLSSignatureVector& sigs, size_t start = 0, size_t count = 0);
132 :
133 : // Internally batched signature signing and verification
134 : void AsyncSign(const CBLSSecretKey& secKey, const uint256& msgHash, SignDoneCallback doneCallback);
135 : std::future<CBLSSignature> AsyncSign(const CBLSSecretKey& secKey, const uint256& msgHash);
136 : void AsyncVerifySig(const CBLSSignature& sig, const CBLSPublicKey& pubKey, const uint256& msgHash, SigVerifyDoneCallback doneCallback, CancelCond cancelCond = [] { return false; });
137 : std::future<bool> AsyncVerifySig(const CBLSSignature& sig, const CBLSPublicKey& pubKey, const uint256& msgHash, CancelCond cancelCond = [] { return false; });
138 : bool IsAsyncVerifyInProgress();
139 :
140 : private:
141 : void PushSigVerifyBatch();
142 : };
143 :
144 : // Builds and caches different things from CBLSWorker
145 : // Cache keys are provided externally as computing hashes on BLS vectors is too expensive
146 : // If multiple threads try to build the same thing at the same time, only one will actually build it
147 : // and the other ones will wait for the result of the first caller
148 : class CBLSWorkerCache
149 : {
150 : private:
151 : CBLSWorker& worker;
152 :
153 : std::mutex cacheCs;
154 : std::map<uint256, std::shared_future<BLSVerificationVectorPtr> > vvecCache;
155 : std::map<uint256, std::shared_future<CBLSSecretKey> > secretKeyShareCache;
156 : std::map<uint256, std::shared_future<CBLSPublicKey> > publicKeyShareCache;
157 :
158 : public:
159 1127 : explicit CBLSWorkerCache(CBLSWorker& _worker) :
160 1127 : worker(_worker) {}
161 :
162 215 : BLSVerificationVectorPtr BuildQuorumVerificationVector(const uint256& cacheKey, const std::vector<BLSVerificationVectorPtr>& vvecs)
163 : {
164 272 : return GetOrBuild(cacheKey, vvecCache, [&]() {
165 57 : return worker.BuildQuorumVerificationVector(vvecs);
166 215 : });
167 : }
168 56 : CBLSSecretKey AggregateSecretKeys(const uint256& cacheKey, const BLSSecretKeyVector& skShares)
169 : {
170 112 : return GetOrBuild(cacheKey, secretKeyShareCache, [&]() {
171 56 : return worker.AggregateSecretKeys(skShares);
172 56 : });
173 : }
174 929 : CBLSPublicKey BuildPubKeyShare(const uint256& cacheKey, const BLSVerificationVectorPtr& vvec, const CBLSId& id)
175 : {
176 1239 : return GetOrBuild(cacheKey, publicKeyShareCache, [&]() {
177 310 : return worker.BuildPubKeyShare(vvec, id);
178 929 : });
179 : }
180 :
181 : private:
182 : template <typename T, typename Builder>
183 1200 : T GetOrBuild(const uint256& cacheKey, std::map<uint256, std::shared_future<T> >& cache, Builder&& builder)
184 : {
185 1200 : cacheCs.lock();
186 1200 : auto it = cache.find(cacheKey);
187 1200 : if (it != cache.end()) {
188 1554 : auto f = it->second;
189 1554 : cacheCs.unlock();
190 777 : return f.get();
191 : }
192 :
193 423 : std::promise<T> p;
194 423 : cache.emplace(cacheKey, p.get_future());
195 846 : cacheCs.unlock();
196 :
197 423 : T v = builder();
198 423 : p.set_value(v);
199 423 : return v;
200 : }
201 : };
202 :
203 : #endif // PIVX_BLS_BLS_WORKER_H
|