mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-20 10:22:18 +01:00
Fix bug with restore form seed. Remove one path element at BIP 44 chain path. Add UTXO provider support.
This commit is contained in:
parent
9dab7aa2ed
commit
c288c107af
@ -8,6 +8,7 @@ import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.UTXO;
|
||||
import org.bitcoinj.core.Utils;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -17,6 +18,8 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.bitcoinj.core.Utils.HEX;
|
||||
|
||||
public class SquUtxoFeedProvider extends HttpClientProvider {
|
||||
private static final Logger log = LoggerFactory.getLogger(SquUtxoFeedProvider.class);
|
||||
|
||||
@ -40,15 +43,43 @@ public class SquUtxoFeedProvider extends HttpClientProvider {
|
||||
|
||||
Set<UTXO> utxoSet = new HashSet<>();
|
||||
|
||||
/*
|
||||
>>> PENDING:
|
||||
Sends 0.00 BTC and receives 0.0001 BTC, total value 0.0001 BTC.
|
||||
ef07b4ce136c1230f9b4a8b9a7914b2f3e1ff1c8da83c50f4190796fd0f69514: Seen by 1 peer. Pending/unconfirmed.
|
||||
time locked until block 259 (estimated to be reached at Sun Dec 11 07:35:34 CET 2016)
|
||||
in PUSHDATA(71)[304402205159d8b87b72bdd988c172806bcd63712c002bdbe94a84759016c702d339730102201101f78cf11c2ad63e2e54fcb7b9afbd99190269163178b18bbe84daf780535501] PUSHDATA(33)[02bb475a2333c036b1ed4afb4747d0b0464858e35c784c3c16fb96ff7423aeaca7]
|
||||
outpoint:3148d764241e00fe46c6b4d7ed60543608a422ca6dc760aad590990355c42e1d:1
|
||||
out DUP HASH160 PUSHDATA(20)[b9466f6ee06ddf0709d99dcee5b46a5693a17f9c] EQUALVERIFY CHECKSIG 0.0453858 BTC (aw scriptPubKey: 76a914b9466f6ee06ddf0709d99dcee5b46a5693a17f9c88ac)
|
||||
out DUP HASH160 PUSHDATA(20)[bf20c68f946157aa3e68c610f220b0dadae80a23] EQUALVERIFY CHECKSIG 0.0001 BTC (aw scriptPubKey: 76a914bf20c68f946157aa3e68c610f220b0dadae80a2388ac)
|
||||
|
||||
UTXO newOut = new UTXO(Sha256Hash.of(Utils.HEX.decode("sdaf9usdoafisdahf;")),
|
||||
>>> UNSPENT:
|
||||
Sends 0.00 BTC and receives 0.00001 BTC, total value 0.00001 BTC.
|
||||
ae71bba68ceac54af639f4b8d86ed1a720a5c9cf23285f1b8ce707d9ce848a04: Appeared in best chain at height 333, depth 1.
|
||||
time locked until block 332 (estimated to be reached at Sun Dec 11 19:45:34 CET 2016)
|
||||
in PUSHDATA(71)[304402207b42682e83a21cfa7c42b9cb8c077061e5a43de3b972296f1240526060d09de5022019611503aaeff6a9dd6576f7016e124bfb0ff316ec200cc7bb97899c8148f65801] PUSHDATA(33)[03cea6bb8e9a1b10a5adeaadac820acea691af18a65d1deb98f478231f256570a9]
|
||||
outpoint:86526aae12247453fcf02f71e5616437d93de7ffbc82f7631225b6bba988d351:0
|
||||
out DUP HASH160 PUSHDATA(20)[cc907567ff7e61e023f5e9e28e333d4149481e4b] EQUALVERIFY CHECKSIG 0.014768 BTC (aw scriptPubKey: 76a914cc907567ff7e61e023f5e9e28e333d4149481e4b88ac)
|
||||
out DUP HASH160 PUSHDATA(20)[027aea6622e5a3f4c4c48e2935544b92008176a1] EQUALVERIFY CHECKSIG 0.00001 BTC (aw scriptPubKey: 76a914027aea6622e5a3f4c4c48e2935544b92008176a188ac)
|
||||
*/
|
||||
UTXO utxo1 = new UTXO(Sha256Hash.of(Utils.HEX.decode("ae71bba68ceac54af639f4b8d86ed1a720a5c9cf23285f1b8ce707d9ce848a04")),
|
||||
1,
|
||||
Coin.valueOf(1000),
|
||||
999,
|
||||
331,
|
||||
false,
|
||||
null,
|
||||
"address1212121");
|
||||
new Script(HEX.decode("76a914027aea6622e5a3f4c4c48e2935544b92008176a188ac")),
|
||||
"mfk4tmJsRjwrDLNgqbLBas1HCt7o4MU84i");
|
||||
utxoSet.add(utxo1);
|
||||
|
||||
UTXO utxo2 = new UTXO(Sha256Hash.of(Utils.HEX.decode("ef07b4ce136c1230f9b4a8b9a7914b2f3e1ff1c8da83c50f4190796fd0f69514")),
|
||||
1,
|
||||
Coin.valueOf(10_000),
|
||||
331,
|
||||
false,
|
||||
new Script(HEX.decode("76a914bf20c68f946157aa3e68c610f220b0dadae80a2388ac")),
|
||||
"mxwYcz39M8BwLSS8wQH3gvPB96PDuNVJTm");
|
||||
utxoSet.add(utxo2);
|
||||
|
||||
SquUtxoFeedData squUtxoFeedData = new SquUtxoFeedData(utxoSet);
|
||||
|
||||
return new Tuple2<>(tsMap, squUtxoFeedData);
|
||||
@ -56,6 +87,6 @@ public class SquUtxoFeedProvider extends HttpClientProvider {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SquUtxoProvider";
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ import org.slf4j.LoggerFactory;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@ -42,7 +43,7 @@ public class SquUtxoFeedService {
|
||||
private static final Logger log = LoggerFactory.getLogger(SquUtxoFeedService.class);
|
||||
|
||||
private final SquUtxoFeedProvider squUtxoFeedProvider;
|
||||
private SquUtxoFeedData squUtxoFeedData;
|
||||
private SquUtxoFeedData data;
|
||||
private Map<String, Long> timeStampMap;
|
||||
private long epochInSecondAtLastRequest;
|
||||
|
||||
@ -60,7 +61,7 @@ public class SquUtxoFeedService {
|
||||
requestSquUtxo(null, null);
|
||||
}
|
||||
|
||||
public void requestSquUtxo(@Nullable Runnable resultHandler, @Nullable FaultHandler faultHandler) {
|
||||
public void requestSquUtxo(@Nullable Consumer<Set<UTXO>> resultHandler, @Nullable FaultHandler faultHandler) {
|
||||
//TODO add throttle
|
||||
Log.traceCall();
|
||||
SquUtxoFeedRequest squUtxoRequest = new SquUtxoFeedRequest();
|
||||
@ -72,9 +73,9 @@ public class SquUtxoFeedService {
|
||||
checkNotNull(result, "Result must not be null at getFees");
|
||||
timeStampMap = result.first;
|
||||
epochInSecondAtLastRequest = timeStampMap.get("getSquUtxoTs");
|
||||
squUtxoFeedData = result.second;
|
||||
data = result.second;
|
||||
if (resultHandler != null)
|
||||
resultHandler.run();
|
||||
resultHandler.accept(data.getUtxoSet());
|
||||
});
|
||||
}
|
||||
|
||||
@ -88,6 +89,6 @@ public class SquUtxoFeedService {
|
||||
}
|
||||
|
||||
public Set<UTXO> getUtxoSet() {
|
||||
return squUtxoFeedData != null ? squUtxoFeedData.getUtxoSet() : null;
|
||||
return data != null ? data.getUtxoSet() : null;
|
||||
}
|
||||
}
|
||||
|
@ -52,19 +52,23 @@ abstract class BitsquareCoinSelector implements CoinSelector {
|
||||
abstract protected boolean matchesRequirement(TransactionOutput transactionOutput);
|
||||
|
||||
private static boolean isInBlockChainOrPending(Transaction tx) {
|
||||
// Pick chain-included transactions and transactions that are pending.
|
||||
TransactionConfidence confidence = tx.getConfidence();
|
||||
TransactionConfidence.ConfidenceType type = confidence.getConfidenceType();
|
||||
if (tx != null) {
|
||||
// Pick chain-included transactions and transactions that are pending.
|
||||
TransactionConfidence confidence = tx.getConfidence();
|
||||
TransactionConfidence.ConfidenceType type = confidence.getConfidenceType();
|
||||
|
||||
log.debug("numBroadcastPeers = " + confidence.numBroadcastPeers());
|
||||
return type.equals(TransactionConfidence.ConfidenceType.BUILDING) ||
|
||||
type.equals(TransactionConfidence.ConfidenceType.PENDING);
|
||||
log.debug("numBroadcastPeers = " + confidence.numBroadcastPeers());
|
||||
return type.equals(TransactionConfidence.ConfidenceType.BUILDING) ||
|
||||
type.equals(TransactionConfidence.ConfidenceType.PENDING);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub-classes can override this to just customize whether transactions are usable, but keep age sorting.
|
||||
*/
|
||||
private boolean shouldSelect(Transaction tx) {
|
||||
protected boolean shouldSelect(Transaction tx) {
|
||||
return isInBlockChainOrPending(tx);
|
||||
}
|
||||
|
||||
|
@ -37,16 +37,16 @@ class BitsquareKeyChainFactory implements KeyChainFactory {
|
||||
|
||||
@Override
|
||||
public DeterministicKeyChain makeKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicSeed seed, KeyCrypter crypter, boolean isMarried) {
|
||||
return useBitcoinDeterministicKeyChain ? new BitcoinDeterministicKeyChain(seed, crypter) : new SquDeterministicKeyChain(seed, crypter);
|
||||
return useBitcoinDeterministicKeyChain ? new BtcDeterministicKeyChain(seed, crypter) : new SquDeterministicKeyChain(seed, crypter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeterministicKeyChain makeWatchingKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicKey accountKey,
|
||||
boolean isFollowingKey, boolean isMarried) throws UnreadableWalletException {
|
||||
ImmutableList<ChildNumber> accountPath = useBitcoinDeterministicKeyChain ? BitcoinDeterministicKeyChain.BIP44_BTC_ACCOUNT_PATH : SquDeterministicKeyChain.BIP44_SQU_ACCOUNT_PATH;
|
||||
ImmutableList<ChildNumber> accountPath = useBitcoinDeterministicKeyChain ? BtcDeterministicKeyChain.BIP44_BTC_ACCOUNT_PATH : SquDeterministicKeyChain.BIP44_SQU_ACCOUNT_PATH;
|
||||
if (!accountKey.getPath().equals(accountPath))
|
||||
throw new UnreadableWalletException("Expecting account key but found key with path: " +
|
||||
HDUtils.formatPath(accountKey.getPath()));
|
||||
return useBitcoinDeterministicKeyChain ? new BitcoinDeterministicKeyChain(accountKey, isFollowingKey) : new SquDeterministicKeyChain(accountKey, isFollowingKey);
|
||||
return useBitcoinDeterministicKeyChain ? new BtcDeterministicKeyChain(accountKey, isFollowingKey) : new SquDeterministicKeyChain(accountKey, isFollowingKey);
|
||||
}
|
||||
}
|
||||
|
@ -19,33 +19,40 @@ package io.bitsquare.btc.wallet;
|
||||
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.wallet.DeterministicKeyChain;
|
||||
import org.bitcoinj.wallet.DeterministicSeed;
|
||||
import org.bitcoinj.wallet.KeyChainGroup;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
class BitsquareKeyChainGroup extends KeyChainGroup {
|
||||
private final boolean useBitcoinDeterministicKeyChain;
|
||||
private int lookaheadSize;
|
||||
|
||||
public boolean isUseBitcoinDeterministicKeyChain() {
|
||||
return useBitcoinDeterministicKeyChain;
|
||||
}
|
||||
|
||||
public BitsquareKeyChainGroup(NetworkParameters params, boolean useBitcoinDeterministicKeyChain) {
|
||||
public BitsquareKeyChainGroup(NetworkParameters params, boolean useBitcoinDeterministicKeyChain, int lookaheadSize) {
|
||||
super(params);
|
||||
this.useBitcoinDeterministicKeyChain = useBitcoinDeterministicKeyChain;
|
||||
this.lookaheadSize = lookaheadSize;
|
||||
}
|
||||
|
||||
public BitsquareKeyChainGroup(NetworkParameters params, DeterministicSeed seed, boolean useBitcoinDeterministicKeyChain) {
|
||||
super(params, seed);
|
||||
public BitsquareKeyChainGroup(NetworkParameters params, DeterministicKeyChain chain, boolean useBitcoinDeterministicKeyChain, int lookaheadSize) {
|
||||
super(params, chain);
|
||||
|
||||
this.useBitcoinDeterministicKeyChain = useBitcoinDeterministicKeyChain;
|
||||
this.lookaheadSize = lookaheadSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLookaheadSize(int lookaheadSize) {
|
||||
super.setLookaheadSize(lookaheadSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createAndActivateNewHDChain() {
|
||||
DeterministicKeyChain chain = useBitcoinDeterministicKeyChain ?
|
||||
new BitcoinDeterministicKeyChain(new SecureRandom()) :
|
||||
new SquDeterministicKeyChain(new SecureRandom());
|
||||
DeterministicKeyChain chain = useBitcoinDeterministicKeyChain ? new BtcDeterministicKeyChain(new SecureRandom()) : new SquDeterministicKeyChain(new SecureRandom());
|
||||
chain.setLookaheadSize(lookaheadSize);
|
||||
addAndActivateHDChain(chain);
|
||||
}
|
||||
}
|
||||
|
@ -28,30 +28,34 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
class BitcoinDeterministicKeyChain extends DeterministicKeyChain {
|
||||
private static final Logger log = LoggerFactory.getLogger(BitcoinDeterministicKeyChain.class);
|
||||
class BtcDeterministicKeyChain extends DeterministicKeyChain {
|
||||
private static final Logger log = LoggerFactory.getLogger(BtcDeterministicKeyChain.class);
|
||||
|
||||
// See https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
|
||||
// https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
||||
// We use 0 (0x80000000) as coin_type for BTC
|
||||
// m / purpose' / coin_type' / account' / change / address_index
|
||||
public static final ImmutableList<ChildNumber> BIP44_BTC_ACCOUNT_PATH = ImmutableList.of(
|
||||
new ChildNumber(44, true),
|
||||
new ChildNumber(0, true),
|
||||
ChildNumber.ZERO_HARDENED,
|
||||
ChildNumber.ZERO_HARDENED);
|
||||
|
||||
public BitcoinDeterministicKeyChain(SecureRandom random) {
|
||||
public BtcDeterministicKeyChain(SecureRandom random) {
|
||||
super(random);
|
||||
}
|
||||
|
||||
public BitcoinDeterministicKeyChain(DeterministicKey accountKey, boolean isFollowingKey) {
|
||||
public BtcDeterministicKeyChain(DeterministicKey accountKey, boolean isFollowingKey) {
|
||||
super(accountKey, isFollowingKey);
|
||||
}
|
||||
|
||||
public BitcoinDeterministicKeyChain(DeterministicSeed seed, KeyCrypter crypter) {
|
||||
public BtcDeterministicKeyChain(DeterministicSeed seed, KeyCrypter crypter) {
|
||||
super(seed, crypter);
|
||||
}
|
||||
|
||||
public BtcDeterministicKeyChain(DeterministicSeed seed) {
|
||||
super(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImmutableList<ChildNumber> getAccountPath() {
|
||||
return BIP44_BTC_ACCOUNT_PATH;
|
@ -17,10 +17,7 @@
|
||||
|
||||
package io.bitsquare.btc.wallet;
|
||||
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
import org.bitcoinj.core.TransactionOutput;
|
||||
import org.bitcoinj.core.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -69,4 +66,9 @@ class SquCoinSelector extends BitsquareCoinSelector {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldSelect(Transaction tx) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ class SquDeterministicKeyChain extends DeterministicKeyChain {
|
||||
public static final ImmutableList<ChildNumber> BIP44_SQU_ACCOUNT_PATH = ImmutableList.of(
|
||||
new ChildNumber(44, true),
|
||||
new ChildNumber(139, true),
|
||||
ChildNumber.ZERO_HARDENED,
|
||||
ChildNumber.ZERO_HARDENED);
|
||||
|
||||
public SquDeterministicKeyChain(SecureRandom random) {
|
||||
@ -53,6 +52,10 @@ class SquDeterministicKeyChain extends DeterministicKeyChain {
|
||||
super(seed, crypter);
|
||||
}
|
||||
|
||||
public SquDeterministicKeyChain(DeterministicSeed seed) {
|
||||
super(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImmutableList<ChildNumber> getAccountPath() {
|
||||
return BIP44_SQU_ACCOUNT_PATH;
|
||||
|
@ -19,11 +19,9 @@ package io.bitsquare.btc.wallet;
|
||||
|
||||
import io.bitsquare.user.Preferences;
|
||||
import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
@ -39,32 +37,12 @@ public class SquUTXOProvider implements UTXOProvider {
|
||||
@Inject
|
||||
public SquUTXOProvider(Preferences preferences) {
|
||||
params = preferences.getBitcoinNetwork().getParameters();
|
||||
|
||||
setTransactions();
|
||||
}
|
||||
|
||||
private void setTransactions() {
|
||||
Transaction tx = new Transaction(null);
|
||||
boolean isCoinBase = tx.isCoinBase();
|
||||
int height = 1;
|
||||
Coin valueOut = Coin.ZERO;
|
||||
Sha256Hash hash = tx.getHash();
|
||||
for (TransactionOutput out : tx.getOutputs()) {
|
||||
valueOut = valueOut.add(out.getValue());
|
||||
// For each output, add it to the set of unspent outputs so it can be consumed in future.
|
||||
Script script = getScript(out.getScriptBytes());
|
||||
UTXO newOut = new UTXO(hash,
|
||||
out.getIndex(),
|
||||
out.getValue(),
|
||||
height,
|
||||
isCoinBase,
|
||||
script,
|
||||
getScriptAddress(script));
|
||||
utxoByAddressAsStringMap.put(newOut.getAddress(), newOut);
|
||||
}
|
||||
public void setUtxoSet(Set<UTXO> utxoSet) {
|
||||
utxoSet.stream().forEach(e -> utxoByAddressAsStringMap.put(e.getAddress(), e));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<UTXO> getOpenTransactionOutputs(List<Address> addresses) throws UTXOProviderException {
|
||||
// addressesAsStringSet.clear();
|
||||
@ -87,14 +65,15 @@ public class SquUTXOProvider implements UTXOProvider {
|
||||
|
||||
@Override
|
||||
public int getChainHeadHeight() throws UTXOProviderException {
|
||||
return 0;
|
||||
//TODO
|
||||
return 331;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkParameters getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
/*
|
||||
private String getScriptAddress(@Nullable Script script) {
|
||||
String address = "";
|
||||
try {
|
||||
@ -112,5 +91,5 @@ public class SquUTXOProvider implements UTXOProvider {
|
||||
} catch (Exception e) {
|
||||
return new Script(new byte[0]);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ import io.bitsquare.btc.exceptions.TransactionVerificationException;
|
||||
import io.bitsquare.btc.exceptions.WalletException;
|
||||
import io.bitsquare.btc.provider.fee.FeeService;
|
||||
import io.bitsquare.btc.provider.squ.SquUtxoFeedService;
|
||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.script.Script;
|
||||
@ -34,6 +36,7 @@ import org.slf4j.LoggerFactory;
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* WalletService handles all non trade specific wallet and bitcoin related services.
|
||||
@ -136,6 +139,17 @@ public class SquWalletService extends WalletService {
|
||||
return wallet != null ? wallet.getBalance(Wallet.BalanceType.AVAILABLE) : Coin.ZERO;
|
||||
}
|
||||
|
||||
public void requestSquUtxo(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
Set<UTXO> utxoSet = squUtxoFeedService.getUtxoSet();
|
||||
if (utxoSet != null)
|
||||
squUTXOProvider.setUtxoSet(utxoSet);
|
||||
|
||||
squUtxoFeedService.requestSquUtxo(utxos -> {
|
||||
squUTXOProvider.setUtxoSet(utxos);
|
||||
resultHandler.handleResult();
|
||||
},
|
||||
(errorMessage, throwable) -> errorMessageHandler.handleErrorMessage(errorMessage));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Send SQU with BTC fee
|
||||
@ -188,4 +202,5 @@ public class SquWalletService extends WalletService {
|
||||
printTx("signFinalSendTx", tx);
|
||||
return tx;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ import org.bitcoinj.store.BlockStoreException;
|
||||
import org.bitcoinj.store.SPVBlockStore;
|
||||
import org.bitcoinj.store.WalletProtobufSerializer;
|
||||
import org.bitcoinj.wallet.DeterministicSeed;
|
||||
import org.bitcoinj.wallet.KeyChainGroup;
|
||||
import org.bitcoinj.wallet.Protos;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
@ -58,12 +57,12 @@ import static com.google.common.base.Preconditions.checkState;
|
||||
public class WalletConfig extends AbstractIdleService {
|
||||
private static final Logger log = LoggerFactory.getLogger(WalletConfig.class);
|
||||
|
||||
private final String walletFilePrefix;
|
||||
private final String tokenWalletFilePrefix;
|
||||
private volatile Wallet vWallet;
|
||||
private final String btcWalletFilePrefix;
|
||||
private final String squWalletFilePrefix;
|
||||
private volatile Wallet vBtcWallet;
|
||||
private volatile Wallet vTokenWallet;
|
||||
private volatile File vWalletFile;
|
||||
private volatile File vTokenWalletFile;
|
||||
private volatile File vBtcWalletFile;
|
||||
private volatile File vSquWalletFile;
|
||||
@Nullable
|
||||
private DeterministicSeed btcSeed;
|
||||
@Nullable
|
||||
@ -91,34 +90,35 @@ public class WalletConfig extends AbstractIdleService {
|
||||
private final Context context;
|
||||
private long bloomFilterTweak = 0;
|
||||
private double bloomFilterFPRate = -1;
|
||||
private int lookaheadSize = -1;
|
||||
private int btcWalletLookaheadSize = -1;
|
||||
private int squWalletLookaheadSize = -1;
|
||||
|
||||
private Socks5Proxy socks5Proxy;
|
||||
|
||||
/**
|
||||
* Creates a new WalletAppKitBitSquare, with a newly created {@link Context}. Files will be stored in the given directory.
|
||||
*/
|
||||
public WalletConfig(NetworkParameters params, Socks5Proxy socks5Proxy, File directory, String walletFilePrefix, String tokenWalletFilePrefix) {
|
||||
this(new Context(params), directory, walletFilePrefix, tokenWalletFilePrefix);
|
||||
public WalletConfig(NetworkParameters params, Socks5Proxy socks5Proxy, File directory, String btcWalletFilePrefix, String squWalletFilePrefix) {
|
||||
this(new Context(params), directory, btcWalletFilePrefix, squWalletFilePrefix);
|
||||
this.socks5Proxy = socks5Proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WalletAppKitBitSquare, with a newly created {@link Context}. Files will be stored in the given directory.
|
||||
*/
|
||||
private WalletConfig(NetworkParameters params, File directory, String walletFilePrefix, String tokenWalletFilePrefix) {
|
||||
this(new Context(params), directory, walletFilePrefix, tokenWalletFilePrefix);
|
||||
private WalletConfig(NetworkParameters params, File directory, String btcWalletFilePrefix, String squWalletFilePrefix) {
|
||||
this(new Context(params), directory, btcWalletFilePrefix, squWalletFilePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WalletAppKitBitSquare, with the given {@link Context}. Files will be stored in the given directory.
|
||||
*/
|
||||
private WalletConfig(Context context, File directory, String walletFilePrefix, String tokenWalletFilePrefix) {
|
||||
private WalletConfig(Context context, File directory, String btcWalletFilePrefix, String squWalletFilePrefix) {
|
||||
this.context = context;
|
||||
this.params = checkNotNull(context.getParams());
|
||||
this.directory = checkNotNull(directory);
|
||||
this.walletFilePrefix = checkNotNull(walletFilePrefix);
|
||||
this.tokenWalletFilePrefix = tokenWalletFilePrefix;
|
||||
this.btcWalletFilePrefix = checkNotNull(btcWalletFilePrefix);
|
||||
this.squWalletFilePrefix = squWalletFilePrefix;
|
||||
if (!Utils.isAndroidRuntime()) {
|
||||
InputStream stream = WalletConfig.class.getResourceAsStream("/" + params.getId() + ".checkpoints");
|
||||
if (stream != null)
|
||||
@ -258,12 +258,12 @@ public class WalletConfig extends AbstractIdleService {
|
||||
* up the new kit. The next time your app starts it should work as normal (that is, don't keep calling this each
|
||||
* time).
|
||||
*/
|
||||
public WalletConfig restoreWalletFromSeed(DeterministicSeed seed) {
|
||||
public WalletConfig setBtcSeed(DeterministicSeed seed) {
|
||||
this.btcSeed = seed;
|
||||
return this;
|
||||
}
|
||||
|
||||
public WalletConfig restoreSquWalletFromSeed(DeterministicSeed seed) {
|
||||
public WalletConfig setSquSeed(DeterministicSeed seed) {
|
||||
this.squSeed = seed;
|
||||
return this;
|
||||
}
|
||||
@ -286,8 +286,13 @@ public class WalletConfig extends AbstractIdleService {
|
||||
return this;
|
||||
}
|
||||
|
||||
public WalletConfig setLookaheadSize(int lookaheadSize) {
|
||||
this.lookaheadSize = lookaheadSize;
|
||||
public WalletConfig setBtcWalletLookaheadSize(int lookaheadSize) {
|
||||
this.btcWalletLookaheadSize = lookaheadSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public WalletConfig setSquWalletLookaheadSize(int lookaheadSize) {
|
||||
this.squWalletLookaheadSize = lookaheadSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -323,7 +328,7 @@ public class WalletConfig extends AbstractIdleService {
|
||||
public boolean isChainFileLocked() throws IOException {
|
||||
RandomAccessFile file2 = null;
|
||||
try {
|
||||
File file = new File(directory, walletFilePrefix + ".spvchain");
|
||||
File file = new File(directory, btcWalletFilePrefix + ".spvchain");
|
||||
if (!file.exists())
|
||||
return false;
|
||||
if (file.isDirectory())
|
||||
@ -351,25 +356,27 @@ public class WalletConfig extends AbstractIdleService {
|
||||
}
|
||||
log.info("Starting up with directory = {}", directory);
|
||||
try {
|
||||
File chainFile = new File(directory, walletFilePrefix + ".spvchain");
|
||||
File chainFile = new File(directory, btcWalletFilePrefix + ".spvchain");
|
||||
boolean chainFileExists = chainFile.exists();
|
||||
|
||||
vWalletFile = new File(directory, walletFilePrefix + ".wallet");
|
||||
boolean shouldReplayBtcWallet = (vWalletFile.exists() && !chainFileExists) || btcSeed != null;
|
||||
// BTC wallet
|
||||
vBtcWalletFile = new File(directory, btcWalletFilePrefix + ".wallet");
|
||||
boolean shouldReplayBtcWallet = (vBtcWalletFile.exists() && !chainFileExists) || btcSeed != null;
|
||||
BitsquareKeyChainGroup keyChainGroup;
|
||||
if (btcSeed != null)
|
||||
keyChainGroup = new BitsquareKeyChainGroup(params, btcSeed, true);
|
||||
keyChainGroup = new BitsquareKeyChainGroup(params, new BtcDeterministicKeyChain(btcSeed), true, btcWalletLookaheadSize);
|
||||
else
|
||||
keyChainGroup = new BitsquareKeyChainGroup(params, true);
|
||||
vWallet = createOrLoadWallet(vWalletFile, shouldReplayBtcWallet, btcSeed, squSeed, keyChainGroup);
|
||||
keyChainGroup = new BitsquareKeyChainGroup(params, true, btcWalletLookaheadSize);
|
||||
vBtcWallet = createOrLoadWallet(vBtcWalletFile, shouldReplayBtcWallet, btcSeed, null, keyChainGroup);
|
||||
|
||||
vTokenWalletFile = new File(directory, tokenWalletFilePrefix + ".wallet");
|
||||
boolean shouldReplaySquWallet = (vTokenWalletFile.exists() && !chainFileExists) || squSeed != null;
|
||||
// SQU walelt
|
||||
vSquWalletFile = new File(directory, squWalletFilePrefix + ".wallet");
|
||||
boolean shouldReplaySquWallet = (vSquWalletFile.exists() && !chainFileExists) || squSeed != null;
|
||||
if (squSeed != null)
|
||||
keyChainGroup = new BitsquareKeyChainGroup(params, squSeed, false);
|
||||
keyChainGroup = new BitsquareKeyChainGroup(params, new SquDeterministicKeyChain(squSeed), false, squWalletLookaheadSize);
|
||||
else
|
||||
keyChainGroup = new BitsquareKeyChainGroup(params, false);
|
||||
vTokenWallet = createOrLoadWallet(vTokenWalletFile, shouldReplaySquWallet, btcSeed, squSeed, keyChainGroup);
|
||||
keyChainGroup = new BitsquareKeyChainGroup(params, false, squWalletLookaheadSize);
|
||||
vTokenWallet = createOrLoadWallet(vSquWalletFile, shouldReplaySquWallet, null, squSeed, keyChainGroup);
|
||||
|
||||
// Initiate Bitcoin network objects (block store, blockchain and peer group)
|
||||
vStore = provideBlockStore(chainFile);
|
||||
@ -389,7 +396,7 @@ public class WalletConfig extends AbstractIdleService {
|
||||
vStore = new SPVBlockStore(params, chainFile);
|
||||
}
|
||||
} else {
|
||||
time = vWallet.getEarliestKeyCreationTime();
|
||||
time = vBtcWallet.getEarliestKeyCreationTime();
|
||||
}
|
||||
|
||||
|
||||
@ -426,9 +433,9 @@ public class WalletConfig extends AbstractIdleService {
|
||||
} else if (params != RegTestParams.get() && !useTor) {
|
||||
vPeerGroup.addPeerDiscovery(discovery != null ? discovery : new DnsDiscovery(params));
|
||||
}
|
||||
vChain.addWallet(vWallet);
|
||||
vChain.addWallet(vBtcWallet);
|
||||
vChain.addWallet(vTokenWallet);
|
||||
vPeerGroup.addWallet(vWallet);
|
||||
vPeerGroup.addWallet(vBtcWallet);
|
||||
vPeerGroup.addWallet(vTokenWallet);
|
||||
onSetupCompleted();
|
||||
|
||||
@ -513,10 +520,7 @@ public class WalletConfig extends AbstractIdleService {
|
||||
return wallet;
|
||||
}
|
||||
|
||||
private Wallet createWallet(KeyChainGroup keyChainGroup) {
|
||||
if (lookaheadSize != -1)
|
||||
keyChainGroup.setLookaheadSize(lookaheadSize);
|
||||
|
||||
private Wallet createWallet(BitsquareKeyChainGroup keyChainGroup) {
|
||||
if (walletFactory != null) {
|
||||
return walletFactory.create(params, keyChainGroup);
|
||||
} else {
|
||||
@ -561,12 +565,12 @@ public class WalletConfig extends AbstractIdleService {
|
||||
try {
|
||||
Context.propagate(context);
|
||||
vPeerGroup.stop();
|
||||
vWallet.saveToFile(vWalletFile);
|
||||
vTokenWallet.saveToFile(vTokenWalletFile);
|
||||
vBtcWallet.saveToFile(vBtcWalletFile);
|
||||
vTokenWallet.saveToFile(vSquWalletFile);
|
||||
vStore.close();
|
||||
|
||||
vPeerGroup = null;
|
||||
vWallet = null;
|
||||
vBtcWallet = null;
|
||||
vTokenWallet = null;
|
||||
vStore = null;
|
||||
vChain = null;
|
||||
@ -591,7 +595,7 @@ public class WalletConfig extends AbstractIdleService {
|
||||
|
||||
public Wallet wallet() {
|
||||
checkState(state() == State.STARTING || state() == State.RUNNING, "Cannot call until startup is complete");
|
||||
return vWallet;
|
||||
return vBtcWallet;
|
||||
}
|
||||
|
||||
public Wallet tokenWallet() {
|
||||
|
@ -218,11 +218,13 @@ public class WalletsSetup {
|
||||
// Avoid the simple attack (see: https://jonasnick.github.io/blog/2015/02/12/privacy-in-bitcoinj/) due to the
|
||||
// default implementation using both pubkey and hash of pubkey. We have set a insertPubKey flag in BasicKeyChain to default false.
|
||||
|
||||
// Default only 266 keys are generated (2 * 100+33). That would trigger new bloom filters when we are reaching
|
||||
// Default only 266 keys are generated (2 * 100+33 -> 100 external and 100 internal keys + buffers of 30%). That would trigger new bloom filters when we are reaching
|
||||
// the threshold. To avoid reaching the threshold we create much more keys which are unlikely to cause update of the
|
||||
// filter for most users. With lookaheadSize of 500 we get 1333 keys which should be enough for most users to
|
||||
// filter for most users. With lookaheadSize of 500 we get 1333 keys (500*1.3=666 666 external and 666 internal keys) which should be enough for most users to
|
||||
// never need to update a bloom filter, which would weaken privacy.
|
||||
walletConfig.setLookaheadSize(500);
|
||||
// As we use 2 wallets (BTC, SQU) we generate 1333 + 266 keys in total.
|
||||
walletConfig.setBtcWalletLookaheadSize(500);
|
||||
walletConfig.setSquWalletLookaheadSize(100);
|
||||
|
||||
// Calculation is derived from: https://www.reddit.com/r/Bitcoin/comments/2vrx6n/privacy_in_bitcoinj_android_wallet_multibit_hive/coknjuz
|
||||
// No. of false positives (56M keys in the blockchain):
|
||||
@ -335,10 +337,8 @@ public class WalletsSetup {
|
||||
.setBlockingStartup(false)
|
||||
.setUserAgent(userAgent.getName(), userAgent.getVersion());
|
||||
|
||||
if (btcSeed != null)
|
||||
walletConfig.restoreWalletFromSeed(btcSeed);
|
||||
if (squSeed != null)
|
||||
walletConfig.restoreSquWalletFromSeed(squSeed);
|
||||
walletConfig.setBtcSeed(btcSeed);
|
||||
walletConfig.setSquSeed(squSeed);
|
||||
|
||||
walletConfig.addListener(new Service.Listener() {
|
||||
@Override
|
||||
|
@ -34,6 +34,7 @@ import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.btc.provider.fee.FeeService;
|
||||
import io.bitsquare.btc.provider.price.MarketPrice;
|
||||
import io.bitsquare.btc.provider.price.PriceFeedService;
|
||||
import io.bitsquare.btc.provider.squ.SquUtxoFeedService;
|
||||
import io.bitsquare.btc.wallet.BtcWalletService;
|
||||
import io.bitsquare.btc.wallet.WalletsManager;
|
||||
import io.bitsquare.btc.wallet.WalletsSetup;
|
||||
@ -115,6 +116,7 @@ public class MainViewModel implements ViewModel {
|
||||
private final TacWindow tacWindow;
|
||||
private final Clock clock;
|
||||
private final FeeService feeService;
|
||||
private final SquUtxoFeedService squUtxoFeedService;
|
||||
private final KeyRing keyRing;
|
||||
private final Navigation navigation;
|
||||
private final BSFormatter formatter;
|
||||
@ -182,7 +184,7 @@ public class MainViewModel implements ViewModel {
|
||||
OpenOfferManager openOfferManager, DisputeManager disputeManager, Preferences preferences,
|
||||
User user, AlertManager alertManager, PrivateNotificationManager privateNotificationManager,
|
||||
FilterManager filterManager, WalletPasswordWindow walletPasswordWindow, AddBitcoinNodesWindow addBitcoinNodesWindow,
|
||||
NotificationCenter notificationCenter, TacWindow tacWindow, Clock clock, FeeService feeService,
|
||||
NotificationCenter notificationCenter, TacWindow tacWindow, Clock clock, FeeService feeService, SquUtxoFeedService squUtxoFeedService,
|
||||
KeyRing keyRing, Navigation navigation, BSFormatter formatter) {
|
||||
this.walletsManager = walletsManager;
|
||||
this.walletsSetup = walletsSetup;
|
||||
@ -204,6 +206,7 @@ public class MainViewModel implements ViewModel {
|
||||
this.tacWindow = tacWindow;
|
||||
this.clock = clock;
|
||||
this.feeService = feeService;
|
||||
this.squUtxoFeedService = squUtxoFeedService;
|
||||
this.keyRing = keyRing;
|
||||
this.navigation = navigation;
|
||||
this.formatter = formatter;
|
||||
@ -546,7 +549,8 @@ public class MainViewModel implements ViewModel {
|
||||
p2PService.onAllServicesInitialized();
|
||||
|
||||
feeService.onAllServicesInitialized();
|
||||
|
||||
squUtxoFeedService.onAllServicesInitialized();
|
||||
|
||||
setupBtcNumPeersWatcher();
|
||||
setupP2PNumPeersWatcher();
|
||||
updateBalance();
|
||||
|
@ -21,8 +21,9 @@ import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.btc.wallet.SquWalletService;
|
||||
import io.bitsquare.gui.common.view.ActivatableView;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.main.overlays.popups.Popup;
|
||||
import io.bitsquare.gui.util.Layout;
|
||||
import io.bitsquare.gui.util.SQUFormatter;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import org.bitcoinj.core.Coin;
|
||||
@ -39,7 +40,7 @@ public class TokenDashboardView extends ActivatableView<GridPane, Void> {
|
||||
private TextField confirmedBalance;
|
||||
|
||||
private final SquWalletService squWalletService;
|
||||
private final BSFormatter formatter;
|
||||
private final SQUFormatter formatter;
|
||||
|
||||
private final int gridRow = 0;
|
||||
private BalanceListener balanceListener;
|
||||
@ -49,7 +50,7 @@ public class TokenDashboardView extends ActivatableView<GridPane, Void> {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
private TokenDashboardView(SquWalletService squWalletService, BSFormatter formatter) {
|
||||
private TokenDashboardView(SquWalletService squWalletService, SQUFormatter formatter) {
|
||||
this.squWalletService = squWalletService;
|
||||
this.formatter = formatter;
|
||||
}
|
||||
@ -69,6 +70,11 @@ public class TokenDashboardView extends ActivatableView<GridPane, Void> {
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
squWalletService.requestSquUtxo(() -> {
|
||||
updateBalance(squWalletService.getAvailableBalance());
|
||||
}, errorMessage -> {
|
||||
new Popup<>().warning(errorMessage);
|
||||
});
|
||||
squWalletService.addBalanceListener(balanceListener);
|
||||
|
||||
updateBalance(squWalletService.getAvailableBalance());
|
||||
|
@ -25,9 +25,9 @@ import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.components.AddressTextField;
|
||||
import io.bitsquare.gui.components.InputTextField;
|
||||
import io.bitsquare.gui.main.overlays.windows.QRCodeWindow;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.GUIUtil;
|
||||
import io.bitsquare.gui.util.Layout;
|
||||
import io.bitsquare.gui.util.SQUFormatter;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.Tooltip;
|
||||
@ -59,7 +59,7 @@ public class TokenReceiveView extends ActivatableView<GridPane, Void> {
|
||||
private TextField confirmedBalance;
|
||||
|
||||
private final SquWalletService squWalletService;
|
||||
private final BSFormatter formatter;
|
||||
private final SQUFormatter formatter;
|
||||
|
||||
@Nullable
|
||||
private Wallet squWallet;
|
||||
@ -74,7 +74,7 @@ public class TokenReceiveView extends ActivatableView<GridPane, Void> {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
private TokenReceiveView(SquWalletService squWalletService, BSFormatter formatter) {
|
||||
private TokenReceiveView(SquWalletService squWalletService, SQUFormatter formatter) {
|
||||
this.squWalletService = squWalletService;
|
||||
this.formatter = formatter;
|
||||
paymentLabelString = "Fund Bitsquare token wallet";
|
||||
|
Loading…
Reference in New Issue
Block a user