Adding functionality to parse a VarInt from a scriptSig, adding TransactionInputFactory

This commit is contained in:
Chris Stewart 2016-02-19 15:55:14 -06:00
parent 50e6aa0d9d
commit bf813287d4
6 changed files with 64 additions and 35 deletions

View file

@ -2,7 +2,7 @@ package org.scalacoin.crypto
import org.scalacoin.marshallers.RawBitcoinSerializerHelper
import org.scalacoin.marshallers.transaction.RawTransactionOutputParser
import org.scalacoin.protocol.script.{ScriptPubKeyFactory, ScriptPubKey}
import org.scalacoin.protocol.script.{ScriptSignatureFactory, ScriptSignatureImpl, ScriptPubKeyFactory, ScriptPubKey}
import org.scalacoin.protocol.transaction.{Transaction, TransactionOutput, TransactionInput}
import org.scalacoin.script.constant.ScriptToken
import org.scalacoin.script.crypto._
@ -55,30 +55,12 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper {
}
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 {
def serialize(inputIndex : Int, nType : Int, script : ScriptPubKey, hashType : HashType) : String = {
//remove signatures from all inputs because we cannot sign existing signatures
val txWithInputSigsRemoved = 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,index) <- spendingTransaction.outputs.zipWithIndex
} yield serializeOutput(output, nType,nVersion,SIGHASH_NONE,-1,-1)
val serializedLockTime = addPrecedingZero(spendingTransaction.lockTime.toHexString)
serializedVersion + serializedNIn + serializedInputs.mkString + serializedNOut +
serializedOutputs.mkString + serializedLockTime
} yield input.factory(ScriptSignatureFactory.empty)
???
}
/**
@ -86,7 +68,7 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper {
* format
* @return
*/
protected def removeOpCodeSeparators(script : Seq[ScriptToken]) : String = {
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(
@ -96,4 +78,4 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper {
}
class TransactionSignatureSerializerr(override val spendingTransaction : Transaction) extends TransactionSignatureSerializer
class BaseTransactionSignatureSerializer(override val spendingTransaction : Transaction) extends TransactionSignatureSerializer

View file

@ -33,7 +33,7 @@ trait RawTransactionInputParser extends RawBitcoinSerializer[Seq[TransactionInpu
logger.debug("VarInt hex: " + ScalacoinUtil.encodeHex(bytes.slice(outPointBytesSize,outPointBytesSize + scriptVarIntSize)))
val scriptSigVarInt : VarInt = ScalacoinUtil.parseVarInt(bytes.slice(outPointBytesSize,outPointBytesSize + scriptVarIntSize))
val scriptSigBytes = bytes.slice(outPointBytesSize+ scriptVarIntSize,
val scriptSigBytes = bytes.slice(outPointBytesSize + scriptVarIntSize,
outPointBytesSize + scriptVarIntSize + scriptSigVarInt.num.toInt)
val scriptSig : ScriptSignature = RawScriptSignatureParser.read(scriptSigBytes)

View file

@ -7,7 +7,8 @@ import org.scalacoin.protocol.script.ScriptSignature
/**
* Created by chris on 12/26/15.
*/
trait TransactionInput extends TransactionElement {
trait TransactionInput extends TransactionElement with TransactionInputFactory {
def previousOutput : TransactionOutPoint
def scriptSignature : ScriptSignature
def sequence : Long

View file

@ -1,5 +1,6 @@
package org.scalacoin.util
import org.scalacoin.protocol.script.ScriptSignature
import org.scalacoin.protocol.{VarIntImpl, VarInt}
import org.slf4j.LoggerFactory
@ -96,8 +97,20 @@ trait NumberUtil {
def toByteList(long : Long) = BigInt(long).toByteArray.toList
/**
* Parses a VarInt from a string of hex characters
* https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer
* @param hex
* @return
*/
def parseVarInt(hex : String) : VarInt = parseVarInt(ScalacoinUtil.decodeHex(hex))
/**
* Parses a VarInt from a sequence of bytes
* https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer
* @param bytes
* @return
*/
def parseVarInt(bytes : Seq[Byte]) : VarInt = {
require(bytes.size > 0, "Cannot parse a VarInt if the byte array is size 0")
//8 bit number
@ -112,6 +125,7 @@ trait NumberUtil {
/**
* Returns the size of a VarInt in the number of bytes
* https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer
* @param byte
* @return
*/
@ -126,6 +140,23 @@ trait NumberUtil {
else 9
}
/**
* Parses a VarInt from a sequence of bytes
* https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer
* @param bytes
* @return
*/
def parseVarInt(scriptSig : ScriptSignature) : VarInt = {
//largest 8 uint
val size = scriptSig.size
if (size < 255) VarIntImpl(size,1)
else if (size < 65536) VarIntImpl(size,3)
else if (size < 4294967296L) VarIntImpl(size,5)
else VarIntImpl(size,9)
}
private def parseLong(hex : String) : Long = java.lang.Long.parseLong(hex,16)
private def parseLong(bytes : List[Byte]) : Long = parseLong(ScalacoinUtil.encodeHex(bytes))

View file

@ -12,21 +12,24 @@ import org.scalatest.{FlatSpec, MustMatchers}
class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
/* "TransactionSignatureSerializer" must "serialize a given script signature without OP_CODESEPARATORS" in {
"TransactionSignatureSerializer" must "serialize a given script signature without OP_CODESEPARATORS" in {
val txSerializer = new BaseTransactionSignatureSerializer(TestUtil.transaction)
val scriptPubKey = TestUtil.scriptPubKey.asm
val expectedScript = removeOpCodeSeparators(scriptPubKey)
serializeScriptCode(scriptPubKey) must be (expectedScript)
val expectedScript = txSerializer.removeOpCodeSeparators(scriptPubKey)
txSerializer.serializeScriptCode(scriptPubKey) must be (expectedScript)
}
it must "serialize a given script with only OP_CODESEPARATORs" in {
val txSerializer = new BaseTransactionSignatureSerializer(TestUtil.transaction)
val script = List(OP_CODESEPARATOR)
serializeScriptCode(script) must be ("00")
txSerializer.serializeScriptCode(script) must be ("00")
}
it must "serialize a given script with mixed in OP_CODESEPARATORs" in {
val txSerializer = new BaseTransactionSignatureSerializer(TestUtil.transaction)
val script = List(OP_CODESEPARATOR, OP_1, OP_CODESEPARATOR, OP_0, OP_CODESEPARATOR, OP_2)
serializeScriptCode(script) must be ("03510052")
}*/
txSerializer.serializeScriptCode(script) must be ("03510052")
}
}

View file

@ -1,7 +1,7 @@
package org.scalacoin.util
import org.scalacoin.marshallers.script.{RawScriptSignatureParser, RawScriptPubKeyParser}
import org.scalacoin.marshallers.transaction.RawTransactionParser
import org.scalacoin.marshallers.transaction.{RawTransactionInputParser, RawTransactionParser}
import org.scalacoin.protocol.{AssetAddress, BitcoinAddress}
import org.scalacoin.script.ScriptProgramImpl
import org.scalacoin.script.bitwise.{OP_EQUAL, OP_EQUALVERIFY}
@ -79,6 +79,18 @@ object TestUtil {
"02c02b00000000000017a914b0b06365c482eb4eabe6e0630029fb8328ea098487e81c0000000000001976a914938da2b50fd6d8acdfa20e30df0e7d8092f0bc7588ac00000000"
def transaction = RawTransactionParser.read(rawTransaction)
//txid cad1082e674a7bd3bc9ab1bc7804ba8a57523607c876b8eb2cbe645f2b1803d6
val rawTxInput = "01" +
"85d6b0da2edf96b282030d3f4f79d14cc8c882cfef1b3064170c850660317de100000000" +
"6f0047304402207df6dd8dad22d49c3c83d8031733c32a53719278eb7985d3b35b375d776f84f102207054f9209a1e87d55feafc90aa04c33008e5bae9191da22aeaa16efde96f41f00125512102b022902a0fdd71e831c37e4136c2754a59887be0618fb75336d7ab67e2982ff551ae" +
"ffffffff"
def txInput = RawTransactionInputParser.read(rawTxInput)
//from txid 44e504f5b7649d215be05ad9f09026dee95201244a3b218013c504a6a49a26ff
val rawTxInputs = "02df80e3e6eba7dcd4650281d3c13f140dafbb823a7227a78eb6ee9f6cedd040011b0000006a473044022040f91c48f4011bf2e2edb6621bfa8fb802241de939cb86f1872c99c580ef0fe402204fc27388bc525e1b655b5f5b35f9d601d28602432dd5672f29e0a47f5b8bbb26012102c114f376c98d12a0540c3a81ab99bb1c5234245c05e8239d09f48229f9ebf011ffffffff" +
"df80e3e6eba7dcd4650281d3c13f140dafbb823a7227a78eb6ee9f6cedd04001340000006b483045022100cf317c320d078c5b884c44e7488825dab5bcdf3f88c66314ac925770cd8773a7022033fde60d33cc2842ea73fce5d9cf4f8da6fadf414a75b7085efdcd300407f438012102605c23537b27b80157c770cd23e066cd11db3800d3066a38b9b592fc08ae9c70ffffffff"
def txInputs = RawTransactionInputParser.read(rawTxInputs)
//simple raw transaction with only one input and two outputs
//txid 92efdd5abb43efd4fe4f89bd080bcddd287a630e8cb6920388dd7880acf4c964
val simpleRawTransaction = "0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e690c865c2f6f7a9710a474154ab1423abb5b9288ac00000000"