mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-03-15 10:26:37 -04:00
add offer validation
This commit is contained in:
parent
7069fb6c4d
commit
2cd3a5bae4
10
src/main/java/io/bitsquare/btc/Restritions.java
Normal file
10
src/main/java/io/bitsquare/btc/Restritions.java
Normal file
@ -0,0 +1,10 @@
|
||||
package io.bitsquare.btc;
|
||||
|
||||
import com.google.bitcoin.core.Coin;
|
||||
|
||||
public class Restritions
|
||||
{
|
||||
public static final Coin MIN_TRADE_AMOUNT = Coin.CENT; // 0.01 Bitcoins
|
||||
|
||||
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
package io.bitsquare.btc.tasks;
|
||||
|
||||
import com.google.bitcoin.core.*;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import io.bitsquare.btc.AddressBasedCoinSelector;
|
||||
import io.bitsquare.btc.AddressEntry;
|
||||
import io.bitsquare.btc.FeePolicy;
|
||||
import io.bitsquare.btc.WalletFacade;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class PayFeeTask
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(PayFeeTask.class);
|
||||
|
||||
private enum State
|
||||
{
|
||||
INIT,
|
||||
TX_COMPLETED,
|
||||
TX_COMMITTED,
|
||||
TX_BROAD_CASTED,
|
||||
}
|
||||
|
||||
private State state;
|
||||
|
||||
public String start(Wallet wallet, FeePolicy feePolicy, AddressEntry addressEntry, FutureCallback<Transaction> callback)
|
||||
{
|
||||
state = State.INIT;
|
||||
|
||||
Transaction tx = new Transaction(wallet.getParams());
|
||||
Coin fee = FeePolicy.CREATE_OFFER_FEE.subtract(FeePolicy.TX_FEE);
|
||||
log.trace("fee: " + fee.toFriendlyString());
|
||||
tx.addOutput(fee, feePolicy.getAddressForCreateOfferFee());
|
||||
|
||||
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 wait for 1 confirmation)
|
||||
sendRequest.coinSelector = new AddressBasedCoinSelector(wallet.getParams(), addressEntry, true);
|
||||
sendRequest.changeAddress = addressEntry.getAddress();
|
||||
|
||||
try
|
||||
{
|
||||
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
|
||||
state = State.TX_COMPLETED;
|
||||
|
||||
|
||||
} catch (IllegalStateException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
} catch (InsufficientMoneyException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
} catch (IllegalArgumentException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
} catch (Wallet.DustySendRequested e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
} catch (Wallet.CouldNotAdjustDownwards e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
} catch (Wallet.ExceededMaxTransactionSize e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
} catch (VerificationException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
/*
|
||||
* @throws IllegalStateException if no transaction broadcaster has been configured.
|
||||
* @throws InsufficientMoneyException if the request could not be completed due to not enough balance.
|
||||
* @throws IllegalArgumentException if you try and complete the same SendRequest twice
|
||||
* @throws DustySendRequested if the resultant transaction would violate the dust rules (an output that's too small to be worthwhile)
|
||||
* @throws CouldNotAdjustDownwards if emptying the wallet was requested and the output can't be shrunk for fees without violating a protocol rule.
|
||||
* @throws ExceededMaxTransactionSize if the resultant transaction is too big for Bitcoin to process (try breaking up the amounts of value)
|
||||
*/
|
||||
|
||||
|
||||
WalletFacade.printInputs("payCreateOfferFee", tx);
|
||||
log.debug("tx=" + tx);
|
||||
|
||||
return tx.getHashAsString();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -46,7 +46,7 @@ public class MessageFacade implements MessageBroker
|
||||
|
||||
public static interface AddOfferListener
|
||||
{
|
||||
void onComplete(String offerId);
|
||||
void onComplete();
|
||||
|
||||
void onFailed(String reason, Throwable throwable);
|
||||
}
|
||||
@ -54,6 +54,11 @@ public class MessageFacade implements MessageBroker
|
||||
private static final Logger log = LoggerFactory.getLogger(MessageFacade.class);
|
||||
private static final String ARBITRATORS_ROOT = "ArbitratorsRoot";
|
||||
|
||||
public P2PNode getP2pNode()
|
||||
{
|
||||
return p2pNode;
|
||||
}
|
||||
|
||||
private P2PNode p2pNode;
|
||||
|
||||
private final List<OrderBookListener> orderBookListeners = new ArrayList<>();
|
||||
@ -165,16 +170,16 @@ public class MessageFacade implements MessageBroker
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
Platform.runLater(() -> {
|
||||
addOfferListener.onComplete(offer.getId());
|
||||
orderBookListeners.stream().forEach(listener -> listener.onOfferAdded(data, future.isSuccess()));
|
||||
|
||||
// TODO will be removed when we don't use polling anymore
|
||||
setDirty(locationKey);
|
||||
});
|
||||
if (future.isSuccess())
|
||||
{
|
||||
Platform.runLater(() -> log.trace("Add offer to DHT was successful. Stored data: [key: " + locationKey + ", value: " + data + "]"));
|
||||
Platform.runLater(() -> {
|
||||
addOfferListener.onComplete();
|
||||
orderBookListeners.stream().forEach(listener -> listener.onOfferAdded(data, future.isSuccess()));
|
||||
|
||||
// TODO will be removed when we don't use polling anymore
|
||||
setDirty(locationKey);
|
||||
log.trace("Add offer to DHT was successful. Stored data: [key: " + locationKey + ", value: " + data + "]");
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -487,7 +492,7 @@ public class MessageFacade implements MessageBroker
|
||||
}
|
||||
}
|
||||
|
||||
private void setDirty(Number160 locationKey)
|
||||
public void setDirty(Number160 locationKey)
|
||||
{
|
||||
// we don't want to get an update from dirty for own changes, so update the lastTimeStamp to omit a change trigger
|
||||
lastTimeStamp = System.currentTimeMillis();
|
||||
|
@ -2,16 +2,17 @@ package io.bitsquare.trade.protocol.createoffer;
|
||||
|
||||
import com.google.bitcoin.core.Transaction;
|
||||
import io.bitsquare.btc.WalletFacade;
|
||||
import io.bitsquare.trade.protocol.createoffer.tasks.BroadCastOfferFeeTx;
|
||||
import io.bitsquare.trade.protocol.createoffer.tasks.CreateOfferFeeTx;
|
||||
import io.bitsquare.msg.MessageFacade;
|
||||
import io.bitsquare.trade.protocol.createoffer.tasks.PublishOfferToDHT;
|
||||
import io.bitsquare.storage.Persistence;
|
||||
import io.bitsquare.trade.Offer;
|
||||
import io.bitsquare.trade.handlers.FaultHandler;
|
||||
import io.bitsquare.trade.handlers.TransactionResultHandler;
|
||||
import io.bitsquare.trade.protocol.createoffer.tasks.BroadCastOfferFeeTx;
|
||||
import io.bitsquare.trade.protocol.createoffer.tasks.CreateOfferFeeTx;
|
||||
import io.bitsquare.trade.protocol.createoffer.tasks.PublishOfferToDHT;
|
||||
import io.bitsquare.trade.protocol.createoffer.tasks.ValidateOffer;
|
||||
import java.io.Serializable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -20,6 +21,8 @@ import org.slf4j.LoggerFactory;
|
||||
* It holds the model.state of the current process and support recovery if possible.
|
||||
*/
|
||||
//TODO recover policy, timer
|
||||
|
||||
@Immutable
|
||||
public class CreateOfferCoordinator
|
||||
{
|
||||
public enum State
|
||||
@ -32,11 +35,14 @@ public class CreateOfferCoordinator
|
||||
OFFER_PUBLISHED_TO_DHT
|
||||
}
|
||||
|
||||
/**
|
||||
* The model is not immutable but only exposed to the CreateOfferCoordinator
|
||||
*/
|
||||
static class Model implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 3027720554200858916L;
|
||||
|
||||
private Persistence persistence;
|
||||
private final Persistence persistence;
|
||||
private State state;
|
||||
//TODO use tx id and make Transaction transient
|
||||
Transaction transaction;
|
||||
@ -54,6 +60,8 @@ public class CreateOfferCoordinator
|
||||
public void setState(State state)
|
||||
{
|
||||
this.state = state;
|
||||
|
||||
//TODO will have performance issues, but could be handled inside the persistence solution (queue up save requests and exec. them on dedicated thread)
|
||||
persistence.write(this);
|
||||
}
|
||||
}
|
||||
@ -94,7 +102,7 @@ public class CreateOfferCoordinator
|
||||
private void onOfferValidated()
|
||||
{
|
||||
model.setState(State.VALIDATED);
|
||||
CreateOfferFeeTx.run(this::onOfferFeeTxCreated, this::onFailed, walletFacade, offer);
|
||||
CreateOfferFeeTx.run(this::onOfferFeeTxCreated, this::onFailed, walletFacade, offer.getId());
|
||||
}
|
||||
|
||||
private void onOfferFeeTxCreated(Transaction transaction)
|
||||
@ -114,6 +122,9 @@ public class CreateOfferCoordinator
|
||||
private void onOfferPublishedToDHT()
|
||||
{
|
||||
model.setState(State.OFFER_PUBLISHED_TO_DHT);
|
||||
// TODO
|
||||
//orderBookListeners.stream().forEach(listener -> listener.onOfferAdded(data, future.isSuccess()));
|
||||
|
||||
|
||||
resultHandler.onResult(model.transaction);
|
||||
}
|
||||
|
@ -2,9 +2,8 @@ package io.bitsquare.trade.protocol.createoffer.tasks;
|
||||
|
||||
import com.google.bitcoin.core.InsufficientMoneyException;
|
||||
import io.bitsquare.btc.WalletFacade;
|
||||
import io.bitsquare.trade.Offer;
|
||||
import io.bitsquare.trade.handlers.TransactionResultHandler;
|
||||
import io.bitsquare.trade.handlers.FaultHandler;
|
||||
import io.bitsquare.trade.handlers.TransactionResultHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -12,11 +11,11 @@ public class CreateOfferFeeTx
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(CreateOfferFeeTx.class);
|
||||
|
||||
public static void run(TransactionResultHandler resultHandler, FaultHandler faultHandler, WalletFacade walletFacade, Offer offer)
|
||||
public static void run(TransactionResultHandler resultHandler, FaultHandler faultHandler, WalletFacade walletFacade, String offerId)
|
||||
{
|
||||
try
|
||||
{
|
||||
resultHandler.onResult(walletFacade.createOfferFeeTx(offer.getId()));
|
||||
resultHandler.onResult(walletFacade.createOfferFeeTx(offerId));
|
||||
} catch (InsufficientMoneyException e)
|
||||
{
|
||||
faultHandler.onFault("Offer fee payment failed because there is insufficient money in the trade pocket. ", e);
|
||||
|
@ -16,7 +16,7 @@ public class PublishOfferToDHT
|
||||
messageFacade.addOffer(offer, new MessageFacade.AddOfferListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(String offerId)
|
||||
public void onComplete()
|
||||
{
|
||||
resultHandler.onResult();
|
||||
}
|
||||
|
@ -1,31 +1,54 @@
|
||||
package io.bitsquare.trade.protocol.createoffer.tasks;
|
||||
|
||||
import io.bitsquare.btc.Restritions;
|
||||
import io.bitsquare.trade.Offer;
|
||||
import io.bitsquare.trade.handlers.FaultHandler;
|
||||
import io.bitsquare.trade.handlers.ResultHandler;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@Immutable
|
||||
public class ValidateOffer
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(ValidateOffer.class);
|
||||
|
||||
public static void run(ResultHandler resultHandler, FaultHandler faultHandler, Offer offer)
|
||||
{
|
||||
boolean isValid = offer.getAmount().isGreaterThan(offer.getAmount());
|
||||
try
|
||||
{
|
||||
checkNotNull(offer.getAcceptedCountries());
|
||||
checkNotNull(offer.getAcceptedLanguageLocales());
|
||||
checkNotNull(offer.getAmount());
|
||||
checkNotNull(offer.getArbitrator());
|
||||
checkNotNull(offer.getBankAccountCountry());
|
||||
checkNotNull(offer.getBankAccountId());
|
||||
checkNotNull(offer.getCollateral());
|
||||
checkNotNull(offer.getCreationDate());
|
||||
checkNotNull(offer.getCurrency());
|
||||
checkNotNull(offer.getDirection());
|
||||
checkNotNull(offer.getId());
|
||||
checkNotNull(offer.getMessagePublicKey());
|
||||
checkNotNull(offer.getMinAmount());
|
||||
checkNotNull(offer.getOfferFeePaymentTxID());
|
||||
checkNotNull(offer.getPrice());
|
||||
|
||||
if (offer.getAmount().compareTo(offer.getMinAmount()) < 0)
|
||||
{
|
||||
faultHandler.onFault("Offer validation failed: Min. amount is larger than amount.", new Exception("Offer validation failed: Min. amount is larger than amount."));
|
||||
checkArgument(offer.getAcceptedCountries().size() > 0);
|
||||
checkArgument(offer.getAcceptedLanguageLocales().size() > 0);
|
||||
checkArgument(offer.getAmount().isGreaterThan(Restritions.MIN_TRADE_AMOUNT));
|
||||
checkArgument(offer.getAmount().compareTo(Restritions.MIN_TRADE_AMOUNT) >= 0);
|
||||
checkArgument(offer.getAmount().compareTo(offer.getMinAmount()) >= 0);
|
||||
checkArgument(offer.getCollateral() > 0);
|
||||
checkArgument(offer.getPrice() > 0);
|
||||
// TODO when offer is flattened continue here...
|
||||
|
||||
}
|
||||
else if (offer.getAcceptedCountries() == null || offer.getAcceptedCountries().size() == 0)
|
||||
{
|
||||
faultHandler.onFault("Offer validation failed: No accepted countries are defined.", new Exception("Offer validation failed: No accepted countries are defined."));
|
||||
} //TODO...
|
||||
else
|
||||
{
|
||||
resultHandler.onResult();
|
||||
} catch (Throwable t)
|
||||
{
|
||||
faultHandler.onFault("Offer validation failed with exception: " + t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user