• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bernardladenthin / BitcoinAddressFinder / #353

21 Jul 2025 10:01PM UTC coverage: 71.342% (+1.2%) from 70.173%
#353

push

bernardladenthin
Implement ZeroMQ instead raw socket. Add and fix some tests.

43 of 47 new or added lines in 4 files covered. (91.49%)

4 existing lines in 2 files now uncovered.

1536 of 2153 relevant lines covered (71.34%)

0.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.39
/src/main/java/net/ladenthin/bitcoinaddressfinder/Finder.java
1
// @formatter:off
2
/**
3
 * Copyright 2020 Bernard Ladenthin bernard.ladenthin@gmail.com
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *    http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
// @formatter:on
19
package net.ladenthin.bitcoinaddressfinder;
20

21
import com.google.common.annotations.VisibleForTesting;
22
import com.google.common.collect.ImmutableMap;
23
import java.time.Duration;
24
import java.time.temporal.ChronoUnit;
25
import java.util.ArrayList;
26
import java.util.Collection;
27
import java.util.HashMap;
28
import java.util.List;
29
import java.util.Map;
30
import java.util.concurrent.ExecutorService;
31
import java.util.concurrent.Executors;
32
import java.util.concurrent.TimeUnit;
33
import net.ladenthin.bitcoinaddressfinder.configuration.CFinder;
34
import net.ladenthin.bitcoinaddressfinder.configuration.CProducer;
35
import net.ladenthin.bitcoinaddressfinder.persistence.PersistenceUtils;
36
import org.bitcoinj.base.Network;
37
import org.jspecify.annotations.Nullable;
38
import org.slf4j.Logger;
39
import org.slf4j.LoggerFactory;
40
import java.util.function.*;
41

42
public class Finder implements Interruptable {
43

44
    /**
45
     * We must define a maximum time to wait for terminate. Wait for 100 thousand years is enough.
46
     */
47
    @VisibleForTesting
48
    static Duration AWAIT_DURATION_TERMINATE = Duration.ofDays(365L * 1000L);
1✔
49
    
50
    protected Logger logger = LoggerFactory.getLogger(this.getClass());
1✔
51
    
52
    private final CFinder finder;
53
    
54
    private final Map<String, KeyProducer> keyProducers = new HashMap<>();
1✔
55
    
56
    @Nullable
57
    private ConsumerJava consumerJava;
58
    
59
    private final List<ProducerOpenCL> openCLProducers = new ArrayList<>();
1✔
60
    private final List<ProducerJava> javaProducers = new ArrayList<>();
1✔
61
    private final List<ProducerJavaSecretsFiles> javaProducersSecretsFiles = new ArrayList<>();
1✔
62
    
63
    @VisibleForTesting
1✔
64
    final ExecutorService producerExecutorService = Executors.newCachedThreadPool();
1✔
65
    
66
    private final Network network = new NetworkParameterFactory().getNetwork();
1✔
67
    private final KeyUtility keyUtility = new KeyUtility(network, new ByteBufferUtility(false));
1✔
68
    private final PersistenceUtils persistenceUtils = new PersistenceUtils(network);
1✔
69
    private final BitHelper bitHelper = new BitHelper();
1✔
70
    
71
    public Finder(CFinder finder) {
1✔
72
        this.finder = finder;
1✔
73
    }
1✔
74
    
