forked from Qortal/qortal
WIP on trade-bot
This commit is contained in:
parent
65ccb80aa4
commit
faa6e82bef
@ -24,6 +24,9 @@ public class CrossChainBuildRequest {
|
|||||||
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||||
public long fundingQortAmount;
|
public long fundingQortAmount;
|
||||||
|
|
||||||
|
@Schema(description = "HASH160 of creator's Bitcoin public key", example = "2daMveGc5pdjRyFacbxBzMksCbyC")
|
||||||
|
public byte[] bitcoinPublicKeyHash;
|
||||||
|
|
||||||
@Schema(description = "HASH160 of secret", example = "43vnftqkjxrhb5kJdkU1ZFQLEnWV")
|
@Schema(description = "HASH160 of secret", example = "43vnftqkjxrhb5kJdkU1ZFQLEnWV")
|
||||||
public byte[] secretHash;
|
public byte[] secretHash;
|
||||||
|
|
||||||
|
@ -180,7 +180,8 @@ public class CrossChainResource {
|
|||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
PublicKeyAccount creatorAccount = new PublicKeyAccount(repository, creatorPublicKey);
|
PublicKeyAccount creatorAccount = new PublicKeyAccount(repository, creatorPublicKey);
|
||||||
|
|
||||||
byte[] creationBytes = BTCACCT.buildQortalAT(creatorAccount.getAddress(), tradeRequest.secretHash, tradeRequest.tradeTimeout, tradeRequest.initialQortAmount, tradeRequest.finalQortAmount, tradeRequest.bitcoinAmount);
|
byte[] creationBytes = BTCACCT.buildQortalAT(creatorAccount.getAddress(), tradeRequest.bitcoinPublicKeyHash, tradeRequest.secretHash,
|
||||||
|
tradeRequest.tradeTimeout, tradeRequest.initialQortAmount, tradeRequest.finalQortAmount, tradeRequest.bitcoinAmount);
|
||||||
|
|
||||||
long txTimestamp = NTP.getTime();
|
long txTimestamp = NTP.getTime();
|
||||||
byte[] lastReference = creatorAccount.getLastReference();
|
byte[] lastReference = creatorAccount.getLastReference();
|
||||||
|
@ -11,15 +11,11 @@ import org.bitcoinj.core.ECKey;
|
|||||||
import org.bitcoinj.core.LegacyAddress;
|
import org.bitcoinj.core.LegacyAddress;
|
||||||
import org.bitcoinj.core.NetworkParameters;
|
import org.bitcoinj.core.NetworkParameters;
|
||||||
import org.qortal.account.PrivateKeyAccount;
|
import org.qortal.account.PrivateKeyAccount;
|
||||||
import org.qortal.api.ApiError;
|
|
||||||
import org.qortal.api.ApiExceptionFactory;
|
|
||||||
import org.qortal.crosschain.BTC;
|
import org.qortal.crosschain.BTC;
|
||||||
import org.qortal.crosschain.BTCACCT;
|
import org.qortal.crosschain.BTCACCT;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
import org.qortal.data.at.ATData;
|
|
||||||
import org.qortal.data.crosschain.CrossChainTradeData;
|
import org.qortal.data.crosschain.CrossChainTradeData;
|
||||||
import org.qortal.data.crosschain.TradeBotData;
|
import org.qortal.data.crosschain.TradeBotData;
|
||||||
import org.qortal.data.crosschain.CrossChainTradeData.Mode;
|
|
||||||
import org.qortal.repository.DataException;
|
import org.qortal.repository.DataException;
|
||||||
import org.qortal.repository.Repository;
|
import org.qortal.repository.Repository;
|
||||||
import org.qortal.repository.RepositoryManager;
|
import org.qortal.repository.RepositoryManager;
|
||||||
|
@ -237,6 +237,7 @@ public class BTCACCT {
|
|||||||
* 32-byte secret to the AT, before the AT automatically refunds the AT's creator.
|
* 32-byte secret to the AT, before the AT automatically refunds the AT's creator.
|
||||||
*
|
*
|
||||||
* @param qortalCreator Qortal address for AT creator, also used for refunds
|
* @param qortalCreator Qortal address for AT creator, also used for refunds
|
||||||
|
* @param bitcoinPublicKeyHash 20-byte HASH160 of creator's bitcoin public key
|
||||||
* @param secretHash 20-byte HASH160 of 32-byte secret
|
* @param secretHash 20-byte HASH160 of 32-byte secret
|
||||||
* @param tradeTimeout how many minutes, from start of 'trade mode' until AT auto-refunds AT creator
|
* @param tradeTimeout how many minutes, from start of 'trade mode' until AT auto-refunds AT creator
|
||||||
* @param initialPayout how much QORT to pay trade partner upon switch to 'trade mode'
|
* @param initialPayout how much QORT to pay trade partner upon switch to 'trade mode'
|
||||||
@ -244,7 +245,7 @@ public class BTCACCT {
|
|||||||
* @param bitcoinAmount how much BTC the AT creator is expecting to trade
|
* @param bitcoinAmount how much BTC the AT creator is expecting to trade
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static byte[] buildQortalAT(String qortalCreator, byte[] secretHash, int tradeTimeout, long initialPayout, long redeemPayout, long bitcoinAmount) {
|
public static byte[] buildQortalAT(String qortalCreator, byte[] bitcoinPublicKeyHash, byte[] secretHash, int tradeTimeout, long initialPayout, long redeemPayout, long bitcoinAmount) {
|
||||||
// Labels for data segment addresses
|
// Labels for data segment addresses
|
||||||
int addrCounter = 0;
|
int addrCounter = 0;
|
||||||
|
|
||||||
@ -255,6 +256,9 @@ public class BTCACCT {
|
|||||||
final int addrQortalCreator3 = addrCounter++;
|
final int addrQortalCreator3 = addrCounter++;
|
||||||
final int addrQortalCreator4 = addrCounter++;
|
final int addrQortalCreator4 = addrCounter++;
|
||||||
|
|
||||||
|
final int addrBitcoinPublickeyHash = addrCounter;
|
||||||
|
addrCounter += 4;
|
||||||
|
|
||||||
final int addrSecretHash = addrCounter;
|
final int addrSecretHash = addrCounter;
|
||||||
addrCounter += 4;
|
addrCounter += 4;
|
||||||
|
|
||||||
@ -303,6 +307,10 @@ public class BTCACCT {
|
|||||||
byte[] qortalCreatorBytes = Base58.decode(qortalCreator);
|
byte[] qortalCreatorBytes = Base58.decode(qortalCreator);
|
||||||
dataByteBuffer.put(Bytes.ensureCapacity(qortalCreatorBytes, 32, 0));
|
dataByteBuffer.put(Bytes.ensureCapacity(qortalCreatorBytes, 32, 0));
|
||||||
|
|
||||||
|
// Bitcoin public key hash
|
||||||
|
assert dataByteBuffer.position() == addrBitcoinPublickeyHash * MachineState.VALUE_SIZE : "addrBitcoinPublicKeyHash incorrect";
|
||||||
|
dataByteBuffer.put(Bytes.ensureCapacity(bitcoinPublicKeyHash, 32, 0));
|
||||||
|
|
||||||
// Hash of secret
|
// Hash of secret
|
||||||
assert dataByteBuffer.position() == addrSecretHash * MachineState.VALUE_SIZE : "addrSecretHash incorrect";
|
assert dataByteBuffer.position() == addrSecretHash * MachineState.VALUE_SIZE : "addrSecretHash incorrect";
|
||||||
dataByteBuffer.put(Bytes.ensureCapacity(secretHash, 32, 0));
|
dataByteBuffer.put(Bytes.ensureCapacity(secretHash, 32, 0));
|
||||||
@ -559,6 +567,11 @@ public class BTCACCT {
|
|||||||
// Skip AT creator address
|
// Skip AT creator address
|
||||||
dataByteBuffer.position(dataByteBuffer.position() + 32);
|
dataByteBuffer.position(dataByteBuffer.position() + 32);
|
||||||
|
|
||||||
|
// Bitcoin/foreign public key hash
|
||||||
|
tradeData.foreignPublicKeyHash = new byte[20];
|
||||||
|
dataByteBuffer.get(tradeData.foreignPublicKeyHash);
|
||||||
|
dataByteBuffer.position(dataByteBuffer.position() + 32 - 20); // skip to 32 bytes
|
||||||
|
|
||||||
// Hash of secret
|
// Hash of secret
|
||||||
tradeData.secretHash = new byte[20];
|
tradeData.secretHash = new byte[20];
|
||||||
dataByteBuffer.get(tradeData.secretHash);
|
dataByteBuffer.get(tradeData.secretHash);
|
||||||
|
@ -59,6 +59,8 @@ public class CrossChainTradeData {
|
|||||||
@Schema(description = "Suggested Bitcoin P2SH nLockTime based on trade timeout")
|
@Schema(description = "Suggested Bitcoin P2SH nLockTime based on trade timeout")
|
||||||
public Integer lockTime;
|
public Integer lockTime;
|
||||||
|
|
||||||
|
public byte[] foreignPublicKeyHash;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
// Necessary for JAXB
|
// Necessary for JAXB
|
||||||
|
@ -620,12 +620,12 @@ public class HSQLDBDatabaseUpdates {
|
|||||||
|
|
||||||
case 20:
|
case 20:
|
||||||
// Trade bot
|
// Trade bot
|
||||||
stmt.execute("CREATE TABLE TradeBotStates (trade_private_key QortalPrivateKey NOT NULL, trade_state TINYINT NOT NULL, "
|
stmt.execute("CREATE TABLE TradeBotStates (trade_private_key QortalKeySeed NOT NULL, trade_state TINYINT NOT NULL, "
|
||||||
+ "trade_native_public_key QortalPublicKey NOT NULL, trade_native_public_key_hash VARBINARY(32) NOT NULL, "
|
+ "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, "
|
+ "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 QortalPublicKey NOT NULL, trade_foreign_public_key_hash VARBINARY(32) NOT NULL, "
|
||||||
+ "at_address QortalAddress, "
|
+ "at_address QortalAddress, "
|
||||||
+ "last_transaction_signature Signature, PRIMARY KEY (trade_private_key)");
|
+ "last_transaction_signature Signature, PRIMARY KEY (trade_private_key))");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -44,6 +44,7 @@ import com.google.common.primitives.Bytes;
|
|||||||
public class AtTests extends Common {
|
public class AtTests extends Common {
|
||||||
|
|
||||||
public static final byte[] secret = "This string is exactly 32 bytes!".getBytes();
|
public static final byte[] secret = "This string is exactly 32 bytes!".getBytes();
|
||||||
|
public static final byte[] bitcoinPublicKeyHash = new byte[20]; // not used in tests
|
||||||
public static final byte[] secretHash = Crypto.hash160(secret); // daf59884b4d1aec8c1b17102530909ee43c0151a
|
public static final byte[] secretHash = Crypto.hash160(secret); // daf59884b4d1aec8c1b17102530909ee43c0151a
|
||||||
public static final int refundTimeout = 10; // blocks
|
public static final int refundTimeout = 10; // blocks
|
||||||
public static final long initialPayout = 100000L;
|
public static final long initialPayout = 100000L;
|
||||||
@ -60,7 +61,7 @@ public class AtTests extends Common {
|
|||||||
public void testCompile() {
|
public void testCompile() {
|
||||||
Account deployer = Common.getTestAccount(null, "chloe");
|
Account deployer = Common.getTestAccount(null, "chloe");
|
||||||
|
|
||||||
byte[] creationBytes = BTCACCT.buildQortalAT(deployer.getAddress(), secretHash, refundTimeout, initialPayout, redeemAmount, bitcoinAmount);
|
byte[] creationBytes = BTCACCT.buildQortalAT(deployer.getAddress(), bitcoinPublicKeyHash, secretHash, refundTimeout, initialPayout, redeemAmount, bitcoinAmount);
|
||||||
System.out.println("CIYAM AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
|
System.out.println("CIYAM AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -434,7 +435,7 @@ public class AtTests extends Common {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer) throws DataException {
|
private DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer) throws DataException {
|
||||||
byte[] creationBytes = BTCACCT.buildQortalAT(deployer.getAddress(), secretHash, refundTimeout, initialPayout, redeemAmount, bitcoinAmount);
|
byte[] creationBytes = BTCACCT.buildQortalAT(deployer.getAddress(), bitcoinPublicKeyHash, secretHash, refundTimeout, initialPayout, redeemAmount, bitcoinAmount);
|
||||||
|
|
||||||
long txTimestamp = System.currentTimeMillis();
|
long txTimestamp = System.currentTimeMillis();
|
||||||
byte[] lastReference = deployer.getLastReference();
|
byte[] lastReference = deployer.getLastReference();
|
||||||
|
@ -34,11 +34,12 @@ public class DeployAT {
|
|||||||
if (error != null)
|
if (error != null)
|
||||||
System.err.println(error);
|
System.err.println(error);
|
||||||
|
|
||||||
System.err.println(String.format("usage: DeployAT <your Qortal PRIVATE key> <QORT amount> <BTC amount> <HASH160-of-secret> <initial QORT payout> <AT funding amount> <AT trade timeout>"));
|
System.err.println(String.format("usage: DeployAT <your Qortal PRIVATE key> <QORT amount> <BTC amount> <your Bitcoin PKH> <HASH160-of-secret> <initial QORT payout> <AT funding amount> <AT trade timeout>"));
|
||||||
System.err.println(String.format("example: DeployAT "
|
System.err.println(String.format("example: DeployAT "
|
||||||
+ "AdTd9SUEYSdTW8mgK3Gu72K97bCHGdUwi2VvLNjUohot \\\n"
|
+ "AdTd9SUEYSdTW8mgK3Gu72K97bCHGdUwi2VvLNjUohot \\\n"
|
||||||
+ "\t80.4020 \\\n"
|
+ "\t80.4020 \\\n"
|
||||||
+ "\t0.00864200 \\\n"
|
+ "\t0.00864200 \\\n"
|
||||||
|
+ "\t750b06757a2448b8a4abebaa6e4662833fd5ddbb \\\n"
|
||||||
+ "\tdaf59884b4d1aec8c1b17102530909ee43c0151a \\\n"
|
+ "\tdaf59884b4d1aec8c1b17102530909ee43c0151a \\\n"
|
||||||
+ "\t0.0001 \\\n"
|
+ "\t0.0001 \\\n"
|
||||||
+ "\t123.456 \\\n"
|
+ "\t123.456 \\\n"
|
||||||
@ -56,6 +57,7 @@ public class DeployAT {
|
|||||||
byte[] refundPrivateKey = null;
|
byte[] refundPrivateKey = null;
|
||||||
long redeemAmount = 0;
|
long redeemAmount = 0;
|
||||||
long expectedBitcoin = 0;
|
long expectedBitcoin = 0;
|
||||||
|
byte[] bitcoinPublicKeyHash = null;
|
||||||
byte[] secretHash = null;
|
byte[] secretHash = null;
|
||||||
long initialPayout = 0;
|
long initialPayout = 0;
|
||||||
long fundingAmount = 0;
|
long fundingAmount = 0;
|
||||||
@ -75,6 +77,10 @@ public class DeployAT {
|
|||||||
if (expectedBitcoin <= 0)
|
if (expectedBitcoin <= 0)
|
||||||
usage("Expected BTC amount must be positive");
|
usage("Expected BTC amount must be positive");
|
||||||
|
|
||||||
|
bitcoinPublicKeyHash = HashCode.fromString(args[argIndex++]).asBytes();
|
||||||
|
if (bitcoinPublicKeyHash.length != 20)
|
||||||
|
usage("Bitcoin PKH must be 20 bytes");
|
||||||
|
|
||||||
secretHash = HashCode.fromString(args[argIndex++]).asBytes();
|
secretHash = HashCode.fromString(args[argIndex++]).asBytes();
|
||||||
if (secretHash.length != 20)
|
if (secretHash.length != 20)
|
||||||
usage("Hash of secret must be 20 bytes");
|
usage("Hash of secret must be 20 bytes");
|
||||||
@ -114,7 +120,7 @@ public class DeployAT {
|
|||||||
System.out.println(String.format("HASH160 of secret: %s", HashCode.fromBytes(secretHash)));
|
System.out.println(String.format("HASH160 of secret: %s", HashCode.fromBytes(secretHash)));
|
||||||
|
|
||||||
// Deploy AT
|
// Deploy AT
|
||||||
byte[] creationBytes = BTCACCT.buildQortalAT(refundAccount.getAddress(), secretHash, tradeTimeout, initialPayout, redeemAmount, expectedBitcoin);
|
byte[] creationBytes = BTCACCT.buildQortalAT(refundAccount.getAddress(), bitcoinPublicKeyHash, secretHash, tradeTimeout, initialPayout, redeemAmount, expectedBitcoin);
|
||||||
System.out.println("CIYAM AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
|
System.out.println("CIYAM AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
|
||||||
|
|
||||||
long txTimestamp = System.currentTimeMillis();
|
long txTimestamp = System.currentTimeMillis();
|
||||||
|
@ -20,15 +20,19 @@ public class AccountUtils {
|
|||||||
public static final int txGroupId = Group.NO_GROUP;
|
public static final int txGroupId = Group.NO_GROUP;
|
||||||
public static final long fee = 1L * Amounts.MULTIPLIER;
|
public static final long fee = 1L * Amounts.MULTIPLIER;
|
||||||
|
|
||||||
public static void pay(Repository repository, String sender, String recipient, long amount) throws DataException {
|
public static void pay(Repository repository, String testSenderName, String testRecipientName, long amount) throws DataException {
|
||||||
PrivateKeyAccount sendingAccount = Common.getTestAccount(repository, sender);
|
PrivateKeyAccount sendingAccount = Common.getTestAccount(repository, testSenderName);
|
||||||
PrivateKeyAccount recipientAccount = Common.getTestAccount(repository, recipient);
|
PrivateKeyAccount recipientAccount = Common.getTestAccount(repository, testRecipientName);
|
||||||
|
|
||||||
|
pay(repository, sendingAccount, recipientAccount.getAddress(), amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void pay(Repository repository, PrivateKeyAccount sendingAccount, String recipientAddress, long amount) throws DataException {
|
||||||
byte[] reference = sendingAccount.getLastReference();
|
byte[] reference = sendingAccount.getLastReference();
|
||||||
long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1;
|
long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1;
|
||||||
|
|
||||||
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, sendingAccount.getPublicKey(), fee, null);
|
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, sendingAccount.getPublicKey(), fee, null);
|
||||||
TransactionData transactionData = new PaymentTransactionData(baseTransactionData, recipientAccount.getAddress(), amount);
|
TransactionData transactionData = new PaymentTransactionData(baseTransactionData, recipientAddress, amount);
|
||||||
|
|
||||||
TransactionUtils.signAndMint(repository, transactionData, sendingAccount);
|
TransactionUtils.signAndMint(repository, transactionData, sendingAccount);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user