mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-12 02:05:53 +00:00
Refactor some Stored* classes that are worthless...
Specifically, this moves StoredTransaction to MemoryFullPrunedBlockStore and uses custom serialization for StoredTransactionOutput.
This commit is contained in:
parent
91cd289a5c
commit
a036b68aa2
@ -84,10 +84,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
|||||||
protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block block)
|
protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block block)
|
||||||
throws BlockStoreException, VerificationException {
|
throws BlockStoreException, VerificationException {
|
||||||
StoredBlock newBlock = storedPrev.build(block);
|
StoredBlock newBlock = storedPrev.build(block);
|
||||||
LinkedList<StoredTransaction> transactions = new LinkedList<StoredTransaction>();
|
blockStore.put(newBlock, new StoredUndoableBlock(newBlock.getHeader().getHash(), block.transactions));
|
||||||
for (Transaction tx : block.transactions)
|
|
||||||
transactions.add(new StoredTransaction(tx, newBlock.getHeight()));
|
|
||||||
blockStore.put(newBlock, new StoredUndoableBlock(newBlock.getHeader().getHash(), transactions));
|
|
||||||
return newBlock;
|
return newBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,14 +284,14 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
|||||||
}
|
}
|
||||||
TransactionOutputChanges txOutChanges;
|
TransactionOutputChanges txOutChanges;
|
||||||
try {
|
try {
|
||||||
List<StoredTransaction> transactions = block.getTransactions();
|
List<Transaction> transactions = block.getTransactions();
|
||||||
if (transactions != null) {
|
if (transactions != null) {
|
||||||
LinkedList<StoredTransactionOutput> txOutsSpent = new LinkedList<StoredTransactionOutput>();
|
LinkedList<StoredTransactionOutput> txOutsSpent = new LinkedList<StoredTransactionOutput>();
|
||||||
LinkedList<StoredTransactionOutput> txOutsCreated = new LinkedList<StoredTransactionOutput>();
|
LinkedList<StoredTransactionOutput> txOutsCreated = new LinkedList<StoredTransactionOutput>();
|
||||||
long sigOps = 0;
|
long sigOps = 0;
|
||||||
final boolean enforcePayToScriptHash = newBlock.getHeader().getTimeSeconds() >= params.BIP16_ENFORCE_TIME;
|
final boolean enforcePayToScriptHash = newBlock.getHeader().getTimeSeconds() >= params.BIP16_ENFORCE_TIME;
|
||||||
if (!params.isCheckpoint(newBlock.getHeight())) {
|
if (!params.isCheckpoint(newBlock.getHeight())) {
|
||||||
for(StoredTransaction tx : transactions) {
|
for(Transaction tx : transactions) {
|
||||||
Sha256Hash hash = tx.getHash();
|
Sha256Hash hash = tx.getHash();
|
||||||
if (blockStore.hasUnspentOutputs(hash, tx.getOutputs().size()))
|
if (blockStore.hasUnspentOutputs(hash, tx.getOutputs().size()))
|
||||||
throw new VerificationException("Block failed BIP30 test!");
|
throw new VerificationException("Block failed BIP30 test!");
|
||||||
@ -306,7 +303,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
|||||||
if (scriptVerificationExecutor.isShutdown())
|
if (scriptVerificationExecutor.isShutdown())
|
||||||
scriptVerificationExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
scriptVerificationExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
||||||
List<Future<VerificationException>> listScriptVerificationResults = new ArrayList<Future<VerificationException>>(transactions.size());
|
List<Future<VerificationException>> listScriptVerificationResults = new ArrayList<Future<VerificationException>>(transactions.size());
|
||||||
for(final StoredTransaction tx : transactions) {
|
for(final Transaction tx : transactions) {
|
||||||
boolean isCoinBase = tx.isCoinBase();
|
boolean isCoinBase = tx.isCoinBase();
|
||||||
BigInteger valueIn = BigInteger.ZERO;
|
BigInteger valueIn = BigInteger.ZERO;
|
||||||
BigInteger valueOut = BigInteger.ZERO;
|
BigInteger valueOut = BigInteger.ZERO;
|
||||||
@ -340,7 +337,6 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
|||||||
// TODO: Find out the underlying issue and create a better work-around
|
// TODO: Find out the underlying issue and create a better work-around
|
||||||
// TODO: Thoroughly test that this fixes the issue like the non-StoredBlock version does
|
// TODO: Thoroughly test that this fixes the issue like the non-StoredBlock version does
|
||||||
final int currentIndex = index;
|
final int currentIndex = index;
|
||||||
final Transaction txCache = new Transaction(params, tx);
|
|
||||||
final Script scriptSig;
|
final Script scriptSig;
|
||||||
try {
|
try {
|
||||||
scriptSig = in.getScriptSig();
|
scriptSig = in.getScriptSig();
|
||||||
@ -356,7 +352,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
|||||||
FutureTask<VerificationException> future = new FutureTask<VerificationException>(new Callable<VerificationException>() {
|
FutureTask<VerificationException> future = new FutureTask<VerificationException>(new Callable<VerificationException>() {
|
||||||
public VerificationException call() {
|
public VerificationException call() {
|
||||||
try{
|
try{
|
||||||
scriptSig.correctlySpends(txCache, currentIndex, scriptPubKey, enforcePayToScriptHash);
|
scriptSig.correctlySpends(tx, currentIndex, scriptPubKey, enforcePayToScriptHash);
|
||||||
} catch (ScriptException e) {
|
} catch (ScriptException e) {
|
||||||
return new VerificationException("Error verifying script: " + e.getMessage());
|
return new VerificationException("Error verifying script: " + e.getMessage());
|
||||||
} catch (VerificationException e) {
|
} catch (VerificationException e) {
|
||||||
@ -372,7 +368,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Sha256Hash hash = tx.getHash();
|
Sha256Hash hash = tx.getHash();
|
||||||
for (StoredTransactionOutput out : tx.getOutputs()) {
|
for (TransactionOutput out : tx.getOutputs()) {
|
||||||
valueOut = valueOut.add(out.getValue());
|
valueOut = valueOut.add(out.getValue());
|
||||||
StoredTransactionOutput newOut = new StoredTransactionOutput(hash, out.getIndex(), out.getValue(),
|
StoredTransactionOutput newOut = new StoredTransactionOutput(hash, out.getIndex(), out.getValue(),
|
||||||
newBlock.getHeight(), isCoinBase,
|
newBlock.getHeight(), isCoinBase,
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2012 Matt Corallo.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.google.bitcoin.core;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
//TODO: Move this to MemoryFullPrunedBlockStore and use a different method for on-disk storage (bitcoin serialization?)
|
|
||||||
/**
|
|
||||||
* A StoredTransaction message contains the information necessary to check a transaction later (ie after a reorg).
|
|
||||||
* It is used to avoid having to store the entire transaction when we only need its inputs+outputs.
|
|
||||||
* Its only really useful for MemoryFullPrunedBlockStore, and should probably be moved there
|
|
||||||
*/
|
|
||||||
public class StoredTransaction implements Serializable {
|
|
||||||
private static final long serialVersionUID = 6243881368122528323L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A transaction has some value and a script used for authenticating that the redeemer is allowed to spend
|
|
||||||
* this output.
|
|
||||||
*/
|
|
||||||
private List<StoredTransactionOutput> outputs;
|
|
||||||
private List<TransactionInput> inputs;
|
|
||||||
private long version;
|
|
||||||
private long lockTime;
|
|
||||||
private Sha256Hash hash;
|
|
||||||
|
|
||||||
public StoredTransaction(Transaction tx, int height) {
|
|
||||||
inputs = new LinkedList<TransactionInput>();
|
|
||||||
outputs = new LinkedList<StoredTransactionOutput>();
|
|
||||||
for (TransactionInput in : tx.getInputs())
|
|
||||||
inputs.add(new TransactionInput(in.params, null, in.getScriptBytes(), in.getOutpoint()));
|
|
||||||
for (TransactionOutput out : tx.getOutputs())
|
|
||||||
outputs.add(new StoredTransactionOutput(null, out, height, tx.isCoinBase()));
|
|
||||||
this.version = tx.getVersion();
|
|
||||||
this.lockTime = tx.getLockTime();
|
|
||||||
this.hash = tx.getHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The lits of inputs in this transaction
|
|
||||||
*/
|
|
||||||
public List<TransactionInput> getInputs() {
|
|
||||||
return inputs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The lits of outputs in this transaction
|
|
||||||
* Note that the hashes of all of these are null
|
|
||||||
*/
|
|
||||||
public List<StoredTransactionOutput> getOutputs() {
|
|
||||||
return outputs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The hash of this stored transaction
|
|
||||||
*/
|
|
||||||
public Sha256Hash getHash() {
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The lockTime of the stored transaction
|
|
||||||
*/
|
|
||||||
public long getLockTime() {
|
|
||||||
return lockTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The version of the stored transaction
|
|
||||||
*/
|
|
||||||
public long getVersion() {
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A coinbase transaction is one that creates a new coin. They are the first transaction in each block and their
|
|
||||||
* value is determined by a formula that all implementations of BitCoin share. In 2011 the value of a coinbase
|
|
||||||
* transaction is 50 coins, but in future it will be less. A coinbase transaction is defined not only by its
|
|
||||||
* position in a block but by the data in the inputs.
|
|
||||||
*/
|
|
||||||
public boolean isCoinBase() {
|
|
||||||
return inputs.get(0).isCoinBase();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return "Stored Transaction: " + hash.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int hashCode() {
|
|
||||||
return getHash().hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (!(o instanceof StoredTransaction)) return false;
|
|
||||||
return ((StoredTransaction) o).getHash().equals(this.getHash());
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,6 +16,10 @@
|
|||||||
|
|
||||||
package com.google.bitcoin.core;
|
package com.google.bitcoin.core;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
|
||||||
@ -69,6 +73,35 @@ public class StoredTransactionOutput implements Serializable {
|
|||||||
this.scriptBytes = out.getScriptBytes();
|
this.scriptBytes = out.getScriptBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StoredTransactionOutput(InputStream in) throws IOException {
|
||||||
|
byte[] valueBytes = new byte[8];
|
||||||
|
in.read(valueBytes, 0, 8);
|
||||||
|
value = BigInteger.valueOf(Utils.readInt64(valueBytes, 0));
|
||||||
|
|
||||||
|
int scriptBytesLength = ((in.read() & 0xFF) << 0) |
|
||||||
|
((in.read() & 0xFF) << 8) |
|
||||||
|
((in.read() & 0xFF) << 16) |
|
||||||
|
((in.read() & 0xFF) << 24);
|
||||||
|
scriptBytes = new byte[scriptBytesLength];
|
||||||
|
if (in.read(scriptBytes) != scriptBytesLength)
|
||||||
|
throw new EOFException();
|
||||||
|
|
||||||
|
byte[] hashBytes = new byte[32];
|
||||||
|
if (in.read(hashBytes) != 32)
|
||||||
|
throw new EOFException();
|
||||||
|
hash = new Sha256Hash(hashBytes);
|
||||||
|
|
||||||
|
byte[] indexBytes = new byte[4];
|
||||||
|
if (in.read(indexBytes) != 4)
|
||||||
|
throw new EOFException();
|
||||||
|
index = Utils.readUint32(indexBytes, 0);
|
||||||
|
|
||||||
|
height = ((in.read() & 0xFF) << 0) |
|
||||||
|
((in.read() & 0xFF) << 8) |
|
||||||
|
((in.read() & 0xFF) << 16) |
|
||||||
|
((in.read() & 0xFF) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The value which this Transaction output holds
|
* The value which this Transaction output holds
|
||||||
* @return the value
|
* @return the value
|
||||||
@ -121,4 +154,22 @@ public class StoredTransactionOutput implements Serializable {
|
|||||||
return ((StoredTransactionOutput) o).getIndex() == this.getIndex() &&
|
return ((StoredTransactionOutput) o).getIndex() == this.getIndex() &&
|
||||||
((StoredTransactionOutput) o).getHash().equals(this.getHash());
|
((StoredTransactionOutput) o).getHash().equals(this.getHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void serializeToStream(OutputStream bos) throws IOException {
|
||||||
|
Utils.uint64ToByteStreamLE(value, bos);
|
||||||
|
|
||||||
|
bos.write((int) (0xFF & (scriptBytes.length >> 0)));
|
||||||
|
bos.write((int) (0xFF & (scriptBytes.length >> 8)));
|
||||||
|
bos.write((int) (0xFF & (scriptBytes.length >> 16)));
|
||||||
|
bos.write((int) (0xFF & (scriptBytes.length >> 24)));
|
||||||
|
bos.write(scriptBytes);
|
||||||
|
|
||||||
|
bos.write(hash.getBytes());
|
||||||
|
Utils.uint32ToByteStreamLE(index, bos);
|
||||||
|
|
||||||
|
bos.write((int) (0xFF & (height >> 0)));
|
||||||
|
bos.write((int) (0xFF & (height >> 8)));
|
||||||
|
bos.write((int) (0xFF & (height >> 16)));
|
||||||
|
bos.write((int) (0xFF & (height >> 24)));
|
||||||
|
}
|
||||||
}
|
}
|
@ -34,7 +34,7 @@ public class StoredUndoableBlock implements Serializable {
|
|||||||
|
|
||||||
// Only one of either txOutChanges or transactions will be set
|
// Only one of either txOutChanges or transactions will be set
|
||||||
private TransactionOutputChanges txOutChanges;
|
private TransactionOutputChanges txOutChanges;
|
||||||
private List<StoredTransaction> transactions;
|
private List<Transaction> transactions;
|
||||||
|
|
||||||
public StoredUndoableBlock(Sha256Hash hash, TransactionOutputChanges txOutChanges) {
|
public StoredUndoableBlock(Sha256Hash hash, TransactionOutputChanges txOutChanges) {
|
||||||
this.blockHash = hash;
|
this.blockHash = hash;
|
||||||
@ -42,7 +42,7 @@ public class StoredUndoableBlock implements Serializable {
|
|||||||
this.txOutChanges = txOutChanges;
|
this.txOutChanges = txOutChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StoredUndoableBlock(Sha256Hash hash, List<StoredTransaction> transactions) {
|
public StoredUndoableBlock(Sha256Hash hash, List<Transaction> transactions) {
|
||||||
this.blockHash = hash;
|
this.blockHash = hash;
|
||||||
this.txOutChanges = null;
|
this.txOutChanges = null;
|
||||||
this.transactions = transactions;
|
this.transactions = transactions;
|
||||||
@ -60,7 +60,7 @@ public class StoredUndoableBlock implements Serializable {
|
|||||||
* Get the full list of transactions if it is stored, otherwise null.
|
* Get the full list of transactions if it is stored, otherwise null.
|
||||||
* Only one of this and getTxOutChanges() will return a non-null value.
|
* Only one of this and getTxOutChanges() will return a non-null value.
|
||||||
*/
|
*/
|
||||||
public List<StoredTransaction> getTransactions() {
|
public List<Transaction> getTransactions() {
|
||||||
return transactions;
|
return transactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,22 +139,6 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
throws ProtocolException {
|
throws ProtocolException {
|
||||||
super(params, msg, 0, parent, parseLazy, parseRetain, length);
|
super(params, msg, 0, parent, parseLazy, parseRetain, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a transaction from the given StoredTransaction
|
|
||||||
* This is unsafe in that editing some aspects of this transaction may edit the stored transaction.
|
|
||||||
* Thus, this should only be used if the resulting transaction will only be used in a read-only manner.
|
|
||||||
*/
|
|
||||||
Transaction(NetworkParameters params, StoredTransaction tx) {
|
|
||||||
super(params);
|
|
||||||
this.version = tx.getVersion();
|
|
||||||
this.lockTime = tx.getLockTime();
|
|
||||||
this.inputs = new ArrayList<TransactionInput>(tx.getInputs());
|
|
||||||
this.outputs = new ArrayList<TransactionOutput>(tx.getOutputs().size());
|
|
||||||
for (StoredTransactionOutput output : tx.getOutputs()) {
|
|
||||||
this.outputs.add(new TransactionOutput(params, this, output.getValue(), output.getScriptBytes()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the transaction hash as you see them in the block explorer.
|
* Returns the transaction hash as you see them in the block explorer.
|
||||||
|
@ -16,7 +16,10 @@
|
|||||||
|
|
||||||
package com.google.bitcoin.core;
|
package com.google.bitcoin.core;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,9 +28,7 @@ import java.util.List;
|
|||||||
* It does contain outputs created that were spent later in the block, as those are needed for
|
* It does contain outputs created that were spent later in the block, as those are needed for
|
||||||
* BIP30 (no duplicate txid creation if the previous one was not fully spent prior to this block) verification.
|
* BIP30 (no duplicate txid creation if the previous one was not fully spent prior to this block) verification.
|
||||||
*/
|
*/
|
||||||
public class TransactionOutputChanges implements Serializable {
|
public class TransactionOutputChanges {
|
||||||
private static final long serialVersionUID = -6169346729324181905L;
|
|
||||||
|
|
||||||
public final List<StoredTransactionOutput> txOutsCreated;
|
public final List<StoredTransactionOutput> txOutsCreated;
|
||||||
public final List<StoredTransactionOutput> txOutsSpent;
|
public final List<StoredTransactionOutput> txOutsSpent;
|
||||||
|
|
||||||
@ -35,4 +36,42 @@ public class TransactionOutputChanges implements Serializable {
|
|||||||
this.txOutsCreated = txOutsCreated;
|
this.txOutsCreated = txOutsCreated;
|
||||||
this.txOutsSpent = txOutsSpent;
|
this.txOutsSpent = txOutsSpent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TransactionOutputChanges(InputStream in) throws IOException {
|
||||||
|
int numOutsCreated = ((in.read() & 0xFF) << 0) |
|
||||||
|
((in.read() & 0xFF) << 8) |
|
||||||
|
((in.read() & 0xFF) << 16) |
|
||||||
|
((in.read() & 0xFF) << 24);
|
||||||
|
txOutsCreated = new LinkedList<StoredTransactionOutput>();
|
||||||
|
for (int i = 0; i < numOutsCreated; i++)
|
||||||
|
txOutsCreated.add(new StoredTransactionOutput(in));
|
||||||
|
|
||||||
|
int numOutsSpent = ((in.read() & 0xFF) << 0) |
|
||||||
|
((in.read() & 0xFF) << 8) |
|
||||||
|
((in.read() & 0xFF) << 16) |
|
||||||
|
((in.read() & 0xFF) << 24);
|
||||||
|
txOutsSpent = new LinkedList<StoredTransactionOutput>();
|
||||||
|
for (int i = 0; i < numOutsSpent; i++)
|
||||||
|
txOutsSpent.add(new StoredTransactionOutput(in));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void serializeToStream(OutputStream bos) throws IOException {
|
||||||
|
int numOutsCreated = txOutsCreated.size();
|
||||||
|
bos.write((int) (0xFF & (numOutsCreated >> 0)));
|
||||||
|
bos.write((int) (0xFF & (numOutsCreated >> 8)));
|
||||||
|
bos.write((int) (0xFF & (numOutsCreated >> 16)));
|
||||||
|
bos.write((int) (0xFF & (numOutsCreated >> 24)));
|
||||||
|
for (StoredTransactionOutput output : txOutsCreated) {
|
||||||
|
output.serializeToStream(bos);
|
||||||
|
}
|
||||||
|
|
||||||
|
int numOutsSpent = txOutsSpent.size();
|
||||||
|
bos.write((int) (0xFF & (numOutsSpent >> 0)));
|
||||||
|
bos.write((int) (0xFF & (numOutsSpent >> 8)));
|
||||||
|
bos.write((int) (0xFF & (numOutsSpent >> 16)));
|
||||||
|
bos.write((int) (0xFF & (numOutsSpent >> 24)));
|
||||||
|
for (StoredTransactionOutput output : txOutsSpent) {
|
||||||
|
output.serializeToStream(bos);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,13 @@ package com.google.bitcoin.store;
|
|||||||
|
|
||||||
import com.google.bitcoin.core.*;
|
import com.google.bitcoin.core.*;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@ -199,7 +202,7 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
|
|||||||
StoredBlock storedGenesisHeader = new StoredBlock(params.genesisBlock.cloneAsHeader(), params.genesisBlock.getWork(), 0);
|
StoredBlock storedGenesisHeader = new StoredBlock(params.genesisBlock.cloneAsHeader(), params.genesisBlock.getWork(), 0);
|
||||||
// The coinbase in the genesis block is not spendable. This is because of how the reference client inits
|
// The coinbase in the genesis block is not spendable. This is because of how the reference client inits
|
||||||
// its database - the genesis transaction isn't actually in the db so its spent flags can never be updated.
|
// its database - the genesis transaction isn't actually in the db so its spent flags can never be updated.
|
||||||
List<StoredTransaction> genesisTransactions = Lists.newLinkedList();
|
List<Transaction> genesisTransactions = Lists.newLinkedList();
|
||||||
StoredUndoableBlock storedGenesis = new StoredUndoableBlock(params.genesisBlock.getHash(), genesisTransactions);
|
StoredUndoableBlock storedGenesis = new StoredUndoableBlock(params.genesisBlock.getHash(), genesisTransactions);
|
||||||
put(storedGenesisHeader, storedGenesis);
|
put(storedGenesisHeader, storedGenesis);
|
||||||
setChainHead(storedGenesisHeader);
|
setChainHead(storedGenesisHeader);
|
||||||
@ -339,15 +342,19 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
|
|||||||
byte[] txOutChanges = null;
|
byte[] txOutChanges = null;
|
||||||
try {
|
try {
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
ObjectOutput out = new ObjectOutputStream(bos);
|
|
||||||
if (undoableBlock.getTxOutChanges() != null) {
|
if (undoableBlock.getTxOutChanges() != null) {
|
||||||
out.writeObject(undoableBlock.getTxOutChanges());
|
undoableBlock.getTxOutChanges().serializeToStream(bos);
|
||||||
txOutChanges = bos.toByteArray();
|
txOutChanges = bos.toByteArray();
|
||||||
} else {
|
} else {
|
||||||
out.writeObject(undoableBlock.getTransactions());
|
int numTxn = undoableBlock.getTransactions().size();
|
||||||
|
bos.write((int) (0xFF & (numTxn >> 0)));
|
||||||
|
bos.write((int) (0xFF & (numTxn >> 8)));
|
||||||
|
bos.write((int) (0xFF & (numTxn >> 16)));
|
||||||
|
bos.write((int) (0xFF & (numTxn >> 24)));
|
||||||
|
for (Transaction tx : undoableBlock.getTransactions())
|
||||||
|
tx.bitcoinSerialize(bos);
|
||||||
transactions = bos.toByteArray();
|
transactions = bos.toByteArray();
|
||||||
}
|
}
|
||||||
out.close();
|
|
||||||
bos.close();
|
bos.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new BlockStoreException(e);
|
throw new BlockStoreException(e);
|
||||||
@ -436,7 +443,6 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public StoredUndoableBlock getUndoBlock(Sha256Hash hash) throws BlockStoreException {
|
public StoredUndoableBlock getUndoBlock(Sha256Hash hash) throws BlockStoreException {
|
||||||
maybeConnect();
|
maybeConnect();
|
||||||
PreparedStatement s = null;
|
PreparedStatement s = null;
|
||||||
@ -456,14 +462,22 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
|
|||||||
byte[] transactions = results.getBytes(2);
|
byte[] transactions = results.getBytes(2);
|
||||||
StoredUndoableBlock block;
|
StoredUndoableBlock block;
|
||||||
if (txOutChanges == null) {
|
if (txOutChanges == null) {
|
||||||
Object transactionsObject = new ObjectInputStream(new ByteArrayInputStream(transactions)).readObject();
|
int offset = 0;
|
||||||
if (!(((List<?>) transactionsObject).get(0) instanceof StoredTransaction))
|
int numTxn = ((transactions[offset++] & 0xFF) << 0) |
|
||||||
throw new BlockStoreException("Corrupted StoredUndoableBlock");
|
((transactions[offset++] & 0xFF) << 8) |
|
||||||
block = new StoredUndoableBlock(hash, (List<StoredTransaction>) transactionsObject);
|
((transactions[offset++] & 0xFF) << 16) |
|
||||||
|
((transactions[offset++] & 0xFF) << 24);
|
||||||
|
List<Transaction> transactionList = new LinkedList<Transaction>();
|
||||||
|
for (int i = 0; i < numTxn; i++) {
|
||||||
|
Transaction tx = new Transaction(params, transactions, offset);
|
||||||
|
transactionList.add(tx);
|
||||||
|
offset += tx.getMessageSize();
|
||||||
|
}
|
||||||
|
block = new StoredUndoableBlock(hash, transactionList);
|
||||||
} else {
|
} else {
|
||||||
ObjectInputStream obj = new ObjectInputStream(new ByteArrayInputStream(txOutChanges));
|
TransactionOutputChanges outChangesObject =
|
||||||
Object transactionsObject = obj.readObject();
|
new TransactionOutputChanges(new ByteArrayInputStream(txOutChanges));
|
||||||
block = new StoredUndoableBlock(hash, (TransactionOutputChanges) transactionsObject);
|
block = new StoredUndoableBlock(hash, outChangesObject);
|
||||||
}
|
}
|
||||||
return block;
|
return block;
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
@ -474,7 +488,7 @@ public class H2FullPrunedBlockStore implements FullPrunedBlockStore {
|
|||||||
} catch (ClassCastException e) {
|
} catch (ClassCastException e) {
|
||||||
// Corrupted database.
|
// Corrupted database.
|
||||||
throw new BlockStoreException(e);
|
throw new BlockStoreException(e);
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ProtocolException e) {
|
||||||
// Corrupted database.
|
// Corrupted database.
|
||||||
throw new BlockStoreException(e);
|
throw new BlockStoreException(e);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -25,6 +25,95 @@ import java.io.Serializable;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A StoredTransaction message contains the information necessary to check a transaction later (ie after a reorg).
|
||||||
|
* It is used to avoid having to store the entire transaction when we only need its inputs+outputs.
|
||||||
|
*/
|
||||||
|
class StoredTransaction implements Serializable {
|
||||||
|
private static final long serialVersionUID = 6243881368122528323L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A transaction has some value and a script used for authenticating that the redeemer is allowed to spend
|
||||||
|
* this output.
|
||||||
|
*/
|
||||||
|
private List<StoredTransactionOutput> outputs;
|
||||||
|
private List<TransactionInput> inputs;
|
||||||
|
private long version;
|
||||||
|
private long lockTime;
|
||||||
|
private Sha256Hash hash;
|
||||||
|
|
||||||
|
public StoredTransaction(NetworkParameters params, Transaction tx, int height) {
|
||||||
|
inputs = new LinkedList<TransactionInput>();
|
||||||
|
outputs = new LinkedList<StoredTransactionOutput>();
|
||||||
|
for (TransactionInput in : tx.getInputs())
|
||||||
|
inputs.add(new TransactionInput(params, null, in.getScriptBytes(), in.getOutpoint()));
|
||||||
|
for (TransactionOutput out : tx.getOutputs())
|
||||||
|
outputs.add(new StoredTransactionOutput(null, out, height, tx.isCoinBase()));
|
||||||
|
this.version = tx.getVersion();
|
||||||
|
this.lockTime = tx.getLockTime();
|
||||||
|
this.hash = tx.getHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The lits of inputs in this transaction
|
||||||
|
*/
|
||||||
|
public List<TransactionInput> getInputs() {
|
||||||
|
return inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The lits of outputs in this transaction
|
||||||
|
* Note that the hashes of all of these are null
|
||||||
|
*/
|
||||||
|
public List<StoredTransactionOutput> getOutputs() {
|
||||||
|
return outputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hash of this stored transaction
|
||||||
|
*/
|
||||||
|
public Sha256Hash getHash() {
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The lockTime of the stored transaction
|
||||||
|
*/
|
||||||
|
public long getLockTime() {
|
||||||
|
return lockTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The version of the stored transaction
|
||||||
|
*/
|
||||||
|
public long getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A coinbase transaction is one that creates a new coin. They are the first transaction in each block and their
|
||||||
|
* value is determined by a formula that all implementations of BitCoin share. In 2011 the value of a coinbase
|
||||||
|
* transaction is 50 coins, but in future it will be less. A coinbase transaction is defined not only by its
|
||||||
|
* position in a block but by the data in the inputs.
|
||||||
|
*/
|
||||||
|
public boolean isCoinBase() {
|
||||||
|
return inputs.get(0).isCoinBase();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "Stored Transaction: " + hash.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return getHash().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof StoredTransaction)) return false;
|
||||||
|
return ((StoredTransaction) o).getHash().equals(this.getHash());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used as a key for memory map (to avoid having to think about NetworkParameters,
|
* Used as a key for memory map (to avoid having to think about NetworkParameters,
|
||||||
* which is required for {@link TransactionOutPoint}
|
* which is required for {@link TransactionOutPoint}
|
||||||
@ -240,7 +329,7 @@ public class MemoryFullPrunedBlockStore implements FullPrunedBlockStore {
|
|||||||
try {
|
try {
|
||||||
StoredBlock storedGenesisHeader = new StoredBlock(params.genesisBlock.cloneAsHeader(), params.genesisBlock.getWork(), 0);
|
StoredBlock storedGenesisHeader = new StoredBlock(params.genesisBlock.cloneAsHeader(), params.genesisBlock.getWork(), 0);
|
||||||
// The coinbase in the genesis block is not spendable
|
// The coinbase in the genesis block is not spendable
|
||||||
List<StoredTransaction> genesisTransactions = Lists.newLinkedList();
|
List<Transaction> genesisTransactions = Lists.newLinkedList();
|
||||||
StoredUndoableBlock storedGenesis = new StoredUndoableBlock(params.genesisBlock.getHash(), genesisTransactions);
|
StoredUndoableBlock storedGenesis = new StoredUndoableBlock(params.genesisBlock.getHash(), genesisTransactions);
|
||||||
put(storedGenesisHeader, storedGenesis);
|
put(storedGenesisHeader, storedGenesis);
|
||||||
setChainHead(storedGenesisHeader);
|
setChainHead(storedGenesisHeader);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user