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

bernardladenthin / BitcoinAddressFinder / #308

28 Apr 2025 10:01PM UTC coverage: 68.174% (-0.7%) from 68.832%
#308

push

bernardladenthin
Refactor OpenCL handling for device endianness and dynamic platform selection

22 of 76 new or added lines in 10 files covered. (28.95%)

1 existing line in 1 file now uncovered.

1221 of 1791 relevant lines covered (68.17%)

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.math.BigInteger;
22
import java.nio.ByteBuffer;
23
import java.nio.ByteOrder;
24
import net.ladenthin.bitcoinaddressfinder.configuration.CConsumerJava;
25

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

41
    private final ByteBufferUtility byteBufferUtility = new ByteBufferUtility(true);
×
42
    
43
    private final BigInteger secretKeyBase;
44
    private final ByteOrder clByteOrder;
45
    private final int workSize;
46
    private ByteBuffer result;
47
    
NEW
48
    OpenCLGridResult(BigInteger secretKeyBase, ByteOrder clByteOrder, int workSize, ByteBuffer result) {
×
49
        this.secretKeyBase = secretKeyBase;
×
NEW
50
        this.clByteOrder = clByteOrder;
×
51
        this.workSize = workSize;
×
52
        this.result = result;
×
53
    }
×
54

55
    public BigInteger getSecretKeyBase() {
56
        return secretKeyBase;
×
57
    }
58

59
    public int getWorkSize() {
60
        return workSize;
×
61
    }
62

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

102
    /**
103
     * Reconstructs a {@link PublicKeyBytes} object from OpenCL kernel result bytes.
104
     * <p>
105
     * This method reads a block of 64 bytes (X and Y coordinates) from a given {@link ByteBuffer},
106
     * corrects for endianness if necessary, and assembles an uncompressed public key
107
     * in SEC (Standards for Efficient Cryptography) format ({@code 04 || X || Y}).
108
     * <p>
109
     * OpenCL devices typically write data in device-native byte order (often Little-Endian).
110
     * Bitcoin and ECC protocols expect Big-Endian (MSB-first) ordering for public key coordinates.
111
     * Therefore, each coordinate is converted to Big-Endian before assembling the public key.
112
     * <p>
113
     * If the reconstructed secret key is zero, a predefined invalid public key constant is returned.
114
     * <p>
115
     * Internal structure:
116
     * <ul>
117
     *   <li>32 bytes: X coordinate</li>
118
     *   <li>32 bytes: Y coordinate</li>
119
     * </ul>
120
     *
121
     * @param clByteOrder the {@link ByteOrder} used by the OpenCL device (e.g., Little-Endian)
122
     * @param byteBufferUtility the {@link ByteBufferUtility} instance for array operations
123
     * @param resultBuffer the {@link ByteBuffer} containing the OpenCL kernel output
124
     * @param keyNumber the index of the key to extract (based on work-item ID)
125
     * @param secretKeyBase the base private key corresponding to the first work item
126
     * @return a {@link PublicKeyBytes} object representing the reconstructed public key
127
     * @throws RuntimeException if all coordinate bytes are zero (invalid GPU output)
128
     */
129
    private static final PublicKeyBytes getPublicKeyFromByteBufferXY(ByteOrder clByteOrder, ByteBufferUtility byteBufferUtility, ByteBuffer resultBuffer, int keyNumber, BigInteger secretKeyBase) {
130
        BigInteger secret = AbstractProducer.calculateSecretKey(secretKeyBase, keyNumber);
×
131
        if(BigInteger.ZERO.equals(secret)) {
×
132
            // the calculated key is invalid, return a fallback
133
            return PublicKeyBytes.INVALID_KEY_ONE;
×
134
        }
135
        final int resultBlockSize = PublicKeyBytes.TWO_COORDINATES_NUM_BYTES;
×
136
        final int keyOffsetInByteBuffer = resultBlockSize*keyNumber;
×
137
        
138
        // Read XY block
139
        byte[] kernelResultBlock = new byte[resultBlockSize];
×
NEW
140
        resultBuffer.get(keyOffsetInByteBuffer, kernelResultBlock, 0, resultBlockSize);
×
141
        
NEW
142
        EndiannessConverter endiannessConverter = new EndiannessConverter(clByteOrder, ByteOrder.BIG_ENDIAN, byteBufferUtility);
×
143
        
144
        // Extract x
145
        byte[] x = new byte[PublicKeyBytes.ONE_COORDINATE_NUM_BYTES];
×
146
        System.arraycopy(kernelResultBlock, 0, x, 0, PublicKeyBytes.ONE_COORDINATE_NUM_BYTES);
×
147
        // To SEC format (Big-Endian) (MSB-first)
NEW
148
        endiannessConverter.convertEndian(x);
×
149
        
150
        // Extract Y
151
        byte[] y = new byte[PublicKeyBytes.ONE_COORDINATE_NUM_BYTES];
×
152
        System.arraycopy(kernelResultBlock, PublicKeyBytes.ONE_COORDINATE_NUM_BYTES, y, 0, PublicKeyBytes.ONE_COORDINATE_NUM_BYTES);
×
153
        // To SEC format (Big-Endian) (MSB-first)
NEW
154
        endiannessConverter.convertEndian(y);
×
155
        
156
        // Assemble uncompressed key
NEW
157
        byte[] uncompressed = PublicKeyBytes.assembleUncompressedPublicKey(x, y);
×
158
        
159
        if (ENABLE_UNCOMPRESSED_KEY_VALIDATION) {
160
            boolean allZero = PublicKeyBytes.isAllCoordinateBytesZero(uncompressed);
161
            if (allZero) {
162
                throw new RuntimeException("Invalid GPU result: all coordinate bytes are zero in uncompressed public key.");
163
            }
164
        }
165
        PublicKeyBytes publicKeyBytes = new PublicKeyBytes(secret, uncompressed);
×
166
        return publicKeyBytes;
×
167
    }
168
}
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