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:
catbref
2020-04-22 16:18:21 +01:00
parent 94d18538d8
commit 833a785996
19 changed files with 792 additions and 114 deletions

View File

@@ -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));
}
}

View 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));
}
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -1,4 +1,5 @@
{
"bitcoinNet": "TEST3",
"restrictedApi": false,
"blockchainConfig": "src/test/resources/test-chain-v2.json",
"wipeUnconfirmedOnStart": false,