mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-01-18 13:24:25 +01:00
Adding bitcoinj util to serialize a transaction for a signature hash - makes for easier testing of the different HashType's
This commit is contained in:
parent
a859d8342d
commit
7a94cb3976
@ -63,7 +63,7 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper {
|
||||
|
||||
}
|
||||
|
||||
def serialize(inputIndex : Int, script : ScriptPubKey, hashType : HashType) : Seq[Byte] = {
|
||||
def serializeForSignature(inputIndex : Int, script : ScriptPubKey, hashType : HashType) : Seq[Byte] = {
|
||||
// Clear input scripts in preparation for signing. If we're signing a fresh
|
||||
// transaction that step isn't very helpful, but it doesn't add much cost relative to the actual
|
||||
// EC math so we'll do it anyway.
|
||||
@ -98,9 +98,12 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper {
|
||||
else input
|
||||
}
|
||||
|
||||
|
||||
val txWithInputSigsRemoved = spendingTransaction.factory(UpdateTransactionInputs(updatedInputs))
|
||||
|
||||
//just need to add the hash type and hash the tx
|
||||
//txWithInputSigsRemoved.bytes
|
||||
val sigHashBytes : List[Byte] = List(0x00.toByte, 0x00.toByte, 0x00.toByte, hashType.byte).reverse
|
||||
|
||||
//check the hash type
|
||||
hashType match {
|
||||
case SIGHASH_NONE =>
|
||||
@ -112,7 +115,7 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper {
|
||||
val updatedInputs : Seq[TransactionInput] = setSequenceNumbersZero(spendingTransaction.inputs,inputIndex)
|
||||
val sigHashNoneTx = txWithNoOutputs.factory(UpdateTransactionInputs(updatedInputs))
|
||||
//append hash type byte onto the end of the tx bytes
|
||||
CryptoUtil.doubleSHA256(sigHashNoneTx.bytes ++ List(hashType.byte))
|
||||
sigHashNoneTx.bytes ++ sigHashBytes
|
||||
case SIGHASH_SINGLE =>
|
||||
//following this implementation from bitcoinj
|
||||
//https://github.com/bitcoinj/bitcoinj/blob/09a2ca64d2134b0dcbb27b1a6eb17dda6087f448/core/src/main/java/org/bitcoinj/core/Transaction.java#L964
|
||||
@ -130,7 +133,14 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper {
|
||||
} else {
|
||||
// In SIGHASH_SINGLE the outputs after the matching input index are deleted, and the outputs before
|
||||
// that position are "nulled out". Unintuitively, the value in a "null" transaction is set to -1.
|
||||
val updatedOutputs = txWithInputSigsRemoved.outputs.map(o => o.empty.factory(CurrencyUnits.negativeSatoshi))
|
||||
val updatedOutputsOpt : Seq[Option[TransactionOutput]] = for {
|
||||
(output,index) <- txWithInputSigsRemoved.outputs.zipWithIndex
|
||||
} yield {
|
||||
if (index < inputIndex) Some(output.empty.factory(CurrencyUnits.negativeSatoshi))
|
||||
else None
|
||||
}
|
||||
val updatedOutputs : Seq[TransactionOutput] = updatedOutputsOpt.flatten
|
||||
|
||||
val spendingTxOutputsEmptied = txWithInputSigsRemoved.factory(UpdateTransactionOutputs(updatedOutputs))
|
||||
//create blank inputs with sequence numbers set to zero EXCEPT
|
||||
//the input at the inputIndex
|
||||
@ -138,17 +148,20 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper {
|
||||
|
||||
val sigHashSingleTx = spendingTxOutputsEmptied.factory(UpdateTransactionInputs(updatedInputs))
|
||||
//append hash type byte onto the end of the tx bytes
|
||||
CryptoUtil.doubleSHA256(sigHashSingleTx.bytes ++ List(hashType.byte))
|
||||
sigHashSingleTx.bytes ++ sigHashBytes
|
||||
}
|
||||
case SIGHASH_ALL =>
|
||||
//just need to add the hash type and hash the tx
|
||||
//txWithInputSigsRemoved.bytes
|
||||
val sigHashBytes : List[Byte] = List(0x00.toByte, 0x00.toByte, 0x00.toByte, hashType.byte).reverse
|
||||
CryptoUtil.doubleSHA256(txWithInputSigsRemoved.bytes ++ sigHashBytes)
|
||||
txWithInputSigsRemoved.bytes ++ sigHashBytes
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
def hashForSignature(inputIndex : Int, script : ScriptPubKey, hashType : HashType) : Seq[Byte] = {
|
||||
val serializedTxForSignature = serializeForSignature(inputIndex,script,hashType)
|
||||
CryptoUtil.doubleSHA256(serializedTxForSignature)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes OP_CODESEPARATOR operations then returns the script in hex
|
||||
* format
|
||||
|
@ -11,9 +11,9 @@ import org.scalacoin.protocol.transaction._
|
||||
import org.scalacoin.script.ScriptOperationFactory
|
||||
import org.scalacoin.script.bitwise.OP_EQUALVERIFY
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.script.crypto.{OP_CHECKSIG, OP_HASH160, SIGHASH_ALL, OP_CODESEPARATOR}
|
||||
import org.scalacoin.script.crypto._
|
||||
import org.scalacoin.script.stack.OP_DUP
|
||||
import org.scalacoin.util.{BitcoinjConversions, ScalacoinUtil, TestUtil}
|
||||
import org.scalacoin.util._
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
@ -22,8 +22,12 @@ import scala.collection.JavaConversions._
|
||||
* Created by chris on 2/19/16.
|
||||
*/
|
||||
class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
|
||||
|
||||
|
||||
val params = TestNet3Params.get();
|
||||
val key1 = new DumpedPrivateKey(params, "cVLwRLTvz3BxDAWkvS3yzT9pUcTCup7kQnfT2smRjvmmm1wAP6QT").getKey();
|
||||
val key2 = new DumpedPrivateKey(params, "cTine92s8GLpVqvebi8rYce3FrUYq78ZGQffBYCS1HmDPJdSTxUo").getKey();
|
||||
val key3 = new DumpedPrivateKey(params, "cVHwXSPRZmL9adctwBwmn4oTZdZMbaCsR5XF6VznqMgcvt1FDDxg").getKey();
|
||||
val multiSigScript : org.bitcoinj.script.Script = ScriptBuilder.createMultiSigOutputScript(2, util.Arrays.asList(key1, key2, key3));
|
||||
val scriptPubKey = BitcoinjConversions.toScriptPubKey(multiSigScript)
|
||||
"TransactionSignatureSerializer" must "serialize a given script signature without OP_CODESEPARATORS" in {
|
||||
val txSerializer = new BaseTransactionSignatureSerializer(TestUtil.transaction)
|
||||
val scriptPubKey = TestUtil.scriptPubKey
|
||||
@ -31,14 +35,35 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
|
||||
txSerializer.serializeScriptCode(scriptPubKey) must be (expectedScript)
|
||||
}
|
||||
|
||||
it must "serialize a transaction for SIGHASH_ALL correctly" in {
|
||||
require(scriptPubKey.hex == ScalacoinUtil.encodeHex(multiSigScript.getProgram), "Script pub key hex not the same as multiSigScript hex")
|
||||
val spendingTx = Transaction.factory(bitcoinjMultiSigTransaction.bitcoinSerialize())
|
||||
|
||||
it must "hash a multisignature SIGHASH_ALL correctly" in {
|
||||
spendingTx.hex must be (ScalacoinUtil.encodeHex(bitcoinjMultiSigTransaction.bitcoinSerialize()))
|
||||
|
||||
val txSignatureSerializer = new BaseTransactionSignatureSerializer(spendingTx)
|
||||
val sigBytes : Seq[Byte] = txSignatureSerializer.serializeForSignature(0,scriptPubKey,SIGHASH_ALL)
|
||||
val bitcoinjSerialization = ScalacoinUtil.encodeHex(
|
||||
BitcoinJSignatureSerialization.serializeForSignature(bitcoinjMultiSigTransaction,0,multiSigScript.getProgram(),SIGHASH_ALL.byte)
|
||||
)
|
||||
ScalacoinUtil.encodeHex(sigBytes) must be (bitcoinjSerialization)
|
||||
}
|
||||
|
||||
val params = TestNet3Params.get();
|
||||
val key1 = new DumpedPrivateKey(params, "cVLwRLTvz3BxDAWkvS3yzT9pUcTCup7kQnfT2smRjvmmm1wAP6QT").getKey();
|
||||
val key2 = new DumpedPrivateKey(params, "cTine92s8GLpVqvebi8rYce3FrUYq78ZGQffBYCS1HmDPJdSTxUo").getKey();
|
||||
val key3 = new DumpedPrivateKey(params, "cVHwXSPRZmL9adctwBwmn4oTZdZMbaCsR5XF6VznqMgcvt1FDDxg").getKey();
|
||||
it must "hash a tranasction with SIGHASH_ALL correctly" in {
|
||||
require(scriptPubKey.hex == ScalacoinUtil.encodeHex(multiSigScript.getProgram), "Script pub key hex not the same as multiSigScript hex")
|
||||
val spendingTx = Transaction.factory(bitcoinjMultiSigTransaction.bitcoinSerialize())
|
||||
|
||||
spendingTx.hex must be (ScalacoinUtil.encodeHex(bitcoinjMultiSigTransaction.bitcoinSerialize()))
|
||||
|
||||
val txSignatureSerializer = new BaseTransactionSignatureSerializer(spendingTx)
|
||||
val bitcoinsTxSigHash : Seq[Byte] = txSignatureSerializer.hashForSignature(0,scriptPubKey,SIGHASH_ALL)
|
||||
val bitcoinjTxSigHash = ScalacoinUtil.encodeHex(
|
||||
BitcoinJSignatureSerialization.hashForSignature(bitcoinjMultiSigTransaction,0,multiSigScript.getProgram(),SIGHASH_ALL.byte)
|
||||
)
|
||||
ScalacoinUtil.encodeHex(bitcoinsTxSigHash) must be (bitcoinjTxSigHash)
|
||||
}
|
||||
/*
|
||||
it must "hash a multisignature SIGHASH_SINGLE correctly with one output and one input" in {
|
||||
val multiSigScript : org.bitcoinj.script.Script = ScriptBuilder.createMultiSigOutputScript(2, util.Arrays.asList(key1, key2, key3));
|
||||
val scriptPubKey = BitcoinjConversions.toScriptPubKey(multiSigScript)
|
||||
require(scriptPubKey.hex == ScalacoinUtil.encodeHex(multiSigScript.getProgram), "Script pub key hex not the same as multiSigScript hex")
|
||||
@ -47,9 +72,15 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
|
||||
spendingTx.hex must be (ScalacoinUtil.encodeHex(bitcoinjMultiSigTransaction.bitcoinSerialize()))
|
||||
|
||||
val txSignatureSerializer = new BaseTransactionSignatureSerializer(spendingTx)
|
||||
val sigBytes : Seq[Byte] = txSignatureSerializer.serialize(0,scriptPubKey,SIGHASH_ALL)
|
||||
ScalacoinUtil.encodeHex(sigBytes) must be (createBitcoinjMultiSigScriptHashForSig)
|
||||
}
|
||||
val serialiazedTxForSig : Seq[Byte] = txSignatureSerializer.serializeForSignature(0,scriptPubKey,SIGHASH_SINGLE)
|
||||
|
||||
val bitcoinjSigSerialization = ScalacoinUtil.encodeHex(BitcoinJSignatureSerialization.serializeForSignature(
|
||||
bitcoinjMultiSigTransaction,0,multiSigScript.getProgram,SIGHASH_SINGLE.byte))
|
||||
|
||||
ScalacoinUtil.encodeHex(serialiazedTxForSig) must be (bitcoinjSigSerialization)
|
||||
|
||||
|
||||
}*/
|
||||
|
||||
|
||||
/**
|
||||
@ -58,15 +89,24 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers {
|
||||
* hashes a bitcoinj tx for a signature
|
||||
* @return
|
||||
*/
|
||||
private def createBitcoinjMultiSigScriptHashForSig : String = {
|
||||
val params = TestNet3Params.get()
|
||||
val key1 = new DumpedPrivateKey(params, "cVLwRLTvz3BxDAWkvS3yzT9pUcTCup7kQnfT2smRjvmmm1wAP6QT").getKey()
|
||||
val key2 = new DumpedPrivateKey(params, "cTine92s8GLpVqvebi8rYce3FrUYq78ZGQffBYCS1HmDPJdSTxUo").getKey()
|
||||
val key3 = new DumpedPrivateKey(params, "cVHwXSPRZmL9adctwBwmn4oTZdZMbaCsR5XF6VznqMgcvt1FDDxg").getKey()
|
||||
private def createBitcoinjMultiSigScriptHashForSig(hashType : HashType) : String = {
|
||||
val spendTx = bitcoinjMultiSigTransaction
|
||||
val multisigScript = ScriptBuilder.createMultiSigOutputScript(2, util.Arrays.asList(key1, key2, key3))
|
||||
val sighash = spendTx.hashForSignature(0, multisigScript, SigHash.ALL, false)
|
||||
ScalacoinUtil.encodeHex(sighash.getBytes)
|
||||
|
||||
val sighash : String = hashType match {
|
||||
case SIGHASH_ALL => ScalacoinUtil.encodeHex(spendTx.hashForSignature(0, multiSigScript, SigHash.ALL, false).getBytes)
|
||||
case SIGHASH_SINGLE => ScalacoinUtil.encodeHex(spendTx.hashForSignature(0,multiSigScript,SigHash.SINGLE, false).getBytes)
|
||||
case SIGHASH_NONE => ScalacoinUtil.encodeHex(spendTx.hashForSignature(0,multiSigScript,SigHash.NONE, false).getBytes)
|
||||
}
|
||||
/* val bitcoinjReplicaSerialization : String = BitcoinjConversions.signatureSerialization(spendTx,0,multiSigScript.getProgram,hashType.byte)
|
||||
val bitcoinjReplicaHash : String = ScalacoinUtil.encodeHex(CryptoUtil.doubleSHA256(bitcoinjReplicaSerialization))
|
||||
|
||||
println("Bitcoinj Replica Serialization: " + bitcoinjReplicaSerialization)
|
||||
require(bitcoinjReplicaHash == sighash, "Bitcoinj replica hash and actual sighash from bitcoinj were different\n" +
|
||||
"Actual bitcoinj hash: " + sighash + "\n" +
|
||||
"Replica bitcoinj hash: " + bitcoinjReplicaHash)*/
|
||||
sighash
|
||||
|
||||
//BitcoinjConversions.signatureSerialization(spendTx,0,multisigScript.getProgram,hashType.byte)
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user