mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-02-23 14:40:40 +01:00
Script: migrate creationTime
field to java.time
API
This commit is contained in:
parent
9ba34fc2ed
commit
a61da4fff6
6 changed files with 111 additions and 35 deletions
|
@ -54,6 +54,7 @@ import java.io.OutputStream;
|
|||
import java.math.BigInteger;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -61,6 +62,7 @@ import java.util.EnumSet;
|
|||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
@ -222,8 +224,8 @@ public class Script {
|
|||
// must preserve the exact bytes that we read off the wire, along with the parsed form.
|
||||
protected byte[] program;
|
||||
|
||||
// Creation time of the associated keys in seconds since the epoch.
|
||||
private long creationTimeSeconds;
|
||||
// Creation time of the associated keys, or null if unknown.
|
||||
@Nullable private Instant creationTime;
|
||||
|
||||
/** Creates an empty script that serializes to nothing. */
|
||||
private Script() {
|
||||
|
@ -233,32 +235,71 @@ public class Script {
|
|||
// Used from ScriptBuilder.
|
||||
Script(List<ScriptChunk> chunks) {
|
||||
this.chunks = Collections.unmodifiableList(new ArrayList<>(chunks));
|
||||
creationTimeSeconds = TimeUtils.currentTimeSeconds();
|
||||
creationTime = TimeUtils.currentTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a Script that copies and wraps the programBytes array. The array is parsed and checked for syntactic
|
||||
* validity. Use this if the creation time is not known.
|
||||
* @param programBytes Array of program bytes from a transaction.
|
||||
*/
|
||||
public Script(byte[] programBytes) throws ScriptException {
|
||||
this.program = programBytes;
|
||||
parse(programBytes);
|
||||
this.creationTime = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a Script that copies and wraps the programBytes array. The array is parsed and checked for syntactic
|
||||
* validity.
|
||||
* @param programBytes Array of program bytes from a transaction.
|
||||
* @param creationTime creation time of the script
|
||||
*/
|
||||
public Script(byte[] programBytes) throws ScriptException {
|
||||
program = programBytes;
|
||||
public Script(byte[] programBytes, Instant creationTime) throws ScriptException {
|
||||
this.program = programBytes;
|
||||
parse(programBytes);
|
||||
creationTimeSeconds = 0;
|
||||
this.creationTime = checkNotNull(creationTime);
|
||||
}
|
||||
|
||||
public Script(byte[] programBytes, long creationTimeSeconds) throws ScriptException {
|
||||
program = programBytes;
|
||||
parse(programBytes);
|
||||
this.creationTimeSeconds = creationTimeSeconds;
|
||||
/**
|
||||
* Gets the creation time of this script, or empty if unknown.
|
||||
* @return creation time of this script, or empty if unknown
|
||||
*/
|
||||
public Optional<Instant> getCreationTime() {
|
||||
return Optional.ofNullable(creationTime);
|
||||
}
|
||||
|
||||
/** @deprecated use {@link #getCreationTime()} */
|
||||
@Deprecated
|
||||
public long getCreationTimeSeconds() {
|
||||
return creationTimeSeconds;
|
||||
return getCreationTime().orElse(Instant.EPOCH).getEpochSecond();
|
||||
}
|
||||
|
||||
public void setCreationTimeSeconds(long creationTimeSeconds) {
|
||||
this.creationTimeSeconds = creationTimeSeconds;
|
||||
/**
|
||||
* Sets the creation time of this script.
|
||||
* @param creationTime creation time of this script
|
||||
*/
|
||||
public void setCreationTime(Instant creationTime) {
|
||||
this.creationTime = checkNotNull(creationTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the creation time of this script. This is mainly used deserialization and cloning. Normally you should not
|
||||
* need to usethis, as keys should have proper creation times whenever possible.
|
||||
*/
|
||||
public void clearCreationTime() {
|
||||
this.creationTime = null;
|
||||
}
|
||||
|
||||
/** @deprecated use {@link #setCreationTime(Instant)} */
|
||||
@Deprecated
|
||||
public void setCreationTimeSeconds(long creationTimeSecs) {
|
||||
if (creationTimeSecs > 0)
|
||||
setCreationTime(Instant.ofEpochSecond(creationTimeSecs));
|
||||
else if (creationTimeSecs == 0)
|
||||
clearCreationTime();
|
||||
else
|
||||
throw new IllegalArgumentException("Cannot set creation time to negative value: " + creationTimeSecs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -252,8 +252,7 @@ public class MarriedKeyChain extends DeterministicKeyChain {
|
|||
builder.append(script.getToAddress(params));
|
||||
builder.append(" hash160:");
|
||||
builder.append(ByteUtils.formatHex(script.getPubKeyHash()));
|
||||
if (script.getCreationTimeSeconds() > 0)
|
||||
builder.append(" creationTimeSeconds:").append(script.getCreationTimeSeconds());
|
||||
script.getCreationTime().ifPresent(creationTime -> builder.append(" creationTimeSeconds:").append(creationTime));
|
||||
builder.append('\n');
|
||||
}
|
||||
|
||||
|
|
|
@ -1038,38 +1038,69 @@ public class Wallet extends BaseTaggableObject
|
|||
* Same as {@link #addWatchedAddress(Address, long)} with the current time as the creation time.
|
||||
*/
|
||||
public boolean addWatchedAddress(final Address address) {
|
||||
long now = TimeUtils.currentTimeSeconds();
|
||||
Instant now = TimeUtils.currentTime();
|
||||
return addWatchedAddresses(Lists.newArrayList(address), now) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given address to the wallet to be watched. Outputs can be retrieved by {@link #getWatchedOutputs(boolean)}.
|
||||
*
|
||||
* @param creationTime creation time in seconds since the epoch, for scanning the blockchain
|
||||
* @param creationTime creation time, for scanning the blockchain
|
||||
* @return whether the address was added successfully (not already present)
|
||||
*/
|
||||
public boolean addWatchedAddress(final Address address, long creationTime) {
|
||||
public boolean addWatchedAddress(final Address address, Instant creationTime) {
|
||||
return addWatchedAddresses(Lists.newArrayList(address), creationTime) == 1;
|
||||
}
|
||||
|
||||
/** @deprecated use {@link #addWatchedAddress(Address, Instant)} or {@link #addWatchedAddress(Address)} */
|
||||
@Deprecated
|
||||
public boolean addWatchedAddress(final Address address, long creationTime) {
|
||||
return creationTime > 0 ?
|
||||
addWatchedAddress(address, Instant.ofEpochSecond(creationTime)) :
|
||||
addWatchedAddress(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given address to the wallet to be watched. Outputs can be retrieved
|
||||
* Adds the given addresses to the wallet to be watched. Outputs can be retrieved
|
||||
* by {@link #getWatchedOutputs(boolean)}.
|
||||
*
|
||||
* @param addresses addresses to be watched
|
||||
* @param creationTime creation time of the addresses
|
||||
* @return how many addresses were added successfully
|
||||
*/
|
||||
public int addWatchedAddresses(final List<Address> addresses, long creationTime) {
|
||||
public int addWatchedAddresses(final List<Address> addresses, Instant creationTime) {
|
||||
List<Script> scripts = new ArrayList<>();
|
||||
|
||||
for (Address address : addresses) {
|
||||
Script script = ScriptBuilder.createOutputScript(address);
|
||||
script.setCreationTimeSeconds(creationTime);
|
||||
script.setCreationTime(creationTime);
|
||||
scripts.add(script);
|
||||
}
|
||||
|
||||
return addWatchedScripts(scripts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given addresses to the wallet to be watched. Outputs can be retrieved
|
||||
* by {@link #getWatchedOutputs(boolean)}. Use this if the creation time of the addresses is unknown.
|
||||
* @param addresses addresses to be watched
|
||||
* @return how many addresses were added successfully
|
||||
*/
|
||||
public int addWatchedAddresses(final List<Address> addresses) {
|
||||
List<Script> scripts = new ArrayList<>();
|
||||
for (Address address : addresses) {
|
||||
Script script = ScriptBuilder.createOutputScript(address);
|
||||
script.clearCreationTime();
|
||||
scripts.add(script);
|
||||
}
|
||||
return addWatchedScripts(scripts);
|
||||
}
|
||||
|
||||
/** @deprecated use {@link #addWatchedAddresses(List, Instant)} or {@link #addWatchedAddresses(List)} */
|
||||
@Deprecated
|
||||
public int addWatchedAddresses(final List<Address> addresses, long creationTime) {
|
||||
return creationTime > 0 ?
|
||||
addWatchedAddresses(addresses, creationTime) :
|
||||
addWatchedAddresses(addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given output scripts to the wallet to be watched. Outputs can be retrieved by {@link #getWatchedOutputs(boolean)}.
|
||||
* If a script is already being watched, the object is replaced with the one in the given list. As {@link Script}
|
||||
|
@ -1088,7 +1119,7 @@ public class Wallet extends BaseTaggableObject
|
|||
// a script in the wallet with an incorrect creation time.
|
||||
if (watchedScripts.contains(script))
|
||||
watchedScripts.remove(script);
|
||||
if (script.getCreationTimeSeconds() == 0)
|
||||
if (!script.getCreationTime().isPresent())
|
||||
log.warn("Adding a script to the wallet with a creation time of zero, this will disable the checkpointing optimization! {}", script);
|
||||
watchedScripts.add(script);
|
||||
added++;
|
||||
|
@ -3596,7 +3627,7 @@ public class Wallet extends BaseTaggableObject
|
|||
try {
|
||||
Instant earliestTime = keyChainGroup.getEarliestKeyCreationTimeInstant();
|
||||
for (Script script : watchedScripts)
|
||||
earliestTime = TimeUtils.earlier(Instant.ofEpochSecond(script.getCreationTimeSeconds()), earliestTime);
|
||||
earliestTime = TimeUtils.earlier(script.getCreationTime().orElse(Instant.EPOCH), earliestTime);
|
||||
return earliestTime;
|
||||
} finally {
|
||||
keyChainGroupLock.unlock();
|
||||
|
|
|
@ -189,7 +189,7 @@ public class WalletProtobufSerializer {
|
|||
Protos.Script protoScript =
|
||||
Protos.Script.newBuilder()
|
||||
.setProgram(ByteString.copyFrom(script.getProgram()))
|
||||
.setCreationTimestamp(script.getCreationTimeSeconds() * 1000)
|
||||
.setCreationTimestamp(script.getCreationTime().orElse(Instant.EPOCH).toEpochMilli())
|
||||
.build();
|
||||
|
||||
walletBuilder.addWatchedScript(protoScript);
|
||||
|
@ -502,9 +502,13 @@ public class WalletProtobufSerializer {
|
|||
List<Script> scripts = new ArrayList<>();
|
||||
for (Protos.Script protoScript : walletProto.getWatchedScriptList()) {
|
||||
try {
|
||||
Script script =
|
||||
new Script(protoScript.getProgram().toByteArray(),
|
||||
protoScript.getCreationTimestamp() / 1000);
|
||||
long creationTimestamp = protoScript.getCreationTimestamp();
|
||||
byte[] programBytes = protoScript.getProgram().toByteArray();
|
||||
Script script;
|
||||
if (creationTimestamp > 0)
|
||||
script = new Script(programBytes, Instant.ofEpochMilli(creationTimestamp));
|
||||
else
|
||||
script = new Script(programBytes);
|
||||
scripts.add(script);
|
||||
} catch (ScriptException e) {
|
||||
throw new UnreadableWalletException("Unparseable script in wallet");
|
||||
|
|
|
@ -67,6 +67,7 @@ import java.math.BigInteger;
|
|||
import java.net.InetAddress;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
|
@ -93,7 +94,7 @@ public class WalletProtobufSerializerTest {
|
|||
private Wallet myWallet;
|
||||
|
||||
public static String WALLET_DESCRIPTION = "The quick brown fox lives in \u4f26\u6566"; // Beijing in Chinese
|
||||
private long mScriptCreationTime;
|
||||
private Instant mScriptCreationTime;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
|
@ -110,7 +111,7 @@ public class WalletProtobufSerializerTest {
|
|||
myAddress = myKey.toAddress(ScriptType.P2PKH, BitcoinNetwork.TESTNET);
|
||||
myWallet = new Wallet(TESTNET, KeyChainGroup.builder(TESTNET).fromRandom(ScriptType.P2PKH).build());
|
||||
myWallet.importKey(myKey);
|
||||
mScriptCreationTime = new Date().getTime() / 1000 - 1234;
|
||||
mScriptCreationTime = TimeUtils.currentTime().minusSeconds(1234);
|
||||
myWallet.addWatchedAddress(myWatchedKey.toAddress(ScriptType.P2PKH, BitcoinNetwork.TESTNET), mScriptCreationTime);
|
||||
myWallet.setDescription(WALLET_DESCRIPTION);
|
||||
}
|
||||
|
@ -125,8 +126,8 @@ public class WalletProtobufSerializerTest {
|
|||
assertArrayEquals(myKey.getPubKey(), foundKey.getPubKey());
|
||||
assertArrayEquals(myKey.getPrivKeyBytes(), foundKey.getPrivKeyBytes());
|
||||
assertEquals(myKey.getCreationTimeSeconds(), foundKey.getCreationTimeSeconds());
|
||||
assertEquals(mScriptCreationTime,
|
||||
wallet1.getWatchedScripts().get(0).getCreationTimeSeconds());
|
||||
assertEquals(mScriptCreationTime.truncatedTo(ChronoUnit.MILLIS),
|
||||
wallet1.getWatchedScripts().get(0).getCreationTime().get());
|
||||
assertEquals(1, wallet1.getWatchedScripts().size());
|
||||
assertEquals(ScriptBuilder.createOutputScript(myWatchedKey.toAddress(ScriptType.P2PKH, BitcoinNetwork.TESTNET)),
|
||||
wallet1.getWatchedScripts().get(0));
|
||||
|
|
|
@ -641,7 +641,7 @@ public class WalletTool implements Callable<Integer> {
|
|||
try {
|
||||
Address address = LegacyAddress.fromBase58(params.network(), addrStr);
|
||||
// If no creation time is specified, assume genesis (zero).
|
||||
wallet.addWatchedAddress(address, getCreationTimeSeconds());
|
||||
wallet.addWatchedAddress(address, Instant.ofEpochSecond(getCreationTimeSeconds()));
|
||||
} catch (AddressFormatException e) {
|
||||
System.err.println("Could not parse given address, or wrong network: " + addrStr);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue