mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-01-30 23:02:15 +00:00
coinbase-tx phase1 complete (add blockheight to wallet and protobuf)
This commit is contained in:
parent
23a960e0f4
commit
6c31abd698
@ -144,6 +144,11 @@ public class Wallet implements Serializable {
|
||||
// this field) then we need to migrate.
|
||||
private boolean hasTransactionConfidences;
|
||||
|
||||
/**
|
||||
* The hash of the last block seen on the best chain
|
||||
*/
|
||||
private Sha256Hash lastBlockSeenHash;
|
||||
|
||||
transient private ArrayList<WalletEventListener> eventListeners;
|
||||
|
||||
/**
|
||||
@ -533,6 +538,18 @@ 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.
|
||||
@ -1640,4 +1657,12 @@ public class Wallet implements Serializable {
|
||||
}
|
||||
return peerEventListener;
|
||||
}
|
||||
|
||||
public Sha256Hash getLastBlockSeenHash() {
|
||||
return lastBlockSeenHash;
|
||||
}
|
||||
|
||||
public void setLastBlockSeenHash(Sha256Hash lastBlockSeenHash) {
|
||||
this.lastBlockSeenHash = lastBlockSeenHash;
|
||||
}
|
||||
}
|
||||
|
@ -90,9 +90,7 @@ public class WalletProtobufSerializer {
|
||||
*/
|
||||
public static Protos.Wallet walletToProto(Wallet wallet) {
|
||||
Protos.Wallet.Builder walletBuilder = Protos.Wallet.newBuilder();
|
||||
walletBuilder.setNetworkIdentifier(wallet.getNetworkParameters().getId())
|
||||
//.setLastSeenBlockHash(null) // TODO
|
||||
;
|
||||
walletBuilder.setNetworkIdentifier(wallet.getNetworkParameters().getId());
|
||||
for (WalletTransaction wtx : wallet.getWalletTransactions()) {
|
||||
Protos.Transaction txProto = makeTxProto(wtx);
|
||||
walletBuilder.addTransaction(txProto);
|
||||
@ -110,6 +108,12 @@ public class WalletProtobufSerializer {
|
||||
buf.setPublicKey(ByteString.copyFrom(key.getPubKey()));
|
||||
walletBuilder.addKey(buf);
|
||||
}
|
||||
|
||||
Sha256Hash lastSeenBlockHash = wallet.getLastBlockSeenHash();
|
||||
if (lastSeenBlockHash != null) {
|
||||
walletBuilder.setLastSeenBlockHash(hashToByteString(lastSeenBlockHash));
|
||||
}
|
||||
|
||||
return walletBuilder.build();
|
||||
}
|
||||
|
||||
@ -238,6 +242,13 @@ public class WalletProtobufSerializer {
|
||||
wallet.addWalletTransaction(wtx);
|
||||
}
|
||||
|
||||
// Update the lastBlockSeenHash.
|
||||
if (!walletProto.hasLastSeenBlockHash()) {
|
||||
wallet.setLastBlockSeenHash(null);
|
||||
} else {
|
||||
wallet.setLastBlockSeenHash(byteStringToHash(walletProto.getLastSeenBlockHash()));
|
||||
}
|
||||
|
||||
for (Protos.Extension extProto : walletProto.getExtensionList()) {
|
||||
if (extProto.getMandatory()) {
|
||||
throw new IllegalArgumentException("Did not understand a mandatory extension in the wallet");
|
||||
|
@ -2323,7 +2323,7 @@ public final class Protos {
|
||||
boolean hasHash();
|
||||
com.google.protobuf.ByteString getHash();
|
||||
|
||||
// required .wallet.Transaction.Pool pool = 3;
|
||||
// optional .wallet.Transaction.Pool pool = 3;
|
||||
boolean hasPool();
|
||||
org.bitcoinj.wallet.Protos.Transaction.Pool getPool();
|
||||
|
||||
@ -2495,7 +2495,7 @@ public final class Protos {
|
||||
return hash_;
|
||||
}
|
||||
|
||||
// required .wallet.Transaction.Pool pool = 3;
|
||||
// optional .wallet.Transaction.Pool pool = 3;
|
||||
public static final int POOL_FIELD_NUMBER = 3;
|
||||
private org.bitcoinj.wallet.Protos.Transaction.Pool pool_;
|
||||
public boolean hasPool() {
|
||||
@ -2618,10 +2618,6 @@ public final class Protos {
|
||||
memoizedIsInitialized = 0;
|
||||
return false;
|
||||
}
|
||||
if (!hasPool()) {
|
||||
memoizedIsInitialized = 0;
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < getTransactionInputCount(); i++) {
|
||||
if (!getTransactionInput(i).isInitialized()) {
|
||||
memoizedIsInitialized = 0;
|
||||
@ -3073,10 +3069,6 @@ public final class Protos {
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!hasPool()) {
|
||||
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < getTransactionInputCount(); i++) {
|
||||
if (!getTransactionInput(i).isInitialized()) {
|
||||
|
||||
@ -3223,7 +3215,7 @@ public final class Protos {
|
||||
return this;
|
||||
}
|
||||
|
||||
// required .wallet.Transaction.Pool pool = 3;
|
||||
// optional .wallet.Transaction.Pool pool = 3;
|
||||
private org.bitcoinj.wallet.Protos.Transaction.Pool pool_ = org.bitcoinj.wallet.Protos.Transaction.Pool.UNSPENT;
|
||||
public boolean hasPool() {
|
||||
return ((bitField0_ & 0x00000004) == 0x00000004);
|
||||
@ -5689,7 +5681,7 @@ public final class Protos {
|
||||
"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 \002(\0162\030.wallet.Transact" +
|
||||
"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" +
|
||||
|
@ -31,7 +31,7 @@ import static org.junit.Assert.*;
|
||||
public class BlockTest {
|
||||
static final NetworkParameters params = NetworkParameters.testNet();
|
||||
|
||||
static final byte[] blockBytes;
|
||||
public static final byte[] blockBytes;
|
||||
|
||||
static {
|
||||
// Block 00000000a6e5eb79dcec11897af55e90cd571a4335383a3ccfbc12ec81085935
|
||||
|
@ -19,10 +19,13 @@ package com.google.bitcoin.core;
|
||||
import com.google.bitcoin.store.BlockStore;
|
||||
import com.google.bitcoin.store.BlockStoreException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class TestUtils {
|
||||
public static Transaction createFakeTx(NetworkParameters params, BigInteger nanocoins, Address to) {
|
||||
public static Transaction createFakeTx(NetworkParameters params, BigInteger nanocoins, Address to) throws IOException, ProtocolException {
|
||||
// Create a fake TX of sufficient realism to exercise the unit tests. Two outputs, one to us, one to somewhere
|
||||
// else to simulate change.
|
||||
Transaction t = new Transaction(params);
|
||||
@ -38,7 +41,51 @@ public class TestUtils {
|
||||
prevTx.addOutput(prevOut);
|
||||
// Connect it.
|
||||
t.addInput(prevOut);
|
||||
return t;
|
||||
|
||||
// roundtrip tx
|
||||
return roundTripTransaction(params, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Transaction[] Transaction[0] is a feeder transaction, supplying BTC to Transaction[1]
|
||||
*/
|
||||
public static Transaction[] createFakeTx(NetworkParameters params, BigInteger nanocoins, Address to, Address from) throws IOException, ProtocolException {
|
||||
// Create fake TXes of sufficient realism to exercise the unit tests. This transaction send BTC from the from address, to the to address
|
||||
// with to one to somewhere else to simulate change.
|
||||
Transaction t = new Transaction(params);
|
||||
TransactionOutput outputToMe = new TransactionOutput(params, t, nanocoins, to);
|
||||
t.addOutput(outputToMe);
|
||||
TransactionOutput change = new TransactionOutput(params, t, Utils.toNanoCoins(1, 11), new ECKey().toAddress(params));
|
||||
t.addOutput(change);
|
||||
// Make a feeder tx that sends to the from address specified. This feeder tx is not really valid but it doesn't
|
||||
// matter for our purposes.
|
||||
Transaction feederTx = new Transaction(params);
|
||||
TransactionOutput feederOut = new TransactionOutput(params, feederTx, nanocoins, from);
|
||||
feederTx.addOutput(feederOut);
|
||||
|
||||
// make a previous tx that sends from the feeder to the from address
|
||||
Transaction prevTx = new Transaction(params);
|
||||
TransactionOutput prevOut = new TransactionOutput(params, prevTx, nanocoins, to);
|
||||
prevTx.addOutput(prevOut);
|
||||
|
||||
// Connect up the txes
|
||||
prevTx.addInput(feederOut);
|
||||
t.addInput(prevOut);
|
||||
|
||||
// roundtrip the tx so that they are just like they would be from the wire
|
||||
return new Transaction[]{roundTripTransaction(params, prevTx), roundTripTransaction(params,t)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Roundtrip a transaction so that it appears as if it has just come from the wire
|
||||
*/
|
||||
private static Transaction roundTripTransaction(NetworkParameters params, Transaction tx) throws IOException, ProtocolException {
|
||||
BitcoinSerializer bs = new BitcoinSerializer(params, true, null);
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
bs.serialize(tx, bos);
|
||||
|
||||
return (Transaction)bs.deserialize(new ByteArrayInputStream(bos.toByteArray()));
|
||||
}
|
||||
|
||||
public static class DoubleSpends {
|
||||
|
@ -656,5 +656,61 @@ public class WalletTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void spendToSameWallet() throws Exception {
|
||||
// Test that a spend to the same wallet is dealt with correctly.
|
||||
// It should appear in the wallet and confirm.
|
||||
// This is a bit of a silly thing to do in the real world as all it does is burn a fee but it is perfectly valid.
|
||||
|
||||
BigInteger coin1 = Utils.toNanoCoins(1, 0);
|
||||
BigInteger coinHalf = Utils.toNanoCoins(0, 50);
|
||||
|
||||
// Start by giving us 1 coin.
|
||||
Transaction inbound1 = createFakeTx(params, coin1, myAddress);
|
||||
wallet.receiveFromBlock(inbound1, null, BlockChain.NewBlockType.BEST_CHAIN);
|
||||
|
||||
// Send half to ourselves. We should then have a balance available to spend of zero.
|
||||
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
|
||||
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.ALL));
|
||||
|
||||
Transaction outbound1 = wallet.createSend(myAddress, coinHalf);
|
||||
wallet.commitTx(outbound1);
|
||||
|
||||
// We should have a zero available balance before the next block.
|
||||
assertEquals(BigInteger.ZERO, wallet.getBalance());
|
||||
|
||||
wallet.receiveFromBlock(outbound1, null, BlockChain.NewBlockType.BEST_CHAIN);
|
||||
|
||||
// We should have a balance of 1 BTC after the block is received.
|
||||
assertEquals(coin1, wallet.getBalance());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rememberLastBlockSeenHash() throws Exception {
|
||||
BigInteger v1 = toNanoCoins(5, 0);
|
||||
BigInteger v2 = toNanoCoins(0, 50);
|
||||
BigInteger v3 = toNanoCoins(0, 25);
|
||||
Transaction t1 = createFakeTx(params, v1, myAddress);
|
||||
Transaction t2 = createFakeTx(params, v2, myAddress);
|
||||
Transaction t3 = createFakeTx(params, v3, myAddress);
|
||||
StoredBlock b1 = createFakeBlock(params, blockStore, t1).storedBlock;
|
||||
// TODO for Mike - b2 gets sent to side chain but b1, b2, b3 are all chained together - unrealistic.
|
||||
StoredBlock b2 = createFakeBlock(params, blockStore, t2).storedBlock;
|
||||
StoredBlock b3 = createFakeBlock(params, blockStore, t3).storedBlock;
|
||||
|
||||
// Receive a block on the best chain - this should set the last block seen hash.
|
||||
wallet.receiveFromBlock(t1, b1, BlockChain.NewBlockType.BEST_CHAIN);
|
||||
assertEquals(b1.getHeader().getHash(), wallet.getLastBlockSeenHash());
|
||||
|
||||
// Receive a block on the side chain - this should not change the last block seen hash.
|
||||
wallet.receiveFromBlock(t2, b2, BlockChain.NewBlockType.SIDE_CHAIN);
|
||||
assertEquals(b1.getHeader().getHash(), wallet.getLastBlockSeenHash());
|
||||
|
||||
// Receive block 3 on the best chain - this should change the last block seen hash.
|
||||
wallet.receiveFromBlock(t3, b3, BlockChain.NewBlockType.BEST_CHAIN);
|
||||
assertEquals(b3.getHeader().getHash(), wallet.getLastBlockSeenHash());
|
||||
}
|
||||
|
||||
|
||||
// Support for offline spending is tested in PeerGroupTest
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ package com.google.bitcoin.store;
|
||||
import com.google.bitcoin.core.*;
|
||||
import com.google.bitcoin.core.TransactionConfidence.ConfidenceType;
|
||||
import com.google.bitcoin.utils.BriefLogFormatter;
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.bitcoinj.wallet.Protos;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -108,6 +110,32 @@ public class WalletProtobufSerializerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastBlockSeenHash() throws Exception {
|
||||
// Test the lastBlockSeenHash field works.
|
||||
|
||||
// LastBlockSeenHash should be empty if never set.
|
||||
wallet = new Wallet(params);
|
||||
Protos.Wallet walletProto = WalletProtobufSerializer.walletToProto(wallet);
|
||||
ByteString lastSeenBlockHash = walletProto.getLastSeenBlockHash();
|
||||
assertTrue(lastSeenBlockHash.isEmpty());
|
||||
|
||||
// Create a block.
|
||||
Block block = new Block(params, BlockTest.blockBytes);
|
||||
Sha256Hash blockHash = block.getHash();
|
||||
wallet.setLastBlockSeenHash(blockHash);
|
||||
|
||||
// Roundtrip the wallet and check it has stored te blockHash.
|
||||
Wallet wallet1 = roundTrip(wallet);
|
||||
assertEquals(blockHash, wallet1.getLastBlockSeenHash());
|
||||
|
||||
// Test the Satoshi genesis block (hash of all zeroes) is roundtripped ok.
|
||||
Block genesisBlock = NetworkParameters.prodNet().genesisBlock;
|
||||
wallet.setLastBlockSeenHash(genesisBlock.getHash());
|
||||
Wallet wallet2 = roundTrip(wallet);
|
||||
assertEquals(genesisBlock.getHash(), wallet2.getLastBlockSeenHash());
|
||||
}
|
||||
|
||||
private Wallet roundTrip(Wallet wallet) throws IOException {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
//System.out.println(WalletProtobufSerializer.walletToText(wallet));
|
||||
|
Loading…
Reference in New Issue
Block a user