mirror of
https://github.com/Qortal/qortal.git
synced 2025-03-15 03:52:31 +00:00
WIP: cross-chain trading with new lockTimes, requires AT v1.3.5
This commit is contained in:
parent
886c9156a5
commit
c3eb385066
2
pom.xml
2
pom.xml
@ -9,7 +9,7 @@
|
||||
<bitcoinj.version>0.15.5</bitcoinj.version>
|
||||
<bouncycastle.version>1.64</bouncycastle.version>
|
||||
<build.timestamp>${maven.build.timestamp}</build.timestamp>
|
||||
<ciyam-at.version>1.3.4</ciyam-at.version>
|
||||
<ciyam-at.version>1.3.5</ciyam-at.version>
|
||||
<commons-net.version>3.6</commons-net.version>
|
||||
<commons-text.version>1.8</commons-text.version>
|
||||
<dagger.version>1.2.2</dagger.version>
|
||||
|
@ -24,7 +24,7 @@ public class CrossChainBuildRequest {
|
||||
public byte[] bitcoinPublicKeyHash;
|
||||
|
||||
@Schema(description = "HASH160 of secret", example = "43vnftqkjxrhb5kJdkU1ZFQLEnWV")
|
||||
public byte[] secretHash;
|
||||
public byte[] hashOfSecretB;
|
||||
|
||||
@Schema(description = "Bitcoin P2SH BTC balance for release of secret", example = "0.00864200")
|
||||
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||
|
@ -156,7 +156,7 @@ public class CrossChainResource {
|
||||
if (creatorPublicKey == null || creatorPublicKey.length != Transformer.PUBLIC_KEY_LENGTH)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PUBLIC_KEY);
|
||||
|
||||
if (tradeRequest.secretHash == null || tradeRequest.secretHash.length != BTC.HASH160_LENGTH)
|
||||
if (tradeRequest.hashOfSecretB == null || tradeRequest.hashOfSecretB.length != BTC.HASH160_LENGTH)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
|
||||
|
||||
if (tradeRequest.tradeTimeout == null)
|
||||
@ -181,8 +181,8 @@ public class CrossChainResource {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
PublicKeyAccount creatorAccount = new PublicKeyAccount(repository, creatorPublicKey);
|
||||
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(creatorAccount.getAddress(), tradeRequest.bitcoinPublicKeyHash, tradeRequest.secretHash,
|
||||
tradeRequest.tradeTimeout, tradeRequest.qortAmount, tradeRequest.bitcoinAmount);
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(creatorAccount.getAddress(), tradeRequest.bitcoinPublicKeyHash, tradeRequest.hashOfSecretB,
|
||||
tradeRequest.qortAmount, tradeRequest.bitcoinAmount);
|
||||
|
||||
long txTimestamp = NTP.getTime();
|
||||
byte[] lastReference = creatorAccount.getLastReference();
|
||||
|
@ -56,11 +56,12 @@ public class TradeBot {
|
||||
|
||||
public static byte[] createTrade(Repository repository, TradeBotCreateRequest tradeBotCreateRequest) throws DataException {
|
||||
byte[] tradePrivateKey = generateTradePrivateKey();
|
||||
byte[] secret = generateSecret();
|
||||
byte[] secretHash = Crypto.digest(secret);
|
||||
byte[] secretB = generateSecret();
|
||||
byte[] hashOfSecretB = Crypto.digest(secretB);
|
||||
|
||||
byte[] tradeNativePublicKey = deriveTradeNativePublicKey(tradePrivateKey);
|
||||
byte[] tradeNativePublicKeyHash = Crypto.hash160(tradeNativePublicKey);
|
||||
String tradeAddress = Crypto.toAddress(tradeNativePublicKey);
|
||||
|
||||
byte[] tradeForeignPublicKey = deriveTradeForeignPublicKey(tradePrivateKey);
|
||||
byte[] tradeForeignPublicKeyHash = Crypto.hash160(tradeForeignPublicKey);
|
||||
@ -78,7 +79,7 @@ public class TradeBot {
|
||||
String description = "QORT/BTC cross-chain trade";
|
||||
String aTType = "ACCT";
|
||||
String tags = "ACCT QORT BTC";
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(creator.getAddress(), tradeNativePublicKeyHash, secretHash, tradeBotCreateRequest.tradeTimeout, tradeBotCreateRequest.qortAmount, tradeBotCreateRequest.bitcoinAmount);
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(tradeAddress, tradeForeignPublicKeyHash, hashOfSecretB, tradeBotCreateRequest.qortAmount, tradeBotCreateRequest.bitcoinAmount);
|
||||
long amount = tradeBotCreateRequest.fundingQortAmount;
|
||||
|
||||
DeployAtTransactionData deployAtTransactionData = new DeployAtTransactionData(baseTransactionData, name, description, aTType, tags, creationBytes, amount, Asset.QORT);
|
||||
@ -86,8 +87,8 @@ public class TradeBot {
|
||||
String atAddress = deployAtTransactionData.getAtAddress();
|
||||
|
||||
TradeBotData tradeBotData = new TradeBotData(tradePrivateKey, TradeBotData.State.BOB_WAITING_FOR_MESSAGE,
|
||||
atAddress, tradeBotCreateRequest.tradeTimeout,
|
||||
tradeNativePublicKey, tradeNativePublicKeyHash, secret, secretHash,
|
||||
atAddress,
|
||||
tradeNativePublicKey, tradeNativePublicKeyHash, secretB, hashOfSecretB,
|
||||
tradeForeignPublicKey, tradeForeignPublicKeyHash,
|
||||
tradeBotCreateRequest.bitcoinAmount, null);
|
||||
repository.getCrossChainRepository().save(tradeBotData);
|
||||
@ -115,7 +116,7 @@ public class TradeBot {
|
||||
byte[] tradeForeignPublicKeyHash = Crypto.hash160(tradeForeignPublicKey);
|
||||
|
||||
TradeBotData tradeBotData = new TradeBotData(tradePrivateKey, TradeBotData.State.ALICE_WAITING_FOR_P2SH_A,
|
||||
crossChainTradeData.qortalAtAddress, crossChainTradeData.tradeTimeout,
|
||||
crossChainTradeData.qortalAtAddress,
|
||||
tradeNativePublicKey, tradeNativePublicKeyHash, secret, secretHash,
|
||||
tradeForeignPublicKey, tradeForeignPublicKeyHash,
|
||||
crossChainTradeData.expectedBitcoin, null);
|
||||
@ -188,8 +189,6 @@ public class TradeBot {
|
||||
return;
|
||||
}
|
||||
|
||||
long atCreationTimestamp = atData.getCreation();
|
||||
|
||||
String address = Crypto.toAddress(tradeBotData.getTradeNativePublicKey());
|
||||
List<MessageTransactionData> messageTransactionsData = repository.getTransactionRepository().getMessagesByRecipient(address, null, null, null);
|
||||
|
||||
@ -212,20 +211,15 @@ public class TradeBot {
|
||||
|
||||
// We're expecting: HASH160(secret) + Alice's Bitcoin pubkeyhash
|
||||
byte[] messageData = messageTransactionData.getData();
|
||||
|
||||
if (messageData.length != 40)
|
||||
BTCACCT.OfferMessageData offerMessageData = BTCACCT.extractOfferMessageData(messageData);
|
||||
if (offerMessageData == null)
|
||||
continue;
|
||||
|
||||
byte[] aliceSecretHash = new byte[20];
|
||||
System.arraycopy(messageData, 0, aliceSecretHash, 0, 20);
|
||||
|
||||
byte[] aliceForeignPublicKeyHash = new byte[20];
|
||||
System.arraycopy(messageData, 20, aliceForeignPublicKeyHash, 0, 20);
|
||||
|
||||
byte[] aliceForeignPublicKeyHash = offerMessageData.recipientBitcoinPKH;
|
||||
byte[] hashOfSecretA = offerMessageData.hashOfSecretA;
|
||||
int lockTimeA = (int) offerMessageData.lockTimeA;
|
||||
// Determine P2SH-A address and confirm funded
|
||||
// First P2SH-A refund timeout is last in chain, so add all of tradeTimeout
|
||||
int lockTimeA = BTCACCT.calcLockTimeA(atCreationTimestamp, tradeBotData.getTradeTimeout());
|
||||
byte[] redeemScript = BTCP2SH.buildScript(aliceForeignPublicKeyHash, lockTimeA, tradeBotData.getTradeForeignPublicKeyHash(), aliceSecretHash);
|
||||
byte[] redeemScript = BTCP2SH.buildScript(aliceForeignPublicKeyHash, lockTimeA, tradeBotData.getTradeForeignPublicKeyHash(), hashOfSecretA);
|
||||
String p2shAddress = BTC.getInstance().deriveP2shAddress(redeemScript);
|
||||
|
||||
Long balance = BTC.getInstance().getBalance(p2shAddress);
|
||||
@ -235,9 +229,10 @@ public class TradeBot {
|
||||
// Good to go - send MESSAGE to AT
|
||||
|
||||
String aliceNativeAddress = Crypto.toAddress(messageTransactionData.getCreatorPublicKey());
|
||||
int lockTimeB = BTCACCT.calcLockTimeB(messageTransactionData.getTimestamp(), lockTimeA);
|
||||
|
||||
// Build outgoing message, padding each part to 32 bytes to make it easier for AT to consume
|
||||
byte[] outgoingMessageData = BTCACCT.buildOfferMessage(aliceNativeAddress, aliceForeignPublicKeyHash, aliceSecretHash);
|
||||
byte[] outgoingMessageData = BTCACCT.buildTradeMessage(aliceNativeAddress, aliceForeignPublicKeyHash, hashOfSecretA, lockTimeA, lockTimeB);
|
||||
|
||||
PrivateKeyAccount sender = new PrivateKeyAccount(repository, tradeBotData.getTradePrivateKey());
|
||||
MessageTransaction outgoingMessageTransaction = MessageTransaction.build(repository, sender, Group.NO_GROUP, tradeBotData.getAtAddress(), outgoingMessageData, false, false);
|
||||
|
@ -22,6 +22,7 @@ import org.qortal.data.crosschain.CrossChainTradeData;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.utils.Base58;
|
||||
import org.qortal.utils.BitTwiddling;
|
||||
|
||||
import com.google.common.hash.HashCode;
|
||||
import com.google.common.primitives.Bytes;
|
||||
@ -87,7 +88,13 @@ public class BTCACCT {
|
||||
|
||||
public static final int SECRET_LENGTH = 32;
|
||||
public static final int MIN_LOCKTIME = 1500000000;
|
||||
public static final byte[] CODE_BYTES_HASH = HashCode.fromString("ae1c6749b08465a5dec0224ab25e7551947f900df404bfed434a02fdad102b03").asBytes(); // SHA256 of AT code bytes
|
||||
public static final byte[] CODE_BYTES_HASH = HashCode.fromString("ca0dc643fdaba4d12cd5550800a8353746f40a0d9824d8c10d8b4bd0324eac0d").asBytes(); // SHA256 of AT code bytes
|
||||
|
||||
public static class OfferMessageData {
|
||||
public byte[] recipientBitcoinPKH;
|
||||
public byte[] hashOfSecretA;
|
||||
public long lockTimeA;
|
||||
}
|
||||
|
||||
private BTCACCT() {
|
||||
}
|
||||
@ -98,17 +105,14 @@ public class BTCACCT {
|
||||
* <tt>tradeTimeout</tt> (minutes) is the time window for the recipient to send the
|
||||
* 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 bitcoinPublicKeyHash 20-byte HASH160 of creator's bitcoin public key
|
||||
* @param hashOfSecretB 20-byte HASH160 of 32-byte secret
|
||||
* @param tradeTimeout how many minutes, from AT creation, until AT auto-refunds AT creator
|
||||
* @param qortAmount how much QORT to pay trade partner if they send correct 32-byte secret to AT
|
||||
* @param creatorTradeAddress AT creator's trade Qortal address, also used for refunds
|
||||
* @param bitcoinPublicKeyHash 20-byte HASH160 of creator's trade Bitcoin public key
|
||||
* @param hashOfSecretB 20-byte HASH160 of 32-byte secret-B
|
||||
* @param qortAmount how much QORT to pay trade partner if they send correct 32-byte secrets to AT
|
||||
* @param bitcoinAmount how much BTC the AT creator is expecting to trade
|
||||
* @return
|
||||
*/
|
||||
public static byte[] buildQortalAT(String creatorTradeAddress, byte[] bitcoinPublicKeyHash, byte[] hashOfSecretB, int tradeTimeout, long qortAmount, long bitcoinAmount) {
|
||||
int refundTimeout = calcRefundTimeout(tradeTimeout);
|
||||
|
||||
public static byte[] buildQortalAT(String creatorTradeAddress, byte[] bitcoinPublicKeyHash, byte[] hashOfSecretB, long qortAmount, long bitcoinAmount) {
|
||||
// Labels for data segment addresses
|
||||
int addrCounter = 0;
|
||||
|
||||
@ -125,8 +129,6 @@ public class BTCACCT {
|
||||
final int addrHashOfSecretB = addrCounter;
|
||||
addrCounter += 4;
|
||||
|
||||
final int addrTradeTimeout = addrCounter++;
|
||||
final int addrRefundTimeout = addrCounter++;
|
||||
final int addrQortAmount = addrCounter++;
|
||||
final int addrBitcoinAmount = addrCounter++;
|
||||
|
||||
@ -163,6 +165,9 @@ public class BTCACCT {
|
||||
final int addrQortalRecipient3 = addrCounter++;
|
||||
final int addrQortalRecipient4 = addrCounter++;
|
||||
|
||||
final int addrLockTimeA = addrCounter++;
|
||||
final int addrLockTimeB = addrCounter++;
|
||||
final int addrRefundTimeout = addrCounter++;
|
||||
final int addrRefundTimestamp = addrCounter++;
|
||||
final int addrLastTxTimestamp = addrCounter++;
|
||||
final int addrBlockTimestamp = addrCounter++;
|
||||
@ -190,7 +195,7 @@ public class BTCACCT {
|
||||
// Data segment
|
||||
ByteBuffer dataByteBuffer = ByteBuffer.allocate(addrCounter * MachineState.VALUE_SIZE);
|
||||
|
||||
// AT creator's Qortal address, decoded from Base58
|
||||
// AT creator's trade Qortal address, decoded from Base58
|
||||
assert dataByteBuffer.position() == addrCreatorTradeAddress1 * MachineState.VALUE_SIZE : "addrCreatorTradeAddress1 incorrect";
|
||||
byte[] creatorTradeAddressBytes = Base58.decode(creatorTradeAddress);
|
||||
dataByteBuffer.put(Bytes.ensureCapacity(creatorTradeAddressBytes, 32, 0));
|
||||
@ -199,18 +204,10 @@ public class BTCACCT {
|
||||
assert dataByteBuffer.position() == addrBitcoinPublicKeyHash * MachineState.VALUE_SIZE : "addrBitcoinPublicKeyHash incorrect";
|
||||
dataByteBuffer.put(Bytes.ensureCapacity(bitcoinPublicKeyHash, 32, 0));
|
||||
|
||||
// Hash of secret
|
||||
// Hash of secret-B
|
||||
assert dataByteBuffer.position() == addrHashOfSecretB * MachineState.VALUE_SIZE : "addrHashOfSecretB incorrect";
|
||||
dataByteBuffer.put(Bytes.ensureCapacity(hashOfSecretB, 32, 0));
|
||||
|
||||
// Trade timeout in minutes
|
||||
assert dataByteBuffer.position() == addrTradeTimeout * MachineState.VALUE_SIZE : "addrTradeTimeout incorrect";
|
||||
dataByteBuffer.putLong(tradeTimeout);
|
||||
|
||||
// Refund timeout in minutes (¾ of trade-timeout)
|
||||
assert dataByteBuffer.position() == addrRefundTimeout * MachineState.VALUE_SIZE : "addrRefundTimeout incorrect";
|
||||
dataByteBuffer.putLong(refundTimeout);
|
||||
|
||||
// Redeem Qort amount
|
||||
assert dataByteBuffer.position() == addrQortAmount * MachineState.VALUE_SIZE : "addrQortAmount incorrect";
|
||||
dataByteBuffer.putLong(qortAmount);
|
||||
@ -235,7 +232,7 @@ public class BTCACCT {
|
||||
assert dataByteBuffer.position() == addrCreatorAddressPointer * MachineState.VALUE_SIZE : "addrCreatorAddressPointer incorrect";
|
||||
dataByteBuffer.putLong(addrCreatorAddress1);
|
||||
|
||||
// Index into data segment of hash, used by GET_B_IND
|
||||
// Index into data segment of hash of secret B, used by GET_B_IND
|
||||
assert dataByteBuffer.position() == addrHashOfSecretBPointer * MachineState.VALUE_SIZE : "addrHashOfSecretBPointer incorrect";
|
||||
dataByteBuffer.putLong(addrHashOfSecretB);
|
||||
|
||||
@ -251,7 +248,7 @@ public class BTCACCT {
|
||||
assert dataByteBuffer.position() == addrOfferMessageRecipientBitcoinPKHOffset * MachineState.VALUE_SIZE : "addrOfferMessageRecipientBitcoinPKHOffset incorrect";
|
||||
dataByteBuffer.putLong(32L);
|
||||
|
||||
// Index into data segment of hash, used by SET_B_IND
|
||||
// Index into data segment of recipient's Bitcoin PKH, used by GET_B_IND
|
||||
assert dataByteBuffer.position() == addrRecipientBitcoinPKHPointer * MachineState.VALUE_SIZE : "addrRecipientBitcoinPKHPointer incorrect";
|
||||
dataByteBuffer.putLong(addrRecipientBitcoinPKH);
|
||||
|
||||
@ -259,7 +256,7 @@ public class BTCACCT {
|
||||
assert dataByteBuffer.position() == addrOfferMessageHashOfSecretAOffset * MachineState.VALUE_SIZE : "addrOfferMessageHashOfSecretAOffset incorrect";
|
||||
dataByteBuffer.putLong(64L);
|
||||
|
||||
// Index into data segment of hash, used by GET_B_IND
|
||||
// Index into data segment of hash of secret A, used by GET_B_IND
|
||||
assert dataByteBuffer.position() == addrHashOfSecretAPointer * MachineState.VALUE_SIZE : "addrHashOfSecretAPointer incorrect";
|
||||
dataByteBuffer.putLong(addrHashOfSecretA);
|
||||
|
||||
@ -289,7 +286,7 @@ public class BTCACCT {
|
||||
Integer labelCheckSecretB = null;
|
||||
Integer labelPayout = null;
|
||||
|
||||
ByteBuffer codeByteBuffer = ByteBuffer.allocate(512);
|
||||
ByteBuffer codeByteBuffer = ByteBuffer.allocate(768);
|
||||
|
||||
// Two-pass version
|
||||
for (int pass = 0; pass < 2; ++pass) {
|
||||
@ -301,9 +298,6 @@ public class BTCACCT {
|
||||
// Use AT creation 'timestamp' as starting point for finding transactions sent to AT
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_RET.compile(FunctionCode.GET_CREATION_TIMESTAMP, addrLastTxTimestamp));
|
||||
|
||||
// Calculate trade timeout refund 'timestamp' by adding addrRefundTimeout minutes to AT creation 'timestamp', then save into addrRefundTimestamp
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_RET_DAT_2.compile(FunctionCode.ADD_MINUTES_TO_TIMESTAMP, addrRefundTimestamp, addrLastTxTimestamp, addrRefundTimeout));
|
||||
|
||||
// Load B register with AT creator's address so we can save it into addrCreatorAddress1-4
|
||||
codeByteBuffer.put(OpCode.EXT_FUN.compile(FunctionCode.PUT_CREATOR_INTO_B));
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_DAT.compile(FunctionCode.GET_B_IND, addrCreatorAddressPointer));
|
||||
@ -371,13 +365,28 @@ public class BTCACCT {
|
||||
|
||||
labelOfferTxExtract = codeByteBuffer.position();
|
||||
|
||||
// Message is expected length, extract recipient's Bitcoin PKH
|
||||
// Message is expected length, grab next 32 bytes
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_DAT.compile(QortalFunctionCode.PUT_PARTIAL_MESSAGE_FROM_TX_IN_A_INTO_B.value, addrOfferMessageRecipientBitcoinPKHOffset));
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_DAT.compile(FunctionCode.GET_B_IND, addrRecipientBitcoinPKHPointer));
|
||||
|
||||
// Extract hash-of-secret-a
|
||||
// Extract recipient's Bitcoin PKH (we only really use values from B1-B3)
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_DAT.compile(FunctionCode.GET_B_IND, addrRecipientBitcoinPKHPointer));
|
||||
// Also extract lockTimeB
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_RET.compile(FunctionCode.GET_B4, addrLockTimeB));
|
||||
|
||||
// Grab next 32 bytes
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_DAT.compile(QortalFunctionCode.PUT_PARTIAL_MESSAGE_FROM_TX_IN_A_INTO_B.value, addrOfferMessageHashOfSecretAOffset));
|
||||
|
||||
// Extract hash-of-secret-a (we only really use values from B1-B3)
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_DAT.compile(FunctionCode.GET_B_IND, addrHashOfSecretAPointer));
|
||||
// Extract lockTimeA (from B4)
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_RET.compile(FunctionCode.GET_B4, addrLockTimeA));
|
||||
|
||||
// Calculate trade refund timeout: (lockTimeA - lockTimeB) / 2 / 60
|
||||
codeByteBuffer.put(OpCode.SET_DAT.compile(addrRefundTimeout, addrLockTimeA)); // refundTimeout = lockTimeA
|
||||
codeByteBuffer.put(OpCode.SUB_DAT.compile(addrRefundTimeout, addrLockTimeB)); // refundTimeout -= lockTimeB
|
||||
codeByteBuffer.put(OpCode.DIV_VAL.compile(addrRefundTimeout, 2L * 60L)); // refundTimeout /= 2 * 60
|
||||
// Calculate trade timeout refund 'timestamp' by adding addrRefundTimeout minutes to this tx 'timestamp', then save into addrRefundTimestamp
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_RET_DAT_2.compile(FunctionCode.ADD_MINUTES_TO_TIMESTAMP, addrRefundTimestamp, addrLastTxTimestamp, addrRefundTimeout));
|
||||
|
||||
/* We are in 'trade mode' */
|
||||
codeByteBuffer.put(OpCode.SET_VAL.compile(addrMode, 1));
|
||||
@ -533,6 +542,8 @@ public class BTCACCT {
|
||||
ByteBuffer dataByteBuffer = ByteBuffer.wrap(dataBytes);
|
||||
byte[] addressBytes = new byte[25];
|
||||
|
||||
/* Constants */
|
||||
|
||||
// Skip creator's trade address
|
||||
dataByteBuffer.get(addressBytes);
|
||||
tradeData.qortalCreatorTradeAddress = Base58.encode(addressBytes);
|
||||
@ -548,12 +559,6 @@ public class BTCACCT {
|
||||
dataByteBuffer.get(tradeData.hashOfSecretB);
|
||||
dataByteBuffer.position(dataByteBuffer.position() + 32 - tradeData.hashOfSecretB.length); // skip to 32 bytes
|
||||
|
||||
// Trade timeout
|
||||
tradeData.tradeTimeout = (int) dataByteBuffer.getLong();
|
||||
|
||||
// AT refund timeout (probably only useful for debugging)
|
||||
tradeData.refundTimeout = (int) dataByteBuffer.getLong();
|
||||
|
||||
// Redeem payout
|
||||
tradeData.qortAmount = dataByteBuffer.getLong();
|
||||
|
||||
@ -602,7 +607,7 @@ public class BTCACCT {
|
||||
// Skip message data length
|
||||
dataByteBuffer.position(dataByteBuffer.position() + 8);
|
||||
|
||||
/* End of constants */
|
||||
/* End of constants / begin variables */
|
||||
|
||||
// Skip AT creator's address
|
||||
dataByteBuffer.position(dataByteBuffer.position() + 8 * 4);
|
||||
@ -612,10 +617,19 @@ public class BTCACCT {
|
||||
String qortalRecipient = Base58.encode(addressBytes);
|
||||
dataByteBuffer.position(dataByteBuffer.position() + 32 - addressBytes.length);
|
||||
|
||||
// Potential lockTimeA (if in trade mode)
|
||||
int lockTimeA = (int) dataByteBuffer.getLong();
|
||||
|
||||
// Potential lockTimeB (if in trade mode)
|
||||
int lockTimeB = (int) dataByteBuffer.getLong();
|
||||
|
||||
// AT refund timeout (probably only useful for debugging)
|
||||
tradeData.refundTimeout = (int) dataByteBuffer.getLong();
|
||||
|
||||
// Trade offer timeout (AT 'timestamp' converted to Qortal block height)
|
||||
long tradeRefundTimestamp = dataByteBuffer.getLong();
|
||||
|
||||
// Last transaction timestamp
|
||||
// Skip last transaction timestamp
|
||||
dataByteBuffer.position(dataByteBuffer.position() + 8);
|
||||
|
||||
// Skip block timestamp
|
||||
@ -654,8 +668,8 @@ public class BTCACCT {
|
||||
tradeData.qortalRecipient = qortalRecipient;
|
||||
tradeData.hashOfSecretA = hashOfSecretA;
|
||||
tradeData.recipientBitcoinPKH = recipientBitcoinPKH;
|
||||
tradeData.lockTimeA = calcLockTimeA(tradeData.creationTimestamp, tradeData.tradeTimeout);
|
||||
tradeData.lockTimeB = calcLockTimeB(tradeData.creationTimestamp, tradeData.tradeTimeout);
|
||||
tradeData.lockTimeA = lockTimeA;
|
||||
tradeData.lockTimeB = lockTimeB;
|
||||
} else {
|
||||
tradeData.mode = CrossChainTradeData.Mode.OFFER;
|
||||
}
|
||||
@ -663,14 +677,37 @@ public class BTCACCT {
|
||||
return tradeData;
|
||||
}
|
||||
|
||||
/** Returns trade-info MESSAGE payload for trade partner/recipient to send to AT creator's trade address. */
|
||||
public static byte[] buildOfferMessage(byte[] recipientBitcoinPKH, byte[] hashOfSecretA, int lockTimeA) {
|
||||
byte[] lockTimeABytes = BitTwiddling.toBEByteArray((long) lockTimeA);
|
||||
return Bytes.concat(recipientBitcoinPKH, hashOfSecretA, lockTimeABytes);
|
||||
}
|
||||
|
||||
/** Returns trade-info extracted from MESSAGE payload sent by trade partner/recipient, or null if not valid. */
|
||||
public static OfferMessageData extractOfferMessageData(byte[] messageData) {
|
||||
if (messageData == null || messageData.length != 32 + 32 + 8)
|
||||
return null;
|
||||
|
||||
OfferMessageData offerMessageData = new OfferMessageData();
|
||||
offerMessageData.recipientBitcoinPKH = Arrays.copyOfRange(messageData, 0, 20);
|
||||
offerMessageData.hashOfSecretA = Arrays.copyOfRange(messageData, 20, 40);
|
||||
offerMessageData.lockTimeA = BitTwiddling.longFromBEBytes(messageData, 40);
|
||||
|
||||
return offerMessageData;
|
||||
}
|
||||
|
||||
/** Returns trade-info MESSAGE payload for AT creator to send to AT. */
|
||||
public static byte[] buildOfferMessage(String recipientQortalAddress, byte[] recipientBitcoinPKH, byte[] hashOfSecretA) {
|
||||
public static byte[] buildTradeMessage(String recipientQortalAddress, byte[] recipientBitcoinPKH, byte[] hashOfSecretA, int lockTimeA, int lockTimeB) {
|
||||
byte[] data = new byte[32 + 32 + 32];
|
||||
byte[] recipientQortalAddressBytes = Base58.decode(recipientQortalAddress);
|
||||
byte[] lockTimeABytes = BitTwiddling.toBEByteArray((long) lockTimeA);
|
||||
byte[] lockTimeBBytes = BitTwiddling.toBEByteArray((long) lockTimeB);
|
||||
|
||||
System.arraycopy(recipientQortalAddressBytes, 0, data, 0, recipientQortalAddressBytes.length);
|
||||
System.arraycopy(recipientBitcoinPKH, 0, data, 32, recipientBitcoinPKH.length);
|
||||
System.arraycopy(lockTimeBBytes, 0, data, 56, lockTimeBBytes.length);
|
||||
System.arraycopy(hashOfSecretA, 0, data, 64, hashOfSecretA.length);
|
||||
System.arraycopy(lockTimeABytes, 0, data, 88, lockTimeABytes.length);
|
||||
|
||||
return data;
|
||||
}
|
||||
@ -686,7 +723,7 @@ public class BTCACCT {
|
||||
}
|
||||
|
||||
/** Returns redeem MESSAGE payload for trade partner/recipient to send to AT. */
|
||||
public static byte[] buildTradeMessage(byte[] secretA, byte[] secretB) {
|
||||
public static byte[] buildRedeemMessage(byte[] secretA, byte[] secretB) {
|
||||
byte[] data = new byte[32 + 32];
|
||||
|
||||
System.arraycopy(secretA, 0, data, 0, secretA.length);
|
||||
@ -695,19 +732,10 @@ public class BTCACCT {
|
||||
return data;
|
||||
}
|
||||
|
||||
/** Returns AT refundTimeout (minutes) based on tradeTimeout. */
|
||||
public static int calcRefundTimeout(int tradeTimeout) {
|
||||
return tradeTimeout * 3 / 4;
|
||||
}
|
||||
|
||||
/** Returns P2SH-A lockTime (epoch seconds). */
|
||||
public static int calcLockTimeA(long atCreationTimestamp, int tradeTimeout) {
|
||||
return (int) (atCreationTimestamp / 1000L + tradeTimeout * 60);
|
||||
}
|
||||
|
||||
/** Returns P2SH-B lockTime (epoch seconds). */
|
||||
public static int calcLockTimeB(long atCreationTimestamp, int tradeTimeout) {
|
||||
return (int) (atCreationTimestamp / 1000L + tradeTimeout / 2 * 60);
|
||||
/** Returns P2SH-B lockTime (epoch seconds) based on trade partner/recipient's MESSAGE timestamp and P2SH-A locktime. */
|
||||
public static int calcLockTimeB(long recipientMessageTimestamp, int lockTimeA) {
|
||||
// lockTimeB is halfway between recipientMessageTimesamp and lockTimeA
|
||||
return (int) ((lockTimeA + (recipientMessageTimestamp / 1000L)) / 2L);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -49,9 +49,6 @@ public class CrossChainTradeData {
|
||||
@Schema(description = "Timestamp when AT switched to trade mode")
|
||||
public Long tradeModeTimestamp;
|
||||
|
||||
@Schema(description = "General trade timeout (minutes) used to derive P2SH locktimes and AT refund timeout")
|
||||
public int tradeTimeout;
|
||||
|
||||
@Schema(description = "How long from AT creation until AT triggers automatic refund to AT creator (minutes)")
|
||||
public int refundTimeout;
|
||||
|
||||
|
@ -38,7 +38,6 @@ public class TradeBotData {
|
||||
private State tradeState;
|
||||
|
||||
private String atAddress;
|
||||
private int tradeTimeout;
|
||||
|
||||
private byte[] tradeNativePublicKey;
|
||||
private byte[] tradeNativePublicKeyHash;
|
||||
@ -53,14 +52,13 @@ public class TradeBotData {
|
||||
|
||||
private byte[] lastTransactionSignature;
|
||||
|
||||
public TradeBotData(byte[] tradePrivateKey, State tradeState, String atAddress, int tradeTimeout,
|
||||
public TradeBotData(byte[] tradePrivateKey, State tradeState, String atAddress,
|
||||
byte[] tradeNativePublicKey, byte[] tradeNativePublicKeyHash, byte[] secret, byte[] secretHash,
|
||||
byte[] tradeForeignPublicKey, byte[] tradeForeignPublicKeyHash,
|
||||
long bitcoinAmount, byte[] lastTransactionSignature) {
|
||||
this.tradePrivateKey = tradePrivateKey;
|
||||
this.tradeState = tradeState;
|
||||
this.atAddress = atAddress;
|
||||
this.tradeTimeout = tradeTimeout;
|
||||
this.tradeNativePublicKey = tradeNativePublicKey;
|
||||
this.tradeNativePublicKeyHash = tradeNativePublicKeyHash;
|
||||
this.secret = secret;
|
||||
@ -91,10 +89,6 @@ public class TradeBotData {
|
||||
this.atAddress = atAddress;
|
||||
}
|
||||
|
||||
public int getTradeTimeout() {
|
||||
return this.tradeTimeout;
|
||||
}
|
||||
|
||||
public byte[] getTradeNativePublicKey() {
|
||||
return this.tradeNativePublicKey;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ public class HSQLDBCrossChainRepository implements CrossChainRepository {
|
||||
|
||||
@Override
|
||||
public List<TradeBotData> getAllTradeBotData() throws DataException {
|
||||
String sql = "SELECT trade_private_key, trade_state, at_address, trade_timeout, "
|
||||
String sql = "SELECT trade_private_key, trade_state, at_address, "
|
||||
+ "trade_native_public_key, trade_native_public_key_hash, "
|
||||
+ "secret, secret_hash, "
|
||||
+ "trade_foreign_public_key, trade_foreign_public_key_hash, "
|
||||
@ -40,18 +40,17 @@ public class HSQLDBCrossChainRepository implements CrossChainRepository {
|
||||
throw new DataException("Illegal trade-bot trade-state fetched from repository");
|
||||
|
||||
String atAddress = resultSet.getString(3);
|
||||
int tradeTimeout = resultSet.getInt(4);
|
||||
byte[] tradeNativePublicKey = resultSet.getBytes(5);
|
||||
byte[] tradeNativePublicKeyHash = resultSet.getBytes(6);
|
||||
byte[] secret = resultSet.getBytes(7);
|
||||
byte[] secretHash = resultSet.getBytes(8);
|
||||
byte[] tradeForeignPublicKey = resultSet.getBytes(9);
|
||||
byte[] tradeForeignPublicKeyHash = resultSet.getBytes(10);
|
||||
long bitcoinAmount = resultSet.getLong(11);
|
||||
byte[] lastTransactionSignature = resultSet.getBytes(12);
|
||||
byte[] tradeNativePublicKey = resultSet.getBytes(4);
|
||||
byte[] tradeNativePublicKeyHash = resultSet.getBytes(5);
|
||||
byte[] secret = resultSet.getBytes(6);
|
||||
byte[] secretHash = resultSet.getBytes(7);
|
||||
byte[] tradeForeignPublicKey = resultSet.getBytes(8);
|
||||
byte[] tradeForeignPublicKeyHash = resultSet.getBytes(9);
|
||||
long bitcoinAmount = resultSet.getLong(10);
|
||||
byte[] lastTransactionSignature = resultSet.getBytes(11);
|
||||
|
||||
TradeBotData tradeBotData = new TradeBotData(tradePrivateKey, tradeState,
|
||||
atAddress, tradeTimeout,
|
||||
atAddress,
|
||||
tradeNativePublicKey, tradeNativePublicKeyHash, secret, secretHash,
|
||||
tradeForeignPublicKey, tradeForeignPublicKeyHash,
|
||||
bitcoinAmount, lastTransactionSignature);
|
||||
@ -71,7 +70,6 @@ public class HSQLDBCrossChainRepository implements CrossChainRepository {
|
||||
saveHelper.bind("trade_private_key", tradeBotData.getTradePrivateKey())
|
||||
.bind("trade_state", tradeBotData.getState().value)
|
||||
.bind("at_address", tradeBotData.getAtAddress())
|
||||
.bind("trade_timeout", tradeBotData.getTradeTimeout())
|
||||
.bind("trade_native_public_key", tradeBotData.getTradeNativePublicKey())
|
||||
.bind("trade_native_public_key_hash", tradeBotData.getTradeNativePublicKeyHash())
|
||||
.bind("secret", tradeBotData.getSecret()).bind("secret_hash", tradeBotData.getSecretHash())
|
||||
|
@ -26,6 +26,17 @@ public class BitTwiddling {
|
||||
return new byte[] { (byte) (value), (byte) (value >> 8), (byte) (value >> 16), (byte) (value >> 24) };
|
||||
}
|
||||
|
||||
/** Convert int to big-endian byte array */
|
||||
public static byte[] toBEByteArray(int value) {
|
||||
return new byte[] { (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) (value) };
|
||||
}
|
||||
|
||||
/** Convert long to big-endian byte array */
|
||||
public static byte[] toBEByteArray(long value) {
|
||||
return new byte[] { (byte) (value >> 56), (byte) (value >> 48), (byte) (value >> 40), (byte) (value >> 32),
|
||||
(byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) (value) };
|
||||
}
|
||||
|
||||
/** Convert little-endian bytes to int */
|
||||
public static int intFromLEBytes(byte[] bytes, int offset) {
|
||||
return (bytes[offset] & 0xff) | (bytes[offset + 1] & 0xff) << 8 | (bytes[offset + 2] & 0xff) << 16 | (bytes[offset + 3] & 0xff) << 24;
|
||||
|
@ -49,7 +49,7 @@ public class AtTests extends Common {
|
||||
public static final byte[] secretB = "This string is roughly 32 bytes?".getBytes();
|
||||
public static final byte[] hashOfSecretB = Crypto.hash160(secretB); // 31f0dd71decf59bbc8ef0661f4030479255cfa58
|
||||
public static final byte[] bitcoinPublicKeyHash = HashCode.fromString("bb00bb11bb22bb33bb44bb55bb66bb77bb88bb99").asBytes();
|
||||
public static final int tradeTimeout = 12; // blocks
|
||||
public static final int tradeTimeout = 20; // blocks
|
||||
public static final long redeemAmount = 80_40200000L;
|
||||
public static final long fundingAmount = 123_45600000L;
|
||||
public static final long bitcoinAmount = 864200L;
|
||||
@ -63,7 +63,7 @@ public class AtTests extends Common {
|
||||
public void testCompile() {
|
||||
Account deployer = Common.getTestAccount(null, "chloe");
|
||||
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(deployer.getAddress(), bitcoinPublicKeyHash, hashOfSecretB, tradeTimeout, redeemAmount, bitcoinAmount);
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(deployer.getAddress(), bitcoinPublicKeyHash, hashOfSecretB, redeemAmount, bitcoinAmount);
|
||||
System.out.println("CIYAM AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
|
||||
}
|
||||
|
||||
@ -176,8 +176,12 @@ public class AtTests extends Common {
|
||||
Account at = deployAtTransaction.getATAccount();
|
||||
String atAddress = at.getAddress();
|
||||
|
||||
long recipientMessageTransactionTimestamp = System.currentTimeMillis();
|
||||
int lockTimeA = calcTestLockTimeA(recipientMessageTransactionTimestamp);
|
||||
int lockTimeB = BTCACCT.calcLockTimeB(recipientMessageTransactionTimestamp, lockTimeA);
|
||||
|
||||
// Send trade info to AT
|
||||
byte[] messageData = BTCACCT.buildOfferMessage(recipient.getAddress(), bitcoinPublicKeyHash, hashOfSecretA);
|
||||
byte[] messageData = BTCACCT.buildTradeMessage(recipient.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, lockTimeB);
|
||||
MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
|
||||
|
||||
Block postDeploymentBlock = BlockUtils.mintBlock(repository);
|
||||
@ -230,8 +234,12 @@ public class AtTests extends Common {
|
||||
Account at = deployAtTransaction.getATAccount();
|
||||
String atAddress = at.getAddress();
|
||||
|
||||
long recipientMessageTransactionTimestamp = System.currentTimeMillis();
|
||||
int lockTimeA = calcTestLockTimeA(recipientMessageTransactionTimestamp);
|
||||
int lockTimeB = BTCACCT.calcLockTimeB(recipientMessageTransactionTimestamp, lockTimeA);
|
||||
|
||||
// Send trade info to AT BUT NOT FROM AT CREATOR
|
||||
byte[] messageData = BTCACCT.buildOfferMessage(recipient.getAddress(), bitcoinPublicKeyHash, hashOfSecretA);
|
||||
byte[] messageData = BTCACCT.buildTradeMessage(recipient.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, lockTimeB);
|
||||
MessageTransaction messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
|
||||
|
||||
BlockUtils.mintBlock(repository);
|
||||
@ -265,8 +273,12 @@ public class AtTests extends Common {
|
||||
Account at = deployAtTransaction.getATAccount();
|
||||
String atAddress = at.getAddress();
|
||||
|
||||
long recipientMessageTransactionTimestamp = System.currentTimeMillis();
|
||||
int lockTimeA = calcTestLockTimeA(recipientMessageTransactionTimestamp);
|
||||
int lockTimeB = BTCACCT.calcLockTimeB(recipientMessageTransactionTimestamp, lockTimeA);
|
||||
|
||||
// Send trade info to AT
|
||||
byte[] messageData = BTCACCT.buildOfferMessage(recipient.getAddress(), bitcoinPublicKeyHash, hashOfSecretA);
|
||||
byte[] messageData = BTCACCT.buildTradeMessage(recipient.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, lockTimeB);
|
||||
MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
|
||||
|
||||
Block postDeploymentBlock = BlockUtils.mintBlock(repository);
|
||||
@ -308,15 +320,19 @@ public class AtTests extends Common {
|
||||
Account at = deployAtTransaction.getATAccount();
|
||||
String atAddress = at.getAddress();
|
||||
|
||||
long recipientMessageTransactionTimestamp = System.currentTimeMillis();
|
||||
int lockTimeA = calcTestLockTimeA(recipientMessageTransactionTimestamp);
|
||||
int lockTimeB = BTCACCT.calcLockTimeB(recipientMessageTransactionTimestamp, lockTimeA);
|
||||
|
||||
// Send trade info to AT
|
||||
byte[] messageData = BTCACCT.buildOfferMessage(recipient.getAddress(), bitcoinPublicKeyHash, hashOfSecretA);
|
||||
byte[] messageData = BTCACCT.buildTradeMessage(recipient.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, lockTimeB);
|
||||
MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
|
||||
|
||||
// Give AT time to process message
|
||||
BlockUtils.mintBlock(repository);
|
||||
|
||||
// Send correct secrets to AT, from correct account
|
||||
messageData = BTCACCT.buildTradeMessage(secretA, secretB);
|
||||
messageData = BTCACCT.buildRedeemMessage(secretA, secretB);
|
||||
messageTransaction = sendMessage(repository, recipient, messageData, atAddress);
|
||||
|
||||
// AT should send funds in the next block
|
||||
@ -366,15 +382,19 @@ public class AtTests extends Common {
|
||||
Account at = deployAtTransaction.getATAccount();
|
||||
String atAddress = at.getAddress();
|
||||
|
||||
long recipientMessageTransactionTimestamp = System.currentTimeMillis();
|
||||
int lockTimeA = calcTestLockTimeA(recipientMessageTransactionTimestamp);
|
||||
int lockTimeB = BTCACCT.calcLockTimeB(recipientMessageTransactionTimestamp, lockTimeA);
|
||||
|
||||
// Send trade info to AT
|
||||
byte[] messageData = BTCACCT.buildOfferMessage(recipient.getAddress(), bitcoinPublicKeyHash, hashOfSecretA);
|
||||
byte[] messageData = BTCACCT.buildTradeMessage(recipient.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, lockTimeB);
|
||||
MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
|
||||
|
||||
// Give AT time to process message
|
||||
BlockUtils.mintBlock(repository);
|
||||
|
||||
// Send correct secrets to AT, but from wrong account
|
||||
messageData = BTCACCT.buildTradeMessage(secretA, secretB);
|
||||
messageData = BTCACCT.buildRedeemMessage(secretA, secretB);
|
||||
messageTransaction = sendMessage(repository, bystander, messageData, atAddress);
|
||||
|
||||
// AT should NOT send funds in the next block
|
||||
@ -412,8 +432,12 @@ public class AtTests extends Common {
|
||||
Account at = deployAtTransaction.getATAccount();
|
||||
String atAddress = at.getAddress();
|
||||
|
||||
long recipientMessageTransactionTimestamp = System.currentTimeMillis();
|
||||
int lockTimeA = calcTestLockTimeA(recipientMessageTransactionTimestamp);
|
||||
int lockTimeB = BTCACCT.calcLockTimeB(recipientMessageTransactionTimestamp, lockTimeA);
|
||||
|
||||
// Send trade info to AT
|
||||
byte[] messageData = BTCACCT.buildOfferMessage(recipient.getAddress(), bitcoinPublicKeyHash, hashOfSecretA);
|
||||
byte[] messageData = BTCACCT.buildTradeMessage(recipient.getAddress(), bitcoinPublicKeyHash, hashOfSecretA, lockTimeA, lockTimeB);
|
||||
MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress);
|
||||
|
||||
// Give AT time to process message
|
||||
@ -423,7 +447,7 @@ public class AtTests extends Common {
|
||||
byte[] wrongSecret = new byte[32];
|
||||
Random random = new Random();
|
||||
random.nextBytes(wrongSecret);
|
||||
messageData = BTCACCT.buildTradeMessage(wrongSecret, secretB);
|
||||
messageData = BTCACCT.buildRedeemMessage(wrongSecret, secretB);
|
||||
messageTransaction = sendMessage(repository, recipient, messageData, atAddress);
|
||||
|
||||
// AT should NOT send funds in the next block
|
||||
@ -442,7 +466,7 @@ public class AtTests extends Common {
|
||||
assertEquals("Recipent's balance incorrect", expectedBalance, actualBalance);
|
||||
|
||||
// Send incorrect secrets to AT, from correct account
|
||||
messageData = BTCACCT.buildTradeMessage(secretA, wrongSecret);
|
||||
messageData = BTCACCT.buildRedeemMessage(secretA, wrongSecret);
|
||||
messageTransaction = sendMessage(repository, recipient, messageData, atAddress);
|
||||
|
||||
// AT should NOT send funds in the next block
|
||||
@ -497,8 +521,12 @@ public class AtTests extends Common {
|
||||
}
|
||||
}
|
||||
|
||||
private int calcTestLockTimeA(long messageTimestamp) {
|
||||
return (int) (messageTimestamp / 1000L + tradeTimeout * 60);
|
||||
}
|
||||
|
||||
private DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer) throws DataException {
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(deployer.getAddress(), bitcoinPublicKeyHash, hashOfSecretB, tradeTimeout, redeemAmount, bitcoinAmount);
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(deployer.getAddress(), bitcoinPublicKeyHash, hashOfSecretB, redeemAmount, bitcoinAmount);
|
||||
|
||||
long txTimestamp = System.currentTimeMillis();
|
||||
byte[] lastReference = deployer.getLastReference();
|
||||
@ -557,7 +585,7 @@ public class AtTests extends Common {
|
||||
|
||||
private void checkTradeRefund(Repository repository, Account deployer, long deployersInitialBalance, long deployAtFee) throws DataException {
|
||||
long deployersPostDeploymentBalance = deployersInitialBalance - fundingAmount - deployAtFee;
|
||||
int refundTimeout = BTCACCT.calcRefundTimeout(tradeTimeout);
|
||||
int refundTimeout = tradeTimeout * 3 / 4 + 1; // close enough
|
||||
|
||||
// AT should automatically refund deployer after 'refundTimeout' blocks
|
||||
for (int blockCount = 0; blockCount <= refundTimeout; ++blockCount)
|
||||
@ -588,7 +616,6 @@ public class AtTests extends Common {
|
||||
+ "\tHASH160 of secret-B: %s,\n"
|
||||
+ "\tredeem payout: %s QORT,\n"
|
||||
+ "\texpected bitcoin: %s BTC,\n"
|
||||
+ "\ttrade timeout: %d minutes (from AT creation),\n"
|
||||
+ "\tcurrent block height: %d,\n",
|
||||
tradeData.qortalAtAddress,
|
||||
tradeData.qortalCreator,
|
||||
@ -598,7 +625,6 @@ public class AtTests extends Common {
|
||||
HashCode.fromBytes(tradeData.hashOfSecretB).toString().substring(0, 40),
|
||||
Amounts.prettyAmount(tradeData.qortAmount),
|
||||
Amounts.prettyAmount(tradeData.expectedBitcoin),
|
||||
tradeData.tradeTimeout,
|
||||
currentBlockHeight));
|
||||
|
||||
// Are we in 'offer' or 'trade' stage?
|
||||
|
@ -34,20 +34,19 @@ public class DeployAT {
|
||||
if (error != null)
|
||||
System.err.println(error);
|
||||
|
||||
System.err.println(String.format("usage: DeployAT <your Qortal PRIVATE key> <QORT amount> <BTC amount> <your Bitcoin PKH> <HASH160-of-secret> <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> <AT funding amount>"));
|
||||
System.err.println(String.format("example: DeployAT "
|
||||
+ "AdTd9SUEYSdTW8mgK3Gu72K97bCHGdUwi2VvLNjUohot \\\n"
|
||||
+ "\t80.4020 \\\n"
|
||||
+ "\t0.00864200 \\\n"
|
||||
+ "\t750b06757a2448b8a4abebaa6e4662833fd5ddbb \\\n"
|
||||
+ "\tdaf59884b4d1aec8c1b17102530909ee43c0151a \\\n"
|
||||
+ "\t123.456 \\\n"
|
||||
+ "\t10"));
|
||||
+ "\t123.456"));
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length != 7)
|
||||
if (args.length != 6)
|
||||
usage(null);
|
||||
|
||||
Security.insertProviderAt(new BouncyCastleProvider(), 0);
|
||||
@ -59,7 +58,6 @@ public class DeployAT {
|
||||
byte[] bitcoinPublicKeyHash = null;
|
||||
byte[] secretHash = null;
|
||||
long fundingAmount = 0;
|
||||
int tradeTimeout = 0;
|
||||
|
||||
int argIndex = 0;
|
||||
try {
|
||||
@ -86,10 +84,6 @@ public class DeployAT {
|
||||
fundingAmount = Long.parseLong(args[argIndex++]);
|
||||
if (fundingAmount <= redeemAmount)
|
||||
usage("AT funding amount must be greater than QORT redeem amount");
|
||||
|
||||
tradeTimeout = Integer.parseInt(args[argIndex++]);
|
||||
if (tradeTimeout < 10 || tradeTimeout > 50000)
|
||||
usage("AT trade timeout should be between 10 and 50,000 minutes");
|
||||
} catch (IllegalArgumentException e) {
|
||||
usage(String.format("Invalid argument %d: %s", argIndex, e.getMessage()));
|
||||
}
|
||||
@ -114,7 +108,7 @@ public class DeployAT {
|
||||
System.out.println(String.format("HASH160 of secret: %s", HashCode.fromBytes(secretHash)));
|
||||
|
||||
// Deploy AT
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(refundAccount.getAddress(), bitcoinPublicKeyHash, secretHash, tradeTimeout, redeemAmount, expectedBitcoin);
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(refundAccount.getAddress(), bitcoinPublicKeyHash, secretHash, redeemAmount, expectedBitcoin);
|
||||
System.out.println("CIYAM AT creation bytes: " + HashCode.fromBytes(creationBytes).toString());
|
||||
|
||||
long txTimestamp = System.currentTimeMillis();
|
||||
|
Loading…
x
Reference in New Issue
Block a user