Fix bug with restore form seed. Remove one path element at BIP 44 chain path. Add UTXO provider support.

This commit is contained in:
Manfred Karrer 2016-12-11 23:00:46 +01:00
parent 9dab7aa2ed
commit c288c107af
15 changed files with 181 additions and 121 deletions

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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]);
}
}
}*/
}

View File

@ -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;
}
}

View File

@ -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() {

View File

@ -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

View File

@ -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();

View File

@ -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());

View File

@ -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";