mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-20 02:11:40 +01:00
Fixing parsing issue with OP_PUSHDATA operations, implementing finding digital signatures contained in ScriptSignatures
This commit is contained in:
parent
6bbcfa8cff
commit
de8b7e15ca
@ -202,13 +202,50 @@ trait ScriptParser extends ScalacoinUtil {
|
||||
val (constant,newTail) = sliceConstant(bytesToPushOntoStack,tail)
|
||||
val scriptConstant = new ScriptConstantImpl(constant)
|
||||
ParsingHelper(newTail,scriptConstant :: bytesToPushOntoStack :: accum)
|
||||
|
||||
case OP_PUSHDATA1 => parseOpPushData(op,accum,tail)
|
||||
case OP_PUSHDATA2 => parseOpPushData(op,accum,tail)
|
||||
case OP_PUSHDATA4 => parseOpPushData(op,accum,tail)
|
||||
case _ =>
|
||||
//means that we need to push the operation onto the stack
|
||||
ParsingHelper(tail,op :: accum)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses OP_PUSHDATA operations correctly. Slices the appropriate amount of bytes off of the tail and pushes
|
||||
* them onto the accumulator.
|
||||
* @param op
|
||||
* @param accum
|
||||
* @param tail
|
||||
* @return
|
||||
*/
|
||||
private def parseOpPushData(op : ScriptOperation, accum : List[ScriptToken], tail : List[Byte]) : ParsingHelper[Byte] = {
|
||||
op match {
|
||||
case OP_PUSHDATA1 =>
|
||||
//next byte is size of the script constant
|
||||
val bytesToPushOntoStack = BytesToPushOntoStackImpl(Integer.parseInt(ScalacoinUtil.encodeHex(tail.head), 16))
|
||||
val scriptConstant = new ScriptConstantImpl(tail.slice(1,bytesToPushOntoStack.num+1))
|
||||
ParsingHelper[Byte](tail.slice(bytesToPushOntoStack.num+1,tail.size),
|
||||
scriptConstant :: bytesToPushOntoStack :: op :: accum)
|
||||
case OP_PUSHDATA2 =>
|
||||
//next 2 bytes is the size of the script constant
|
||||
val scriptConstantHex = ScalacoinUtil.encodeHex(tail.slice(0,2))
|
||||
val bytesToPushOntoStack = BytesToPushOntoStackImpl(Integer.parseInt(scriptConstantHex, 16))
|
||||
val scriptConstant = new ScriptConstantImpl(tail.slice(2,bytesToPushOntoStack.num + 2))
|
||||
ParsingHelper[Byte](tail.slice(bytesToPushOntoStack.num + 2,tail.size),
|
||||
scriptConstant :: bytesToPushOntoStack :: op :: accum)
|
||||
case OP_PUSHDATA4 =>
|
||||
//nextt 4 bytes is the size of the script constant
|
||||
val scriptConstantHex = ScalacoinUtil.encodeHex(tail.slice(0,4))
|
||||
val bytesToPushOntoStack = BytesToPushOntoStackImpl(Integer.parseInt(scriptConstantHex, 16))
|
||||
val scriptConstant = new ScriptConstantImpl(tail.slice(4,bytesToPushOntoStack.num + 4))
|
||||
ParsingHelper[Byte](tail.slice(bytesToPushOntoStack.num + 4,tail.size),
|
||||
scriptConstant :: bytesToPushOntoStack :: op :: accum)
|
||||
case _ => throw new RuntimeException("parseOpPushData can only parse OP_PUSHDATA operations")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an operation if the tail is a List[String]
|
||||
* If the operation is a bytesToPushOntoStack, it pushes the number of bytes onto the stack
|
||||
|
@ -1,8 +1,8 @@
|
||||
package org.scalacoin.protocol.script
|
||||
|
||||
import org.scalacoin.marshallers.transaction.TransactionElement
|
||||
import org.scalacoin.script.constant.{OP_0, ScriptToken}
|
||||
import org.scalacoin.script.crypto.{HashType, HashTypeFactory}
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.script.crypto.{OP_CHECKMULTISIG, HashType, HashTypeFactory}
|
||||
import org.scalacoin.util.ScalacoinUtil
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
@ -15,17 +15,27 @@ trait ScriptSignature extends TransactionElement {
|
||||
def asm : Seq[ScriptToken]
|
||||
def hex : String
|
||||
|
||||
def signature : ScriptToken = {
|
||||
def signatures : Seq[ScriptToken] = {
|
||||
if (asm.headOption.isDefined && asm.head == OP_0) {
|
||||
//must be p2sh because of bug that forces p2sh scripts
|
||||
//to begin with OP_0
|
||||
asm(2)
|
||||
} else asm(1)
|
||||
//scripSig for p2sh input script
|
||||
//OP_0 <scriptSig> <scriptSig> ... <scriptSig> <redeemScript>
|
||||
|
||||
//this list still contains the ByteToPushOntoStack operations
|
||||
//need to filter those out
|
||||
val scriptSigs = asm.slice(1,asm.size-1)
|
||||
//filter out all of the PUSHDATA / BytesToPushOntoStack operations
|
||||
scriptSigs.filterNot( op => op.isInstanceOf[BytesToPushOntoStack]
|
||||
|| op == OP_PUSHDATA1
|
||||
|| op == OP_PUSHDATA2
|
||||
|| op == OP_PUSHDATA4)
|
||||
} else Seq(asm(1))
|
||||
}
|
||||
def hashType : HashType = {
|
||||
require(HashTypeFactory.fromByte(signature.bytes.last).isDefined,
|
||||
"Hash type could not be read for this scriptSig: " + signature.hex)
|
||||
HashTypeFactory.fromByte(signature.bytes.last).get
|
||||
require(HashTypeFactory.fromByte(signatures.head.bytes.last).isDefined,
|
||||
"Hash type could not be read for this scriptSig: " + signatures.head.hex)
|
||||
HashTypeFactory.fromByte(signatures.head.bytes.last).get
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package org.scalacoin.marshallers.script
|
||||
|
||||
import org.scalacoin.protocol.script.ScriptSignature
|
||||
import org.scalacoin.script.constant.{BytesToPushOntoStackImpl, ScriptConstantImpl}
|
||||
import org.scalacoin.script.constant.{OP_PUSHDATA1, ScriptConstantImpl, BytesToPushOntoStackImpl, OP_0}
|
||||
import org.scalacoin.util.TestUtil
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
|
||||
@ -33,4 +33,26 @@ class RawScriptSignatureParserTest extends FlatSpec with MustMatchers with RawSc
|
||||
|
||||
scriptSig.asm must be (Seq(BytesToPushOntoStackImpl(72), ScriptConstantImpl("3045022100ad8e961fe3c22b2647d92b078f4c0cf81b3106ea5bf8b900ab8646aa4430216f022071d4edc2b5588be20ac4c2d07edd8ed069e10b2402d3dce2d3b835ccd075f28301"), BytesToPushOntoStackImpl(65), ScriptConstantImpl("04fa79182bbc26c708b5d9f36b8635947d4a834ea356cf612ede08395c295f962e0b1dc2557aba34188640e51a58ed547f2c89c8265cd0c04ff890d8435648746e")))
|
||||
}
|
||||
|
||||
|
||||
it must "parse a raw scriptSig for a p2sh address with a lot of signatures" in {
|
||||
TestUtil.p2shInputScriptLargeSignature.asm must be (Seq(OP_0,
|
||||
BytesToPushOntoStackImpl(72),
|
||||
ScriptConstantImpl("3045022100a077d4fe9a81411ecb796c254d8b4e0bc73ff86a42288bc3b3ecfa1ef26c00dd02202389bf96cf38c14c3a6ccb8c688339f3fd880b724322862547a8ee3b547a9df901"),
|
||||
BytesToPushOntoStackImpl(71),
|
||||
ScriptConstantImpl("304402207c0692464998e7f3869f8501cdd25bbcd9d32b6fd34ae8aeae643b422a8dfd42022057eb16f8ca1f34e88babc9f8beb4c2521eb5c4dea41f8902a70d045f1c132a4401"),
|
||||
BytesToPushOntoStackImpl(71),
|
||||
ScriptConstantImpl("3044022024233923253c73569f4b34723a5495698bc124b099c5542a5997d13fba7d18a802203c317bddc070276c6f6c79cb3415413e608af30e4759e31b0d53eab3ca0acd4e01"),
|
||||
BytesToPushOntoStackImpl(72),
|
||||
ScriptConstantImpl("30450221009b9f0d8b945717d2fca3685093d547a3928d122b8894903ed51e2248303213bc022008b376422c9f2cd713b9d10b5b106d1c56c5893dcc01ae300253ed2234bdb63f01"),
|
||||
BytesToPushOntoStackImpl(71),
|
||||
ScriptConstantImpl("30440220257b57cb09386d82c4328461f8fe200c2f381d6b635e2a2f4ea40c8d945e9ec102201ec67d58d51a309af4d8896e9147a42944e9f9833a456f733ea5fa6954ed2fed01"),
|
||||
OP_PUSHDATA1,
|
||||
BytesToPushOntoStackImpl(241),
|
||||
ScriptConstantImpl("55210269992fb441ae56968e5b77d46a3e53b69f136444ae65a94041fc937bdb28d93321021df31471281d4478df85bfce08a10aab82601dca949a79950f8ddf7002bd915a2102174c82021492c2c6dfcbfa4187d10d38bed06afb7fdcd72c880179fddd641ea121033f96e43d72c33327b6a4631ccaa6ea07f0b106c88b9dc71c9000bb6044d5e88a210313d8748790f2a86fb524579b46ce3c68fedd58d2a738716249a9f7d5458a15c221030b632eeb079eb83648886122a04c7bf6d98ab5dfb94cf353ee3e9382a4c2fab02102fb54a7fcaa73c307cfd70f3fa66a2e4247a71858ca731396343ad30c7c4009ce57ae")
|
||||
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -117,6 +117,13 @@ class ScriptParserTest extends FlatSpec with MustMatchers with ScriptParser with
|
||||
parse(str) must be (expectedScript)
|
||||
}
|
||||
|
||||
it must "parse a OP_PUSHDATA1 correct from a scriptSig" in {
|
||||
//https://tbtc.blockr.io/api/v1/tx/raw/5d254a872c9197c683ea9111fb5c0e2e0f49280a89961c45b9fea76834d335fe
|
||||
val str = "4cf1" +
|
||||
"55210269992fb441ae56968e5b77d46a3e53b69f136444ae65a94041fc937bdb28d93321021df31471281d4478df85bfce08a10aab82601dca949a79950f8ddf7002bd915a2102174c82021492c2c6dfcbfa4187d10d38bed06afb7fdcd72c880179fddd641ea121033f96e43d72c33327b6a4631ccaa6ea07f0b106c88b9dc71c9000bb6044d5e88a210313d8748790f2a86fb524579b46ce3c68fedd58d2a738716249a9f7d5458a15c221030b632eeb079eb83648886122a04c7bf6d98ab5dfb94cf353ee3e9382a4c2fab02102fb54a7fcaa73c307cfd70f3fa66a2e4247a71858ca731396343ad30c7c4009ce57ae"
|
||||
parse(str) must be (Seq(OP_PUSHDATA1, BytesToPushOntoStackImpl(241), ScriptConstantImpl("55210269992fb441ae56968e5b77d46a3e53b69f136444ae65a94041fc937bdb28d93321021df31471281d4478df85bfce08a10aab82601dca949a79950f8ddf7002bd915a2102174c82021492c2c6dfcbfa4187d10d38bed06afb7fdcd72c880179fddd641ea121033f96e43d72c33327b6a4631ccaa6ea07f0b106c88b9dc71c9000bb6044d5e88a210313d8748790f2a86fb524579b46ce3c68fedd58d2a738716249a9f7d5458a15c221030b632eeb079eb83648886122a04c7bf6d98ab5dfb94cf353ee3e9382a4c2fab02102fb54a7fcaa73c307cfd70f3fa66a2e4247a71858ca731396343ad30c7c4009ce57ae") ))
|
||||
}
|
||||
|
||||
|
||||
it must "parse bytes from a string" in {
|
||||
val str = "0xFF00"
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.scalacoin.protocol.script
|
||||
|
||||
import org.scalacoin.script.crypto.SIGHASH_ALL
|
||||
import org.scalacoin.script.constant.ScriptConstantImpl
|
||||
import org.scalacoin.script.crypto.{SIGHASH_SINGLE, SIGHASH_ALL}
|
||||
import org.scalacoin.util.{TestUtil, ScalacoinUtil}
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
|
||||
@ -11,7 +12,7 @@ class ScriptSignatureTest extends FlatSpec with MustMatchers {
|
||||
|
||||
"ScriptSignature" must "find the digital signature for the transaction inside of a p2pkh script signature" in {
|
||||
val scriptSig = ScriptSignatureFactory.factory(TestUtil.rawScriptSig)
|
||||
scriptSig.signature.hex must be ("3045022100ad8e961fe3c22b2647d92b078f4c0cf81b3106ea5bf8b900ab8646aa4430216f022071d4edc2b5588be20ac4c2d07edd8ed069e10b2402d3dce2d3b835ccd075f28301")
|
||||
scriptSig.signatures.head.hex must be ("3045022100ad8e961fe3c22b2647d92b078f4c0cf81b3106ea5bf8b900ab8646aa4430216f022071d4edc2b5588be20ac4c2d07edd8ed069e10b2402d3dce2d3b835ccd075f28301")
|
||||
}
|
||||
|
||||
it must "derive the signature hash type from the signature" in {
|
||||
@ -22,12 +23,31 @@ class ScriptSignatureTest extends FlatSpec with MustMatchers {
|
||||
|
||||
it must "find the digital signature for a p2sh script signature" in {
|
||||
val scriptSig = TestUtil.p2shInputScript
|
||||
scriptSig.signature.hex must be ("304402207df6dd8dad22d49c3c83d8031733c32a53719278eb7985d3b35b375d776f84f102207054f9209a1e87d55feafc90aa04c33008e5bae9191da22aeaa16efde96f41f001")
|
||||
scriptSig.signatures.head.hex must be ("304402207df6dd8dad22d49c3c83d8031733c32a53719278eb7985d3b35b375d776f84f102207054f9209a1e87d55feafc90aa04c33008e5bae9191da22aeaa16efde96f41f001")
|
||||
}
|
||||
|
||||
it must "find all the digital signatures for a p2sh script signature with a large amount of sigs" in {
|
||||
val scriptSig = TestUtil.p2shInputScriptLargeSignature
|
||||
|
||||
println(scriptSig.asm)
|
||||
scriptSig.signatures must be (Seq(
|
||||
ScriptConstantImpl("3045022100a077d4fe9a81411ecb796c254d8b4e0bc73ff86a42288bc3b3ecfa1ef26c00dd02202389bf96cf38c14c3a6ccb8c688339f3fd880b724322862547a8ee3b547a9df901"),
|
||||
ScriptConstantImpl("304402207c0692464998e7f3869f8501cdd25bbcd9d32b6fd34ae8aeae643b422a8dfd42022057eb16f8ca1f34e88babc9f8beb4c2521eb5c4dea41f8902a70d045f1c132a4401"),
|
||||
ScriptConstantImpl("3044022024233923253c73569f4b34723a5495698bc124b099c5542a5997d13fba7d18a802203c317bddc070276c6f6c79cb3415413e608af30e4759e31b0d53eab3ca0acd4e01"),
|
||||
ScriptConstantImpl("30450221009b9f0d8b945717d2fca3685093d547a3928d122b8894903ed51e2248303213bc022008b376422c9f2cd713b9d10b5b106d1c56c5893dcc01ae300253ed2234bdb63f01"),
|
||||
ScriptConstantImpl("30440220257b57cb09386d82c4328461f8fe200c2f381d6b635e2a2f4ea40c8d945e9ec102201ec67d58d51a309af4d8896e9147a42944e9f9833a456f733ea5fa6954ed2fed01")
|
||||
))
|
||||
}
|
||||
it must "find the hash type for a p2sh script signature" in {
|
||||
TestUtil.p2shInputScript.hashType must be (SIGHASH_ALL)
|
||||
}
|
||||
|
||||
it must "find the digital signature and hash type for a SIGHASH_SINGLE" in {
|
||||
TestUtil.p2shInputScriptSigHashSingle.signatures.head.hex must be ("3045022100dfcfafcea73d83e1c54d444a19fb30d17317f922c19e2ff92dcda65ad09cba24022001e7a805c5672c49b222c5f2f1e67bb01f87215fb69df184e7c16f66c1f87c2903")
|
||||
TestUtil.p2shInputScriptSigHashSingle.hashType must be (SIGHASH_SINGLE)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -48,10 +48,21 @@ object TestUtil {
|
||||
ScriptConstantImpl("512102b022902a0fdd71e831c37e4136c2754a59887be0618fb75336d7ab67e2982ff551ae")
|
||||
)
|
||||
|
||||
|
||||
val p2shOutputScript = "a914eda8ae08b5c9f973f49543e90a7c292367b3337c87"
|
||||
val p2shOutputScriptNotParsedAsm = "OP_HASH160 eda8ae08b5c9f973f49543e90a7c292367b3337c OP_EQUAL"
|
||||
val p2shOutputScriptAsm = List(OP_HASH160, BytesToPushOntoStackImpl(20), ScriptConstantImpl("eda8ae08b5c9f973f49543e90a7c292367b3337c"), OP_EQUAL)
|
||||
|
||||
//https://btc.blockr.io/api/v1/tx/raw/791fe035d312dcf9196b48649a5c9a027198f623c0a5f5bd4cc311b8864dd0cf
|
||||
val rawP2shInputScriptSigHashSingle = "00483045022100dfcfafcea73d83e1c54d444a19fb30d17317f922c19e2ff92dcda65ad09cba24022001e7a805c5672c49b222c5f2f1e67bb01f87215fb69df184e7c16f66c1f87c290347304402204a657ab8358a2edb8fd5ed8a45f846989a43655d2e8f80566b385b8f5a70dab402207362f870ce40f942437d43b6b99343419b14fb18fa69bee801d696a39b3410b8034c695221023927b5cd7facefa7b85d02f73d1e1632b3aaf8dd15d4f9f359e37e39f05611962103d2c0e82979b8aba4591fe39cffbf255b3b9c67b3d24f94de79c5013420c67b802103ec010970aae2e3d75eef0b44eaa31d7a0d13392513cd0614ff1c136b3b1020df53ae"
|
||||
def p2shInputScriptSigHashSingle = RawScriptSignatureParser.read(rawP2shInputScriptSigHashSingle)
|
||||
|
||||
//p2sh input with large amount of signatures
|
||||
//https://tbtc.blockr.io/api/v1/tx/raw/5d254a872c9197c683ea9111fb5c0e2e0f49280a89961c45b9fea76834d335fe
|
||||
val rawP2shInputScriptLargeSignature = "00483045022100a077d4fe9a81411ecb796c254d8b4e0bc73ff86a42288bc3b3ecfa1ef26c00dd02202389bf96cf38c14c3a6ccb8c688339f3fd880b724322862547a8ee3b547a9df90147304402207c0692464998e7f3869f8501cdd25bbcd9d32b6fd34ae8aeae643b422a8dfd42022057eb16f8ca1f34e88babc9f8beb4c2521eb5c4dea41f8902a70d045f1c132a4401473044022024233923253c73569f4b34723a5495698bc124b099c5542a5997d13fba7d18a802203c317bddc070276c6f6c79cb3415413e608af30e4759e31b0d53eab3ca0acd4e014830450221009b9f0d8b945717d2fca3685093d547a3928d122b8894903ed51e2248303213bc022008b376422c9f2cd713b9d10b5b106d1c56c5893dcc01ae300253ed2234bdb63f014730440220257b57cb09386d82c4328461f8fe200c2f381d6b635e2a2f4ea40c8d945e9ec102201ec67d58d51a309af4d8896e9147a42944e9f9833a456f733ea5fa6954ed2fed01" +
|
||||
"4cf155210269992fb441ae56968e5b77d46a3e53b69f136444ae65a94041fc937bdb28d93321021df31471281d4478df85bfce08a10aab82601dca949a79950f8ddf7002bd915a2102174c82021492c2c6dfcbfa4187d10d38bed06afb7fdcd72c880179fddd641ea121033f96e43d72c33327b6a4631ccaa6ea07f0b106c88b9dc71c9000bb6044d5e88a210313d8748790f2a86fb524579b46ce3c68fedd58d2a738716249a9f7d5458a15c221030b632eeb079eb83648886122a04c7bf6d98ab5dfb94cf353ee3e9382a4c2fab02102fb54a7fcaa73c307cfd70f3fa66a2e4247a71858ca731396343ad30c7c4009ce57ae"
|
||||
val p2shInputScriptLargeSignature = RawScriptSignatureParser.read(rawP2shInputScriptLargeSignature)
|
||||
|
||||
//txid on testnet 44e504f5b7649d215be05ad9f09026dee95201244a3b218013c504a6a49a26ff
|
||||
//this tx has multiple inputs and outputs
|
||||
val rawTransaction = "01000000" +
|
||||
|
Loading…
Reference in New Issue
Block a user