mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-24 23:08:31 +01:00
Fixing an anti-pattern inside of ScriptSignature companion object, now ScriptSignature case classes reside in the companion object of the subtype - i.e. P2PKHScriptSig instead of the generic ScriptSignature companion object
This commit is contained in:
parent
087c80ef4b
commit
962833c350
2 changed files with 144 additions and 118 deletions
|
@ -5,7 +5,7 @@ import org.bitcoins.core.protocol.NetworkElement
|
|||
import org.bitcoins.core.script.constant._
|
||||
import org.bitcoins.core.script.crypto.{HashType, HashTypeFactory, SIGHASH_ALL}
|
||||
import org.bitcoins.core.serializers.script.{RawScriptSignatureParser, ScriptParser}
|
||||
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinScriptUtil, Factory}
|
||||
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil, BitcoinScriptUtil, Factory}
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
|
@ -14,8 +14,6 @@ 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
|
||||
|
@ -57,6 +55,21 @@ trait NonStandardScriptSignature extends ScriptSignature {
|
|||
def signatures : Seq[ECDigitalSignature] = Seq()
|
||||
}
|
||||
|
||||
object NonStandardScriptSignature extends Factory[NonStandardScriptSignature] {
|
||||
private case class NonStandardScriptSignatureImpl(hex : String) extends NonStandardScriptSignature
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): NonStandardScriptSignature = {
|
||||
//make sure we can parse the bytes
|
||||
val asm = ScriptParser.fromBytes(bytes)
|
||||
NonStandardScriptSignature.fromAsm(asm)
|
||||
}
|
||||
|
||||
def fromAsm(asm : Seq[ScriptToken]): NonStandardScriptSignature = {
|
||||
val hex = asm.map(_.hex).mkString
|
||||
NonStandardScriptSignatureImpl(hex)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
@ -95,21 +108,44 @@ trait P2PKHScriptSignature extends ScriptSignature {
|
|||
}
|
||||
|
||||
object P2PKHScriptSignature extends Factory[P2PKHScriptSignature] {
|
||||
private case class P2PKHScriptSignatureImpl(hex : String) extends P2PKHScriptSignature
|
||||
|
||||
override def fromBytes(bytes : Seq[Byte]): P2PKHScriptSignature = {
|
||||
val scriptSig = RawScriptSignatureParser.read(bytes)
|
||||
matchP2PKHScriptSig(scriptSig)
|
||||
val asm = ScriptParser.fromBytes(bytes)
|
||||
P2PKHScriptSignature.fromAsm(asm)
|
||||
}
|
||||
|
||||
def apply(signature : ECDigitalSignature, pubKey : ECPublicKey): P2PKHScriptSignature = {
|
||||
val scriptSig = ScriptSignature(signature,pubKey)
|
||||
matchP2PKHScriptSig(scriptSig)
|
||||
def fromAsm(asm: Seq[ScriptToken]): P2PKHScriptSignature = {
|
||||
require(isP2PKHScriptSig(asm), "Given asm was not a P2PKHScriptSignature, got: " + asm)
|
||||
val hex = asm.map(_.hex).mkString
|
||||
P2PKHScriptSignatureImpl(hex)
|
||||
}
|
||||
|
||||
private def matchP2PKHScriptSig(scriptSig : ScriptSignature): P2PKHScriptSignature = scriptSig match {
|
||||
case p2pkhScriptSig : P2PKHScriptSignature => p2pkhScriptSig
|
||||
case _ : MultiSignatureScriptSignature | _ : P2PKScriptSignature | _ : NonStandardScriptSignature |
|
||||
_ : P2SHScriptSignature | EmptyScriptSignature =>
|
||||
throw new IllegalArgumentException("We cannot have a non p2pkh scriptSig returned from the p2pkh script sig factory")
|
||||
/**
|
||||
* 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 apply(signature : ECDigitalSignature, pubKey : ECPublicKey) : P2PKHScriptSignature = {
|
||||
val signatureBytesToPushOntoStack = BitcoinScriptUtil.calculatePushOp(signature.bytes)
|
||||
val pubKeyBytesToPushOntoStack = BitcoinScriptUtil.calculatePushOp(pubKey.bytes)
|
||||
val asm : Seq[ScriptToken] = signatureBytesToPushOntoStack ++ Seq(ScriptConstant(signature.hex)) ++
|
||||
pubKeyBytesToPushOntoStack ++ Seq(ScriptConstant(pubKey.hex))
|
||||
fromAsm(asm)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given asm matches a [[P2PKHScriptSignature]]
|
||||
* @param asm
|
||||
* @return
|
||||
*/
|
||||
def isP2PKHScriptSig(asm: Seq[ScriptToken]): Boolean = asm match {
|
||||
case List(w : BytesToPushOntoStack, x : ScriptConstant, y : BytesToPushOntoStack,
|
||||
z : ScriptConstant) => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,10 +212,11 @@ trait P2SHScriptSignature extends ScriptSignature {
|
|||
}
|
||||
|
||||
object P2SHScriptSignature extends Factory[P2SHScriptSignature] with BitcoinSLogger {
|
||||
private case class P2SHScriptSignatureImpl(hex : String) extends P2SHScriptSignature
|
||||
|
||||
override def fromBytes(bytes : Seq[Byte]): P2SHScriptSignature = {
|
||||
val scriptSig = RawScriptSignatureParser.read(bytes)
|
||||
logger.info("p2sh script sig asm: " + scriptSig.asm)
|
||||
matchP2SHScriptSignature(scriptSig)
|
||||
val asm = ScriptParser.fromBytes(bytes)
|
||||
P2SHScriptSignature.fromAsm(asm)
|
||||
}
|
||||
|
||||
def apply(scriptSig : ScriptSignature, redeemScript : ScriptPubKey): P2SHScriptSignature = {
|
||||
|
@ -189,9 +226,56 @@ object P2SHScriptSignature extends Factory[P2SHScriptSignature] with BitcoinSLog
|
|||
fromBytes(bytes)
|
||||
}
|
||||
|
||||
private def matchP2SHScriptSignature(scriptSig : ScriptSignature): P2SHScriptSignature = scriptSig match {
|
||||
case p2shScriptSig : P2SHScriptSignature => p2shScriptSig
|
||||
case x : ScriptSignature => throw new IllegalArgumentException("Expected p2sh script signature, got: " + x)
|
||||
def fromAsm(asm: Seq[ScriptToken]): P2SHScriptSignature = {
|
||||
require(isP2SHScriptSig(asm), "Given asm tokens are not a p2sh scriptSig, got: " + asm)
|
||||
val hex = asm.map(_.hex).mkString
|
||||
P2SHScriptSignatureImpl(hex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the given asm tokens are a [[P2SHScriptSignature]]
|
||||
* @param asm
|
||||
* @return
|
||||
*/
|
||||
def isP2SHScriptSig(asm: Seq[ScriptToken]): Boolean = asm match {
|
||||
case _ if (asm.size > 1 && isRedeemScript(asm.last)) => true
|
||||
case _ => false
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects if the given script token is a redeem script
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -297,23 +381,34 @@ trait P2PKScriptSignature extends ScriptSignature {
|
|||
}
|
||||
|
||||
object P2PKScriptSignature extends Factory[P2PKScriptSignature] {
|
||||
private case class P2PKScriptSignatureImpl(hex : String) extends P2PKScriptSignature
|
||||
|
||||
def apply(signature: ECDigitalSignature): P2PKScriptSignature = {
|
||||
val pushOps = BitcoinScriptUtil.calculatePushOp(signature.bytes)
|
||||
val signatureConstant = ScriptConstant(signature.bytes)
|
||||
val scriptSig = ScriptSignature.fromAsm(pushOps ++ Seq(signatureConstant))
|
||||
matchP2pkScriptSig(scriptSig)
|
||||
val asm = pushOps ++ Seq(signatureConstant)
|
||||
P2PKScriptSignature.fromAsm(asm)
|
||||
}
|
||||
|
||||
def fromAsm(asm: Seq[ScriptToken]): P2PKScriptSignature = {
|
||||
require(isP2PKScriptSignature(asm), "The given asm tokens were not a p2pk scriptSig, got: " + asm)
|
||||
val hex = asm.map(_.hex).mkString
|
||||
P2PKScriptSignatureImpl(hex)
|
||||
}
|
||||
|
||||
override def fromBytes(bytes: Seq[Byte]): P2PKScriptSignature = {
|
||||
val scriptSig = RawScriptSignatureParser.read(bytes)
|
||||
matchP2pkScriptSig(scriptSig)
|
||||
val asm = ScriptParser.fromBytes(bytes)
|
||||
P2PKScriptSignature.fromAsm(asm)
|
||||
}
|
||||
|
||||
private def matchP2pkScriptSig(scriptSig: ScriptSignature) = scriptSig match {
|
||||
case p2pkScriptSig: P2PKScriptSignature => p2pkScriptSig
|
||||
case x: ScriptSignature =>
|
||||
throw new IllegalArgumentException("We cannot have a non p2pk scriptsig returned from the p2pk script sig factory, got: " + x)
|
||||
|
||||
/**
|
||||
* P2PK scriptSigs always have the pattern [pushop, digitalSignature]
|
||||
* @param asm
|
||||
* @return
|
||||
*/
|
||||
def isP2PKScriptSignature(asm: Seq[ScriptToken]): Boolean = asm match {
|
||||
case List(w : BytesToPushOntoStack, x : ScriptConstant) => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,29 +422,6 @@ case object EmptyScriptSignature extends ScriptSignature {
|
|||
|
||||
object ScriptSignature extends Factory[ScriptSignature] with BitcoinSLogger {
|
||||
|
||||
private case class NonStandardScriptSignatureImpl(hex : String) extends NonStandardScriptSignature
|
||||
|
||||
private case class P2PKScriptSignatureImpl(hex : String) 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 = BitcoinScriptUtil.calculatePushOp(signature.bytes)
|
||||
val pubKeyBytesToPushOntoStack = BitcoinScriptUtil.calculatePushOp(pubKey.bytes)
|
||||
val asm : Seq[ScriptToken] = signatureBytesToPushOntoStack ++ Seq(ScriptConstant(signature.hex)) ++
|
||||
pubKeyBytesToPushOntoStack ++ Seq(ScriptConstant(pubKey.hex))
|
||||
fromAsm(asm)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an empty script signature
|
||||
|
@ -366,21 +438,18 @@ object ScriptSignature extends Factory[ScriptSignature] with BitcoinSLogger {
|
|||
* @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 (MultiSignatureScriptSignature.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
|
||||
MultiSignatureScriptSignature(scriptSigHex)
|
||||
case List(w : BytesToPushOntoStack, x : ScriptConstant, y : BytesToPushOntoStack,
|
||||
z : ScriptConstant) => P2PKHScriptSignatureImpl(scriptSigHex)
|
||||
case List(w : BytesToPushOntoStack, x : ScriptConstant) => P2PKScriptSignatureImpl(scriptSigHex)
|
||||
case _ => NonStandardScriptSignatureImpl(scriptSigHex)
|
||||
}
|
||||
def fromAsm(tokens : Seq[ScriptToken]) : ScriptSignature = tokens match {
|
||||
case Nil => EmptyScriptSignature
|
||||
case _ if (tokens.size > 1 && P2SHScriptSignature.isRedeemScript(tokens.last)) =>
|
||||
P2SHScriptSignature.fromAsm(tokens)
|
||||
case _ if (MultiSignatureScriptSignature.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
|
||||
MultiSignatureScriptSignature.fromAsm(tokens)
|
||||
case List(w : BytesToPushOntoStack, x : ScriptConstant, y : BytesToPushOntoStack,
|
||||
z : ScriptConstant) => P2PKHScriptSignature.fromAsm(tokens)
|
||||
case List(w : BytesToPushOntoStack, x : ScriptConstant) => P2PKScriptSignature.fromAsm(tokens)
|
||||
case _ => NonStandardScriptSignature.fromAsm(tokens)
|
||||
}
|
||||
|
||||
|
||||
|
@ -391,59 +460,16 @@ object ScriptSignature extends Factory[ScriptSignature] with BitcoinSLogger {
|
|||
* @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 => MultiSignatureScriptSignature(scriptSigHex)
|
||||
case s : NonStandardScriptPubKey => NonStandardScriptSignatureImpl(scriptSigHex)
|
||||
case EmptyScriptPubKey if (tokens.size == 0) => EmptyScriptSignature
|
||||
case EmptyScriptPubKey => NonStandardScriptSignatureImpl(scriptSigHex)
|
||||
}
|
||||
def fromScriptPubKey(tokens : Seq[ScriptToken], scriptPubKey : ScriptPubKey) : ScriptSignature = scriptPubKey match {
|
||||
case s : P2SHScriptPubKey => P2SHScriptSignature.fromAsm(tokens)
|
||||
case s : P2PKHScriptPubKey => P2PKHScriptSignature.fromAsm(tokens)
|
||||
case s : P2PKScriptPubKey => P2PKScriptSignature.fromAsm(tokens)
|
||||
case s : MultiSignatureScriptPubKey => MultiSignatureScriptSignature.fromAsm(tokens)
|
||||
case s : NonStandardScriptPubKey => NonStandardScriptSignature.fromAsm(tokens)
|
||||
case EmptyScriptPubKey if (tokens.size == 0) => EmptyScriptSignature
|
||||
case EmptyScriptPubKey => NonStandardScriptSignature.fromAsm(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
|
||||
}
|
||||
|
||||
def apply(signature : ECDigitalSignature, pubKey : ECPublicKey) : ScriptSignature = factory(signature,pubKey)
|
||||
|
||||
def apply(tokens : Seq[ScriptToken], scriptPubKey : ScriptPubKey) : ScriptSignature = fromScriptPubKey(tokens, scriptPubKey)
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ class ScriptSignatureFactoryTest extends FlatSpec with MustMatchers {
|
|||
val digitalSignature : ECDigitalSignature = ECDigitalSignature(digitalSignatureBytes)
|
||||
val publicKeyBytes = TestUtil.p2pkhInputScriptAsm(3).bytes
|
||||
val publicKey : ECPublicKey = ECPublicKey(publicKeyBytes)
|
||||
val actualScriptSig : ScriptSignature = ScriptSignature(digitalSignature,publicKey)
|
||||
val actualScriptSig : ScriptSignature = P2PKHScriptSignature(digitalSignature,publicKey)
|
||||
actualScriptSig.asm must be (TestUtil.p2pkhInputScriptAsm)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue