diff --git a/src/main/java/org/qortal/api/model/TradeBotCreateRequest.java b/src/main/java/org/qortal/api/model/TradeBotCreateRequest.java index 609f1735..c1db35e7 100644 --- a/src/main/java/org/qortal/api/model/TradeBotCreateRequest.java +++ b/src/main/java/org/qortal/api/model/TradeBotCreateRequest.java @@ -12,7 +12,7 @@ public class TradeBotCreateRequest { @Schema(description = "Trade creator's public key", example = "2zR1WFsbM7akHghqSCYKBPk6LDP8aKiQSRS1FrwoLvoB") public byte[] creatorPublicKey; - @Schema(description = "QORT amount paid out on successful trade", example = "8040200000") + @Schema(description = "QORT amount paid out on successful trade", example = "80.40200000") @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) public long qortAmount; @@ -20,7 +20,7 @@ public class TradeBotCreateRequest { @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) public long fundingQortAmount; - @Schema(description = "Bitcoin amount wanted in return", example = "000864200") + @Schema(description = "Bitcoin amount wanted in return", example = "0.00864200") @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) public long bitcoinAmount; diff --git a/src/main/java/org/qortal/api/resource/CrossChainResource.java b/src/main/java/org/qortal/api/resource/CrossChainResource.java index 41089e91..477a3ef9 100644 --- a/src/main/java/org/qortal/api/resource/CrossChainResource.java +++ b/src/main/java/org/qortal/api/resource/CrossChainResource.java @@ -32,6 +32,7 @@ import org.bitcoinj.core.ECKey; import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.TransactionOutput; +import org.qortal.account.Account; import org.qortal.account.PublicKeyAccount; import org.qortal.api.ApiError; import org.qortal.api.ApiErrors; @@ -866,6 +867,11 @@ public class CrossChainResource { @ApiErrors({ApiError.INVALID_PUBLIC_KEY, ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE}) public String tradeBotCreator(TradeBotCreateRequest tradeBotCreateRequest) { try (final Repository repository = RepositoryManager.getRepository()) { + // Do some simple checking first + Account creator = new PublicKeyAccount(repository, tradeBotCreateRequest.creatorPublicKey); + if (creator.getConfirmedBalance(Asset.QORT) < tradeBotCreateRequest.fundingQortAmount) + throw TransactionsResource.createTransactionInvalidException(request, ValidationResult.NO_BALANCE); + byte[] unsignedBytes = TradeBot.createTrade(repository, tradeBotCreateRequest); return Base58.encode(unsignedBytes); diff --git a/src/main/java/org/qortal/controller/Controller.java b/src/main/java/org/qortal/controller/Controller.java index 2f1e4175..948b46cc 100644 --- a/src/main/java/org/qortal/controller/Controller.java +++ b/src/main/java/org/qortal/controller/Controller.java @@ -238,11 +238,9 @@ public class Controller extends Thread { return this.chainTip; } - /** Cache new blockchain tip, maybe call trade-bot. */ + /** Cache new blockchain tip. */ public void setChainTip(BlockData blockData) { this.chainTip = blockData; - - TradeBot.getInstance().onChainTipChange(); } public ReentrantLock getBlockchainLock() { @@ -793,6 +791,9 @@ public class Controller extends Thread { this.notifyGroupMembershipChange = false; ChatNotifier.getInstance().onGroupMembershipChange(); } + + // Trade-bot might want to perform some actions too + TradeBot.getInstance().onChainTipChange(); }); } diff --git a/src/main/java/org/qortal/controller/TradeBot.java b/src/main/java/org/qortal/controller/TradeBot.java index 806805dc..8c8369e4 100644 --- a/src/main/java/org/qortal/controller/TradeBot.java +++ b/src/main/java/org/qortal/controller/TradeBot.java @@ -57,7 +57,7 @@ public class TradeBot { public static byte[] createTrade(Repository repository, TradeBotCreateRequest tradeBotCreateRequest) throws DataException { byte[] tradePrivateKey = generateTradePrivateKey(); byte[] secretB = generateSecret(); - byte[] hashOfSecretB = Crypto.digest(secretB); + byte[] hashOfSecretB = Crypto.hash160(secretB); byte[] tradeNativePublicKey = deriveTradeNativePublicKey(tradePrivateKey); byte[] tradeNativePublicKeyHash = Crypto.hash160(tradeNativePublicKey); @@ -83,15 +83,21 @@ public class TradeBot { long amount = tradeBotCreateRequest.fundingQortAmount; DeployAtTransactionData deployAtTransactionData = new DeployAtTransactionData(baseTransactionData, name, description, aTType, tags, creationBytes, amount, Asset.QORT); + + DeployAtTransaction deployAtTransaction = new DeployAtTransaction(repository, deployAtTransactionData); + fee = deployAtTransaction.calcRecommendedFee(); + deployAtTransactionData.setFee(fee); + DeployAtTransaction.ensureATAddress(deployAtTransactionData); String atAddress = deployAtTransactionData.getAtAddress(); - TradeBotData tradeBotData = new TradeBotData(tradePrivateKey, TradeBotData.State.BOB_WAITING_FOR_MESSAGE, + TradeBotData tradeBotData = new TradeBotData(tradePrivateKey, TradeBotData.State.BOB_WAITING_FOR_AT_CONFIRM, atAddress, tradeNativePublicKey, tradeNativePublicKeyHash, secretB, hashOfSecretB, tradeForeignPublicKey, tradeForeignPublicKeyHash, tradeBotCreateRequest.bitcoinAmount, null); repository.getCrossChainRepository().save(tradeBotData); + repository.saveChanges(); // Return to user for signing and broadcast as we don't have their Qortal private key try { @@ -121,6 +127,7 @@ public class TradeBot { tradeForeignPublicKey, tradeForeignPublicKeyHash, crossChainTradeData.expectedBitcoin, null); repository.getCrossChainRepository().save(tradeBotData); + repository.saveChanges(); // P2SH_a to be funded byte[] redeemScriptBytes = BTCP2SH.buildScript(tradeForeignPublicKeyHash, crossChainTradeData.lockTimeA, crossChainTradeData.creatorBitcoinPKH, secretHash); @@ -179,6 +186,7 @@ public class TradeBot { tradeBotData.setState(TradeBotData.State.BOB_WAITING_FOR_MESSAGE); repository.getCrossChainRepository().save(tradeBotData); + repository.saveChanges(); } private void handleBobWaitingForMessage(Repository repository, TradeBotData tradeBotData) throws DataException { @@ -252,6 +260,7 @@ public class TradeBot { } repository.getCrossChainRepository().save(tradeBotData); + repository.saveChanges(); } } diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java index f1f13e3f..346a1daa 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java @@ -620,11 +620,11 @@ public class HSQLDBDatabaseUpdates { case 20: // Trade bot - stmt.execute("CREATE TABLE TradeBotStates (trade_private_key QortalKeySeed NOT NULL, trade_state TINYINT NOT NULL, " + stmt.execute("CREATE TABLE TradeBotStates (trade_private_key QortalKeySeed NOT NULL, trade_state SMALLINT NOT NULL, " + "at_address QortalAddress, " + "trade_native_public_key QortalPublicKey NOT NULL, trade_native_public_key_hash VARBINARY(32) NOT NULL, " + "secret VARBINARY(32) NOT NULL, secret_hash VARBINARY(32) NOT NULL, " - + "trade_foreign_public_key QortalPublicKey NOT NULL, trade_foreign_public_key_hash VARBINARY(32) NOT NULL, " + + "trade_foreign_public_key VARBINARY(33) NOT NULL, trade_foreign_public_key_hash VARBINARY(32) NOT NULL, " + "bitcoin_amount BIGINT NOT NULL, last_transaction_signature Signature, PRIMARY KEY (trade_private_key))"); break;