Removing inheritance for ScriptPubKeyFactory from ScriptSignature - these are unique enough to not warrant inheritance

This commit is contained in:
Chris Stewart 2016-02-29 15:44:37 -06:00
parent fa31877758
commit 4b9a203786
13 changed files with 105 additions and 35 deletions

View file

@ -1,9 +1,33 @@
package org.scalacoin.crypto
import org.scalacoin.protocol.script.ScriptPubKey
import org.scalacoin.protocol.transaction.{Transaction, TransactionInput}
import org.scalacoin.script.crypto.HashType
/**
* Created by chris on 2/16/16.
*/
trait TransactionSignatureChecker extends BaseSignatureChecker {
trait TransactionSignatureChecker {
/**
* Checks the signature of a scriptSig in the spending transaction against the
* given scriptPubKey
* @param spendingTransaction
* @param inputIndex
* @param scriptPubKey
* @param signature
* @param pubKey
* @param hashType
* @return
*/
def checkSignature(spendingTransaction : Transaction, inputIndex : Int, scriptPubKey : ScriptPubKey,
signature : ECDigitalSignature, pubKey: ECPublicKey, hashType : HashType) : Boolean = {
val hashForSignature = TransactionSignatureSerializer.hashForSignature(spendingTransaction,inputIndex,scriptPubKey,hashType)
val isValid = pubKey.verify(hashForSignature,signature)
isValid
}
}
object TransactionSignatureChecker extends TransactionSignatureChecker

View file

@ -3,7 +3,7 @@ package org.scalacoin.crypto
import org.scalacoin.currency.CurrencyUnits
import org.scalacoin.marshallers.RawBitcoinSerializerHelper
import org.scalacoin.marshallers.transaction.RawTransactionOutputParser
import org.scalacoin.protocol.script.{ScriptSignatureFactory, ScriptSignatureImpl, ScriptPubKeyFactory, ScriptPubKey}
import org.scalacoin.protocol.script._
import org.scalacoin.protocol.transaction._
import org.scalacoin.script.constant.ScriptToken
import org.scalacoin.script.crypto._
@ -168,7 +168,7 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper {
updatedTx
}
/**
* Removes OP_CODESEPARATOR operations then returns the script in hex
* Removes OP_CODESEPARATOR operations then returns the script
* format
* @return
*/

View file

@ -1,5 +1,6 @@
package org.scalacoin.protocol.script
import org.scalacoin.marshallers.transaction.TransactionElement
import org.scalacoin.protocol._
import org.scalacoin.script.bitwise.{OP_EQUAL, OP_EQUALVERIFY}
import org.scalacoin.script.constant.{BytesToPushOntoStackImpl, ScriptConstantImpl, ScriptToken}
@ -9,7 +10,15 @@ import org.scalacoin.script.stack.OP_DUP
/**
* Created by chris on 12/26/15.
*/
trait ScriptPubKey extends ScriptSignature {
sealed trait ScriptPubKey extends TransactionElement {
/**
* Representation of a scriptSignature in a parsed assembly format
* this data structure can be run through the script interpreter to
* see if a script evaluates to true
* @return
*/
def asm : Seq[ScriptToken]
def reqSigs : Option[Int] = {
@ -33,4 +42,5 @@ trait ScriptPubKey extends ScriptSignature {
}
case class ScriptPubKeyImpl(asm : Seq[ScriptToken], hex : String, addresses : Seq[BitcoinAddress]) extends ScriptPubKey

View file

@ -11,7 +11,7 @@ import org.slf4j.LoggerFactory
/**
* Created by chris on 12/26/15.
*/
trait ScriptSignature extends TransactionElement {
sealed trait ScriptSignature extends TransactionElement {
private def logger = LoggerFactory.getLogger(this.getClass())
@ -47,23 +47,15 @@ trait ScriptSignature extends TransactionElement {
}
/**
* Derives the hash type for a given scriptSig
* @param scriptSig
* Derives the hash type for a given digitalSignature
* @param digitalSignature
* @return
*/
def hashType(scriptSig : Seq[Byte]) : HashType = {
require(HashTypeFactory.fromByte(scriptSig.last).isDefined,
"Hash type could not be read for this scriptSig: " + BitcoinSUtil.encodeHex(scriptSig))
HashTypeFactory.fromByte(scriptSig.last).get
def hashType(digitalSignature: ECDigitalSignature) = {
require(HashTypeFactory.fromByte(digitalSignature.bytes.last).isDefined,
"Hash type could not be read for this scriptSig: " + digitalSignature.hex)
HashTypeFactory.fromByte(digitalSignature.bytes.last).get
}
/**
* Derives the hash type for a give scriptSig
* @param scriptSigHex
* @return
*/
def hashType(scriptSigHex : String) : HashType = hashType(BitcoinSUtil.decodeHex(scriptSigHex))
}
case class ScriptSignatureImpl(asm : Seq[ScriptToken], hex : String) extends ScriptSignature

View file

@ -8,7 +8,7 @@ import org.scalacoin.util.{BitcoinSUtil, CryptoUtil}
*/
trait Transaction extends TransactionElement with TransactionFactory {
sealed trait Transaction extends TransactionElement with TransactionFactory {
def txId : String = BitcoinSUtil.encodeHex(CryptoUtil.doubleSHA256(hex).reverse)
def version : Long
def inputs : Seq[TransactionInput]

View file

@ -9,7 +9,7 @@ import org.scalacoin.util.{BitcoinSUtil, ScalacoinUtil}
/**
* Created by chris on 12/26/15.
*/
trait TransactionInput extends TransactionElement with TransactionInputFactory {
sealed trait TransactionInput extends TransactionElement with TransactionInputFactory {
def previousOutput : TransactionOutPoint
def scriptSignature : ScriptSignature

View file

@ -5,7 +5,7 @@ import org.scalacoin.marshallers.transaction.{RawTransactionOutPointParser, Tran
/**
* Created by chris on 12/26/15.
*/
trait TransactionOutPoint extends TransactionElement with TransactionOutPointFactory {
sealed trait TransactionOutPoint extends TransactionElement with TransactionOutPointFactory {
def txId : String
def vout : Int

View file

@ -10,7 +10,7 @@ import org.scalacoin.protocol.script.{ScriptPubKeyFactory, ScriptPubKey}
/**
* Created by chris on 12/26/15.
*/
trait TransactionOutput extends TransactionElement with TransactionOutputFactory {
sealed trait TransactionOutput extends TransactionElement with TransactionOutputFactory {
def value : CurrencyUnit
def n : Int

View file

@ -19,7 +19,7 @@ class ECPublicKeyTest extends FlatSpec with MustMatchers {
isValid must be (true)
}
it must "fail to validate a piece of data if the wrong public key is given" in {
it must "fail to verify a piece of data if the wrong public key is given" in {
val privateKeyHex = "180cb41c7c600be951b5d3d0a7334acc7506173875834f7a6c4c786a28fcbb19"
val key: ECPrivateKey = ECFactory.privateKey(privateKeyHex)
val signature: ECDigitalSignature = key.sign(Sha256Hash.ZERO_HASH.getBytes.toSeq)

View file

@ -0,0 +1,26 @@
package org.scalacoin.crypto
import org.scalacoin.protocol.script.ScriptSignature
import org.scalacoin.protocol.transaction.{TransactionInput, Transaction, TransactionOutput}
import org.scalacoin.script.crypto.SIGHASH_ALL
import org.scalacoin.util.{TransactionTestUtil, TestUtil}
import org.scalatest.{FlatSpec, MustMatchers}
/**
* Created by chris on 2/29/16.
*/
class TransactionSignatureCheckerTest extends FlatSpec with MustMatchers {
"TransactionSignatureChecker" must "check to see if an input correctly spends a scriptPubKey" in {
val (spendingTx,spendingInput,creditingOutput) : (Transaction,TransactionInput,TransactionOutput) =
TransactionTestUtil.transactionWithSpendingInputAndCreditingOutput
val scriptSig : ScriptSignature = spendingInput.scriptSignature
val pubKey : ECPublicKey = ECFactory.publicKey(scriptSig.asm.last.bytes)
require("30450221008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01" == scriptSig.signatures.head.hex)
require("0241d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353" == pubKey.hex)
val hashType = scriptSig.hashType(scriptSig.signatures.head)
require(hashType == SIGHASH_ALL)
TransactionSignatureChecker.checkSignature(spendingTx,0,creditingOutput.scriptPubKey,
scriptSig.signatures.head,pubKey,hashType) must be (true)
}
}

View file

@ -233,14 +233,16 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
private def createBitcoinjMultiSigScriptHashForSig(hashType : HashType) : String = {
val spendTx = bitcoinjMultiSigTransaction
val sighash : String = hashType match {
case SIGHASH_ALL => BitcoinSUtil.encodeHex(spendTx.hashForSignature(0, multiSigScript, SigHash.ALL, false).getBytes)
case SIGHASH_SINGLE => BitcoinSUtil.encodeHex(spendTx.hashForSignature(0,multiSigScript,SigHash.SINGLE, false).getBytes)
case SIGHASH_NONE => BitcoinSUtil.encodeHex(spendTx.hashForSignature(0,multiSigScript,SigHash.NONE, false).getBytes)
case SIGHASH_ANYONECANPAY => BitcoinSUtil.encodeHex(spendTx.hashForSignature(0,multiSigScript.getProgram,0x80.toByte).getBytes)
val sighash : Seq[Byte] = hashType match {
case SIGHASH_ALL => spendTx.hashForSignature(0, multiSigScript, SigHash.ALL, false).getBytes
case SIGHASH_SINGLE => spendTx.hashForSignature(0,multiSigScript,SigHash.SINGLE, false).getBytes
case SIGHASH_NONE => spendTx.hashForSignature(0,multiSigScript,SigHash.NONE, false).getBytes
case SIGHASH_ANYONECANPAY => spendTx.hashForSignature(0,multiSigScript.getProgram,0x80.toByte).getBytes
case SIGHASH_ALL_ANYONECANPAY => spendTx.hashForSignature(0,multiSigScript.getProgram,SIGHASH_ALL_ANYONECANPAY.byte).getBytes
case SIGHASH_SINGLE_ANYONECANPAY => spendTx.hashForSignature(0,multiSigScript.getProgram,SIGHASH_SINGLE_ANYONECANPAY.byte).getBytes
case SIGHASH_NONE_ANYONECANPAY => spendTx.hashForSignature(0,multiSigScript.getProgram,SIGHASH_NONE_ANYONECANPAY.byte).getBytes
}
sighash
BitcoinSUtil.encodeHex(sighash)
}

View file

@ -19,7 +19,7 @@ class ScriptSignatureTest extends FlatSpec with MustMatchers {
}
it must "derive the signature hash type from the signature" in {
TestUtil.scriptSig.hashType(TestUtil.scriptSig.signatures.head.bytes) must be (SIGHASH_ALL)
TestUtil.scriptSig.hashType(TestUtil.scriptSig.signatures.head) must be (SIGHASH_ALL)
}
@ -48,19 +48,19 @@ class ScriptSignatureTest extends FlatSpec with MustMatchers {
))
}
it must "find the hash type for a p2sh script signature" in {
TestUtil.p2shInputScript.hashType(TestUtil.p2shInputScript2Of3.signatures.head.hex) must be (SIGHASH_ALL)
TestUtil.p2shInputScript.hashType(TestUtil.p2shInputScript2Of3.signatures.head) 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(TestUtil.p2shInputScriptSigHashSingle.signatures.head.hex) must be (SIGHASH_SINGLE)
TestUtil.p2shInputScriptSigHashSingle.hashType(TestUtil.p2shInputScriptSigHashSingle.signatures.head) must be (SIGHASH_SINGLE)
}
it must "the hash type for the weird occurrence of hash type being 0 on the blockchain" in {
//from this tx https://btc.blockr.io/api/v1/tx/raw/c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73
val hex = "493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5"
val scriptSig : ScriptSignature = RawScriptSignatureParser.read(hex)
scriptSig.hashType(scriptSig.signatures.head.bytes) must be (SIGHASH_ALL)
scriptSig.hashType(scriptSig.signatures.head) must be (SIGHASH_ALL)
}
}

View file

@ -43,4 +43,20 @@ trait TransactionTestUtil {
TransactionImpl(TransactionConstants.version,Seq(input),Seq(output),TransactionConstants.lockTime)
}
/**
* Returns a transaction and its crediting output
* @return
*/
def transactionWithSpendingInputAndCreditingOutput : (Transaction, TransactionInput, TransactionOutput) = {
val spendingTx = TestUtil.simpleTransaction
val creditingTx = TestUtil.parentSimpleTransaction
val creditingOutput = TestUtil.parentSimpleTransaction.outputs(creditingTx.inputs.head.previousOutput.vout)
//make sure the outpoint index and the outpoint txid are correct
require(spendingTx.inputs.head.previousOutput.txId == creditingTx.txId)
(spendingTx,spendingTx.inputs.head, creditingOutput)
}
}
object TransactionTestUtil extends TransactionTestUtil