sign psbt inputs

This commit is contained in:
Craig Raw 2020-07-22 14:56:41 +02:00
parent 9e6cdf74f4
commit af562dad10
5 changed files with 49 additions and 18 deletions

View file

@ -73,11 +73,12 @@ public class ExtendedKey {
return buffer.array(); return buffer.array();
} }
public static ExtendedKey fromDescriptor(String extPubKey) { public static ExtendedKey fromDescriptor(String descriptor) {
byte[] serializedKey = Base58.decodeChecked(extPubKey); byte[] serializedKey = Base58.decodeChecked(descriptor);
ByteBuffer buffer = ByteBuffer.wrap(serializedKey); ByteBuffer buffer = ByteBuffer.wrap(serializedKey);
int header = buffer.getInt(); int headerInt = buffer.getInt();
if(!Header.isValidHeader(header)) { Header header = Header.getHeader(headerInt);
if(header == null) {
throw new IllegalArgumentException("Unknown header bytes: " + DeterministicKey.toBase58(serializedKey).substring(0, 4)); throw new IllegalArgumentException("Unknown header bytes: " + DeterministicKey.toBase58(serializedKey).substring(0, 4));
} }
@ -106,9 +107,14 @@ public class ExtendedKey {
throw new IllegalArgumentException("Found unexpected data in key"); throw new IllegalArgumentException("Found unexpected data in key");
} }
if(header.isPrivate()) {
DeterministicKey prvKey = HDKeyDerivation.createMasterPrivKeyFromBytes(Arrays.copyOfRange(data, 1, 33), chainCode, path);
return new ExtendedKey(prvKey, parentFingerprint, childNumber);
} else {
DeterministicKey pubKey = new DeterministicKey(path, chainCode, new LazyECPoint(ECKey.CURVE.getCurve(), data), depth, parentFingerprint); DeterministicKey pubKey = new DeterministicKey(path, chainCode, new LazyECPoint(ECKey.CURVE.getCurve(), data), depth, parentFingerprint);
return new ExtendedKey(pubKey, parentFingerprint, childNumber); return new ExtendedKey(pubKey, parentFingerprint, childNumber);
} }
}
public static boolean isValid(String extPubKey) { public static boolean isValid(String extPubKey) {
try { try {
@ -206,14 +212,14 @@ public class ExtendedKey {
return name.endsWith("prv"); return name.endsWith("prv");
} }
public static boolean isValidHeader(int header) { public static Header getHeader(int header) {
for(Header extendedKeyHeader : Header.values()) { for(Header extendedKeyHeader : Header.values()) {
if(header == extendedKeyHeader.header) { if(header == extendedKeyHeader.header) {
return true; return extendedKeyHeader;
} }
} }
return false; return null;
} }
} }
} }

View file

