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();
}
public static ExtendedKey fromDescriptor(String extPubKey) {
byte[] serializedKey = Base58.decodeChecked(extPubKey);
public static ExtendedKey fromDescriptor(String descriptor) {
byte[] serializedKey = Base58.decodeChecked(descriptor);
ByteBuffer buffer = ByteBuffer.wrap(serializedKey);
int header = buffer.getInt();
if(!Header.isValidHeader(header)) {
int headerInt = buffer.getInt();
Header header = Header.getHeader(headerInt);
if(header == null) {
throw new IllegalArgumentException("Unknown header bytes: " + DeterministicKey.toBase58(serializedKey).substring(0, 4));
}
@ -106,8 +107,13 @@ public class ExtendedKey {
throw new IllegalArgumentException("Found unexpected data in key");
}
DeterministicKey pubKey = new DeterministicKey(path, chainCode, new LazyECPoint(ECKey.CURVE.getCurve(), data), depth, parentFingerprint);
return new ExtendedKey(pubKey, parentFingerprint, childNumber);
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);
return new ExtendedKey(pubKey, parentFingerprint, childNumber);
}
}
public static boolean isValid(String extPubKey) {
@ -206,14 +212,14 @@ public class ExtendedKey {
return name.endsWith("prv");
}
public static boolean isValidHeader(int header) {
public static Header getHeader(int header) {
for(Header extendedKeyHeader : Header.values()) {
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<>();
for(Keystore keystore : wallet.getKeystores()) {
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());
@ -119,7 +119,7 @@ public class PSBT {
Map<ECKey, KeyDerivation> derivedPublicKeys = new LinkedHashMap<>();
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());

View file

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

View file

@ -101,14 +101,26 @@ public class Keystore {
public ExtendedKey getExtendedPrivateKey() throws MnemonicException {
List<ChildNumber> derivation = getKeyDerivation().getDerivation();
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());
}
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));
return extendedPublicKey.getKey(derivation);
}

View file

@ -169,7 +169,7 @@ public class Wallet {
}
Keystore keystore = getKeystores().get(0);
return keystore.getKey(keyPurpose, index);
return keystore.getPubKey(keyPurpose, index);
}
public List<ECKey> getPubKeys(WalletNode node) {
@ -183,7 +183,7 @@ public class Wallet {
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) {
@ -615,7 +615,7 @@ public class Wallet {
for(PSBTInput psbtInput : signingNodes.keySet()) {
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()); },
LinkedHashMap::new));
@ -628,6 +628,19 @@ public class Wallet {
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() {
for(KeyPurpose keyPurpose : KeyPurpose.values()) {
for(WalletNode addressNode : getNode(keyPurpose).getChildren()) {