All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bitcoinj.script.Script Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2011 Google Inc.
 * Copyright 2012 Matt Corallo.
 * Copyright 2014 Andreas Schildbach
 * Copyright 2017 Nicola Atzei
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.bitcoinj.script;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static org.bitcoinj.script.ScriptOpCodes.OP_0;
import static org.bitcoinj.script.ScriptOpCodes.OP_1;
import static org.bitcoinj.script.ScriptOpCodes.OP_16;
import static org.bitcoinj.script.ScriptOpCodes.OP_1NEGATE;
import static org.bitcoinj.script.ScriptOpCodes.OP_CHECKMULTISIG;
import static org.bitcoinj.script.ScriptOpCodes.OP_CHECKMULTISIGVERIFY;
import static org.bitcoinj.script.ScriptOpCodes.OP_CHECKSIG;
import static org.bitcoinj.script.ScriptOpCodes.OP_CHECKSIGVERIFY;
import static org.bitcoinj.script.ScriptOpCodes.OP_INVALIDOPCODE;
import static org.bitcoinj.script.ScriptOpCodes.OP_PUSHDATA1;
import static org.bitcoinj.script.ScriptOpCodes.OP_PUSHDATA2;
import static org.bitcoinj.script.ScriptOpCodes.OP_PUSHDATA4;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;

import javax.annotation.Nullable;

import org.bitcoinj.core.Address;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.SegwitAddress;
import org.bitcoinj.core.UnsafeByteArrayOutputStream;
import org.bitcoinj.core.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// TODO: Redesign this entire API to be more type safe and organised.

