forked from Qortal/qortal
commit
61d6bd9bca
@ -0,0 +1,17 @@
|
||||
package org.qortal.api.model.crosschain;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class AddressRequest {
|
||||
|
||||
@Schema(description = "Litecoin BIP32 extended public key", example = "tpub___________________________________________________________________________________________________________")
|
||||
public String xpub58;
|
||||
|
||||
public AddressRequest() {
|
||||
}
|
||||
|
||||
}
|
@ -13,7 +13,9 @@ import org.qortal.api.ApiError;
|
||||
import org.qortal.api.ApiErrors;
|
||||
import org.qortal.api.ApiExceptionFactory;
|
||||
import org.qortal.api.Security;
|
||||
import org.qortal.api.model.crosschain.AddressRequest;
|
||||
import org.qortal.api.model.crosschain.BitcoinSendRequest;
|
||||
import org.qortal.crosschain.AddressInfo;
|
||||
import org.qortal.crosschain.Bitcoin;
|
||||
import org.qortal.crosschain.ForeignBlockchainException;
|
||||
import org.qortal.crosschain.SimpleTransaction;
|
||||
@ -148,6 +150,44 @@ public class CrossChainBitcoinResource {
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/addressinfos")
|
||||
@Operation(
|
||||
summary = "Returns information for each address for a hierarchical, deterministic BIP32 wallet",
|
||||
description = "Supply BIP32 'm' private/public key in base58, starting with 'xprv'/'xpub' for mainnet, 'tprv'/'tpub' for testnet",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(
|
||||
implementation = AddressRequest.class
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(array = @ArraySchema( schema = @Schema( implementation = AddressInfo.class ) ) )
|
||||
)
|
||||
}
|
||||
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
|
||||
@SecurityRequirement(name = "apiKey")
|
||||
public List<AddressInfo> getBitcoinAddressInfos(@HeaderParam(Security.API_KEY_HEADER) String apiKey, AddressRequest addressRequest) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
Bitcoin bitcoin = Bitcoin.getInstance();
|
||||
|
||||
if (!bitcoin.isValidDeterministicKey(addressRequest.xpub58))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
||||
|
||||
try {
|
||||
return bitcoin.getWalletAddressInfos(addressRequest.xpub58);
|
||||
} catch (ForeignBlockchainException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/unusedaddress")
|
||||
@Operation(
|
||||
|
@ -13,7 +13,9 @@ import org.qortal.api.ApiError;
|
||||
import org.qortal.api.ApiErrors;
|
||||
import org.qortal.api.ApiExceptionFactory;
|
||||
import org.qortal.api.Security;
|
||||
import org.qortal.api.model.crosschain.AddressRequest;
|
||||
import org.qortal.api.model.crosschain.DigibyteSendRequest;
|
||||
import org.qortal.crosschain.AddressInfo;
|
||||
import org.qortal.crosschain.Digibyte;
|
||||
import org.qortal.crosschain.ForeignBlockchainException;
|
||||
import org.qortal.crosschain.SimpleTransaction;
|
||||
@ -148,6 +150,44 @@ public class CrossChainDigibyteResource {
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/addressinfos")
|
||||
@Operation(
|
||||
summary = "Returns information for each address for a hierarchical, deterministic BIP32 wallet",
|
||||
description = "Supply BIP32 'm' private/public key in base58, starting with 'xprv'/'xpub' for mainnet, 'tprv'/'tpub' for testnet",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(
|
||||
implementation = AddressRequest.class
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(array = @ArraySchema( schema = @Schema( implementation = AddressInfo.class ) ) )
|
||||
)
|
||||
}
|
||||
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
|
||||
@SecurityRequirement(name = "apiKey")
|
||||
public List<AddressInfo> getDigibyteAddressInfos(@HeaderParam(Security.API_KEY_HEADER) String apiKey, AddressRequest addressRequest) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
Digibyte digibyte = Digibyte.getInstance();
|
||||
|
||||
if (!digibyte.isValidDeterministicKey(addressRequest.xpub58))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
||||
|
||||
try {
|
||||
return digibyte.getWalletAddressInfos(addressRequest.xpub58);
|
||||
} catch (ForeignBlockchainException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/unusedaddress")
|
||||
@Operation(
|
||||
|
@ -13,7 +13,9 @@ import org.qortal.api.ApiError;
|
||||
import org.qortal.api.ApiErrors;
|
||||
import org.qortal.api.ApiExceptionFactory;
|
||||
import org.qortal.api.Security;
|
||||
import org.qortal.api.model.crosschain.AddressRequest;
|
||||
import org.qortal.api.model.crosschain.DogecoinSendRequest;
|
||||
import org.qortal.crosschain.AddressInfo;
|
||||
import org.qortal.crosschain.Dogecoin;
|
||||
import org.qortal.crosschain.ForeignBlockchainException;
|
||||
import org.qortal.crosschain.SimpleTransaction;
|
||||
@ -148,6 +150,44 @@ public class CrossChainDogecoinResource {
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/addressinfos")
|
||||
@Operation(
|
||||
summary = "Returns information for each address for a hierarchical, deterministic BIP32 wallet",
|
||||
description = "Supply BIP32 'm' private/public key in base58, starting with 'xprv'/'xpub' for mainnet, 'tprv'/'tpub' for testnet",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(
|
||||
implementation = AddressRequest.class
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(array = @ArraySchema( schema = @Schema( implementation = AddressInfo.class ) ) )
|
||||
)
|
||||
}
|
||||
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
|
||||
@SecurityRequirement(name = "apiKey")
|
||||
public List<AddressInfo> getDogecoinAddressInfos(@HeaderParam(Security.API_KEY_HEADER) String apiKey, AddressRequest addressRequest) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
Dogecoin dogecoin = Dogecoin.getInstance();
|
||||
|
||||
if (!dogecoin.isValidDeterministicKey(addressRequest.xpub58))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
||||
|
||||
try {
|
||||
return dogecoin.getWalletAddressInfos(addressRequest.xpub58);
|
||||
} catch (ForeignBlockchainException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/unusedaddress")
|
||||
@Operation(
|
||||
|
@ -13,7 +13,9 @@ import org.qortal.api.ApiError;
|
||||
import org.qortal.api.ApiErrors;
|
||||
import org.qortal.api.ApiExceptionFactory;
|
||||
import org.qortal.api.Security;
|
||||
import org.qortal.api.model.crosschain.AddressRequest;
|
||||
import org.qortal.api.model.crosschain.LitecoinSendRequest;
|
||||
import org.qortal.crosschain.AddressInfo;
|
||||
import org.qortal.crosschain.ForeignBlockchainException;
|
||||
import org.qortal.crosschain.Litecoin;
|
||||
import org.qortal.crosschain.SimpleTransaction;
|
||||
@ -148,6 +150,44 @@ public class CrossChainLitecoinResource {
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/addressinfos")
|
||||
@Operation(
|
||||
summary = "Returns information for each address for a hierarchical, deterministic BIP32 wallet",
|
||||
description = "Supply BIP32 'm' private/public key in base58, starting with 'xprv'/'xpub' for mainnet, 'tprv'/'tpub' for testnet",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(
|
||||
implementation = AddressRequest.class
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(array = @ArraySchema( schema = @Schema( implementation = AddressInfo.class ) ) )
|
||||
)
|
||||
}
|
||||
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
|
||||
@SecurityRequirement(name = "apiKey")
|
||||
public List<AddressInfo> getLitecoinAddressInfos(@HeaderParam(Security.API_KEY_HEADER) String apiKey, AddressRequest addressRequest) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
Litecoin litecoin = Litecoin.getInstance();
|
||||
|
||||
if (!litecoin.isValidDeterministicKey(addressRequest.xpub58))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
||||
|
||||
try {
|
||||
return litecoin.getWalletAddressInfos(addressRequest.xpub58);
|
||||
} catch (ForeignBlockchainException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/unusedaddress")
|
||||
@Operation(
|
||||
|
@ -13,7 +13,9 @@ import org.qortal.api.ApiError;
|
||||
import org.qortal.api.ApiErrors;
|
||||
import org.qortal.api.ApiExceptionFactory;
|
||||
import org.qortal.api.Security;
|
||||
import org.qortal.api.model.crosschain.AddressRequest;
|
||||
import org.qortal.api.model.crosschain.RavencoinSendRequest;
|
||||
import org.qortal.crosschain.AddressInfo;
|
||||
import org.qortal.crosschain.ForeignBlockchainException;
|
||||
import org.qortal.crosschain.Ravencoin;
|
||||
import org.qortal.crosschain.SimpleTransaction;
|
||||
@ -148,6 +150,44 @@ public class CrossChainRavencoinResource {
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/addressinfos")
|
||||
@Operation(
|
||||
summary = "Returns information for each address for a hierarchical, deterministic BIP32 wallet",
|
||||
description = "Supply BIP32 'm' private/public key in base58, starting with 'xprv'/'xpub' for mainnet, 'tprv'/'tpub' for testnet",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(
|
||||
implementation = AddressRequest.class
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(array = @ArraySchema( schema = @Schema( implementation = AddressInfo.class ) ) )
|
||||
)
|
||||
}
|
||||
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
|
||||
@SecurityRequirement(name = "apiKey")
|
||||
public List<AddressInfo> getRavencoinAddressInfos(@HeaderParam(Security.API_KEY_HEADER) String apiKey, AddressRequest addressRequest) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
Ravencoin ravencoin = Ravencoin.getInstance();
|
||||
|
||||
if (!ravencoin.isValidDeterministicKey(addressRequest.xpub58))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
||||
|
||||
try {
|
||||
return ravencoin.getWalletAddressInfos(addressRequest.xpub58);
|
||||
} catch (ForeignBlockchainException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/unusedaddress")
|
||||
@Operation(
|
||||
|
78
src/main/java/org/qortal/crosschain/AddressInfo.java
Normal file
78
src/main/java/org/qortal/crosschain/AddressInfo.java
Normal file
@ -0,0 +1,78 @@
|
||||
package org.qortal.crosschain;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Class AddressInfo
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class AddressInfo {
|
||||
|
||||
private String address;
|
||||
|
||||
private List<Integer> path;
|
||||
|
||||
private long value;
|
||||
|
||||
private String pathAsString;
|
||||
|
||||
private int transactionCount;
|
||||
|
||||
public AddressInfo() {
|
||||
}
|
||||
|
||||
public AddressInfo(String address, List<Integer> path, long value, String pathAsString, int transactionCount) {
|
||||
this.address = address;
|
||||
this.path = path;
|
||||
this.value = value;
|
||||
this.pathAsString = pathAsString;
|
||||
this.transactionCount = transactionCount;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public List<Integer> getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public long getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getPathAsString() {
|
||||
return pathAsString;
|
||||
}
|
||||
|
||||
public int getTransactionCount() {
|
||||
return transactionCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
AddressInfo that = (AddressInfo) o;
|
||||
return value == that.value && transactionCount == that.transactionCount && Objects.equals(address, that.address) && Objects.equals(path, that.path) && Objects.equals(pathAsString, that.pathAsString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(address, path, value, pathAsString, transactionCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AddressInfo{" +
|
||||
"address='" + address + '\'' +
|
||||
", path=" + path +
|
||||
", value=" + value +
|
||||
", pathAsString='" + pathAsString + '\'' +
|
||||
", transactionCount=" + transactionCount +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package org.qortal.crosschain;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.hash.HashCode;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@ -474,6 +475,37 @@ public abstract class Bitcoiny implements ForeignBlockchain {
|
||||
}
|
||||
}
|
||||
|
||||
public List<AddressInfo> getWalletAddressInfos(String key58) throws ForeignBlockchainException {
|
||||
List<AddressInfo> infos = new ArrayList<>();
|
||||
|
||||
for(DeterministicKey key : getWalletKeys(key58)) {
|
||||
infos.add(buildAddressInfo(key));
|
||||
}
|
||||
|
||||
return infos.stream()
|
||||
.sorted(new PathComparator(1))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public AddressInfo buildAddressInfo(DeterministicKey key) throws ForeignBlockchainException {
|
||||
|
||||
Address address = Address.fromKey(this.params, key, ScriptType.P2PKH);
|
||||
|
||||
int transactionCount = getAddressTransactions(ScriptBuilder.createOutputScript(address).getProgram(), true).size();
|
||||
|
||||
return new AddressInfo(
|
||||
address.toString(),
|
||||
toIntegerList( key.getPath()),
|
||||
summingUnspentOutputs(address.toString()),
|
||||
key.getPathAsString(),
|
||||
transactionCount);
|
||||
}
|
||||
|
||||
private static List<Integer> toIntegerList(ImmutableList<ChildNumber> path) {
|
||||
|
||||
return path.stream().map(ChildNumber::num).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Set<String> getWalletAddresses(String key58) throws ForeignBlockchainException {
|
||||
synchronized (this) {
|
||||
Context.propagate(bitcoinjContext);
|
||||
@ -532,6 +564,61 @@ public abstract class Bitcoiny implements ForeignBlockchain {
|
||||
}
|
||||
}
|
||||
|
||||
private List<DeterministicKey> getWalletKeys(String key58) throws ForeignBlockchainException {
|
||||
synchronized (this) {
|
||||
Context.propagate(bitcoinjContext);
|
||||
|
||||
Wallet wallet = walletFromDeterministicKey58(key58);
|
||||
DeterministicKeyChain keyChain = wallet.getActiveKeyChain();
|
||||
|
||||
keyChain.setLookaheadSize(Bitcoiny.WALLET_KEY_LOOKAHEAD_INCREMENT);
|
||||
keyChain.maybeLookAhead();
|
||||
|
||||
List<DeterministicKey> keys = new ArrayList<>(keyChain.getLeafKeys());
|
||||
|
||||
int unusedCounter = 0;
|
||||
int ki = 0;
|
||||
do {
|
||||
boolean areAllKeysUnused = true;
|
||||
|
||||
for (; ki < keys.size(); ++ki) {
|
||||
DeterministicKey dKey = keys.get(ki);
|
||||
|
||||
// Check for transactions
|
||||
Address address = Address.fromKey(this.params, dKey, ScriptType.P2PKH);
|
||||
byte[] script = ScriptBuilder.createOutputScript(address).getProgram();
|
||||
|
||||
// Ask for transaction history - if it's empty then key has never been used
|
||||
List<TransactionHash> historicTransactionHashes = this.getAddressTransactions(script, false);
|
||||
|
||||
if (!historicTransactionHashes.isEmpty()) {
|
||||
areAllKeysUnused = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (areAllKeysUnused) {
|
||||
// No transactions
|
||||
if (unusedCounter >= Settings.getInstance().getGapLimit()) {
|
||||
// ... and we've hit our search limit
|
||||
break;
|
||||
}
|
||||
// We haven't hit our search limit yet so increment the counter and keep looking
|
||||
unusedCounter += WALLET_KEY_LOOKAHEAD_INCREMENT;
|
||||
} else {
|
||||
// Some keys in this batch were used, so reset the counter
|
||||
unusedCounter = 0;
|
||||
}
|
||||
|
||||
// Generate some more keys
|
||||
keys.addAll(generateMoreKeys(keyChain));
|
||||
|
||||
// Process new keys
|
||||
} while (true);
|
||||
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
protected SimpleTransaction convertToSimpleTransaction(BitcoinyTransaction t, Set<String> keySet) {
|
||||
long amount = 0;
|
||||
long total = 0L;
|
||||
@ -804,6 +891,13 @@ public abstract class Bitcoiny implements ForeignBlockchain {
|
||||
}
|
||||
}
|
||||
|
||||
private Long summingUnspentOutputs(String walletAddress) throws ForeignBlockchainException {
|
||||
return this.getUnspentOutputs(walletAddress).stream()
|
||||
.map(TransactionOutput::getValue)
|
||||
.mapToLong(Coin::longValue)
|
||||
.sum();
|
||||
}
|
||||
|
||||
// Utility methods for others
|
||||
|
||||
public static List<SimpleForeignTransaction> simplifyWalletTransactions(List<BitcoinyTransaction> transactions) {
|
||||
|
29
src/main/java/org/qortal/crosschain/PathComparator.java
Normal file
29
src/main/java/org/qortal/crosschain/PathComparator.java
Normal file
@ -0,0 +1,29 @@
|
||||
package org.qortal.crosschain;
|
||||
|
||||
/**
|
||||
* Class PathComparator
|
||||
*/
|
||||
public class PathComparator implements java.util.Comparator<AddressInfo> {
|
||||
|
||||
private int max;
|
||||
|
||||
public PathComparator(int max) {
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(AddressInfo info1, AddressInfo info2) {
|
||||
return compareAtLevel(info1, info2, 0);
|
||||
}
|
||||
|
||||
private int compareAtLevel(AddressInfo info1, AddressInfo info2, int level) {
|
||||
|
||||
if( level < 0 ) return 0;
|
||||
|
||||
int compareTo = info1.getPath().get(level).compareTo(info2.getPath().get(level));
|
||||
|
||||
if(compareTo != 0 || level == max) return compareTo;
|
||||
|
||||
return compareAtLevel(info1, info2,level + 1);
|
||||
}
|
||||
}
|
@ -221,7 +221,7 @@ public class Settings {
|
||||
public long recoveryModeTimeout = 9999999999999L;
|
||||
|
||||
/** Minimum peer version number required in order to sync with them */
|
||||
private String minPeerVersion = "4.3.1";
|
||||
private String minPeerVersion = "4.3.2";
|
||||
/** Whether to allow connections with peers below minPeerVersion
|
||||
* If true, we won't sync with them but they can still sync with us, and will show in the peers list
|
||||
* If false, sync will be blocked both ways, and they will not appear in the peers list */
|
||||
|
@ -4,6 +4,7 @@ import org.bitcoinj.core.Transaction;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.qortal.crosschain.AddressInfo;
|
||||
import org.qortal.crosschain.Bitcoiny;
|
||||
import org.qortal.crosschain.BitcoinyHTLC;
|
||||
import org.qortal.crosschain.ForeignBlockchainException;
|
||||
@ -11,6 +12,8 @@ import org.qortal.repository.DataException;
|
||||
import org.qortal.test.common.Common;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@ -127,4 +130,36 @@ public abstract class BitcoinyTests extends Common {
|
||||
System.out.println(address);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateRootKeyForTesting() {
|
||||
|
||||
String rootKey = BitcoinyTestsUtils.generateBip32RootKey( this.bitcoiny.getNetworkParameters() );
|
||||
|
||||
System.out.println(String.format(getCoinName() + " generated BIP32 Root Key: " + rootKey));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetWalletAddresses() throws ForeignBlockchainException {
|
||||
|
||||
String xprv58 = getDeterministicKey58();
|
||||
|
||||
Set<String> addresses = this.bitcoiny.getWalletAddresses(xprv58);
|
||||
|
||||
System.out.println( "root key = " + xprv58 );
|
||||
System.out.println( "keys ...");
|
||||
addresses.stream().forEach(System.out::println);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWalletAddressInfos() throws ForeignBlockchainException {
|
||||
|
||||
String xprv58 = getDeterministicKey58();
|
||||
|
||||
List<AddressInfo> addressInfos = this.bitcoiny.getWalletAddressInfos(xprv58);
|
||||
|
||||
System.out.println("address count = " + addressInfos.size() );
|
||||
System.out.println( "address infos ..." );
|
||||
addressInfos.forEach( System.out::println );
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
package org.qortal.test.crosschain;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.crypto.ChildNumber;
|
||||
import org.bitcoinj.crypto.DeterministicKey;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.wallet.DeterministicKeyChain;
|
||||
import org.bitcoinj.wallet.DeterministicSeed;
|
||||
import org.bitcoinj.wallet.Wallet;
|
||||
import org.qortal.crosschain.ForeignBlockchainException;
|
||||
import org.qortal.crosschain.Litecoin;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.test.common.Common;
|
||||
|
||||
public class BitcoinyTestsUtils {
|
||||
|
||||
public static void main(String[] args) throws DataException, ForeignBlockchainException {
|
||||
|
||||
Common.useDefaultSettings();
|
||||
|
||||
final String rootKey = generateBip32RootKey( Litecoin.LitecoinNet.TEST3.getParams());
|
||||
String address = Litecoin.getInstance().getUnusedReceiveAddress(rootKey);
|
||||
|
||||
System.out.println("rootKey = " + rootKey);
|
||||
System.out.println("address = " + address);
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
public static String generateBip32RootKey(NetworkParameters networkParameters) {
|
||||
|
||||
final Wallet wallet = Wallet.createDeterministic(networkParameters, Script.ScriptType.P2PKH);
|
||||
final DeterministicSeed seed = wallet.getKeyChainSeed();
|
||||
final DeterministicKeyChain keyChain = DeterministicKeyChain.builder().seed(seed).build();
|
||||
final ImmutableList<ChildNumber> path = keyChain.getAccountPath();
|
||||
final DeterministicKey parent = keyChain.getKeyByPath(path, true);
|
||||
final String rootKey = parent.serializePrivB58(networkParameters);
|
||||
|
||||
return rootKey;
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import org.qortal.crypto.Crypto;
|
||||
import org.qortal.transform.TransformationException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.qortal.crosschain.BitcoinyHTLC.Status.*;
|
||||
@ -241,4 +242,12 @@ public class PirateChainTests extends BitcoinyTests {
|
||||
@Test
|
||||
@Ignore(value = "Needs adapting for Pirate Chain")
|
||||
public void testGetUnusedReceiveAddress() {}
|
||||
|
||||
@Test
|
||||
@Ignore(value = "Needs adapting for Pirate Chain")
|
||||
public void testGetWalletAddresses() throws ForeignBlockchainException {}
|
||||
|
||||
@Test
|
||||
@Ignore(value = "Needs adapting for Pirate Chain")
|
||||
public void testWalletAddressInfos() throws ForeignBlockchainException {}
|
||||
}
|
Loading…
Reference in New Issue
Block a user