75
    public void startKeyProducer() {
76
        logger.info("startKeyProducer");
1✔
77
        processKeyProducers(
1✔
78
            finder.keyProducerJavaRandom,
79
            cKeyProducerJavaRandom -> new KeyProducerJavaRandom(cKeyProducerJavaRandom, keyUtility, bitHelper),
1✔
80
            cKeyProducerJavaRandom -> cKeyProducerJavaRandom.keyProducerId,
1✔
81
            keyProducers
82
        );
83

84
        processKeyProducers(
1✔
85
            finder.keyProducerJavaBip39,
86
            cKeyProducerJavaBip39 -> new KeyProducerJavaBip39(cKeyProducerJavaBip39, keyUtility, bitHelper),
1✔
87
            cKeyProducerJavaBip39 -> cKeyProducerJavaBip39.keyProducerId,
1✔
88
            keyProducers
89
        );
90

91
        processKeyProducers(
1✔
92
            finder.keyProducerJavaIncremental,
93
            cKeyProducerJavaIncremental -> new KeyProducerJavaIncremental(cKeyProducerJavaIncremental, keyUtility, bitHelper),
1✔
94
            cKeyProducerJavaIncremental -> cKeyProducerJavaIncremental.keyProducerId,
1✔
95
            keyProducers
96
        );
97

98
        processKeyProducers(
1✔
99
            finder.keyProducerJavaZmq,
100
            cKeyProducerJavaZmq -> new KeyProducerJavaZmq(cKeyProducerJavaZmq, keyUtility, bitHelper),
1✔
101
            cKeyProducerJavaZmq -> cKeyProducerJavaZmq.keyProducerId,
1✔
102
            keyProducers
103
        );
104
    }
1✔
105
    
106
    private <T, K> void processKeyProducers(
107
        List<T> configList,
108
        Function<T, K> constructor,
109
        Function<T, String> getId,
110
        Map<String, K> keyProducers
111
    ) {
112
        if (configList != null) {
1✔
113
            for (T config : configList) {
1✔
114
                String keyProducerId = getId.apply(config);
1✔
115
                if (keyProducerId == null) {
1✔
116
                    throw new KeyProducerIdNullException();
1✔
117
                }
118
                if (keyProducers.containsKey(keyProducerId)) {
1✔
119
                    throw new KeyProducerIdIsNotUniqueException(keyProducerId);
1✔
120
                }
121
                K keyProducer = constructor.apply(config);
1✔
122
                keyProducers.put(keyProducerId, keyProducer);
1✔
123
            }
1✔
124
        }
125
    }
1✔
126

127
    public void startConsumer() {
128
        logger.info("startConsumer");
1✔
129
        if (finder.consumerJava != null) {
1✔
130
            consumerJava = new ConsumerJava(finder.consumerJava, keyUtility, persistenceUtils);
1✔
131
            consumerJava.initLMDB();
1✔
132
            consumerJava.startConsumer();
1✔
133
            consumerJava.startStatisticsTimer();
1✔
134
        }
135
    }
1✔
136

137
    public void configureProducer() {
138
        logger.info("configureProducer");
1✔
139
        processProducers(
1✔
140
            finder.producerJava,
141
            bitHelper::assertBatchSizeInBitsIsInRange,
1✔
142
            this::getKeyProducer,
143
            (config, keyProducer) -> new ProducerJava(config, consumerJava, keyUtility, keyProducer, bitHelper),
1✔
144
            javaProducers
145
        );
146

147
        processProducers(
1✔
148
            finder.producerJavaSecretsFiles,
149
            bitHelper::assertBatchSizeInBitsIsInRange,
1✔
150
            this::getKeyProducer,
151
            (config, keyProducer) -> new ProducerJavaSecretsFiles(config, consumerJava, keyUtility, keyProducer, bitHelper),
1✔
152
            javaProducersSecretsFiles
153
        );
154

155
        processProducers(
1✔
156
            finder.producerOpenCL,
157
            bitHelper::assertBatchSizeInBitsIsInRange,
1✔
158
            this::getKeyProducer,
159
            (config, keyProducer) -> new ProducerOpenCL(config, consumerJava, keyUtility, keyProducer, bitHelper),
1✔
160
            openCLProducers
161
        );
162
    }
1✔
163
    
164
    private <T extends CProducer, P> void processProducers(
165
        List<T> configs,
166
        java.util.function.Consumer<Integer> batchSizeAssert,
167
        Function<T, KeyProducer> getKeyProducer,
168
        BiFunction<T, KeyProducer, P> producerConstructor,
169
        Collection<P> targetCollection
170
    ) {
171
        if (configs != null) {
1✔
172
            for (T config : configs) {
1✔
173
                batchSizeAssert.accept(config.batchSizeInBits);
1✔
174
                KeyProducer keyProducer = getKeyProducer.apply(config);
1✔
175
                P producer = producerConstructor.apply(config, keyProducer);
1✔
176
                targetCollection.add(producer);
1✔
177
            }
1✔
178
        }
179
    }
