mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-03 18:47:38 +01:00
Implementing serializeScriptCode inside of TransactionSignatureSerializer
This commit is contained in:
parent
db473a5473
commit
b4b47b6ecf
8 changed files with 156 additions and 36 deletions
|
@ -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
|
|
@ -5,7 +5,7 @@ import org.scalacoin.util.ScalacoinUtil
|
||||||
/**
|
/**
|
||||||
* Created by chris on 1/11/16.
|
* 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
|
* Reads a hexadecimal value and transforms it into the native
|
||||||
|
@ -36,30 +36,4 @@ trait RawBitcoinSerializer[T] {
|
||||||
*/
|
*/
|
||||||
def write(t : T) : String
|
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -1,6 +1,7 @@
|
||||||
package org.scalacoin.protocol.script
|
package org.scalacoin.protocol.script
|
||||||
|
|
||||||
import org.scalacoin.marshallers.transaction.TransactionElement
|
import org.scalacoin.marshallers.transaction.TransactionElement
|
||||||
|
|
||||||
import org.scalacoin.script.constant._
|
import org.scalacoin.script.constant._
|
||||||
import org.scalacoin.script.crypto.{OP_CHECKMULTISIG, HashType, HashTypeFactory}
|
import org.scalacoin.script.crypto.{OP_CHECKMULTISIG, HashType, HashTypeFactory}
|
||||||
import org.scalacoin.util.ScalacoinUtil
|
import org.scalacoin.util.ScalacoinUtil
|
||||||
|
@ -22,7 +23,6 @@ trait ScriptSignature extends TransactionElement {
|
||||||
def asm : Seq[ScriptToken]
|
def asm : Seq[ScriptToken]
|
||||||
|
|
||||||
def hex : String
|
def hex : String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The digital signatures contained inside of the script signature
|
* The digital signatures contained inside of the script signature
|
||||||
* p2pkh script signatures only have one sig
|
* p2pkh script signatures only have one sig
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import org.scalacoin.script.bitwise.OP_EQUALVERIFY
|
||||||
import org.scalacoin.script.constant.{BytesToPushOntoStackImpl, ScriptConstantImpl}
|
import org.scalacoin.script.constant.{BytesToPushOntoStackImpl, ScriptConstantImpl}
|
||||||
import org.scalacoin.script.crypto.{OP_CHECKSIG, OP_HASH160}
|
import org.scalacoin.script.crypto.{OP_CHECKSIG, OP_HASH160}
|
||||||
import org.scalacoin.script.stack.OP_DUP
|
import org.scalacoin.script.stack.OP_DUP
|
||||||
|
import org.scalacoin.util.TestUtil
|
||||||
import org.scalatest.{FlatSpec, MustMatchers}
|
import org.scalatest.{FlatSpec, MustMatchers}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,18 +14,16 @@ import org.scalatest.{FlatSpec, MustMatchers}
|
||||||
class RawScriptPubKeyParserTest extends FlatSpec with MustMatchers with RawScriptPubKeyParser {
|
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 {
|
"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),
|
scriptPubKey.asm must be (Seq(OP_DUP,OP_HASH160, BytesToPushOntoStackImpl(20),
|
||||||
ScriptConstantImpl("cbc20a7664f2f69e5355aa427045bc15e7c6c772"),OP_EQUALVERIFY,OP_CHECKSIG))
|
ScriptConstantImpl("cbc20a7664f2f69e5355aa427045bc15e7c6c772"),OP_EQUALVERIFY,OP_CHECKSIG))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it must "read then write the scriptPubKey and get the original scriptPubKey" in {
|
it must "read then write the scriptPubKey and get the original scriptPubKey" in {
|
||||||
val scriptPubKey : ScriptPubKey = read(rawScriptPubKey)
|
val scriptPubKey : ScriptPubKey = read(TestUtil.rawScriptPubKey)
|
||||||
write(scriptPubKey) must be (rawScriptPubKey)
|
write(scriptPubKey) must be (TestUtil.rawScriptPubKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.scalacoin.protocol.script
|
package org.scalacoin.protocol.script
|
||||||
|
|
||||||
|
|
||||||
import org.scalacoin.script.constant.ScriptConstantImpl
|
import org.scalacoin.script.constant.ScriptConstantImpl
|
||||||
import org.scalacoin.script.crypto.{SIGHASH_SINGLE, SIGHASH_ALL}
|
import org.scalacoin.script.crypto.{SIGHASH_SINGLE, SIGHASH_ALL}
|
||||||
import org.scalacoin.util.{TestUtil, ScalacoinUtil}
|
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)
|
TestUtil.p2shInputScriptSigHashSingle.hashType(TestUtil.p2shInputScriptSigHashSingle.signatures.head.hex) must be (SIGHASH_SINGLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,7 @@ object TestUtil {
|
||||||
//txid b30d3148927f620f5b1228ba941c211fdabdae75d0ba0b688a58accbf018f3cc
|
//txid b30d3148927f620f5b1228ba941c211fdabdae75d0ba0b688a58accbf018f3cc
|
||||||
val parentSimpleRawTransaction = "0100000001cda741646fada7272b900719f7ac9d68d633d0e8aa9501eed3c90afbd323bd65010000006a4730440220048e15422cf62349dc586ffb8c749d40280781edd5064ff27a5910ff5cf225a802206a82685dbc2cf195d158c29309939d5a3cd41a889db6f766f3809fff35722305012103dcfc9882c1b3ae4e03fb6cac08bdb39e284e81d70c7aa8b27612457b2774509bffffffff026c405d05000000001976a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac809698000000000017a914af575bd77c5ce7eba3bd9ce6f89774713ae62c798700000000"
|
val parentSimpleRawTransaction = "0100000001cda741646fada7272b900719f7ac9d68d633d0e8aa9501eed3c90afbd323bd65010000006a4730440220048e15422cf62349dc586ffb8c749d40280781edd5064ff27a5910ff5cf225a802206a82685dbc2cf195d158c29309939d5a3cd41a889db6f766f3809fff35722305012103dcfc9882c1b3ae4e03fb6cac08bdb39e284e81d70c7aa8b27612457b2774509bffffffff026c405d05000000001976a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac809698000000000017a914af575bd77c5ce7eba3bd9ce6f89774713ae62c798700000000"
|
||||||
def parentSimpleTransaction = RawTransactionParser.read(parentSimpleRawTransaction)
|
def parentSimpleTransaction = RawTransactionParser.read(parentSimpleRawTransaction)
|
||||||
|
|
||||||
//scriptPubKey taken from https://bitcoin.org/en/developer-reference#raw-transaction-format
|
//scriptPubKey taken from https://bitcoin.org/en/developer-reference#raw-transaction-format
|
||||||
val rawScriptPubKey = "1976a914cbc20a7664f2f69e5355aa427045bc15e7c6c77288ac"
|
val rawScriptPubKey = "1976a914cbc20a7664f2f69e5355aa427045bc15e7c6c77288ac"
|
||||||
def scriptPubKey = RawScriptPubKeyParser.read(rawScriptPubKey)
|
def scriptPubKey = RawScriptPubKeyParser.read(rawScriptPubKey)
|
||||||
|
|
Loading…
Add table
Reference in a new issue