forked from Qortal/qortal
Compute balances for Bitcoin-like coins using unspent outputs. Should fix occasional incorrect balance issue, and speed up loading time.
This commit is contained in:
parent
64529e8abf
commit
2f7912abce
@ -68,7 +68,7 @@ public class CrossChainBitcoinResource {
|
|||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Long balance = bitcoin.getWalletBalanceFromTransactions(key58);
|
Long balance = bitcoin.getWalletBalance(key58);
|
||||||
if (balance == null)
|
if (balance == null)
|
||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ public class CrossChainDigibyteResource {
|
|||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Long balance = digibyte.getWalletBalanceFromTransactions(key58);
|
Long balance = digibyte.getWalletBalance(key58);
|
||||||
if (balance == null)
|
if (balance == null)
|
||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ public class CrossChainDogecoinResource {
|
|||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Long balance = dogecoin.getWalletBalanceFromTransactions(key58);
|
Long balance = dogecoin.getWalletBalance(key58);
|
||||||
if (balance == null)
|
if (balance == null)
|
||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ public class CrossChainLitecoinResource {
|
|||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Long balance = litecoin.getWalletBalanceFromTransactions(key58);
|
Long balance = litecoin.getWalletBalance(key58);
|
||||||
if (balance == null)
|
if (balance == null)
|
||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ public class CrossChainRavencoinResource {
|
|||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Long balance = ravencoin.getWalletBalanceFromTransactions(key58);
|
Long balance = ravencoin.getWalletBalance(key58);
|
||||||
if (balance == null)
|
if (balance == null)
|
||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||||
|
|
||||||
|
@ -357,19 +357,33 @@ public abstract class Bitcoiny implements ForeignBlockchain {
|
|||||||
* @return unspent BTC balance, or null if unable to determine balance
|
* @return unspent BTC balance, or null if unable to determine balance
|
||||||
*/
|
*/
|
||||||
public Long getWalletBalance(String key58) throws ForeignBlockchainException {
|
public Long getWalletBalance(String key58) throws ForeignBlockchainException {
|
||||||
// It's more accurate to calculate the balance from the transactions, rather than asking Bitcoinj
|
Long balance = 0L;
|
||||||
return this.getWalletBalanceFromTransactions(key58);
|
|
||||||
|
|
||||||
// Context.propagate(bitcoinjContext);
|
List<TransactionOutput> allUnspentOutputs = new ArrayList<>();
|
||||||
//
|
Set<String> walletAddresses = this.getWalletAddresses(key58);
|
||||||
// Wallet wallet = walletFromDeterministicKey58(key58);
|
for (String address : walletAddresses) {
|
||||||
// wallet.setUTXOProvider(new WalletAwareUTXOProvider(this, wallet));
|
allUnspentOutputs.addAll(this.getUnspentOutputs(address));
|
||||||
//
|
}
|
||||||
// Coin balance = wallet.getBalance();
|
for (TransactionOutput output : allUnspentOutputs) {
|
||||||
// if (balance == null)
|
if (!output.isAvailableForSpending()) {
|
||||||
// return null;
|
continue;
|
||||||
//
|
}
|
||||||
// return balance.value;
|
balance += output.getValue().value;
|
||||||
|
}
|
||||||
|
return balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getWalletBalanceFromBitcoinj(String key58) {
|
||||||
|
Context.propagate(bitcoinjContext);
|
||||||
|
|
||||||
|
Wallet wallet = walletFromDeterministicKey58(key58);
|
||||||
|
wallet.setUTXOProvider(new WalletAwareUTXOProvider(this, wallet));
|
||||||
|
|
||||||
|
Coin balance = wallet.getBalance();
|
||||||
|
if (balance == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return balance.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getWalletBalanceFromTransactions(String key58) throws ForeignBlockchainException {
|
public Long getWalletBalanceFromTransactions(String key58) throws ForeignBlockchainException {
|
||||||
@ -464,6 +478,64 @@ public abstract class Bitcoiny implements ForeignBlockchain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<String> getWalletAddresses(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());
|
||||||
|
|
||||||
|
Set<String> keySet = new HashSet<>();
|
||||||
|
|
||||||
|
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);
|
||||||
|
keySet.add(address.toString());
|
||||||
|
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 keySet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected SimpleTransaction convertToSimpleTransaction(BitcoinyTransaction t, Set<String> keySet) {
|
protected SimpleTransaction convertToSimpleTransaction(BitcoinyTransaction t, Set<String> keySet) {
|
||||||
long amount = 0;
|
long amount = 0;
|
||||||
long total = 0L;
|
long total = 0L;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user