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