blockchain to block rename for transaction in block

This commit is contained in:
Craig Raw 2020-06-02 11:05:23 +02:00
parent a6e340eef5
commit e97f652769
2 changed files with 41 additions and 41 deletions

2
drongo

@ -1 +1 @@
Subproject commit a25d020e54543cd48be1501f4390cff4c8daeee1 Subproject commit cb18f7c4c3b2e1061565e3c78597d90aa019224c

View file

@ -61,28 +61,28 @@ public class ElectrumServer {
return serverVersion.get(1); return serverVersion.get(1);
} }
public Map<WalletNode, Set<BlockchainTransactionHash>> getHistory(Wallet wallet) throws ServerException { public Map<WalletNode, Set<BlockTransactionHash>> getHistory(Wallet wallet) throws ServerException {
Map<WalletNode, Set<BlockchainTransactionHash>> nodeTransactionMap = new HashMap<>(); Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap = new HashMap<>();
getHistory(wallet, KeyPurpose.RECEIVE, nodeTransactionMap); getHistory(wallet, KeyPurpose.RECEIVE, nodeTransactionMap);
getHistory(wallet, KeyPurpose.CHANGE, nodeTransactionMap); getHistory(wallet, KeyPurpose.CHANGE, nodeTransactionMap);
return nodeTransactionMap; return nodeTransactionMap;
} }
public void getHistory(Wallet wallet, KeyPurpose keyPurpose, Map<WalletNode, Set<BlockchainTransactionHash>> nodeTransactionMap) throws ServerException { public void getHistory(Wallet wallet, KeyPurpose keyPurpose, Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap) throws ServerException {
getHistory(wallet, wallet.getNode(keyPurpose).getChildren(), nodeTransactionMap); getHistory(wallet, wallet.getNode(keyPurpose).getChildren(), nodeTransactionMap);
getMempool(wallet, wallet.getNode(keyPurpose).getChildren(), nodeTransactionMap); getMempool(wallet, wallet.getNode(keyPurpose).getChildren(), nodeTransactionMap);
} }
public void getHistory(Wallet wallet, Collection<WalletNode> nodes, Map<WalletNode, Set<BlockchainTransactionHash>> nodeTransactionMap) throws ServerException { public void getHistory(Wallet wallet, Collection<WalletNode> nodes, Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap) throws ServerException {
getReferences(wallet, "blockchain.scripthash.get_history", nodes, nodeTransactionMap); getReferences(wallet, "blockchain.scripthash.get_history", nodes, nodeTransactionMap);
} }
public void getMempool(Wallet wallet, Collection<WalletNode> nodes, Map<WalletNode, Set<BlockchainTransactionHash>> nodeTransactionMap) throws ServerException { public void getMempool(Wallet wallet, Collection<WalletNode> nodes, Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap) throws ServerException {
getReferences(wallet, "blockchain.scripthash.get_mempool", nodes, nodeTransactionMap); getReferences(wallet, "blockchain.scripthash.get_mempool", nodes, nodeTransactionMap);
} }
public void getReferences(Wallet wallet, String method, Collection<WalletNode> nodes, Map<WalletNode, Set<BlockchainTransactionHash>> nodeTransactionMap) throws ServerException { public void getReferences(Wallet wallet, String method, Collection<WalletNode> nodes, Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap) throws ServerException {
try { try {
JsonRpcClient client = new JsonRpcClient(getTransport()); JsonRpcClient client = new JsonRpcClient(getTransport());
BatchRequestBuilder<String, ScriptHashTx[]> batchRequest = client.createBatchRequest().keysType(String.class).returnType(ScriptHashTx[].class); BatchRequestBuilder<String, ScriptHashTx[]> batchRequest = client.createBatchRequest().keysType(String.class).returnType(ScriptHashTx[].class);
@ -98,17 +98,17 @@ public class ElectrumServer {
if(optionalNode.isPresent()) { if(optionalNode.isPresent()) {
WalletNode node = optionalNode.get(); WalletNode node = optionalNode.get();
Set<BlockchainTransactionHash> references = Arrays.stream(txes).map(ScriptHashTx::getBlockchainTransactionHash).collect(Collectors.toCollection(TreeSet::new)); Set<BlockTransactionHash> references = Arrays.stream(txes).map(ScriptHashTx::getBlockchainTransactionHash).collect(Collectors.toCollection(TreeSet::new));
Set<BlockchainTransactionHash> existingReferences = nodeTransactionMap.get(node); Set<BlockTransactionHash> existingReferences = nodeTransactionMap.get(node);
if(existingReferences == null && !references.isEmpty()) { if(existingReferences == null && !references.isEmpty()) {
nodeTransactionMap.put(node, references); nodeTransactionMap.put(node, references);
} else { } else {
for(BlockchainTransactionHash reference : references) { for(BlockTransactionHash reference : references) {
if(!existingReferences.add(reference)) { if(!existingReferences.add(reference)) {
Optional<BlockchainTransactionHash> optionalReference = existingReferences.stream().filter(tr -> tr.getHash().equals(reference.getHash())).findFirst(); Optional<BlockTransactionHash> optionalReference = existingReferences.stream().filter(tr -> tr.getHash().equals(reference.getHash())).findFirst();
if(optionalReference.isPresent()) { if(optionalReference.isPresent()) {
BlockchainTransactionHash existingReference = optionalReference.get(); BlockTransactionHash existingReference = optionalReference.get();
if(existingReference.getHeight() < reference.getHeight()) { if(existingReference.getHeight() < reference.getHeight()) {
existingReferences.remove(existingReference); existingReferences.remove(existingReference);
existingReferences.add(reference); existingReferences.add(reference);
@ -126,13 +126,13 @@ public class ElectrumServer {
} }
} }
public void getReferencedTransactions(Wallet wallet, Map<WalletNode, Set<BlockchainTransactionHash>> nodeTransactionMap) throws ServerException { public void getReferencedTransactions(Wallet wallet, Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap) throws ServerException {
Set<BlockchainTransactionHash> references = new TreeSet<>(); Set<BlockTransactionHash> references = new TreeSet<>();
for(Set<BlockchainTransactionHash> nodeReferences : nodeTransactionMap.values()) { for(Set<BlockTransactionHash> nodeReferences : nodeTransactionMap.values()) {
references.addAll(nodeReferences); references.addAll(nodeReferences);
} }
Map<Sha256Hash, BlockchainTransaction> transactionMap = getTransactions(references); Map<Sha256Hash, BlockTransaction> transactionMap = getTransactions(references);
for(Sha256Hash hash : transactionMap.keySet()) { for(Sha256Hash hash : transactionMap.keySet()) {
if(wallet.getTransactions().get(hash) == null) { if(wallet.getTransactions().get(hash) == null) {
wallet.getTransactions().put(hash, transactionMap.get(hash)); wallet.getTransactions().put(hash, transactionMap.get(hash));
@ -143,29 +143,29 @@ public class ElectrumServer {
} }
} }
public Map<Sha256Hash, BlockchainTransaction> getTransactions(Set<BlockchainTransactionHash> references) throws ServerException { public Map<Sha256Hash, BlockTransaction> getTransactions(Set<BlockTransactionHash> references) throws ServerException {
try { try {
Set<BlockchainTransactionHash> checkReferences = new TreeSet<>(references); Set<BlockTransactionHash> checkReferences = new TreeSet<>(references);
JsonRpcClient client = new JsonRpcClient(getTransport()); JsonRpcClient client = new JsonRpcClient(getTransport());
BatchRequestBuilder<String, String> batchRequest = client.createBatchRequest().keysType(String.class).returnType(String.class); BatchRequestBuilder<String, String> batchRequest = client.createBatchRequest().keysType(String.class).returnType(String.class);
for(BlockchainTransactionHash reference : references) { for(BlockTransactionHash reference : references) {
batchRequest.add(reference.getHashAsString(), "blockchain.transaction.get", reference.getHashAsString()); batchRequest.add(reference.getHashAsString(), "blockchain.transaction.get", reference.getHashAsString());
} }
Map<String, String> result = batchRequest.execute(); Map<String, String> result = batchRequest.execute();
Map<Sha256Hash, BlockchainTransaction> transactionMap = new HashMap<>(); Map<Sha256Hash, BlockTransaction> transactionMap = new HashMap<>();
for(String txid : result.keySet()) { for(String txid : result.keySet()) {
Sha256Hash hash = Sha256Hash.wrap(txid); Sha256Hash hash = Sha256Hash.wrap(txid);
byte[] rawtx = Utils.hexToBytes(result.get(txid)); byte[] rawtx = Utils.hexToBytes(result.get(txid));
Transaction transaction = new Transaction(rawtx); Transaction transaction = new Transaction(rawtx);
Optional<BlockchainTransactionHash> optionalReference = references.stream().filter(reference -> reference.getHash().equals(hash)).findFirst(); Optional<BlockTransactionHash> optionalReference = references.stream().filter(reference -> reference.getHash().equals(hash)).findFirst();
if(optionalReference.isEmpty()) { if(optionalReference.isEmpty()) {
throw new IllegalStateException("Returned transaction " + hash.toString() + " that was not requested"); throw new IllegalStateException("Returned transaction " + hash.toString() + " that was not requested");
} }
BlockchainTransactionHash reference = optionalReference.get(); BlockTransactionHash reference = optionalReference.get();
BlockchainTransaction blockchainTransaction = new BlockchainTransaction(reference.getHash(), reference.getHeight(), reference.getFee(), transaction); BlockTransaction blockchainTransaction = new BlockTransaction(reference.getHash(), reference.getHeight(), reference.getFee(), transaction);
transactionMap.put(hash, blockchainTransaction); transactionMap.put(hash, blockchainTransaction);
checkReferences.remove(reference); checkReferences.remove(reference);
@ -183,17 +183,17 @@ public class ElectrumServer {
} }
} }
public void calculateNodeHistory(Wallet wallet, Map<WalletNode, Set<BlockchainTransactionHash>> nodeTransactionMap) { public void calculateNodeHistory(Wallet wallet, Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap) {
for(WalletNode node : nodeTransactionMap.keySet()) { for(WalletNode node : nodeTransactionMap.keySet()) {
calculateNodeHistory(wallet, nodeTransactionMap, node); calculateNodeHistory(wallet, nodeTransactionMap, node);
} }
} }
public void calculateNodeHistory(Wallet wallet, Map<WalletNode, Set<BlockchainTransactionHash>> nodeTransactionMap, WalletNode node) { public void calculateNodeHistory(Wallet wallet, Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap, WalletNode node) {
Script nodeScript = wallet.getOutputScript(node); Script nodeScript = wallet.getOutputScript(node);
Set<BlockchainTransactionHash> history = nodeTransactionMap.get(node); Set<BlockTransactionHash> history = nodeTransactionMap.get(node);
for(BlockchainTransactionHash reference : history) { for(BlockTransactionHash reference : history) {
BlockchainTransaction blockchainTransaction = wallet.getTransactions().get(reference.getHash()); BlockTransaction blockchainTransaction = wallet.getTransactions().get(reference.getHash());
if(blockchainTransaction == null) { if(blockchainTransaction == null) {
throw new IllegalStateException("Could not retrieve transaction for hash " + reference.getHashAsString()); throw new IllegalStateException("Could not retrieve transaction for hash " + reference.getHashAsString());
} }
@ -202,32 +202,32 @@ public class ElectrumServer {
for(int inputIndex = 0; inputIndex < transaction.getInputs().size(); inputIndex++) { for(int inputIndex = 0; inputIndex < transaction.getInputs().size(); inputIndex++) {
TransactionInput input = transaction.getInputs().get(inputIndex); TransactionInput input = transaction.getInputs().get(inputIndex);
Sha256Hash previousHash = input.getOutpoint().getHash(); Sha256Hash previousHash = input.getOutpoint().getHash();
BlockchainTransaction previousTransaction = wallet.getTransactions().get(previousHash); BlockTransaction previousTransaction = wallet.getTransactions().get(previousHash);
if(previousTransaction == null) { if(previousTransaction == null) {
//No referenced transaction found, cannot check if spends from wallet //No referenced transaction found, cannot check if spends from wallet
//This is fine so long as all referenced transactions have been returned, in which case this refers to a transaction that does not affect this wallet //This is fine so long as all referenced transactions have been returned, in which case this refers to a transaction that does not affect this wallet
continue; continue;
} }
Optional<BlockchainTransactionHash> optionalTxHash = history.stream().filter(txHash -> txHash.getHash().equals(previousHash)).findFirst(); Optional<BlockTransactionHash> optionalTxHash = history.stream().filter(txHash -> txHash.getHash().equals(previousHash)).findFirst();
if(optionalTxHash.isEmpty()) { if(optionalTxHash.isEmpty()) {
//No previous transaction history found, cannot check if spends from wallet //No previous transaction history found, cannot check if spends from wallet
//This is fine so long as all referenced transactions have been returned, in which case this refers to a transaction that does not affect this wallet node //This is fine so long as all referenced transactions have been returned, in which case this refers to a transaction that does not affect this wallet node
continue; continue;
} }
BlockchainTransactionHash spentTxHash = optionalTxHash.get(); BlockTransactionHash spentTxHash = optionalTxHash.get();
TransactionOutput spentOutput = previousTransaction.getTransaction().getOutputs().get((int)input.getOutpoint().getIndex()); TransactionOutput spentOutput = previousTransaction.getTransaction().getOutputs().get((int)input.getOutpoint().getIndex());
if(spentOutput.getScript().equals(nodeScript)) { if(spentOutput.getScript().equals(nodeScript)) {
BlockchainTransactionHashIndex spendingTXI = new BlockchainTransactionHashIndex(reference.getHash(), reference.getHeight(), reference.getFee(), inputIndex, spentOutput.getValue()); BlockTransactionHashIndex spendingTXI = new BlockTransactionHashIndex(reference.getHash(), reference.getHeight(), reference.getFee(), inputIndex, spentOutput.getValue());
BlockchainTransactionHashIndex spentTXO = new BlockchainTransactionHashIndex(spentTxHash.getHash(), spentTxHash.getHeight(), spentTxHash.getFee(), spentOutput.getIndex(), spentOutput.getValue(), spendingTXI); BlockTransactionHashIndex spentTXO = new BlockTransactionHashIndex(spentTxHash.getHash(), spentTxHash.getHeight(), spentTxHash.getFee(), spentOutput.getIndex(), spentOutput.getValue(), spendingTXI);
Optional<BlockchainTransactionHashIndex> optionalReference = node.getTransactionOutputs().stream().filter(receivedTXO -> receivedTXO.equals(spentTXO)).findFirst(); Optional<BlockTransactionHashIndex> optionalReference = node.getTransactionOutputs().stream().filter(receivedTXO -> receivedTXO.equals(spentTXO)).findFirst();
if(optionalReference.isEmpty()) { if(optionalReference.isEmpty()) {
throw new IllegalStateException("Found spent transaction output " + spentTXO + " but no record of receiving it"); throw new IllegalStateException("Found spent transaction output " + spentTXO + " but no record of receiving it");
} }
BlockchainTransactionHashIndex receivedTXO = optionalReference.get(); BlockTransactionHashIndex receivedTXO = optionalReference.get();
receivedTXO.setSpentBy(spendingTXI); receivedTXO.setSpentBy(spendingTXI);
} }
} }
@ -235,12 +235,12 @@ public class ElectrumServer {
for(int outputIndex = 0; outputIndex < transaction.getOutputs().size(); outputIndex++) { for(int outputIndex = 0; outputIndex < transaction.getOutputs().size(); outputIndex++) {
TransactionOutput output = transaction.getOutputs().get(outputIndex); TransactionOutput output = transaction.getOutputs().get(outputIndex);
if(output.getScript().equals(nodeScript)) { if(output.getScript().equals(nodeScript)) {
BlockchainTransactionHashIndex receivingTXO = new BlockchainTransactionHashIndex(reference.getHash(), reference.getHeight(), reference.getFee(), output.getIndex(), output.getValue()); BlockTransactionHashIndex receivingTXO = new BlockTransactionHashIndex(reference.getHash(), reference.getHeight(), reference.getFee(), output.getIndex(), output.getValue());
Optional<BlockchainTransactionHashIndex> optionalExistingTXO = node.getTransactionOutputs().stream().filter(txo -> txo.getHash().equals(receivingTXO.getHash()) && txo.getIndex() == receivingTXO.getIndex() && txo.getHeight() != receivingTXO.getHeight()).findFirst(); Optional<BlockTransactionHashIndex> optionalExistingTXO = node.getTransactionOutputs().stream().filter(txo -> txo.getHash().equals(receivingTXO.getHash()) && txo.getIndex() == receivingTXO.getIndex() && txo.getHeight() != receivingTXO.getHeight()).findFirst();
if(optionalExistingTXO.isEmpty()) { if(optionalExistingTXO.isEmpty()) {
node.getTransactionOutputs().add(receivingTXO); node.getTransactionOutputs().add(receivingTXO);
} else { } else {
BlockchainTransactionHashIndex existingTXO = optionalExistingTXO.get(); BlockTransactionHashIndex existingTXO = optionalExistingTXO.get();
if(existingTXO.getHeight() < receivingTXO.getHeight()) { if(existingTXO.getHeight() < receivingTXO.getHeight()) {
node.getTransactionOutputs().remove(existingTXO); node.getTransactionOutputs().remove(existingTXO);
node.getTransactionOutputs().add(receivingTXO); node.getTransactionOutputs().add(receivingTXO);
@ -262,9 +262,9 @@ public class ElectrumServer {
public String tx_hash; public String tx_hash;
public long fee; public long fee;
public BlockchainTransactionHash getBlockchainTransactionHash() { public BlockTransactionHash getBlockchainTransactionHash() {
Sha256Hash hash = Sha256Hash.wrap(tx_hash); Sha256Hash hash = Sha256Hash.wrap(tx_hash);
return new BlockchainTransaction(hash, height, fee, null); return new BlockTransaction(hash, height, fee, null);
} }
@Override @Override
@ -386,7 +386,7 @@ public class ElectrumServer {
return new Task<>() { return new Task<>() {
protected Boolean call() throws ServerException { protected Boolean call() throws ServerException {
ElectrumServer electrumServer = new ElectrumServer(); ElectrumServer electrumServer = new ElectrumServer();
Map<WalletNode, Set<BlockchainTransactionHash>> nodeTransactionMap = electrumServer.getHistory(wallet); Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap = electrumServer.getHistory(wallet);
electrumServer.getReferencedTransactions(wallet, nodeTransactionMap); electrumServer.getReferencedTransactions(wallet, nodeTransactionMap);
electrumServer.calculateNodeHistory(wallet, nodeTransactionMap); electrumServer.calculateNodeHistory(wallet, nodeTransactionMap);
return true; return true;