/**
 * 

Programs embedded inside transactions that control redemption of payments.

* *

Bitcoin transactions don't specify what they do directly. Instead a * small binary stack language is used to define programs that when evaluated return whether the transaction * "accepts" or rejects the other transactions connected to it.

* *

In SPV mode, scripts are not run, because that would require all transactions to be available and lightweight * clients don't have that data. In full mode, this class is used to run the interpreted language. It also has * static methods for building scripts.

*/ public class Script { /** Enumeration to encapsulate the type of this script. */ public enum ScriptType { P2PKH(1), // pay to pubkey hash (aka pay to address) P2PK(2), // pay to pubkey P2SH(3), // pay to script hash P2WPKH(4), // pay to witness pubkey hash P2WSH(5); // pay to witness script hash public final int id; private ScriptType(int id) { this.id = id; } } /** * Note currently only P2SH, DERSIG and NULLDUMMY are actually supported. */ public enum VerifyFlag { P2SH, // Enable BIP16-style subscript evaluation. STRICTENC, // Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure. DERSIG, // Passing a non-strict-DER signature to a checksig operation causes script failure (softfork safe, BIP66 rule 1) LOW_S, // Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure NULLDUMMY, // Verify dummy stack item consumed by CHECKMULTISIG is of zero-length. SIGPUSHONLY, // Using a non-push operator in the scriptSig causes script failure (softfork safe, BIP62 rule 2). MINIMALDATA, // Require minimal encodings for all push operations DISCOURAGE_UPGRADABLE_NOPS, // Discourage use of NOPs reserved for upgrades (NOP1-10) CLEANSTACK, // Require that only a single stack element remains after evaluation. CHECKLOCKTIMEVERIFY, // Enable CHECKLOCKTIMEVERIFY operation CHECKSEQUENCEVERIFY // Enable CHECKSEQUENCEVERIFY operation } public static final EnumSet ALL_VERIFY_FLAGS = EnumSet.allOf(VerifyFlag.class); private static final Logger log = LoggerFactory.getLogger(Script.class); public static final long MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes private static final int MAX_OPS_PER_SCRIPT = 201; private static final int MAX_STACK_SIZE = 1000; private static final int MAX_PUBKEYS_PER_MULTISIG = 20; private static final int MAX_SCRIPT_SIZE = 10000; public static final int SIG_SIZE = 75; /** Max number of sigops allowed in a standard p2sh redeem script */ public static final int MAX_P2SH_SIGOPS = 15; // The program is a set of chunks where each element is either [opcode] or [data, data, data ...] protected List chunks; // Unfortunately, scripts are not ever re-serialized or canonicalized when used in signature hashing. Thus we // must preserve the exact bytes that we read off the wire, along with the parsed form. protected byte[] program; // Creation time of the associated keys in seconds since the epoch. private long creationTimeSeconds; /** Creates an empty script that serializes to nothing. */ private Script() { chunks = new ArrayList<>(); } // Used from ScriptBuilder. Script(List chunks) { this.chunks = Collections.unmodifiableList(new ArrayList<>(chunks)); creationTimeSeconds = Utils.currentTimeSeconds(); } /** * Construct a Script that copies and wraps the programBytes array. The array is parsed and checked for syntactic * validity. * @param programBytes Array of program bytes from a transaction. */ public Script(byte[] programBytes) throws ScriptException { program = programBytes; parse(programBytes); creationTimeSeconds = 0; } public Script(byte[] programBytes, long creationTimeSeconds) throws ScriptException { program = programBytes; parse(programBytes); this.creationTimeSeconds = creationTimeSeconds; } public long getCreationTimeSeconds() { return creationTimeSeconds; } public void setCreationTimeSeconds(long creationTimeSeconds) { this.creationTimeSeconds = creationTimeSeconds; } /** * Returns the program opcodes as a string, for example "[1234] DUP HASH160", or "<empty>". */ @Override public String toString() { if (!chunks.isEmpty()) return Utils.SPACE_JOINER.join(chunks); else return ""; } /** Returns the serialized program as a newly created byte array. */ public byte[] getProgram() { try { // Don't round-trip as Bitcoin Core doesn't and it would introduce a mismatch. if (program != null) return Arrays.copyOf(program, program.length); ByteArrayOutputStream bos = new ByteArrayOutputStream(); for (ScriptChunk chunk : chunks) { chunk.write(bos); } program = bos.toByteArray(); return program; } catch (IOException e) { throw new RuntimeException(e); // Cannot happen. } } /** Returns an immutable list of the scripts parsed form. Each chunk is either an opcode or data element. */ public List getChunks() { return Collections.unmodifiableList(chunks); } private static final ScriptChunk[] STANDARD_TRANSACTION_SCRIPT_CHUNKS = { new ScriptChunk(ScriptOpCodes.OP_DUP, null), new ScriptChunk(ScriptOpCodes.OP_HASH160, null), new ScriptChunk(ScriptOpCodes.OP_EQUALVERIFY, null), new ScriptChunk(ScriptOpCodes.OP_CHECKSIG, null), }; /** *

To run a script, first we parse it which breaks it up into chunks representing pushes of data or logical * opcodes. Then we can run the parsed chunks.

* *

The reason for this split, instead of just interpreting directly, is to make it easier * to reach into a programs structure and pull out bits of data without having to run it. * This is necessary to render the to addresses of transactions in a user interface. * Bitcoin Core does something similar.

*/ private void parse(byte[] program) throws ScriptException { chunks = new ArrayList<>(5); // Common size. ByteArrayInputStream bis = new ByteArrayInputStream(program); while (bis.available() > 0) { int opcode = bis.read(); long dataToRead = -1; if (opcode >= 0 && opcode < OP_PUSHDATA1) { // Read some bytes of data, where how many is the opcode value itself. dataToRead = opcode; } else if (opcode == OP_PUSHDATA1) { if (bis.available() < 1) throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Unexpected end of script"); dataToRead = bis.read(); } else if (opcode == OP_PUSHDATA2) { // Read a short, then read that many bytes of data. if (bis.available() < 2) throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Unexpected end of script"); dataToRead = Utils.readUint16FromStream(bis); } else if (opcode == OP_PUSHDATA4) { // Read a uint32, then read that many bytes of data. // Though this is allowed, because its value cannot be > 520, it should never actually be used if (bis.available() < 4) throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Unexpected end of script"); dataToRead = Utils.readUint32FromStream(bis); } ScriptChunk chunk; if (dataToRead == -1) { chunk = new ScriptChunk(opcode, null); } else { if (dataToRead > bis.available()) throw new ScriptException(ScriptError.SCRIPT_ERR_BAD_OPCODE, "Push of data element that is larger than remaining data"); byte[] data = new byte[(int)dataToRead]; checkState(dataToRead == 0 || bis.read(data, 0, (int)dataToRead) == dataToRead); chunk = new ScriptChunk(opcode, data); } // Save some memory by eliminating redundant copies of the same chunk objects. for (ScriptChunk c : STANDARD_TRANSACTION_SCRIPT_CHUNKS) { if (c.equals(chunk)) chunk = c; } chunks.add(chunk); } } /** @deprecated use {@link ScriptPattern#isP2PK(Script)} */ @Deprecated public boolean isSentToRawPubKey() { return ScriptPattern.isP2PK(this); } /** @deprecated use {@link ScriptPattern#isP2PKH(Script)} */ @Deprecated public boolean isSentToAddress() { return ScriptPattern.isP2PKH(this); } /** *

If the program somehow pays to a hash, returns the hash.

* *

Otherwise this method throws a ScriptException.

*/ public byte[] getPubKeyHash() throws ScriptException { if (ScriptPattern.isP2PKH(this)) return ScriptPattern.extractHashFromP2PKH(this); else if (ScriptPattern.isP2SH(this)) return ScriptPattern.extractHashFromP2SH(this); else if (ScriptPattern.isP2WH(this)) return ScriptPattern.extractHashFromP2WH(this); else throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Script not in the standard scriptPubKey form"); } /** * Gets the destination address from this script, if it's in the required form. */ public Address getToAddress(NetworkParameters params) throws ScriptException { return getToAddress(params, false); } /** * Gets the destination address from this script, if it's in the required form. * * @param forcePayToPubKey * If true, allow payToPubKey to be casted to the corresponding address. This is useful if you prefer * showing addresses rather than pubkeys. */ public Address getToAddress(NetworkParameters params, boolean forcePayToPubKey) throws ScriptException { if (ScriptPattern.isP2PKH(this)) return LegacyAddress.fromPubKeyHash(params, ScriptPattern.extractHashFromP2PKH(this)); else if (ScriptPattern.isP2SH(this)) return LegacyAddress.fromScriptHash(params, ScriptPattern.extractHashFromP2SH(this)); else if (forcePayToPubKey && ScriptPattern.isP2PK(this)) return LegacyAddress.fromKey(params, ECKey.fromPublicOnly(ScriptPattern.extractKeyFromP2PK(this))); else if (ScriptPattern.isP2WH(this)) return SegwitAddress.fromHash(params, ScriptPattern.extractHashFromP2WH(this)); else throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Cannot cast this script to an address"); } ////////////////////// Interface for writing scripts from scratch //////////////////////////////// /** * Writes out the given byte buffer to the output stream with the correct opcode prefix * To write an integer call writeBytes(out, Utils.reverseBytes(Utils.encodeMPI(val, false))); */ public static void writeBytes(OutputStream os, byte[] buf) throws IOException { if (buf.length < OP_PUSHDATA1) { os.write(buf.length); os.write(buf); } else if (buf.length < 256) { os.write(OP_PUSHDATA1); os.write(buf.length); os.write(buf); } else if (buf.length < 65536) { os.write(OP_PUSHDATA2); Utils.uint16ToByteStreamLE(buf.length, os); os.write(buf); } else { throw new RuntimeException("Unimplemented"); } } /** Creates a program that requires at least N of the given keys to sign, using OP_CHECKMULTISIG. */ public static byte[] createMultiSigOutputScript(int threshold, List pubkeys) { checkArgument(threshold > 0); checkArgument(threshold <= pubkeys.size()); checkArgument(pubkeys.size() <= 16); // That's the max we can represent with a single opcode. if (pubkeys.size() > 3) { log.warn("Creating a multi-signature output that is non-standard: {} pubkeys, should be <= 3", pubkeys.size()); } try { ByteArrayOutputStream bits = new ByteArrayOutputStream(); bits.write(encodeToOpN(threshold)); for (ECKey key : pubkeys) { writeBytes(bits, key.getPubKey()); } bits.write(encodeToOpN(pubkeys.size())); bits.write(OP_CHECKMULTISIG); return bits.toByteArray(); } catch (IOException e) { throw new RuntimeException(e); // Cannot happen. } } public static byte[] createInputScript(byte[] signature, byte[] pubkey) { try { // TODO: Do this by creating a Script *first* then having the script reassemble itself into bytes. ByteArrayOutputStream bits = new UnsafeByteArrayOutputStream(signature.length + pubkey.length + 2); writeBytes(bits, signature); writeBytes(bits, pubkey); return bits.toByteArray(); } catch (IOException e) { throw new RuntimeException(e); } } public static byte[] createInputScript(byte[] signature) { try { // TODO: Do this by creating a Script *first* then having the script reassemble itself into bytes. ByteArrayOutputStream bits = new UnsafeByteArrayOutputStream(signature.length + 2); writeBytes(bits, signature); return bits.toByteArray(); } catch (IOException e) { throw new RuntimeException(e); } } /** * Returns a copy of the given scriptSig with the signature inserted in the given position. */ public Script getScriptSigWithSignature(Script scriptSig, byte[] sigBytes, int index) { int sigsPrefixCount = 0; int sigsSuffixCount = 0; if (ScriptPattern.isP2SH(this)) { sigsPrefixCount = 1; // OP_0 * sigsSuffixCount = 1; } else if (ScriptPattern.isSentToMultisig(this)) { sigsPrefixCount = 1; // OP_0 * } else if (ScriptPattern.isP2PKH(this)) { sigsSuffixCount = 1; // } return ScriptBuilder.updateScriptWithSignature(scriptSig, sigBytes, index, sigsPrefixCount, sigsSuffixCount); } private int findKeyInRedeem(ECKey key) { checkArgument(chunks.get(0).isOpCode()); // P2SH scriptSig int numKeys = Script.decodeFromOpN(chunks.get(chunks.size() - 2).opcode); for (int i = 0 ; i < numKeys ; i++) { if (Arrays.equals(chunks.get(1 + i).data, key.getPubKey())) { return i; } } throw new IllegalStateException("Could not find matching key " + key.toString() + " in script " + this); } /** * Returns a list of the keys required by this script, assuming a multi-sig script. * * @throws ScriptException if the script type is not understood or is pay to address or is P2SH (run this method on the "Redeem script" instead). */ public List getPubKeys() { if (!ScriptPattern.isSentToMultisig(this)) throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Only usable for multisig scripts."); ArrayList result = new ArrayList<>(); int numKeys = Script.decodeFromOpN(chunks.get(chunks.size() - 2).opcode); for (int i = 0 ; i < numKeys ; i++) result.add(ECKey.fromPublicOnly(chunks.get(1 + i).data)); return result; } ////////////////////// Interface used during verification of transactions/blocks //////////////////////////////// private static int getSigOpCount(List chunks, boolean accurate) throws ScriptException { int sigOps = 0; int lastOpCode = OP_INVALIDOPCODE; for (ScriptChunk chunk : chunks) { if (chunk.isOpCode()) { switch (chunk.opcode) { case OP_CHECKSIG: case OP_CHECKSIGVERIFY: sigOps++; break; case OP_CHECKMULTISIG: case OP_CHECKMULTISIGVERIFY: if (accurate && lastOpCode >= OP_1 && lastOpCode <= OP_16) sigOps += decodeFromOpN(lastOpCode); else sigOps += 20; break; default: break; } lastOpCode = chunk.opcode; } } return sigOps; } public static int decodeFromOpN(int opcode) { checkArgument((opcode == OP_0 || opcode == OP_1NEGATE) || (opcode >= OP_1 && opcode <= OP_16), "decodeFromOpN called on non OP_N opcode: %s", ScriptOpCodes.getOpCodeName(opcode)); if (opcode == OP_0) return 0; else if (opcode == OP_1NEGATE) return -1; else return opcode + 1 - OP_1; } public static int encodeToOpN(int value) { checkArgument(value >= -1 && value <= 16, "encodeToOpN called for " + value + " which we cannot encode in an opcode."); if (value == 0) return OP_0; else if (value == -1) return OP_1NEGATE; else return value - 1 + OP_1; } /** * Gets the count of regular SigOps in the script program (counting multisig ops as 20) */ public static int getSigOpCount(byte[] program) throws ScriptException { Script script = new Script(); try { script.parse(program); } catch (ScriptException e) { // Ignore errors and count up to the parse-able length } return getSigOpCount(script.chunks, false); } /** * Gets the count of P2SH Sig Ops in the Script scriptSig */ public static long getP2SHSigOpCount(byte[] scriptSig) throws ScriptException { Script script = new Script(); try { script.parse(scriptSig); } catch (ScriptException e) { // Ignore errors and count up to the parse-able length } for (int i = script.chunks.size() - 1; i >= 0; i--) if (!script.chunks.get(i).isOpCode()) { Script subScript = new Script(); subScript.parse(script.chunks.get(i).data); return getSigOpCount(subScript.chunks, true); } return 0; } /** * Returns number of signatures required to satisfy this script. */ public int getNumberOfSignaturesRequiredToSpend() { if (ScriptPattern.isSentToMultisig(this)) { // for N of M CHECKMULTISIG script we will need N signatures to spend ScriptChunk nChunk = chunks.get(0); return Script.decodeFromOpN(nChunk.opcode); } else if (ScriptPattern.isP2PKH(this) || ScriptPattern.isP2PK(this)) { // P2PKH and P2PK require single sig return 1; } else if (ScriptPattern.isP2SH(this)) { throw new IllegalStateException("For P2SH number of signatures depends on redeem script"); } else { throw new IllegalStateException("Unsupported script type"); } } /** * Returns number of bytes required to spend this script. It accepts optional ECKey and redeemScript that may * be required for certain types of script to estimate target size. */ public int getNumberOfBytesRequiredToSpend(@Nullable ECKey pubKey, @Nullable Script redeemScript) { if (ScriptPattern.isP2SH(this)) { // scriptSig: [sig] [sig...] checkArgument(redeemScript != null, "P2SH script requires redeemScript to be spent"); return redeemScript.getNumberOfSignaturesRequiredToSpend() * SIG_SIZE + redeemScript.getProgram().length; } else if (ScriptPattern.isSentToMultisig(this)) { // scriptSig: OP_0 [sig] [sig...] return getNumberOfSignaturesRequiredToSpend() * SIG_SIZE + 1; } else if (ScriptPattern.isP2PK(this)) { // scriptSig: return SIG_SIZE; } else if (ScriptPattern.isP2PKH(this)) { // scriptSig: int uncompressedPubKeySize = 65; // very conservative return SIG_SIZE + (pubKey != null ? pubKey.getPubKey().length : uncompressedPubKeySize); } else if (ScriptPattern.isP2WPKH(this)) { // scriptSig is empty // witness: int compressedPubKeySize = 33; return SIG_SIZE + (pubKey != null ? pubKey.getPubKey().length : compressedPubKeySize); } else { throw new IllegalStateException("Unsupported script type"); } } /** @deprecated use {@link ScriptPattern#isP2SH(Script)} */ @Deprecated public boolean isPayToScriptHash() { return ScriptPattern.isP2SH(this); } /** @deprecated use {@link ScriptPattern#isSentToMultisig(Script)} */ @Deprecated public boolean isSentToMultiSig() { return ScriptPattern.isSentToMultisig(this); } private static boolean equalsRange(byte[] a, int start, byte[] b) { if (start + b.length > a.length) return false; for (int i = 0; i < b.length; i++) if (a[i + start] != b[i]) return false; return true; } /** * Returns the script bytes of inputScript with all instances of the specified script object removed */ public static byte[] removeAllInstancesOf(byte[] inputScript, byte[] chunkToRemove) { // We usually don't end up removing anything UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(inputScript.length); int cursor = 0; while (cursor < inputScript.length) { boolean skip = equalsRange(inputScript, cursor, chunkToRemove); int opcode = inputScript[cursor++] & 0xFF; int additionalBytes = 0; if (opcode >= 0 && opcode < OP_PUSHDATA1) { additionalBytes = opcode; } else if (opcode == OP_PUSHDATA1) { additionalBytes = (0xFF & inputScript[cursor]) + 1; } else if (opcode == OP_PUSHDATA2) { additionalBytes = Utils.readUint16(inputScript, cursor) + 2; } else if (opcode == OP_PUSHDATA4) { additionalBytes = (int) Utils.readUint32(inputScript, cursor) + 4; } if (!skip) { try { bos.write(opcode); bos.write(Arrays.copyOfRange(inputScript, cursor, cursor + additionalBytes)); } catch (IOException e) { throw new RuntimeException(e); } } cursor += additionalBytes; } return bos.toByteArray(); } /** * Returns the script bytes of inputScript with all instances of the given op code removed */ public static byte[] removeAllInstancesOfOp(byte[] inputScript, int opCode) { return removeAllInstancesOf(inputScript, new byte[] {(byte)opCode}); } ////////////////////// Script verification and helpers //////////////////////////////// private static boolean castToBool(byte[] data) { for (int i = 0; i < data.length; i++) { // "Can be negative zero" - Bitcoin Core (see OpenSSL's BN_bn2mpi) if (data[i] != 0) return !(i == data.length - 1 && (data[i] & 0xFF) == 0x80); } return false; } /** * Cast a script chunk to a BigInteger. * * @see #castToBigInteger(byte[], int, boolean) for values with different maximum * sizes. * @throws ScriptException if the chunk is longer than 4 bytes. */ private static BigInteger castToBigInteger(byte[] chunk, final boolean requireMinimal) throws ScriptException { return castToBigInteger(chunk, 4, requireMinimal); } /** * Cast a script chunk to a BigInteger. Normally you would want * {@link #castToBigInteger(byte[], boolean)} instead, this is only for cases where * the normal maximum length does not apply (i.e. CHECKLOCKTIMEVERIFY, CHECKSEQUENCEVERIFY). * * @param maxLength the maximum length in bytes. * @param requireMinimal check if the number is encoded with the minimum possible number of bytes * @throws ScriptException if the chunk is longer than the specified maximum. */ /* package private */ static BigInteger castToBigInteger(final byte[] chunk, final int maxLength, final boolean requireMinimal) throws ScriptException { if (chunk.length > maxLength) throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Script attempted to use an integer larger than " + maxLength + " bytes"); if (requireMinimal && chunk.length > 0) { // Check that the number is encoded with the minimum possible // number of bytes. // // If the most-significant-byte - excluding the sign bit - is zero // then we're not minimal. Note how this test also rejects the // negative-zero encoding, 0x80. if ((chunk[chunk.length - 1] & 0x7f) == 0) { // One exception: if there's more than one byte and the most // significant bit of the second-most-significant-byte is set // it would conflict with the sign bit. An example of this case // is +-255, which encode to 0xff00 and 0xff80 respectively. // (big-endian). if (chunk.length <= 1 || (chunk[chunk.length - 2] & 0x80) == 0) { throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "non-minimally encoded script number"); } } } return Utils.decodeMPI(Utils.reverseBytes(chunk), false); } /** @deprecated use {@link ScriptPattern#isOpReturn(Script)} */ @Deprecated public boolean isOpReturn() { return ScriptPattern.isOpReturn(this); } // Utility that doesn't copy for internal use private byte[] getQuickProgram() { if (program != null) return program; return getProgram(); } /** * Get the {@link Script.ScriptType}. * @return The script type, or null if the script is of unknown type */ public @Nullable ScriptType getScriptType() { if (ScriptPattern.isP2PKH(this)) return ScriptType.P2PKH; if (ScriptPattern.isP2PK(this)) return ScriptType.P2PK; if (ScriptPattern.isP2SH(this)) return ScriptType.P2SH; if (ScriptPattern.isP2WPKH(this)) return ScriptType.P2WPKH; if (ScriptPattern.isP2WSH(this)) return ScriptType.P2WSH; return null; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; return Arrays.equals(getQuickProgram(), ((Script)o).getQuickProgram()); } @Override public int hashCode() { return Arrays.hashCode(getQuickProgram()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy