3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-07 06:44:16 +00:00

Add a few Storage classes which avoid storing unnecessary data.

Specifically, this adds:
* StoredTransaction, which avoid having to store the entire
  transaction when we only need its inputs+outputs.
* StoredTransactionOutput, which avoids having to store the entire
  parentTransaction just to get the hash and index.
* TransactionOutputChanges, which is used to store two lists of
  StoredTransactionOutputs, one for the created set and one for
  the spent set.
* StoredUndoableBlock, which can store either only
  TransactionOutputChanges or only StoredTransactions so that the
  block can be more easily connected/disconnected at will.
This commit is contained in:
Matt Corallo 2012-07-09 04:23:27 +02:00 committed by Mike Hearn
parent 8a4c34edd0
commit b4215e8b01
8 changed files with 382 additions and 4 deletions

View File

@ -840,7 +840,8 @@ public class Block extends Message {
// Here we will do things a bit differently so a new address isn't needed every time. We'll put a simple // Here we will do things a bit differently so a new address isn't needed every time. We'll put a simple
// counter in the scriptSig so every transaction has a different hash. // counter in the scriptSig so every transaction has a different hash.
coinbase.addInput(new TransactionInput(params, coinbase, new byte[]{(byte) txCounter++})); coinbase.addInput(new TransactionInput(params, coinbase, new byte[]{(byte) txCounter++}));
coinbase.addOutput(new TransactionOutput(params, coinbase, Script.createOutputScript(pubKeyTo))); coinbase.addOutput(new TransactionOutput(params, coinbase, Script.createOutputScript(pubKeyTo),
Utils.toNanoCoins(50, 0)));
transactions.add(coinbase); transactions.add(coinbase);
coinbase.setParent(this); coinbase.setParent(this);
coinbase.length = coinbase.bitcoinSerialize().length; coinbase.length = coinbase.bitcoinSerialize().length;

View File

@ -128,7 +128,7 @@ public class NetworkParameters implements Serializable {
Script.writeBytes(scriptPubKeyBytes, Hex.decode Script.writeBytes(scriptPubKeyBytes, Hex.decode
("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f")); ("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"));
scriptPubKeyBytes.write(Script.OP_CHECKSIG); scriptPubKeyBytes.write(Script.OP_CHECKSIG);
t.addOutput(new TransactionOutput(n, t, scriptPubKeyBytes.toByteArray())); t.addOutput(new TransactionOutput(n, t, scriptPubKeyBytes.toByteArray(), Utils.toNanoCoins(50, 0)));
} catch (Exception e) { } catch (Exception e) {
// Cannot happen. // Cannot happen.
throw new RuntimeException(e); throw new RuntimeException(e);

View File

@ -0,0 +1,112 @@
/**
* 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());
}
}

View File

@ -0,0 +1,124 @@
/**
* 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.math.BigInteger;
/**
* A StoredTransactionOutput message contains the information necessary to check a spending transaction.
* It avoids having to store the entire parentTransaction just to get the hash and index.
* Its only really useful for MemoryFullPrunedBlockStore, and should probably be moved there
*/
public class StoredTransactionOutput implements Serializable {
private static final long serialVersionUID = -8744924157056340509L;
/**
* A transaction output has some value and a script used for authenticating that the redeemer is allowed to spend
* this output.
*/
private BigInteger value;
private byte[] scriptBytes;
/** Hash of the transaction to which we refer. */
private Sha256Hash hash;
/** Which output of that transaction we are talking about. */
private long index;
/** arbitrary value lower than -{@link NetworkParameters.spendableCoinbaseDepth}
* (not too low to get overflows when we do blockHeight - NONCOINBASE_HEIGHT, though) */
private static final int NONCOINBASE_HEIGHT = -200;
/** The height of the creating block (for coinbases, NONCOINBASE_HEIGHT otherwise) */
private int height;
/**
* Creates a stored transaction output
* @param hash the hash of the containing transaction
* @param index the outpoint
* @param value the value available
* @param height the height this output was created in
* @param scriptBytes
*/
public StoredTransactionOutput(Sha256Hash hash, long index, BigInteger value, int height, boolean isCoinbase, byte[] scriptBytes) {
this.hash = hash;
this.index = index;
this.value = value;
this.height = isCoinbase ? height : NONCOINBASE_HEIGHT;
this.scriptBytes = scriptBytes;
}
public StoredTransactionOutput(Sha256Hash hash, TransactionOutput out, int height, boolean isCoinbase) {
this.hash = hash;
this.index = out.getIndex();
this.value = out.getValue();
this.height = isCoinbase ? height : NONCOINBASE_HEIGHT;
this.scriptBytes = out.getScriptBytes();
}
/**
* The value which this Transaction output holds
* @return the value
*/
public BigInteger getValue() {
return value;
}
/**
* The backing script bytes which can be turned into a Script object.
* @return the scriptBytes
*/
public byte[] getScriptBytes() {
return scriptBytes;
}
/**
* The hash of the transaction which holds this output
* @return the hash
*/
public Sha256Hash getHash() {
return hash;
}
/**
* The index of this output in the transaction which holds it
* @return the index
*/
public long getIndex() {
return index;
}
/**
* Gets the height of the block that created this output (or -1 if this output was not created by a coinbase)
*/
public int getHeight() {
return height;
}
public String toString() {
return String.format("Stored TxOut of %s (%s:%l)", Utils.bitcoinValueToFriendlyString(value), hash.toString(), index);
}
public int hashCode() {
return hash.hashCode() + (int)index;
}
public boolean equals(Object o) {
if (!(o instanceof StoredTransactionOutput)) return false;
return ((StoredTransactionOutput) o).getIndex() == this.getIndex() &&
((StoredTransactionOutput) o).getHash().equals(this.getHash());
}
}

View File

@ -0,0 +1,87 @@
/**
* Copyright 2011 Google Inc.
* 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.List;
/**
* Contains minimal data neccessary to disconnect/connect the transactions
* in the stored block at will. Can either store the full set of
* transactions (if the inputs for the block have not been tested to work)
* or the set of transaction outputs created/destroyed when the block is
* connected.
*/
public class StoredUndoableBlock implements Serializable {
private static final long serialVersionUID = 5127353027086786117L;
Sha256Hash blockHash;
// Only one of either txOutChanges or transactions will be set
private TransactionOutputChanges txOutChanges;
private List<StoredTransaction> transactions;
public StoredUndoableBlock(Sha256Hash hash, TransactionOutputChanges txOutChanges) {
this.blockHash = hash;
this.transactions = null;
this.txOutChanges = txOutChanges;
}
public StoredUndoableBlock(Sha256Hash hash, List<StoredTransaction> transactions) {
this.blockHash = hash;
this.txOutChanges = null;
this.transactions = transactions;
}
/**
* Get the transaction output changes if they have been calculated, otherwise null.
* Only one of this and getTransactions() will return a non-null value.
*/
public TransactionOutputChanges getTxOutChanges() {
return txOutChanges;
}
/**
* Get the full list of transactions if it is stored, otherwise null.
* Only one of this and getTxOutChanges() will return a non-null value.
*/
public List<StoredTransaction> getTransactions() {
return transactions;
}
/**
* Get the hash of the represented block
*/
public Sha256Hash getHash() {
return blockHash;
}
public int hashCode() {
return blockHash.hashCode();
}
public boolean equals(Object o) {
if (!(o instanceof StoredUndoableBlock)) return false;
return ((StoredUndoableBlock)o).getHash().equals(this.getHash());
}
@Override
public String toString() {
return "Undoable Block " + blockHash.toString();
}
}

View File

@ -136,6 +136,22 @@ 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.getScriptBytes(), output.getValue()));
}
}
/** /**
* Returns the transaction hash as you see them in the block explorer. * Returns the transaction hash as you see them in the block explorer.

View File

@ -114,10 +114,10 @@ public class TransactionOutput extends ChildMessage implements Serializable {
/** /**
* Used only in creation of the genesis blocks and in unit tests. * Used only in creation of the genesis blocks and in unit tests.
*/ */
TransactionOutput(NetworkParameters params, Transaction parent, byte[] scriptBytes) { TransactionOutput(NetworkParameters params, Transaction parent, byte[] scriptBytes, BigInteger value) {
super(params); super(params);
this.scriptBytes = scriptBytes; this.scriptBytes = scriptBytes;
this.value = Utils.toNanoCoins(50, 0); this.value = value;
parentTransaction = parent; parentTransaction = parent;
availableForSpending = true; availableForSpending = true;
} }

View File

@ -0,0 +1,38 @@
/**
* 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.List;
/**
* TransactionOutputChanges is used as a return value for BlockChainBase.connectInputs.
* It contains the full list of transaction outputs created and spent in a block.
* 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.
*/
public class TransactionOutputChanges implements Serializable {
private static final long serialVersionUID = -6169346729324181905L;
public final List<StoredTransactionOutput> txOutsCreated;
public final List<StoredTransactionOutput> txOutsSpent;
public TransactionOutputChanges(List<StoredTransactionOutput> txOutsCreated, List<StoredTransactionOutput> txOutsSpent) {
this.txOutsCreated = txOutsCreated;
this.txOutsSpent = txOutsSpent;
}
}