mirror of
https://github.com/Qortal/qortal.git
synced 2025-07-23 04:36:50 +00:00
work in progress: btc-qort cross-chain trades
Streamlined BTC class and switched to memory block store. Split BTCACCTTests into BTCACCT utility class and (so far) three stand-alone apps: Initiate1, Refund2 and Respond2 Moved some Qortal-specific CIYAM AT constants into blockchain config. Removed redundant BTCTests
This commit is contained in:
@@ -1,30 +1,23 @@
|
||||
package org.qora.test.btcacct;
|
||||
|
||||
import java.io.File;
|
||||
import java.math.BigDecimal;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.InsufficientMoneyException;
|
||||
import org.bitcoinj.core.LegacyAddress;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.TransactionBroadcast;
|
||||
import org.bitcoinj.kits.WalletAppKit;
|
||||
import org.bitcoinj.params.TestNet3Params;
|
||||
import org.bitcoinj.script.Script.ScriptType;
|
||||
import org.bitcoinj.wallet.SendRequest;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.qora.account.PrivateKeyAccount;
|
||||
import org.qora.account.PublicKeyAccount;
|
||||
import org.qora.asset.Asset;
|
||||
import org.qora.controller.Controller;
|
||||
import org.qora.crosschain.BTC;
|
||||
import org.qora.crosschain.BTCACCT;
|
||||
@@ -99,13 +92,12 @@ public class Initiate1 {
|
||||
|
||||
byte[] yourQortPrivKey = Base58.decode(yourQortPrivKey58);
|
||||
PrivateKeyAccount yourQortalAccount = new PrivateKeyAccount(repository, yourQortPrivKey);
|
||||
byte[] yourQortPubKey = yourQortalAccount.getPublicKey();
|
||||
System.out.println(String.format("Your Qortal address: %s", yourQortalAccount.getAddress()));
|
||||
|
||||
byte[] yourBitcoinPubKey = HashCode.fromString(yourBitcoinPubKeyHex).asBytes();
|
||||
ECKey yourBitcoinKey = ECKey.fromPublicOnly(yourBitcoinPubKey);
|
||||
Address yourBitcoinAddress = Address.fromKey(params, yourBitcoinKey, ScriptType.P2PKH);
|
||||
System.out.println(String.format("Your Bitcoin address: %s", yourBitcoinAddress.toString()));
|
||||
System.out.println(String.format("Your Bitcoin address: %s", yourBitcoinAddress));
|
||||
|
||||
byte[] theirQortPubKey = Base58.decode(theirQortPubKey58);
|
||||
PublicKeyAccount theirQortalAccount = new PublicKeyAccount(repository, theirQortPubKey);
|
||||
@@ -114,7 +106,15 @@ public class Initiate1 {
|
||||
byte[] theirBitcoinPubKey = HashCode.fromString(theirBitcoinPubKeyHex).asBytes();
|
||||
ECKey theirBitcoinKey = ECKey.fromPublicOnly(theirBitcoinPubKey);
|
||||
Address theirBitcoinAddress = Address.fromKey(params, theirBitcoinKey, ScriptType.P2PKH);
|
||||
System.out.println(String.format("Their Bitcoin address: %s", theirBitcoinAddress.toString()));
|
||||
System.out.println(String.format("Their Bitcoin address: %s", theirBitcoinAddress));
|
||||
|
||||
// Some checks
|
||||
BigDecimal qortAmount = new BigDecimal(rawQortAmount).setScale(8);
|
||||
BigDecimal yourQortBalance = yourQortalAccount.getConfirmedBalance(Asset.QORT);
|
||||
if (yourQortBalance.compareTo(qortAmount) <= 0) {
|
||||
System.err.println(String.format("Your QORT balance %s is less than required %s", yourQortBalance.toPlainString(), qortAmount.toPlainString()));
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
// New/derived info
|
||||
|
||||
@@ -129,7 +129,7 @@ public class Initiate1 {
|
||||
System.out.println("Hash of secret: " + HashCode.fromBytes(secretHash).toString());
|
||||
|
||||
int lockTime = (int) ((System.currentTimeMillis() / 1000L) + REFUND_TIMEOUT);
|
||||
System.out.println(String.format("Redeem script lockTime: %s (%d)", LocalDateTime.ofInstant(Instant.ofEpochSecond(lockTime), ZoneId.systemDefault()).toString(), lockTime));
|
||||
System.out.println(String.format("Redeem script lockTime: %s (%d)", LocalDateTime.ofInstant(Instant.ofEpochSecond(lockTime), ZoneId.systemDefault()), lockTime));
|
||||
|
||||
byte[] redeemScriptBytes = BTCACCT.buildRedeemScript(secretHash, yourBitcoinPubKey, theirBitcoinPubKey, lockTime);
|
||||
System.out.println("Redeem script: " + HashCode.fromBytes(redeemScriptBytes).toString());
|
||||
@@ -139,10 +139,10 @@ public class Initiate1 {
|
||||
Address p2shAddress = LegacyAddress.fromScriptHash(params, redeemScriptHash);
|
||||
System.out.println("P2SH address: " + p2shAddress.toString());
|
||||
|
||||
Coin bitcoinAmount = Coin.parseCoin(rawBitcoinAmount);
|
||||
Coin bitcoinAmount = Coin.parseCoin(rawBitcoinAmount).add(BTCACCT.DEFAULT_BTC_FEE);
|
||||
|
||||
// Fund P2SH
|
||||
System.out.println(String.format("\nYou need to fund %s with %s BTC", p2shAddress.toString(), bitcoinAmount.toPlainString()));
|
||||
System.out.println(String.format("\nYou need to fund %s with %s BTC (includes redeem/refund fee)", p2shAddress.toString(), bitcoinAmount.toPlainString()));
|
||||
|
||||
System.out.println("Once this is done, responder should run Respond2 to check P2SH funding and create AT");
|
||||
} catch (NumberFormatException e) {
|
||||
|
174
src/test/java/org/qora/test/btcacct/Refund2.java
Normal file
174
src/test/java/org/qora/test/btcacct/Refund2.java
Normal file
@@ -0,0 +1,174 @@
|
||||
package org.qora.test.btcacct;
|
||||
|
||||
import java.security.Security;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.List;
|
||||
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.LegacyAddress;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.TransactionOutput;
|
||||
import org.bitcoinj.params.TestNet3Params;
|
||||
import org.bitcoinj.script.Script.ScriptType;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.qora.controller.Controller;
|
||||
import org.qora.crosschain.BTC;
|
||||
import org.qora.crosschain.BTCACCT;
|
||||
import org.qora.repository.DataException;
|
||||
import org.qora.repository.Repository;
|
||||
import org.qora.repository.RepositoryFactory;
|
||||
import org.qora.repository.RepositoryManager;
|
||||
import org.qora.repository.hsqldb.HSQLDBRepositoryFactory;
|
||||
import org.qora.settings.Settings;
|
||||
|
||||
import com.google.common.hash.HashCode;
|
||||
|
||||
/**
|
||||
* Initiator must be Qora-chain so that initiator can send initial message to BTC P2SH then Qora can scan for P2SH add send corresponding message to Qora AT.
|
||||
*
|
||||
* Initiator (wants Qora, has BTC)
|
||||
* Funds BTC P2SH address
|
||||
*
|
||||
* Responder (has Qora, wants BTC)
|
||||
* Builds Qora ACCT AT and funds it with Qora
|
||||
*
|
||||
* Initiator sends recipient+secret+script as input to BTC P2SH address, releasing BTC amount - fees to responder
|
||||
*
|
||||
* Qora nodes scan for P2SH output, checks amount and recipient and if ok sends secret to Qora ACCT AT
|
||||
* (Or it's possible to feed BTC transaction details into Qora AT so it can check them itself?)
|
||||
*
|
||||
* Qora ACCT AT sends its Qora to initiator
|
||||
*
|
||||
*/
|
||||
|
||||
public class Refund2 {
|
||||
|
||||
static {
|
||||
// This must go before any calls to LogManager/Logger
|
||||
System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager");
|
||||
}
|
||||
|
||||
private static final long REFUND_TIMEOUT = 600L; // seconds
|
||||
|
||||
private static void usage() {
|
||||
System.err.println(String.format("usage: Refund2 <your-BTC-PRIVkey> <their-BTC-pubkey> <hash-of-secret> <locktime> <P2SH-address>"));
|
||||
System.err.println(String.format("example: Refund2 027fb5828c5e201eaf6de4cd3b0b340d16a191ef848cd691f35ef8f727358c9c \\\n"
|
||||
+ "\t032783606be32a3e639a33afe2b15f058708ab124f3b290d595ee954390a0c8559 \\\n"
|
||||
+ "\tb837056cdc5d805e4db1f830a58158e1131ac96ea71de4c6f9d7854985e153e2 1575021641 2MvGdGUgAfc7qTHaZJwWmZ26Fg6Hjif8gNy"));
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length != 5)
|
||||
usage();
|
||||
|
||||
Security.insertProviderAt(new BouncyCastleProvider(), 0);
|
||||
|
||||
Settings.fileInstance("settings-test.json");
|
||||
|
||||
NetworkParameters params = TestNet3Params.get();
|
||||
|
||||
int argIndex = 0;
|
||||
String yourBitcoinPrivKeyHex = args[argIndex++];
|
||||
String theirBitcoinPubKeyHex = args[argIndex++];
|
||||
|
||||
String secretHashHex = args[argIndex++];
|
||||
String rawLockTime = args[argIndex++];
|
||||
String rawP2shAddress = args[argIndex++];
|
||||
|
||||
try {
|
||||
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Controller.getRepositoryUrl());
|
||||
RepositoryManager.setRepositoryFactory(repositoryFactory);
|
||||
} catch (DataException e) {
|
||||
throw new RuntimeException("Repository startup issue: " + e.getMessage());
|
||||
}
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
System.out.println("Confirm the following is correct based on the info you've given:");
|
||||
|
||||
byte[] yourBitcoinPrivKey = HashCode.fromString(yourBitcoinPrivKeyHex).asBytes();
|
||||
ECKey yourBitcoinKey = ECKey.fromPrivate(yourBitcoinPrivKey);
|
||||
Address yourBitcoinAddress = Address.fromKey(params, yourBitcoinKey, ScriptType.P2PKH);
|
||||
System.out.println(String.format("Your Bitcoin address: %s", yourBitcoinAddress));
|
||||
|
||||
byte[] theirBitcoinPubKey = HashCode.fromString(theirBitcoinPubKeyHex).asBytes();
|
||||
ECKey theirBitcoinKey = ECKey.fromPublicOnly(theirBitcoinPubKey);
|
||||
Address theirBitcoinAddress = Address.fromKey(params, theirBitcoinKey, ScriptType.P2PKH);
|
||||
System.out.println(String.format("Their Bitcoin address: %s", theirBitcoinAddress));
|
||||
|
||||
// New/derived info
|
||||
|
||||
int lockTime = Integer.valueOf(rawLockTime);
|
||||
System.out.println(String.format("Redeem script lockTime: %s (%d)", LocalDateTime.ofInstant(Instant.ofEpochSecond(lockTime), ZoneId.systemDefault()), lockTime));
|
||||
|
||||
byte[] secretHash = HashCode.fromString(secretHashHex).asBytes();
|
||||
System.out.println("Hash of secret: " + HashCode.fromBytes(secretHash).toString());
|
||||
|
||||
byte[] redeemScriptBytes = BTCACCT.buildRedeemScript(secretHash, yourBitcoinKey.getPubKey(), theirBitcoinPubKey, lockTime);
|
||||
System.out.println("Redeem script: " + HashCode.fromBytes(redeemScriptBytes).toString());
|
||||
|
||||
byte[] redeemScriptHash = BTC.hash160(redeemScriptBytes);
|
||||
|
||||
Address p2shAddress = LegacyAddress.fromScriptHash(params, redeemScriptHash);
|
||||
System.out.println(String.format("P2SH address: %s", p2shAddress));
|
||||
|
||||
if (!p2shAddress.toString().equals(rawP2shAddress)) {
|
||||
System.err.println(String.format("Derived P2SH address %s does not match given address %s", p2shAddress, rawP2shAddress));
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
// Some checks
|
||||
long medianBlockTime = BTC.getInstance().getMedianBlockTime();
|
||||
System.out.println(String.format("Median block time: %s", LocalDateTime.ofInstant(Instant.ofEpochSecond(medianBlockTime), ZoneId.systemDefault())));
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if (now < medianBlockTime * 1000L) {
|
||||
System.err.println(String.format("Too soon (%s) to refund based on median block time %s", LocalDateTime.ofInstant(Instant.ofEpochMilli(now), ZoneId.systemDefault()), LocalDateTime.ofInstant(Instant.ofEpochSecond(medianBlockTime), ZoneId.systemDefault())));
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
if (now < lockTime * 1000L) {
|
||||
System.err.println(String.format("Too soon (%s) to refund based on lockTime %s", LocalDateTime.ofInstant(Instant.ofEpochMilli(now), ZoneId.systemDefault()), LocalDateTime.ofInstant(Instant.ofEpochSecond(lockTime), ZoneId.systemDefault())));
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
Coin p2shBalance = BTC.getInstance().getBalance(p2shAddress.toString(), lockTime - REFUND_TIMEOUT);
|
||||
if (p2shBalance == null) {
|
||||
System.err.println(String.format("Unable to check P2SH address %s balance", p2shAddress));
|
||||
System.exit(2);
|
||||
}
|
||||
System.out.println(String.format("P2SH address %s balance: %s BTC", p2shAddress, p2shBalance.toPlainString()));
|
||||
|
||||
// Grab all P2SH funding transactions (just in case there are more than one)
|
||||
List<TransactionOutput> fundingOutputs = BTC.getInstance().getUnspentOutputs(p2shAddress.toString(), lockTime - REFUND_TIMEOUT);
|
||||
System.out.println(String.format("Found %d unspent output%s for P2SH", fundingOutputs.size(), (fundingOutputs.size() != 1 ? "s" : "")));
|
||||
|
||||
if (fundingOutputs.isEmpty()) {
|
||||
System.err.println(String.format("Can't refund spent/unfunded P2SH"));
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
if (fundingOutputs.size() != 1) {
|
||||
System.err.println(String.format("Expecting only one unspent output for P2SH"));
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
Transaction refundTransaction = BTCACCT.buildRefundTransaction(p2shBalance, yourBitcoinKey, fundingOutputs.get(0), redeemScriptBytes, lockTime);
|
||||
|
||||
byte[] refundBytes = refundTransaction.bitcoinSerialize();
|
||||
|
||||
System.out.println(String.format("\nLoad this transaction into your wallet, sign and broadcast:\n%s\n", HashCode.fromBytes(refundBytes).toString()));
|
||||
} catch (NumberFormatException e) {
|
||||
usage();
|
||||
} catch (DataException e) {
|
||||
throw new RuntimeException("Repository issue: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,13 +1,13 @@
|
||||
package org.qora.test.btcacct;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.LegacyAddress;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
@@ -20,7 +20,6 @@ import org.qora.asset.Asset;
|
||||
import org.qora.controller.Controller;
|
||||
import org.qora.crosschain.BTC;
|
||||
import org.qora.crosschain.BTCACCT;
|
||||
import org.qora.crypto.Crypto;
|
||||
import org.qora.data.transaction.BaseTransactionData;
|
||||
import org.qora.data.transaction.DeployAtTransactionData;
|
||||
import org.qora.data.transaction.TransactionData;
|
||||
@@ -30,8 +29,11 @@ import org.qora.repository.Repository;
|
||||
import org.qora.repository.RepositoryFactory;
|
||||
import org.qora.repository.RepositoryManager;
|
||||
import org.qora.repository.hsqldb.HSQLDBRepositoryFactory;
|
||||
import org.qora.settings.Settings;
|
||||
import org.qora.transaction.DeployAtTransaction;
|
||||
import org.qora.transaction.Transaction;
|
||||
import org.qora.transform.TransformationException;
|
||||
import org.qora.transform.transaction.TransactionTransformer;
|
||||
import org.qora.utils.Base58;
|
||||
|
||||
import com.google.common.hash.HashCode;
|
||||
@@ -60,12 +62,12 @@ public class Respond2 {
|
||||
|
||||
private static void usage() {
|
||||
System.err.println(String.format("usage: Respond2 <your-QORT-PRIVkey> <your-BTC-pubkey> <QORT-amount> <BTC-amount> <their-QORT-pubkey> <their-BTC-pubkey> <hash-of-secret> <locktime> <P2SH-address>"));
|
||||
System.err.println(String.format("example: Respond2 pYQ6DpQBJ2n72TCLJLScEvwhf3boxWy2kQEPynakwpj \\\n"
|
||||
+ "\t03aa20871c2195361f2826c7a649eab6b42639630c4d8c33c55311d5c1e476b5d6 \\\n"
|
||||
+ "\t123 0.00008642 \\\n"
|
||||
+ "\tJBNBQQDzZsm5do1BrwWAp53Ps4KYJVt749EGpCf7ofte \\\n"
|
||||
System.err.println(String.format("example: Respond2 3jjoToDaDpsdUHqaouLGypFeewNVKvtkmdM38i54WVra \\\n"
|
||||
+ "\t032783606be32a3e639a33afe2b15f058708ab124f3b290d595ee954390a0c8559 \\\n"
|
||||
+ "\te43f5ab106b70df2e85656de30e1862891752f81e82f5dfd03abb8465a7346f9 1574441679 2N4R2pSEzLcJgtgAbFuLvviwwEkBrmq6sx4"));
|
||||
+ "\t123 0.00008642 \\\n"
|
||||
+ "\t6rNn9b3pYRrG9UKqzMWYZ9qa8F3Zgv2mVWrULGHUusb \\\n"
|
||||
+ "\t03aa20871c2195361f2826c7a649eab6b42639630c4d8c33c55311d5c1e476b5d6 \\\n"
|
||||
+ "\tb837056cdc5d805e4db1f830a58158e1131ac96ea71de4c6f9d7854985e153e2 1575021641 2MvGdGUgAfc7qTHaZJwWmZ26Fg6Hjif8gNy"));
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
@@ -74,6 +76,9 @@ public class Respond2 {
|
||||
usage();
|
||||
|
||||
Security.insertProviderAt(new BouncyCastleProvider(), 0);
|
||||
|
||||
Settings.fileInstance("settings-test.json");
|
||||
|
||||
NetworkParameters params = TestNet3Params.get();
|
||||
|
||||
int argIndex = 0;
|
||||
@@ -108,7 +113,7 @@ public class Respond2 {
|
||||
byte[] yourBitcoinPubKey = HashCode.fromString(yourBitcoinPubKeyHex).asBytes();
|
||||
ECKey yourBitcoinKey = ECKey.fromPublicOnly(yourBitcoinPubKey);
|
||||
Address yourBitcoinAddress = Address.fromKey(params, yourBitcoinKey, ScriptType.P2PKH);
|
||||
System.out.println(String.format("Your Bitcoin address: %s", yourBitcoinAddress.toString()));
|
||||
System.out.println(String.format("Your Bitcoin address: %s", yourBitcoinAddress));
|
||||
|
||||
byte[] theirQortPubKey = Base58.decode(theirQortPubKey58);
|
||||
PublicKeyAccount theirQortalAccount = new PublicKeyAccount(repository, theirQortPubKey);
|
||||
@@ -117,7 +122,7 @@ public class Respond2 {
|
||||
byte[] theirBitcoinPubKey = HashCode.fromString(theirBitcoinPubKeyHex).asBytes();
|
||||
ECKey theirBitcoinKey = ECKey.fromPublicOnly(theirBitcoinPubKey);
|
||||
Address theirBitcoinAddress = Address.fromKey(params, theirBitcoinKey, ScriptType.P2PKH);
|
||||
System.out.println(String.format("Their Bitcoin address: %s", theirBitcoinAddress.toString()));
|
||||
System.out.println(String.format("Their Bitcoin address: %s", theirBitcoinAddress));
|
||||
|
||||
System.out.println("Hash of secret: " + secretHashHex);
|
||||
|
||||
@@ -126,7 +131,7 @@ public class Respond2 {
|
||||
System.out.println("\nCHECKING info from other party:");
|
||||
|
||||
int lockTime = Integer.valueOf(rawLockTime);
|
||||
System.out.println(String.format("Redeem script lockTime: %s (%d)", LocalDateTime.ofInstant(Instant.ofEpochSecond(lockTime), ZoneId.systemDefault()).toString(), lockTime));
|
||||
System.out.println(String.format("Redeem script lockTime: %s (%d)", LocalDateTime.ofInstant(Instant.ofEpochSecond(lockTime), ZoneId.systemDefault()), lockTime));
|
||||
|
||||
byte[] secretHash = HashCode.fromString(secretHashHex).asBytes();
|
||||
System.out.println("Hash of secret: " + HashCode.fromBytes(secretHash).toString());
|
||||
@@ -137,15 +142,26 @@ public class Respond2 {
|
||||
byte[] redeemScriptHash = BTC.hash160(redeemScriptBytes);
|
||||
|
||||
Address p2shAddress = LegacyAddress.fromScriptHash(params, redeemScriptHash);
|
||||
System.out.println("P2SH address: " + p2shAddress.toString());
|
||||
System.out.println(String.format("P2SH address: %s", p2shAddress));
|
||||
|
||||
if (!p2shAddress.toString().equals(rawP2shAddress)) {
|
||||
System.err.println(String.format("Derived P2SH address %s does not match given address %s", p2shAddress.toString(), rawP2shAddress));
|
||||
System.err.println(String.format("Derived P2SH address %s does not match given address %s", p2shAddress, rawP2shAddress));
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
// TODO: Check for funded P2SH
|
||||
// Check for funded P2SH
|
||||
Coin bitcoinAmount = Coin.parseCoin(rawBitcoinAmount).add(BTCACCT.DEFAULT_BTC_FEE);
|
||||
|
||||
Coin p2shBalance = BTC.getInstance().getBalance(p2shAddress.toString(), lockTime - REFUND_TIMEOUT);
|
||||
if (p2shBalance == null) {
|
||||
System.err.println(String.format("Unable to check P2SH address %s balance", p2shAddress));
|
||||
System.exit(2);
|
||||
}
|
||||
if (p2shBalance.isLessThan(bitcoinAmount)) {
|
||||
System.err.println(String.format("P2SH address %s has lower balance than expected %s BTC", p2shAddress, p2shBalance.toPlainString()));
|
||||
System.exit(2);
|
||||
}
|
||||
System.out.println(String.format("P2SH address %s balance: %s BTC", p2shAddress, p2shBalance.toPlainString()));
|
||||
|
||||
System.out.println("\nYour response:");
|
||||
|
||||
@@ -173,6 +189,16 @@ public class Respond2 {
|
||||
deployAtTransactionData.setFee(fee);
|
||||
|
||||
deployAtTransaction.sign(yourQortalAccount);
|
||||
|
||||
byte[] signedBytes = null;
|
||||
try {
|
||||
signedBytes = TransactionTransformer.toBytes(deployAtTransactionData);
|
||||
} catch (TransformationException e) {
|
||||
System.err.println(String.format("Unable to convert transaction to base58: %s", e.getMessage()));
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
System.out.println(String.format("\nSigned transaction in base58, ready for POST /transactions/process:\n%s\n", Base58.encode(signedBytes)));
|
||||
} catch (NumberFormatException e) {
|
||||
usage();
|
||||
} catch (DataException e) {
|
||||
|
Reference in New Issue
Block a user