burrow/src/main/java/io/rudefox/cold/WalletCommand.java

137 lines
5.4 KiB
Java

package io.rudefox.cold;
import com.bjdweck.bitcoin.address.BitcoinAddress;
import com.bjdweck.bitcoin.extendedkey.AddressChain;
import com.bjdweck.bitcoin.extendedkey.DerivationPath;
import com.bjdweck.bitcoin.extendedkey.KeyOrigin;
import com.bjdweck.bitcoin.extendedkey.PrivateExtendedKey;
import com.bjdweck.bitcoin.hdwallet.Bip44Purpose;
import com.bjdweck.bitcoin.hdwallet.Bip44SigningAccount;
import com.bjdweck.bitcoin.hdwallet.Bip44Wallet;
import com.bjdweck.bitcoin.mnemonic.Mnemonic;
import com.bjdweck.bitcoin.mnemonic.Seed;
import com.bjdweck.bitcoin.psbt.Psbt;
import com.bjdweck.bitcoin.transaction.scriptpubkey.ScriptPubKey;
import picocli.CommandLine;
@SuppressWarnings("ALL")
@CommandLine.Command(name = "wallet", description = "BIP44 wallet operations")
public class WalletCommand implements Runnable {
@CommandLine.ParentCommand
private RudefoxCold globalOptions;
@CommandLine.Option(names = {"-s", "--sentence"}, description = "mnemonic sentence", required = true)
String sentence;
@CommandLine.Option(names = {"-p", "--passphrase"}, description = "optional mnemonic passphrase")
String passphrase;
Bip44Wallet getWallet() {
Seed seed = Mnemonic.fromSentence(sentence).generateSeed(passphrase);
return new Bip44Wallet(PrivateExtendedKey.fromSeed(seed), globalOptions.getNetworkParameters());
}
Bip44SigningAccount getAccount(Bip44Purpose purpose, int accountSeq) {
return getWallet().deriveAccount(purpose, accountSeq);
}
@CommandLine.Command(name = "listxpub", description = "list xpub (default: SLIP-0132)")
public int listXpub(
@CommandLine.Option(names = {"-q", "--qrcode"}, description = "output QR code")
boolean isQRCode,
@CommandLine.Option(names = {"-p", "--purpose"}, description = "purpose",
paramLabel = "[P2PKH, P2SH_P2WPKH, P2WPKH, P2SH_MultiSigP2WSH, MultiSigP2WSH]",
defaultValue = "P2PKH")
Bip44Purpose purpose,
@CommandLine.Option(names = {"-a", "--account"}, description = "account sequence number",
paramLabel = "n", defaultValue = "0")
int accountSeq) {
Bip44SigningAccount bip44SigningAccount = getAccount(purpose, accountSeq);
String slip0132 = bip44SigningAccount.toXpubSlip0132Base58();
String keyOrigin = bip44SigningAccount.getKeyOrigin().toString();
if (isQRCode) {
System.out.println(QRCode.toQRCode(slip0132));
return 0;
}
System.out.printf("[%s]%s%n", keyOrigin, slip0132);
return 0;
}
@CommandLine.Command(name = "listaddress", description = "list account addresses")
public int listAddress(
@CommandLine.Option(names = {"-p", "--purpose"}, description = "purpose",
paramLabel = "[P2PKH, P2SH_P2WPKH, P2WPKH, P2SH_MultiSigP2WSH, MultiSigP2WSH]",
defaultValue = "P2PKH")
Bip44Purpose purpose,
@CommandLine.Option(names = {"-a", "--account"}, description = "account sequence number",
paramLabel = "<n>", defaultValue = "0")
int accountSeq,
@CommandLine.Option(names = {"-c", "--address-chain"}, description = "address chain",
paramLabel = "[INTERNAL|EXTERNAL]", defaultValue = "EXTERNAL")
AddressChain addressChain,
@CommandLine.Option(names = {"-s", "--address-seq"}, description = "starting address sequence number",
paramLabel = "<n>", defaultValue = "0")
int addressSeq,
@CommandLine.Option(names = {"-n", "--count"}, description = "number of addresses to list",
paramLabel = "<n>", defaultValue = "10")
int count) {
Bip44SigningAccount account = getAccount(purpose, accountSeq);
for (int i = 0; i < count; i++) {
int seq = addressSeq + i;
DerivationPath addressPath = DerivationPath.m().slash(addressChain.getSequence()).slash(seq);
KeyOrigin keyOrigin = account.getKeyOrigin().combineRelativePath(addressPath);
ScriptPubKey scriptPubKey = account.deriveScriptPubKey(addressChain, seq);
BitcoinAddress address = scriptPubKey.getAddress(globalOptions.getNetworkParameters());
System.out.printf("%s: %s%n", keyOrigin, address);
}
return 0;
}
@CommandLine.Command(name = "signpsbt", description = "sign a PSBT")
public int signPsbt(
@CommandLine.Parameters()
String psbtBase64) {
Psbt psbt = Psbt.fromBase64(psbtBase64);
psbt.getValidationViolationMap();
/*
Satoshi fee = psbt.getFee();
Satoshi spendAmount = psbt.getSpendAmount(getWallet());
BitcoinAddress spendAddress = psbt.getSpendAddress(getWallet());
System.out.printf("Confirm spend of %s to %s with a fee of %s", spendAmount.toBitcoinString(), spendAddress.getAddressString(), fee.toBitcoinString());
*/
psbt.signPsbt(getWallet());
System.out.println(psbt.toBase64());
return 0;
}
@CommandLine.Spec
CommandLine.Model.CommandSpec commandSpec;
public void run() {
throw new CommandLine.ParameterException(commandSpec.commandLine(), "Missing required wallet subcommand");
}
}