-
Notifications
You must be signed in to change notification settings - Fork 1
/
DeterministicWallet.java
114 lines (99 loc) · 3.68 KB
/
DeterministicWallet.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package app.keyconnect.sdk.wallets;
import app.keyconnect.api.client.model.BlockchainAccountInfo.ChainIdEnum;
import app.keyconnect.sdk.wallets.factories.BlockchainWalletFactory;
import app.keyconnect.sdk.wallets.factories.EthHdWalletFactory;
import app.keyconnect.sdk.wallets.factories.XrpHdWalletFactory;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.apache.commons.lang3.NotImplementedException;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.HDUtils;
import org.bitcoinj.wallet.DeterministicKeyChain;
import org.bitcoinj.wallet.DeterministicSeed;
import org.bitcoinj.wallet.UnreadableWalletException;
import org.web3j.utils.Strings;
// https://github.com/satoshilabs/slips/blob/master/slip-0044.md
// https://medium.com/myetherwallet/hd-wallets-and-derivation-paths-explained-865a643c7bf2
public class DeterministicWallet {
private final DeterministicSeed seed;
private final DeterministicKeyChain chain;
private final Map<String, BlockchainWalletFactory> factoryMap = new ConcurrentHashMap<>(2);
@Nullable
private final String passphrase;
public DeterministicWallet(@Nullable String passphrase) {
this.passphrase = passphrase;
seed = new DeterministicSeed(
new SecureRandom(),
256,
Optional.ofNullable(passphrase).orElse("")
);
chain = DeterministicKeyChain.builder().seed(seed).build();
}
public DeterministicWallet(@Nullable String passphrase, String mnemonic) {
this(passphrase, mnemonic, System.currentTimeMillis());
}
public DeterministicWallet(@Nullable String passphrase, String mnemonicString,
long creationTimeInSeconds) {
this.passphrase = passphrase;
try {
seed = new DeterministicSeed(
mnemonicString,
null,
Optional.ofNullable(passphrase).orElse(""),
creationTimeInSeconds
);
} catch (UnreadableWalletException e) {
throw new RuntimeException("Wallet unreadable!", e);
}
chain = DeterministicKeyChain.builder().seed(seed).build();
}
public BlockchainWalletFactory getWalletFactory(String chainIndex) {
return getAllFactories()
.stream()
.filter(f -> f.getChainIndex().equals(chainIndex))
.findFirst()
.get();
}
public BlockchainWalletFactory getWalletFactory(ChainIdEnum chainId) {
switch (chainId) {
case ETH:
return factoryMap.computeIfAbsent(chainId.getValue(), c -> new EthHdWalletFactory(this));
case XRP:
return factoryMap.computeIfAbsent(chainId.getValue(), c -> new XrpHdWalletFactory(this));
default:
throw new NotImplementedException(
"Wallet factory for blockchain " + chainId.getValue() + " is not yet implemented");
}
}
public BlockchainWallet generate(ChainIdEnum chainId, String name) {
return getWalletFactory(chainId).generateNext(name);
}
public Set<BlockchainWalletFactory> getAllFactories() {
return Arrays.stream(ChainIdEnum.values())
.map(this::getWalletFactory)
.collect(Collectors.toSet());
}
@Nullable
public String getPassphrase() {
return passphrase;
}
public List<ChildNumber> buildPath(String coinType, String account) {
return HDUtils.parsePath(String.format("M/44H/%sH/%sH/0/0", coinType, account));
}
public DeterministicKeyChain getChain() {
return chain;
}
public String getMnemonic() {
return Strings.join(this.seed.getMnemonicCode(), " ");
}
public DeterministicSeed getDeterministicSeed() {
return this.seed;
}
}