mirror of
				https://github.com/haveno-dex/haveno.git
				synced 2025-10-31 03:19:09 -04:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master'
Conflicts: src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferController.java src/main/java/io/bitsquare/gui/util/BSFormatter.java
This commit is contained in:
		
						commit
						a9794a79d2
					
				
					 20 changed files with 78 additions and 551 deletions
				
			
		|  | @ -55,11 +55,6 @@ public class AddressBasedCoinSelector extends DefaultCoinSelector { | |||
|     // Constructor | ||||
|     /////////////////////////////////////////////////////////////////////////////////////////// | ||||
| 
 | ||||
|     /*public AddressBasedCoinSelector(NetworkParameters params, AddressInfo addressInfo) | ||||
|     { | ||||
|         this(params, addressInfo, false); | ||||
|     }   */ | ||||
| 
 | ||||
|     public AddressBasedCoinSelector(NetworkParameters params, AddressEntry addressEntry, boolean includePending) { | ||||
|         this.params = params; | ||||
|         this.addressEntry = addressEntry; | ||||
|  | @ -172,19 +167,4 @@ public class AddressBasedCoinSelector extends DefaultCoinSelector { | |||
|         return new CoinSelection(Coin.valueOf(total), selected); | ||||
|     } | ||||
| 
 | ||||
|     /* | ||||
|       public static boolean isSelectable(Transaction tx) | ||||
|     { | ||||
|         // Only pick chain-included transactions, or transactions that are ours and pending. | ||||
|         TransactionConfidence confidence = tx.getConfidence(); | ||||
|         TransactionConfidence.ConfidenceType type = confidence.getConfidenceType(); | ||||
|         return type.equals(TransactionConfidence.ConfidenceType.BUILDING) || | ||||
|                 type.equals(TransactionConfidence.ConfidenceType.PENDING) && | ||||
|                         confidence.getSource().equals(TransactionConfidence.Source.SELF) && | ||||
|                         // In regtest mode we expect to have only one peer, so we won't see transactions propagate. | ||||
|                         // TODO: The value 1 below dates from a time when transactions we broadcast *to* were | ||||
|                         // counted, set to 0 | ||||
|                         (confidence.numBroadcastPeers() > 1 || tx.getParams() == RegTestParams.get()); | ||||
|     } | ||||
|      */ | ||||
| } | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ import io.bitsquare.bank.BankAccount; | |||
| import javax.inject.Inject; | ||||
| 
 | ||||
| /** | ||||
|  * That facade delivers blockchain functionality from the bitcoinJ library | ||||
|  * A facade delivers blockchain functionality from the BitcoinJ library. | ||||
|  */ | ||||
| @SuppressWarnings({"SameReturnValue", "UnusedParameters"}) | ||||
| public class BlockChainFacade { | ||||
|  | @ -39,44 +39,31 @@ public class BlockChainFacade { | |||
|     //TODO | ||||
|     public boolean verifyAccountRegistration() { | ||||
|         return true; | ||||
| 
 | ||||
|         // tx id 76982adc582657b2eb68f3e43341596a68aadc4ef6b9590e88e93387d4d5d1f9 | ||||
|         // address: mjbxLbuVpU1cNXLJbrJZyirYwweoRPVVTj | ||||
|             /* | ||||
|         if (findAddressInBlockChain(address) && isFeePayed(address)) | ||||
|             return getDataForTxWithAddress(address) != null; | ||||
|         else | ||||
|             return true;  */ | ||||
|     } | ||||
| 
 | ||||
|     private boolean findAddressInBlockChain(String address) { | ||||
|         // TODO | ||||
|         // lookup for address in blockchain | ||||
|         // TODO lookup for address in blockchain | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private byte[] getDataForTxWithAddress(String address) { | ||||
|         // TODO | ||||
|         // return data after OP_RETURN | ||||
|         // TODO return data after OP_RETURN | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     private boolean isFeePayed(String address) { | ||||
|         // TODO | ||||
|         // check if fee is payed | ||||
|         // TODO check if fee is paid | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     private boolean isAccountIDBlacklisted(String accountID) { | ||||
|         // TODO | ||||
|         // check if accountID is on blacklist | ||||
|         // TODO check if accountID is on blacklist | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     private boolean isBankAccountBlacklisted(BankAccount bankAccount) { | ||||
|         // TODO | ||||
|         // check if accountID is on blacklist | ||||
|         // TODO check if accountID is on blacklist | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -35,16 +35,4 @@ public class BtcValidator { | |||
|         return amount != null && amount.compareTo(FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT)) > 0; | ||||
|     } | ||||
| 
 | ||||
|    /* public boolean isAddressValid(String addressString) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             new Address(BtcValidator.params, addressString); | ||||
|             return true; | ||||
|         } catch (AddressFormatException e) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } */ | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -182,8 +182,6 @@ public class WalletFacade { | |||
|         if (params == RegTestParams.get()) { | ||||
|             walletAppKit.peerGroup().setMinBroadcastConnections(1); | ||||
|         } | ||||
|        /* else | ||||
|             walletAppKit.peerGroup().setMinBroadcastConnections(2);  */ | ||||
| 
 | ||||
|         walletEventListener = new WalletEventListener() { | ||||
|             @Override | ||||
|  | @ -348,12 +346,6 @@ public class WalletFacade { | |||
|         if (transactions != null) { | ||||
|             transactionConfidenceList.addAll(transactions.stream().map(tx -> | ||||
|                     getTransactionConfidence(tx, address)).collect(Collectors.toList())); | ||||
|             /*  same as: | ||||
|              for (Transaction tx : transactions) | ||||
|             { | ||||
|                 transactionConfidenceList.add(getTransactionConfidence(tx, address)); | ||||
|             } | ||||
|              */ | ||||
|         } | ||||
|         return getMostRecentConfidence(transactionConfidenceList); | ||||
|     } | ||||
|  | @ -380,21 +372,6 @@ public class WalletFacade { | |||
|                 transactionConfidenceList.add(tx.getConfidence()); | ||||
|             } | ||||
|         }); | ||||
|         /* | ||||
|         same as: | ||||
|         for (TransactionOutput transactionOutput : mergedOutputs) | ||||
|         { | ||||
|             if (transactionOutput.getScriptPubKey().isSentToAddress() || | ||||
|                     transactionOutput.getScriptPubKey().isSentToP2SH()) | ||||
|             { | ||||
|                 Address outputAddress = transactionOutput.getScriptPubKey().getToAddress(params); | ||||
|                 if (address.equals(outputAddress)) | ||||
|                 { | ||||
|                     transactionConfidenceList.add(tx.getConfidence()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|          */ | ||||
|         return getMostRecentConfidence(transactionConfidenceList); | ||||
|     } | ||||
| 
 | ||||
|  | @ -502,20 +479,6 @@ public class WalletFacade { | |||
|         return getRegistrationBalance().compareTo(FeePolicy.ACCOUNT_REGISTRATION_FEE) >= 0; | ||||
|     } | ||||
| 
 | ||||
|   /*  public boolean isUnusedTradeAddressBalanceAboveCreationFee() | ||||
|     { | ||||
|         AddressEntry unUsedAddressEntry = getUnusedTradeAddressInfo(); | ||||
|         Coin unUsedAddressInfoBalance = getBalanceForAddress(unUsedAddressEntry.getAddress()); | ||||
|         return unUsedAddressInfoBalance.compareTo(FeePolicy.CREATE_OFFER_FEE) > 0; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isUnusedTradeAddressBalanceAboveTakeOfferFee() | ||||
|     { | ||||
|         AddressEntry unUsedAddressEntry = getUnusedTradeAddressInfo(); | ||||
|         Coin unUsedAddressInfoBalance = getBalanceForAddress(unUsedAddressEntry.getAddress()); | ||||
|         return unUsedAddressInfoBalance.compareTo(FeePolicy.TAKE_OFFER_FEE) > 0; | ||||
|     }*/ | ||||
| 
 | ||||
|     //TODO | ||||
|     public int getNumOfPeersSeenTx(String txID) { | ||||
|         // TODO check from blockchain | ||||
|  | @ -553,21 +516,7 @@ public class WalletFacade { | |||
|         sendRequest.coinSelector = new AddressBasedCoinSelector(params, getRegistrationAddressEntry(), false); | ||||
|         sendRequest.changeAddress = getRegistrationAddressEntry().getAddress(); | ||||
|         Wallet.SendResult sendResult = wallet.sendCoins(sendRequest); | ||||
|         //Object k = getRegistrationAddressInfo().getKey(); | ||||
|         Futures.addCallback(sendResult.broadcastComplete, callback); | ||||
|         /*Futures.addCallback(sendResult.broadcastComplete, new FutureCallback<Transaction>(){ | ||||
|             @Override | ||||
|             public void onSuccess(@Nullable Transaction result) | ||||
|             { | ||||
|                 Object k = getRegistrationAddressInfo().getKey(); | ||||
|             } | ||||
| 
 | ||||
|             @Override | ||||
|             public void onFailure(Throwable t) | ||||
|             { | ||||
| 
 | ||||
|             } | ||||
|         }); */ | ||||
| 
 | ||||
|         log.debug("Registration transaction: " + tx); | ||||
|         printInputs("payRegistrationFee", tx); | ||||
|  | @ -579,7 +528,6 @@ public class WalletFacade { | |||
|         Coin fee = FeePolicy.CREATE_OFFER_FEE.subtract(FeePolicy.TX_FEE); | ||||
|         log.trace("fee: " + fee.toFriendlyString()); | ||||
|         tx.addOutput(fee, feePolicy.getAddressForCreateOfferFee()); | ||||
|         // printInputs("payCreateOfferFee", tx); | ||||
|         Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx); | ||||
|         sendRequest.shuffleOutputs = false; | ||||
|         // we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to | ||||
|  | @ -1183,4 +1131,4 @@ public class WalletFacade { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -310,12 +310,6 @@ public class MainController extends ViewController { | |||
|     /////////////////////////////////////////////////////////////////////////////////////////// | ||||
| 
 | ||||
|     private void loadViewFromNavButton(NavigationItem navigationItem) { | ||||
| 
 | ||||
|        /* if (childController instanceof CachedViewController) | ||||
|             ((CachedViewController) childController).deactivate(); | ||||
|         else if (childController != null) | ||||
|             childController.terminate();*/ | ||||
| 
 | ||||
|         final GuiceFXMLLoader loader = new GuiceFXMLLoader(getClass().getResource(navigationItem.getFxmlUrl())); | ||||
|         try { | ||||
|             final Node view = loader.load(); | ||||
|  |  | |||
|  | @ -1,20 +1,3 @@ | |||
| /* | ||||
|  * 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/>. | ||||
|  */ | ||||
| 
 | ||||
| /* | ||||
|  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. | ||||
|  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||||
|  |  | |||
|  | @ -1,21 +1,3 @@ | |||
| /* | ||||
|  * 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.gui.components.confidence.behavior; | ||||
| /* | ||||
|  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. | ||||
|  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||||
|  | @ -41,6 +23,8 @@ package io.bitsquare.gui.components.confidence.behavior; | |||
|  * questions. | ||||
|  */ | ||||
| 
 | ||||
| package io.bitsquare.gui.components.confidence.behavior; | ||||
| 
 | ||||
| import io.bitsquare.gui.components.confidence.ConfidenceProgressIndicator; | ||||
| 
 | ||||
| import java.util.Collections; | ||||
|  |  | |||
|  | @ -1,20 +1,3 @@ | |||
| /* | ||||
|  * 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/>. | ||||
|  */ | ||||
| 
 | ||||
| /* | ||||
|  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. | ||||
|  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||||
|  | @ -93,17 +76,6 @@ public class ConfidenceProgressIndicatorSkin extends BehaviorSkinBase<Confidence | |||
|      * ************************************************************************ | ||||
|      */ | ||||
| 
 | ||||
|     //private static final String DONE = ControlResources.getString("ProgressIndicator.doneString"); | ||||
| 
 | ||||
|     /** | ||||
|      * doneText is just used to know the size of done as that is the biggest text we need to allow for | ||||
|      */ | ||||
|    /* private static final Text doneText = new Text(DONE); | ||||
|     static { | ||||
|         doneText.getStyleClass().add("text"); | ||||
|     }  */ | ||||
| 
 | ||||
| 
 | ||||
|     private IndeterminateSpinner spinner; | ||||
|     /** | ||||
|      * The number of segments in the spinner. | ||||
|  | @ -399,10 +371,6 @@ public class ConfidenceProgressIndicatorSkin extends BehaviorSkinBase<Confidence | |||
| 
 | ||||
|             getChildren().clear(); | ||||
| 
 | ||||
|           /*  text = new Text((control.getProgress() >= 1) ? (DONE) : ("" + intProgress + "%")); | ||||
|             text.setTextOrigin(VPos.TOP); | ||||
|             text.getStyleClass().setAll("text", "percentage"); */ | ||||
| 
 | ||||
|             // The circular background for the progress pie piece | ||||
|             indicator = new StackPane(); | ||||
|             indicator.setScaleShape(false); | ||||
|  | @ -519,17 +487,6 @@ public class ConfidenceProgressIndicatorSkin extends BehaviorSkinBase<Confidence | |||
|             tick.setLayoutY(centerY - squareBoxHalfWidth); | ||||
|             tick.resize(squareBoxHalfWidth + squareBoxHalfWidth, squareBoxHalfWidth + squareBoxHalfWidth); | ||||
|             tick.setVisible(control.getProgress() >= 1); | ||||
| 
 | ||||
|             // if the % text can't fit anywhere in the bounds then don't display it | ||||
|           /*  double textWidth = text.getLayoutBounds().getWidth(); | ||||
|             double textHeight = text.getLayoutBounds().getHeight(); | ||||
|             if (control.getWidth() >= textWidth && control.getHeight() >= textHeight) { | ||||
|                 if (!text.isVisible()) text.setVisible(true); | ||||
|                 text.setLayoutY(snapPosition(centerY + radius + textGap)); | ||||
|                 text.setLayoutX(snapPosition(centerX - (textWidth/2))); | ||||
|             } else { | ||||
|                 if (text.isVisible()) text.setVisible(false); | ||||
|             } */ | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|  |  | |||
|  | @ -143,13 +143,8 @@ class ProcessStepBarSkin<T> extends BehaviorSkinBase<ProcessStepBar<T>, Behavior | |||
|         } | ||||
| 
 | ||||
|         public void deSelect() { | ||||
|             /*BorderStroke borderStroke = new BorderStroke(Color.GRAY, BorderStrokeStyle.SOLID, null, | ||||
|                     new BorderWidths(borderWidth, borderWidth, borderWidth, borderWidth), Insets.EMPTY); | ||||
|             this.setBorder(new Border(borderStroke)); | ||||
|             setTextFill(Color.GRAY);  */ | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         public double getArrowWidth() { | ||||
|             return arrowWidth; | ||||
|         } | ||||
|  |  | |||
|  | @ -162,8 +162,7 @@ public class SettingsController extends CachedViewController { | |||
| 
 | ||||
|     @Override | ||||
|     public ViewController loadViewAndGetChildController(NavigationItem navigationItem) { | ||||
|         // TODO | ||||
|         // caching causes exception | ||||
|         // TODO caching causes exception | ||||
|         final GuiceFXMLLoader loader = new GuiceFXMLLoader(getClass().getResource(navigationItem.getFxmlUrl()), false); | ||||
|         try { | ||||
|             final Node view = loader.load(); | ||||
|  |  | |||
|  | @ -23,13 +23,10 @@ import io.bitsquare.trade.Direction; | |||
| import io.bitsquare.user.Arbitrator; | ||||
| 
 | ||||
| import com.google.bitcoin.core.Coin; | ||||
| import com.google.bitcoin.utils.CoinFormat; | ||||
| import com.google.bitcoin.utils.Fiat; | ||||
| 
 | ||||
| import java.text.DateFormat; | ||||
| import java.text.DecimalFormat; | ||||
| 
 | ||||
| import java.util.Currency; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
|  | @ -39,85 +36,38 @@ import org.slf4j.LoggerFactory; | |||
| 
 | ||||
| import static com.google.common.base.Preconditions.*; | ||||
| 
 | ||||
| //TODO a lot of old trash... need to cleanup... | ||||
| public class BSFormatter | ||||
| { | ||||
|     private static final Logger log = LoggerFactory.getLogger(BSFormatter.class); | ||||
| 
 | ||||
|     // format is like: 1,00 or 1,0010  never more then 4 decimals  | ||||
|     private static CoinFormat coinFormat = CoinFormat.BTC.repeatOptionalDecimals(2, 1); | ||||
|     // format is like: 1,00  never more then 2 decimals  | ||||
|     private static CoinFormat fiatFormat = CoinFormat.FIAT.repeatOptionalDecimals(0, 0); | ||||
|     private static String currencyCode = Currency.getInstance(Locale.getDefault()).getCurrencyCode(); | ||||
|     private static Locale locale = Locale.getDefault(); | ||||
| 
 | ||||
|     public static void useMilliBitFormat() | ||||
|     { | ||||
|         coinFormat = CoinFormat.MBTC.repeatOptionalDecimals(2, 1); | ||||
|     } | ||||
| 
 | ||||
|     public static void setFiatCurrencyCode(String currencyCode) | ||||
|     { | ||||
|         BSFormatter.currencyCode = currencyCode; | ||||
|     } | ||||
| 
 | ||||
|     public static void setLocale(Locale locale) | ||||
|     { | ||||
|         BSFormatter.locale = locale; | ||||
|     } | ||||
| 
 | ||||
| //TODO cleanup... | ||||
| public class BitSquareFormatter { | ||||
|     private static final Logger log = LoggerFactory.getLogger(BitSquareFormatter.class); | ||||
| 
 | ||||
|     /////////////////////////////////////////////////////////////////////////////////////////// | ||||
|     // BTC | ||||
|     /////////////////////////////////////////////////////////////////////////////////////////// | ||||
| 
 | ||||
|     public static String formatBtc(Coin coin) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             return coinFormat.noCode().format(coin).toString(); | ||||
|         } catch (Throwable t) | ||||
|         { | ||||
|             log.warn("Exception at formatBtc: " + t.toString()); | ||||
|             return ""; | ||||
|         } | ||||
|     public static String formatCoin(Coin coin) { | ||||
|         return coin != null ? coin.toPlainString() : ""; | ||||
|     } | ||||
| 
 | ||||
|     public static String formatBtcWithCode(Coin coin) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             return coinFormat.postfixCode().format(coin).toString(); | ||||
|         } catch (Throwable t) | ||||
|         { | ||||
|             log.warn("Exception at formatBtcWithCode: " + t.toString()); | ||||
|             return ""; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static Coin parseToCoin(String input) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             input = input.replace(",", "."); | ||||
|             Double.parseDouble(input); // test if valid double | ||||
|             return Coin.parseCoin(input); | ||||
|         } catch (Throwable t) | ||||
|         { | ||||
|             log.warn("Exception at parseToCoin: " + t.toString()); | ||||
|             return Coin.ZERO; | ||||
|         } | ||||
|     public static String formatCoinWithCode(Coin coin) { | ||||
|         return coin != null ? coin.toFriendlyString() : ""; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Transform a coin with the properties defined in the format (used to reduce decimal places) | ||||
|      * | ||||
|      * @param coin The coin which should be transformed | ||||
|      * @return The transformed coin | ||||
|      * @param input String input in decimal or integer format. Both decimal marks (",", ".") are supported. | ||||
|      *              If input has an incorrect format it returns a zero value coin. | ||||
|      * @return | ||||
|      */ | ||||
|     public static Coin applyFormatRules(Coin coin) | ||||
|     { | ||||
|         return parseToCoin(formatBtc(coin)); | ||||
|     public static Coin parseToCoin(String input) { | ||||
|         Coin result; | ||||
|         try { | ||||
|             input = input.replace(",", "."); | ||||
|             Double.parseDouble(input); | ||||
|             result = Coin.parseCoin(input); | ||||
|         } catch (Exception e) { | ||||
|             //log.warn("Exception at parseBtcToCoin: " + e.toString()); | ||||
|             result = Coin.ZERO; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -125,42 +75,16 @@ public class BSFormatter | |||
|     // FIAT | ||||
|     /////////////////////////////////////////////////////////////////////////////////////////// | ||||
| 
 | ||||
|     public static String formatFiat(Fiat fiat) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             return fiatFormat.noCode().format(fiat).toString(); | ||||
|         } catch (Throwable t) | ||||
|         { | ||||
|             log.warn("Exception at formatFiat: " + t.toString()); | ||||
|             return ""; | ||||
|         } | ||||
|     public static String formatPrice(double price) { | ||||
|         return formatDouble(price); | ||||
|     } | ||||
| 
 | ||||
|     public static String formatFiatWithCode(Fiat fiat) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             return fiatFormat.postfixCode().format(fiat).toString(); | ||||
|         } catch (Throwable t) | ||||
|         { | ||||
|             log.warn("Exception at formatFiatWithCode: " + t.toString()); | ||||
|             return ""; | ||||
|         } | ||||
|     public static String formatVolume(double volume) { | ||||
|         return formatDouble(volume); | ||||
|     } | ||||
| 
 | ||||
|     public static Fiat parseToFiat(String input) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             input = input.replace(",", "."); | ||||
|             Double.parseDouble(input); // test if valid double | ||||
|             return Fiat.parseFiat(currencyCode, input); | ||||
|         } catch (Exception e) | ||||
|         { | ||||
|             //log.warn("Exception at parseBtcToCoin: " + e.toString()); | ||||
|             return Fiat.valueOf(currencyCode, 0); | ||||
|         } | ||||
|     public static String formatVolumeWithMinVolume(double volume, double minVolume) { | ||||
|         return formatDouble(volume) + " (" + formatDouble(minVolume) + ")"; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -169,79 +93,79 @@ public class BSFormatter | |||
|     /////////////////////////////////////////////////////////////////////////////////////////// | ||||
| 
 | ||||
|     /** | ||||
|      * @param input String to be converted to a double. Both decimal points "." and "," are supported. Thousands separator is not supported. | ||||
|      * @param input String to be converted to a double. Both decimal points "." and ", | ||||
|      *              " are supported. Thousands separator is not supported. | ||||
|      * @return Returns a double value. Any invalid value returns Double.NEGATIVE_INFINITY. | ||||
|      */ | ||||
|     public static double parseToDouble(String input) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|     public static double parseToDouble(String input) { | ||||
|         try { | ||||
|             checkNotNull(input); | ||||
|             checkArgument(input.length() > 0); | ||||
|             input = input.replace(",", ".").trim(); | ||||
|             return Double.parseDouble(input); | ||||
|         } catch (Exception e) | ||||
|         { | ||||
|         } catch (Exception e) { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static String formatDirection(Direction direction, boolean allUpperCase) | ||||
|     { | ||||
|     public static String formatCollateralAsBtc(String amount, double collateral) { | ||||
|         Coin amountAsCoin = BitSquareFormatter.parseToCoin(amount); | ||||
|         Coin collateralAsCoin = amountAsCoin.divide((long) (1d / collateral)); | ||||
|         return formatCoinWithCode(collateralAsCoin); | ||||
|     } | ||||
| 
 | ||||
|     public static String formatTotalsAsBtc(String amount, double collateral, Coin fees) { | ||||
|         Coin amountAsCoin = BitSquareFormatter.parseToCoin(amount); | ||||
|         Coin collateralAsCoin = amountAsCoin.divide((long) (1d / collateral)); | ||||
|         Coin totals = collateralAsCoin.add(fees); | ||||
|         return formatCoinWithCode(totals); | ||||
|     } | ||||
| 
 | ||||
|     public static String formatDirection(Direction direction, boolean allUpperCase) { | ||||
|         String result = (direction == Direction.BUY) ? "Buy" : "Sell"; | ||||
|         if (allUpperCase) | ||||
|         { | ||||
|         if (allUpperCase) { | ||||
|             result = result.toUpperCase(); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     public static String formatDouble(double value) | ||||
|     { | ||||
|     public static String formatDouble(double value) { | ||||
|         return formatDouble(value, 4); | ||||
|     } | ||||
| 
 | ||||
|     public static String formatDouble(double value, int fractionDigits) | ||||
|     { | ||||
|     public static String formatDouble(double value, int fractionDigits) { | ||||
|         DecimalFormat decimalFormat = getDecimalFormat(fractionDigits); | ||||
|         return decimalFormat.format(value); | ||||
|     } | ||||
| 
 | ||||
|     public static DecimalFormat getDecimalFormat(int fractionDigits) | ||||
|     { | ||||
|         DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat.getInstance(locale); | ||||
|     public static DecimalFormat getDecimalFormat(int fractionDigits) { | ||||
|         DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat.getInstance(Locale.getDefault()); | ||||
|         decimalFormat.setMinimumFractionDigits(fractionDigits); | ||||
|         decimalFormat.setMaximumFractionDigits(fractionDigits); | ||||
|         decimalFormat.setGroupingUsed(false); | ||||
|         return decimalFormat; | ||||
|     } | ||||
| 
 | ||||
|     public static String countryLocalesToString(List<Country> countries) | ||||
|     { | ||||
|     public static String countryLocalesToString(List<Country> countries) { | ||||
|         String result = ""; | ||||
|         int i = 0; | ||||
|         for (Country country : countries) | ||||
|         { | ||||
|         for (Country country : countries) { | ||||
|             result += country.getName(); | ||||
|             i++; | ||||
|             if (i < countries.size()) | ||||
|             { | ||||
|             if (i < countries.size()) { | ||||
|                 result += ", "; | ||||
|             } | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     public static String languageLocalesToString(List<Locale> languageLocales) | ||||
|     { | ||||
|     public static String languageLocalesToString(List<Locale> languageLocales) { | ||||
|         String result = ""; | ||||
|         int i = 0; | ||||
|         for (Locale locale : languageLocales) | ||||
|         { | ||||
|         for (Locale locale : languageLocales) { | ||||
|             result += locale.getDisplayLanguage(); | ||||
|             i++; | ||||
|             if (i < languageLocales.size()) | ||||
|             { | ||||
|             if (i < languageLocales.size()) { | ||||
|                 result += ", "; | ||||
|             } | ||||
|         } | ||||
|  | @ -249,16 +173,13 @@ public class BSFormatter | |||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public static String arbitrationMethodsToString(List<Arbitrator.METHOD> items) | ||||
|     { | ||||
|     public static String arbitrationMethodsToString(List<Arbitrator.METHOD> items) { | ||||
|         String result = ""; | ||||
|         int i = 0; | ||||
|         for (Arbitrator.METHOD item : items) | ||||
|         { | ||||
|         for (Arbitrator.METHOD item : items) { | ||||
|             result += Localisation.get(item.toString()); | ||||
|             i++; | ||||
|             if (i < items.size()) | ||||
|             { | ||||
|             if (i < items.size()) { | ||||
|                 result += ", "; | ||||
|             } | ||||
|         } | ||||
|  | @ -266,62 +187,26 @@ public class BSFormatter | |||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public static String arbitrationIDVerificationsToString(List<Arbitrator.ID_VERIFICATION> items) | ||||
|     { | ||||
|     public static String arbitrationIDVerificationsToString(List<Arbitrator.ID_VERIFICATION> items) { | ||||
|         String result = ""; | ||||
|         int i = 0; | ||||
|         for (Arbitrator.ID_VERIFICATION item : items) | ||||
|         { | ||||
|         for (Arbitrator.ID_VERIFICATION item : items) { | ||||
|             result += Localisation.get(item.toString()); | ||||
|             i++; | ||||
|             if (i < items.size()) | ||||
|             { | ||||
|             if (i < items.size()) { | ||||
|                 result += ", "; | ||||
|             } | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     public static String formatDateTime(Date date) | ||||
|     { | ||||
|         DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.DEFAULT, locale); | ||||
|         DateFormat timeFormatter = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale); | ||||
|     public static String formatDateTime(Date date) { | ||||
|         DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.DEFAULT, Locale.getDefault()); | ||||
|         DateFormat timeFormatter = DateFormat.getTimeInstance(DateFormat.DEFAULT, Locale.getDefault()); | ||||
|         return dateFormatter.format(date) + " " + timeFormatter.format(date); | ||||
|     } | ||||
| 
 | ||||
|     public static String formatCollateralPercent(long collateral) | ||||
|     { | ||||
|         return getDecimalFormat(1).format(collateral / 10) + " %"; | ||||
|     } | ||||
| 
 | ||||
|     @Deprecated | ||||
|     public static String formatPrice(double volume) | ||||
|     { | ||||
|         return formatDouble(volume); | ||||
|     } | ||||
| 
 | ||||
|     @Deprecated | ||||
|     public static String formatVolume(double volume) | ||||
|     { | ||||
|         return formatDouble(volume); | ||||
|     } | ||||
| 
 | ||||
|     @Deprecated | ||||
|     public static String formatVolumeWithMinVolume(double volume, double minVolume) | ||||
|     { | ||||
|         return formatDouble(volume) + " (" + formatDouble(minVolume) + ")"; | ||||
|     } | ||||
| 
 | ||||
|     @Deprecated | ||||
|     public static String formatCoin(Coin coin) | ||||
|     { | ||||
|         return coin != null ? coin.toPlainString() : ""; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     @Deprecated | ||||
|     public static String formatCoinWithCode(Coin coin) | ||||
|     { | ||||
|         return coin != null ? coin.toFriendlyString() : ""; | ||||
|     public static String formatCollateralPercent(double collateral) { | ||||
|         return getDecimalFormat(2).format(collateral * 100) + " %"; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -120,16 +120,6 @@ public class CountryUtil { | |||
|         Set<Locale> allLocalesAsSet = | ||||
|                 allLocales.stream().filter(locale -> !"".equals(locale.getCountry())).map(locale -> | ||||
|                         new Locale("", locale.getCountry(), "")).collect(Collectors.toSet()); | ||||
|         /* | ||||
|         same as: | ||||
|         Set<Locale> allLocalesAsSet = new HashSet<>(); | ||||
|         for (Locale locale : allLocales) | ||||
|         { | ||||
|             if (!locale.getCountry().equals("")) | ||||
|             { | ||||
|                 allLocalesAsSet.add(new Locale("", locale.getCountry(), "")); | ||||
|             } | ||||
|         }  */ | ||||
| 
 | ||||
|         allLocales = new ArrayList<>(); | ||||
|         allLocales.addAll(allLocalesAsSet); | ||||
|  |  | |||
|  | @ -26,23 +26,6 @@ import java.util.stream.Collectors; | |||
| 
 | ||||
| public class LanguageUtil { | ||||
| 
 | ||||
|     /*public static List<Locale> getPopularLanguages() | ||||
|     { | ||||
|         List<Locale> list = new ArrayList<>(); | ||||
|         list.add(new Locale("de", "AT")); | ||||
|         list.add(new Locale("de", "DE")); | ||||
|         list.add(new Locale("en", "US")); | ||||
|         list.add(new Locale("en", "UK")); | ||||
|         list.add(new Locale("es", "ES")); | ||||
|         list.add(new Locale("ru", "RU")); | ||||
|         list.add(new Locale("zh", "CN")); | ||||
|         list.add(new Locale("en", "AU")); | ||||
|         list.add(new Locale("it", "IT")); | ||||
|         list.add(new Locale("en", "CA")); | ||||
|         return list; | ||||
|     }  */ | ||||
| 
 | ||||
| 
 | ||||
|     public static List<Locale> getAllLanguageLocales() { | ||||
|         List<Locale> allLocales = Arrays.asList(Locale.getAvailableLocales()); | ||||
|         final Set<Locale> allLocalesAsSet = | ||||
|  |  | |||
|  | @ -117,16 +117,6 @@ public class BootstrappedPeerFactory { | |||
|                 @Override | ||||
|                 public void peerInserted(PeerAddress peerAddress, boolean verified) { | ||||
|                     log.debug("Peer inserted: peerAddress=" + peerAddress + ", verified=" + verified); | ||||
| 
 | ||||
|                   /*  NavigableSet<PeerAddress> closePeers = peer.peerBean().peerMap().closePeers(2); | ||||
|                     log.debug("closePeers size  = " + closePeers.size()); | ||||
|                     log.debug("closePeers  = " + closePeers); | ||||
|                     closePeers.forEach(e -> log.debug("forEach: " + e.toString())); | ||||
| 
 | ||||
|                     List<PeerAddress> allPeers = peer.peerBean().peerMap().all(); | ||||
|                     log.debug("allPeers size  = " + allPeers.size()); | ||||
|                     log.debug("allPeers  = " + allPeers); | ||||
|                     allPeers.forEach(e -> log.debug("forEach: " + e.toString()));*/ | ||||
|                 } | ||||
| 
 | ||||
|                 @Override | ||||
|  | @ -136,7 +126,6 @@ public class BootstrappedPeerFactory { | |||
| 
 | ||||
|                 @Override | ||||
|                 public void peerUpdated(PeerAddress peerAddress, PeerStatatistic peerStatistics) { | ||||
|                     // log.debug("Peer updated: peerAddress=" + peerAddress + ", peerStatistics=" + peerStatistics); | ||||
|                 } | ||||
|             }); | ||||
| 
 | ||||
|  | @ -358,17 +347,5 @@ public class BootstrappedPeerFactory { | |||
|     // The seed node should only be used if no other known peers are available | ||||
|     private void requestBootstrapPeerMap() { | ||||
|         log.debug("getBootstrapPeerMap"); | ||||
| 
 | ||||
|       /*  NavigableSet<PeerAddress> closePeers = peer.peerBean().peerMap().closePeers(2); | ||||
|         log.debug("closePeers size  = " + closePeers.size()); | ||||
|         log.debug("closePeers  = " + closePeers); | ||||
|         closePeers.forEach(e -> log.debug("forEach: " + e.toString())); | ||||
| 
 | ||||
|         List<PeerAddress> allPeers = peer.peerBean().peerMap().all(); | ||||
|         log.debug("allPeers size  = " + allPeers.size()); | ||||
|         log.debug("allPeers  = " + allPeers); | ||||
|         allPeers.forEach(e -> log.debug("forEach: " + e.toString())); */ | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -377,16 +377,6 @@ public class MessageFacade implements MessageBroker { | |||
|         orderBookListeners.remove(listener); | ||||
|     } | ||||
| 
 | ||||
|    /* public void addPingPeerListener(PingPeerListener listener) | ||||
|     { | ||||
|         pingPeerListeners.add(listener); | ||||
|     } | ||||
| 
 | ||||
|     public void removePingPeerListener(PingPeerListener listener) | ||||
|     { | ||||
|         pingPeerListeners.remove(listener); | ||||
|     }    */ | ||||
| 
 | ||||
|     public void addArbitratorListener(ArbitratorListener listener) { | ||||
|         arbitratorListeners.add(listener); | ||||
|     } | ||||
|  |  | |||
|  | @ -73,32 +73,6 @@ public class P2PNode { | |||
|     private Thread bootstrapToLocalhostThread; | ||||
|     private Thread bootstrapToServerThread; | ||||
| 
 | ||||
|     // just for lightweight client test | ||||
|    /* public static void main(String[] args) | ||||
|     { | ||||
|         P2PNode p2pNode = new P2PNode(DSAKeyUtil.generateKeyPair(), false, SeedNodeAddress.StaticSeedNodeAddresses | ||||
|         .DIGITAL_OCEAN, | ||||
|                                       (message, peerAddress) -> log.debug("handleMessage: message= " + message + "/ | ||||
|                                       peerAddress=" + peerAddress)); | ||||
|         p2pNode.start(new FutureCallback<PeerDHT>() | ||||
|         { | ||||
|             @Override | ||||
|             public void onSuccess(@Nullable PeerDHT result) | ||||
|             { | ||||
|                 log.debug("p2pNode.start success result = " + result); | ||||
|             } | ||||
| 
 | ||||
|             @Override | ||||
|             public void onFailure(Throwable t) | ||||
|             { | ||||
|                 log.error(t.toString()); | ||||
|             } | ||||
|         }); | ||||
|         for (; ; ) | ||||
|         { | ||||
|         } | ||||
|     }*/ | ||||
| 
 | ||||
|     private KeyPair keyPair; | ||||
|     private final Boolean useDiskStorage; | ||||
|     private final SeedNodeAddress.StaticSeedNodeAddresses defaultStaticSeedNodeAddresses; | ||||
|  |  | |||
|  | @ -113,11 +113,7 @@ public class Settings implements Serializable { | |||
|         List<Arbitrator> candidates = new ArrayList<>(); | ||||
|         //noinspection Convert2streamapi | ||||
|         for (Arbitrator arbitrator : acceptedArbitrators) { | ||||
|             /*if (arbitrator.getArbitrationFeePercent() >= collateral && | ||||
|                     arbitrator.getMinArbitrationAmount().compareTo(amount) < 0) | ||||
|             {   */ | ||||
|             candidates.add(arbitrator); | ||||
|             // } | ||||
|         } | ||||
|         return !candidates.isEmpty() ? candidates.get((int) (Math.random() * candidates.size())) : null; | ||||
|     } | ||||
|  |  | |||
|  | @ -153,35 +153,6 @@ public class OrderBook implements OrderBookListener { | |||
|             boolean result = currencyResult && countryResult && languageResult && amountResult && directionResult && | ||||
|                     priceResult && arbitratorResult; | ||||
| 
 | ||||
|                 /* | ||||
|             log.debug("result = " + result + | ||||
|                     ", currencyResult = " + currencyResult + | ||||
|                     ", countryResult = " + countryResult + | ||||
|                     ", languageResult = " + languageResult + | ||||
|                     ", amountResult = " + amountResult + | ||||
|                     ", directionResult = " + directionResult + | ||||
|                     ", priceResult = " + priceResult + | ||||
|                     ", arbitratorResult = " + arbitratorResult | ||||
|             );*/ | ||||
|  /* | ||||
|             log.debug("currentBankAccount.getCurrency() = " + currentBankAccount.getCurrency() + | ||||
|                     ", offer.getCurrency() = " + offer.getCurrency()); | ||||
|             log.debug("offer.getCountryLocale() = " + offer.getBankAccountCountryLocale() + | ||||
|                     ", settings.getAcceptedCountries() = " + settings.getAcceptedCountries().toString()); | ||||
|             log.debug("settings.getAcceptedLanguageLocales() = " + settings.getAcceptedLanguageLocales() + | ||||
|                     ", offer.getAcceptedLanguageLocales() = " + offer.getAcceptedLanguageLocales()); | ||||
|             log.debug("currentBankAccount.getBankAccountType().getType() = " + currentBankAccount.getBankAccountType | ||||
|             ().getType() + | ||||
|                     ", offer.getBankAccountTypeEnum() = " + offer.getBankAccountTypeEnum()); | ||||
|             log.debug("orderBookFilter.getAmount() = " + orderBookFilter.getAmount() + | ||||
|                     ", offer.getAmount() = " + offer.getAmount()); | ||||
|             log.debug("orderBookFilter.getDirection() = " + orderBookFilter.getDirection() + | ||||
|                     ", offer.getDirection() = " + offer.getDirection()); | ||||
|             log.debug("orderBookFilter.getPrice() = " + orderBookFilter.getPrice() + | ||||
|                     ", offer.getPrice() = " + offer.getPrice()); | ||||
|             log.debug("offer.getArbitrator() = " + offer.getArbitrator() + | ||||
|                     ", settings.getAcceptedArbitrators() = " + settings.getAcceptedArbitrators()); | ||||
|                      */ | ||||
|             return result; | ||||
|         }); | ||||
|     } | ||||
|  |  | |||
|  | @ -54,27 +54,11 @@ public class VerifyAndSignContract { | |||
|                 bankAccount, peersBankAccount, messagePublicKey, takerMessagePublicKey); | ||||
| 
 | ||||
|         String contractAsJson = Utilities.objectToJson(contract); | ||||
|         // log.trace("Offerer contract created: " + contract); | ||||
|         // log.trace("Offerers contractAsJson: " + contractAsJson); | ||||
|         // log.trace("Takers contractAsJson: " + sharedModel.peersContractAsJson); | ||||
| 
 | ||||
|         //TODO PublicKey cause problems, need to be changed to hex | ||||
|         /*if (contractAsJson.equals(peersContractAsJson)) | ||||
|         {*/ | ||||
|         log.trace("The 2 contracts as json does match"); | ||||
|         String signature = cryptoFacade.signContract(registrationKey, contractAsJson); | ||||
|         //log.trace("signature: " + signature); | ||||
|         resultHandler.onResult(contract, contractAsJson, signature); | ||||
|        /* } | ||||
|         else | ||||
|         { | ||||
|             // TODO use diff output as feedback ? | ||||
|             log.error("Contracts are not matching."); | ||||
|             log.error("Offerers contractAsJson: " + contractAsJson); | ||||
|             log.error("Takers contractAsJson: " + peersContractAsJson); | ||||
| 
 | ||||
|             faultHandler.onFault(new Exception("Contracts are not matching")); | ||||
|         }*/ | ||||
|     } | ||||
| 
 | ||||
|     public interface ResultHandler { | ||||
|  |  | |||
|  | @ -36,44 +36,6 @@ public class FileUtil { | |||
|         return File.createTempFile("temp_" + prefix, null, StorageDirectory.getStorageDirectory()); | ||||
|     } | ||||
| 
 | ||||
|     /* | ||||
|     public static String getApplicationFileName() | ||||
|     { | ||||
|         File executionRoot = new File(StorageDirectory.class.getProtectionDomain().getCodeSource().getLocation() | ||||
|         .getFile()); | ||||
|         try | ||||
|         { | ||||
|             log.trace("getApplicationFileName " + executionRoot.getCanonicalPath()); | ||||
|         } catch (IOException e) | ||||
|         { | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|         // check if it is packed into a mac app  (e.g.: "/Users/mk/Desktop/bitsquare.app/Contents/Java/bitsquare.jar") | ||||
|         try | ||||
|         { | ||||
|             if (executionRoot.getCanonicalPath().endsWith(".app/Contents/Java/bitsquare.jar") && System.getProperty | ||||
|             ("os.name").startsWith("Mac")) | ||||
|             { | ||||
|                 File appFile = executionRoot.getParentFile().getParentFile().getParentFile(); | ||||
|                 try | ||||
|                 { | ||||
|                     int lastSlash = appFile.getCanonicalPath().lastIndexOf("/") + 1; | ||||
|                     return appFile.getCanonicalPath().substring(lastSlash).replace(".app", ""); | ||||
|                 } catch (IOException e) | ||||
|                 { | ||||
|                     e.printStackTrace(); | ||||
|                 } | ||||
|             } | ||||
|         } catch (IOException e) | ||||
|         { | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
| 
 | ||||
|         // fallback use AppName | ||||
|         return BitSquare.getAppName(); | ||||
|     } | ||||
| */ | ||||
| 
 | ||||
|     public static void writeTempFileToFile(File tempFile, File file) throws IOException { | ||||
|         if (Utils.isWindows()) { | ||||
|             // Work around an issue on Windows whereby you can't rename over existing files. | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Manfred Karrer
						Manfred Karrer