diff --git a/src/main/java/org/qortal/controller/Controller.java b/src/main/java/org/qortal/controller/Controller.java index 6c9bac27..2f1e4175 100644 --- a/src/main/java/org/qortal/controller/Controller.java +++ b/src/main/java/org/qortal/controller/Controller.java @@ -238,9 +238,11 @@ public class Controller extends Thread { return this.chainTip; } - /** Cache new blockchain tip. */ + /** Cache new blockchain tip, maybe call trade-bot. */ public void setChainTip(BlockData blockData) { this.chainTip = blockData; + + TradeBot.getInstance().onChainTipChange(); } public ReentrantLock getBlockchainLock() { diff --git a/src/main/java/org/qortal/controller/TradeBot.java b/src/main/java/org/qortal/controller/TradeBot.java new file mode 100644 index 00000000..fe2b16d1 --- /dev/null +++ b/src/main/java/org/qortal/controller/TradeBot.java @@ -0,0 +1,27 @@ +package org.qortal.controller; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class TradeBot { + + private static final Logger LOGGER = LogManager.getLogger(TradeBot.class); + + private static TradeBot instance; + + private TradeBot() { + + } + + public static synchronized TradeBot getInstance() { + if (instance == null) + instance = new TradeBot(); + + return instance; + } + + public void onChainTipChange() { + // Get repo for trade situations + } + +} diff --git a/src/main/java/org/qortal/data/crosschain/TradeBotData.java b/src/main/java/org/qortal/data/crosschain/TradeBotData.java new file mode 100644 index 00000000..25c27b83 --- /dev/null +++ b/src/main/java/org/qortal/data/crosschain/TradeBotData.java @@ -0,0 +1,97 @@ +package org.qortal.data.crosschain; + +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.toMap; + +import java.util.Map; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlTransient; + +import io.swagger.v3.oas.annotations.media.Schema; + +// All properties to be converted to JSON via JAXB +@XmlAccessorType(XmlAccessType.FIELD) +public class TradeBotData { + + public enum State { + BOB_START(0), BOB_WAITING_FOR_P2SH_A(10), BOB_WAITING_FOR_P2SH_B(20), BOB_WAITING_FOR_AT_REDEEM(30), + ALICE_START(100), ALICE_WAITING_FOR_P2SH_A(110), ALICE_WAITING_FOR_AT_LOCK(120), ALICE_WATCH_P2SH_B(130); + + public final int value; + private static final Map map = stream(State.values()).collect(toMap(state -> state.value, state -> state)); + + State(int value) { + this.value = value; + } + + public static State valueOf(int value) { + return map.get(value); + } + } + + private State tradeState; + + // Never expose this + @XmlTransient + @Schema(hidden = true) + private byte[] tradePrivateKey; + + private byte[] secret; + + private String atAddress; + + private byte[] lastTransactionSignature; + + public TradeBotData(byte[] tradePrivateKey, State tradeState, byte[] secret, String atAddress, + byte[] lastTransactionSignature) { + this.tradePrivateKey = tradePrivateKey; + this.tradeState = tradeState; + this.secret = secret; + this.atAddress = atAddress; + this.lastTransactionSignature = lastTransactionSignature; + } + + public TradeBotData(byte[] tradePrivateKey, State tradeState) { + this.tradePrivateKey = tradePrivateKey; + this.tradeState = tradeState; + } + + public State getState() { + return this.tradeState; + } + + public void setState(State state) { + this.tradeState = state; + } + + public byte[] getSecret() { + return this.secret; + } + + public void setSecret(byte[] secret) { + this.secret = secret; + } + + public byte[] getTradePrivateKey() { + return this.tradePrivateKey; + } + + public String getAtAddress() { + return this.atAddress; + } + + public void setAtAddress(String atAddress) { + this.atAddress = atAddress; + } + + public byte[] getLastTransactionSignature() { + return this.lastTransactionSignature; + } + + public void setLastTransactionSignature(byte[] lastTransactionSignature) { + this.lastTransactionSignature = lastTransactionSignature; + } + +} diff --git a/src/main/java/org/qortal/repository/CrossChainRepository.java b/src/main/java/org/qortal/repository/CrossChainRepository.java new file mode 100644 index 00000000..2c0ef31b --- /dev/null +++ b/src/main/java/org/qortal/repository/CrossChainRepository.java @@ -0,0 +1,11 @@ +package org.qortal.repository; + +import java.util.List; + +import org.qortal.data.crosschain.TradeBotData; + +public interface CrossChainRepository { + + public List getAllTradeBotData() throws DataException; + +} diff --git a/src/main/java/org/qortal/repository/Repository.java b/src/main/java/org/qortal/repository/Repository.java index 5c28253b..aecf4ef0 100644 --- a/src/main/java/org/qortal/repository/Repository.java +++ b/src/main/java/org/qortal/repository/Repository.java @@ -14,6 +14,8 @@ public interface Repository extends AutoCloseable { public ChatRepository getChatRepository(); + public CrossChainRepository getCrossChainRepository(); + public GroupRepository getGroupRepository(); public NameRepository getNameRepository(); diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBCrossChainRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBCrossChainRepository.java new file mode 100644 index 00000000..07f29961 --- /dev/null +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBCrossChainRepository.java @@ -0,0 +1,52 @@ +package org.qortal.repository.hsqldb; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import org.qortal.data.crosschain.TradeBotData; +import org.qortal.repository.CrossChainRepository; +import org.qortal.repository.DataException; + +public class HSQLDBCrossChainRepository implements CrossChainRepository { + + protected HSQLDBRepository repository; + + public HSQLDBCrossChainRepository(HSQLDBRepository repository) { + this.repository = repository; + } + + @Override + public List getAllTradeBotData() throws DataException { + String sql = "SELECT trade_private_key, trade_state, secret, at_address, last_transaction_signature " + + "FROM TradeBotStates"; + + List allTradeBotData = new ArrayList<>(); + + try (ResultSet resultSet = this.repository.checkedExecute(sql)) { + if (resultSet == null) + return allTradeBotData; + + do { + byte[] tradePrivateKey = resultSet.getBytes(1); + int tradeStateValue = resultSet.getInt(2); + TradeBotData.State tradeState = TradeBotData.State.valueOf(tradeStateValue); + if (tradeState == null) + throw new DataException("Illegal trade-bot trade-state fetched from repository"); + + byte[] secret = resultSet.getBytes(3); + String atAddress = resultSet.getString(4); + byte[] lastTransactionSignature = resultSet.getBytes(5); + + TradeBotData tradeBotData = new TradeBotData(tradePrivateKey, tradeState, secret, atAddress, lastTransactionSignature); + allTradeBotData.add(tradeBotData); + } while (resultSet.next()); + + return allTradeBotData; + } catch (SQLException e) { + throw new DataException("Unable to fetch trade-bot trading states from repository", e); + } + } + +} diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java index c4751f44..e142e956 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java @@ -618,6 +618,13 @@ public class HSQLDBDatabaseUpdates { stmt.execute("CREATE TABLE PublicizeTransactions (signature Signature, nonce INT NOT NULL, " + TRANSACTION_KEYS + ")"); break; + case 20: + // Trade bot + stmt.execute("CREATE TABLE TradeBotStates (trade_private_key QortalPrivateKey NOT NULL, trade_state TINYINT NOT NULL, " + + "secret VARBINARY(32) NOT NULL, at_address QortalAddress, " + + "last_transaction_signature Signature, PRIMARY KEY (trade_private_key)"); + break; + default: // nothing to do return false; diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java index 9c0ad9ab..dbee6cc0 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java @@ -32,6 +32,7 @@ import org.qortal.repository.ArbitraryRepository; import org.qortal.repository.AssetRepository; import org.qortal.repository.BlockRepository; import org.qortal.repository.ChatRepository; +import org.qortal.repository.CrossChainRepository; import org.qortal.repository.DataException; import org.qortal.repository.GroupRepository; import org.qortal.repository.NameRepository; @@ -115,6 +116,11 @@ public class HSQLDBRepository implements Repository { return new HSQLDBChatRepository(this); } + @Override + public CrossChainRepository getCrossChainRepository() { + return new HSQLDBCrossChainRepository(this); + } + @Override public GroupRepository getGroupRepository() { return new HSQLDBGroupRepository(this);