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

bernardladenthin / BitcoinAddressFinder / #312

06 May 2025 08:41PM UTC coverage: 67.925% (+0.4%) from 67.512%
#312

push

bernardladenthin
Refactor OpenCL I/O to use unified chunk layout and consistent endianness handling

0 of 10 new or added lines in 3 files covered. (0.0%)

2 existing lines in 2 files now uncovered.

1224 of 1802 relevant lines covered (67.92%)

0.68 hits per line

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

0.0
/src/main/java/net/ladenthin/bitcoinaddressfinder/OpenCLGridResult.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 java.util.Arrays;
22
import java.math.BigInteger;
23
import java.nio.ByteBuffer;
24
import java.nio.ByteOrder;
25
import static net.ladenthin.bitcoinaddressfinder.PublicKeyBytes.CHUNK_SIZE_00_NUM_BYTES_BIG_ENDIAN_X;
26
import static net.ladenthin.bitcoinaddressfinder.PublicKeyBytes.CHUNK_SIZE_01_NUM_BYTES_BIG_ENDIAN_Y;
27
import static net.ladenthin.bitcoinaddressfinder.PublicKeyBytes.CHUNK_SIZE_10_NUM_BYTES_RIPEMD160_UNCOMPRESSED;
28
import static net.ladenthin.bitcoinaddressfinder.PublicKeyBytes.CHUNK_SIZE_11_NUM_BYTES_RIPEMD160_COMPRESSED;
29
import net.ladenthin.bitcoinaddressfinder.configuration.CConsumerJava;
30

