mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-24 06:57:51 +01:00
Fixing bug in transaction serialization for SIGHASH_SINGLE|SIGHASH_ALL of segwit txs
This commit is contained in:
parent
9a5864c3bf
commit
ab54cdab67
3 changed files with 35 additions and 20 deletions
|
@ -165,32 +165,33 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
|
|||
def serializeForSignature(spendingTx: WitnessTransaction, inputIndex: UInt32, script: Seq[ScriptToken], hashType: HashType,
|
||||
amount: CurrencyUnit): Seq[Byte] = {
|
||||
val isNotAnyoneCanPay = !HashType.isAnyoneCanPay(hashType)
|
||||
val isNotSigHashSingle = !(hashType.isInstanceOf[SIGHASH_SINGLE])
|
||||
val isNotSigHashNone = !(hashType.isInstanceOf[SIGHASH_NONE])
|
||||
val isNotSigHashSingle = !(HashType.isSIGHASH_SINGLE(hashType.num))
|
||||
val isNotSigHashNone = !(HashType.isSIGHASH_NONE(hashType.num))
|
||||
val inputIndexInt = inputIndex.toInt
|
||||
val outPointHash: Option[Seq[Byte]] = if (isNotAnyoneCanPay) {
|
||||
val emptyHash = DoubleSha256Digest("0000000000000000000000000000000000000000000000000000000000000000")
|
||||
|
||||
val outPointHash: Seq[Byte] = if (isNotAnyoneCanPay) {
|
||||
val bytes: Seq[Byte] = spendingTx.inputs.flatMap(_.previousOutput.bytes)
|
||||
Some(CryptoUtil.doubleSHA256(bytes).bytes)
|
||||
} else None
|
||||
CryptoUtil.doubleSHA256(bytes).bytes
|
||||
} else emptyHash.bytes
|
||||
|
||||
logger.debug("outPointHash: " + outPointHash.map(BitcoinSUtil.encodeHex(_)))
|
||||
val sequenceHash: Option[Seq[Byte]] = if (isNotAnyoneCanPay && isNotSigHashNone && isNotSigHashSingle) {
|
||||
val sequenceHash: Seq[Byte] = if (isNotAnyoneCanPay && isNotSigHashNone && isNotSigHashSingle) {
|
||||
val bytes = spendingTx.inputs.flatMap(_.sequence.bytes)
|
||||
Some(CryptoUtil.doubleSHA256(bytes).bytes)
|
||||
} else None
|
||||
CryptoUtil.doubleSHA256(bytes).bytes
|
||||
} else emptyHash.bytes
|
||||
|
||||
logger.debug("sequenceHash: " + sequenceHash.map(BitcoinSUtil.encodeHex(_)))
|
||||
val outputHash: Option[Seq[Byte]] = if (isNotSigHashSingle && isNotSigHashNone) {
|
||||
val outputHash: Seq[Byte] = if (isNotSigHashSingle && isNotSigHashNone) {
|
||||
logger.debug("Not SIGHASH_SINGLE & Not SIGHASH_NONE")
|
||||
//val bytes = spendingTx.outputs.flatMap(_.bytes)
|
||||
val bytes = spendingTx.outputs.flatMap(o => BitcoinSUtil.decodeHex(RawTransactionOutputParser.write(o)))
|
||||
Some(CryptoUtil.doubleSHA256(bytes).bytes)
|
||||
} else if (hashType.isInstanceOf[SIGHASH_SINGLE] && inputIndex < UInt32(spendingTx.outputs.size)) {
|
||||
CryptoUtil.doubleSHA256(bytes).bytes
|
||||
} else if (HashType.isSIGHASH_SINGLE(hashType.num) && inputIndex < UInt32(spendingTx.outputs.size)) {
|
||||
logger.debug("SIGHASH_SINGLE and input index < outputs size")
|
||||
val output = spendingTx.outputs(inputIndexInt)
|
||||
val bytes = CryptoUtil.doubleSHA256(output.bytes).bytes
|
||||
Some(bytes)
|
||||
} else None
|
||||
val bytes = CryptoUtil.doubleSHA256(RawTransactionOutputParser.write(output)).bytes
|
||||
bytes
|
||||
} else emptyHash.bytes
|
||||
|
||||
logger.debug("outputHash: " + outputHash.map(BitcoinSUtil.encodeHex(_)))
|
||||
logger.debug("Script: " + script)
|
||||
|
@ -198,10 +199,10 @@ trait TransactionSignatureSerializer extends RawBitcoinSerializerHelper with Bit
|
|||
//helper function to flip endianness
|
||||
val fe: Seq[Byte] => Seq[Byte] = {bytes: Seq[Byte] => BitcoinSUtil.decodeHex(BitcoinSUtil.flipEndianness(bytes)) }
|
||||
|
||||
val serializationForSig: Seq[Byte] = fe(spendingTx.version.bytes) ++ outPointHash.getOrElse(Nil) ++ sequenceHash.getOrElse(Nil) ++
|
||||
val serializationForSig: Seq[Byte] = fe(spendingTx.version.bytes) ++ outPointHash ++ sequenceHash ++
|
||||
spendingTx.inputs(inputIndexInt).previousOutput.bytes ++ CompactSizeUInt.calculateCompactSizeUInt(scriptBytes).bytes ++
|
||||
scriptBytes ++ fe(amount.bytes) ++ fe(spendingTx.inputs(inputIndexInt).sequence.bytes) ++
|
||||
outputHash.getOrElse(Nil) ++ fe(spendingTx.lockTime.bytes) ++ hashType.num.bytes.reverse
|
||||
outputHash ++ fe(spendingTx.lockTime.bytes) ++ hashType.num.bytes.reverse
|
||||
logger.info("Serialization for signature for WitnessV0Sig: " + BitcoinSUtil.encodeHex(serializationForSig))
|
||||
serializationForSig
|
||||
}
|
||||
|
|
|
@ -12,10 +12,11 @@ import org.bitcoins.core.policy.Policy
|
|||
import org.bitcoins.core.serializers.script.ScriptParser
|
||||
import org.bitcoins.core.protocol.script._
|
||||
import org.bitcoins.core.protocol.transaction._
|
||||
import org.bitcoins.core.script.ScriptOperationFactory
|
||||
import org.bitcoins.core.script.{ScriptOperationFactory, ScriptProgram}
|
||||
import org.bitcoins.core.script.bitwise.OP_EQUALVERIFY
|
||||
import org.bitcoins.core.script.constant._
|
||||
import org.bitcoins.core.script.crypto._
|
||||
import org.bitcoins.core.script.flag.{ScriptVerifyP2SH, ScriptVerifyWitness}
|
||||
import org.bitcoins.core.script.stack.OP_DUP
|
||||
import org.bitcoins.core.util._
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
|
@ -29,8 +30,6 @@ import scala.collection.JavaConversions._
|
|||
class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with BitcoinSLogger {
|
||||
val scriptPubKey = BitcoinjConversions.toScriptPubKey(BitcoinJTestUtil.multiSigScript)
|
||||
|
||||
|
||||
|
||||
"TransactionSignatureSerializer" must "serialize a transaction for SIGHASH_ALL correctly" in {
|
||||
val spendingTx = Transaction(BitcoinJTestUtil.multiSigTransaction.bitcoinSerialize())
|
||||
|
||||
|
@ -465,4 +464,18 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers with
|
|||
BitcoinSUtil.encodeHex(serializedForSig) must be (expected)
|
||||
}
|
||||
|
||||
it must "serialize a p2wsh with SIGHASH_SINGLE|SIGHASH_ANYONECANPAY" in {
|
||||
val rawTx = "0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff05540b0000000000000151d0070000000000000151840300000000000001513c0f00000000000001512c010000000000000151000248304502210092f4777a0f17bf5aeb8ae768dec5f2c14feabf9d1fe2c89c78dfed0f13fdb86902206da90a86042e252bcd1e80a168c719e4a1ddcc3cebea24b9812c5453c79107e9832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71000000000000"
|
||||
val inputIndex = UInt32(1)
|
||||
val wtx = WitnessTransaction(rawTx)
|
||||
val scriptWitness = wtx.witness.witnesses(inputIndex.toInt)
|
||||
val witScriptPubKey = WitnessScriptPubKeyV0("1600144c9c3dfac4207d5d8cb89df5722cb3d712385e3f")
|
||||
val (_,scriptPubKey) = witScriptPubKey.witnessVersion.rebuild(scriptWitness,witScriptPubKey.witnessProgram).left.get
|
||||
|
||||
val amount = Satoshis(Int64(2000))
|
||||
val serializedForSig = TransactionSignatureSerializer.serializeForSignature(wtx,inputIndex,scriptPubKey.asm,HashType.sigHashSingleAnyoneCanPay, amount)
|
||||
|
||||
BitcoinSUtil.encodeHex(serializedForSig) must be ("01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88acd007000000000000ffffffff2d793f9722ac8cbea9b2e0a2929cda4007b8312c6ec3b997088439e48e7aa64e0000000083000000")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,4 +29,5 @@ class ScriptProgramTest extends FlatSpec with MustMatchers {
|
|||
val program3 = ScriptProgram(program, List(ScriptNumber.negativeZero), ScriptProgram.Stack)
|
||||
program3.stackTopIsTrue must be (false)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue