This commit is contained in:
Sean Gilligan 2025-03-10 13:51:32 +00:00 committed by GitHub
commit 13eac0c6d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 43 additions and 3 deletions

View file

@ -34,6 +34,7 @@ import org.bitcoinj.wallet.Wallet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
@ -41,6 +42,7 @@ import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import static org.bitcoinj.base.internal.Preconditions.checkArgument;
import static org.bitcoinj.base.internal.Preconditions.checkState;
@ -160,6 +162,25 @@ public class TransactionOutput {
return size;
}
/**
* Can this output be converted to an address (for a given network.)
* @return true if this output has an address, false otherwise
*/
public boolean hasAddress() {
Script script = getScriptPubKey();
return ScriptPattern.isP2PKH(script) || ScriptPattern.isP2WPKH(script) || ScriptPattern.isP2TR(script) || ScriptPattern.isP2SH(script);
}
/**
* Get the address for this output.
* @param network the network this output exists on
* @return The address for this output or empty if an address can't be extracted
*/
public Optional<Address> getAddress(@Nonnull Network network) {
Objects.requireNonNull(network);
return Optional.ofNullable(hasAddress() ? getScriptPubKey().getToAddress(network) : null);
}
/**
* Returns the value of this output. This is the amount of currency that the destination address
* receives.
@ -357,11 +378,10 @@ public class TransactionOutput {
buf.append(Coin.valueOf(value).toFriendlyString());
try {
Script script = getScriptPubKey();
if (ScriptPattern.isP2PKH(script) || ScriptPattern.isP2WPKH(script) || ScriptPattern.isP2TR(script)
|| ScriptPattern.isP2SH(script)) {
if (hasAddress()) {
buf.append(" to ").append(script.getScriptType().name());
if (network != null)
buf.append(" ").append(script.getToAddress(network));
buf.append(" ").append(getAddress(network).get());
} else if (ScriptPattern.isP2PK(script)) {
buf.append(" to pubkey ").append(ByteUtils.formatHex(ScriptPattern.extractKeyFromP2PK(script)));
} else if (ScriptPattern.isSentToMultisig(script)) {

View file

@ -107,6 +107,7 @@ public class WalletTool implements Callable<Integer> {
" dump Loads and prints the given wallet in textual form to stdout. Private keys and seed are only printed if --dump-privkeys is specified. If the wallet is encrypted, also specify the --password option to dump the private keys and seed.%n" +
" If --dump-lookahead is present, also show pregenerated but not yet issued keys.%n" +
" raw-dump Prints the wallet as a raw protobuf with no parsing or sanity checking applied.%n" +
" listunspent Prints unspent outputs in a simple format%n" +
" create Makes a new wallet in the file specified by --wallet. Will complain and require --force if the wallet already exists.%n" +
" If --seed is present, it should specify either a mnemonic code or hex/base58 raw seed bytes.%n" +
" If --watchkey is present, it creates a watching wallet using the specified base58 xpub.%n" +
@ -291,6 +292,7 @@ public class WalletTool implements Callable<Integer> {
public enum ActionEnum {
DUMP,
RAW_DUMP,
LISTUNSPENT,
CREATE,
ADD_KEY,
ADD_ADDR,
@ -391,6 +393,7 @@ public class WalletTool implements Callable<Integer> {
// What should we do?
switch (action) {
case DUMP: dumpWallet(); break;
case LISTUNSPENT: listUnspent(); break;
case ADD_KEY: addKey(); break;
case ADD_ADDR: addAddr(); break;
case DELETE_KEY: deleteKey(); break;
@ -1100,6 +1103,23 @@ public class WalletTool implements Callable<Integer> {
}
}
private void listUnspent() throws BlockStoreException {
// Setup to get the chain height so we can estimate lock times, but don't wipe the transactions if it's not
// there just for the listUnspent case.
if (chainFile.exists())
setup();
List<TransactionOutput> unspent = wallet.getUnspents();
Coin total = unspent.stream()
.map(TransactionOutput::getValue)
.reduce(Coin.ZERO, Coin::add);
unspent.forEach(output -> {
String addressInfo = output.getAddress(net).map(Object::toString).orElse("address unavailable");
System.out.printf("%s: %s\n", addressInfo , output.getValue().toPlainString());
});
System.out.printf("\nTotal: %s\n", total.toPlainString());
}
private void printWallet(@Nullable AesKey aesKey) {
System.out.println(wallet.toString(dumpLookAhead, dumpPrivKeys, aesKey, true, true, chain));
}