3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-07 06:44:16 +00:00

Signers: Share keypaths for watching wallets

Now LocalTransactionSigner populates ProposedTransaction.keyPaths map even if
there is no private key in the wallet. Other signers may take advantage
of that.
This commit is contained in:
Kosta Korenkov 2014-09-05 16:46:06 +07:00 committed by Mike Hearn
parent 5910a7f25e
commit f4879e22a6
2 changed files with 54 additions and 5 deletions

View File

@ -70,6 +70,16 @@ public class LocalTransactionSigner extends StatelessTransactionSigner {
}
RedeemData redeemData = txIn.getConnectedRedeemData(keyBag);
Script scriptPubKey = txIn.getConnectedOutput().getScriptPubKey();
// For P2SH inputs we need to share derivation path of the signing key with other signers, so that they
// use correct key to calculate their signatures.
// Married keys all have the same derivation path, so we can safely just take first one here.
ECKey pubKey = redeemData.keys.get(0);
if (pubKey instanceof DeterministicKey)
propTx.keyPaths.put(scriptPubKey, (((DeterministicKey) pubKey).getPath()));
ECKey key;
// locate private key in redeem data. For pay-to-address and pay-to-key inputs RedeemData will always contain
// only one key (with private bytes). For P2SH inputs RedeemData will contain multiple keys, one of which MAY
@ -79,7 +89,6 @@ public class LocalTransactionSigner extends StatelessTransactionSigner {
continue;
}
Script scriptPubKey = txIn.getConnectedOutput().getScriptPubKey();
Script inputScript = txIn.getScriptSig();
// script here would be either a standard CHECKSIG program for pay-to-address or pay-to-pubkey inputs or
// a CHECKMULTISIG program for P2SH inputs
@ -98,10 +107,6 @@ public class LocalTransactionSigner extends StatelessTransactionSigner {
inputScript = scriptPubKey.getScriptSigWithSignature(inputScript, signature.encodeToBitcoin(), sigIndex);
txIn.setScriptSig(inputScript);
// for P2SH inputs we need to share derivation path of the signing key with other signers, so that they
// use correct key to calculate their signatures
if (key instanceof DeterministicKey)
propTx.keyPaths.put(scriptPubKey, (((DeterministicKey) key).getPath()));
} catch (ECKey.KeyIsEncryptedException e) {
throw e;
} catch (ECKey.MissingPrivateKeyException e) {

View File

@ -19,6 +19,7 @@ package com.google.bitcoin.core;
import com.google.bitcoin.core.Wallet.SendRequest;
import com.google.bitcoin.crypto.*;
import com.google.bitcoin.signers.StatelessTransactionSigner;
import com.google.bitcoin.signers.TransactionSigner;
import com.google.bitcoin.store.BlockStoreException;
import com.google.bitcoin.store.MemoryBlockStore;
@ -2620,6 +2621,49 @@ public class WalletTest extends TestWithWallet {
assertTrue(wallet.getTransactionSigners().get(1).isReady());
}
@Test
public void watchingMarriedWallet() throws Exception {
DeterministicKey watchKey = wallet.getWatchingKey();
String serialized = watchKey.serializePubB58();
watchKey = DeterministicKey.deserializeB58(null, serialized);
Wallet wallet = Wallet.fromWatchingKey(params, watchKey);
blockStore = new MemoryBlockStore(params);
chain = new BlockChain(params, wallet, blockStore);
final DeterministicKeyChain keyChain = new DeterministicKeyChain(new SecureRandom());
DeterministicKey partnerKey = DeterministicKey.deserializeB58(null, keyChain.getWatchingKey().serializePubB58());
TransactionSigner signer = new StatelessTransactionSigner() {
@Override
public boolean isReady() {
return true;
}
@Override
public boolean signInputs(ProposedTransaction propTx, KeyBag keyBag) {
assertEquals(propTx.partialTx.getInputs().size(), propTx.keyPaths.size());
List<ChildNumber> externalZeroLeaf = ImmutableList.<ChildNumber>builder()
.addAll(DeterministicKeyChain.EXTERNAL_PATH).add(ChildNumber.ZERO).build();
for (TransactionInput input : propTx.partialTx.getInputs()) {
List<ChildNumber> keypath = propTx.keyPaths.get(input.getConnectedOutput().getScriptPubKey());
assertNotNull(keypath);
assertEquals(externalZeroLeaf, keypath);
}
return true;
}
};
wallet.addTransactionSigner(signer);
wallet.addFollowingAccountKeys(ImmutableList.of(partnerKey));
myAddress = wallet.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS);
sendMoneyToWallet(wallet, COIN, myAddress, AbstractBlockChain.NewBlockType.BEST_CHAIN);
ECKey dest = new ECKey();
Wallet.SendRequest req = Wallet.SendRequest.emptyWallet(dest.toAddress(params));
req.missingSigsMode = Wallet.MissingSigsMode.USE_DUMMY_SIG;
wallet.completeTx(req);
}
@Test
public void sendRequestExchangeRate() throws Exception {
receiveATransaction(wallet, myAddress);