/** * Copyright 2011 multibit.org * * Licensed under the MIT license (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://opensource.org/licenses/mit-license.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.multibit.viewsystem.swing.action; import com.google.bitcoin.core.Address; import com.google.bitcoin.core.AddressFormatException; import com.google.bitcoin.core.Transaction; import com.google.bitcoin.core.Wallet.BalanceType; import org.multibit.controller.Controller; import org.multibit.controller.bitcoin.BitcoinController; import org.multibit.exchange.CurrencyConverter; import org.multibit.exchange.CurrencyConverterResult; import org.multibit.model.bitcoin.BitcoinModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.math.BigInteger; /** * A class to validate String addresses and amounts. * TODO - this should create a validation state object and have a getter * * @author jim * */ public class Validator { private static final Logger log = LoggerFactory.getLogger(Validator.class); private final Controller controller; private final BitcoinController bitcoinController; public Validator(BitcoinController bitcoinController) { this.bitcoinController = bitcoinController; this.controller = this.bitcoinController; } /** * Validate a String address and amount. * * @param address * @param amount * @return */ public boolean validate(String address, String amount) { clearValidationState(); boolean validAddress = validateAddress(address); boolean validAmount = validateAmount(amount); return validAddress && validAmount; } private boolean validateAmount(String amount) { // Copy amount to wallet preferences. this.bitcoinController.getModel().setActiveWalletPreference(BitcoinModel.VALIDATION_AMOUNT_VALUE, amount); Boolean amountValidatesOk = Boolean.TRUE; Boolean amountIsInvalid = Boolean.FALSE; Boolean notEnoughFunds = Boolean.FALSE; Boolean amountIsMissing = Boolean.FALSE; Boolean amountIsNegativeOrZero = Boolean.FALSE; Boolean amountIsTooSmall = Boolean.FALSE; // See if the amount is missing. if (amount == null || "".equals(amount) || amount.trim().length() == 0) { amountIsMissing = Boolean.TRUE; amountValidatesOk = Boolean.FALSE; } else { // See if the amount is a number. BigInteger amountBigInteger = null; try { CurrencyConverterResult converterResult = CurrencyConverter.INSTANCE.parseToBTCNotLocalised(amount); if (converterResult.isBtcMoneyValid()) { // Parses ok. amountBigInteger = converterResult.getBtcMoney().getAmount().toBigInteger(); } else { amountIsInvalid = Boolean.TRUE; amountValidatesOk = Boolean.FALSE; } } catch (NumberFormatException nfe) { amountValidatesOk = Boolean.FALSE; amountIsInvalid = Boolean.TRUE; } catch (ArithmeticException ae) { amountValidatesOk = Boolean.FALSE; amountIsInvalid = Boolean.TRUE; } // See if the amount is negative or zero. if (amountValidatesOk.booleanValue()) { if (amountBigInteger.compareTo(BigInteger.ZERO) <= 0) { amountValidatesOk = Boolean.FALSE; amountIsNegativeOrZero = Boolean.TRUE; } else { if (amountBigInteger.compareTo(Transaction.MIN_NONDUST_OUTPUT) < 0) { amountValidatesOk = Boolean.FALSE; amountIsTooSmall = Boolean.TRUE; } else { // The fee is worked out in detail later, but we know it will be at least the minimum reference amount. BigInteger totalSpend = amountBigInteger.add(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE); BigInteger availableBalance = this.bitcoinController.getModel().getActiveWallet().getBalance(BalanceType.AVAILABLE); BigInteger estimatedBalance = this.bitcoinController.getModel().getActiveWallet().getBalance(BalanceType.ESTIMATED); log.debug("Amount = " + amountBigInteger.toString() + ", fee of at least " + Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.toString() + ", totalSpend = " + totalSpend.toString() + ", availableBalance = " + availableBalance.toString() + ", estimatedBalance = " + estimatedBalance.toString()); if (totalSpend.compareTo(availableBalance) > 0) { // Not enough funds. amountValidatesOk = Boolean.FALSE; notEnoughFunds = Boolean.TRUE; } } } } } this.bitcoinController.getModel().setActiveWalletPreference(BitcoinModel.VALIDATION_AMOUNT_IS_MISSING, amountIsMissing.toString()); this.bitcoinController.getModel().setActiveWalletPreference(BitcoinModel.VALIDATION_AMOUNT_IS_NEGATIVE_OR_ZERO, amountIsNegativeOrZero.toString()); this.bitcoinController.getModel().setActiveWalletPreference(BitcoinModel.VALIDATION_AMOUNT_IS_TOO_SMALL, amountIsTooSmall.toString()); this.bitcoinController.getModel().setActiveWalletPreference(BitcoinModel.VALIDATION_AMOUNT_IS_INVALID, amountIsInvalid.toString()); this.bitcoinController.getModel().setActiveWalletPreference(BitcoinModel.VALIDATION_NOT_ENOUGH_FUNDS, notEnoughFunds.toString()); return amountValidatesOk.booleanValue(); } private boolean validateAddress(String address) { Boolean addressIsInvalid = Boolean.TRUE; if (address != null && !address.isEmpty()) { // Copy address to wallet preferences. this.bitcoinController.getModel().setActiveWalletPreference(BitcoinModel.VALIDATION_ADDRESS_VALUE, address); try { new Address(this.bitcoinController.getModel().getNetworkParameters(), address); addressIsInvalid = Boolean.FALSE; } catch (AddressFormatException afe) { // Carry on. } catch (java.lang.StringIndexOutOfBoundsException e) { // Carry on. } } else { this.bitcoinController.getModel().setActiveWalletPreference(BitcoinModel.VALIDATION_ADDRESS_VALUE, ""); } this.bitcoinController.getModel().setActiveWalletPreference(BitcoinModel.VALIDATION_ADDRESS_IS_INVALID, addressIsInvalid.toString()); return !addressIsInvalid.booleanValue(); } public void clearValidationState() { this.bitcoinController.getModel().setActiveWalletPreference(BitcoinModel.VALIDATION_AMOUNT_IS_MISSING, Boolean.FALSE.toString()); this.bitcoinController.getModel().setActiveWalletPreference(BitcoinModel.VALIDATION_AMOUNT_IS_NEGATIVE_OR_ZERO, Boolean.FALSE.toString()); this.bitcoinController.getModel().setActiveWalletPreference(BitcoinModel.VALIDATION_AMOUNT_IS_TOO_SMALL, Boolean.FALSE.toString()); this.bitcoinController.getModel().setActiveWalletPreference(BitcoinModel.VALIDATION_AMOUNT_IS_INVALID, Boolean.FALSE.toString()); this.bitcoinController.getModel().setActiveWalletPreference(BitcoinModel.VALIDATION_NOT_ENOUGH_FUNDS, Boolean.FALSE.toString()); this.bitcoinController.getModel().setActiveWalletPreference(BitcoinModel.VALIDATION_ADDRESS_IS_INVALID, Boolean.FALSE.toString()); } }