mirror of
https://github.com/Qortal/qortal.git
synced 2025-07-24 10:41:23 +00:00
Improved cross-chain AT and more API support for same.
Reworked the cross-chain trading AT so it is now 2-stage: stage 1: 'offer' mode waiting for message from creator containing trade partner's address stage 2: 'trade' mode waiting for message from trade partner containing secret Adjusted unit tests to cover above. Changed QortalATAPI.putCreatorAddressIntoB from storing creator's public key to actually storing creator's address. Refactored BTCACCT.AtConstants to CrossChainTradeData. Now we also store hash of AT's code bytes in DB so we can look up ATs by what they do. Affects ATData class, ATRepository, etc. Added "Automated Transactions" and "Cross-Chain" API sections. New API call GET /at/byfunction/{codehash} for looking up ATs by what they do, based on hash of their code bytes. New API call GET /at/{ataddress} for fetching info for specific AT. New API call GET /at/{ataddress}/data for fetch an AT's data segment. Mostly for diagnosis of AT's current state. New API call POST /at for building a raw, unsigned DEPLOY_AT transaction. New API call GET /crosschain/tradeoffers for finding open BTC-QORT trading ATs.
This commit is contained in:
@@ -10,7 +10,9 @@ import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.bitcoinj.core.Base58;
|
||||
import org.ciyam.at.MachineState;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -22,6 +24,7 @@ import org.qortal.crosschain.BTCACCT;
|
||||
import org.qortal.crypto.Crypto;
|
||||
import org.qortal.data.at.ATData;
|
||||
import org.qortal.data.at.ATStateData;
|
||||
import org.qortal.data.crosschain.CrossChainTradeData;
|
||||
import org.qortal.data.transaction.BaseTransactionData;
|
||||
import org.qortal.data.transaction.DeployAtTransactionData;
|
||||
import org.qortal.data.transaction.MessageTransactionData;
|
||||
@@ -37,6 +40,7 @@ import org.qortal.transaction.DeployAtTransaction;
|
||||
import org.qortal.transaction.MessageTransaction;
|
||||
|
||||
import com.google.common.hash.HashCode;
|
||||
import com.google.common.primitives.Bytes;
|
||||
|
||||
public class AtTests extends Common {
|
||||
|
||||
@@ -46,6 +50,7 @@ public class AtTests extends Common {
|
||||
public static final BigDecimal initialPayout = new BigDecimal("0.001").setScale(8);
|
||||
public static final BigDecimal redeemAmount = new BigDecimal("80.4020").setScale(8);
|
||||
public static final BigDecimal fundingAmount = new BigDecimal("123.456").setScale(8);
|
||||
public static final BigDecimal bitcoinAmount = new BigDecimal("0.00864200").setScale(8);
|
||||
|
||||
@Before
|
||||
public void beforeTest() throws DataException {
|
||||
@@ -54,9 +59,9 @@ public class AtTests extends Common {
|
||||
|
||||
@Test
|
||||
public void testCompile() {
|
||||
String redeemAddress = Common.getTestAccount(null, "chloe").getAddress();
|
||||
Account deployer = Common.getTestAccount(null, "chloe");
|
||||
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(secretHash, redeemAddress, refundTimeout, initialPayout, redeemAmount);
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(deployer.getAddress(), secretHash, refundTimeout, refundTimeout, initialPayout, redeemAmount, bitcoinAmount);
|
||||
System.out.println("CIYAM AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
|
||||
}
|
||||
|
||||
@@ -69,7 +74,7 @@ public class AtTests extends Common {
|
||||
BigDecimal deployersInitialBalance = deployer.getBalance(Asset.QORT);
|
||||
BigDecimal recipientsInitialBalance = recipient.getBalance(Asset.QORT);
|
||||
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, recipient);
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer);
|
||||
|
||||
BigDecimal expectedBalance = deployersInitialBalance.subtract(fundingAmount).subtract(deployAtTransaction.getTransactionData().getFee());
|
||||
BigDecimal actualBalance = deployer.getBalance(Asset.QORT);
|
||||
@@ -106,6 +111,37 @@ public class AtTests extends Common {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
public void testAutomaticOfferRefund() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
|
||||
PrivateKeyAccount recipient = Common.getTestAccount(repository, "dilbert");
|
||||
|
||||
BigDecimal deployersInitialBalance = deployer.getBalance(Asset.QORT);
|
||||
BigDecimal recipientsInitialBalance = recipient.getBalance(Asset.QORT);
|
||||
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer);
|
||||
Account at = deployAtTransaction.getATAccount();
|
||||
String atAddress = at.getAddress();
|
||||
|
||||
BigDecimal deployAtFee = deployAtTransaction.getTransactionData().getFee();
|
||||
BigDecimal deployersPostDeploymentBalance = deployersInitialBalance.subtract(fundingAmount).subtract(deployAtFee);
|
||||
|
||||
checkAtRefund(repository, deployer, deployersInitialBalance, deployAtFee);
|
||||
|
||||
describeAt(repository, atAddress);
|
||||
|
||||
// Test orphaning
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
|
||||
BigDecimal expectedBalance = deployersPostDeploymentBalance;
|
||||
BigDecimal actualBalance = deployer.getBalance(Asset.QORT);
|
||||
|
||||
Common.assertEqualBigDecimals("Deployer's post-orphan/pre-refund balance incorrect", expectedBalance, actualBalance);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
public void testInitialPayment() throws DataException {
|
||||
@@ -116,9 +152,15 @@ public class AtTests extends Common {
|
||||
BigDecimal deployersInitialBalance = deployer.getBalance(Asset.QORT);
|
||||
BigDecimal recipientsInitialBalance = recipient.getBalance(Asset.QORT);
|
||||
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, recipient);
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer);
|
||||
Account at = deployAtTransaction.getATAccount();
|
||||
String atAddress = at.getAddress();
|
||||
|
||||
// Initial payment should happen 1st block after deployment
|
||||
// Send recipient's address to AT
|
||||
byte[] recipientAddressBytes = Bytes.ensureCapacity(Base58.decode(recipient.getAddress()), 32, 0);
|
||||
MessageTransaction messageTransaction = sendMessage(repository, deployer, recipientAddressBytes, atAddress);
|
||||
|
||||
// Initial payment should happen 1st block after receiving recipient address
|
||||
BlockUtils.mintBlock(repository);
|
||||
|
||||
BigDecimal expectedBalance = recipientsInitialBalance.add(initialPayout);
|
||||
@@ -126,6 +168,8 @@ public class AtTests extends Common {
|
||||
|
||||
Common.assertEqualBigDecimals("Recipient's post-initial-payout balance incorrect", expectedBalance, actualBalance);
|
||||
|
||||
describeAt(repository, atAddress);
|
||||
|
||||
// Test orphaning
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
|
||||
@@ -136,9 +180,41 @@ public class AtTests extends Common {
|
||||
}
|
||||
}
|
||||
|
||||
// TEST SENDING RECIPIENT ADDRESS BUT NOT FROM AT CREATOR (SHOULD BE IGNORED)
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
public void testAutomaticRefund() throws DataException {
|
||||
public void testIncorrectTradeSender() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
|
||||
PrivateKeyAccount recipient = Common.getTestAccount(repository, "dilbert");
|
||||
PrivateKeyAccount bystander = Common.getTestAccount(repository, "bob");
|
||||
|
||||
BigDecimal deployersInitialBalance = deployer.getBalance(Asset.QORT);
|
||||
BigDecimal recipientsInitialBalance = recipient.getBalance(Asset.QORT);
|
||||
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer);
|
||||
Account at = deployAtTransaction.getATAccount();
|
||||
String atAddress = at.getAddress();
|
||||
|
||||
// Send recipient's address to AT BUT NOT FROM AT CREATOR
|
||||
byte[] recipientAddressBytes = Bytes.ensureCapacity(Base58.decode(recipient.getAddress()), 32, 0);
|
||||
MessageTransaction messageTransaction = sendMessage(repository, bystander, recipientAddressBytes, atAddress);
|
||||
|
||||
// Initial payment should NOT happen
|
||||
BlockUtils.mintBlock(repository);
|
||||
|
||||
BigDecimal expectedBalance = recipientsInitialBalance;
|
||||
BigDecimal actualBalance = recipient.getConfirmedBalance(Asset.QORT);
|
||||
|
||||
Common.assertEqualBigDecimals("Recipient's post-initial-payout balance incorrect", expectedBalance, actualBalance);
|
||||
|
||||
describeAt(repository, atAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
public void testAutomaticTradeRefund() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
|
||||
PrivateKeyAccount recipient = Common.getTestAccount(repository, "dilbert");
|
||||
@@ -146,15 +222,28 @@ public class AtTests extends Common {
|
||||
BigDecimal deployersInitialBalance = deployer.getBalance(Asset.QORT);
|
||||
BigDecimal recipientsInitialBalance = recipient.getBalance(Asset.QORT);
|
||||
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, recipient);
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer);
|
||||
Account at = deployAtTransaction.getATAccount();
|
||||
String atAddress = at.getAddress();
|
||||
|
||||
// Send recipient's address to AT
|
||||
byte[] recipientAddressBytes = Bytes.ensureCapacity(Base58.decode(recipient.getAddress()), 32, 0);
|
||||
MessageTransaction messageTransaction = sendMessage(repository, deployer, recipientAddressBytes, atAddress);
|
||||
|
||||
// Initial payment should happen 1st block after receiving recipient address
|
||||
BlockUtils.mintBlock(repository);
|
||||
|
||||
BigDecimal deployAtFee = deployAtTransaction.getTransactionData().getFee();
|
||||
BigDecimal deployersPostDeploymentBalance = deployersInitialBalance.subtract(fundingAmount).subtract(deployAtFee);
|
||||
BigDecimal messageFee = messageTransaction.getTransactionData().getFee();
|
||||
BigDecimal deployersPostDeploymentBalance = deployersInitialBalance.subtract(fundingAmount).subtract(deployAtFee).subtract(messageFee);
|
||||
|
||||
checkAtRefund(repository, deployer, deployersInitialBalance, deployAtFee);
|
||||
|
||||
describeAt(repository, atAddress);
|
||||
|
||||
// Test orphaning
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
|
||||
BigDecimal expectedBalance = deployersPostDeploymentBalance;
|
||||
BigDecimal actualBalance = deployer.getBalance(Asset.QORT);
|
||||
@@ -173,12 +262,19 @@ public class AtTests extends Common {
|
||||
BigDecimal deployersInitialBalance = deployer.getBalance(Asset.QORT);
|
||||
BigDecimal recipientsInitialBalance = recipient.getBalance(Asset.QORT);
|
||||
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, recipient);
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer);
|
||||
Account at = deployAtTransaction.getATAccount();
|
||||
String atAddress = at.getAddress();
|
||||
|
||||
// Send recipient's address to AT
|
||||
byte[] recipientAddressBytes = Bytes.ensureCapacity(Base58.decode(recipient.getAddress()), 32, 0);
|
||||
MessageTransaction messageTransaction = sendMessage(repository, deployer, recipientAddressBytes, atAddress);
|
||||
|
||||
// Initial payment should happen 1st block after receiving recipient address
|
||||
BlockUtils.mintBlock(repository);
|
||||
|
||||
// Send correct secret to AT
|
||||
MessageTransaction messageTransaction = sendSecret(repository, recipient, secret, atAddress);
|
||||
messageTransaction = sendMessage(repository, recipient, secret, atAddress);
|
||||
|
||||
// AT should send funds in the next block
|
||||
ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
|
||||
@@ -189,6 +285,8 @@ public class AtTests extends Common {
|
||||
|
||||
Common.assertEqualBigDecimals("Recipent's post-redeem balance incorrect", expectedBalance, actualBalance);
|
||||
|
||||
describeAt(repository, atAddress);
|
||||
|
||||
// Orphan redeem
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
|
||||
@@ -215,14 +313,21 @@ public class AtTests extends Common {
|
||||
BigDecimal deployersInitialBalance = deployer.getBalance(Asset.QORT);
|
||||
BigDecimal recipientsInitialBalance = recipient.getBalance(Asset.QORT);
|
||||
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, recipient);
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer);
|
||||
BigDecimal deployAtFee = deployAtTransaction.getTransactionData().getFee();
|
||||
|
||||
Account at = deployAtTransaction.getATAccount();
|
||||
String atAddress = at.getAddress();
|
||||
|
||||
// Send recipient's address to AT
|
||||
byte[] recipientAddressBytes = Bytes.ensureCapacity(Base58.decode(recipient.getAddress()), 32, 0);
|
||||
MessageTransaction messageTransaction = sendMessage(repository, deployer, recipientAddressBytes, atAddress);
|
||||
|
||||
// Initial payment should happen 1st block after receiving recipient address
|
||||
BlockUtils.mintBlock(repository);
|
||||
|
||||
// Send correct secret to AT, but from wrong account
|
||||
MessageTransaction messageTransaction = sendSecret(repository, bystander, secret, atAddress);
|
||||
messageTransaction = sendMessage(repository, bystander, secret, atAddress);
|
||||
|
||||
// AT should NOT send funds in the next block
|
||||
ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
|
||||
@@ -233,6 +338,8 @@ public class AtTests extends Common {
|
||||
|
||||
Common.assertEqualBigDecimals("Recipent's balance incorrect", expectedBalance, actualBalance);
|
||||
|
||||
describeAt(repository, atAddress);
|
||||
|
||||
checkAtRefund(repository, deployer, deployersInitialBalance, deployAtFee);
|
||||
}
|
||||
}
|
||||
@@ -247,15 +354,22 @@ public class AtTests extends Common {
|
||||
BigDecimal deployersInitialBalance = deployer.getBalance(Asset.QORT);
|
||||
BigDecimal recipientsInitialBalance = recipient.getBalance(Asset.QORT);
|
||||
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, recipient);
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer);
|
||||
BigDecimal deployAtFee = deployAtTransaction.getTransactionData().getFee();
|
||||
|
||||
Account at = deployAtTransaction.getATAccount();
|
||||
String atAddress = at.getAddress();
|
||||
|
||||
// Send recipient's address to AT
|
||||
byte[] recipientAddressBytes = Bytes.ensureCapacity(Base58.decode(recipient.getAddress()), 32, 0);
|
||||
MessageTransaction messageTransaction = sendMessage(repository, deployer, recipientAddressBytes, atAddress);
|
||||
|
||||
// Initial payment should happen 1st block after receiving recipient address
|
||||
BlockUtils.mintBlock(repository);
|
||||
|
||||
// Send correct secret to AT, but from wrong account
|
||||
byte[] wrongSecret = Crypto.digest(secret);
|
||||
MessageTransaction messageTransaction = sendSecret(repository, recipient, wrongSecret, atAddress);
|
||||
messageTransaction = sendMessage(repository, recipient, wrongSecret, atAddress);
|
||||
|
||||
// AT should NOT send funds in the next block
|
||||
ATStateData preRedeemAtStateData = repository.getATRepository().getLatestATState(atAddress);
|
||||
@@ -266,6 +380,8 @@ public class AtTests extends Common {
|
||||
|
||||
Common.assertEqualBigDecimals("Recipent's balance incorrect", expectedBalance, actualBalance);
|
||||
|
||||
describeAt(repository, atAddress);
|
||||
|
||||
checkAtRefund(repository, deployer, deployersInitialBalance, deployAtFee);
|
||||
}
|
||||
}
|
||||
@@ -280,10 +396,10 @@ public class AtTests extends Common {
|
||||
BigDecimal deployersInitialBalance = deployer.getBalance(Asset.QORT);
|
||||
BigDecimal recipientsInitialBalance = recipient.getBalance(Asset.QORT);
|
||||
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, recipient);
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer);
|
||||
|
||||
List<ATData> executableAts = repository.getATRepository().getAllExecutableATs();
|
||||
|
||||
|
||||
for (ATData atData : executableAts) {
|
||||
String atAddress = atData.getATAddress();
|
||||
byte[] codeBytes = atData.getCodeBytes();
|
||||
@@ -299,37 +415,13 @@ public class AtTests extends Common {
|
||||
if (!Arrays.equals(codeHash, BTCACCT.CODE_BYTES_HASH))
|
||||
continue;
|
||||
|
||||
ATStateData atStateData = repository.getATRepository().getLatestATState(atAddress);
|
||||
byte[] stateData = atStateData.getStateData();
|
||||
|
||||
QortalAtLoggerFactory loggerFactory = QortalAtLoggerFactory.getInstance();
|
||||
byte[] dataBytes = MachineState.extractDataBytes(loggerFactory, stateData);
|
||||
|
||||
BTCACCT.AtConstants atConstants = BTCACCT.extractAtConstants(dataBytes);
|
||||
|
||||
long autoRefundTimestamp = atData.getCreation() + atConstants.refundMinutes * 60 * 1000L;
|
||||
|
||||
String autoRefundString = LocalDateTime.ofInstant(Instant.ofEpochMilli(autoRefundTimestamp), ZoneOffset.UTC).format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM));
|
||||
System.out.println(String.format("%s:\n"
|
||||
+ "\tcreator: %s,\n"
|
||||
+ "\tHASH160 of secret: %s,\n"
|
||||
+ "\trecipient: %s,\n"
|
||||
+ "\tinitial payout: %s QORT,\n"
|
||||
+ "\tredeem payout: %s QORT,\n"
|
||||
+ "\tauto-refund at: %s (local time)",
|
||||
atAddress,
|
||||
Crypto.toAddress(atData.getCreatorPublicKey()),
|
||||
HashCode.fromBytes(atConstants.secretHash).toString().substring(0, 40),
|
||||
atConstants.recipient,
|
||||
atConstants.initialPayout.toPlainString(),
|
||||
atConstants.redeemPayout.toPlainString(),
|
||||
autoRefundString));
|
||||
describeAt(repository, atAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer, Account recipient) throws DataException {
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(secretHash, recipient.getAddress(), refundTimeout, initialPayout, redeemAmount);
|
||||
private DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer) throws DataException {
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(deployer.getAddress(), secretHash, refundTimeout, refundTimeout, initialPayout, redeemAmount, bitcoinAmount);
|
||||
|
||||
long txTimestamp = System.currentTimeMillis();
|
||||
byte[] lastReference = deployer.getLastReference();
|
||||
@@ -341,7 +433,7 @@ public class AtTests extends Common {
|
||||
|
||||
BigDecimal fee = BigDecimal.ZERO;
|
||||
String name = "QORT-BTC cross-chain trade";
|
||||
String description = String.format("Qortal-Bitcoin cross-chain trade between %s and %s", deployer.getAddress(), recipient.getAddress());
|
||||
String description = String.format("Qortal-Bitcoin cross-chain trade");
|
||||
String atType = "ACCT";
|
||||
String tags = "QORT-BTC ACCT";
|
||||
|
||||
@@ -358,7 +450,7 @@ public class AtTests extends Common {
|
||||
return deployAtTransaction;
|
||||
}
|
||||
|
||||
private MessageTransaction sendSecret(Repository repository, PrivateKeyAccount sender, byte[] data, String recipient) throws DataException {
|
||||
private MessageTransaction sendMessage(Repository repository, PrivateKeyAccount sender, byte[] data, String recipient) throws DataException {
|
||||
long txTimestamp = System.currentTimeMillis();
|
||||
byte[] lastReference = sender.getLastReference();
|
||||
|
||||
@@ -400,4 +492,61 @@ public class AtTests extends Common {
|
||||
assertTrue(String.format("Deployer's balance %s should be below maximum %s", actualBalance.toPlainString(), expectedMaximumBalance.toPlainString()), actualBalance.compareTo(expectedMaximumBalance) < 0);
|
||||
}
|
||||
|
||||
private void describeAt(Repository repository, String atAddress) throws DataException {
|
||||
ATData atData = repository.getATRepository().fromATAddress(atAddress);
|
||||
|
||||
ATStateData atStateData = repository.getATRepository().getLatestATState(atAddress);
|
||||
byte[] stateData = atStateData.getStateData();
|
||||
|
||||
QortalAtLoggerFactory loggerFactory = QortalAtLoggerFactory.getInstance();
|
||||
byte[] dataBytes = MachineState.extractDataBytes(loggerFactory, stateData);
|
||||
|
||||
CrossChainTradeData tradeData = new CrossChainTradeData();
|
||||
tradeData.qortalAddress = atAddress;
|
||||
tradeData.qortalCreator = Crypto.toAddress(atData.getCreatorPublicKey());
|
||||
tradeData.creationTimestamp = atData.getCreation();
|
||||
tradeData.qortBalance = repository.getAccountRepository().getBalance(atAddress, Asset.QORT).getBalance();
|
||||
|
||||
BTCACCT.populateTradeData(tradeData, dataBytes);
|
||||
|
||||
Function<Long, String> epochMilliFormatter = (timestamp) -> LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC).format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM));
|
||||
int currentBlockHeight = repository.getBlockRepository().getBlockchainHeight();
|
||||
|
||||
System.out.print(String.format("%s:\n"
|
||||
+ "\tcreator: %s,\n"
|
||||
+ "\tcreation timestamp: %s,\n"
|
||||
+ "\tcurrent balance: %s QORT,\n"
|
||||
+ "\tHASH160 of secret: %s,\n"
|
||||
+ "\tinitial payout: %s QORT,\n"
|
||||
+ "\tredeem payout: %s QORT,\n"
|
||||
+ "\texpected bitcoin: %s BTC,\n"
|
||||
+ "\toffer timeout: %d minutes (from creation),\n"
|
||||
+ "\ttrade timeout: %d minutes (from trade start),\n"
|
||||
+ "\tcurrent block height: %d,\n",
|
||||
tradeData.qortalAddress,
|
||||
tradeData.qortalCreator,
|
||||
epochMilliFormatter.apply(tradeData.creationTimestamp),
|
||||
tradeData.qortBalance.toPlainString(),
|
||||
HashCode.fromBytes(tradeData.secretHash).toString().substring(0, 40),
|
||||
tradeData.initialPayout.toPlainString(),
|
||||
tradeData.redeemPayout.toPlainString(),
|
||||
tradeData.expectedBitcoin.toPlainString(),
|
||||
tradeData.offerRefundTimeout,
|
||||
tradeData.tradeRefundTimeout,
|
||||
currentBlockHeight));
|
||||
|
||||
// Are we in 'offer' or 'trade' stage?
|
||||
if (tradeData.tradeRefundHeight == null) {
|
||||
// Offer
|
||||
System.out.println(String.format("\toffer timeout: block %d",
|
||||
tradeData.offerRefundHeight));
|
||||
} else {
|
||||
// Trade
|
||||
System.out.println(String.format("\ttrade timeout: block %d,\n"
|
||||
+ "\ttrade recipient: %s",
|
||||
tradeData.tradeRefundHeight,
|
||||
tradeData.qortalRecipient));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -2,16 +2,12 @@ package org.qortal.test.btcacct;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.security.Security;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.qortal.account.PrivateKeyAccount;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.controller.Controller;
|
||||
import org.qortal.crosschain.BTCACCT;
|
||||
import org.qortal.crypto.Crypto;
|
||||
import org.qortal.data.transaction.BaseTransactionData;
|
||||
import org.qortal.data.transaction.DeployAtTransactionData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
@@ -38,14 +34,14 @@ public class DeployAT {
|
||||
if (error != null)
|
||||
System.err.println(error);
|
||||
|
||||
System.err.println(String.format("usage: DeployAT <your Qortal PRIVATE key> <QORT amount> <redeem Qortal address> <HASH160-of-secret> <locktime> [<initial QORT payout> [<AT funding amount>]]"));
|
||||
System.err.println(String.format("usage: DeployAT <your Qortal PRIVATE key> <QORT amount> <BTC amount> <HASH160-of-secret> [<initial QORT payout> [<AT funding amount>]]"));
|
||||
System.err.println(String.format("example: DeployAT "
|
||||
+ "AdTd9SUEYSdTW8mgK3Gu72K97bCHGdUwi2VvLNjUohot \\\n"
|
||||
+ "\t3.1415 \\\n"
|
||||
+ "\tQgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v \\\n"
|
||||
+ "\t80.4020 \\\n"
|
||||
+ "\t0.00864200 \\\n"
|
||||
+ "\tdaf59884b4d1aec8c1b17102530909ee43c0151a \\\n"
|
||||
+ "\t1585920000 \\\n"
|
||||
+ "\t0.0001"));
|
||||
+ "\t0.0001 \\\n"
|
||||
+ "\t123.456"));
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
@@ -58,9 +54,8 @@ public class DeployAT {
|
||||
|
||||
byte[] refundPrivateKey = null;
|
||||
BigDecimal redeemAmount = null;
|
||||
String redeemAddress = null;
|
||||
BigDecimal expectedBitcoin = null;
|
||||
byte[] secretHash = null;
|
||||
int lockTime = 0;
|
||||
BigDecimal initialPayout = BigDecimal.ZERO.setScale(8);
|
||||
BigDecimal fundingAmount = null;
|
||||
|
||||
@@ -74,16 +69,14 @@ public class DeployAT {
|
||||
if (redeemAmount.signum() <= 0)
|
||||
usage("QORT amount must be positive");
|
||||
|
||||
redeemAddress = args[argIndex++];
|
||||
if (!Crypto.isValidAddress(redeemAddress))
|
||||
usage("Redeem address invalid");
|
||||
expectedBitcoin = new BigDecimal(args[argIndex++]).setScale(8);
|
||||
if (expectedBitcoin.signum() <= 0)
|
||||
usage("Expected BTC amount must be positive");
|
||||
|
||||
secretHash = HashCode.fromString(args[argIndex++]).asBytes();
|
||||
if (secretHash.length != 20)
|
||||
usage("Hash of secret must be 20 bytes");
|
||||
|
||||
lockTime = Integer.parseInt(args[argIndex++]);
|
||||
|
||||
if (args.length > argIndex)
|
||||
initialPayout = new BigDecimal(args[argIndex++]).setScale(8);
|
||||
|
||||
@@ -118,20 +111,11 @@ public class DeployAT {
|
||||
|
||||
System.out.println(String.format("HASH160 of secret: %s", HashCode.fromBytes(secretHash)));
|
||||
|
||||
System.out.println(String.format("Redeem Qortal address: %s", redeemAddress));
|
||||
|
||||
// New/derived info
|
||||
|
||||
System.out.println("\nCHECKING info from other party:");
|
||||
|
||||
System.out.println(String.format("Redeem script lockTime: %s (%d)", LocalDateTime.ofInstant(Instant.ofEpochSecond(lockTime), ZoneId.systemDefault()), lockTime));
|
||||
System.out.println("Make sure this is BEFORE P2SH lockTime to allow you to refund AT before P2SH refunded");
|
||||
|
||||
// Deploy AT
|
||||
final int BLOCK_TIME = 60; // seconds
|
||||
final int refundTimeout = (lockTime - (int) (System.currentTimeMillis() / 1000L)) / BLOCK_TIME;
|
||||
final int offerTimeout = 2 * 60; // minutes
|
||||
final int tradeTimeout = 60; // minutes
|
||||
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(secretHash, redeemAddress, refundTimeout, initialPayout, fundingAmount);
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(refundAccount.getAddress(), secretHash, offerTimeout, tradeTimeout, initialPayout, fundingAmount, expectedBitcoin);
|
||||
System.out.println("CIYAM AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
|
||||
|
||||
long txTimestamp = System.currentTimeMillis();
|
||||
@@ -144,7 +128,7 @@ public class DeployAT {
|
||||
|
||||
BigDecimal fee = BigDecimal.ZERO;
|
||||
String name = "QORT-BTC cross-chain trade";
|
||||
String description = String.format("Qortal-Bitcoin cross-chain trade between %s and %s", refundAccount.getAddress(), redeemAddress);
|
||||
String description = String.format("Qortal-Bitcoin cross-chain trade");
|
||||
String atType = "ACCT";
|
||||
String tags = "QORT-BTC ACCT";
|
||||
|
||||
|
Reference in New Issue
Block a user