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

bernardladenthin / BitcoinAddressFinder / #319

04 Jun 2025 05:09PM UTC coverage: 66.939% (+1.3%) from 65.637%
#319

push

bernardladenthin
Remove duplicate address.

1312 of 1960 relevant lines covered (66.94%)

0.67 hits per line

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

97.86
/src/main/java/net/ladenthin/bitcoinaddressfinder/AddressTxtLine.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.github.kiulian.converter.AddressConverter;
22
import java.nio.ByteBuffer;
23
import org.bitcoinj.base.Base58;
24
import org.bitcoinj.base.Coin;
25
import org.bitcoinj.base.SegwitAddress;
26
import org.bitcoinj.base.exceptions.AddressFormatException;
27
import org.jspecify.annotations.Nullable;
28

29

30
/**
31
 * Most txt files have a common format which uses Base58 address and separated
32
 * anmount.
33
 */
34
public class AddressTxtLine {
1✔
35

36
    /**
37
     * Should not be {@link Coin#ZERO} because it can't be written to LMDB.
38
     */
39
    public static final Coin DEFAULT_COIN = Coin.SATOSHI;
1✔
40

41
    public static final String IGNORE_LINE_PREFIX = "#";
42
    public static final String ADDRESS_HEADER = "address";
43
    
44
    
45
    private final static int VERSION_BYTES_REGULAR = 1;
46
    private final static int VERSION_BYTES_ZCASH = 2;
47

48
    /**
49
     * If no coins can be found in the line {@link #DEFAULT_COIN} is used.
50
     *
51
     * @param line The line to parse.
52
     * @param keyUtility The {@link KeyUtility}.
53
     * @return Returns an {@link AddressToCoin} instance.
54
     */
55
    @Nullable
56
    public AddressToCoin fromLine(String line, KeyUtility keyUtility) {
57
        String[] lineSplitted = SeparatorFormat.split(line);
1✔
58
        String address = lineSplitted[0];
1✔
59
        Coin amount = getCoinIfPossible(lineSplitted, DEFAULT_COIN);
1✔
60
        address = address.trim();
1✔
61
        if (address.isEmpty() || address.startsWith(IGNORE_LINE_PREFIX) || address.startsWith(ADDRESS_HEADER)) {
1✔
62
            return null;
1✔
63
        }
64

65
        if (address.startsWith("q")) {
1✔
66
            // q: bitcoin cash Base58 (P2PKH)
67
            // convert to legacy address
68
            address = AddressConverter.toLegacyAddress(address);
1✔
69
        }
70

71
        if (address.startsWith("d-") || address.startsWith("m-") || address.startsWith("s-")) {
1✔
72
            // blockchair format for Bitcoin (d-) and Bitcoin Cash (m-) and (s-) (P2MS)
73
            return null;
1✔
74
        } else if (address.startsWith("bc1")) {
1✔
75
            // bitcoin Bech32 (P2WSH or P2WPKH) or P2TR
76
            // supported (20 bytes): https://privatekeys.pw/address/bitcoin/bc1qazcm763858nkj2dj986etajv6wquslv8uxwczt
77
            SegwitAddress segwitAddress = SegwitAddress.fromBech32(address, keyUtility.network);
1✔
78
            byte[] hash = segwitAddress.getHash();
1✔
79
            ByteBuffer hash160 = keyUtility.byteBufferUtility.byteArrayToByteBuffer(hash);
1✔
80
            if (hash160.limit() != PublicKeyBytes.RIPEMD160_HASH_NUM_BYTES) {
1✔
81
                // unsupported (32 bytes): https://privatekeys.pw/bitcoin/address/bc1qp762gmkychywl4elnuyuwph68hqw0uc2jkzu3ax48zfjkskslpsq8p66gf
82
                return null;
1✔
83
            }
84
            return new AddressToCoin(hash160, amount);
1✔
85
        } else if (address.startsWith("btco1")) {
1✔
86
            // Bitcoin Oil Bech32 (P2WSH or P2WPKH)
87
            // https://btc.cryptoid.info/btco/address.dws?3851.htm
88
            return null;
1✔
89
        } else if (address.startsWith("cdn1q")) {
1✔
90
            // Canada-eCoin Bech32 (P2WSH or P2WPKH)
91
            // https://btc.cryptoid.info/cdn/address.dws?cdn1qf7kyhfue3fjn4y09ec6cqqsxq56vh7q3z9ea8e.htm
92
            return null;
1✔
93
        } else if (address.startsWith("dc1q")) {
1✔
94
            // Doichain Bech32 (P2WSH or P2WPKH)
95
            // https://btc.cryptoid.info/doi/address.dws?2378564.htm
96
            return null;
1✔
97
        } else if (address.startsWith("df1q")) {
1✔
98
            // DeFiChain Bech32 (P2WSH or P2WPKH)
99
            // https://btc.cryptoid.info/dfi/address.dws?df1qvkmmsj6z602rymrx7fn82gqh68dp3v2f068gsu.htm
100
            return null;
1✔
101
        } else if (address.startsWith("rog1q")) {
1✔
102
            // TheHolyRogerCoin Bech32 (P2WSH or P2WPKH)
103
            // https://btc.cryptoid.info/roger/address.dws?139264.htm
104
            return null;
1✔
105
        } else if (address.startsWith("uf1q")) {
1✔
106
            // UFO Bech32 (P2WSH or P2WPKH)
107
            // https://btc.cryptoid.info/ufo/address.dws?570258.htm
108
            return null;
1✔
109
        } else if (address.startsWith("fc1")) {
1✔
110
            // feathercoin Bech32 (P2WSH or P2WPKH)
111
            // https://chainz.cryptoid.info/ftc/address.dws?fc1qvr9zesajsdw8aydcndd70wxj2wdgzu6zzltsph.htm
112
            return null;
1✔
113
        } else if (address.startsWith("lcc1")) {
1✔
114
            // litecoin cash Bech32 (P2WSH or P2WPKH)
115
            // https://chainz.cryptoid.info/lcc/address.dws?lcc1qrzlsxpjl0tynu3t2fkrw2ff2dgm0pv53ern0s5.htm
116
            return null;
1✔
117
        } else if (address.startsWith("ltc1")) {
1✔
118
            // litecoin Bech32 (P2WSH or P2WPKH)
119
            // https://privatekeys.pw/litecoin/address/ltc1qd5wm03t5kcdupjuyq5jffpuacnaqahvfsdu8smf8z0u0pqdqpatqsdrn8h
120
            return null;
1✔
121
        } else if (address.startsWith("nc1")) {
1✔
122
            // namecoin Bech32 (P2WSH or P2WPKH)
123
            // https://chainz.cryptoid.info/nmc/address.dws?nc1q2ml905jv7gx0d8z5f7kl23af0vtrjk4j0llmwr.htm
124
            return null;
1✔
125
        } else if (address.startsWith("vtc1")) {
1✔
126
            // vertcoin Bech32 (P2WSH or P2WPKH)
127
            // https://chainz.cryptoid.info/vtc/address.dws?vtc1qa4wejdlw9lmc7ks7l8hplc9fm394u79qjj0792.htm
128
            return null;
1✔
129
        } else if (address.startsWith("dgb1")) {
1✔
130
            // digibyte Bech32 (P2WPKH or P2SH)
131
            return null;
1✔
132
        } else if (address.startsWith("sys1")) {
1✔
133
            // syscoin Bech32 (P2WPKH or P2SH)
134
            return null;
1✔
135
        } else if (address.startsWith("btx1")) {
1✔
136
            // BitCore Bech32 (P2WPKH or P2SH)
137
            return null;
1✔
138
        } else if (address.startsWith("wkh_")) {
1✔
139
            // BitCore (WKH)
140
            return null;
1✔
141
        } else if (address.startsWith("grs1")) {
1✔
142
            // Groestlcoin Bech32 (P2WPKH or P2SH)
143
            return null;
1✔
144
        } else if (address.startsWith("tgrs1")) {
1✔
145
            // Groestlcoin TestNet Bech32 (P2WPKH or P2SH)
146
            return null;
1✔
147
        } else if (address.startsWith("ric1")) {
1✔
148
            // Riecoin Bech32 (P2WPKH or P2SH)
149
            return null;
1✔
150
        } else if (address.startsWith("rod1q")) {
1✔
151
            // SpaceXpanse Bech32 (P2WPKH or P2SH)
152
            return null;
1✔
153
        } else if (address.startsWith("my1q")) {
1✔
154
            // Myriad Bech32 (P2WPKH or P2SH)
155
            return null;
1✔
156
        } else if (address.startsWith("p")) {
1✔
157
            // p: bitcoin cash / CashAddr (P2SH), this is a unique format and does not work
158
            // p: peercoin possible
159
            try {
160
                ByteBuffer hash160 = getHash160AsByteBufferFromBase58AddressUnchecked(address, keyUtility, VERSION_BYTES_ZCASH);
1✔
161
                return new AddressToCoin(hash160, amount);
1✔
162
            } catch (RuntimeException e) {
1✔
163
                return null;
1✔
164
            }
165
        } else if (
1✔
166
                   address.startsWith("1")
1✔
167
                || address.startsWith("2")
1✔
168
                || address.startsWith("3")
1✔
169
                || address.startsWith("4")
1✔
170
                || address.startsWith("7")
1✔
171
                || address.startsWith("8")
1✔
172
                || address.startsWith("9")
1✔
173
                || address.startsWith("a")
1✔
174
                || address.startsWith("A")
1✔
175
                || address.startsWith("B")
1✔
176
                || address.startsWith("b")
1✔
177
                || address.startsWith("C")
1✔
178
                || address.startsWith("d")
1✔
179
                || address.startsWith("D")
1✔
180
                || address.startsWith("E")
1✔
181
                || address.startsWith("F")
1✔
182
                || address.startsWith("G")
1✔
183
                || address.startsWith("H")
1✔
184
                || address.startsWith("i")
1✔
185
                || address.startsWith("J")
1✔
186
                || address.startsWith("K")
1✔
187
                || address.startsWith("L")
1✔
188
                || address.startsWith("M")
1✔
189
                || address.startsWith("N")
1✔
190
                || address.startsWith("p")
1✔
191
                || address.startsWith("P")
1✔
192
                || address.startsWith("Q")
1✔
193
                || address.startsWith("R")
1✔
194
                || address.startsWith("s")
1✔
195
                || address.startsWith("S")
1✔
196
                || address.startsWith("t")
1✔
197
                || address.startsWith("T")
1✔
198
                || address.startsWith("u")
1✔
199
                || address.startsWith("V")
1✔
200
                || address.startsWith("W")
1✔
201
                || address.startsWith("x")
1✔
202
                || address.startsWith("X")
1✔
203
        ) {
204
            // prefix clashes for signs: 2, 7, 8, 9, A, C, M
205
            //
206
            // Base58 P2SH
207
            // 2: Mooncoin / Particl
208
            // 3: litecoin deprecated / bitcoin / Particl
209
            // 7: dash / Riecoin
210
            // 8: DeFiChain / BYTZ
211
            // 9: dogecoin multisig / 42-coin
212
            // A: dogecoin / SaluS
213
            // B: CloakCoin
214
            // C: UFO
215
            // M: litecoin / Myriad
216
            // t: Zcash
217
            //
218
            // Base58 P2PKH
219
            // 1: Terracoin
220
            // 2: BitCore / Pinkcoin
221
            // 4: novacoin / Myriad / 42-coin
222
            // 7: feathercoin / Dimecoin
223
            // 8: Vanillacash
224
            // 9: Catcoin
225
            // a: Firo
226
            // A: AuroraCoin / Primecoin
227
            // B: curecoin / BitBlocks / blackcoin / UFO / Smileycoin / Blocknet / BitcoinPlus
228
            // b: Bitmark / BolivarCoin
229
            // C: CloakCoin / CROWN / ChessCoin / Artbyte / Canada-eCoin
230
            // d: LiteDoge / Diamond / DeFiChain
231
            // D: dogecoin / digibyte / PIVX / DigitalCoin / Divicoin / ColossusXT
232
            // e: Electron
233
            // E: Emerald / InfiniLooP
234
            // F: Groestlcoin
235
            // G: bitcoin gold / Goldcash
236
            // H: Herencia
237
            // i: Innova / I/O Coin / Infinitecoin
238
            // J: MasterNoder2
239
            // K: Lynx
240
            // L: litecoin / Luckycoin / Lanacoin / e-Gulden / Elite
241
            // M: Mooncoin
242
            // N: namecoin / Deutsche eMark / Doichain
243
            // p: Element
244
            // P: Peercoin / PotCoin / PAC Protocol / PutinCoin v2 / Particl / PandaCoin / PakCoin
245
            // Q: Quark
246
            // R: reddcoin / Komodo / NewYorkCoin / Particl / Raptoreum / SpaceXpanse
247
            // s: BYTZ
248
            // S: Sterlingcoin / Syscoin / SaluS / Alias
249
            // t: Zcash
250
            // T: Trezarcoin
251
            // u: Unobtanium
252
            // U: Coino
253
            // V: vertcoin / VeriCoin / Versacoin / TheHolyRogerCoin
254
            // W: WorldCoin
255
            // x: Clam / iXcoin
256
            // X: dash / Validity
257
            
258
            // Riecoin
259
            {
260
                final String OP_DUP = "76";
1✔
261
                final String OP_HASH160 = "a9";
1✔
262
                final String OP_PUSH_20_BYTES = "14";
1✔
263
                final int length20Bytes = 20;
1✔
264
                final String riecoinP2SHPrefix = OP_DUP + OP_HASH160 + OP_PUSH_20_BYTES;
1✔
265
                final int riecoinScriptPubKeyLengthHex = length20Bytes * 2 + riecoinP2SHPrefix.length();
1✔
266
                if (address.length() >= riecoinScriptPubKeyLengthHex && address.startsWith(riecoinP2SHPrefix)) {
1✔
267
                    final String hash160Hex = address.substring(riecoinP2SHPrefix.length(), length20Bytes*2+riecoinP2SHPrefix.length());
1✔
268
                    final ByteBuffer hash160 = keyUtility.byteBufferUtility.getByteBufferFromHex(hash160Hex);
1✔
269
                    return new AddressToCoin(hash160, amount);
1✔
270
                }
271
            }
272

273
            if (address.startsWith("t")) {
1✔
274
                // ZCash has two version bytes
275
                ByteBuffer hash160 = getHash160AsByteBufferFromBase58AddressUnchecked(address, keyUtility, VERSION_BYTES_ZCASH);
1✔
276
                return new AddressToCoin(hash160, amount);
1✔
277
            } else {
278
                ByteBuffer hash160 = getHash160AsByteBufferFromBase58AddressUnchecked(address, keyUtility, VERSION_BYTES_REGULAR);
1✔
279
                return new AddressToCoin(hash160, amount);
1✔
280
            }
281
        } else {
282
            // bitcoin Base58 (P2PKH)
283
            ByteBuffer hash160;
284
            try {
285
                hash160 = keyUtility.getHash160ByteBufferFromBase58String(address);
×
286
            } catch (AddressFormatException.InvalidChecksum e) {
1✔
287
                hash160 = getHash160AsByteBufferFromBase58AddressUnchecked(address, keyUtility, VERSION_BYTES_REGULAR);
1✔
288
            } catch (AddressFormatException.WrongNetwork e) {
1✔
289
                // bitcoin testnet
290
                hash160 = getHash160AsByteBufferFromBase58AddressUnchecked(address, keyUtility, VERSION_BYTES_REGULAR);
1✔
291
            } catch (AddressFormatException.InvalidDataLength e) {
×
292
                // too short address
293
                hash160 = getHash160AsByteBufferFromBase58AddressUnchecked(address, keyUtility, VERSION_BYTES_REGULAR);
×
294
            }
1✔
295
            return new AddressToCoin(hash160, amount);
1✔
296
        }
297
    }
298

299
    private ByteBuffer getHash160AsByteBufferFromBase58AddressUnchecked(String base58, KeyUtility keyUtility, int srcPos) {
300
        byte[] hash160 = getHash160fromBase58AddressUnchecked(base58, srcPos);
1✔
301
        ByteBuffer hash160AsByteBuffer = keyUtility.byteBufferUtility.byteArrayToByteBuffer(hash160);
1✔
302
        return hash160AsByteBuffer;
1✔
303
    }
304

305
    byte[] getHash160fromBase58AddressUnchecked(String base58, int srcPos) {
306
        byte[] decoded = Base58.decode(base58);
1✔
307
        byte[] hash160 = new byte[20];
1✔
308
        int toCopy = Math.min(decoded.length - srcPos, hash160.length);
1✔
309
        System.arraycopy(decoded, srcPos, hash160, 0, toCopy);
1✔
310
        return hash160;
1✔
311
    }
312

313
    @Nullable
314
    private Coin getCoinIfPossible(String[] lineSplitted, Coin defaultValue) throws NumberFormatException {
315
        if (lineSplitted.length > 1) {
1✔
316
            String amountString = lineSplitted[1];
1✔
317
            try {
318
                return Coin.valueOf(Long.valueOf(amountString));
1✔
319
            } catch (NumberFormatException e) {
1✔
320
                return defaultValue;
1✔
321
            }
322
        } else {
323
            return defaultValue;
1✔
324
        }
325
    }
326
}
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