diff --git a/core/src/bitcoin.proto b/core/src/bitcoin.proto index fa900a6d..4223513e 100644 --- a/core/src/bitcoin.proto +++ b/core/src/bitcoin.proto @@ -253,6 +253,7 @@ message Wallet { optional bytes last_seen_block_hash = 2; // The height in the chain of the last seen block. optional uint32 last_seen_block_height = 12; + optional int64 last_seen_block_time_secs = 14; repeated Key key = 3; repeated Transaction transaction = 4; @@ -278,4 +279,6 @@ message Wallet { // wanted. Money sent to them will be re-spent automatically to the first key that was created after this time. It // can be used to recover a compromised wallet, or just as part of preventative defence-in-depth measures. optional uint64 key_rotation_time = 13; + + // Next tag: 15 } diff --git a/core/src/main/java/com/google/bitcoin/core/Wallet.java b/core/src/main/java/com/google/bitcoin/core/Wallet.java index 64296279..9711cdb8 100644 --- a/core/src/main/java/com/google/bitcoin/core/Wallet.java +++ b/core/src/main/java/com/google/bitcoin/core/Wallet.java @@ -132,6 +132,7 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi private Sha256Hash lastBlockSeenHash; private int lastBlockSeenHeight; + private long lastBlockSeenTimeSecs; private transient CopyOnWriteArrayList> eventListeners; @@ -901,6 +902,7 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi // Store the new block hash. setLastBlockSeenHash(newBlockHash); setLastBlockSeenHeight(block.getHeight()); + setLastBlockSeenTimeSecs(block.getHeader().getTimeSeconds()); // TODO: Clarify the code below. // Notify all the BUILDING transactions of the new block. // This is so that they can update their work done and depth. @@ -2127,7 +2129,8 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi * @param includeExtensions Whether to print extension data. * @param chain If set, will be used to estimate lock times for block timelocked transactions. */ - public String toString(boolean includePrivateKeys, boolean includeTransactions, boolean includeExtensions, AbstractBlockChain chain) { + public String toString(boolean includePrivateKeys, boolean includeTransactions, boolean includeExtensions, + @Nullable AbstractBlockChain chain) { lock.lock(); try { StringBuilder builder = new StringBuilder(); @@ -2136,8 +2139,10 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi builder.append(String.format(" %d spent transactions%n", spent.size())); builder.append(String.format(" %d pending transactions%n", pending.size())); builder.append(String.format(" %d dead transactions%n", dead.size())); - builder.append(String.format("Last seen best block: (%d) %s%n", - getLastBlockSeenHeight(), getLastBlockSeenHash())); + final Date lastBlockSeenTime = getLastBlockSeenTime(); + final String lastBlockSeenTimeStr = lastBlockSeenTime == null ? "time unknown" : lastBlockSeenTime.toString(); + builder.append(String.format("Last seen best block: %d (%s): %s%n", + getLastBlockSeenHeight(), lastBlockSeenTimeStr, getLastBlockSeenHash())); if (this.keyCrypter != null) { builder.append(String.format("Encryption: %s%n", keyCrypter.toString())); } @@ -2456,6 +2461,47 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi } } + public void setLastBlockSeenTimeSecs(long timeSecs) { + lock.lock(); + try { + lastBlockSeenTimeSecs = timeSecs; + } finally { + lock.unlock(); + } + } + + /** + * Returns the UNIX time in seconds since the epoch extracted from the last best seen block header. This timestamp + * is not the local time at which the block was first observed by this application but rather what the block + * (i.e. miner) self declares. It is allowed to have some significant drift from the real time at which the block + * was found, although most miners do use accurate times. If this wallet is old and does not have a recorded + * time then this method returns zero. + */ + public long getLastBlockSeenTimeSecs() { + lock.lock(); + try { + return lastBlockSeenTimeSecs; + } finally { + lock.unlock(); + } + } + + /** + * Returns a {@link Date} representing the time extracted from the last best seen block header. This timestamp + * is not the local time at which the block was first observed by this application but rather what the block + * (i.e. miner) self declares. It is allowed to have some significant drift from the real time at which the block + * was found, although most miners do use accurate times. If this wallet is old and does not have a recorded + * time then this method returns null. + */ + @Nullable + public Date getLastBlockSeenTime() { + final long secs = getLastBlockSeenTimeSecs(); + if (secs == 0) + return null; + else + return new Date(secs * 1000); + } + /** * Returns the height of the last seen best-chain block. Can be 0 if a wallet is brand new or -1 if the wallet * is old and doesn't have that data. diff --git a/core/src/main/java/com/google/bitcoin/store/WalletProtobufSerializer.java b/core/src/main/java/com/google/bitcoin/store/WalletProtobufSerializer.java index cbd2dcd8..fa012540 100644 --- a/core/src/main/java/com/google/bitcoin/store/WalletProtobufSerializer.java +++ b/core/src/main/java/com/google/bitcoin/store/WalletProtobufSerializer.java @@ -160,6 +160,8 @@ public class WalletProtobufSerializer { walletBuilder.setLastSeenBlockHash(hashToByteString(lastSeenBlockHash)); walletBuilder.setLastSeenBlockHeight(wallet.getLastBlockSeenHeight()); } + if (wallet.getLastBlockSeenTimeSecs() > 0) + walletBuilder.setLastSeenBlockTimeSecs(wallet.getLastBlockSeenTimeSecs()); // Populate the scrypt parameters. KeyCrypter keyCrypter = wallet.getKeyCrypter(); @@ -423,6 +425,8 @@ public class WalletProtobufSerializer { } else { wallet.setLastBlockSeenHeight(walletProto.getLastSeenBlockHeight()); } + // Will default to zero if not present. + wallet.setLastBlockSeenTimeSecs(walletProto.getLastSeenBlockTimeSecs()); if (walletProto.hasKeyRotationTime()) { wallet.setKeyRotationTime(new Date(walletProto.getKeyRotationTime() * 1000)); diff --git a/core/src/main/java/org/bitcoinj/wallet/Protos.java b/core/src/main/java/org/bitcoinj/wallet/Protos.java index d0f9d1b0..cc3f9e52 100644 --- a/core/src/main/java/org/bitcoinj/wallet/Protos.java +++ b/core/src/main/java/org/bitcoinj/wallet/Protos.java @@ -10064,6 +10064,16 @@ public final class Protos { */ int getLastSeenBlockHeight(); + // optional int64 last_seen_block_time_secs = 14; + /** + * optional int64 last_seen_block_time_secs = 14; + */ + boolean hasLastSeenBlockTimeSecs(); + /** + * optional int64 last_seen_block_time_secs = 14; + */ + long getLastSeenBlockTimeSecs(); + // repeated .wallet.Key key = 3; /** * repeated .wallet.Key key = 3; @@ -10298,17 +10308,17 @@ public final class Protos { break; } case 26: { - if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + if (!((mutable_bitField0_ & 0x00000010) == 0x00000010)) { key_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000008; + mutable_bitField0_ |= 0x00000010; } key_.add(input.readMessage(org.bitcoinj.wallet.Protos.Key.PARSER, extensionRegistry)); break; } case 34: { - if (!((mutable_bitField0_ & 0x00000010) == 0x00000010)) { + if (!((mutable_bitField0_ & 0x00000020) == 0x00000020)) { transaction_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000010; + mutable_bitField0_ |= 0x00000020; } transaction_.add(input.readMessage(org.bitcoinj.wallet.Protos.Transaction.PARSER, extensionRegistry)); break; @@ -10319,14 +10329,14 @@ public final class Protos { if (value == null) { unknownFields.mergeVarintField(5, rawValue); } else { - bitField0_ |= 0x00000008; + bitField0_ |= 0x00000010; encryptionType_ = value; } break; } case 50: { org.bitcoinj.wallet.Protos.ScryptParameters.Builder subBuilder = null; - if (((bitField0_ & 0x00000010) == 0x00000010)) { + if (((bitField0_ & 0x00000020) == 0x00000020)) { subBuilder = encryptionParameters_.toBuilder(); } encryptionParameters_ = input.readMessage(org.bitcoinj.wallet.Protos.ScryptParameters.PARSER, extensionRegistry); @@ -10334,24 +10344,24 @@ public final class Protos { subBuilder.mergeFrom(encryptionParameters_); encryptionParameters_ = subBuilder.buildPartial(); } - bitField0_ |= 0x00000010; + bitField0_ |= 0x00000020; break; } case 56: { - bitField0_ |= 0x00000020; + bitField0_ |= 0x00000040; version_ = input.readInt32(); break; } case 82: { - if (!((mutable_bitField0_ & 0x00000100) == 0x00000100)) { + if (!((mutable_bitField0_ & 0x00000200) == 0x00000200)) { extension_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000100; + mutable_bitField0_ |= 0x00000200; } extension_.add(input.readMessage(org.bitcoinj.wallet.Protos.Extension.PARSER, extensionRegistry)); break; } case 90: { - bitField0_ |= 0x00000040; + bitField0_ |= 0x00000080; description_ = input.readBytes(); break; } @@ -10361,10 +10371,15 @@ public final class Protos { break; } case 104: { - bitField0_ |= 0x00000080; + bitField0_ |= 0x00000100; keyRotationTime_ = input.readUInt64(); break; } + case 112: { + bitField0_ |= 0x00000008; + lastSeenBlockTimeSecs_ = input.readInt64(); + break; + } } } } catch (com.google.protobuf.InvalidProtocolBufferException e) { @@ -10373,13 +10388,13 @@ public final class Protos { throw new com.google.protobuf.InvalidProtocolBufferException( e.getMessage()).setUnfinishedMessage(this); } finally { - if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + if (((mutable_bitField0_ & 0x00000010) == 0x00000010)) { key_ = java.util.Collections.unmodifiableList(key_); } - if (((mutable_bitField0_ & 0x00000010) == 0x00000010)) { + if (((mutable_bitField0_ & 0x00000020) == 0x00000020)) { transaction_ = java.util.Collections.unmodifiableList(transaction_); } - if (((mutable_bitField0_ & 0x00000100) == 0x00000100)) { + if (((mutable_bitField0_ & 0x00000200) == 0x00000200)) { extension_ = java.util.Collections.unmodifiableList(extension_); } this.unknownFields = unknownFields.build(); @@ -10624,6 +10639,22 @@ public final class Protos { return lastSeenBlockHeight_; } + // optional int64 last_seen_block_time_secs = 14; + public static final int LAST_SEEN_BLOCK_TIME_SECS_FIELD_NUMBER = 14; + private long lastSeenBlockTimeSecs_; + /** + * optional int64 last_seen_block_time_secs = 14; + */ + public boolean hasLastSeenBlockTimeSecs() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional int64 last_seen_block_time_secs = 14; + */ + public long getLastSeenBlockTimeSecs() { + return lastSeenBlockTimeSecs_; + } + // repeated .wallet.Key key = 3; public static final int KEY_FIELD_NUMBER = 3; private java.util.List key_; @@ -10703,7 +10734,7 @@ public final class Protos { * optional .wallet.Wallet.EncryptionType encryption_type = 5 [default = UNENCRYPTED]; */ public boolean hasEncryptionType() { - return ((bitField0_ & 0x00000008) == 0x00000008); + return ((bitField0_ & 0x00000010) == 0x00000010); } /** * optional .wallet.Wallet.EncryptionType encryption_type = 5 [default = UNENCRYPTED]; @@ -10719,7 +10750,7 @@ public final class Protos { * optional .wallet.ScryptParameters encryption_parameters = 6; */ public boolean hasEncryptionParameters() { - return ((bitField0_ & 0x00000010) == 0x00000010); + return ((bitField0_ & 0x00000020) == 0x00000020); } /** * optional .wallet.ScryptParameters encryption_parameters = 6; @@ -10746,7 +10777,7 @@ public final class Protos { * */ public boolean hasVersion() { - return ((bitField0_ & 0x00000020) == 0x00000020); + return ((bitField0_ & 0x00000040) == 0x00000040); } /** * optional int32 version = 7; @@ -10807,7 +10838,7 @@ public final class Protos { * */ public boolean hasDescription() { - return ((bitField0_ & 0x00000040) == 0x00000040); + return ((bitField0_ & 0x00000080) == 0x00000080); } /** * optional string description = 11; @@ -10864,7 +10895,7 @@ public final class Protos { * */ public boolean hasKeyRotationTime() { - return ((bitField0_ & 0x00000080) == 0x00000080); + return ((bitField0_ & 0x00000100) == 0x00000100); } /** * optional uint64 key_rotation_time = 13; @@ -10883,6 +10914,7 @@ public final class Protos { networkIdentifier_ = ""; lastSeenBlockHash_ = com.google.protobuf.ByteString.EMPTY; lastSeenBlockHeight_ = 0; + lastSeenBlockTimeSecs_ = 0L; key_ = java.util.Collections.emptyList(); transaction_ = java.util.Collections.emptyList(); encryptionType_ = org.bitcoinj.wallet.Protos.Wallet.EncryptionType.UNENCRYPTED; @@ -10944,27 +10976,30 @@ public final class Protos { for (int i = 0; i < transaction_.size(); i++) { output.writeMessage(4, transaction_.get(i)); } - if (((bitField0_ & 0x00000008) == 0x00000008)) { + if (((bitField0_ & 0x00000010) == 0x00000010)) { output.writeEnum(5, encryptionType_.getNumber()); } - if (((bitField0_ & 0x00000010) == 0x00000010)) { + if (((bitField0_ & 0x00000020) == 0x00000020)) { output.writeMessage(6, encryptionParameters_); } - if (((bitField0_ & 0x00000020) == 0x00000020)) { + if (((bitField0_ & 0x00000040) == 0x00000040)) { output.writeInt32(7, version_); } for (int i = 0; i < extension_.size(); i++) { output.writeMessage(10, extension_.get(i)); } - if (((bitField0_ & 0x00000040) == 0x00000040)) { + if (((bitField0_ & 0x00000080) == 0x00000080)) { output.writeBytes(11, getDescriptionBytes()); } if (((bitField0_ & 0x00000004) == 0x00000004)) { output.writeUInt32(12, lastSeenBlockHeight_); } - if (((bitField0_ & 0x00000080) == 0x00000080)) { + if (((bitField0_ & 0x00000100) == 0x00000100)) { output.writeUInt64(13, keyRotationTime_); } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeInt64(14, lastSeenBlockTimeSecs_); + } getUnknownFields().writeTo(output); } @@ -10990,15 +11025,15 @@ public final class Protos { size += com.google.protobuf.CodedOutputStream .computeMessageSize(4, transaction_.get(i)); } - if (((bitField0_ & 0x00000008) == 0x00000008)) { + if (((bitField0_ & 0x00000010) == 0x00000010)) { size += com.google.protobuf.CodedOutputStream .computeEnumSize(5, encryptionType_.getNumber()); } - if (((bitField0_ & 0x00000010) == 0x00000010)) { + if (((bitField0_ & 0x00000020) == 0x00000020)) { size += com.google.protobuf.CodedOutputStream .computeMessageSize(6, encryptionParameters_); } - if (((bitField0_ & 0x00000020) == 0x00000020)) { + if (((bitField0_ & 0x00000040) == 0x00000040)) { size += com.google.protobuf.CodedOutputStream .computeInt32Size(7, version_); } @@ -11006,7 +11041,7 @@ public final class Protos { size += com.google.protobuf.CodedOutputStream .computeMessageSize(10, extension_.get(i)); } - if (((bitField0_ & 0x00000040) == 0x00000040)) { + if (((bitField0_ & 0x00000080) == 0x00000080)) { size += com.google.protobuf.CodedOutputStream .computeBytesSize(11, getDescriptionBytes()); } @@ -11014,10 +11049,14 @@ public final class Protos { size += com.google.protobuf.CodedOutputStream .computeUInt32Size(12, lastSeenBlockHeight_); } - if (((bitField0_ & 0x00000080) == 0x00000080)) { + if (((bitField0_ & 0x00000100) == 0x00000100)) { size += com.google.protobuf.CodedOutputStream .computeUInt64Size(13, keyRotationTime_); } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeInt64Size(14, lastSeenBlockTimeSecs_); + } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -11148,38 +11187,40 @@ public final class Protos { bitField0_ = (bitField0_ & ~0x00000002); lastSeenBlockHeight_ = 0; bitField0_ = (bitField0_ & ~0x00000004); + lastSeenBlockTimeSecs_ = 0L; + bitField0_ = (bitField0_ & ~0x00000008); if (keyBuilder_ == null) { key_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); + bitField0_ = (bitField0_ & ~0x00000010); } else { keyBuilder_.clear(); } if (transactionBuilder_ == null) { transaction_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000010); + bitField0_ = (bitField0_ & ~0x00000020); } else { transactionBuilder_.clear(); } encryptionType_ = org.bitcoinj.wallet.Protos.Wallet.EncryptionType.UNENCRYPTED; - bitField0_ = (bitField0_ & ~0x00000020); + bitField0_ = (bitField0_ & ~0x00000040); if (encryptionParametersBuilder_ == null) { encryptionParameters_ = org.bitcoinj.wallet.Protos.ScryptParameters.getDefaultInstance(); } else { encryptionParametersBuilder_.clear(); } - bitField0_ = (bitField0_ & ~0x00000040); - version_ = 0; bitField0_ = (bitField0_ & ~0x00000080); + version_ = 0; + bitField0_ = (bitField0_ & ~0x00000100); if (extensionBuilder_ == null) { extension_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000100); + bitField0_ = (bitField0_ & ~0x00000200); } else { extensionBuilder_.clear(); } description_ = ""; - bitField0_ = (bitField0_ & ~0x00000200); - keyRotationTime_ = 0L; bitField0_ = (bitField0_ & ~0x00000400); + keyRotationTime_ = 0L; + bitField0_ = (bitField0_ & ~0x00000800); return this; } @@ -11220,56 +11261,60 @@ public final class Protos { to_bitField0_ |= 0x00000004; } result.lastSeenBlockHeight_ = lastSeenBlockHeight_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.lastSeenBlockTimeSecs_ = lastSeenBlockTimeSecs_; if (keyBuilder_ == null) { - if (((bitField0_ & 0x00000008) == 0x00000008)) { + if (((bitField0_ & 0x00000010) == 0x00000010)) { key_ = java.util.Collections.unmodifiableList(key_); - bitField0_ = (bitField0_ & ~0x00000008); + bitField0_ = (bitField0_ & ~0x00000010); } result.key_ = key_; } else { result.key_ = keyBuilder_.build(); } if (transactionBuilder_ == null) { - if (((bitField0_ & 0x00000010) == 0x00000010)) { + if (((bitField0_ & 0x00000020) == 0x00000020)) { transaction_ = java.util.Collections.unmodifiableList(transaction_); - bitField0_ = (bitField0_ & ~0x00000010); + bitField0_ = (bitField0_ & ~0x00000020); } result.transaction_ = transaction_; } else { result.transaction_ = transactionBuilder_.build(); } - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000008; - } - result.encryptionType_ = encryptionType_; if (((from_bitField0_ & 0x00000040) == 0x00000040)) { to_bitField0_ |= 0x00000010; } + result.encryptionType_ = encryptionType_; + if (((from_bitField0_ & 0x00000080) == 0x00000080)) { + to_bitField0_ |= 0x00000020; + } if (encryptionParametersBuilder_ == null) { result.encryptionParameters_ = encryptionParameters_; } else { result.encryptionParameters_ = encryptionParametersBuilder_.build(); } - if (((from_bitField0_ & 0x00000080) == 0x00000080)) { - to_bitField0_ |= 0x00000020; + if (((from_bitField0_ & 0x00000100) == 0x00000100)) { + to_bitField0_ |= 0x00000040; } result.version_ = version_; if (extensionBuilder_ == null) { - if (((bitField0_ & 0x00000100) == 0x00000100)) { + if (((bitField0_ & 0x00000200) == 0x00000200)) { extension_ = java.util.Collections.unmodifiableList(extension_); - bitField0_ = (bitField0_ & ~0x00000100); + bitField0_ = (bitField0_ & ~0x00000200); } result.extension_ = extension_; } else { result.extension_ = extensionBuilder_.build(); } - if (((from_bitField0_ & 0x00000200) == 0x00000200)) { - to_bitField0_ |= 0x00000040; - } - result.description_ = description_; if (((from_bitField0_ & 0x00000400) == 0x00000400)) { to_bitField0_ |= 0x00000080; } + result.description_ = description_; + if (((from_bitField0_ & 0x00000800) == 0x00000800)) { + to_bitField0_ |= 0x00000100; + } result.keyRotationTime_ = keyRotationTime_; result.bitField0_ = to_bitField0_; onBuilt(); @@ -11298,11 +11343,14 @@ public final class Protos { if (other.hasLastSeenBlockHeight()) { setLastSeenBlockHeight(other.getLastSeenBlockHeight()); } + if (other.hasLastSeenBlockTimeSecs()) { + setLastSeenBlockTimeSecs(other.getLastSeenBlockTimeSecs()); + } if (keyBuilder_ == null) { if (!other.key_.isEmpty()) { if (key_.isEmpty()) { key_ = other.key_; - bitField0_ = (bitField0_ & ~0x00000008); + bitField0_ = (bitField0_ & ~0x00000010); } else { ensureKeyIsMutable(); key_.addAll(other.key_); @@ -11315,7 +11363,7 @@ public final class Protos { keyBuilder_.dispose(); keyBuilder_ = null; key_ = other.key_; - bitField0_ = (bitField0_ & ~0x00000008); + bitField0_ = (bitField0_ & ~0x00000010); keyBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? getKeyFieldBuilder() : null; @@ -11328,7 +11376,7 @@ public final class Protos { if (!other.transaction_.isEmpty()) { if (transaction_.isEmpty()) { transaction_ = other.transaction_; - bitField0_ = (bitField0_ & ~0x00000010); + bitField0_ = (bitField0_ & ~0x00000020); } else { ensureTransactionIsMutable(); transaction_.addAll(other.transaction_); @@ -11341,7 +11389,7 @@ public final class Protos { transactionBuilder_.dispose(); transactionBuilder_ = null; transaction_ = other.transaction_; - bitField0_ = (bitField0_ & ~0x00000010); + bitField0_ = (bitField0_ & ~0x00000020); transactionBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? getTransactionFieldBuilder() : null; @@ -11363,7 +11411,7 @@ public final class Protos { if (!other.extension_.isEmpty()) { if (extension_.isEmpty()) { extension_ = other.extension_; - bitField0_ = (bitField0_ & ~0x00000100); + bitField0_ = (bitField0_ & ~0x00000200); } else { ensureExtensionIsMutable(); extension_.addAll(other.extension_); @@ -11376,7 +11424,7 @@ public final class Protos { extensionBuilder_.dispose(); extensionBuilder_ = null; extension_ = other.extension_; - bitField0_ = (bitField0_ & ~0x00000100); + bitField0_ = (bitField0_ & ~0x00000200); extensionBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? getExtensionFieldBuilder() : null; @@ -11386,7 +11434,7 @@ public final class Protos { } } if (other.hasDescription()) { - bitField0_ |= 0x00000200; + bitField0_ |= 0x00000400; description_ = other.description_; onChanged(); } @@ -11647,13 +11695,46 @@ public final class Protos { return this; } + // optional int64 last_seen_block_time_secs = 14; + private long lastSeenBlockTimeSecs_ ; + /** + * optional int64 last_seen_block_time_secs = 14; + */ + public boolean hasLastSeenBlockTimeSecs() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional int64 last_seen_block_time_secs = 14; + */ + public long getLastSeenBlockTimeSecs() { + return lastSeenBlockTimeSecs_; + } + /** + * optional int64 last_seen_block_time_secs = 14; + */ + public Builder setLastSeenBlockTimeSecs(long value) { + bitField0_ |= 0x00000008; + lastSeenBlockTimeSecs_ = value; + onChanged(); + return this; + } + /** + * optional int64 last_seen_block_time_secs = 14; + */ + public Builder clearLastSeenBlockTimeSecs() { + bitField0_ = (bitField0_ & ~0x00000008); + lastSeenBlockTimeSecs_ = 0L; + onChanged(); + return this; + } + // repeated .wallet.Key key = 3; private java.util.List key_ = java.util.Collections.emptyList(); private void ensureKeyIsMutable() { - if (!((bitField0_ & 0x00000008) == 0x00000008)) { + if (!((bitField0_ & 0x00000010) == 0x00000010)) { key_ = new java.util.ArrayList(key_); - bitField0_ |= 0x00000008; + bitField0_ |= 0x00000010; } } @@ -11802,7 +11883,7 @@ public final class Protos { public Builder clearKey() { if (keyBuilder_ == null) { key_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); + bitField0_ = (bitField0_ & ~0x00000010); onChanged(); } else { keyBuilder_.clear(); @@ -11879,7 +11960,7 @@ public final class Protos { keyBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< org.bitcoinj.wallet.Protos.Key, org.bitcoinj.wallet.Protos.Key.Builder, org.bitcoinj.wallet.Protos.KeyOrBuilder>( key_, - ((bitField0_ & 0x00000008) == 0x00000008), + ((bitField0_ & 0x00000010) == 0x00000010), getParentForChildren(), isClean()); key_ = null; @@ -11891,9 +11972,9 @@ public final class Protos { private java.util.List transaction_ = java.util.Collections.emptyList(); private void ensureTransactionIsMutable() { - if (!((bitField0_ & 0x00000010) == 0x00000010)) { + if (!((bitField0_ & 0x00000020) == 0x00000020)) { transaction_ = new java.util.ArrayList(transaction_); - bitField0_ |= 0x00000010; + bitField0_ |= 0x00000020; } } @@ -12042,7 +12123,7 @@ public final class Protos { public Builder clearTransaction() { if (transactionBuilder_ == null) { transaction_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000010); + bitField0_ = (bitField0_ & ~0x00000020); onChanged(); } else { transactionBuilder_.clear(); @@ -12119,7 +12200,7 @@ public final class Protos { transactionBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< org.bitcoinj.wallet.Protos.Transaction, org.bitcoinj.wallet.Protos.Transaction.Builder, org.bitcoinj.wallet.Protos.TransactionOrBuilder>( transaction_, - ((bitField0_ & 0x00000010) == 0x00000010), + ((bitField0_ & 0x00000020) == 0x00000020), getParentForChildren(), isClean()); transaction_ = null; @@ -12133,7 +12214,7 @@ public final class Protos { * optional .wallet.Wallet.EncryptionType encryption_type = 5 [default = UNENCRYPTED]; */ public boolean hasEncryptionType() { - return ((bitField0_ & 0x00000020) == 0x00000020); + return ((bitField0_ & 0x00000040) == 0x00000040); } /** * optional .wallet.Wallet.EncryptionType encryption_type = 5 [default = UNENCRYPTED]; @@ -12148,7 +12229,7 @@ public final class Protos { if (value == null) { throw new NullPointerException(); } - bitField0_ |= 0x00000020; + bitField0_ |= 0x00000040; encryptionType_ = value; onChanged(); return this; @@ -12157,7 +12238,7 @@ public final class Protos { * optional .wallet.Wallet.EncryptionType encryption_type = 5 [default = UNENCRYPTED]; */ public Builder clearEncryptionType() { - bitField0_ = (bitField0_ & ~0x00000020); + bitField0_ = (bitField0_ & ~0x00000040); encryptionType_ = org.bitcoinj.wallet.Protos.Wallet.EncryptionType.UNENCRYPTED; onChanged(); return this; @@ -12171,7 +12252,7 @@ public final class Protos { * optional .wallet.ScryptParameters encryption_parameters = 6; */ public boolean hasEncryptionParameters() { - return ((bitField0_ & 0x00000040) == 0x00000040); + return ((bitField0_ & 0x00000080) == 0x00000080); } /** * optional .wallet.ScryptParameters encryption_parameters = 6; @@ -12196,7 +12277,7 @@ public final class Protos { } else { encryptionParametersBuilder_.setMessage(value); } - bitField0_ |= 0x00000040; + bitField0_ |= 0x00000080; return this; } /** @@ -12210,7 +12291,7 @@ public final class Protos { } else { encryptionParametersBuilder_.setMessage(builderForValue.build()); } - bitField0_ |= 0x00000040; + bitField0_ |= 0x00000080; return this; } /** @@ -12218,7 +12299,7 @@ public final class Protos { */ public Builder mergeEncryptionParameters(org.bitcoinj.wallet.Protos.ScryptParameters value) { if (encryptionParametersBuilder_ == null) { - if (((bitField0_ & 0x00000040) == 0x00000040) && + if (((bitField0_ & 0x00000080) == 0x00000080) && encryptionParameters_ != org.bitcoinj.wallet.Protos.ScryptParameters.getDefaultInstance()) { encryptionParameters_ = org.bitcoinj.wallet.Protos.ScryptParameters.newBuilder(encryptionParameters_).mergeFrom(value).buildPartial(); @@ -12229,7 +12310,7 @@ public final class Protos { } else { encryptionParametersBuilder_.mergeFrom(value); } - bitField0_ |= 0x00000040; + bitField0_ |= 0x00000080; return this; } /** @@ -12242,14 +12323,14 @@ public final class Protos { } else { encryptionParametersBuilder_.clear(); } - bitField0_ = (bitField0_ & ~0x00000040); + bitField0_ = (bitField0_ & ~0x00000080); return this; } /** * optional .wallet.ScryptParameters encryption_parameters = 6; */ public org.bitcoinj.wallet.Protos.ScryptParameters.Builder getEncryptionParametersBuilder() { - bitField0_ |= 0x00000040; + bitField0_ |= 0x00000080; onChanged(); return getEncryptionParametersFieldBuilder().getBuilder(); } @@ -12291,7 +12372,7 @@ public final class Protos { * */ public boolean hasVersion() { - return ((bitField0_ & 0x00000080) == 0x00000080); + return ((bitField0_ & 0x00000100) == 0x00000100); } /** * optional int32 version = 7; @@ -12313,7 +12394,7 @@ public final class Protos { * */ public Builder setVersion(int value) { - bitField0_ |= 0x00000080; + bitField0_ |= 0x00000100; version_ = value; onChanged(); return this; @@ -12327,7 +12408,7 @@ public final class Protos { * */ public Builder clearVersion() { - bitField0_ = (bitField0_ & ~0x00000080); + bitField0_ = (bitField0_ & ~0x00000100); version_ = 0; onChanged(); return this; @@ -12337,9 +12418,9 @@ public final class Protos { private java.util.List extension_ = java.util.Collections.emptyList(); private void ensureExtensionIsMutable() { - if (!((bitField0_ & 0x00000100) == 0x00000100)) { + if (!((bitField0_ & 0x00000200) == 0x00000200)) { extension_ = new java.util.ArrayList(extension_); - bitField0_ |= 0x00000100; + bitField0_ |= 0x00000200; } } @@ -12488,7 +12569,7 @@ public final class Protos { public Builder clearExtension() { if (extensionBuilder_ == null) { extension_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000100); + bitField0_ = (bitField0_ & ~0x00000200); onChanged(); } else { extensionBuilder_.clear(); @@ -12565,7 +12646,7 @@ public final class Protos { extensionBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< org.bitcoinj.wallet.Protos.Extension, org.bitcoinj.wallet.Protos.Extension.Builder, org.bitcoinj.wallet.Protos.ExtensionOrBuilder>( extension_, - ((bitField0_ & 0x00000100) == 0x00000100), + ((bitField0_ & 0x00000200) == 0x00000200), getParentForChildren(), isClean()); extension_ = null; @@ -12583,7 +12664,7 @@ public final class Protos { * */ public boolean hasDescription() { - return ((bitField0_ & 0x00000200) == 0x00000200); + return ((bitField0_ & 0x00000400) == 0x00000400); } /** * optional string description = 11; @@ -12635,7 +12716,7 @@ public final class Protos { if (value == null) { throw new NullPointerException(); } - bitField0_ |= 0x00000200; + bitField0_ |= 0x00000400; description_ = value; onChanged(); return this; @@ -12648,7 +12729,7 @@ public final class Protos { * */ public Builder clearDescription() { - bitField0_ = (bitField0_ & ~0x00000200); + bitField0_ = (bitField0_ & ~0x00000400); description_ = getDefaultInstance().getDescription(); onChanged(); return this; @@ -12665,7 +12746,7 @@ public final class Protos { if (value == null) { throw new NullPointerException(); } - bitField0_ |= 0x00000200; + bitField0_ |= 0x00000400; description_ = value; onChanged(); return this; @@ -12683,7 +12764,7 @@ public final class Protos { * */ public boolean hasKeyRotationTime() { - return ((bitField0_ & 0x00000400) == 0x00000400); + return ((bitField0_ & 0x00000800) == 0x00000800); } /** * optional uint64 key_rotation_time = 13; @@ -12707,7 +12788,7 @@ public final class Protos { * */ public Builder setKeyRotationTime(long value) { - bitField0_ |= 0x00000400; + bitField0_ |= 0x00000800; keyRotationTime_ = value; onChanged(); return this; @@ -12722,7 +12803,7 @@ public final class Protos { * */ public Builder clearKeyRotationTime() { - bitField0_ = (bitField0_ & ~0x00000400); + bitField0_ = (bitField0_ & ~0x00000800); keyRotationTime_ = 0L; onChanged(); return this; @@ -12842,19 +12923,20 @@ public final class Protos { "Parameters\022\014\n\004salt\030\001 \002(\014\022\020\n\001n\030\002 \001(\003:\005163" + "84\022\014\n\001r\030\003 \001(\005:\0018\022\014\n\001p\030\004 \001(\005:\0011\"8\n\tExtens" + "ion\022\n\n\002id\030\001 \002(\t\022\014\n\004data\030\002 \002(\014\022\021\n\tmandato" + - "ry\030\003 \002(\010\"\310\003\n\006Wallet\022\032\n\022network_identifie" + + "ry\030\003 \002(\010\"\353\003\n\006Wallet\022\032\n\022network_identifie" + "r\030\001 \002(\t\022\034\n\024last_seen_block_hash\030\002 \001(\014\022\036\n" + - "\026last_seen_block_height\030\014 \001(\r\022\030\n\003key\030\003 \003" + - "(\0132\013.wallet.Key\022(\n\013transaction\030\004 \003(\0132\023.w" + - "allet.Transaction\022C\n\017encryption_type\030\005 \001" + - "(\0162\035.wallet.Wallet.EncryptionType:\013UNENC", - "RYPTED\0227\n\025encryption_parameters\030\006 \001(\0132\030." + - "wallet.ScryptParameters\022\017\n\007version\030\007 \001(\005" + - "\022$\n\textension\030\n \003(\0132\021.wallet.Extension\022\023" + - "\n\013description\030\013 \001(\t\022\031\n\021key_rotation_time" + - "\030\r \001(\004\";\n\016EncryptionType\022\017\n\013UNENCRYPTED\020" + - "\001\022\030\n\024ENCRYPTED_SCRYPT_AES\020\002B\035\n\023org.bitco" + - "inj.walletB\006Protos" + "\026last_seen_block_height\030\014 \001(\r\022!\n\031last_se" + + "en_block_time_secs\030\016 \001(\003\022\030\n\003key\030\003 \003(\0132\013." + + "wallet.Key\022(\n\013transaction\030\004 \003(\0132\023.wallet" + + ".Transaction\022C\n\017encryption_type\030\005 \001(\0162\035.", + "wallet.Wallet.EncryptionType:\013UNENCRYPTE" + + "D\0227\n\025encryption_parameters\030\006 \001(\0132\030.walle" + + "t.ScryptParameters\022\017\n\007version\030\007 \001(\005\022$\n\te" + + "xtension\030\n \003(\0132\021.wallet.Extension\022\023\n\013des" + + "cription\030\013 \001(\t\022\031\n\021key_rotation_time\030\r \001(" + + "\004\";\n\016EncryptionType\022\017\n\013UNENCRYPTED\020\001\022\030\n\024" + + "ENCRYPTED_SCRYPT_AES\020\002B\035\n\023org.bitcoinj.w" + + "alletB\006Protos" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -12920,7 +13002,7 @@ public final class Protos { internal_static_wallet_Wallet_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_wallet_Wallet_descriptor, - new java.lang.String[] { "NetworkIdentifier", "LastSeenBlockHash", "LastSeenBlockHeight", "Key", "Transaction", "EncryptionType", "EncryptionParameters", "Version", "Extension", "Description", "KeyRotationTime", }); + new java.lang.String[] { "NetworkIdentifier", "LastSeenBlockHash", "LastSeenBlockHeight", "LastSeenBlockTimeSecs", "Key", "Transaction", "EncryptionType", "EncryptionParameters", "Version", "Extension", "Description", "KeyRotationTime", }); return null; } }; diff --git a/core/src/test/java/com/google/bitcoin/core/WalletTest.java b/core/src/test/java/com/google/bitcoin/core/WalletTest.java index 3b9629e4..b2a83e62 100644 --- a/core/src/test/java/com/google/bitcoin/core/WalletTest.java +++ b/core/src/test/java/com/google/bitcoin/core/WalletTest.java @@ -857,7 +857,7 @@ public class WalletTest extends TestWithWallet { } @Test - public void rememberLastBlockSeenHash() throws Exception { + public void lastBlockSeen() throws Exception { BigInteger v1 = toNanoCoins(5, 0); BigInteger v2 = toNanoCoins(0, 50); BigInteger v3 = toNanoCoins(0, 25); @@ -874,6 +874,8 @@ public class WalletTest extends TestWithWallet { // Receive a block on the best chain - this should set the last block seen hash. chain.add(b10); assertEquals(b10.getHash(), wallet.getLastBlockSeenHash()); + assertEquals(b10.getTimeSeconds(), wallet.getLastBlockSeenTimeSecs()); + assertEquals(1, wallet.getLastBlockSeenHeight()); // Receive a block on the side chain - this should not change the last block seen hash. chain.add(b11); assertEquals(b10.getHash(), wallet.getLastBlockSeenHash());