@ -84,7 +84,7 @@ public class PSBT {
Map<ECKey, KeyDerivation> derivedPublicKeys = new LinkedHashMap<>(); Map<ECKey, KeyDerivation> derivedPublicKeys = new LinkedHashMap<>();
for(Keystore keystore : wallet.getKeystores()) { for(Keystore keystore : wallet.getKeystores()) {
WalletNode walletNode = utxoEntry.getValue(); WalletNode walletNode = utxoEntry.getValue();
derivedPublicKeys.put(keystore.getKey(walletNode.getKeyPurpose(), walletNode.getIndex()), keystore.getKeyDerivation()); derivedPublicKeys.put(keystore.getPubKey(walletNode), keystore.getKeyDerivation());
} }
PSBTInput psbtInput = new PSBTInput(wallet.getScriptType(), transaction, inputIndex, utxo, utxoIndex, redeemScript, witnessScript, derivedPublicKeys, Collections.emptyMap()); PSBTInput psbtInput = new PSBTInput(wallet.getScriptType(), transaction, inputIndex, utxo, utxoIndex, redeemScript, witnessScript, derivedPublicKeys, Collections.emptyMap());
@ -119,7 +119,7 @@ public class PSBT {
Map<ECKey, KeyDerivation> derivedPublicKeys = new LinkedHashMap<>(); Map<ECKey, KeyDerivation> derivedPublicKeys = new LinkedHashMap<>();
for(Keystore keystore : wallet.getKeystores()) { for(Keystore keystore : wallet.getKeystores()) {
derivedPublicKeys.put(keystore.getKey(outputNode.getKeyPurpose(), outputNode.getIndex()), keystore.getKeyDerivation()); derivedPublicKeys.put(keystore.getPubKey(outputNode), keystore.getKeyDerivation());
} }
PSBTOutput walletOutput = new PSBTOutput(redeemScript, witnessScript, derivedPublicKeys, Collections.emptyMap()); PSBTOutput walletOutput = new PSBTOutput(redeemScript, witnessScript, derivedPublicKeys, Collections.emptyMap());

View file

@ -280,7 +280,7 @@ public class PSBTInput {
} }
} }
boolean sign(ECKey privKey) { public boolean sign(ECKey privKey) {
SigHash localSigHash = getSigHash(); SigHash localSigHash = getSigHash();
if(localSigHash == null) { if(localSigHash == null) {
//Assume SigHash.ALL //Assume SigHash.ALL

View file

@ -101,14 +101,26 @@ public class Keystore {
public ExtendedKey getExtendedPrivateKey() throws MnemonicException { public ExtendedKey getExtendedPrivateKey() throws MnemonicException {
List<ChildNumber> derivation = getKeyDerivation().getDerivation(); List<ChildNumber> derivation = getKeyDerivation().getDerivation();
DeterministicKey derivedKey = getExtendedMasterPrivateKey().getKey(derivation); DeterministicKey derivedKey = getExtendedMasterPrivateKey().getKey(derivation);
return new ExtendedKey(derivedKey, derivedKey.getParentFingerprint(), derivation.get(derivation.size() - 1)); ExtendedKey xprv = new ExtendedKey(derivedKey, derivedKey.getParentFingerprint(), derivation.get(derivation.size() - 1));
//Recreate from xprv string to reset path to single ChildNumber at the derived depth
return ExtendedKey.fromDescriptor(xprv.toString());
} }
public DeterministicKey getKey(WalletNode walletNode) { public DeterministicKey getKey(WalletNode walletNode) throws MnemonicException {
return getKey(walletNode.getKeyPurpose(), walletNode.getIndex()); return getKey(walletNode.getKeyPurpose(), walletNode.getIndex());
} }
public DeterministicKey getKey(KeyPurpose keyPurpose, int keyIndex) { public DeterministicKey getKey(KeyPurpose keyPurpose, int keyIndex) throws MnemonicException {
ExtendedKey extendedPrivateKey = getExtendedPrivateKey();
List<ChildNumber> derivation = List.of(extendedPrivateKey.getKeyChildNumber(), keyPurpose.getPathIndex(), new ChildNumber(keyIndex));
return extendedPrivateKey.getKey(derivation);
}
public DeterministicKey getPubKey(WalletNode walletNode) {
return getPubKey(walletNode.getKeyPurpose(), walletNode.getIndex());
}
public DeterministicKey getPubKey(KeyPurpose keyPurpose, int keyIndex) {
List<ChildNumber> derivation = List.of(extendedPublicKey.getKeyChildNumber(), keyPurpose.getPathIndex(), new ChildNumber(keyIndex)); List<ChildNumber> derivation = List.of(extendedPublicKey.getKeyChildNumber(), keyPurpose.getPathIndex(), new ChildNumber(keyIndex));
return extendedPublicKey.getKey(derivation); return extendedPublicKey.getKey(derivation);
} }

View file

@ -169,7 +169,7 @@ public class Wallet {
} }
Keystore keystore = getKeystores().get(0); Keystore keystore = getKeystores().get(0);
return keystore.getKey(keyPurpose, index); return keystore.getPubKey(keyPurpose, index);
} }
public List<ECKey> getPubKeys(WalletNode node) { public List<ECKey> getPubKeys(WalletNode node) {
@ -183,7 +183,7 @@ public class Wallet {
throw new UnsupportedOperationException("Cannot determine public keys for a custom policy"); throw new UnsupportedOperationException("Cannot determine public keys for a custom policy");
} }
return getKeystores().stream().map(keystore -> keystore.getKey(keyPurpose, index)).collect(Collectors.toList()); return getKeystores().stream().map(keystore -> keystore.getPubKey(keyPurpose, index)).collect(Collectors.toList());
} }
public Address getAddress(WalletNode node) { public Address getAddress(WalletNode node) {
@ -615,7 +615,7 @@ public class Wallet {
for(PSBTInput psbtInput : signingNodes.keySet()) { for(PSBTInput psbtInput : signingNodes.keySet()) {
WalletNode walletNode = signingNodes.get(psbtInput); WalletNode walletNode = signingNodes.get(psbtInput);
Map<ECKey, Keystore> keystoreKeysForNode = getKeystores().stream().collect(Collectors.toMap(keystore -> keystore.getKey(walletNode), Function.identity(), Map<ECKey, Keystore> keystoreKeysForNode = getKeystores().stream().collect(Collectors.toMap(keystore -> keystore.getPubKey(walletNode), Function.identity(),
(u, v) -> { throw new IllegalStateException("Duplicate keys from different keystores for node " + walletNode.getDerivationPath()); }, (u, v) -> { throw new IllegalStateException("Duplicate keys from different keystores for node " + walletNode.getDerivationPath()); },
LinkedHashMap::new)); LinkedHashMap::new));
@ -628,6 +628,19 @@ public class Wallet {
return signedKeystores; return signedKeystores;
} }
public void sign(PSBT psbt) throws MnemonicException {
Map<PSBTInput, WalletNode> signingNodes = getSigningNodes(psbt);
for(Keystore keystore : getKeystores()) {
if(keystore.hasSeed()) {
for(Map.Entry<PSBTInput, WalletNode> signingEntry : signingNodes.entrySet()) {
ECKey privKey = keystore.getKey(signingEntry.getValue());
PSBTInput psbtInput = signingEntry.getKey();
psbtInput.sign(privKey);
}
}
}
}
public BitcoinUnit getAutoUnit() { public BitcoinUnit getAutoUnit() {
for(KeyPurpose keyPurpose : KeyPurpose.values()) { for(KeyPurpose keyPurpose : KeyPurpose.values()) {
for(WalletNode addressNode : getNode(keyPurpose).getChildren()) { for(WalletNode addressNode : getNode(keyPurpose).getChildren()) {