mirror of
https://github.com/Qortal/qortal.git
synced 2025-07-23 04:36:50 +00:00
More work on Bitcoin-side of cross-chain trading.
Tidied up duplicated cross-chain API code that fetched Qortal AT info. Added Bitcoin-related cross-chain API calls for building, checking, refunding and redeeming P2SH. Added new Bitcoin-related API error codes. Controller now starts up, and shuts down, bitcoinj. Speed-up in BTC class so bitcoinj doesn't have to throw away all peers and rediscover & reconnect to them with every chain-related call.
This commit is contained in:
@@ -13,13 +13,11 @@ 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;
|
||||
import org.qortal.account.Account;
|
||||
import org.qortal.account.PrivateKeyAccount;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.at.QortalAtLoggerFactory;
|
||||
import org.qortal.crosschain.BTCACCT;
|
||||
import org.qortal.crypto.Crypto;
|
||||
import org.qortal.data.at.ATData;
|
||||
@@ -508,20 +506,7 @@ public class AtTests extends Common {
|
||||
|
||||
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);
|
||||
CrossChainTradeData tradeData = BTCACCT.populateTradeData(repository, atData);
|
||||
|
||||
Function<Long, String> epochMilliFormatter = (timestamp) -> LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC).format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM));
|
||||
int currentBlockHeight = repository.getBlockRepository().getBlockchainHeight();
|
||||
@@ -536,7 +521,7 @@ public class AtTests extends Common {
|
||||
+ "\texpected bitcoin: %s BTC,\n"
|
||||
+ "\ttrade timeout: %d minutes (from trade start),\n"
|
||||
+ "\tcurrent block height: %d,\n",
|
||||
tradeData.qortalAddress,
|
||||
tradeData.qortalAtAddress,
|
||||
tradeData.qortalCreator,
|
||||
epochMilliFormatter.apply(tradeData.creationTimestamp),
|
||||
tradeData.qortBalance.toPlainString(),
|
||||
@@ -555,8 +540,10 @@ public class AtTests extends Common {
|
||||
// Trade
|
||||
System.out.println(String.format("\tstatus: 'trade mode',\n"
|
||||
+ "\ttrade timeout: block %d,\n"
|
||||
+ "\tBitcoin P2SH nLockTime: %d (%s),\n"
|
||||
+ "\ttrade recipient: %s",
|
||||
tradeData.tradeRefundHeight,
|
||||
tradeData.lockTime, epochMilliFormatter.apply(tradeData.lockTime * 1000L),
|
||||
tradeData.qortalRecipient));
|
||||
}
|
||||
}
|
||||
|
65
src/test/java/org/qortal/test/btcacct/BtcTests.java
Normal file
65
src/test/java/org/qortal/test/btcacct/BtcTests.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package org.qortal.test.btcacct;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.bitcoinj.store.BlockStoreException;
|
||||
import org.bitcoinj.wallet.WalletTransaction;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.qortal.crosschain.BTC;
|
||||
import org.qortal.crosschain.BTCACCT;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.test.common.Common;
|
||||
|
||||
public class BtcTests extends Common {
|
||||
|
||||
@Before
|
||||
public void beforeTest() throws DataException {
|
||||
Common.useDefaultSettings();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMedianBlockTime() throws BlockStoreException {
|
||||
System.out.println(String.format("Starting BTC instance..."));
|
||||
BTC btc = BTC.getInstance();
|
||||
System.out.println(String.format("BTC instance started"));
|
||||
|
||||
long before = System.currentTimeMillis();
|
||||
System.out.println(String.format("Bitcoin median blocktime: %d", btc.getMedianBlockTime()));
|
||||
long afterFirst = System.currentTimeMillis();
|
||||
|
||||
System.out.println(String.format("Bitcoin median blocktime: %d", btc.getMedianBlockTime()));
|
||||
long afterSecond = System.currentTimeMillis();
|
||||
|
||||
long firstPeriod = afterFirst - before;
|
||||
long secondPeriod = afterSecond - afterFirst;
|
||||
|
||||
System.out.println(String.format("1st call: %d ms, 2nd call: %d ms", firstPeriod, secondPeriod));
|
||||
|
||||
assertTrue("2nd call should be quicker than 1st", secondPeriod < firstPeriod);
|
||||
assertTrue("2nd call should take less than 5 seconds", secondPeriod < 5000L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindP2shSecret() {
|
||||
// This actually exists on TEST3
|
||||
String p2shAddress = "2N8WCg52ULCtDSMjkgVTm5mtPdCsUptkHWE";
|
||||
int startTime = 1587510000;
|
||||
|
||||
List<WalletTransaction> walletTransactions = new ArrayList<>();
|
||||
|
||||
BTC.getInstance().getBalanceAndOtherInfo(p2shAddress, startTime, null, walletTransactions);
|
||||
|
||||
byte[] expectedSecret = AtTests.secret;
|
||||
byte[] secret = BTCACCT.findP2shSecret(p2shAddress, walletTransactions);
|
||||
|
||||
assertNotNull(secret);
|
||||
assertTrue("secret incorrect", Arrays.equals(expectedSecret, secret));
|
||||
}
|
||||
|
||||
}
|
@@ -134,7 +134,7 @@ public class CheckP2SH {
|
||||
System.out.println(String.format("Too soon (%s) to redeem based on median block time %s", LocalDateTime.ofInstant(Instant.ofEpochMilli(now), ZoneOffset.UTC), LocalDateTime.ofInstant(Instant.ofEpochSecond(medianBlockTime), ZoneOffset.UTC)));
|
||||
|
||||
// Check P2SH is funded
|
||||
final long startTime = lockTime - 86400;
|
||||
final int startTime = lockTime - 86400;
|
||||
|
||||
Coin p2shBalance = BTC.getInstance().getBalance(p2shAddress.toString(), startTime);
|
||||
if (p2shBalance == null) {
|
||||
|
@@ -146,7 +146,7 @@ public class Redeem {
|
||||
}
|
||||
|
||||
// Check P2SH is funded
|
||||
final long startTime = ((int) (System.currentTimeMillis() / 1000L)) - 86400;
|
||||
final int startTime = ((int) (System.currentTimeMillis() / 1000L)) - 86400;
|
||||
|
||||
Coin p2shBalance = BTC.getInstance().getBalance(p2shAddress.toString(), startTime);
|
||||
if (p2shBalance == null) {
|
||||
|
@@ -150,7 +150,7 @@ public class Refund {
|
||||
}
|
||||
|
||||
// Check P2SH is funded
|
||||
final long startTime = ((int) (System.currentTimeMillis() / 1000L)) - 86400;
|
||||
final int startTime = ((int) (System.currentTimeMillis() / 1000L)) - 86400;
|
||||
|
||||
Coin p2shBalance = BTC.getInstance().getBalance(p2shAddress.toString(), startTime);
|
||||
if (p2shBalance == null) {
|
||||
|
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"bitcoinNet": "TEST3",
|
||||
"restrictedApi": false,
|
||||
"blockchainConfig": "src/test/resources/test-chain-v2.json",
|
||||
"wipeUnconfirmedOnStart": false,
|
||||
|
Reference in New Issue
Block a user