Implementing serializeScriptCode inside of TransactionSignatureSerializer

This commit is contained in:
Chris Stewart 2016-02-19 10:43:01 -06:00
parent db473a5473
commit b4b47b6ecf
8 changed files with 156 additions and 36 deletions

View file

@ -0,0 +1,81 @@
package org.scalacoin.crypto
import org.scalacoin.marshallers.RawBitcoinSerializerHelper
import org.scalacoin.protocol.script.{ScriptPubKeyFactory, ScriptPubKey}
import org.scalacoin.protocol.transaction.{Transaction, TransactionOutput, TransactionInput}
import org.scalacoin.script.constant.ScriptToken
import org.scalacoin.script.crypto._
/**
* Created by chris on 2/16/16.
* A trait used to serialize various components of a bitcoin transaction for
* hashing to compare against a digital signature
* https://github.com/bitcoin/bitcoin/blob/93c85d458ac3e2c496c1a053e1f5925f55e29100/src/script/interpreter.cpp#L1016-L1105
*/
trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper {
/**
* Serialized the passed in script code, skipping OP_CODESEPARATORs
* definition for CScript https://github.com/bitcoin/bitcoin/blob/93c85d458ac3e2c496c1a053e1f5925f55e29100/src/script/script.h#L373
* @param script
* @return
*/
def serializeScriptCode(script : Seq[ScriptToken]) : String = {
val serializedScript : String = removeOpCodeSeparators(script)
serializedScript
}
def serializeInput(input : TransactionInput, nType : Int, nVersion : Int) : String = ???
/**
* Serializes an output of a transaction
* https://github.com/bitcoin/bitcoin/blob/93c85d458ac3e2c496c1a053e1f5925f55e29100/src/script/interpreter.cpp#L1079
* @param output
* @param nType
* @param nVersion
* @return
*/
def serializeOutput(output : TransactionOutput, nType : Int, nVersion : Int) : String = ???
def serialize(spendingTransaction : Transaction, nIn : Int, nType : Int, nVersion : Int, nHashTypeIn : HashType) : String = {
val serializedVersion =spendingTransaction.version.toHexString
val serializedNIn = nHashTypeIn match {
case SIGHASH_ANYONECANPAY => "01"
case _ => addPrecedingZero(spendingTransaction.inputs.size.toHexString)
}
val serializedInputs = for {
input <- spendingTransaction.inputs
} yield serializeInput(input,nType,nVersion)
val serializedNOut = nHashTypeIn match {
case SIGHASH_NONE => "00"
case SIGHASH_SINGLE => addPrecedingZero(nIn.toHexString + 1)
case _ => addPrecedingZero(spendingTransaction.outputs.size.toHexString)
}
val serializedOutputs = for {
output <- spendingTransaction.outputs
} yield serializeOutput(output, nType,nVersion)
val serializedLockTime = addPrecedingZero(spendingTransaction.lockTime.toHexString)
serializedVersion + serializedNIn + serializedInputs.mkString + serializedNOut +
serializedOutputs.mkString + serializedLockTime
}
/**
* Removes OP_CODESEPARATOR operations then returns the script in hex
* format
* @return
*/
protected def removeOpCodeSeparators(script : Seq[ScriptToken]) : String = {
val scriptWithoutOpCodeSeparators : String = script.filterNot(_ == OP_CODESEPARATOR).map(_.hex).mkString
val scriptWithoutOpCodeSeparatorSize = addPrecedingZero((scriptWithoutOpCodeSeparators.size / 2).toHexString)
val expectedScript : ScriptPubKey = ScriptPubKeyFactory.factory(
scriptWithoutOpCodeSeparatorSize + scriptWithoutOpCodeSeparators)
expectedScript.hex
}
}
object TransactionSignatureSerializer extends TransactionSignatureSerializer

View file

