forked from Qortal/qortal
Merge pull request #202 from kennycud/master
Foreign Wallet Balance Enhancements
This commit is contained in:
commit
cbb171f859
@ -55,6 +55,13 @@ public abstract class Bitcoiny implements ForeignBlockchain {
|
|||||||
|
|
||||||
protected Coin feePerKb;
|
protected Coin feePerKb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blockchain Cache
|
||||||
|
*
|
||||||
|
* To store blockchain data and reduce redundant RPCs to the ElectrumX servers
|
||||||
|
*/
|
||||||
|
private final BlockchainCache blockchainCache = new BlockchainCache();
|
||||||
|
|
||||||
// Constructors and instance
|
// Constructors and instance
|
||||||
|
|
||||||
protected Bitcoiny(BitcoinyBlockchainProvider blockchainProvider, Context bitcoinjContext, String currencyCode, Coin feePerKb) {
|
protected Bitcoiny(BitcoinyBlockchainProvider blockchainProvider, Context bitcoinjContext, String currencyCode, Coin feePerKb) {
|
||||||
@ -509,8 +516,22 @@ public abstract class Bitcoiny implements ForeignBlockchain {
|
|||||||
if (!historicTransactionHashes.isEmpty()) {
|
if (!historicTransactionHashes.isEmpty()) {
|
||||||
areAllKeysUnused = false;
|
areAllKeysUnused = false;
|
||||||
|
|
||||||
for (TransactionHash transactionHash : historicTransactionHashes)
|
for (TransactionHash transactionHash : historicTransactionHashes) {
|
||||||
walletTransactions.add(this.getTransaction(transactionHash.txHash));
|
|
||||||
|
Optional<BitcoinyTransaction> walletTransaction
|
||||||
|
= this.blockchainCache.getTransactionByHash( transactionHash.txHash );
|
||||||
|
|
||||||
|
// if the wallet transaction is already cached
|
||||||
|
if(walletTransaction.isPresent() ) {
|
||||||
|
walletTransactions.add( walletTransaction.get() );
|
||||||
|
}
|
||||||
|
// otherwise get the transaction from the blockchain server
|
||||||
|
else {
|
||||||
|
BitcoinyTransaction transaction = getTransaction(transactionHash.txHash);
|
||||||
|
walletTransactions.add( transaction );
|
||||||
|
this.blockchainCache.addTransactionByHash(transactionHash.txHash, transaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,17 +623,25 @@ public abstract class Bitcoiny implements ForeignBlockchain {
|
|||||||
for (; ki < keys.size(); ++ki) {
|
for (; ki < keys.size(); ++ki) {
|
||||||
DeterministicKey dKey = keys.get(ki);
|
DeterministicKey dKey = keys.get(ki);
|
||||||
|
|
||||||
// Check for transactions
|
|
||||||
Address address = Address.fromKey(this.params, dKey, ScriptType.P2PKH);
|
Address address = Address.fromKey(this.params, dKey, ScriptType.P2PKH);
|
||||||
keySet.add(address.toString());
|
keySet.add(address.toString());
|
||||||
byte[] script = ScriptBuilder.createOutputScript(address).getProgram();
|
|
||||||
|
|
||||||
// Ask for transaction history - if it's empty then key has never been used
|
// if the key already has a verified transaction history
|
||||||
List<TransactionHash> historicTransactionHashes = this.getAddressTransactions(script, true);
|
if( this.blockchainCache.keyHasHistory( dKey ) ){
|
||||||
|
|
||||||
if (!historicTransactionHashes.isEmpty()) {
|
|
||||||
areAllKeysUnused = false;
|
areAllKeysUnused = false;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// Check for transactions
|
||||||
|
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, true);
|
||||||
|
|
||||||
|
if (!historicTransactionHashes.isEmpty()) {
|
||||||
|
areAllKeysUnused = false;
|
||||||
|
this.blockchainCache.addKeyWithHistory(dKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (areAllKeysUnused) {
|
if (areAllKeysUnused) {
|
||||||
@ -667,19 +696,26 @@ public abstract class Bitcoiny implements ForeignBlockchain {
|
|||||||
do {
|
do {
|
||||||
boolean areAllKeysUnused = true;
|
boolean areAllKeysUnused = true;
|
||||||
|
|
||||||
for (; ki < keys.size(); ++ki) {
|
for (; areAllKeysUnused && ki < keys.size(); ++ki) {
|
||||||
DeterministicKey dKey = keys.get(ki);
|
DeterministicKey dKey = keys.get(ki);
|
||||||
|
|
||||||
// Check for transactions
|
// if the key already has a verified transaction history
|
||||||
Address address = Address.fromKey(this.params, dKey, ScriptType.P2PKH);
|
if( this.blockchainCache.keyHasHistory(dKey)) {
|
||||||
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;
|
areAllKeysUnused = false;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// 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, true);
|
||||||
|
|
||||||
|
if (!historicTransactionHashes.isEmpty()) {
|
||||||
|
areAllKeysUnused = false;
|
||||||
|
this.blockchainCache.addKeyWithHistory(dKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (areAllKeysUnused) {
|
if (areAllKeysUnused) {
|
||||||
|
89
src/main/java/org/qortal/crosschain/BlockchainCache.java
Normal file
89
src/main/java/org/qortal/crosschain/BlockchainCache.java
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package org.qortal.crosschain;
|
||||||
|
|
||||||
|
import org.bitcoinj.crypto.DeterministicKey;
|
||||||
|
import org.qortal.settings.Settings;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class BlockchainCache
|
||||||
|
*
|
||||||
|
* Cache blockchain information to reduce redundant RPCs to the ElectrumX servers.
|
||||||
|
*/
|
||||||
|
public class BlockchainCache {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keys With History
|
||||||
|
*
|
||||||
|
* Deterministic Keys with any transaction history.
|
||||||
|
*/
|
||||||
|
private Queue<DeterministicKey> keysWithHistory = new ConcurrentLinkedDeque<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transactions By Hash
|
||||||
|
*
|
||||||
|
* Transaction Hash -> Transaction
|
||||||
|
*/
|
||||||
|
private ConcurrentHashMap<String, BitcoinyTransaction> transactionByHash = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache Limit
|
||||||
|
*
|
||||||
|
* If this limit is reached, the cache will be cleared or reduced.
|
||||||
|
*/
|
||||||
|
private static final int CACHE_LIMIT = Settings.getInstance().getBlockchainCacheLimit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Key With History
|
||||||
|
*
|
||||||
|
* @param key a deterministic key with a verified history
|
||||||
|
*/
|
||||||
|
public void addKeyWithHistory(DeterministicKey key) {
|
||||||
|
|
||||||
|
if( this.keysWithHistory.size() > CACHE_LIMIT ) {
|
||||||
|
this.keysWithHistory.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.keysWithHistory.add(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key Has History?
|
||||||
|
*
|
||||||
|
* @param key the deterministic key
|
||||||
|
*
|
||||||
|
* @return true if the key has a history, otherwise false
|
||||||
|
*/
|
||||||
|
public boolean keyHasHistory( DeterministicKey key ) {
|
||||||
|
return this.keysWithHistory.contains(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Transaction By Hash
|
||||||
|
*
|
||||||
|
* @param hash the transaction hash
|
||||||
|
* @param transaction the transaction
|
||||||
|
*/
|
||||||
|
public void addTransactionByHash( String hash, BitcoinyTransaction transaction ) {
|
||||||
|
|
||||||
|
if( this.transactionByHash.size() > CACHE_LIMIT ) {
|
||||||
|
this.transactionByHash.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.transactionByHash.put(hash, transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Transaction By Hash
|
||||||
|
*
|
||||||
|
* @param hash the transaction hash
|
||||||
|
*
|
||||||
|
* @return the transaction, empty if the hash is not in the cache
|
||||||
|
*/
|
||||||
|
public Optional<BitcoinyTransaction> getTransactionByHash( String hash ) {
|
||||||
|
return Optional.ofNullable( this.transactionByHash.get(hash) );
|
||||||
|
}
|
||||||
|
}
|
@ -323,11 +323,14 @@ public class Settings {
|
|||||||
/* Foreign chains */
|
/* Foreign chains */
|
||||||
|
|
||||||
/** The number of consecutive empty addresses required before treating a wallet's transaction set as complete */
|
/** The number of consecutive empty addresses required before treating a wallet's transaction set as complete */
|
||||||
private int gapLimit = 24;
|
private int gapLimit = 3;
|
||||||
|
|
||||||
/** How many wallet keys to generate when using bitcoinj as the blockchain interface (e.g. when sending coins) */
|
/** How many wallet keys to generate when using bitcoinj as the blockchain interface (e.g. when sending coins) */
|
||||||
private int bitcoinjLookaheadSize = 50;
|
private int bitcoinjLookaheadSize = 50;
|
||||||
|
|
||||||
|
/** How many units of data to be kept in a blockchain cache before the cache should be reduced or cleared. */
|
||||||
|
private int blockchainCacheLimit = 1000;
|
||||||
|
|
||||||
// Data storage (QDN)
|
// Data storage (QDN)
|
||||||
|
|
||||||
/** Data storage enabled/disabled*/
|
/** Data storage enabled/disabled*/
|
||||||
@ -1049,6 +1052,9 @@ public class Settings {
|
|||||||
return bitcoinjLookaheadSize;
|
return bitcoinjLookaheadSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getBlockchainCacheLimit() {
|
||||||
|
return blockchainCacheLimit;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isQdnEnabled() {
|
public boolean isQdnEnabled() {
|
||||||
return this.qdnEnabled;
|
return this.qdnEnabled;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user