forked from Qortal/qortal
Work on BTC-ACCT
Bump CIYAM AT dependency to v1.2 for MachineState.toCreationBytes()
This commit is contained in:
parent
5011a2be22
commit
d58b7c1f53
BIN
lib/org/ciyam/at/1.2/at-1.2.jar
Normal file
BIN
lib/org/ciyam/at/1.2/at-1.2.jar
Normal file
Binary file not shown.
9
lib/org/ciyam/at/1.2/at-1.2.pom
Normal file
9
lib/org/ciyam/at/1.2/at-1.2.pom
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>org.ciyam</groupId>
|
||||||
|
<artifactId>at</artifactId>
|
||||||
|
<version>1.2</version>
|
||||||
|
<description>POM was created from install:install-file</description>
|
||||||
|
</project>
|
@ -3,10 +3,11 @@
|
|||||||
<groupId>org.ciyam</groupId>
|
<groupId>org.ciyam</groupId>
|
||||||
<artifactId>at</artifactId>
|
<artifactId>at</artifactId>
|
||||||
<versioning>
|
<versioning>
|
||||||
<release>1.0</release>
|
<release>1.2</release>
|
||||||
<versions>
|
<versions>
|
||||||
<version>1.0</version>
|
<version>1.0</version>
|
||||||
|
<version>1.2</version>
|
||||||
</versions>
|
</versions>
|
||||||
<lastUpdated>20181105100741</lastUpdated>
|
<lastUpdated>20191120104937</lastUpdated>
|
||||||
</versioning>
|
</versioning>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
181
src/main/java/org/qora/crosschain/BTCACCT.java
Normal file
181
src/main/java/org/qora/crosschain/BTCACCT.java
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
package org.qora.crosschain;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
import org.ciyam.at.FunctionCode;
|
||||||
|
import org.ciyam.at.MachineState;
|
||||||
|
import org.ciyam.at.OpCode;
|
||||||
|
import org.qora.utils.BitTwiddling;
|
||||||
|
|
||||||
|
import com.google.common.hash.HashCode;
|
||||||
|
import com.google.common.primitives.Bytes;
|
||||||
|
|
||||||
|
public class BTCACCT {
|
||||||
|
|
||||||
|
private static final byte[] redeemScript1 = HashCode.fromString("76a820").asBytes(); // OP_DUP OP_SHA256 push(0x20 bytes)
|
||||||
|
private static final byte[] redeemScript2 = HashCode.fromString("87637576a914").asBytes(); // OP_EQUAL OP_IF OP_DROP OP_DUP OP_HASH160 push(0x14 bytes)
|
||||||
|
private static final byte[] redeemScript3 = HashCode.fromString("88ac6704").asBytes(); // OP_EQUALVERIFY OP_CHECKSIG OP_ELSE push(0x4 bytes)
|
||||||
|
private static final byte[] redeemScript4 = HashCode.fromString("b17576a914").asBytes(); // OP_CHECKLOCKTIMEVERIFY OP_DROP OP_DUP OP_HASH160 push(0x14 bytes)
|
||||||
|
private static final byte[] redeemScript5 = HashCode.fromString("88ac68").asBytes(); // OP_EQUALVERIFY OP_CHECKSIG OP_ENDIF
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Bitcoin redeem script.
|
||||||
|
* <p>
|
||||||
|
* <pre>
|
||||||
|
* OP_DUP OP_SHA256 push(0x20) <SHA256 of secret> OP_EQUAL
|
||||||
|
* OP_IF
|
||||||
|
* OP_DROP OP_DUP OP_HASH160 push(0x14) <HASH160 of recipient pubkey>
|
||||||
|
* OP_EQUALVERIFY OP_CHECKSIG
|
||||||
|
* OP_ELSE
|
||||||
|
* push(0x04) <refund locktime> OP_CHECKLOCKTIMEVERIFY
|
||||||
|
* OP_DROP OP_DUP OP_HASH160 push(0x14) <HASH160 of sender pubkey>
|
||||||
|
* OP_EQUALVERIFY OP_CHECKSIG
|
||||||
|
* OP_ENDIF
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param secretHash
|
||||||
|
* @param senderPubKey
|
||||||
|
* @param recipientPubKey
|
||||||
|
* @param lockTime
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static byte[] buildRedeemScript(byte[] secretHash, byte[] senderPubKey, byte[] recipientPubKey, long lockTime) {
|
||||||
|
byte[] senderPubKeyHash160 = BTC.hash160(senderPubKey);
|
||||||
|
byte[] recipientPubKeyHash160 = BTC.hash160(recipientPubKey);
|
||||||
|
|
||||||
|
return Bytes.concat(redeemScript1, secretHash, redeemScript2, recipientPubKeyHash160, redeemScript3, BitTwiddling.toLEByteArray((int) (lockTime & 0xffffffffL)),
|
||||||
|
redeemScript4, senderPubKeyHash160, redeemScript5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] buildCiyamAT(byte[] secretHash, byte[] destinationQortalPubKey, long refundMinutes) {
|
||||||
|
// Labels for data segment addresses
|
||||||
|
int addrCounter = 0;
|
||||||
|
final int addrHashPart1 = addrCounter++;
|
||||||
|
final int addrHashPart2 = addrCounter++;
|
||||||
|
final int addrHashPart3 = addrCounter++;
|
||||||
|
final int addrHashPart4 = addrCounter++;
|
||||||
|
final int addrAddressPart1 = addrCounter++;
|
||||||
|
final int addrAddressPart2 = addrCounter++;
|
||||||
|
final int addrAddressPart3 = addrCounter++;
|
||||||
|
final int addrAddressPart4 = addrCounter++;
|
||||||
|
final int addrRefundMinutes = addrCounter++;
|
||||||
|
final int addrRefundTimestamp = addrCounter++;
|
||||||
|
final int addrLastTimestamp = addrCounter++;
|
||||||
|
final int addrBlockTimestamp = addrCounter++;
|
||||||
|
final int addrTxType = addrCounter++;
|
||||||
|
final int addrComparator = addrCounter++;
|
||||||
|
final int addrAddressTemp1 = addrCounter++;
|
||||||
|
final int addrAddressTemp2 = addrCounter++;
|
||||||
|
final int addrAddressTemp3 = addrCounter++;
|
||||||
|
final int addrAddressTemp4 = addrCounter++;
|
||||||
|
|
||||||
|
// Data segment
|
||||||
|
ByteBuffer dataByteBuffer = ByteBuffer.allocate(addrCounter * 8).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
|
||||||
|
// Hash of secret into HashPart1-4
|
||||||
|
dataByteBuffer.put(secretHash);
|
||||||
|
|
||||||
|
// Destination Qortal account's public key
|
||||||
|
dataByteBuffer.put(destinationQortalPubKey);
|
||||||
|
|
||||||
|
// Expiry in minutes
|
||||||
|
dataByteBuffer.putLong(refundMinutes);
|
||||||
|
|
||||||
|
// Code labels
|
||||||
|
final int addrTxLoop = 0x36;
|
||||||
|
final int addrCheckTx = 0x4b;
|
||||||
|
final int addrCheckSender = 0x64;
|
||||||
|
final int addrCheckMessage = 0xab;
|
||||||
|
final int addrPayout = 0xdf;
|
||||||
|
final int addrRefund = 0x102;
|
||||||
|
final int addrEndOfCode = 0x109;
|
||||||
|
|
||||||
|
int tempPC;
|
||||||
|
ByteBuffer codeByteBuffer = ByteBuffer.allocate(addrEndOfCode * 1).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
|
||||||
|
// init:
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_CREATION_TIMESTAMP.value).putInt(addrRefundTimestamp);
|
||||||
|
codeByteBuffer.put(OpCode.SET_DAT.value).putInt(addrLastTimestamp).putInt(addrRefundTimestamp);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_RET_DAT_2.value).putShort(FunctionCode.ADD_MINUTES_TO_TIMESTAMP.value).putInt(addrRefundTimestamp)
|
||||||
|
.putInt(addrRefundTimestamp).putInt(addrRefundMinutes);
|
||||||
|
codeByteBuffer.put(OpCode.SET_PCS.value);
|
||||||
|
|
||||||
|
// loop:
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_BLOCK_TIMESTAMP.value).putInt(addrBlockTimestamp);
|
||||||
|
tempPC = codeByteBuffer.position();
|
||||||
|
codeByteBuffer.put(OpCode.BLT_DAT.value).putInt(addrBlockTimestamp).putInt(addrRefundTimestamp).put((byte) (addrTxLoop - tempPC));
|
||||||
|
codeByteBuffer.put(OpCode.JMP_ADR.value).putInt(addrRefund);
|
||||||
|
|
||||||
|
// txloop:
|
||||||
|
assert codeByteBuffer.position() == addrTxLoop : "addrTxLoop incorrect";
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.PUT_TX_AFTER_TIMESTAMP_IN_A.value).putInt(addrLastTimestamp);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.CHECK_A_IS_ZERO.value).putInt(addrComparator);
|
||||||
|
tempPC = codeByteBuffer.position();
|
||||||
|
codeByteBuffer.put(OpCode.BZR_DAT.value).putInt(addrComparator).put((byte) (addrCheckTx - tempPC));
|
||||||
|
codeByteBuffer.put(OpCode.STP_IMD.value);
|
||||||
|
|
||||||
|
// checkTx:
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_TIMESTAMP_FROM_TX_IN_A.value).putInt(addrLastTimestamp);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_TYPE_FROM_TX_IN_A.value).putInt(addrTxType);
|
||||||
|
tempPC = codeByteBuffer.position();
|
||||||
|
codeByteBuffer.put(OpCode.BNZ_DAT.value).putInt(addrTxType).put((byte) (addrCheckSender - tempPC));
|
||||||
|
codeByteBuffer.put(OpCode.JMP_ADR.value).putInt(addrTxLoop);
|
||||||
|
|
||||||
|
// checkSender
|
||||||
|
assert codeByteBuffer.position() == addrCheckSender : "addrCheckSender incorrect";
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PUT_ADDRESS_FROM_TX_IN_A_INTO_B.value);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_B1.value).putInt(addrAddressTemp1);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_B2.value).putInt(addrAddressTemp2);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_B3.value).putInt(addrAddressTemp3);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_B4.value).putInt(addrAddressTemp4);
|
||||||
|
tempPC = codeByteBuffer.position();
|
||||||
|
codeByteBuffer.put(OpCode.BNE_DAT.value).putInt(addrAddressTemp1).putInt(addrAddressPart1).put((byte) (addrTxLoop - tempPC));
|
||||||
|
tempPC = codeByteBuffer.position();
|
||||||
|
codeByteBuffer.put(OpCode.BNE_DAT.value).putInt(addrAddressTemp2).putInt(addrAddressPart2).put((byte) (addrTxLoop - tempPC));
|
||||||
|
tempPC = codeByteBuffer.position();
|
||||||
|
codeByteBuffer.put(OpCode.BNE_DAT.value).putInt(addrAddressTemp3).putInt(addrAddressPart3).put((byte) (addrTxLoop - tempPC));
|
||||||
|
tempPC = codeByteBuffer.position();
|
||||||
|
codeByteBuffer.put(OpCode.BNE_DAT.value).putInt(addrAddressTemp4).putInt(addrAddressPart4).put((byte) (addrTxLoop - tempPC));
|
||||||
|
|
||||||
|
// checkMessage:
|
||||||
|
assert codeByteBuffer.position() == addrCheckMessage : "addrCheckMessage incorrect";
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PUT_MESSAGE_FROM_TX_IN_A_INTO_B.value);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.SWAP_A_AND_B.value);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B1.value).putInt(addrHashPart1);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B2.value).putInt(addrHashPart2);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B3.value).putInt(addrHashPart3);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B4.value).putInt(addrHashPart4);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.CHECK_SHA256_A_WITH_B.value).putInt(addrComparator);
|
||||||
|
tempPC = codeByteBuffer.position();
|
||||||
|
codeByteBuffer.put(OpCode.BNZ_DAT.value).putInt(addrComparator).put((byte) (addrPayout - tempPC));
|
||||||
|
codeByteBuffer.put(OpCode.JMP_ADR.value).putInt(addrTxLoop);
|
||||||
|
|
||||||
|
// payout:
|
||||||
|
assert codeByteBuffer.position() == addrPayout : "addrPayout incorrect";
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B1.value).putInt(addrAddressPart1);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B2.value).putInt(addrAddressPart2);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B3.value).putInt(addrAddressPart3);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B4.value).putInt(addrAddressPart4);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.MESSAGE_A_TO_ADDRESS_IN_B.value);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PAY_ALL_TO_ADDRESS_IN_B.value);
|
||||||
|
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||||
|
|
||||||
|
// refund:
|
||||||
|
assert codeByteBuffer.position() == addrRefund : "addrRefund incorrect";
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PUT_CREATOR_INTO_B.value);
|
||||||
|
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PAY_ALL_TO_ADDRESS_IN_B.value);
|
||||||
|
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||||
|
|
||||||
|
// end-of-code
|
||||||
|
assert codeByteBuffer.position() == addrEndOfCode : "addrEndOfCode incorrect";
|
||||||
|
|
||||||
|
final short ciyamAtVersion = 2;
|
||||||
|
final short numCallStackPages = 0;
|
||||||
|
final short numUserStackPages = 0;
|
||||||
|
final long minActivationAmount = 0L;
|
||||||
|
|
||||||
|
return MachineState.toCreationBytes(ciyamAtVersion, codeByteBuffer.array(), dataByteBuffer.array(), numCallStackPages, numUserStackPages, minActivationAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -14,6 +14,7 @@ import java.nio.ByteBuffer;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.DigestOutputStream;
|
import java.security.DigestOutputStream;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
@ -47,8 +48,28 @@ import org.qortal.settings.Settings;
|
|||||||
|
|
||||||
public class BTC {
|
public class BTC {
|
||||||
|
|
||||||
private static class RollbackBlockChain extends BlockChain {
|
private static final MessageDigest RIPE_MD160_DIGESTER;
|
||||||
|
private static final MessageDigest SHA256_DIGESTER;
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
RIPE_MD160_DIGESTER = MessageDigest.getInstance("RIPEMD160");
|
||||||
|
SHA256_DIGESTER = MessageDigest.getInstance("SHA-256");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BTC instance;
|
||||||
|
|
||||||
|
private static File directory;
|
||||||
|
private static String chainFileName;
|
||||||
|
private static String checkpointsFileName;
|
||||||
|
|
||||||
|
private static NetworkParameters params;
|
||||||
|
private static PeerGroup peerGroup;
|
||||||
|
private static BlockStore blockStore;
|
||||||
|
|
||||||
|
private static class RollbackBlockChain extends BlockChain {
|
||||||
public RollbackBlockChain(NetworkParameters params, BlockStore blockStore) throws BlockStoreException {
|
public RollbackBlockChain(NetworkParameters params, BlockStore blockStore) throws BlockStoreException {
|
||||||
super(params, blockStore);
|
super(params, blockStore);
|
||||||
}
|
}
|
||||||
@ -57,11 +78,10 @@ public class BTC {
|
|||||||
public void setChainHead(StoredBlock chainHead) throws BlockStoreException {
|
public void setChainHead(StoredBlock chainHead) throws BlockStoreException {
|
||||||
super.setChainHead(chainHead);
|
super.setChainHead(chainHead);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
private static RollbackBlockChain chain;
|
||||||
|
|
||||||
private static class UpdateableCheckpointManager extends CheckpointManager implements NewBestBlockListener {
|
private static class UpdateableCheckpointManager extends CheckpointManager implements NewBestBlockListener {
|
||||||
|
|
||||||
private static final int checkpointInterval = 500;
|
private static final int checkpointInterval = 500;
|
||||||
|
|
||||||
private static final String minimalTestNet3TextFile = "TXT CHECKPOINTS 1\n0\n1\nAAAAAAAAB+EH4QfhAAAH4AEAAAApmwX6UCEnJcYIKTa7HO3pFkqqNhAzJVBMdEuGAAAAAPSAvVCBUypCbBW/OqU0oIF7ISF84h2spOqHrFCWN9Zw6r6/T///AB0E5oOO\n";
|
private static final String minimalTestNet3TextFile = "TXT CHECKPOINTS 1\n0\n1\nAAAAAAAAB+EH4QfhAAAH4AEAAAApmwX6UCEnJcYIKTa7HO3pFkqqNhAzJVBMdEuGAAAAAPSAvVCBUypCbBW/OqU0oIF7ISF84h2spOqHrFCWN9Zw6r6/T///AB0E5oOO\n";
|
||||||
@ -130,23 +150,24 @@ public class BTC {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BTC instance;
|
|
||||||
private static final Object instanceLock = new Object();
|
|
||||||
|
|
||||||
private static File directory;
|
|
||||||
private static String chainFileName;
|
|
||||||
private static String checkpointsFileName;
|
|
||||||
|
|
||||||
private static NetworkParameters params;
|
|
||||||
private static PeerGroup peerGroup;
|
|
||||||
private static BlockStore blockStore;
|
|
||||||
private static RollbackBlockChain chain;
|
|
||||||
private static UpdateableCheckpointManager manager;
|
private static UpdateableCheckpointManager manager;
|
||||||
|
|
||||||
private BTC() {
|
private BTC() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized BTC getInstance() {
|
||||||
|
if (instance == null)
|
||||||
|
instance = new BTC();
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] hash160(byte[] message) {
|
||||||
|
return RIPE_MD160_DIGESTER.digest(SHA256_DIGESTER.digest(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
// Start wallet
|
// Start wallet
|
||||||
if (Settings.getInstance().useBitcoinTestNet()) {
|
if (Settings.getInstance().useBitcoinTestNet()) {
|
||||||
params = TestNet3Params.get();
|
params = TestNet3Params.get();
|
||||||
@ -196,20 +217,11 @@ public class BTC {
|
|||||||
peerGroup.start();
|
peerGroup.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized BTC getInstance() {
|
public synchronized void shutdown() {
|
||||||
if (instance == null)
|
if (instance == null)
|
||||||
instance = new BTC();
|
return;
|
||||||
|
|
||||||
return instance;
|
instance = null;
|
||||||
}
|
|
||||||
|
|
||||||
public void shutdown() {
|
|
||||||
synchronized (instanceLock) {
|
|
||||||
if (instance == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
instance = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
peerGroup.stop();
|
peerGroup.stop();
|
||||||
|
|
||||||
|
@ -21,4 +21,9 @@ public class BitTwiddling {
|
|||||||
return maxValue;
|
return maxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Convert int to little-endian byte array */
|
||||||
|
public static byte[] toLEByteArray(int value) {
|
||||||
|
return new byte[] { (byte) (value), (byte) (value >> 8), (byte) (value >> 16), (byte) (value >> 24) };
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
107
src/test/java/org/qora/test/btcacct/Initiate1.java
Normal file
107
src/test/java/org/qora/test/btcacct/Initiate1.java
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
package org.qora.test.btcacct;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.Security;
|
||||||
|
|
||||||
|
import org.bitcoinj.core.Address;
|
||||||
|
import org.bitcoinj.core.ECKey;
|
||||||
|
import org.bitcoinj.core.LegacyAddress;
|
||||||
|
import org.bitcoinj.core.NetworkParameters;
|
||||||
|
import org.bitcoinj.params.TestNet3Params;
|
||||||
|
import org.bitcoinj.script.Script.ScriptType;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.qora.account.PublicKeyAccount;
|
||||||
|
import org.qora.crosschain.BTC;
|
||||||
|
import org.qora.crosschain.BTCACCT;
|
||||||
|
import org.qora.crypto.Crypto;
|
||||||
|
import org.qora.utils.Base58;
|
||||||
|
|
||||||
|
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 Initiate1 {
|
||||||
|
|
||||||
|
private static final long REFUND_TIMEOUT = 600L; // seconds
|
||||||
|
|
||||||
|
private static void usage() {
|
||||||
|
System.err.println(String.format("usage: Initiate1 <your-QORT-pubkey> <your-BTC-pubkey> <QORT-amount> <BTC-amount> <their-QORT-pubkey> <their-BTC-pubkey>"));
|
||||||
|
System.err.println(String.format("example: Initiate1 6rNn9b3pYRrG9UKqzMWYZ9qa8F3Zgv2mVWrULGHUusb \\\n"
|
||||||
|
+ "\t03aa20871c2195361f2826c7a649eab6b42639630c4d8c33c55311d5c1e476b5d6 \\\n"
|
||||||
|
+ "\t123 0.00008642 \\\n"
|
||||||
|
+ "\tJBNBQQDzZsm5do1BrwWAp53Ps4KYJVt749EGpCf7ofte \\\n"
|
||||||
|
+ "\t032783606be32a3e639a33afe2b15f058708ab124f3b290d595ee954390a0c8559"));
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
if (args.length != 6)
|
||||||
|
usage();
|
||||||
|
|
||||||
|
Security.insertProviderAt(new BouncyCastleProvider(), 0);
|
||||||
|
NetworkParameters params = TestNet3Params.get();
|
||||||
|
|
||||||
|
String yourQortPubKey58 = args[0];
|
||||||
|
String yourBitcoinPubKeyHex = args[1];
|
||||||
|
|
||||||
|
String theirBitcoinPubKeyHex = args[5];
|
||||||
|
|
||||||
|
try {
|
||||||
|
System.out.println("Confirm the following is correct based on the info you've given:");
|
||||||
|
|
||||||
|
byte[] yourQortPubKey = Base58.decode(yourQortPubKey58);
|
||||||
|
PublicKeyAccount yourQortalAccount = new PublicKeyAccount(null, yourQortPubKey);
|
||||||
|
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()));
|
||||||
|
|
||||||
|
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()));
|
||||||
|
|
||||||
|
// New/derived info
|
||||||
|
|
||||||
|
byte[] secret = new byte[32];
|
||||||
|
new SecureRandom().nextBytes(secret);
|
||||||
|
System.out.println("\nSecret info (DO NOT share with other party):");
|
||||||
|
System.out.println("Secret: " + HashCode.fromBytes(secret).toString());
|
||||||
|
|
||||||
|
System.out.println("\nGive this info to other party:");
|
||||||
|
|
||||||
|
byte[] secretHash = Crypto.digest(secret);
|
||||||
|
System.out.println("Hash of secret: " + HashCode.fromBytes(secretHash).toString());
|
||||||
|
|
||||||
|
long lockTime = System.currentTimeMillis() + REFUND_TIMEOUT;
|
||||||
|
byte[] redeemScriptBytes = BTCACCT.buildRedeemScript(secretHash, yourBitcoinPubKey, 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("P2SH address: " + p2shAddress.toString());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -70,11 +70,11 @@ public class BTCACCTTests {
|
|||||||
// For when we want to re-run
|
// For when we want to re-run
|
||||||
private static final byte[] prevSecret = HashCode.fromString("30a13291e350214bea5318f990b77bc11d2cb709f7c39859f248bef396961dcc").asBytes();
|
private static final byte[] prevSecret = HashCode.fromString("30a13291e350214bea5318f990b77bc11d2cb709f7c39859f248bef396961dcc").asBytes();
|
||||||
private static final long prevLockTime = 1539347892L;
|
private static final long prevLockTime = 1539347892L;
|
||||||
private static final boolean usePreviousFundingTx = true;
|
private static final boolean usePreviousFundingTx = false;
|
||||||
|
|
||||||
private static final boolean doRefundNotRedeem = false;
|
private static final boolean doRefundNotRedeem = false;
|
||||||
|
|
||||||
public void main(String[] args) throws NoSuchAlgorithmException, InsufficientMoneyException, InterruptedException, ExecutionException, UnknownHostException {
|
public static void main(String[] args) throws NoSuchAlgorithmException, InsufficientMoneyException, InterruptedException, ExecutionException, UnknownHostException {
|
||||||
Security.insertProviderAt(new BouncyCastleProvider(), 0);
|
Security.insertProviderAt(new BouncyCastleProvider(), 0);
|
||||||
|
|
||||||
byte[] secret = new byte[32];
|
byte[] secret = new byte[32];
|
||||||
@ -173,7 +173,7 @@ public class BTCACCTTests {
|
|||||||
private static final byte[] redeemScript4 = HashCode.fromString("b17576a914").asBytes();
|
private static final byte[] redeemScript4 = HashCode.fromString("b17576a914").asBytes();
|
||||||
private static final byte[] redeemScript5 = HashCode.fromString("88ac68").asBytes();
|
private static final byte[] redeemScript5 = HashCode.fromString("88ac68").asBytes();
|
||||||
|
|
||||||
private byte[] buildRedeemScript(byte[] secret, byte[] senderPubKey, byte[] recipientPubKey, long lockTime) {
|
private static byte[] buildRedeemScript(byte[] secret, byte[] senderPubKey, byte[] recipientPubKey, long lockTime) {
|
||||||
try {
|
try {
|
||||||
MessageDigest sha256Digester = MessageDigest.getInstance("SHA-256");
|
MessageDigest sha256Digester = MessageDigest.getInstance("SHA-256");
|
||||||
|
|
||||||
@ -188,7 +188,7 @@ public class BTCACCTTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] hash160(byte[] input) {
|
private static byte[] hash160(byte[] input) {
|
||||||
try {
|
try {
|
||||||
MessageDigest rmd160Digester = MessageDigest.getInstance("RIPEMD160");
|
MessageDigest rmd160Digester = MessageDigest.getInstance("RIPEMD160");
|
||||||
MessageDigest sha256Digester = MessageDigest.getInstance("SHA-256");
|
MessageDigest sha256Digester = MessageDigest.getInstance("SHA-256");
|
||||||
@ -199,7 +199,7 @@ public class BTCACCTTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Transaction buildFundingTransaction(NetworkParameters params, Sha256Hash prevTxHash, long outputIndex, Coin balance, ECKey sigKey, Coin value,
|
private static Transaction buildFundingTransaction(NetworkParameters params, Sha256Hash prevTxHash, long outputIndex, Coin balance, ECKey sigKey, Coin value,
|
||||||
byte[] redeemScriptHash) {
|
byte[] redeemScriptHash) {
|
||||||
Transaction fundingTransaction = new Transaction(params);
|
Transaction fundingTransaction = new Transaction(params);
|
||||||
|
|
||||||
@ -218,7 +218,7 @@ public class BTCACCTTests {
|
|||||||
return fundingTransaction;
|
return fundingTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Transaction buildRedeemTransaction(NetworkParameters params, TransactionOutPoint fundingOutPoint, ECKey recipientKey, Coin value, byte[] secret,
|
private static Transaction buildRedeemTransaction(NetworkParameters params, TransactionOutPoint fundingOutPoint, ECKey recipientKey, Coin value, byte[] secret,
|
||||||
byte[] redeemScriptBytes) {
|
byte[] redeemScriptBytes) {
|
||||||
Transaction redeemTransaction = new Transaction(params);
|
Transaction redeemTransaction = new Transaction(params);
|
||||||
redeemTransaction.setVersion(2);
|
redeemTransaction.setVersion(2);
|
||||||
@ -255,7 +255,7 @@ public class BTCACCTTests {
|
|||||||
return redeemTransaction;
|
return redeemTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Transaction buildRefundTransaction(NetworkParameters params, TransactionOutPoint fundingOutPoint, ECKey senderKey, Coin value,
|
private static Transaction buildRefundTransaction(NetworkParameters params, TransactionOutPoint fundingOutPoint, ECKey senderKey, Coin value,
|
||||||
byte[] redeemScriptBytes, long lockTime) {
|
byte[] redeemScriptBytes, long lockTime) {
|
||||||
Transaction refundTransaction = new Transaction(params);
|
Transaction refundTransaction = new Transaction(params);
|
||||||
refundTransaction.setVersion(2);
|
refundTransaction.setVersion(2);
|
||||||
@ -294,7 +294,7 @@ public class BTCACCTTests {
|
|||||||
return refundTransaction;
|
return refundTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void broadcastWithConfirmation(WalletAppKit kit, Transaction transaction) {
|
private static void broadcastWithConfirmation(WalletAppKit kit, Transaction transaction) {
|
||||||
System.out.println("Broadcasting tx: " + transaction.getTxId().toString());
|
System.out.println("Broadcasting tx: " + transaction.getTxId().toString());
|
||||||
System.out.println("TX hex: " + HashCode.fromBytes(transaction.bitcoinSerialize()).toString());
|
System.out.println("TX hex: " + HashCode.fromBytes(transaction.bitcoinSerialize()).toString());
|
||||||
|
|
||||||
@ -320,7 +320,7 @@ public class BTCACCTTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Convert int to little-endian byte array */
|
/** Convert int to little-endian byte array */
|
||||||
private byte[] toLEByteArray(int value) {
|
private static byte[] toLEByteArray(int value) {
|
||||||
return new byte[] { (byte) (value), (byte) (value >> 8), (byte) (value >> 16), (byte) (value >> 24) };
|
return new byte[] { (byte) (value), (byte) (value >> 8), (byte) (value >> 16), (byte) (value >> 24) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user