@ -5,7 +5,7 @@ import org.scalacoin.util.ScalacoinUtil
/**
* Created by chris on 1/11/16.
*/
trait RawBitcoinSerializer[T] {
trait RawBitcoinSerializer[T] extends RawBitcoinSerializerHelper {
/**
* Reads a hexadecimal value and transforms it into the native
@ -36,30 +36,4 @@ trait RawBitcoinSerializer[T] {
*/
def write(t : T) : String
/**
* Adds the amount padding bytes needed to fix the size of the hex string
* for instance, vouts are required to be 4 bytes. If the number is just 1
* it will only take 1 byte. We need to pad the byte with an extra 3 bytes so the result is
* 01000000 instead of just 01
* @param charactersNeeded
* @param hex
* @return
*/
def addPadding(charactersNeeded : Int, hex : String) : String = {
val paddingNeeded = charactersNeeded - hex.size
val padding = for { i <- 0 until paddingNeeded} yield "0"
val paddedHex = hex + padding.mkString
paddedHex
}
/**
* Adds a preceding zero to a hex string.
* Example: if '1' was passed in, it would return the hex string '01'
* @param hex
* @return
*/
def addPrecedingZero(hex : String) = {
if (hex.size == 1) "0" + hex else hex
}
}

View file

@ -0,0 +1,35 @@
package org.scalacoin.marshallers
/**
* Created by chris on 2/18/16.
*/
trait RawBitcoinSerializerHelper {
/**
* Adds the amount padding bytes needed to fix the size of the hex string
* for instance, vouts are required to be 4 bytes. If the number is just 1
* it will only take 1 byte. We need to pad the byte with an extra 3 bytes so the result is
* 01000000 instead of just 01
* @param charactersNeeded
* @param hex
* @return
*/
def addPadding(charactersNeeded : Int, hex : String) : String = {
val paddingNeeded = charactersNeeded - hex.size
val padding = for { i <- 0 until paddingNeeded} yield "0"
val paddedHex = hex + padding.mkString
paddedHex
}
/**
* Adds a preceding zero to a hex string.
* Example: if '1' was passed in, it would return the hex string '01'
* @param hex
* @return
*/
def addPrecedingZero(hex : String) = {
if (hex.size == 1) "0" + hex else hex
}
}
object RawBitcoinSerializerHelper extends RawBitcoinSerializerHelper

View file

@ -1,6 +1,7 @@
package org.scalacoin.protocol.script
import org.scalacoin.marshallers.transaction.TransactionElement
import org.scalacoin.script.constant._
import org.scalacoin.script.crypto.{OP_CHECKMULTISIG, HashType, HashTypeFactory}
import org.scalacoin.util.ScalacoinUtil
@ -22,7 +23,6 @@ trait ScriptSignature extends TransactionElement {
def asm : Seq[ScriptToken]
def hex : String
/**
* The digital signatures contained inside of the script signature
* p2pkh script signatures only have one sig

View file

@ -0,0 +1,32 @@
package org.scalacoin.crypto
import org.scalacoin.protocol.script.{ScriptPubKey, ScriptPubKeyFactory}
import org.scalacoin.script.constant.{OP_2, OP_0, OP_1, ScriptToken}
import org.scalacoin.script.crypto.OP_CODESEPARATOR
import org.scalacoin.util.{ScalacoinUtil, TestUtil}
import org.scalatest.{FlatSpec, MustMatchers}
/**
* Created by chris on 2/19/16.
*/
class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with TransactionSignatureSerializer {
"TransactionSignatureSerializer" must "serialize a given script signature without OP_CODESEPARATORS" in {
val scriptPubKey = TestUtil.scriptPubKey.asm
val expectedScript = removeOpCodeSeparators(scriptPubKey)
serializeScriptCode(scriptPubKey) must be (expectedScript)
}
it must "serialize a given script with only OP_CODESEPARATORs" in {
val script = List(OP_CODESEPARATOR)
serializeScriptCode(script) must be ("00")
}
it must "serialize a given script with mixed in OP_CODESEPARATORs" in {
val script = List(OP_CODESEPARATOR, OP_1, OP_CODESEPARATOR, OP_0, OP_CODESEPARATOR, OP_2)
serializeScriptCode(script) must be ("03510052")
}
}

View file

@ -5,6 +5,7 @@ import org.scalacoin.script.bitwise.OP_EQUALVERIFY
import org.scalacoin.script.constant.{BytesToPushOntoStackImpl, ScriptConstantImpl}
import org.scalacoin.script.crypto.{OP_CHECKSIG, OP_HASH160}
import org.scalacoin.script.stack.OP_DUP
import org.scalacoin.util.TestUtil
import org.scalatest.{FlatSpec, MustMatchers}
/**
@ -13,18 +14,16 @@ import org.scalatest.{FlatSpec, MustMatchers}
class RawScriptPubKeyParserTest extends FlatSpec with MustMatchers with RawScriptPubKeyParser {
//scriptPubKey taken from https://bitcoin.org/en/developer-reference#raw-transaction-format
val rawScriptPubKey = "1976a914cbc20a7664f2f69e5355aa427045bc15e7c6c77288ac"
"RawScriptPubKeyParser" must "parse a hex string into a scriptPubKey" in {
val scriptPubKey : ScriptPubKey = read(rawScriptPubKey)
val scriptPubKey : ScriptPubKey = read(TestUtil.rawScriptPubKey)
scriptPubKey.asm must be (Seq(OP_DUP,OP_HASH160, BytesToPushOntoStackImpl(20),
ScriptConstantImpl("cbc20a7664f2f69e5355aa427045bc15e7c6c772"),OP_EQUALVERIFY,OP_CHECKSIG))
}
it must "read then write the scriptPubKey and get the original scriptPubKey" in {
val scriptPubKey : ScriptPubKey = read(rawScriptPubKey)
write(scriptPubKey) must be (rawScriptPubKey)
val scriptPubKey : ScriptPubKey = read(TestUtil.rawScriptPubKey)
write(scriptPubKey) must be (TestUtil.rawScriptPubKey)
}
}

View file

@ -1,5 +1,6 @@
package org.scalacoin.protocol.script
import org.scalacoin.script.constant.ScriptConstantImpl
import org.scalacoin.script.crypto.{SIGHASH_SINGLE, SIGHASH_ALL}
import org.scalacoin.util.{TestUtil, ScalacoinUtil}
@ -53,7 +54,4 @@ class ScriptSignatureTest extends FlatSpec with MustMatchers {
TestUtil.p2shInputScriptSigHashSingle.hashType(TestUtil.p2shInputScriptSigHashSingle.signatures.head.hex) must be (SIGHASH_SINGLE)
}
}

View file

@ -89,6 +89,7 @@ object TestUtil {
//txid b30d3148927f620f5b1228ba941c211fdabdae75d0ba0b688a58accbf018f3cc
val parentSimpleRawTransaction = "0100000001cda741646fada7272b900719f7ac9d68d633d0e8aa9501eed3c90afbd323bd65010000006a4730440220048e15422cf62349dc586ffb8c749d40280781edd5064ff27a5910ff5cf225a802206a82685dbc2cf195d158c29309939d5a3cd41a889db6f766f3809fff35722305012103dcfc9882c1b3ae4e03fb6cac08bdb39e284e81d70c7aa8b27612457b2774509bffffffff026c405d05000000001976a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac809698000000000017a914af575bd77c5ce7eba3bd9ce6f89774713ae62c798700000000"
def parentSimpleTransaction = RawTransactionParser.read(parentSimpleRawTransaction)
//scriptPubKey taken from https://bitcoin.org/en/developer-reference#raw-transaction-format
val rawScriptPubKey = "1976a914cbc20a7664f2f69e5355aa427045bc15e7c6c77288ac"
def scriptPubKey = RawScriptPubKeyParser.read(rawScriptPubKey)