mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-20 10:13:26 +01:00
Creating P2SHScriptPubKeySpec & P2SHScriptSignatureSpec, adding companion object to create P2SHScriptSigs & P2SHScriptPubKeys, adding functionality to calculate push operators for constants in scripts
This commit is contained in:
parent
44d01ef65e
commit
b4b93bf49b
@ -170,6 +170,26 @@ object MultiSignatureScriptPubKey extends Factory[MultiSignatureScriptPubKey] wi
|
||||
*/
|
||||
trait P2SHScriptPubKey extends ScriptPubKey
|
||||
|
||||
object P2SHScriptPubKey extends Factory[P2SHScriptPubKey] {
|
||||
override def fromBytes(bytes : Seq[Byte]): P2SHScriptPubKey = {
|
||||
val scriptPubKey = RawScriptPubKeyParser.read(bytes)
|
||||
matchP2SHScriptPubKey(scriptPubKey)
|
||||
}
|
||||
|
||||
def apply(scriptPubKey: ScriptPubKey) : P2SHScriptPubKey = {
|
||||
val hash = CryptoUtil.ripeMd160(scriptPubKey.bytes)
|
||||
val asm = Seq(OP_HASH160, BytesToPushOntoStack(hash.bytes.size), ScriptConstant(hash.bytes), OP_EQUAL)
|
||||
val p2shScriptPubKey = ScriptPubKey.fromAsm(asm)
|
||||
matchP2SHScriptPubKey(p2shScriptPubKey)
|
||||
}
|
||||
|
||||
private def matchP2SHScriptPubKey(scriptPubKey : ScriptPubKey): P2SHScriptPubKey = scriptPubKey match {
|
||||
case p2shScriptPubKey : P2SHScriptPubKey => p2shScriptPubKey
|
||||
case scriptPubKey : ScriptPubKey =>
|
||||
throw new IllegalArgumentException("Exepected multisignature scriptPubKey, got: " + scriptPubKey)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a pay to public key script public key
|
||||
* https://bitcoin.org/en/developer-guide#pubkey
|
||||
|
@ -173,6 +173,27 @@ trait P2SHScriptSignature extends ScriptSignature {
|
||||
//call .tail twice to remove the serialized redeemScript & it's bytesToPushOntoStack constant
|
||||
(asm.reverse.tail.tail.reverse, Seq(asm.last))
|
||||
}
|
||||
}
|
||||
|
||||
object P2SHScriptSignature extends Factory[P2SHScriptSignature] with BitcoinSLogger {
|
||||
override def fromBytes(bytes : Seq[Byte]): P2SHScriptSignature = {
|
||||
val scriptSig = RawScriptSignatureParser.read(bytes)
|
||||
logger.info("p2sh script sig asm: " + scriptSig.asm)
|
||||
matchP2SHScriptSignature(scriptSig)
|
||||
}
|
||||
|
||||
def apply(scriptSig : ScriptSignature, redeemScript : ScriptPubKey): P2SHScriptSignature = {
|
||||
//we need to calculate the size of the redeemScript and add the corresponding push op
|
||||
val pushOps = BitcoinScriptUtil.calculatePushOp(ScriptConstant(redeemScript.bytes))
|
||||
val bytes = scriptSig.bytes ++ pushOps.flatMap(_.bytes) ++ redeemScript.bytes
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -54,8 +54,6 @@ trait ConstantInterpreter extends BitcoinSLogger {
|
||||
* @return
|
||||
*/
|
||||
def pushScriptNumberBytesToStack(program : ScriptProgram) : ScriptProgram = {
|
||||
|
||||
|
||||
val bytesNeeded : Long = program.script.head match {
|
||||
case OP_PUSHDATA1 | OP_PUSHDATA2 | OP_PUSHDATA4 =>
|
||||
bytesNeededForPushOp(program.script(1))
|
||||
|
@ -50,7 +50,6 @@ trait ScriptOperation extends ScriptToken {
|
||||
sealed trait ScriptConstant extends ScriptToken {
|
||||
/**
|
||||
* Returns if the constant is encoded in the shortest possible way
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
def isShortestEncoding : Boolean = BitcoinScriptUtil.isShortestEncoding(this)
|
||||
@ -63,7 +62,6 @@ sealed trait ScriptConstant extends ScriptToken {
|
||||
sealed trait ScriptNumber extends ScriptConstant {
|
||||
/**
|
||||
* The underlying number of the ScriptNumber
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
def num : Long
|
||||
@ -87,7 +85,6 @@ sealed trait ScriptNumber extends ScriptConstant {
|
||||
* This equality just checks that the underlying scala numbers are equivalent, NOT if the numbers
|
||||
* are bitwise equivalent in Script. For instance ScriptNumber(0x01).numEqual(ScriptNumber(0x00000000001)) == true
|
||||
* but (ScriptNumber(0x01) == (ScriptNumber(0x00000000001))) == false
|
||||
*
|
||||
* @param that
|
||||
* @return
|
||||
*/
|
||||
@ -179,6 +176,11 @@ object ScriptNumber extends Factory[ScriptNumber] {
|
||||
*/
|
||||
case object OP_PUSHDATA1 extends ScriptOperation {
|
||||
override def opCode = 76
|
||||
|
||||
/**
|
||||
* The maximum amount of bytes OP_PUSHDATA1 can push onto the stack
|
||||
*/
|
||||
def max = 255
|
||||
}
|
||||
|
||||
/**
|
||||
@ -186,6 +188,12 @@ case object OP_PUSHDATA1 extends ScriptOperation {
|
||||
*/
|
||||
case object OP_PUSHDATA2 extends ScriptOperation {
|
||||
override def opCode = 77
|
||||
|
||||
/**
|
||||
* The max amount of data that OP_PUSHDATA2 can push onto the stack
|
||||
* @return
|
||||
*/
|
||||
def max = 65535
|
||||
}
|
||||
|
||||
/**
|
||||
@ -193,6 +201,12 @@ case object OP_PUSHDATA2 extends ScriptOperation {
|
||||
*/
|
||||
case object OP_PUSHDATA4 extends ScriptOperation {
|
||||
override def opCode = 78
|
||||
|
||||
/**
|
||||
* The maximum amount of data that OP_PUSHDATA4 can be push on the stack
|
||||
* @return
|
||||
*/
|
||||
def max = 4294967295L
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
package org.bitcoins.core.util
|
||||
|
||||
import org.bitcoins.core.crypto.ECPublicKey
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
import org.bitcoins.core.script.constant._
|
||||
import org.bitcoins.core.script.crypto.{OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY, OP_CHECKSIG, OP_CHECKSIGVERIFY}
|
||||
import org.bitcoins.core.script.flag.{ScriptFlag, ScriptFlagUtil}
|
||||
@ -178,12 +180,36 @@ trait BitcoinScriptUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the push operation for the given [[ScriptToken]]
|
||||
* @param scriptToken
|
||||
* @return
|
||||
*/
|
||||
def calculatePushOp(scriptToken : ScriptToken) : Seq[ScriptToken] = {
|
||||
//push ops following an OP_PUSHDATA operation are interpreted as unsigned numbers
|
||||
val scriptTokenSize = UInt32(scriptToken.bytes.size)
|
||||
val bytes = scriptTokenSize.bytes
|
||||
if (scriptTokenSize <= UInt32(75)) Seq(BytesToPushOntoStack(scriptToken.bytes.size))
|
||||
else if (scriptTokenSize <= UInt32(OP_PUSHDATA1.max)) {
|
||||
val pushConstant = ScriptConstant(BitcoinSUtil.flipEndianess(bytes.slice(bytes.length-1,bytes.length)))
|
||||
Seq(OP_PUSHDATA1, pushConstant)
|
||||
}
|
||||
else if (scriptTokenSize <= UInt32(OP_PUSHDATA2.max)) {
|
||||
val pushConstant = ScriptConstant(BitcoinSUtil.flipEndianess(bytes.slice(bytes.length-2,bytes.length)))
|
||||
Seq(OP_PUSHDATA2, pushConstant)
|
||||
}
|
||||
else if (scriptTokenSize <= UInt32(OP_PUSHDATA4.max)) {
|
||||
val pushConstant = ScriptConstant(BitcoinSUtil.flipEndianess(bytes))
|
||||
Seq(OP_PUSHDATA4, pushConstant)
|
||||
}
|
||||
else throw new IllegalArgumentException("ScriptToken is to large for pushops, size: " + scriptTokenSize)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whenever a script constant is interpreted to a number BIP62 could enforce that number to be encoded
|
||||
* in the smallest encoding possible
|
||||
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L220-L237
|
||||
*
|
||||
* @param constant
|
||||
* @return
|
||||
*/
|
||||
@ -193,7 +219,6 @@ trait BitcoinScriptUtil {
|
||||
* Whenever a script constant is interpreted to a number BIP62 could enforce that number to be encoded
|
||||
* in the smallest encoding possible
|
||||
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L220-L237
|
||||
*
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
@ -217,7 +242,6 @@ trait BitcoinScriptUtil {
|
||||
* Whenever a script constant is interpreted to a number BIP62 could enforce that number to be encoded
|
||||
* in the smallest encoding possible
|
||||
* https://github.com/bitcoin/bitcoin/blob/a6a860796a44a2805a58391a009ba22752f64e32/src/script/script.h#L220-L237
|
||||
*
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
@ -225,7 +249,6 @@ trait BitcoinScriptUtil {
|
||||
/**
|
||||
* Checks the public key encoding according to bitcoin core's function
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L202
|
||||
*
|
||||
* @param key the key whose encoding we are checking
|
||||
* @param program the program whose flags which dictate the rules for the public keys encoding
|
||||
* @return if the key is encoded correctly against the rules give in the flags parameter
|
||||
@ -235,7 +258,6 @@ trait BitcoinScriptUtil {
|
||||
/**
|
||||
* Checks the public key encoding according to bitcoin core's function
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L202
|
||||
*
|
||||
* @param key the key whose encoding we are checking
|
||||
* @param flags the flags which dictate the rules for the public keys encoding
|
||||
* @return if the key is encoded correctly against the rules givein the flags parameter
|
||||
@ -249,7 +271,6 @@ trait BitcoinScriptUtil {
|
||||
/**
|
||||
* Returns true if the key is compressed or uncompressed, false otherwise
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L66
|
||||
*
|
||||
* @param key the public key that is being checked
|
||||
* @return true if the key is compressed/uncompressed otherwise false
|
||||
*/
|
||||
|
@ -0,0 +1,14 @@
|
||||
package org.bitcoins.core.protocol.script
|
||||
|
||||
import org.scalacheck.{Prop, Properties}
|
||||
|
||||
/**
|
||||
* Created by chris on 6/24/16.
|
||||
*/
|
||||
class P2SHScriptPubKeySpec extends Properties("P2SHScriptPubKeySpec") {
|
||||
|
||||
property("Symmetrical serialization") =
|
||||
Prop.forAll(ScriptGenerators.p2shScriptPubKey) { p2shScriptPubKey =>
|
||||
P2SHScriptPubKey(p2shScriptPubKey.hex) == p2shScriptPubKey
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package org.bitcoins.core.protocol.script
|
||||
|
||||
import org.scalacheck.{Prop, Properties}
|
||||
|
||||
/**
|
||||
* Created by chris on 6/24/16.
|
||||
*/
|
||||
class P2SHScriptSignatureSpec extends Properties("P2SHScriptSignatureSpec") {
|
||||
|
||||
|
||||
property("Symmetrical serialization") =
|
||||
Prop.forAll(ScriptGenerators.p2shScriptSignature) { p2shScriptSig =>
|
||||
P2SHScriptSignature(p2shScriptSig.hex) == p2shScriptSig
|
||||
|
||||
}
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
package org.bitcoins.core.protocol.script
|
||||
|
||||
|
||||
import org.bitcoins.core.crypto.{ECPublicKey}
|
||||
import org.bitcoins.core.crypto.ECPublicKey
|
||||
import org.bitcoins.core.script.constant.{BytesToPushOntoStack, OP_0, ScriptConstant}
|
||||
import org.bitcoins.core.util.TestUtil
|
||||
import org.bitcoins.core.util.{BitcoinSLogger, TestUtil}
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
|
||||
/**
|
||||
* Created by chris on 3/8/16.
|
||||
*/
|
||||
class P2SHScriptSignatureTest extends FlatSpec with MustMatchers {
|
||||
class P2SHScriptSignatureTest extends FlatSpec with MustMatchers with BitcoinSLogger {
|
||||
|
||||
"P2SHScriptSignature" must "find the public keys embedded inside of the redeemScript" in {
|
||||
val rawP2SHScriptSig = TestUtil.rawP2shInputScript2Of2
|
||||
@ -37,4 +37,13 @@ class P2SHScriptSignatureTest extends FlatSpec with MustMatchers {
|
||||
ScriptConstant("3045022100906aaca39f022acd8b7a38fd2f92aca9e9f35cfeaee69a6f13e1d083ae18222602204c9ed96fc6c4de56fd85c679fc59c16ee1ccc80c42563b86174e1a506fc007c801")
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
it must "create a p2sh script signature out of a large multisignature script signature & multiSignature scriptPubKey" in {
|
||||
val multiSigScriptSig = MultiSignatureScriptSignature("00473045022100867aa98fe0210eef1dff246de2ada03f4846ac9ec006ab24c1d868e6f08fb4b30220131acbe68c3302ed7bf82949810d6daf780703021cdcf481f5142f76fe31af04")
|
||||
logger.info("==============================================================")
|
||||
val multiSigScriptPubKey = MultiSignatureScriptPubKey("542103a1d052fcc371fc80d0d268169620ee125167335d9895f1639897c774deb80e1221038d83a6ed9f25ada86473f00200e31e11e7f5756c2a152e58f12ba078536d86a921031078d342d0ad318d865b83aa8eab7b747e92382e1f361f897f280560c984b426210255715a21757122db811b3b806ab2a3f5c4b04e229907c5a9497d30a7e6803441210244fe1d3ec95e0c3c9aa796686fffd9ba98ae778b80fc1038f171ced12d9b5f662102fcaf377e285e759bc0f0afc0d43b9f1f76cb8cd1279542e739261b9f577f5954210335e6efec824fdd539132a63696dd1dc719fbfc5efffa3d2576601c98eae6619821028af3bf77385560fc68b2932b66cae607052b0c53ae74c015fecf2d7ee9837e5f2102e1404987c0ee03803487923998f862700dcf9bdf4518d744a94fa208ff1f33d02103eaea267ef814f1ba57f43fe8f8dd8a1e8ab99d9d0e34e9d0a144e2a5e4c70efc21035e7bdaeba5d370bf5565937d3734c6e5bc0746d9d9bbf6846c7f3219a406a0da21026eb05e10608a3543b182a69d1dc651bdbab69e684be98260041a89aed1c4e1e62102e407986723ccdf2858c0d2116ed450d7b9ee6609d96603018c31470b065569eb2103f4ca93db51e4c602eda84fbb6dd285b66b12e53f1606a992bbc5bde0323ee64621022c83ca35993a8e18d76c386bb1d8e21c2575b2f675528e162b4cf81e7502745e5fae")
|
||||
val p2shScriptSig = P2SHScriptSignature(multiSigScriptSig, multiSigScriptPubKey)
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user