mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-12 10:15:52 +00:00
Determine native segwit destination addresses of a transaction by parsing the segwit scriptPubKey.
This commit is contained in:
parent
52afdc629e
commit
061f5c9841
@ -131,7 +131,7 @@ public class TransactionOutput extends ChildMessage {
|
||||
@Nullable
|
||||
public LegacyAddress getAddressFromP2PKHScript(NetworkParameters networkParameters) throws ScriptException{
|
||||
if (ScriptPattern.isPayToPubKeyHash(getScriptPubKey()))
|
||||
return getScriptPubKey().getToAddress(networkParameters);
|
||||
return (LegacyAddress) getScriptPubKey().getToAddress(networkParameters);
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -151,7 +151,7 @@ public class TransactionOutput extends ChildMessage {
|
||||
@Nullable
|
||||
public LegacyAddress getAddressFromP2SH(NetworkParameters networkParameters) throws ScriptException{
|
||||
if (ScriptPattern.isPayToScriptHash(getScriptPubKey()))
|
||||
return getScriptPubKey().getToAddress(networkParameters);
|
||||
return (LegacyAddress) getScriptPubKey().getToAddress(networkParameters);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -239,22 +239,17 @@ public class Script {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>If a program matches the standard template DUP HASH160 <pubkey hash> EQUALVERIFY CHECKSIG
|
||||
* then this function retrieves the third element.
|
||||
* In this case, this is useful for fetching the destination address of a transaction.</p>
|
||||
* <p>If the program somehow pays to a hash, returns the hash.</p>
|
||||
*
|
||||
* <p>If a program matches the standard template HASH160 <script hash> EQUAL
|
||||
* then this function retrieves the second element.
|
||||
* In this case, this is useful for fetching the hash of the redeem script of a transaction.</p>
|
||||
*
|
||||
* <p>Otherwise it throws a ScriptException.</p>
|
||||
*
|
||||
* <p>Otherwise this method throws a ScriptException.</p>
|
||||
*/
|
||||
public byte[] getPubKeyHash() throws ScriptException {
|
||||
if (ScriptPattern.isPayToPubKeyHash(this))
|
||||
return ScriptPattern.extractHashFromPayToPubKeyHash(this);
|
||||
else if (ScriptPattern.isPayToScriptHash(this))
|
||||
return ScriptPattern.extractHashFromPayToScriptHash(this);
|
||||
else if (ScriptPattern.isPayToWitnessHash(this))
|
||||
return ScriptPattern.extractHashFromPayToWitnessHash(this);
|
||||
else
|
||||
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Script not in the standard scriptPubKey form");
|
||||
}
|
||||
@ -283,7 +278,7 @@ public class Script {
|
||||
/**
|
||||
* Gets the destination address from this script, if it's in the required form.
|
||||
*/
|
||||
public LegacyAddress getToAddress(NetworkParameters params) throws ScriptException {
|
||||
public Address getToAddress(NetworkParameters params) throws ScriptException {
|
||||
return getToAddress(params, false);
|
||||
}
|
||||
|
||||
@ -294,13 +289,15 @@ public class Script {
|
||||
* If true, allow payToPubKey to be casted to the corresponding address. This is useful if you prefer
|
||||
* showing addresses rather than pubkeys.
|
||||
*/
|
||||
public LegacyAddress getToAddress(NetworkParameters params, boolean forcePayToPubKey) throws ScriptException {
|
||||
public Address getToAddress(NetworkParameters params, boolean forcePayToPubKey) throws ScriptException {
|
||||
if (ScriptPattern.isPayToPubKeyHash(this))
|
||||
return LegacyAddress.fromPubKeyHash(params, ScriptPattern.extractHashFromPayToPubKeyHash(this));
|
||||
else if (ScriptPattern.isPayToScriptHash(this))
|
||||
return LegacyAddress.fromP2SHScript(params, this);
|
||||
else if (forcePayToPubKey && ScriptPattern.isPayToPubKey(this))
|
||||
return LegacyAddress.fromKey(params, ECKey.fromPublicOnly(ScriptPattern.extractKeyFromPayToPubKey(this)));
|
||||
else if (ScriptPattern.isPayToWitnessHash(this))
|
||||
return SegwitAddress.fromHash(params, ScriptPattern.extractHashFromPayToWitnessHash(this));
|
||||
else
|
||||
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Cannot cast this script to a pay-to-address type");
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
package org.bitcoinj.script;
|
||||
|
||||
import org.bitcoinj.core.LegacyAddress;
|
||||
import org.bitcoinj.core.SegwitAddress;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
@ -105,6 +106,34 @@ public class ScriptPattern {
|
||||
return script.chunks.get(0).data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this script is of the form OP_0 <hash>. This can either be a P2WPKH or P2WSH scriptPubKey. These
|
||||
* two script types were introduced with segwit.
|
||||
*/
|
||||
public static boolean isPayToWitnessHash(Script script) {
|
||||
List<ScriptChunk> chunks = script.chunks;
|
||||
if (chunks.size() != 2)
|
||||
return false;
|
||||
if (!chunks.get(0).equalsOpCode(OP_0))
|
||||
return false;
|
||||
byte[] chunk1data = chunks.get(1).data;
|
||||
if (chunk1data == null)
|
||||
return false;
|
||||
if (chunk1data.length != SegwitAddress.WITNESS_PROGRAM_LENGTH_PKH
|
||||
&& chunk1data.length != SegwitAddress.WITNESS_PROGRAM_LENGTH_SH)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the pubkey hash from a P2WPKH or the script hash from a P2WSH scriptPubKey. It's important that the
|
||||
* script is in the correct form, so you will want to guard calls to this method with
|
||||
* {@link #isPayToWitnessHash(Script)}.
|
||||
*/
|
||||
public static byte[] extractHashFromPayToWitnessHash(Script script) {
|
||||
return script.chunks.get(1).data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this script matches the format used for multisig outputs: [n] [keys...] [m] CHECKMULTISIG
|
||||
*/
|
||||
|
@ -29,6 +29,7 @@ import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.bitcoinj.core.LegacyAddress;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.AddressFormatException;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
@ -461,7 +462,7 @@ public class LevelDBFullPrunedBlockStore implements FullPrunedBlockStore {
|
||||
}
|
||||
if (txout != null) {
|
||||
Script sc = txout.getScript();
|
||||
LegacyAddress address = sc.getToAddress(params, true);
|
||||
Address address = sc.getToAddress(params, true);
|
||||
UTXO output = new UTXO(txout.getHash(), txout.getIndex(), txout.getValue(), txout.getHeight(),
|
||||
txout.isCoinbase(), txout.getScript(), address.toString());
|
||||
results.add(output);
|
||||
@ -887,7 +888,7 @@ public class LevelDBFullPrunedBlockStore implements FullPrunedBlockStore {
|
||||
String address = out.getAddress();
|
||||
if (address == null || address.equals("")) {
|
||||
Script sc = out.getScript();
|
||||
a = sc.getToAddress(params);
|
||||
a = (LegacyAddress) sc.getToAddress(params);
|
||||
hashBytes = a.getHash();
|
||||
} else {
|
||||
a = LegacyAddress.fromBase58(params, out.getAddress());
|
||||
|
@ -124,8 +124,8 @@ public class KeyChainGroup implements KeyBag {
|
||||
|
||||
if (isMarried()) {
|
||||
for (Map.Entry<KeyChain.KeyPurpose, DeterministicKey> entry : this.currentKeys.entrySet()) {
|
||||
LegacyAddress address = makeP2SHOutputScript(entry.getValue(), getActiveKeyChain()).getToAddress(params);
|
||||
currentAddresses.put(entry.getKey(), address);
|
||||
Address address = makeP2SHOutputScript(entry.getValue(), getActiveKeyChain()).getToAddress(params);
|
||||
currentAddresses.put(entry.getKey(), (LegacyAddress) address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1005,7 +1005,7 @@ public class Wallet extends BaseTaggableObject
|
||||
List<LegacyAddress> addresses = new LinkedList<>();
|
||||
for (Script script : watchedScripts)
|
||||
if (ScriptPattern.isPayToPubKeyHash(script))
|
||||
addresses.add(script.getToAddress(params));
|
||||
addresses.add(((LegacyAddress) script.getToAddress(params)));
|
||||
return addresses;
|
||||
} finally {
|
||||
keyChainGroupLock.unlock();
|
||||
|
@ -28,8 +28,10 @@ import java.util.Locale;
|
||||
|
||||
import org.bitcoinj.params.MainNetParams;
|
||||
import org.bitcoinj.params.TestNet3Params;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.script.Script.ScriptType;
|
||||
import org.bitcoinj.script.ScriptBuilder;
|
||||
import org.bitcoinj.script.ScriptPattern;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
@ -103,6 +105,11 @@ public class SegwitAddressTest {
|
||||
assertEquals(valid.expectedScriptPubKey,
|
||||
Utils.HEX.encode(ScriptBuilder.createOutputScript(address).getProgram()));
|
||||
assertEquals(valid.address.toLowerCase(Locale.ROOT), address.toBech32());
|
||||
if (valid.expectedWitnessVersion == 0) {
|
||||
Script expectedScriptPubKey = new Script(Utils.HEX.decode(valid.expectedScriptPubKey));
|
||||
assertEquals(address, SegwitAddress.fromHash(valid.expectedParams,
|
||||
ScriptPattern.extractHashFromPayToWitnessHash(expectedScriptPubKey)));
|
||||
}
|
||||
assertEquals(valid.expectedWitnessVersion, address.getWitnessVersion());
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,10 @@ package org.bitcoinj.script;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.bitcoinj.core.LegacyAddress;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.SegwitAddress;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.params.MainNetParams;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -42,12 +44,18 @@ public class ScriptPatternTest {
|
||||
assertTrue(ScriptPattern.isPayToScriptHash(
|
||||
ScriptBuilder.createP2SHOutputScript(2, keys)
|
||||
));
|
||||
assertTrue(ScriptPattern.isSentToMultisig(
|
||||
ScriptBuilder.createMultiSigOutputScript(2, keys)
|
||||
));
|
||||
assertTrue(ScriptPattern.isPayToPubKey(
|
||||
ScriptBuilder.createOutputScript(keys.get(0))
|
||||
));
|
||||
assertTrue(ScriptPattern.isPayToWitnessHash(
|
||||
ScriptBuilder.createOutputScript(SegwitAddress.fromHash(MAINNET, keys.get(0).getPubKeyHash()))
|
||||
));
|
||||
assertTrue(ScriptPattern.isPayToWitnessHash(
|
||||
ScriptBuilder.createOutputScript(SegwitAddress.fromHash(MAINNET, Sha256Hash.hash(new byte[0])))
|
||||
));
|
||||
assertTrue(ScriptPattern.isSentToMultisig(
|
||||
ScriptBuilder.createMultiSigOutputScript(2, keys)
|
||||
));
|
||||
assertTrue(ScriptPattern.isSentToCltvPaymentChannel(
|
||||
ScriptBuilder.createCLTVPaymentChannelOutput(BigInteger.ONE, keys.get(0), keys.get(1))
|
||||
));
|
||||
|
@ -2928,7 +2928,7 @@ public class WalletTest extends TestWithWallet {
|
||||
assertEquals(THREE_CENTS, tx.getValueSentFromMe(wallet));
|
||||
assertEquals(THREE_CENTS.subtract(tx.getFee()), tx.getValueSentToMe(wallet));
|
||||
// TX sends to one of our addresses (for now we ignore married wallets).
|
||||
final LegacyAddress toAddress = tx.getOutput(0).getScriptPubKey().getToAddress(UNITTEST);
|
||||
final LegacyAddress toAddress = (LegacyAddress) tx.getOutput(0).getScriptPubKey().getToAddress(UNITTEST);
|
||||
final ECKey rotatingToKey = wallet.findKeyFromPubHash(toAddress.getHash());
|
||||
assertNotNull(rotatingToKey);
|
||||
assertFalse(wallet.isKeyRotating(rotatingToKey));
|
||||
|
Loading…
x
Reference in New Issue
Block a user