1✔
180

181
    public KeyProducer getKeyProducer(CProducer cProducer) throws RuntimeException {
182
        KeyProducer keyProducer = keyProducers.get(cProducer.keyProducerId);
1✔
183
        if(keyProducer == null) {
1✔
184
            throw new KeyProducerIdUnknownException(cProducer.keyProducerId);
1✔
185
        }
186
        return keyProducer;
1✔
187
    }
188
    
189
    public void initProducer() {
190
        logger.info("initProducer");
1✔
191
        for (Producer producer : getAllProducers()) {
1✔
192
            producer.initProducer();
1✔
193
        }
1✔
194
    }
1✔
195
    
196
    public void startProducer() {
197
        logger.info("startProducer");
1✔
198
        for (Producer producer : getAllProducers()) {
1✔
199
            producerExecutorService.submit(producer);
1✔
200
        }
1✔
201
    }
1✔
202
    
203
    public void shutdownAndAwaitTermination() {
204
        logger.info("shutdownAndAwaitTermination");
1✔
205
        try {
206
            producerExecutorService.shutdown();
1✔
207
            producerExecutorService.awaitTermination(AWAIT_DURATION_TERMINATE.get(ChronoUnit.SECONDS), TimeUnit.SECONDS);
1✔
UNCOV
208
        } catch (InterruptedException ex) {
×
UNCOV
209
            throw new RuntimeException(ex);
×
210
        }
1✔
211
        
212
        // no producers are running anymore, the consumer can be interrupted
213
        if (consumerJava != null) {
1✔
214
            logger.info("Interrupt: " + consumerJava.toString());
1✔
215
            consumerJava.interrupt();
1✔
216
            consumerJava = null;
1✔
217
        }
218
        logger.info("consumerJava released.");
1✔
219
    }
1✔
220
    
221
    @Override
222
    public void interrupt() {
223
        logger.info("interrupt called: delegate interrupt to all keyProducers and producers");
1✔
224
        
225
        // Interrupt all Producers
226
        for (Producer producer : getAllProducers()) {
1✔
227
            logger.info("Interrupt Producer: " + producer.toString());
1✔
228
            producer.interrupt();
1✔
229
            logger.info("waitTillProducerNotRunning ...");
1✔
230
            producer.waitTillProducerNotRunning();
1✔
231
            producer.releaseProducer();
1✔
232
        }
1✔
233
        freeAllProducers();
1✔
234
        
235
        // Interrupt all KeyProducers
236
        for (KeyProducer keyProducer : getKeyProducers().values()) {
1✔
237
            logger.info("Interrupt KeyProducer: " + keyProducer.toString());
1✔
238
            keyProducer.interrupt();
1✔
239
        }
1✔
240
        freeAllKeyProducers();
1✔
241

242
        logger.info("All producers released and freed.");
1✔
243
    }
1✔
244
    
245
    public Map<String, KeyProducer> getKeyProducers() {
246
        return ImmutableMap.copyOf(keyProducers);
1✔
247
    }
248
    
249
    public List<Producer> getAllProducers() {
250
        List<Producer> producers = new ArrayList<>();
1✔
251
        producers.addAll(javaProducers);
1✔
252
        producers.addAll(javaProducersSecretsFiles);
1✔
253
        producers.addAll(openCLProducers);
1✔
254
        return producers;
1✔
255
    }
256
    
257
    public void freeAllProducers() {
258
        javaProducers.clear();
1✔
259
        javaProducersSecretsFiles.clear();
1✔
260
        openCLProducers.clear();
1✔
261
    }
1✔
262
    
263
    public void freeAllKeyProducers() {
264
        keyProducers.clear();
1✔
265
    }
1✔
266
    
267
    public List<Consumer> getAllConsumers() {
268
        List<Consumer> consumers = new ArrayList<>();
1✔
269
        if (consumerJava != null) {
1✔
270
            consumers.add(consumerJava);
1✔
271
        }
272
        return consumers;
1✔
273
    }
274
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc