forked from Qortal/qortal
WIP: trade-bot. move trade-bot hook, fix bugs, etc.
Controller now calls TradeBot.onChainTipChange() inside thread started by Controller.onNewBlock(), instead of blocking Controller.setChainTip(). DB TradeBotStates has trade_foreign_public_key changed to VARBINARY(33) as Bitcoin pubkeys aren't uniformly 32 bytes! Also, trade_state changed from TINYINT to SMALLINT to cover enum value range. TradeBot.createTrade() incorrectly used Crypto.digest() to create hash-of-secret instead of Crypto.hash160(). Also corrected tradeState to BOB_WAITING_FOR_AT_CONFIRM. Also added missing fee calculation. Added missing repository.saveChanges() to TradeBot methods. Added balance check to API POST /crosschain/tradebot before passing request to TradeBot.createTrade(), which also ensures there's a usable account last-reference too.
This commit is contained in:
parent
11bf5ac6fc
commit
ee5119e4dd
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user