Fixing issue where we have the wrong version of ScriptSignature in our tree

This commit is contained in:
Chris Stewart 2016-05-31 08:39:03 -05:00
parent 40929aa5bf
commit 81f2cf3ba2

View file

@ -6,6 +6,7 @@ import org.bitcoins.core.serializers.script.{RawScriptPubKeyParser, RawScriptSig
import org.bitcoins.core.script.constant._
import org.bitcoins.core.script.crypto.{HashType, HashTypeFactory, OP_CHECKMULTISIG, SIGHASH_ALL}
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil, Factory}
import org.slf4j.LoggerFactory
import scala.util.{Failure, Success, Try}
@ -15,50 +16,44 @@ import scala.util.{Failure, Success, Try}
*/
sealed trait ScriptSignature extends NetworkElement with BitcoinSLogger {
/**
* Representation of a scriptSignature in a parsed assembly format
* this data structure can be run through the script interpreter to
* see if a script evaluates to true
* @return
*/
def asm : Seq[ScriptToken]
/**
* The digital signatures contained inside of the script signature
* p2pkh script signatures only have one sig
* p2pk script signatures only have one sigs
* p2sh script signatures can have m sigs
* multisignature scripts can have m sigs
* @return
*/
* Representation of a scriptSignature in a parsed assembly format
* this data structure can be run through the script interpreter to
* see if a script evaluates to true
* @return
*/
lazy val asm : Seq[ScriptToken] = ScriptParser.fromHex(hex)
/**
* The digital signatures contained inside of the script signature
* p2pkh script signatures only have one sig
* p2pk script signatures only have one sigs
* p2sh script signatures can have m sigs
* multisignature scripts can have m sigs
* @return
*/
def signatures : Seq[ECDigitalSignature]
/**
* Derives the hash type for a given digitalSignature
*
* Derives the hash type for a given digitalSignature
* @param digitalSignature
* @return
*/
* @return
*/
def hashType(digitalSignature: ECDigitalSignature) = {
digitalSignature match {
case EmptyDigitalSignature => SIGHASH_ALL()
case sig : ECDigitalSignature => HashTypeFactory.fromByte(digitalSignature.bytes.last)
}
}
}
trait NonStandardScriptSignature extends ScriptSignature {
def signatures : Seq[ECDigitalSignature] = ???
def signatures : Seq[ECDigitalSignature] = Seq()
}
object NonStandardScriptSignatureImpl {
def apply(hex : String) : NonStandardScriptSignatureImpl = NonStandardScriptSignatureImpl(hex, RawScriptSignatureParser.read(hex).asm)
}
case class NonStandardScriptSignatureImpl(hex : String, asm : Seq[ScriptToken]) extends NonStandardScriptSignature
/**
@ -71,24 +66,22 @@ trait P2PKHScriptSignature extends ScriptSignature {
/**
* P2PKH scriptSigs only have one signature
*
* P2PKH scriptSigs only have one signature
* @return
*/
*/
def signature : ECDigitalSignature = signatures.head
/**
* Gives us the public key inside of a p2pkh script signature
*
* Gives us the public key inside of a p2pkh script signature
* @return
*/
*/
def publicKey : ECPublicKey = ECFactory.publicKey(asm.last.bytes)
/**
* Returns the hash type for the p2pkh script signature
*
* Returns the hash type for the p2pkh script signature
* @return
*/
*/
def hashType : HashType = HashTypeFactory.fromByte(signature.bytes.last)
override def signatures : Seq[ECDigitalSignature] = {
@ -104,37 +97,36 @@ trait P2PKHScriptSignature extends ScriptSignature {
* <sig> [sig] [sig...] <redeemScript>
*/
trait P2SHScriptSignature extends ScriptSignature {
/**
* The redeemScript represents the conditions that must be satisfied to spend the output
*
* The redeemScript represents the conditions that must be satisfied to spend the output
* @return
*/
*/
def redeemScript : ScriptPubKey = ScriptPubKey(asm.last.bytes)
/**
* Returns the script signature of this p2shScriptSig with no serialized redeemScript
*
* Returns the script signature of this p2shScriptSig with no serialized redeemScript
* @return
*/
*/
def scriptSignatureNoRedeemScript = ScriptSignature.fromAsm(splitAtRedeemScript(asm)._1)
/**
* Returns the public keys for the p2sh scriptSignature
*
* Returns the public keys for the p2sh scriptSignature
* @return
*/
*/
def publicKeys : Seq[ECPublicKey] = {
val pubKeys : Seq[ScriptToken] = redeemScript.asm.filter(_.isInstanceOf[ScriptConstant])
.filterNot(_.isInstanceOf[ScriptNumberOperation])
pubKeys.map(k => ECFactory.publicKey(k.hex))
}
/**
* The digital signatures inside of the scriptSig
*
* The digital signatures inside of the scriptSig
* @return
*/
*/
def signatures : Seq[ECDigitalSignature] = {
val nonRedeemScript = splitAtRedeemScript(asm)._1
val sigs = nonRedeemScript.filter(_.isInstanceOf[ScriptConstant]).filterNot(_.isInstanceOf[ScriptNumberOperation])
@ -143,13 +135,12 @@ trait P2SHScriptSignature extends ScriptSignature {
/**
* Splits the given asm into two parts
* the first part is the digital signatures
* the second part is the redeem script
*
* Splits the given asm into two parts
* the first part is the digital signatures
* the second part is the redeem script
* @param asm
* @return
*/
* @return
*/
def splitAtRedeemScript(asm : Seq[ScriptToken]) : (Seq[ScriptToken],Seq[ScriptToken]) = {
//call .tail twice to remove the serialized redeemScript & it's bytesToPushOntoStack constant
(asm.reverse.tail.tail.reverse, Seq(asm.last))
@ -181,191 +172,180 @@ trait MultiSignatureScriptSignature extends ScriptSignature {
* Signature script: <sig>
*/
trait P2PKScriptSignature extends ScriptSignature {
/**
* Returns the hash type for the signature inside of the p2pk script signature
*
* Returns the hash type for the signature inside of the p2pk script signature
* @return
*/
*/
def hashType = HashTypeFactory.fromByte(signature.bytes.last)
/**
* PubKey scriptSignatures only have one signature
*
* PubKey scriptSignatures only have one signature
* @return
*/
*/
def signature : ECDigitalSignature = signatures.head
/**
* The digital signatures inside of the scriptSig
*
* The digital signatures inside of the scriptSig
* @return
*/
*/
def signatures : Seq[ECDigitalSignature] = {
Seq(ECFactory.digitalSignature(BitcoinScriptUtil.filterPushOps(asm).head.hex))
}
}
/**
* Represents the empty script signature
*/
case object EmptyScriptSignature extends ScriptSignature {
def asm = List()
def signatures = List()
def hex = ""
}
object ScriptSignature extends Factory[ScriptSignature] with BitcoinSLogger {
private object P2PKHScriptSignatureImpl {
def apply(hex : String) : P2PKHScriptSignatureImpl = P2PKHScriptSignatureImpl(hex, RawScriptSignatureParser.read(hex).asm)
}
private case class P2PKHScriptSignatureImpl(hex : String, asm : Seq[ScriptToken]) extends P2PKHScriptSignature
private case class NonStandardScriptSignatureImpl(hex : String) extends NonStandardScriptSignature
private object P2SHScriptSignatureImpl {
def apply(hex : String) : P2SHScriptSignatureImpl = P2SHScriptSignatureImpl(hex, RawScriptSignatureParser.read(hex).asm)
}
private case class P2SHScriptSignatureImpl(hex : String, asm : Seq[ScriptToken]) extends P2SHScriptSignature
private case class P2PKScriptSignatureImpl(hex : String) extends P2PKScriptSignature
private object MultiSignatureScriptSignatureImpl {
def apply(hex : String) : MultiSignatureScriptSignatureImpl = MultiSignatureScriptSignatureImpl(hex, RawScriptSignatureParser.read(hex).asm)
}
private case class MultiSignatureScriptSignatureImpl(hex : String, asm : Seq[ScriptToken]) extends MultiSignatureScriptSignature
private case class MultiSignatureScriptSignatureImpl(hex : String) extends MultiSignatureScriptSignature
private object P2PKScriptSignatureImpl {
def apply(hex : String) : P2PKScriptSignatureImpl = P2PKScriptSignatureImpl(hex, RawScriptSignatureParser.read(hex).asm)
}
private case class P2PKScriptSignatureImpl(hex : String, asm : Seq[ScriptToken]) extends P2PKScriptSignature
private case class P2SHScriptSignatureImpl(hex : String) extends P2SHScriptSignature
private case class P2PKHScriptSignatureImpl(hex : String) extends P2PKHScriptSignature
/**
* Builds a script signature from a digital signature and a public key
* this is a pay to public key hash script sig
*
* @param signature
* @param pubKey
* @return
*/
def factory(signature : ECDigitalSignature, pubKey : ECPublicKey) : ScriptSignature = {
val signatureBytesToPushOntoStack = BytesToPushOntoStack(signature.bytes.size)
val pubKeyBytesToPushOntoStack = BytesToPushOntoStack(pubKey.bytes.size)
val asm : Seq[ScriptToken] = Seq(signatureBytesToPushOntoStack, ScriptConstant(signature.hex),
pubKeyBytesToPushOntoStack, ScriptConstant(pubKey.hex))
fromAsm(asm)
* Builds a script signature from a digital signature and a public key
* this is a pay to public key hash script sig
*
* @param signature
* @param pubKey
* @return
*/
def factory(signature : ECDigitalSignature, pubKey : ECPublicKey) : ScriptSignature = {
val signatureBytesToPushOntoStack = BytesToPushOntoStack(signature.bytes.size)
val pubKeyBytesToPushOntoStack = BytesToPushOntoStack(pubKey.bytes.size)
val asm : Seq[ScriptToken] = Seq(signatureBytesToPushOntoStack, ScriptConstant(signature.hex),
pubKeyBytesToPushOntoStack, ScriptConstant(pubKey.hex))
fromAsm(asm)
}
/**
* Returns an empty script signature
*
* @return
*/
def empty : ScriptSignature = EmptyScriptSignature
def fromBytes(bytes : Seq[Byte]) : ScriptSignature = RawScriptSignatureParser.read(bytes)
/**
* Creates a scriptSignature from the list of script tokens
*
* @param tokens
* @return
*/
def fromAsm(tokens : Seq[ScriptToken]) : ScriptSignature = {
val scriptSigHex = tokens.map(_.hex).mkString
tokens match {
case Nil => EmptyScriptSignature
case _ if (tokens.size > 1 && isRedeemScript(tokens.last)) =>
P2SHScriptSignatureImpl(scriptSigHex)
case _ if (isMultiSignatureScriptSignature(tokens)) =>
//the head of the asm does not neccessarily have to be an OP_0 if the NULLDUMMY script
//flag is not set. It can be any script number operation
MultiSignatureScriptSignatureImpl(scriptSigHex)
case List(w : BytesToPushOntoStack, x : ScriptConstant, y : BytesToPushOntoStack,
z : ScriptConstant) => P2PKHScriptSignatureImpl(scriptSigHex)
case List(w : BytesToPushOntoStack, x : ScriptConstant) => P2PKScriptSignatureImpl(scriptSigHex)
case _ => NonStandardScriptSignatureImpl(scriptSigHex)
}
}
/**
* Returns an empty script signature
*
* @return
*/
def empty : ScriptSignature = EmptyScriptSignature
def fromBytes(bytes : Seq[Byte]) : ScriptSignature = {
RawScriptSignatureParser.read(bytes)
/**
* Creates a script signature from the given tokens and scriptPubKey
*
* @param tokens the script signature's tokens
* @param scriptPubKey the scriptPubKey which the script signature is trying to spend
* @return
*/
def fromScriptPubKey(tokens : Seq[ScriptToken], scriptPubKey : ScriptPubKey) : ScriptSignature = {
val scriptSigHex = tokens.map(_.hex).mkString
scriptPubKey match {
case s : P2SHScriptPubKey => P2SHScriptSignatureImpl(scriptSigHex)
case s : P2PKHScriptPubKey => P2PKHScriptSignatureImpl(scriptSigHex)
case s : P2PKScriptPubKey => P2PKScriptSignatureImpl(scriptSigHex)
case s : MultiSignatureScriptPubKey => MultiSignatureScriptSignatureImpl(scriptSigHex)
case s : NonStandardScriptPubKey => NonStandardScriptSignatureImpl(scriptSigHex)
case EmptyScriptPubKey if (tokens.size == 0) => EmptyScriptSignature
case EmptyScriptPubKey => NonStandardScriptSignatureImpl(scriptSigHex)
}
}
/**
* Creates a scriptSignature from the list of script tokens
*
* @param tokens
* @return
*/
def fromAsm(tokens : Seq[ScriptToken]) : ScriptSignature = {
val scriptSigHex = tokens.map(_.hex).mkString
tokens match {
case Nil => EmptyScriptSignature
case _ if (tokens.size > 1 && isRedeemScript(tokens.last)) =>
P2SHScriptSignatureImpl(scriptSigHex,tokens)
case _ if (isMultiSignatureScriptSignature(tokens)) =>
//the head of the asm does not neccessarily have to be an OP_0 if the NULLDUMMY script
//flag is not set. It can be any script number operation
MultiSignatureScriptSignatureImpl(scriptSigHex,tokens)
case List(w : BytesToPushOntoStack, x : ScriptConstant, y : BytesToPushOntoStack,
z : ScriptConstant) => P2PKHScriptSignatureImpl(scriptSigHex,tokens)
case List(w : BytesToPushOntoStack, x : ScriptConstant) => P2PKScriptSignatureImpl(scriptSigHex,tokens)
case _ => NonStandardScriptSignatureImpl(scriptSigHex,tokens)
}
/**
* Detects if the given script token is a redeem script
*
* @param token
* @return
*/
private def isRedeemScript(token : ScriptToken) : Boolean = {
logger.debug("Checking if last token is redeem script")
val redeemScriptTry : Try[ScriptPubKey] = parseRedeemScript(token)
redeemScriptTry match {
case Success(redeemScript) =>
logger.debug("Possible redeemScript: " + redeemScript)
redeemScript match {
case x : P2PKHScriptPubKey => true
case x : MultiSignatureScriptPubKey => true
case x : P2SHScriptPubKey => true
case x : P2PKScriptPubKey => true
case x : NonStandardScriptPubKey => false
case EmptyScriptPubKey => false
}
case Failure(_) => false
}
}
/**
* Parses a redeem script from the given script token
*
* @param scriptToken
* @return
*/
def parseRedeemScript(scriptToken : ScriptToken) : Try[ScriptPubKey] = {
val redeemScript : Try[ScriptPubKey] = Try(ScriptPubKey(scriptToken.bytes))
redeemScript
}
/**
* Creates a script signature from the given tokens and scriptPubKey
*
* @param tokens the script signature's tokens
* @param scriptPubKey the scriptPubKey which the script signature is trying to spend
* @return
*/
def fromScriptPubKey(tokens : Seq[ScriptToken], scriptPubKey : ScriptPubKey) : ScriptSignature = {
val scriptSigHex = tokens.map(_.hex).mkString
scriptPubKey match {
case s : P2SHScriptPubKey => P2SHScriptSignatureImpl(scriptSigHex,tokens)
case s : P2PKHScriptPubKey => P2PKHScriptSignatureImpl(scriptSigHex,tokens)
case s : P2PKScriptPubKey => P2PKScriptSignatureImpl(scriptSigHex,tokens)
case s : MultiSignatureScriptPubKey => MultiSignatureScriptSignatureImpl(scriptSigHex,tokens)
case s : NonStandardScriptPubKey => NonStandardScriptSignatureImpl(scriptSigHex, tokens)
case EmptyScriptPubKey if (tokens.size == 0) => EmptyScriptSignature
case EmptyScriptPubKey => NonStandardScriptSignatureImpl(scriptSigHex,tokens)
}
}
/**
* Detects if the given script token is a redeem script
*
* @param token
* @return
*/
private def isRedeemScript(token : ScriptToken) : Boolean = {
logger.debug("Checking if last token is redeem script")
val redeemScriptTry : Try[ScriptPubKey] = parseRedeemScript(token)
redeemScriptTry match {
case Success(redeemScript) =>
logger.debug("Possible redeemScript: " + redeemScript)
redeemScript match {
case x : P2PKHScriptPubKey => true
case x : MultiSignatureScriptPubKey => true
case x : P2SHScriptPubKey => true
case x : P2PKScriptPubKey => true
case x : NonStandardScriptPubKey => false
case EmptyScriptPubKey => false
}
case Failure(_) => false
}
}
/**
* Parses a redeem script from the given script token
*
* @param scriptToken
* @return
*/
def parseRedeemScript(scriptToken : ScriptToken) : Try[ScriptPubKey] = {
val redeemScript : Try[ScriptPubKey] = Try(ScriptPubKey(scriptToken.bytes))
redeemScript
}
/**
* Checks if the given script tokens are a multisignature script sig
* format: OP_0 <A sig> [B sig] [C sig...]
*
* @param asm the asm to check if it falls in the multisignature script sig format
* @return boolean indicating if the scriptsignature is a multisignature script signature
*/
def isMultiSignatureScriptSignature(asm : Seq[ScriptToken]) : Boolean = {
asm.isEmpty match {
case true => false
case false if (asm.size == 1) => false
case false =>
val firstTokenIsScriptNumberOperation = asm.head.isInstanceOf[ScriptNumberOperation]
val restOfScriptIsPushOpsOrScriptConstants = asm.tail.map(
token => token.isInstanceOf[ScriptConstant] || StackPushOperationFactory.isPushOperation(token)
).exists(_ == false)
logger.debug("First number is script op: " + firstTokenIsScriptNumberOperation)
logger.debug("tail is true: " +restOfScriptIsPushOpsOrScriptConstants )
firstTokenIsScriptNumberOperation && !restOfScriptIsPushOpsOrScriptConstants
}
/**
* Checks if the given script tokens are a multisignature script sig
* format: OP_0 <A sig> [B sig] [C sig...]
*
* @param asm the asm to check if it falls in the multisignature script sig format
* @return boolean indicating if the scriptsignature is a multisignature script signature
*/
def isMultiSignatureScriptSignature(asm : Seq[ScriptToken]) : Boolean = {
asm.isEmpty match {
case true => false
case false if (asm.size == 1) => false
case false =>
val firstTokenIsScriptNumberOperation = asm.head.isInstanceOf[ScriptNumberOperation]
val restOfScriptIsPushOpsOrScriptConstants = asm.tail.map(
token => token.isInstanceOf[ScriptConstant] || StackPushOperationFactory.isPushOperation(token)
).exists(_ == false)
logger.debug("First number is script op: " + firstTokenIsScriptNumberOperation)
logger.debug("tail is true: " +restOfScriptIsPushOpsOrScriptConstants )
firstTokenIsScriptNumberOperation && !restOfScriptIsPushOpsOrScriptConstants
}
}
def apply(bytes: Seq[Byte]) : ScriptSignature = fromBytes(bytes)
def apply(hex : String) : ScriptSignature = fromHex(hex)