Wallet: migrate lastBlockSeenTime field to java.time API

This commit is contained in:
Andreas Schildbach 2023-03-10 10:35:17 +01:00
parent be0b2b46b8
commit da8b9ce434
6 changed files with 57 additions and 36 deletions

View File

@ -31,7 +31,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkState;
@ -91,21 +93,23 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
if (wallet == null)
return null;
final int height = wallet.getLastBlockSeenHeight();
final long time = wallet.getLastBlockSeenTimeSecs();
if (time == 0)
final Optional<Instant> time = wallet.getLastBlockSeenTimeInstant();
if (!time.isPresent())
return null;
// If the transaction has a lock time specified in blocks, we consider that if the tx would become final in the
// next block it is not risky (as it would confirm normally).
final int adjustedHeight = height + 1;
final long timeSecs = time.get().getEpochSecond();
if (!tx.isFinal(adjustedHeight, time)) {
if (!tx.isFinal(adjustedHeight, timeSecs)) {
nonFinal = tx;
return Result.NON_FINAL;
}
for (Transaction dep : dependencies) {
if (!dep.isFinal(adjustedHeight, time)) {
if (!dep.isFinal(adjustedHeight, timeSecs)) {
nonFinal = dep;
return Result.NON_FINAL;
}

View File

@ -249,7 +249,7 @@ public class Wallet extends BaseTaggableObject
@Nullable private Sha256Hash lastBlockSeenHash;
private int lastBlockSeenHeight;
private long lastBlockSeenTimeSecs;
@Nullable private Instant lastBlockSeenTime;
private final List<ListenerRegistration<WalletChangeEventListener>> changeListeners
= new CopyOnWriteArrayList<>();
@ -2447,7 +2447,7 @@ public class Wallet extends BaseTaggableObject
// Store the new block hash.
setLastBlockSeenHash(newBlockHash);
setLastBlockSeenHeight(block.getHeight());
setLastBlockSeenTimeSecs(block.getHeader().getTimeSeconds());
setLastBlockSeenTime(block.getHeader().getTimeInstant());
// Notify all the BUILDING transactions of the new block.
// This is so that they can update their depth.
Set<Transaction> transactions = getTransactions(true);
@ -3292,7 +3292,7 @@ public class Wallet extends BaseTaggableObject
clearTransactions();
lastBlockSeenHash = null;
lastBlockSeenHeight = -1; // Magic value for 'never'.
lastBlockSeenTimeSecs = 0;
lastBlockSeenTime = null;
saveLater();
maybeQueueOnWalletChanged();
} finally {
@ -3510,9 +3510,10 @@ public class Wallet extends BaseTaggableObject
builder.append(" ").append(unspent.size()).append(" unspent\n");
builder.append(" ").append(spent.size()).append(" spent\n");
builder.append(" ").append(dead.size()).append(" dead\n");
final Date lastBlockSeenTime = getLastBlockSeenTime();
builder.append("Last seen best block: ").append(getLastBlockSeenHeight()).append(" (")
.append(lastBlockSeenTime == null ? "time unknown" : TimeUtils.dateTimeFormat(lastBlockSeenTime))
.append(getLastBlockSeenTimeInstant()
.map(instant -> TimeUtils.dateTimeFormat(instant.toEpochMilli()))
.orElse("time unknown"))
.append("): ").append(getLastBlockSeenHash()).append('\n');
final KeyCrypter crypter = keyChainGroup.getKeyCrypter();
if (crypter != null)
@ -3667,45 +3668,58 @@ public class Wallet extends BaseTaggableObject
}
}
public void setLastBlockSeenTimeSecs(long timeSecs) {
public void setLastBlockSeenTime(Instant time) {
lock.lock();
try {
lastBlockSeenTimeSecs = timeSecs;
lastBlockSeenTime = checkNotNull(time);
} finally {
lock.unlock();
}
}
public void clearLastBlockSeenTime() {
lock.lock();
try {
lastBlockSeenTime = null;
} finally {
lock.unlock();
}
}
/** @deprecated use {@link #setLastBlockSeenTime(Instant)} or {@link #clearLastBlockSeenTime()} */
@Deprecated
public void setLastBlockSeenTimeSecs(long timeSecs) {
checkArgument(timeSecs > 0);
setLastBlockSeenTime(Instant.ofEpochSecond(timeSecs));
}
/**
* Returns the UNIX time in seconds since the epoch extracted from the last best seen block header. This timestamp
* Returns time extracted from the last best seen block header, or empty. This timestamp
* is <b>not</b> 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() {
public Optional<Instant> getLastBlockSeenTimeInstant() {
lock.lock();
try {
return lastBlockSeenTimeSecs;
return Optional.ofNullable(lastBlockSeenTime);
} finally {
lock.unlock();
}
}
/**
* Returns a {@link Date} representing the time extracted from the last best seen block header. This timestamp
* is <b>not</b> 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.
*/
/** @deprecated use {@link #getLastBlockSeenTimeInstant()} */
@Deprecated
public long getLastBlockSeenTimeSecs() {
return getLastBlockSeenTimeInstant().map(Instant::getEpochSecond).orElse((long) 0);
}
/** @deprecated use {@link #getLastBlockSeenTimeInstant()} */
@Deprecated
@Nullable
public Date getLastBlockSeenTime() {
final long secs = getLastBlockSeenTimeSecs();
if (secs == 0)
return null;
else
return new Date(secs * 1000);
return getLastBlockSeenTimeInstant().map(Date::from).orElse(null);
}
/**

View File

@ -94,10 +94,11 @@ public class WalletFiles {
// Some other scheduled request already beat us to it.
return null;
}
Date lastBlockSeenTime = wallet.getLastBlockSeenTime();
log.info("Background saving wallet; last seen block is height {}, date {}, hash {}",
wallet.getLastBlockSeenHeight(),
lastBlockSeenTime != null ? TimeUtils.dateTimeFormat(lastBlockSeenTime) : "unknown",
wallet.getLastBlockSeenTimeInstant()
.map(time -> TimeUtils.dateTimeFormat(time.toEpochMilli()))
.orElse("unknown"),
wallet.getLastBlockSeenHash());
saveNowInternal();
return null;
@ -128,9 +129,10 @@ public class WalletFiles {
// but they will serialize (using different temp files).
if (executor.isShutdown())
return;
Date lastBlockSeenTime = wallet.getLastBlockSeenTime();
log.info("Saving wallet; last seen block is height {}, date {}, hash {}", wallet.getLastBlockSeenHeight(),
lastBlockSeenTime != null ? TimeUtils.dateTimeFormat(lastBlockSeenTime) : "unknown",
wallet.getLastBlockSeenTimeInstant()
.map(time -> TimeUtils.dateTimeFormat(time.toEpochMilli()))
.orElse("unknown"),
wallet.getLastBlockSeenHash());
saveNowInternal();
}

View File

@ -201,8 +201,8 @@ public class WalletProtobufSerializer {
walletBuilder.setLastSeenBlockHash(hashToByteString(lastSeenBlockHash));
walletBuilder.setLastSeenBlockHeight(wallet.getLastBlockSeenHeight());
}
if (wallet.getLastBlockSeenTimeSecs() > 0)
walletBuilder.setLastSeenBlockTimeSecs(wallet.getLastBlockSeenTimeSecs());
wallet.getLastBlockSeenTimeInstant().ifPresent(
time -> walletBuilder.setLastSeenBlockTimeSecs(time.getEpochSecond()));
// Populate the scrypt parameters.
KeyCrypter keyCrypter = wallet.getKeyCrypter();
@ -525,7 +525,7 @@ public class WalletProtobufSerializer {
// Should mirror Wallet.reset()
wallet.setLastBlockSeenHash(null);
wallet.setLastBlockSeenHeight(-1);
wallet.setLastBlockSeenTimeSecs(0);
wallet.clearLastBlockSeenTime();
} else {
// Read all transactions and insert into the txMap.
for (Protos.Transaction txProto : walletProto.getTransactionList()) {
@ -550,7 +550,7 @@ public class WalletProtobufSerializer {
wallet.setLastBlockSeenHeight(walletProto.getLastSeenBlockHeight());
}
// Will default to zero if not present.
wallet.setLastBlockSeenTimeSecs(walletProto.getLastSeenBlockTimeSecs());
wallet.setLastBlockSeenTime(Instant.ofEpochSecond(walletProto.getLastSeenBlockTimeSecs()));
if (walletProto.hasKeyRotationTime()) {
wallet.setKeyRotationTime(Instant.ofEpochSecond(walletProto.getKeyRotationTime()));

View File

@ -37,6 +37,7 @@ import org.bitcoinj.wallet.DefaultRiskAnalysis.RuleViolation;
import org.junit.Before;
import org.junit.Test;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -61,7 +62,7 @@ public class DefaultRiskAnalysisTest {
Context.propagate(new Context());
wallet = Wallet.createDeterministic(MAINNET, ScriptType.P2PKH);
wallet.setLastBlockSeenHeight(1000);
wallet.setLastBlockSeenTimeSecs(TIMESTAMP);
wallet.setLastBlockSeenTime(Instant.ofEpochSecond(TIMESTAMP));
}
@Test(expected = IllegalStateException.class)

View File

@ -1538,7 +1538,7 @@ 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(b10.getTimeInstant(), wallet.getLastBlockSeenTimeInstant().get());
assertEquals(1, wallet.getLastBlockSeenHeight());
// Receive a block on the side chain - this should not change the last block seen hash.
chain.add(b11);