mirror of
https://github.com/sparrowwallet/drongo.git
synced 2024-12-27 02:26:44 +00:00
sign psbt inputs
This commit is contained in:
parent
9e6cdf74f4
commit
af562dad10
5 changed files with 49 additions and 18 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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());
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
Loading…
Reference in a new issue