diff --git a/core-test/src/test/scala/org/bitcoins/core/protocol/script/ScriptWitnessSpec.scala b/core-test/src/test/scala/org/bitcoins/core/protocol/script/ScriptWitnessSpec.scala index a3b93f7113..414d3c4b75 100644 --- a/core-test/src/test/scala/org/bitcoins/core/protocol/script/ScriptWitnessSpec.scala +++ b/core-test/src/test/scala/org/bitcoins/core/protocol/script/ScriptWitnessSpec.scala @@ -19,4 +19,12 @@ class ScriptWitnessSpec extends Properties("ScriptWitnessSpec") { P2WSHWitnessV0(spk).redeemScript == spk } } + + property("pull script signature out of p2wsh witness") = { + Prop.forAll(ScriptGenerators.rawScriptPubKey, + ScriptGenerators.rawScriptSignature) { + case ((spk, _), scriptSig) => + P2WSHWitnessV0(spk, scriptSig).scriptSignature == scriptSig + } + } } diff --git a/core/src/main/scala/org/bitcoins/core/protocol/script/ScriptWitness.scala b/core/src/main/scala/org/bitcoins/core/protocol/script/ScriptWitness.scala index f242c651be..5d03354fad 100644 --- a/core/src/main/scala/org/bitcoins/core/protocol/script/ScriptWitness.scala +++ b/core/src/main/scala/org/bitcoins/core/protocol/script/ScriptWitness.scala @@ -1,7 +1,11 @@ package org.bitcoins.core.protocol.script import org.bitcoins.core.protocol.CompactSizeUInt -import org.bitcoins.core.serializers.script.RawScriptWitnessParser +import org.bitcoins.core.script.constant.{OP_0, ScriptNumberOperation} +import org.bitcoins.core.serializers.script.{ + RawScriptWitnessParser, + ScriptParser +} import org.bitcoins.core.util.{BitcoinScriptUtil, BytesUtil} import org.bitcoins.crypto.{ ECDigitalSignature, @@ -106,6 +110,21 @@ sealed abstract class P2WSHWitnessV0 extends ScriptWitnessV0 { relevantStack.map(ECDigitalSignature.fromBytes) } + // Note that this is not guaranteed to work for non-standard script signatures + lazy val scriptSignature: ScriptSignature = { + val asm = stack.toVector.tail.reverse.flatMap { bytes => + if (bytes.isEmpty) { + Vector(OP_0) + } else if (bytes.length == 1 && bytes.head <= 16 && bytes.head >= -1) { + ScriptNumberOperation.fromNumber(bytes.head.toLong).toVector + } else { + ScriptParser.fromBytes(CompactSizeUInt.calc(bytes).bytes ++ bytes) + } + } + + ScriptSignature.fromAsm(asm) + } + override def toString = s"P2WSHWitnessV0(${stack.map(BytesUtil.encodeHex(_)).toString})" } diff --git a/testkit/src/main/scala/org/bitcoins/testkit/core/gen/ScriptGenerators.scala b/testkit/src/main/scala/org/bitcoins/testkit/core/gen/ScriptGenerators.scala index bbc04b9cf9..be4c836e50 100644 --- a/testkit/src/main/scala/org/bitcoins/testkit/core/gen/ScriptGenerators.scala +++ b/testkit/src/main/scala/org/bitcoins/testkit/core/gen/ScriptGenerators.scala @@ -539,6 +539,17 @@ sealed abstract class ScriptGenerators extends BitcoinSLogger { ) } + def rawScriptSignature: Gen[ScriptSignature] = { + Gen.oneOf( + p2pkScriptSignature, + p2pkhScriptSignature, + p2pkWithTimeoutScriptSignature, + multiSignatureScriptSignature, + emptyScriptSignature, + conditionalScriptSignature + ) + } + /** * Generates a `ScriptSignature` corresponding to the type of * `ScriptPubKey` given.