Remove UTXOProvider -> use CoinSelector instead. Add prog args for block and wallet notification ports for rpc to support 2 apps

This commit is contained in:
Manfred Karrer 2017-01-10 03:06:58 +01:00
parent 7ea1718d86
commit dd591a44da
12 changed files with 68 additions and 102 deletions

View File

@ -80,7 +80,7 @@ public class BitsquareEnvironment extends StandardEnvironment {
private final String btcNetworkDir; private final String btcNetworkDir;
private final String logLevel, providers; private final String logLevel, providers;
private BitcoinNetwork bitcoinNetwork; private BitcoinNetwork bitcoinNetwork;
private final String btcNodes, seedNodes, ignoreDevMsg, useTorForBtc, rpcUser, rpcPassword, rpcPort, private final String btcNodes, seedNodes, ignoreDevMsg, useTorForBtc, rpcUser, rpcPassword, rpcPort, rpcBlockPort, rpcWalletPort,
myAddress, banList, dumpStatistics, maxMemory, socks5ProxyBtcAddress, socks5ProxyHttpAddress; myAddress, banList, dumpStatistics, maxMemory, socks5ProxyBtcAddress, socks5ProxyHttpAddress;
public BitsquareEnvironment(OptionSet options) { public BitsquareEnvironment(OptionSet options) {
@ -162,6 +162,13 @@ public class BitsquareEnvironment extends StandardEnvironment {
rpcPort = commandLineProperties.containsProperty(RpcOptionKeys.RPC_PORT) ? rpcPort = commandLineProperties.containsProperty(RpcOptionKeys.RPC_PORT) ?
(String) commandLineProperties.getProperty(RpcOptionKeys.RPC_PORT) : (String) commandLineProperties.getProperty(RpcOptionKeys.RPC_PORT) :
""; "";
rpcBlockPort = commandLineProperties.containsProperty(RpcOptionKeys.RPC_BLOCK_PORT) ?
(String) commandLineProperties.getProperty(RpcOptionKeys.RPC_BLOCK_PORT) :
"";
rpcWalletPort = commandLineProperties.containsProperty(RpcOptionKeys.RPC_WALLET_PORT) ?
(String) commandLineProperties.getProperty(RpcOptionKeys.RPC_WALLET_PORT) :
"";
myAddress = commandLineProperties.containsProperty(NetworkOptionKeys.MY_ADDRESS) ? myAddress = commandLineProperties.containsProperty(NetworkOptionKeys.MY_ADDRESS) ?
(String) commandLineProperties.getProperty(NetworkOptionKeys.MY_ADDRESS) : (String) commandLineProperties.getProperty(NetworkOptionKeys.MY_ADDRESS) :
""; "";
@ -258,6 +265,8 @@ public class BitsquareEnvironment extends StandardEnvironment {
setProperty(RpcOptionKeys.RPC_USER, rpcUser); setProperty(RpcOptionKeys.RPC_USER, rpcUser);
setProperty(RpcOptionKeys.RPC_PASSWORD, rpcPassword); setProperty(RpcOptionKeys.RPC_PASSWORD, rpcPassword);
setProperty(RpcOptionKeys.RPC_PORT, rpcPort); setProperty(RpcOptionKeys.RPC_PORT, rpcPort);
setProperty(RpcOptionKeys.RPC_BLOCK_PORT, rpcBlockPort);
setProperty(RpcOptionKeys.RPC_WALLET_PORT, rpcWalletPort);
setProperty(AppOptionKeys.BTC_NODES, btcNodes); setProperty(AppOptionKeys.BTC_NODES, btcNodes);
setProperty(AppOptionKeys.USE_TOR_FOR_BTC, useTorForBtc); setProperty(AppOptionKeys.USE_TOR_FOR_BTC, useTorForBtc);

View File

@ -119,6 +119,10 @@ public abstract class BitsquareExecutable {
.withRequiredArg(); .withRequiredArg();
parser.accepts(RpcOptionKeys.RPC_PORT, description("Bitcoind rpc port", "")) parser.accepts(RpcOptionKeys.RPC_PORT, description("Bitcoind rpc port", ""))
.withRequiredArg(); .withRequiredArg();
parser.accepts(RpcOptionKeys.RPC_BLOCK_PORT, description("Bitcoind rpc port for block notifications", ""))
.withRequiredArg();
parser.accepts(RpcOptionKeys.RPC_WALLET_PORT, description("Bitcoind rpc port for wallet notifications", ""))
.withRequiredArg();
parser.accepts(BtcOptionKeys.BTC_NETWORK, description("Bitcoin network", BitcoinNetwork.DEFAULT)) parser.accepts(BtcOptionKeys.BTC_NETWORK, description("Bitcoin network", BitcoinNetwork.DEFAULT))
.withRequiredArg() .withRequiredArg()

View File

@ -23,7 +23,10 @@ import io.bitsquare.app.AppOptionKeys;
import io.bitsquare.btc.provider.fee.FeeService; import io.bitsquare.btc.provider.fee.FeeService;
import io.bitsquare.btc.provider.price.PriceFeedService; import io.bitsquare.btc.provider.price.PriceFeedService;
import io.bitsquare.btc.provider.squ.SquUtxoFeedService; import io.bitsquare.btc.provider.squ.SquUtxoFeedService;
import io.bitsquare.btc.wallet.*; import io.bitsquare.btc.wallet.BtcWalletService;
import io.bitsquare.btc.wallet.SquWalletService;
import io.bitsquare.btc.wallet.TradeWalletService;
import io.bitsquare.btc.wallet.WalletsSetup;
import io.bitsquare.http.HttpClient; import io.bitsquare.http.HttpClient;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -66,7 +69,6 @@ public class BitcoinModule extends AppModule {
bind(SquUtxoFeedService.class).in(Singleton.class); bind(SquUtxoFeedService.class).in(Singleton.class);
bind(PriceFeedService.class).in(Singleton.class); bind(PriceFeedService.class).in(Singleton.class);
bind(FeeService.class).in(Singleton.class); bind(FeeService.class).in(Singleton.class);
bind(SquUTXOProvider.class).in(Singleton.class);
} }
} }

View File

@ -45,7 +45,7 @@ public abstract class BsDefaultCoinSelector implements CoinSelector {
ArrayList<TransactionOutput> sortedOutputs = new ArrayList<TransactionOutput>(candidates); ArrayList<TransactionOutput> sortedOutputs = new ArrayList<TransactionOutput>(candidates);
// When calculating the wallet balance, we may be asked to select all possible coins, if so, avoid sorting // When calculating the wallet balance, we may be asked to select all possible coins, if so, avoid sorting
// them in order to improve performance. // them in order to improve performance.
// TODO: Take in network parameters when instanatiated, and then test against the current network. Or just have a boolean parameter for "give me everything" // TODO: Take in network parameters when instantiated, and then test against the current network. Or just have a boolean parameter for "give me everything"
if (!target.equals(NetworkParameters.MAX_MONEY)) { if (!target.equals(NetworkParameters.MAX_MONEY)) {
sortOutputs(sortedOutputs); sortOutputs(sortedOutputs);
} }

View File

@ -17,13 +17,20 @@
package io.bitsquare.btc.wallet; package io.bitsquare.btc.wallet;
import io.bitsquare.dao.blockchain.SquUTXO;
import org.bitcoinj.core.Transaction; import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionConfidence; import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.core.TransactionOutput; import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.params.RegTestParams; import org.bitcoinj.params.RegTestParams;
import org.bitcoinj.script.Script;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/** /**
* We use a specialized version of the CoinSelector based on the DefaultCoinSelector implementation. * We use a specialized version of the CoinSelector based on the DefaultCoinSelector implementation.
* We lookup for spendable outputs which matches our address of our address. * We lookup for spendable outputs which matches our address of our address.
@ -33,18 +40,26 @@ class SquCoinSelector extends BsDefaultCoinSelector {
private final boolean permitForeignPendingTx; private final boolean permitForeignPendingTx;
private Map<Script, Set<SquUTXO>> utxoSetByScriptMap = new HashMap<>();
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public SquCoinSelector() {
this(true);
}
public SquCoinSelector(boolean permitForeignPendingTx) { public SquCoinSelector(boolean permitForeignPendingTx) {
this.permitForeignPendingTx = permitForeignPendingTx; this.permitForeignPendingTx = permitForeignPendingTx;
} }
public void setUtxoSet(Set<SquUTXO> utxoSet) {
utxoSet.stream().forEach(utxo -> {
Script script = utxo.getScript();
if (!utxoSetByScriptMap.containsKey(script))
utxoSetByScriptMap.put(script, new HashSet<>());
utxoSetByScriptMap.get(script).add(utxo);
});
}
@Override @Override
protected boolean isSelectable(Transaction tx) { protected boolean isSelectable(Transaction tx) {
TransactionConfidence confidence = tx.getConfidence(); TransactionConfidence confidence = tx.getConfidence();
@ -62,7 +77,12 @@ class SquCoinSelector extends BsDefaultCoinSelector {
@Override @Override
protected boolean selectOutput(TransactionOutput transactionOutput) { protected boolean selectOutput(TransactionOutput transactionOutput) {
return (transactionOutput.getScriptPubKey().isSentToAddress() || Script scriptPubKey = transactionOutput.getScriptPubKey();
transactionOutput.getScriptPubKey().isPayToScriptHash()); if (scriptPubKey.isSentToAddress() || scriptPubKey.isPayToScriptHash()) {
return utxoSetByScriptMap.containsKey(scriptPubKey);
} else {
log.warn("transactionOutput.getScriptPubKey() not isSentToAddress or isPayToScriptHash");
return false;
}
} }
} }

View File

@ -1,75 +0,0 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.btc.wallet;
import io.bitsquare.user.Preferences;
import org.bitcoinj.core.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import java.util.*;
public class SquUTXOProvider implements UTXOProvider {
private static final Logger log = LoggerFactory.getLogger(SquUTXOProvider.class);
private Map<String, Set<UTXO>> utxoSetByAddressMap = new HashMap<>();
private NetworkParameters parameters;
private int chainHeadHeight;
@Inject
public SquUTXOProvider(Preferences preferences) {
this.parameters = preferences.getBitcoinNetwork().getParameters();
}
public void setUtxoSet(Set<UTXO> utxoSet) {
utxoSet.stream().forEach(utxo -> {
String address = utxo.getAddress();
if (!utxoSetByAddressMap.containsKey(address))
utxoSetByAddressMap.put(address, new HashSet<>());
utxoSetByAddressMap.get(address).add(utxo);
});
log.info("utxoSetByAddressMap " + utxoSetByAddressMap.toString());
}
@Override
public List<UTXO> getOpenTransactionOutputs(List<Address> addresses) throws UTXOProviderException {
List<UTXO> result = new ArrayList<>();
addresses.stream()
.filter(address -> utxoSetByAddressMap.containsKey(address.toString()))
.forEach(address -> result.addAll(utxoSetByAddressMap.get(address.toString())));
return result;
}
public void setChainHeadHeight(int chainHeadHeight) {
this.chainHeadHeight = chainHeadHeight;
}
@Override
public int getChainHeadHeight() throws UTXOProviderException {
return chainHeadHeight;
}
@Override
public NetworkParameters getParams() {
return parameters;
}
}

View File

@ -52,7 +52,6 @@ public class SquWalletService extends WalletService {
private static final Logger log = LoggerFactory.getLogger(SquWalletService.class); private static final Logger log = LoggerFactory.getLogger(SquWalletService.class);
private final BlockchainService blockchainService; private final BlockchainService blockchainService;
private final SquUTXOProvider squUTXOProvider;
private final SquCoinSelector squCoinSelector; private final SquCoinSelector squCoinSelector;
@ -63,23 +62,18 @@ public class SquWalletService extends WalletService {
@Inject @Inject
public SquWalletService(WalletsSetup walletsSetup, public SquWalletService(WalletsSetup walletsSetup,
BlockchainService blockchainService, BlockchainService blockchainService,
SquUTXOProvider squUTXOProvider,
Preferences preferences, Preferences preferences,
FeeService feeService) { FeeService feeService) {
super(walletsSetup, super(walletsSetup,
preferences, preferences,
feeService); feeService);
this.blockchainService = blockchainService; this.blockchainService = blockchainService;
this.squUTXOProvider = squUTXOProvider; this.squCoinSelector = new SquCoinSelector(true);
this.squCoinSelector = new SquCoinSelector();
walletsSetup.addSetupCompletedHandler(() -> { walletsSetup.addSetupCompletedHandler(() -> {
wallet = walletsSetup.getSquWallet(); wallet = walletsSetup.getSquWallet();
wallet.setCoinSelector(squCoinSelector); wallet.setCoinSelector(squCoinSelector);
//TODO
wallet.setUTXOProvider(squUTXOProvider);
wallet.addEventListener(new BitsquareWalletEventListener()); wallet.addEventListener(new BitsquareWalletEventListener());
wallet.addEventListener(new AbstractWalletEventListener() { wallet.addEventListener(new AbstractWalletEventListener() {
@Override @Override
@ -167,12 +161,11 @@ public class SquWalletService extends WalletService {
} }
private void applyUtxoSetToUTXOProvider(Map<String, Map<Integer, SquUTXO>> utxoByTxIdMap) { private void applyUtxoSetToUTXOProvider(Map<String, Map<Integer, SquUTXO>> utxoByTxIdMap) {
squUTXOProvider.setChainHeadHeight(blockchainService.getChainHeadHeight()); Set<SquUTXO> utxoSet = new HashSet<>();
Set<UTXO> utxoSet = new HashSet<>();
utxoByTxIdMap.entrySet().stream() utxoByTxIdMap.entrySet().stream()
.forEach(e -> e.getValue().entrySet().stream() .forEach(e -> e.getValue().entrySet().stream()
.forEach(u -> utxoSet.add(u.getValue()))); .forEach(u -> utxoSet.add(u.getValue())));
squUTXOProvider.setUtxoSet(utxoSet); squCoinSelector.setUtxoSet(utxoSet);
} }
@ -253,7 +246,6 @@ public class SquWalletService extends WalletService {
public Transaction getPreparedBurnFeeTx(Coin fee) throws WalletException, TransactionVerificationException, public Transaction getPreparedBurnFeeTx(Coin fee) throws WalletException, TransactionVerificationException,
InsufficientMoneyException, ChangeBelowDustException { InsufficientMoneyException, ChangeBelowDustException {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
SquCoinSelector squCoinSelector = new SquCoinSelector();
CoinSelection coinSelection = squCoinSelector.select(fee, getTransactionOutputsFromUtxoProvider()); CoinSelection coinSelection = squCoinSelector.select(fee, getTransactionOutputsFromUtxoProvider());
coinSelection.gathered.stream().forEach(tx::addInput); coinSelection.gathered.stream().forEach(tx::addInput);
Coin change = squCoinSelector.getChange(fee, coinSelection); Coin change = squCoinSelector.getChange(fee, coinSelection);

View File

@ -54,6 +54,8 @@ public class DaoModule extends AppModule {
bindConstant().annotatedWith(named(RpcOptionKeys.RPC_USER)).to(env.getRequiredProperty(RpcOptionKeys.RPC_USER)); bindConstant().annotatedWith(named(RpcOptionKeys.RPC_USER)).to(env.getRequiredProperty(RpcOptionKeys.RPC_USER));
bindConstant().annotatedWith(named(RpcOptionKeys.RPC_PASSWORD)).to(env.getRequiredProperty(RpcOptionKeys.RPC_PASSWORD)); bindConstant().annotatedWith(named(RpcOptionKeys.RPC_PASSWORD)).to(env.getRequiredProperty(RpcOptionKeys.RPC_PASSWORD));
bindConstant().annotatedWith(named(RpcOptionKeys.RPC_PORT)).to(env.getRequiredProperty(RpcOptionKeys.RPC_PORT)); bindConstant().annotatedWith(named(RpcOptionKeys.RPC_PORT)).to(env.getRequiredProperty(RpcOptionKeys.RPC_PORT));
bindConstant().annotatedWith(named(RpcOptionKeys.RPC_BLOCK_PORT)).to(env.getRequiredProperty(RpcOptionKeys.RPC_BLOCK_PORT));
bindConstant().annotatedWith(named(RpcOptionKeys.RPC_WALLET_PORT)).to(env.getRequiredProperty(RpcOptionKeys.RPC_WALLET_PORT));
} }
} }

View File

@ -62,6 +62,8 @@ public class BlockchainRpcService extends BlockchainService {
private final String rpcUser; private final String rpcUser;
private final String rpcPassword; private final String rpcPassword;
private final String rpcPort; private final String rpcPort;
private final String rpcBlockPort;
private final String rpcWalletPort;
private final ListeningExecutorService setupExecutorService = Utilities.getListeningExecutorService("BlockchainRpcService.setup", 1, 1, 5); private final ListeningExecutorService setupExecutorService = Utilities.getListeningExecutorService("BlockchainRpcService.setup", 1, 1, 5);
private final ListeningExecutorService rpcRequestsExecutor = Utilities.getListeningExecutorService("BlockchainRpcService.requests", 1, 1, 10); private final ListeningExecutorService rpcRequestsExecutor = Utilities.getListeningExecutorService("BlockchainRpcService.requests", 1, 1, 10);
@ -75,10 +77,14 @@ public class BlockchainRpcService extends BlockchainService {
@Inject @Inject
public BlockchainRpcService(@Named(RpcOptionKeys.RPC_USER) String rpcUser, public BlockchainRpcService(@Named(RpcOptionKeys.RPC_USER) String rpcUser,
@Named(RpcOptionKeys.RPC_PASSWORD) String rpcPassword, @Named(RpcOptionKeys.RPC_PASSWORD) String rpcPassword,
@Named(RpcOptionKeys.RPC_PORT) String rpcPort) { @Named(RpcOptionKeys.RPC_PORT) String rpcPort,
@Named(RpcOptionKeys.RPC_BLOCK_PORT) String rpcBlockPort,
@Named(RpcOptionKeys.RPC_WALLET_PORT) String rpcWalletPort) {
this.rpcUser = rpcUser; this.rpcUser = rpcUser;
this.rpcPassword = rpcPassword; this.rpcPassword = rpcPassword;
this.rpcPort = rpcPort; this.rpcPort = rpcPort;
this.rpcBlockPort = rpcBlockPort;
this.rpcWalletPort = rpcWalletPort;
} }
@ -98,9 +104,11 @@ public class BlockchainRpcService extends BlockchainService {
try (FileInputStream fileInputStream = new FileInputStream(new File(resource.toURI()))) { try (FileInputStream fileInputStream = new FileInputStream(new File(resource.toURI()))) {
try (InputStream inputStream = new BufferedInputStream(fileInputStream)) { try (InputStream inputStream = new BufferedInputStream(fileInputStream)) {
nodeConfig.load(inputStream); nodeConfig.load(inputStream);
nodeConfig.setProperty("node.bitcoind.rpc.port", rpcPort);
nodeConfig.setProperty("node.bitcoind.rpc.user", rpcUser); nodeConfig.setProperty("node.bitcoind.rpc.user", rpcUser);
nodeConfig.setProperty("node.bitcoind.rpc.password", rpcPassword); nodeConfig.setProperty("node.bitcoind.rpc.password", rpcPassword);
nodeConfig.setProperty("node.bitcoind.rpc.port", rpcPort);
nodeConfig.setProperty("node.bitcoind.notification.block.port", rpcBlockPort);
nodeConfig.setProperty("node.bitcoind.notification.wallet.port", rpcWalletPort);
BtcdClientImpl client = new BtcdClientImpl(httpProvider, nodeConfig); BtcdClientImpl client = new BtcdClientImpl(httpProvider, nodeConfig);
daemon = new BtcdDaemonImpl(client); daemon = new BtcdDaemonImpl(client);
log.info("Setup took {} ms", System.currentTimeMillis() - startTs); log.info("Setup took {} ms", System.currentTimeMillis() - startTs);

View File

@ -32,7 +32,7 @@ public class BlockchainRpcServiceMain {
Log.setLevel(Level.WARN); Log.setLevel(Level.WARN);
// regtest uses port 18332, mainnet 8332 // regtest uses port 18332, mainnet 8332
BlockchainRpcService blockchainRpcService = new BlockchainRpcService(args[0], args[1], args[2]); BlockchainRpcService blockchainRpcService = new BlockchainRpcService(args[0], args[1], args[2], args[3], args[4]);
blockchainRpcService.onAllServicesInitialized(); blockchainRpcService.onAllServicesInitialized();
} }
} }

View File

@ -4,4 +4,6 @@ public class RpcOptionKeys {
public static final String RPC_USER = "rpcUser"; public static final String RPC_USER = "rpcUser";
public static final String RPC_PASSWORD = "rpcPassword"; public static final String RPC_PASSWORD = "rpcPassword";
public static final String RPC_PORT = "rpcPort"; public static final String RPC_PORT = "rpcPort";
public static final String RPC_BLOCK_PORT = "rpcBlockPort";
public static final String RPC_WALLET_PORT = "rpcWalletPort";
} }

View File

@ -1,12 +1,14 @@
node.bitcoind.rpc.protocol = http node.bitcoind.rpc.protocol = http
node.bitcoind.rpc.host = 127.0.0.1 node.bitcoind.rpc.host = 127.0.0.1
node.bitcoind.http.auth_scheme = Basic node.bitcoind.http.auth_scheme = Basic
node.bitcoind.notification.alert.port = 5158 #node.bitcoind.notification.alert.port = 5158
node.bitcoind.notification.block.port = 5159
node.bitcoind.notification.wallet.port = 5160
# we pass the port/user/pw via prog arg # we pass the port/user/pw via prog arg
#node.bitcoind.notification.block.port = 5159
#node.bitcoind.notification.wallet.port = 5160
# regtest # regtest
# node.bitcoind.rpc.port = 18332 # node.bitcoind.rpc.port = 18332
# mainnet # mainnet