Don't allow more than one (active) trade-bot entry per trade-offer

This commit is contained in:
catbref 2020-12-28 15:55:14 +00:00
parent 3fdef9ea6d
commit 3ec307a2a1
6 changed files with 65 additions and 1 deletions

View File

@ -1,5 +1,7 @@
package org.qortal.controller.tradebot;
import java.util.List;
import org.qortal.api.model.crosschain.TradeBotCreateRequest;
import org.qortal.crosschain.ACCT;
import org.qortal.crosschain.ForeignBlockchainException;
@ -11,7 +13,10 @@ import org.qortal.repository.Repository;
public interface AcctTradeBot {
public enum ResponseResult { OK, BALANCE_ISSUE, NETWORK_ISSUE }
public enum ResponseResult { OK, BALANCE_ISSUE, NETWORK_ISSUE, TRADE_ALREADY_EXISTS }
/** Returns list of state names for trade-bot entries that have ended, e.g. redeemed, refunded or cancelled. */
public List<String> getEndStates();
public byte[] createTrade(Repository repository, TradeBotCreateRequest tradeBotCreateRequest) throws DataException;

View File

@ -6,6 +6,7 @@ import static java.util.stream.Collectors.toMap;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -110,6 +111,10 @@ public class BitcoinACCTv1TradeBot implements AcctTradeBot {
private static BitcoinACCTv1TradeBot instance;
private final List<String> endStates = Arrays.asList(State.BOB_DONE, State.BOB_REFUNDED, State.ALICE_DONE, State.ALICE_REFUNDING_A, State.ALICE_REFUNDING_B, State.ALICE_REFUNDED).stream()
.map(State::name)
.collect(Collectors.toUnmodifiableList());
private BitcoinACCTv1TradeBot() {
}
@ -120,6 +125,11 @@ public class BitcoinACCTv1TradeBot implements AcctTradeBot {
return instance;
}
@Override
public List<String> getEndStates() {
return this.endStates;
}
/**
* Creates a new trade-bot entry from the "Bob" viewpoint, i.e. OFFERing QORT in exchange for BTC.
* <p>

View File

@ -3,8 +3,10 @@ package org.qortal.controller.tradebot;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -102,6 +104,10 @@ public class LitecoinACCTv1TradeBot implements AcctTradeBot {
private static LitecoinACCTv1TradeBot instance;
private final List<String> endStates = Arrays.asList(State.BOB_DONE, State.BOB_REFUNDED, State.ALICE_DONE, State.ALICE_REFUNDING_A, State.ALICE_REFUNDED).stream()
.map(State::name)
.collect(Collectors.toUnmodifiableList());
private LitecoinACCTv1TradeBot() {
}
@ -112,6 +118,11 @@ public class LitecoinACCTv1TradeBot implements AcctTradeBot {
return instance;
}
@Override
public List<String> getEndStates() {
return this.endStates;
}
/**
* Creates a new trade-bot entry from the "Bob" viewpoint, i.e. OFFERing QORT in exchange for LTC.
* <p>

View File

@ -176,6 +176,10 @@ public class TradeBot implements Listener {
return ResponseResult.NETWORK_ISSUE;
}
// Check Alice doesn't already have an existing, on-going trade-bot entry for this AT.
if (repository.getCrossChainRepository().existsTradeWithAtExcludingStates(atData.getATAddress(), acctTradeBot.getEndStates()))
return ResponseResult.TRADE_ALREADY_EXISTS;
return acctTradeBot.startResponse(repository, atData, acct, crossChainTradeData, foreignKey, receivingAddress);
}

View File

@ -8,6 +8,9 @@ public interface CrossChainRepository {
public TradeBotData getTradeBotData(byte[] tradePrivateKey) throws DataException;
/** Returns true if there is an existing trade-bot entry relating to given AT address, excluding trade-bot entries with given states. */
public boolean existsTradeWithAtExcludingStates(String atAddress, List<String> excludeStates) throws DataException;
public List<TradeBotData> getAllTradeBotData() throws DataException;
public void save(TradeBotData tradeBotData) throws DataException;

View File

@ -3,6 +3,7 @@ package org.qortal.repository.hsqldb;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.qortal.data.crosschain.TradeBotData;
@ -68,6 +69,36 @@ public class HSQLDBCrossChainRepository implements CrossChainRepository {
}
}
@Override
public boolean existsTradeWithAtExcludingStates(String atAddress, List<String> excludeStates) throws DataException {
if (excludeStates == null)
excludeStates = Collections.emptyList();
StringBuilder whereClause = new StringBuilder(256);
whereClause.append("at_address = ?");
Object[] bindParams = new Object[1 + excludeStates.size()];
bindParams[0] = atAddress;
if (!excludeStates.isEmpty()) {
whereClause.append(" AND trade_state NOT IN (?");
bindParams[1] = excludeStates.get(0);
for (int i = 1; i < excludeStates.size(); ++i) {
whereClause.append(", ?");
bindParams[1 + i] = excludeStates.get(i);
}
whereClause.append(")");
}
try {
return this.repository.exists("TradeBotStates", whereClause.toString(), bindParams);
} catch (SQLException e) {
throw new DataException("Unable to check for trade-bot state in repository", e);
}
}
@Override
public List<TradeBotData> getAllTradeBotData() throws DataException {
String sql = "SELECT trade_private_key, acct_name, trade_state, trade_state_value, "