31
public class OpenCLGridResult {
32
    
33
    /**
34
     * Enable additional validation to check if generated uncompressed keys are not all zero.
35
     * <p>
36
     * This should remain <b>disabled in production</b> to avoid unnecessary performance overhead.
37
     * Useful only during debugging or when validating OpenCL/GPU kernel correctness.
38
     * </p>
39
     * <p>
40
     * Superseded by {@link PublicKeyBytes#runtimePublicKeyCalculationCheck(org.slf4j.Logger)}
41
     * and its activation via {@link CConsumerJava#runtimePublicKeyCalculationCheck}.
42
     * </p>
43
     */
44
    private static final boolean ENABLE_UNCOMPRESSED_KEY_VALIDATION = false;
45

46
    private final ByteBufferUtility byteBufferUtility = new ByteBufferUtility(true);
×
47
    
48
    private final BigInteger secretKeyBase;
49
    private final int workSize;
50
    private ByteBuffer result;
51
    
52
    OpenCLGridResult(BigInteger secretKeyBase, int workSize, ByteBuffer result) {
×
53
        this.secretKeyBase = secretKeyBase;
×
54
        this.workSize = workSize;
×
55
        this.result = result;
×
56
    }
×
57

58
    public BigInteger getSecretKeyBase() {
59
        return secretKeyBase;
×
60
    }
61

62
    public int getWorkSize() {
63
        return workSize;
×
64
    }
65

66
    public ByteBuffer getResult() {
67
        return result;
×
68
    }
69
    
70
    public void freeResult() {
71
        // free and do not use anymore
72
        byteBufferUtility.freeByteBuffer(result);
×
73
        result = null;
×
74
    }
×
75
    
76
    /**
77
     * Reads the computed public keys from the OpenCL result buffer and converts them into the correct format.
78
     * <p>
79
     * OpenCL writes 32-bit integers (u32) into memory using the device's native endianness (typically Little-Endian).
80
     * However, Bitcoin/ECC standards expect public keys in Big-Endian (MSB-first) byte order.
81
     * <p>
82
     * Therefore, after reading X and Y coordinates for each public key, the bytes of each coordinate
83
     * must be reversed if the device endianness differs from the target Big-Endian format.
84
     * <p>
85
     * The resulting format matches the uncompressed SEC (Standards for Efficient Cryptography) format:
86
     * <ul>
87
     *   <li>Prefix byte 0x04</li>
88
     *   <li>Followed by 32 bytes for X coordinate (Big-Endian)</li>
89
     *   <li>Followed by 32 bytes for Y coordinate (Big-Endian)</li>
90
     * </ul>
91
     * <p>
92
     * <b>Note:</b> This operation is relatively time-consuming because it involves memory copying and per-key byte order correction.
93
     *
94
     * @return an array of {@link PublicKeyBytes} containing the reconstructed public keys.
95
     */
96
    public PublicKeyBytes[] getPublicKeyBytes() {
97
        PublicKeyBytes[] publicKeys = new PublicKeyBytes[workSize];
×
98
        for (int i = 0; i < workSize; i++) {
×
99
            PublicKeyBytes publicKeyBytes = getPublicKeyFromByteBufferXY(byteBufferUtility, result, i, secretKeyBase);
×
100
            publicKeys[i] = publicKeyBytes;
×
101
        }
102
        return publicKeys;
×
103
    }
104
    
105
    public static byte[] trimU32PrefixBytes(byte[] fullArray) {
106
        final int PREFIX_BYTES_TO_SKIP = 3;
×
107
        return Arrays.copyOfRange(fullArray, PREFIX_BYTES_TO_SKIP, fullArray.length);
×
108
    }
109

110
    /**
111
     * Reconstructs a {@link PublicKeyBytes} object from the OpenCL kernel output.
112
     * <p>
113
     * This method extracts a block of bytes for one key from the given {@link ByteBuffer},
114
     * based on the work-item index ({@code keyNumber}). The layout of each chunk is defined
115
     * by constants in {@link PublicKeyBytes}:
116
     * <ul>
117
     *   <li>{@code CHUNK_SIZE_00_NUM_BYTES_BIG_ENDIAN_X}: X coordinate (Big-Endian)</li>
118
     *   <li>{@code CHUNK_SIZE_01_NUM_BYTES_BIG_ENDIAN_Y}: Y coordinate (Big-Endian)</li>
119
     *   <li>{@code CHUNK_SIZE_10_NUM_BYTES_RIPEMD160_UNCOMPRESSED}: RIPEMD-160 hash of the uncompressed key</li>
120
     *   <li>{@code CHUNK_SIZE_11_NUM_BYTES_RIPEMD160_COMPRESSED}: RIPEMD-160 hash of the compressed key</li>
121
     * </ul>
122
     * <p>
123
     * The method reads and assembles the uncompressed public key in SEC format
124
     * ({@code 04 || X || Y}) using the provided X and Y coordinates. It also
125
     * extracts the precomputed RIPEMD-160 hashes for both uncompressed and
126
     * compressed formats from the buffer.
127
     * <p>
128
     * If the reconstructed secret key is zero, a predefined fallback key is returned.
129
     *
130
     * @param byteBufferUtility utility for interacting with byte arrays and buffers
131
     * @param resultBuffer the buffer containing OpenCL results for all keys
132
     * @param keyNumber the zero-based index of the key to extract
133
     * @param secretKeyBase the base secret key used to derive the current key
134
     * @return the reconstructed {@link PublicKeyBytes} object
135
     * @throws RuntimeException if the key bytes are invalid (e.g. all coordinate bytes are zero)
136
     */
137
    private static final PublicKeyBytes getPublicKeyFromByteBufferXY(ByteBufferUtility byteBufferUtility, ByteBuffer resultBuffer, int keyNumber, BigInteger secretKeyBase) {
138
        BigInteger secret = AbstractProducer.calculateSecretKey(secretKeyBase, keyNumber);
×
139
        if(BigInteger.ZERO.equals(secret)) {
×
140
            // the calculated key is invalid, return a fallback
141
            return PublicKeyBytes.INVALID_KEY_ONE;
×
142
        }
143
        
144
        // Read a chunk completely
NEW
145
        byte[] kernelResultChunk = new byte[PublicKeyBytes.CHUNK_SIZE_NUM_BYTES];
×
NEW
146
        final int keyOffsetInByteBuffer = kernelResultChunk.length * keyNumber;
×
NEW
147
        resultBuffer.get(keyOffsetInByteBuffer, kernelResultChunk, 0, kernelResultChunk.length);
×
148
        
149
        // Get X
UNCOV
150
        byte[] xFromBigEndian = Arrays.copyOfRange(
×
151
            kernelResultChunk,
152
            PublicKeyBytes.CHUNK_OFFSET_00_NUM_BYTES_BIG_ENDIAN_X,
153
            PublicKeyBytes.CHUNK_OFFSET_00_NUM_BYTES_BIG_ENDIAN_X + PublicKeyBytes.CHUNK_SIZE_00_NUM_BYTES_BIG_ENDIAN_X
154
        );
155
        
156
        // Get Y
157
        byte[] yFromBigEndian = Arrays.copyOfRange(
×
158
            kernelResultChunk,
159
            PublicKeyBytes.CHUNK_OFFSET_01_NUM_BYTES_BIG_ENDIAN_Y,
160
            PublicKeyBytes.CHUNK_OFFSET_01_NUM_BYTES_BIG_ENDIAN_Y + PublicKeyBytes.CHUNK_SIZE_01_NUM_BYTES_BIG_ENDIAN_Y
161
        );
162
        
163
        // Assemble uncompressed key
NEW
164
        byte[] uncompressedFromBigEndian = PublicKeyBytes.assembleUncompressedPublicKey(xFromBigEndian, yFromBigEndian);
×
165
        
166
        // Get RIPEMD160 for uncompressed key
167
        byte[] ripemd160Uncompressed = Arrays.copyOfRange(
×
168
            kernelResultChunk,
169
            PublicKeyBytes.CHUNK_OFFSET_10_NUM_BYTES_RIPEMD160_UNCOMPRESSED,
170
            PublicKeyBytes.CHUNK_OFFSET_10_NUM_BYTES_RIPEMD160_UNCOMPRESSED + PublicKeyBytes.CHUNK_SIZE_10_NUM_BYTES_RIPEMD160_UNCOMPRESSED
171
        );
172
        
173
        // Get RIPEMD160 for uncompressed key
174
        byte[] ripemd160Compressed = Arrays.copyOfRange(
×
175
            kernelResultChunk,
176
            PublicKeyBytes.CHUNK_OFFSET_11_NUM_BYTES_RIPEMD160_COMPRESSED,
177
            PublicKeyBytes.CHUNK_OFFSET_11_NUM_BYTES_RIPEMD160_COMPRESSED + PublicKeyBytes.CHUNK_SIZE_11_NUM_BYTES_RIPEMD160_COMPRESSED
178
        );
179
        
180
        if (ENABLE_UNCOMPRESSED_KEY_VALIDATION) {
181
            boolean allZero = PublicKeyBytes.isAllCoordinateBytesZero(uncompressedFromBigEndian);
182
            if (allZero) {
183
                throw new RuntimeException("Invalid GPU result: all coordinate bytes are zero in uncompressed public key.");
184
            }
185
        }
186
        
NEW
187
        PublicKeyBytes publicKeyBytes = new PublicKeyBytes(secret, uncompressedFromBigEndian,  ripemd160Uncompressed, ripemd160Compressed);
×
188
        
NEW
189
        return publicKeyBytes;
×
190
    }
191
}
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