mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-01-30 23:02:15 +00:00
TransactionConfidence changes (coinbase phase 2) + Mike's feedback
This commit is contained in:
parent
d1c2dfecbe
commit
80f141cbf5
@ -100,6 +100,14 @@ message TransactionConfidence {
|
||||
// multiple transactions in the case of several inputs being re-spent by several transactions but we don't
|
||||
// bother to track them all, just the first. This only makes sense if type = OVERRIDDEN_BY_DOUBLE_SPEND.
|
||||
optional bytes overriding_transaction = 3;
|
||||
|
||||
// If type == BUILDING then this is the depth of the transaction in the blockchain.
|
||||
// Zero confirmations: depth = 0, one confirmation: depth = 1 etc.
|
||||
optional int32 depth = 4;
|
||||
|
||||
// If type == BUILDING then this is the cumulative workDone for the block the transaction appears in, together with
|
||||
// all blocks that bury it.
|
||||
optional int64 work_done = 5;
|
||||
}
|
||||
|
||||
/** A bitcoin transaction */
|
||||
|
@ -290,7 +290,7 @@ public class BlockChain {
|
||||
// can be updated to take into account the re-organize. We might also have received new coins we didn't have
|
||||
// before and our previous spends might have been undone.
|
||||
for (Wallet wallet : wallets) {
|
||||
wallet.reorganize(oldBlocks, newBlocks);
|
||||
wallet.reorganize(splitPoint, oldBlocks, newBlocks);
|
||||
}
|
||||
// Update the pointer to the best known block.
|
||||
setChainHead(newChainHead);
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.google.bitcoin.core;
|
||||
|
||||
import com.google.bitcoin.core.TransactionConfidence.ConfidenceType;
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -259,13 +260,23 @@ public class Transaction extends ChildMessage implements Serializable {
|
||||
if (bestChain && (updatedAt == null || updatedAt.getTime() == 0 || updatedAt.getTime() > blockTime)) {
|
||||
updatedAt = new Date(blockTime);
|
||||
}
|
||||
|
||||
|
||||
addBlockAppearance(block.getHeader().getHash());
|
||||
|
||||
if (bestChain) {
|
||||
// This can cause event listeners on TransactionConfidence to run. After this line completes, the wallets
|
||||
// This can cause event listeners on TransactionConfidence to run. After these lines complete, the wallets
|
||||
// state may have changed!
|
||||
getConfidence().setAppearedAtChainHeight(block.getHeight());
|
||||
TransactionConfidence transactionConfidence = getConfidence();
|
||||
transactionConfidence.setAppearedAtChainHeight(block.getHeight());
|
||||
|
||||
// Reset the confidence block depth.
|
||||
transactionConfidence.setDepthInBlocks(0);
|
||||
|
||||
// Reset the work done.
|
||||
transactionConfidence.setWorkDone(BigInteger.ZERO);
|
||||
|
||||
// The transaction is now on the best chain.
|
||||
transactionConfidence.setConfidenceType(ConfidenceType.BUILDING);
|
||||
}
|
||||
}
|
||||
|
||||
@ -278,7 +289,10 @@ public class Transaction extends ChildMessage implements Serializable {
|
||||
|
||||
/** Called by the wallet once a re-org means we don't appear in the best chain anymore. */
|
||||
void notifyNotOnBestChain() {
|
||||
getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.NOT_IN_BEST_CHAIN);
|
||||
TransactionConfidence transactionConfidence = getConfidence();
|
||||
transactionConfidence.setConfidenceType(TransactionConfidence.ConfidenceType.NOT_IN_BEST_CHAIN);
|
||||
transactionConfidence.setDepthInBlocks(0);
|
||||
transactionConfidence.setWorkDone(BigInteger.ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package com.google.bitcoin.core;
|
||||
|
||||
import com.google.bitcoin.store.BlockStoreException;
|
||||
import com.google.bitcoin.utils.EventListenerInvoker;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
@ -50,11 +49,9 @@ import java.util.Set;
|
||||
* <p>Alternatively, you may know that the transaction is "dead", that is, one or more of its inputs have
|
||||
* been double spent and will never confirm unless there is another re-org.</p>
|
||||
*
|
||||
* <p>TransactionConfidence is purely a data structure, it doesn't try and keep itself up to date. To have fresh
|
||||
* confidence data, you need to ensure the owning {@link Transaction} is being updated by something, like
|
||||
* a {@link Wallet}.</p>Confidence objects <b>are live</b> and can be updated by other threads running in parallel
|
||||
* to your own. To make a copy that won't be changed, use
|
||||
* {@link com.google.bitcoin.core.TransactionConfidence#duplicate()}.
|
||||
* <p>TransactionConfidence is updated via the {@link com.google.bitcoin.core.TransactionConfidence#notifyWorkDone()}
|
||||
* method to ensure the block depth and work done are up to date.</p>
|
||||
* To make a copy that won't be changed, use {@link com.google.bitcoin.core.TransactionConfidence#duplicate()}.
|
||||
*/
|
||||
public class TransactionConfidence implements Serializable {
|
||||
private static final long serialVersionUID = 4577920141400556444L;
|
||||
@ -70,6 +67,18 @@ public class TransactionConfidence implements Serializable {
|
||||
// Lazily created listeners array.
|
||||
private transient ArrayList<Listener> listeners;
|
||||
|
||||
/**
|
||||
* The depth of the transaction on the best chain in blocks. An unconfirmed block has depth 0, after one confirmation
|
||||
* its depth is 1.
|
||||
*/
|
||||
private int depth;
|
||||
|
||||
/**
|
||||
* The cumulative work done for the blocks that bury this transaction. BigInteger.ZERO if the transaction is not
|
||||
* on the best chain.
|
||||
*/
|
||||
private BigInteger workDone = BigInteger.ZERO;
|
||||
|
||||
// TODO: The advice below is a mess. There should be block chain listeners, see issue 94.
|
||||
/**
|
||||
* <p>Adds an event listener that will be run when this confidence object is updated. The listener will be locked and
|
||||
@ -278,6 +287,18 @@ public class TransactionConfidence implements Serializable {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the wallet when the tx appears on the best chain and a new block is added to the top.
|
||||
* Updates the internal counter that tracks how deeply buried the block is.
|
||||
* Work is the value of block.getWork().
|
||||
*/
|
||||
public synchronized void notifyWorkDone(Block block) throws VerificationException {
|
||||
if (getConfidenceType() == ConfidenceType.BUILDING) {
|
||||
this.depth++;
|
||||
this.workDone = this.workDone.add(block.getWork());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Depth in the chain is an approximation of how much time has elapsed since the transaction has been confirmed. On
|
||||
* average there is supposed to be a new block every 10 minutes, but the actual rate may vary. The reference
|
||||
@ -289,16 +310,21 @@ public class TransactionConfidence implements Serializable {
|
||||
* chain yet, throws IllegalStateException, so use {@link com.google.bitcoin.core.TransactionConfidence#getConfidenceType()}
|
||||
* to check first.
|
||||
*
|
||||
* @param chain a {@link BlockChain} instance.
|
||||
* @throws IllegalStateException if confidence type != BUILDING.
|
||||
* @return depth
|
||||
*/
|
||||
public synchronized int getDepthInBlocks(BlockChain chain) {
|
||||
public synchronized int getDepthInBlocks() {
|
||||
if (getConfidenceType() != ConfidenceType.BUILDING) {
|
||||
throw new IllegalStateException("Confidence type is not BUILDING");
|
||||
}
|
||||
int height = getAppearedAtChainHeight();
|
||||
return chain.getBestChainHeight() - height + 1;
|
||||
return depth;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the depth in blocks. Having one block confirmation is a depth of one.
|
||||
*/
|
||||
public synchronized void setDepthInBlocks(int depth) {
|
||||
this.depth = depth;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -307,24 +333,18 @@ public class TransactionConfidence implements Serializable {
|
||||
* blocks per hour by adjusting the difficulty target. So to know how much real computation effort is needed to
|
||||
* reverse a transaction, counting blocks is not enough.
|
||||
*
|
||||
* @param chain
|
||||
* @throws IllegalStateException if confidence type is not BUILDING
|
||||
* @return estimated number of hashes needed to reverse the transaction.
|
||||
*/
|
||||
public synchronized BigInteger getWorkDone(BlockChain chain) throws BlockStoreException {
|
||||
int depth;
|
||||
synchronized (this) {
|
||||
if (getConfidenceType() != ConfidenceType.BUILDING)
|
||||
throw new IllegalStateException("Confidence type is " + getConfidenceType() + ", not BUILDING");
|
||||
depth = getDepthInBlocks(chain);
|
||||
public synchronized BigInteger getWorkDone() {
|
||||
if (getConfidenceType() != ConfidenceType.BUILDING) {
|
||||
throw new IllegalStateException("Confidence type is not BUILDING");
|
||||
}
|
||||
BigInteger work = BigInteger.ZERO;
|
||||
StoredBlock block = chain.getChainHead();
|
||||
for (; depth > 0; depth--) {
|
||||
work = work.add(block.getChainWork());
|
||||
block = block.getPrev(chain.blockStore);
|
||||
}
|
||||
return work;
|
||||
return workDone;
|
||||
}
|
||||
|
||||
public synchronized void setWorkDone(BigInteger workDone) {
|
||||
this.workDone = workDone;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.google.bitcoin.core;
|
||||
|
||||
import com.google.bitcoin.core.TransactionConfidence.ConfidenceType;
|
||||
import com.google.bitcoin.core.WalletTransaction.Pool;
|
||||
import com.google.bitcoin.store.WalletProtobufSerializer;
|
||||
import com.google.bitcoin.utils.EventListenerInvoker;
|
||||
@ -551,29 +552,39 @@ public class Wallet implements Serializable {
|
||||
|
||||
log.info("Balance is now: " + bitcoinValueToFriendlyString(getBalance()));
|
||||
|
||||
// Store the block hash
|
||||
if (bestChain) {
|
||||
if (block != null && block.getHeader() != null) {
|
||||
// Check to see if this block has been seen before
|
||||
Sha256Hash newBlockHash = block.getHeader().getHash();
|
||||
if (!newBlockHash.equals(getLastBlockSeenHash())) {
|
||||
// new hash
|
||||
setLastBlockSeenHash(newBlockHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WARNING: The code beyond this point can trigger event listeners on transaction confidence objects, which are
|
||||
// in turn allowed to re-enter the Wallet. This means we cannot assume anything about the state of the wallet
|
||||
// from now on. The balance just received may already be spent.
|
||||
|
||||
// Mark the tx as appearing in this block so we can find it later after a re-org. This also lets the
|
||||
// transaction update its confidence and timestamp bookkeeping data.
|
||||
// Mark the tx as appearing in this block so we can find it later after a re-org.
|
||||
if (block != null) {
|
||||
tx.setBlockAppearance(block, bestChain);
|
||||
invokeOnTransactionConfidenceChanged(tx);
|
||||
}
|
||||
|
||||
// If this is the first time the the block hash has been seen, store it and notify transactions of the block.
|
||||
if (bestChain) {
|
||||
if (block != null && block.getHeader() != null) {
|
||||
// Check to see if this block has been seen before.
|
||||
Sha256Hash newBlockHash = block.getHeader().getHash();
|
||||
|
||||
if (!newBlockHash.equals(getLastBlockSeenHash())) {
|
||||
// Store the new block hash.
|
||||
setLastBlockSeenHash(newBlockHash);
|
||||
|
||||
// Notify all the BUILDING transactions of the new block.
|
||||
// This is so that they can update their work done and depth.
|
||||
Set<Transaction> transactions = getTransactions(true, false);
|
||||
|
||||
for (Transaction t : transactions) {
|
||||
t.getConfidence().notifyWorkDone(block.getHeader());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the transaction confidence and timestamp bookkeeping data.
|
||||
invokeOnTransactionConfidenceChanged(tx);
|
||||
|
||||
// Inform anyone interested that we have received or sent coins but only if:
|
||||
// - This is not due to a re-org.
|
||||
// - The coins appeared on the best chain.
|
||||
@ -598,7 +609,7 @@ public class Wallet implements Serializable {
|
||||
invokeOnCoinsSent(tx, prevBalance, newBalance);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
checkState(isConsistent());
|
||||
}
|
||||
|
||||
@ -789,7 +800,6 @@ public class Wallet implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns a set of all transactions in the wallet.
|
||||
*
|
||||
* @param includeDead If true, transactions that were overridden by a double spend are included.
|
||||
* @param includeInactive If true, transactions that are on side chains (are unspendable) are included.
|
||||
*/
|
||||
@ -1370,7 +1380,7 @@ public class Wallet implements Serializable {
|
||||
*
|
||||
* The oldBlocks/newBlocks lists are ordered height-wise from top first to bottom last.
|
||||
*/
|
||||
synchronized void reorganize(List<StoredBlock> oldBlocks, List<StoredBlock> newBlocks) throws VerificationException {
|
||||
synchronized void reorganize(StoredBlock splitPoint, List<StoredBlock> oldBlocks, List<StoredBlock> newBlocks) throws VerificationException {
|
||||
// This runs on any peer thread with the block chain synchronized.
|
||||
//
|
||||
// The reorganize functionality of the wallet is tested in ChainSplitTests.
|
||||
@ -1486,9 +1496,32 @@ public class Wallet implements Serializable {
|
||||
// Now replay the act of receiving the blocks that were previously in a side chain. This will:
|
||||
// - Move any transactions that were pending and are now accepted into the right bucket.
|
||||
// - Connect the newly active transactions.
|
||||
|
||||
Collections.reverse(newBlocks); // Need bottom-to-top but we get top-to-bottom.
|
||||
|
||||
// The old blocks have contributed to the depth and work done for all the transactions in the
|
||||
// wallet that are in blocks up to and including the chain split block.
|
||||
// The total depth and work done is calculated here and then subtracted from the appropriate transactions.
|
||||
int depthToSubtract = oldBlocks == null ? 0 : oldBlocks.size();
|
||||
|
||||
BigInteger workDoneToSubtract = BigInteger.ZERO;
|
||||
for (StoredBlock b : oldBlocks) {
|
||||
workDoneToSubtract = workDoneToSubtract.add(b.getHeader().getWork());
|
||||
}
|
||||
log.info("DepthToSubtract = " + depthToSubtract + ", workDoneToSubtract = " + workDoneToSubtract);
|
||||
|
||||
// Remove depthToSubtract and workDoneToSubtract from all transactions in the wallet except for pending and inactive
|
||||
// (i.e. the transactions in the two chains of blocks we are reorganising).
|
||||
subtractDepthAndWorkDone(depthToSubtract, workDoneToSubtract, spent.values());
|
||||
subtractDepthAndWorkDone(depthToSubtract, workDoneToSubtract, unspent.values());
|
||||
subtractDepthAndWorkDone(depthToSubtract, workDoneToSubtract, dead.values());
|
||||
|
||||
// The effective last seen block is now the split point so set the lastSeenBlockHash.
|
||||
setLastBlockSeenHash(splitPoint.getHeader().getHash());
|
||||
|
||||
for (StoredBlock b : newBlocks) {
|
||||
log.info("Replaying block {}", b.getHeader().getHashAsString());
|
||||
|
||||
Set<Transaction> txns = new HashSet<Transaction>();
|
||||
Sha256Hash blockHash = b.getHeader().getHash();
|
||||
for (Transaction tx : newChainTransactions.values()) {
|
||||
@ -1497,11 +1530,26 @@ public class Wallet implements Serializable {
|
||||
log.info(" containing tx {}", tx.getHashAsString());
|
||||
}
|
||||
}
|
||||
for (Transaction t : txns) {
|
||||
try {
|
||||
receive(t, b, BlockChain.NewBlockType.BEST_CHAIN, true);
|
||||
} catch (ScriptException e) {
|
||||
throw new RuntimeException(e); // Cannot happen as these blocks were already verified.
|
||||
|
||||
if (txns.isEmpty()) {
|
||||
// If there are no new transactions in this block we still need to bury existing transactions one block deeper.
|
||||
for (Transaction t : spent.values()) {
|
||||
t.getConfidence().notifyWorkDone(b.getHeader());
|
||||
}
|
||||
for (Transaction t : unspent.values()) {
|
||||
t.getConfidence().notifyWorkDone(b.getHeader());
|
||||
}
|
||||
for (Transaction t : dead.values()) {
|
||||
t.getConfidence().notifyWorkDone(b.getHeader());
|
||||
}
|
||||
} else {
|
||||
// Add the transactions to the new blocks.
|
||||
for (Transaction t : txns) {
|
||||
try {
|
||||
receive(t, b, BlockChain.NewBlockType.BEST_CHAIN, true);
|
||||
} catch (ScriptException e) {
|
||||
throw new RuntimeException(e); // Cannot happen as these blocks were already verified.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1550,6 +1598,18 @@ public class Wallet implements Serializable {
|
||||
checkState(isConsistent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract the supplied depth and work done from the given transactions.
|
||||
*/
|
||||
synchronized private void subtractDepthAndWorkDone(int depthToSubtract, BigInteger workDoneToSubtract, Collection<Transaction> transactions) {
|
||||
for (Transaction tx : transactions) {
|
||||
if (tx.getConfidence().getConfidenceType() == ConfidenceType.BUILDING) {
|
||||
tx.getConfidence().setDepthInBlocks(tx.getConfidence().getDepthInBlocks() - depthToSubtract);
|
||||
tx.getConfidence().setWorkDone(tx.getConfidence().getWorkDone().subtract(workDoneToSubtract));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reprocessUnincludedTxAfterReorg(Map<Sha256Hash, Transaction> pool, Transaction tx) {
|
||||
log.info("TX {}", tx.getHashAsString());
|
||||
int numInputs = tx.getInputs().size();
|
||||
|
@ -201,6 +201,10 @@ public class WalletProtobufSerializer {
|
||||
confidenceBuilder.setType(Protos.TransactionConfidence.Type.valueOf(confidence.getConfidenceType().getValue()));
|
||||
if (confidence.getConfidenceType() == ConfidenceType.BUILDING) {
|
||||
confidenceBuilder.setAppearedAtHeight(confidence.getAppearedAtChainHeight());
|
||||
confidenceBuilder.setDepth(confidence.getDepthInBlocks());
|
||||
if (confidence.getWorkDone() != null) {
|
||||
confidenceBuilder.setWorkDone(confidence.getWorkDone().longValue());
|
||||
}
|
||||
}
|
||||
if (confidence.getConfidenceType() == ConfidenceType.OVERRIDDEN_BY_DOUBLE_SPEND) {
|
||||
Sha256Hash overridingHash = confidence.getOverridingTransaction().getHash();
|
||||
@ -361,6 +365,20 @@ public class WalletProtobufSerializer {
|
||||
}
|
||||
confidence.setAppearedAtChainHeight(confidenceProto.getAppearedAtHeight());
|
||||
}
|
||||
if (confidenceProto.hasDepth()) {
|
||||
if (confidence.getConfidenceType() != ConfidenceType.BUILDING) {
|
||||
log.warn("Have depth but not BUILDING for tx {}", tx.getHashAsString());
|
||||
return;
|
||||
}
|
||||
confidence.setDepthInBlocks(confidenceProto.getDepth());
|
||||
}
|
||||
if (confidenceProto.hasWorkDone()) {
|
||||
if (confidence.getConfidenceType() != ConfidenceType.BUILDING) {
|
||||
log.warn("Have workDone but not BUILDING for tx {}", tx.getHashAsString());
|
||||
return;
|
||||
}
|
||||
confidence.setWorkDone(BigInteger.valueOf(confidenceProto.getWorkDone()));
|
||||
}
|
||||
if (confidenceProto.hasOverridingTransaction()) {
|
||||
if (confidence.getConfidenceType() != ConfidenceType.OVERRIDDEN_BY_DOUBLE_SPEND) {
|
||||
log.warn("Have overridingTransaction but not OVERRIDDEN for tx {}", tx.getHashAsString());
|
||||
|
@ -1784,6 +1784,14 @@ public final class Protos {
|
||||
// optional bytes overriding_transaction = 3;
|
||||
boolean hasOverridingTransaction();
|
||||
com.google.protobuf.ByteString getOverridingTransaction();
|
||||
|
||||
// optional int32 depth = 4;
|
||||
boolean hasDepth();
|
||||
int getDepth();
|
||||
|
||||
// optional int64 work_done = 5;
|
||||
boolean hasWorkDone();
|
||||
long getWorkDone();
|
||||
}
|
||||
public static final class TransactionConfidence extends
|
||||
com.google.protobuf.GeneratedMessage
|
||||
@ -1922,10 +1930,32 @@ public final class Protos {
|
||||
return overridingTransaction_;
|
||||
}
|
||||
|
||||
// optional int32 depth = 4;
|
||||
public static final int DEPTH_FIELD_NUMBER = 4;
|
||||
private int depth_;
|
||||
public boolean hasDepth() {
|
||||
return ((bitField0_ & 0x00000008) == 0x00000008);
|
||||
}
|
||||
public int getDepth() {
|
||||
return depth_;
|
||||
}
|
||||
|
||||
// optional int64 work_done = 5;
|
||||
public static final int WORK_DONE_FIELD_NUMBER = 5;
|
||||
private long workDone_;
|
||||
public boolean hasWorkDone() {
|
||||
return ((bitField0_ & 0x00000010) == 0x00000010);
|
||||
}
|
||||
public long getWorkDone() {
|
||||
return workDone_;
|
||||
}
|
||||
|
||||
private void initFields() {
|
||||
type_ = org.bitcoinj.wallet.Protos.TransactionConfidence.Type.UNKNOWN;
|
||||
appearedAtHeight_ = 0;
|
||||
overridingTransaction_ = com.google.protobuf.ByteString.EMPTY;
|
||||
depth_ = 0;
|
||||
workDone_ = 0L;
|
||||
}
|
||||
private byte memoizedIsInitialized = -1;
|
||||
public final boolean isInitialized() {
|
||||
@ -1948,6 +1978,12 @@ public final class Protos {
|
||||
if (((bitField0_ & 0x00000004) == 0x00000004)) {
|
||||
output.writeBytes(3, overridingTransaction_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000008) == 0x00000008)) {
|
||||
output.writeInt32(4, depth_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000010) == 0x00000010)) {
|
||||
output.writeInt64(5, workDone_);
|
||||
}
|
||||
getUnknownFields().writeTo(output);
|
||||
}
|
||||
|
||||
@ -1969,6 +2005,14 @@ public final class Protos {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeBytesSize(3, overridingTransaction_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000008) == 0x00000008)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeInt32Size(4, depth_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000010) == 0x00000010)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeInt64Size(5, workDone_);
|
||||
}
|
||||
size += getUnknownFields().getSerializedSize();
|
||||
memoizedSerializedSize = size;
|
||||
return size;
|
||||
@ -2099,6 +2143,10 @@ public final class Protos {
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
overridingTransaction_ = com.google.protobuf.ByteString.EMPTY;
|
||||
bitField0_ = (bitField0_ & ~0x00000004);
|
||||
depth_ = 0;
|
||||
bitField0_ = (bitField0_ & ~0x00000008);
|
||||
workDone_ = 0L;
|
||||
bitField0_ = (bitField0_ & ~0x00000010);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -2149,6 +2197,14 @@ public final class Protos {
|
||||
to_bitField0_ |= 0x00000004;
|
||||
}
|
||||
result.overridingTransaction_ = overridingTransaction_;
|
||||
if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
|
||||
to_bitField0_ |= 0x00000008;
|
||||
}
|
||||
result.depth_ = depth_;
|
||||
if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
|
||||
to_bitField0_ |= 0x00000010;
|
||||
}
|
||||
result.workDone_ = workDone_;
|
||||
result.bitField0_ = to_bitField0_;
|
||||
onBuilt();
|
||||
return result;
|
||||
@ -2174,6 +2230,12 @@ public final class Protos {
|
||||
if (other.hasOverridingTransaction()) {
|
||||
setOverridingTransaction(other.getOverridingTransaction());
|
||||
}
|
||||
if (other.hasDepth()) {
|
||||
setDepth(other.getDepth());
|
||||
}
|
||||
if (other.hasWorkDone()) {
|
||||
setWorkDone(other.getWorkDone());
|
||||
}
|
||||
this.mergeUnknownFields(other.getUnknownFields());
|
||||
return this;
|
||||
}
|
||||
@ -2226,6 +2288,16 @@ public final class Protos {
|
||||
overridingTransaction_ = input.readBytes();
|
||||
break;
|
||||
}
|
||||
case 32: {
|
||||
bitField0_ |= 0x00000008;
|
||||
depth_ = input.readInt32();
|
||||
break;
|
||||
}
|
||||
case 40: {
|
||||
bitField0_ |= 0x00000010;
|
||||
workDone_ = input.readInt64();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2301,6 +2373,48 @@ public final class Protos {
|
||||
return this;
|
||||
}
|
||||
|
||||
// optional int32 depth = 4;
|
||||
private int depth_ ;
|
||||
public boolean hasDepth() {
|
||||
return ((bitField0_ & 0x00000008) == 0x00000008);
|
||||
}
|
||||
public int getDepth() {
|
||||
return depth_;
|
||||
}
|
||||
public Builder setDepth(int value) {
|
||||
bitField0_ |= 0x00000008;
|
||||
depth_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
public Builder clearDepth() {
|
||||
bitField0_ = (bitField0_ & ~0x00000008);
|
||||
depth_ = 0;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
// optional int64 work_done = 5;
|
||||
private long workDone_ ;
|
||||
public boolean hasWorkDone() {
|
||||
return ((bitField0_ & 0x00000010) == 0x00000010);
|
||||
}
|
||||
public long getWorkDone() {
|
||||
return workDone_;
|
||||
}
|
||||
public Builder setWorkDone(long value) {
|
||||
bitField0_ |= 0x00000010;
|
||||
workDone_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
public Builder clearWorkDone() {
|
||||
bitField0_ = (bitField0_ & ~0x00000010);
|
||||
workDone_ = 0L;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(builder_scope:wallet.TransactionConfidence)
|
||||
}
|
||||
|
||||
@ -5673,30 +5787,31 @@ public final class Protos {
|
||||
"\003 \002(\014\022\020\n\010sequence\030\004 \001(\r\"\177\n\021TransactionOu" +
|
||||
"tput\022\r\n\005value\030\001 \002(\003\022\024\n\014script_bytes\030\002 \002(" +
|
||||
"\014\022!\n\031spent_by_transaction_hash\030\003 \001(\014\022\"\n\032",
|
||||
"spent_by_transaction_index\030\004 \001(\005\"\366\001\n\025Tra" +
|
||||
"spent_by_transaction_index\030\004 \001(\005\"\230\002\n\025Tra" +
|
||||
"nsactionConfidence\0220\n\004type\030\001 \001(\0162\".walle" +
|
||||
"t.TransactionConfidence.Type\022\032\n\022appeared" +
|
||||
"_at_height\030\002 \001(\005\022\036\n\026overriding_transacti" +
|
||||
"on\030\003 \001(\014\"o\n\004Type\022\013\n\007UNKNOWN\020\000\022\014\n\010BUILDIN" +
|
||||
"G\020\001\022\025\n\021NOT_SEEN_IN_CHAIN\020\002\022\025\n\021NOT_IN_BES" +
|
||||
"T_CHAIN\020\003\022\036\n\032OVERRIDDEN_BY_DOUBLE_SPEND\020" +
|
||||
"\004\"\211\003\n\013Transaction\022\017\n\007version\030\001 \002(\005\022\014\n\004ha" +
|
||||
"sh\030\002 \002(\014\022&\n\004pool\030\003 \001(\0162\030.wallet.Transact" +
|
||||
"ion.Pool\022\021\n\tlock_time\030\004 \001(\r\022\022\n\nupdated_a",
|
||||
"t\030\005 \001(\003\0223\n\021transaction_input\030\006 \003(\0132\030.wal" +
|
||||
"let.TransactionInput\0225\n\022transaction_outp" +
|
||||
"ut\030\007 \003(\0132\031.wallet.TransactionOutput\022\022\n\nb" +
|
||||
"lock_hash\030\010 \003(\014\0221\n\nconfidence\030\t \001(\0132\035.wa" +
|
||||
"llet.TransactionConfidence\"Y\n\004Pool\022\013\n\007UN" +
|
||||
"SPENT\020\004\022\t\n\005SPENT\020\005\022\014\n\010INACTIVE\020\002\022\010\n\004DEAD" +
|
||||
"\020\n\022\013\n\007PENDING\020\020\022\024\n\020PENDING_INACTIVE\020\022\"8\n" +
|
||||
"\tExtension\022\n\n\002id\030\001 \002(\t\022\014\n\004data\030\002 \002(\014\022\021\n\t" +
|
||||
"mandatory\030\003 \002(\010\"\254\001\n\006Wallet\022\032\n\022network_id" +
|
||||
"entifier\030\001 \002(\t\022\034\n\024last_seen_block_hash\030\002",
|
||||
" \001(\014\022\030\n\003key\030\003 \003(\0132\013.wallet.Key\022(\n\013transa" +
|
||||
"ction\030\004 \003(\0132\023.wallet.Transaction\022$\n\texte" +
|
||||
"nsion\030\n \003(\0132\021.wallet.ExtensionB\035\n\023org.bi" +
|
||||
"tcoinj.walletB\006Protos"
|
||||
"on\030\003 \001(\014\022\r\n\005depth\030\004 \001(\005\022\021\n\twork_done\030\005 \001" +
|
||||
"(\003\"o\n\004Type\022\013\n\007UNKNOWN\020\000\022\014\n\010BUILDING\020\001\022\025\n" +
|
||||
"\021NOT_SEEN_IN_CHAIN\020\002\022\025\n\021NOT_IN_BEST_CHAI" +
|
||||
"N\020\003\022\036\n\032OVERRIDDEN_BY_DOUBLE_SPEND\020\004\"\211\003\n\013" +
|
||||
"Transaction\022\017\n\007version\030\001 \002(\005\022\014\n\004hash\030\002 \002" +
|
||||
"(\014\022&\n\004pool\030\003 \001(\0162\030.wallet.Transaction.Po",
|
||||
"ol\022\021\n\tlock_time\030\004 \001(\r\022\022\n\nupdated_at\030\005 \001(" +
|
||||
"\003\0223\n\021transaction_input\030\006 \003(\0132\030.wallet.Tr" +
|
||||
"ansactionInput\0225\n\022transaction_output\030\007 \003" +
|
||||
"(\0132\031.wallet.TransactionOutput\022\022\n\nblock_h" +
|
||||
"ash\030\010 \003(\014\0221\n\nconfidence\030\t \001(\0132\035.wallet.T" +
|
||||
"ransactionConfidence\"Y\n\004Pool\022\013\n\007UNSPENT\020" +
|
||||
"\004\022\t\n\005SPENT\020\005\022\014\n\010INACTIVE\020\002\022\010\n\004DEAD\020\n\022\013\n\007" +
|
||||
"PENDING\020\020\022\024\n\020PENDING_INACTIVE\020\022\"8\n\tExten" +
|
||||
"sion\022\n\n\002id\030\001 \002(\t\022\014\n\004data\030\002 \002(\014\022\021\n\tmandat" +
|
||||
"ory\030\003 \002(\010\"\254\001\n\006Wallet\022\032\n\022network_identifi",
|
||||
"er\030\001 \002(\t\022\034\n\024last_seen_block_hash\030\002 \001(\014\022\030" +
|
||||
"\n\003key\030\003 \003(\0132\013.wallet.Key\022(\n\013transaction\030" +
|
||||
"\004 \003(\0132\023.wallet.Transaction\022$\n\textension\030" +
|
||||
"\n \003(\0132\021.wallet.ExtensionB\035\n\023org.bitcoinj" +
|
||||
".walletB\006Protos"
|
||||
};
|
||||
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
|
||||
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
|
||||
@ -5732,7 +5847,7 @@ public final class Protos {
|
||||
internal_static_wallet_TransactionConfidence_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_wallet_TransactionConfidence_descriptor,
|
||||
new java.lang.String[] { "Type", "AppearedAtHeight", "OverridingTransaction", },
|
||||
new java.lang.String[] { "Type", "AppearedAtHeight", "OverridingTransaction", "Depth", "WorkDone", },
|
||||
org.bitcoinj.wallet.Protos.TransactionConfidence.class,
|
||||
org.bitcoinj.wallet.Protos.TransactionConfidence.Builder.class);
|
||||
internal_static_wallet_Transaction_descriptor =
|
||||
|
@ -26,11 +26,12 @@ import java.util.ArrayList;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ChainSplitTests {
|
||||
public class ChainSplitTest {
|
||||
private NetworkParameters unitTestParams;
|
||||
private Wallet wallet;
|
||||
private BlockChain chain;
|
||||
private Address coinsTo;
|
||||
private Address coinsTo2;
|
||||
private Address someOtherGuy;
|
||||
|
||||
@Before
|
||||
@ -39,8 +40,10 @@ public class ChainSplitTests {
|
||||
unitTestParams = NetworkParameters.unitTests();
|
||||
wallet = new Wallet(unitTestParams);
|
||||
wallet.addKey(new ECKey());
|
||||
wallet.addKey(new ECKey());
|
||||
chain = new BlockChain(unitTestParams, wallet, new MemoryBlockStore(unitTestParams));
|
||||
coinsTo = wallet.keychain.get(0).toAddress(unitTestParams);
|
||||
coinsTo2 = wallet.keychain.get(1).toAddress(unitTestParams);
|
||||
someOtherGuy = new ECKey().toAddress(unitTestParams);
|
||||
}
|
||||
|
||||
@ -283,7 +286,7 @@ public class ChainSplitTests {
|
||||
public void txConfidenceLevels() throws Exception {
|
||||
// Check that as the chain forks and re-orgs, the confidence data associated with each transaction is
|
||||
// maintained correctly.
|
||||
final ArrayList<Transaction> txns = new ArrayList<Transaction>(2);
|
||||
final ArrayList<Transaction> txns = new ArrayList<Transaction>(3);
|
||||
wallet.addEventListener(new AbstractWalletEventListener() {
|
||||
@Override
|
||||
public void onCoinsReceived(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance) {
|
||||
@ -291,62 +294,125 @@ public class ChainSplitTests {
|
||||
}
|
||||
});
|
||||
|
||||
// Start by building a couple of blocks on top of the genesis block.
|
||||
// Start by building three blocks on top of the genesis block.
|
||||
Block b1 = unitTestParams.genesisBlock.createNextBlock(coinsTo);
|
||||
Block b2 = b1.createNextBlock(coinsTo);
|
||||
BigInteger work1 = b1.getWork();
|
||||
|
||||
Block b2 = b1.createNextBlock(coinsTo2);
|
||||
BigInteger work2 = b2.getWork();
|
||||
|
||||
Block b3 = b2.createNextBlock(coinsTo2);
|
||||
BigInteger work3 = b3.getWork();
|
||||
|
||||
assertTrue(chain.add(b1));
|
||||
assertTrue(chain.add(b2));
|
||||
assertTrue(chain.add(b3));
|
||||
|
||||
// Check the transaction confidence levels are correct.
|
||||
assertEquals(2, txns.size());
|
||||
assertEquals(3, txns.size());
|
||||
|
||||
assertEquals(1, txns.get(0).getConfidence().getAppearedAtChainHeight());
|
||||
assertEquals(2, txns.get(1).getConfidence().getAppearedAtChainHeight());
|
||||
assertEquals(1, txns.get(1).getConfidence().getDepthInBlocks(chain));
|
||||
assertEquals(2, txns.get(0).getConfidence().getDepthInBlocks(chain));
|
||||
assertEquals(10, txns.get(0).getConfidence().getWorkDone(chain).intValue());
|
||||
assertEquals(6, txns.get(1).getConfidence().getWorkDone(chain).intValue());
|
||||
assertEquals(3, txns.get(2).getConfidence().getAppearedAtChainHeight());
|
||||
|
||||
assertEquals(3, txns.get(0).getConfidence().getDepthInBlocks());
|
||||
assertEquals(2, txns.get(1).getConfidence().getDepthInBlocks());
|
||||
assertEquals(1, txns.get(2).getConfidence().getDepthInBlocks());
|
||||
|
||||
assertEquals(work1.add(work2).add(work3), txns.get(0).getConfidence().getWorkDone());
|
||||
assertEquals(work2.add(work3), txns.get(1).getConfidence().getWorkDone());
|
||||
assertEquals(work3, txns.get(2).getConfidence().getWorkDone());
|
||||
|
||||
// We now have the following chain:
|
||||
// genesis -> b1 -> b2
|
||||
// genesis -> b1 -> b2 -> b3
|
||||
//
|
||||
// so fork like this:
|
||||
//
|
||||
// genesis -> b1 -> b2
|
||||
// \-> b3
|
||||
// genesis -> b1 -> b2 -> b3
|
||||
// \-> b4 -> b5
|
||||
//
|
||||
// Nothing should happen at this point. We saw b2 first so it takes priority.
|
||||
Block b3 = b1.createNextBlock(someOtherGuy);
|
||||
assertTrue(chain.add(b3));
|
||||
assertEquals(2, txns.size());
|
||||
// Nothing should happen at this point. We saw b2 and b3 first so it takes priority.
|
||||
Block b4 = b1.createNextBlock(someOtherGuy);
|
||||
BigInteger work4 = b4.getWork();
|
||||
|
||||
Block b5 = b4.createNextBlock(someOtherGuy);
|
||||
BigInteger work5 = b5.getWork();
|
||||
|
||||
assertTrue(chain.add(b4));
|
||||
assertTrue(chain.add(b5));
|
||||
assertEquals(3, txns.size());
|
||||
|
||||
assertEquals(1, txns.get(0).getConfidence().getAppearedAtChainHeight());
|
||||
assertEquals(2, txns.get(1).getConfidence().getAppearedAtChainHeight());
|
||||
assertEquals(3, txns.get(2).getConfidence().getAppearedAtChainHeight());
|
||||
|
||||
assertEquals(3, txns.get(0).getConfidence().getDepthInBlocks());
|
||||
assertEquals(2, txns.get(1).getConfidence().getDepthInBlocks());
|
||||
assertEquals(1, txns.get(2).getConfidence().getDepthInBlocks());
|
||||
|
||||
assertEquals(work1.add(work2).add(work3), txns.get(0).getConfidence().getWorkDone());
|
||||
assertEquals(work2.add(work3), txns.get(1).getConfidence().getWorkDone());
|
||||
assertEquals(work3, txns.get(2).getConfidence().getWorkDone());
|
||||
|
||||
// Now we add another block to make the alternative chain longer.
|
||||
assertTrue(chain.add(b3.createNextBlock(someOtherGuy)));
|
||||
Block b6 = b5.createNextBlock(someOtherGuy);
|
||||
BigInteger work6 = b6.getWork();
|
||||
assertTrue(chain.add(b6));
|
||||
//
|
||||
// genesis -> b1 -> b2
|
||||
// \-> b3 -> b4
|
||||
// genesis -> b1 -> b2 -> b3
|
||||
// \-> b4 -> b5 -> b6
|
||||
//
|
||||
assertEquals(2, txns.size());
|
||||
|
||||
assertEquals(3, txns.size());
|
||||
assertEquals(1, txns.get(0).getConfidence().getAppearedAtChainHeight());
|
||||
assertEquals(4, txns.get(0).getConfidence().getDepthInBlocks());
|
||||
assertEquals(work1.add(work4).add(work5).add(work6), txns.get(0).getConfidence().getWorkDone());
|
||||
|
||||
// Transaction 1 (in block b2) is now on a side chain.
|
||||
assertEquals(TransactionConfidence.ConfidenceType.NOT_IN_BEST_CHAIN, txns.get(1).getConfidence().getConfidenceType());
|
||||
try {
|
||||
txns.get(1).getConfidence().getAppearedAtChainHeight();
|
||||
fail();
|
||||
} catch (IllegalStateException e) {}
|
||||
try {
|
||||
txns.get(1).getConfidence().getDepthInBlocks();
|
||||
fail();
|
||||
} catch (IllegalStateException e) {}
|
||||
try {
|
||||
txns.get(1).getConfidence().getWorkDone();
|
||||
fail();
|
||||
} catch (IllegalStateException e) {}
|
||||
// ... and back to the first chain.
|
||||
Block b5 = b2.createNextBlock(coinsTo);
|
||||
Block b6 = b5.createNextBlock(coinsTo);
|
||||
assertTrue(chain.add(b5));
|
||||
assertTrue(chain.add(b6));
|
||||
Block b7 = b3.createNextBlock(coinsTo);
|
||||
BigInteger work7 = b7.getWork();
|
||||
|
||||
Block b8 = b7.createNextBlock(coinsTo);
|
||||
BigInteger work8 = b7.getWork();
|
||||
|
||||
assertTrue(chain.add(b7));
|
||||
|
||||
assertTrue(chain.add(b8));
|
||||
//
|
||||
// genesis -> b1 -> b2 -> b5 -> b6
|
||||
// \-> b3 -> b4
|
||||
// genesis -> b1 -> b2 -> b3 -> b7 -> b8
|
||||
// \-> b4 -> b5 -> b6
|
||||
//
|
||||
|
||||
// This should be enabled, once we figure out the best way to inform the user of how the wallet is changing
|
||||
// during the re-org.
|
||||
// assertEquals(4, txns.size());
|
||||
//assertEquals(5, txns.size());
|
||||
|
||||
assertEquals(1, txns.get(0).getConfidence().getAppearedAtChainHeight());
|
||||
assertEquals(2, txns.get(1).getConfidence().getAppearedAtChainHeight());
|
||||
assertEquals("200.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
assertEquals(3, txns.get(2).getConfidence().getAppearedAtChainHeight());
|
||||
|
||||
assertEquals(5, txns.get(0).getConfidence().getDepthInBlocks());
|
||||
assertEquals(4, txns.get(1).getConfidence().getDepthInBlocks());
|
||||
assertEquals(3, txns.get(2).getConfidence().getDepthInBlocks());
|
||||
|
||||
assertEquals(work1.add(work2).add(work3).add(work7).add(work8), txns.get(0).getConfidence().getWorkDone());
|
||||
assertEquals(work2.add(work3).add(work7).add(work8), txns.get(1).getConfidence().getWorkDone());
|
||||
assertEquals(work3.add(work7).add(work8), txns.get(2).getConfidence().getWorkDone());
|
||||
|
||||
assertEquals("250.00", Utils.bitcoinValueToFriendlyString(wallet.getBalance()));
|
||||
}
|
||||
}
|
@ -653,7 +653,6 @@ public class WalletTest {
|
||||
// Now check we can serialize old wallets to protocol buffers. Covers bug 134.
|
||||
bios.reset();
|
||||
new WalletProtobufSerializer().writeWallet(wallet, bios);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -17,6 +17,9 @@ import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.bitcoin.core.TestUtils.createFakeTx;
|
||||
import static org.junit.Assert.*;
|
||||
@ -128,7 +131,7 @@ public class WalletProtobufSerializerTest {
|
||||
Sha256Hash blockHash = block.getHash();
|
||||
wallet.setLastBlockSeenHash(blockHash);
|
||||
|
||||
// Roundtrip the wallet and check it has stored te blockHash.
|
||||
// Roundtrip the wallet and check it has stored the blockHash.
|
||||
Wallet wallet1 = roundTrip(wallet);
|
||||
assertEquals(blockHash, wallet1.getLastBlockSeenHash());
|
||||
|
||||
@ -139,6 +142,83 @@ public class WalletProtobufSerializerTest {
|
||||
assertEquals(genesisBlock.getHash(), wallet2.getLastBlockSeenHash());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppearedAtChainHeightDepthAndWorkDone() throws Exception {
|
||||
// Test the TransactionConfidence appearedAtChainHeight, depth and workDone field are stored.
|
||||
|
||||
BlockChain chain = new BlockChain(params, myWallet, new MemoryBlockStore(params));
|
||||
|
||||
final ArrayList<Transaction> txns = new ArrayList<Transaction>(2);
|
||||
myWallet.addEventListener(new AbstractWalletEventListener() {
|
||||
@Override
|
||||
public void onCoinsReceived(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance) {
|
||||
txns.add(tx);
|
||||
}
|
||||
});
|
||||
|
||||
// Start by building two blocks on top of the genesis block.
|
||||
Block b1 = params.genesisBlock.createNextBlock(myAddress);
|
||||
BigInteger work1 = b1.getWork();
|
||||
assertTrue(work1.compareTo(BigInteger.ZERO) > 0);
|
||||
|
||||
Block b2 = b1.createNextBlock(myAddress);
|
||||
BigInteger work2 = b2.getWork();
|
||||
assertTrue(work2.compareTo(BigInteger.ZERO) > 0);
|
||||
|
||||
assertTrue(chain.add(b1));
|
||||
assertTrue(chain.add(b2));
|
||||
|
||||
// We now have the following chain:
|
||||
// genesis -> b1 -> b2
|
||||
|
||||
// Check the transaction confidence levels are correct before wallet roundtrip.
|
||||
assertEquals(2, txns.size());
|
||||
|
||||
TransactionConfidence confidence0 = txns.get(0).getConfidence();
|
||||
TransactionConfidence confidence1 = txns.get(1).getConfidence();
|
||||
|
||||
assertEquals(1, confidence0.getAppearedAtChainHeight());
|
||||
assertEquals(2, confidence1.getAppearedAtChainHeight());
|
||||
|
||||
assertEquals(2, confidence0.getDepthInBlocks());
|
||||
assertEquals(1, confidence1.getDepthInBlocks());
|
||||
|
||||
assertEquals(work1.add(work2), confidence0.getWorkDone());
|
||||
assertEquals(work2, confidence1.getWorkDone());
|
||||
|
||||
// Roundtrip the wallet and check it has stored the depth and workDone.
|
||||
Wallet rebornWallet = roundTrip(myWallet);
|
||||
|
||||
Set<Transaction> rebornTxns = rebornWallet.getTransactions(false, false);
|
||||
assertEquals(2, rebornTxns.size());
|
||||
|
||||
// The transactions are not guaranteed to be in the same order so sort them to be in chain height order if required.
|
||||
Iterator<Transaction> it = rebornTxns.iterator();
|
||||
Transaction txA = it.next();
|
||||
Transaction txB = it.next();
|
||||
|
||||
Transaction rebornTx0, rebornTx1;
|
||||
if (txA.getConfidence().getAppearedAtChainHeight() == 1) {
|
||||
rebornTx0 = txA;
|
||||
rebornTx1 = txB;
|
||||
} else {
|
||||
rebornTx0 = txB;
|
||||
rebornTx1 = txA;
|
||||
}
|
||||
|
||||
TransactionConfidence rebornConfidence0 = rebornTx0.getConfidence();
|
||||
TransactionConfidence rebornConfidence1 = rebornTx1.getConfidence();
|
||||
|
||||
assertEquals(1, rebornConfidence0.getAppearedAtChainHeight());
|
||||
assertEquals(2, rebornConfidence1.getAppearedAtChainHeight());
|
||||
|
||||
assertEquals(2, rebornConfidence0.getDepthInBlocks());
|
||||
assertEquals(1, rebornConfidence1.getDepthInBlocks());
|
||||
|
||||
assertEquals(work1.add(work2), rebornConfidence0.getWorkDone());
|
||||
assertEquals(work2, rebornConfidence1.getWorkDone());
|
||||
}
|
||||
|
||||
private Wallet roundTrip(Wallet wallet) throws IOException {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
//System.out.println(WalletProtobufSerializer.walletToText(wallet));
|
||||
|
@ -125,13 +125,9 @@ public class PingService {
|
||||
System.out.println("Confidences of wallet transactions:");
|
||||
for (Transaction tx : transactions) {
|
||||
System.out.println(tx);
|
||||
try {
|
||||
System.out.println(tx.getConfidence());
|
||||
if (tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING)
|
||||
System.out.println("Work done: " + tx.getConfidence().getWorkDone(chain).toString());
|
||||
} catch (BlockStoreException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
System.out.println(tx.getConfidence());
|
||||
if (tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING)
|
||||
System.out.println("Work done: " + tx.getConfidence().getWorkDone().toString());
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user