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

bernardladenthin / BitcoinAddressFinder / #349

17 Jul 2025 09:52PM UTC coverage: 69.883% (+0.3%) from 69.604%
#349

push

bernardladenthin
refactor: move BIP39 key producer to dedicated class, add coverage tests, update README with usage examples

70 of 76 new or added lines in 10 files covered. (92.11%)

1 existing line in 1 file now uncovered.

1434 of 2052 relevant lines covered (69.88%)

0.7 hits per line

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

96.4
/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,
NEW
86
            cKeyProducerJavaBip39 -> new KeyProducerJavaBip39(cKeyProducerJavaBip39, keyUtility, bitHelper),
×
NEW
87
            cKeyProducerJavaBip39 -> cKeyProducerJavaBip39.keyProducerId,
×
88
            keyProducers
89
        );
90
    }
1✔
91
    
92
    private <T, K> void processKeyProducers(
93
        List<T> configList,
94
        Function<T, K> constructor,
95
        Function<T, String> getId,
96
        Map<String, K> keyProducers
97
    ) {
98
        if (configList != null) {
1✔
99
            for (T config : configList) {
1✔
100
                String keyProducerId = getId.apply(config);
1✔
101
                if (keyProducerId == null) {
1✔
102
                    throw new KeyProducerIdNullException();
1✔
103
                }
104
                if (keyProducers.containsKey(keyProducerId)) {
1✔
105
                    throw new KeyProducerIdIsNotUniqueException(keyProducerId);
1✔
106
                }
107
                K keyProducer = constructor.apply(config);
1✔
108
                keyProducers.put(keyProducerId, keyProducer);
1✔
109
            }
1✔
110
        }
111
    }
1✔
112

113
    public void startConsumer() {
114
        logger.info("startConsumer");
1✔
115
        if (finder.consumerJava != null) {
1✔
116
            consumerJava = new ConsumerJava(finder.consumerJava, keyUtility, persistenceUtils);
1✔
117
            consumerJava.initLMDB();
1✔
118
            consumerJava.startConsumer();
1✔
119
            consumerJava.startStatisticsTimer();
1✔
120
        }
121
    }
1✔
122

123
    public void configureProducer() {
124
        logger.info("configureProducer");
1✔
125
        processProducers(
1✔
126
            finder.producerJava,
127
            bitHelper::assertBatchSizeInBitsIsInRange,
1✔
128
            this::getKeyProducerJava,
129
            (config, keyProducer) -> new ProducerJava(config, consumerJava, keyUtility, keyProducer, bitHelper),
1✔
130
            javaProducers
131
        );
132

133
        processProducers(
1✔
134
            finder.producerJavaSecretsFiles,
135
            bitHelper::assertBatchSizeInBitsIsInRange,
1✔
136
            this::getKeyProducerJava,
137
            (config, keyProducer) -> new ProducerJavaSecretsFiles(config, consumerJava, keyUtility, keyProducer, bitHelper),
1✔
138
            javaProducersSecretsFiles
139
        );
140

141
        processProducers(
1✔
142
            finder.producerOpenCL,
143
            bitHelper::assertBatchSizeInBitsIsInRange,
1✔
144
            this::getKeyProducerJava,
145
            (config, keyProducer) -> new ProducerOpenCL(config, consumerJava, keyUtility, keyProducer, bitHelper),
1✔
146
            openCLProducers
147
        );
148
    }
1✔
149
    
150
    private <T extends CProducer, P> void processProducers(
151
        List<T> configs,
152
        java.util.function.Consumer<Integer> batchSizeAssert,
153
        Function<T, KeyProducer> getKeyProducer,
154
        BiFunction<T, KeyProducer, P> producerConstructor,
155
        Collection<P> targetCollection
156
    ) {
157
        if (configs != null) {
1✔
158
            for (T config : configs) {
1✔
159
                batchSizeAssert.accept(config.batchSizeInBits);
1✔
160
                KeyProducer keyProducer = getKeyProducer.apply(config);
1✔
161
                P producer = producerConstructor.apply(config, keyProducer);
1✔
162
                targetCollection.add(producer);
1✔
163
            }
1✔
164
        }
165
    }
1✔
166

167
    public KeyProducer getKeyProducerJava(CProducer cProducer) throws RuntimeException {
168
        KeyProducer keyProducer = keyProducers.get(cProducer.keyProducerId);
1✔
169
        if(keyProducer == null) {
1✔
170
            throw new KeyProducerIdUnknownException(cProducer.keyProducerId);
1✔
171
        }
172
        return keyProducer;
1✔
173
    }
174
    
175
    public void initProducer() {
176
        logger.info("initProducer");
1✔
177
        for (Producer producer : getAllProducers()) {
1✔
178
            producer.initProducer();
1✔
179
        }
1✔
180
    }
1✔
181
    
182
    public void startProducer() {
183
        logger.info("startProducer");
1✔
184
        for (Producer producer : getAllProducers()) {
1✔
185
            producerExecutorService.submit(producer);
1✔
186
        }
1✔
187
    }
1✔
188
    
189
    public void shutdownAndAwaitTermination() {
190
        logger.info("shutdownAndAwaitTermination");
1✔
191
        try {
192
            producerExecutorService.shutdown();
1✔
193
            producerExecutorService.awaitTermination(AWAIT_DURATION_TERMINATE.get(ChronoUnit.SECONDS), TimeUnit.SECONDS);
1✔
194
        } catch (InterruptedException ex) {
×
195
            throw new RuntimeException(ex);
×
196
        }
1✔
197
        
198
        // no producers are running anymore, the consumer can be interrupted
199
        if (consumerJava != null) {
1✔
200
            logger.info("Interrupt: " + consumerJava.toString());
1✔
201
            consumerJava.interrupt();
1✔
202
            consumerJava = null;
1✔
203
        }
204
        logger.info("consumerJava released.");
1✔
205
    }
1✔
206
    
207
    @Override
208
    public void interrupt() {
209
        logger.info("interrupt called: delegate interrupt to all producer");
1✔
210
        for (Producer producer : getAllProducers()) {
1✔
211
            logger.info("Interrupt: " + producer.toString());
1✔
212
            producer.interrupt();
1✔
213
            logger.info("waitTillProducerNotRunning ...");
1✔
214
            producer.waitTillProducerNotRunning();
1✔
215
            producer.releaseProducer();
1✔
216
        }
1✔
217
        freeAllProducers();
1✔
218
        logger.info("All producers released and freed.");
1✔
219
    }
1✔
220
    
221
    public Map<String, KeyProducer> getKeyProducers() {
222
        return ImmutableMap.copyOf(keyProducers);
1✔
223
    }
224
    
225
    public List<Producer> getAllProducers() {
226
        List<Producer> producers = new ArrayList<>();
1✔
227
        producers.addAll(javaProducers);
1✔
228
        producers.addAll(javaProducersSecretsFiles);
1✔
229
        producers.addAll(openCLProducers);
1✔
230
        return producers;
1✔
231
    }
232
    
233
    public void freeAllProducers() {
234
        javaProducers.clear();
1✔
235
        javaProducersSecretsFiles.clear();
1✔
236
        openCLProducers.clear();
1✔
237
    }
1✔
238
    
239
    public List<Consumer> getAllConsumers() {
240
        List<Consumer> consumers = new ArrayList<>();
1✔
241
        if (consumerJava != null) {
1✔
242
            consumers.add(consumerJava);
1✔
243
        }
244
        return consumers;
1✔
245
    }
246
}
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