mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-01-19 05:43:51 +01:00
Merge pull request #170 from Christewart/sighash_byte_bug_fix
refactor hash type to derive the byte from the underlying int32, refa…
This commit is contained in:
commit
b94af42dfa
@ -0,0 +1,24 @@
|
||||
package org.bitcoins.core.script.crypto
|
||||
|
||||
import org.bitcoins.core.gen.NumberGenerator
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.scalacheck.{ Prop, Properties }
|
||||
|
||||
class HashTypeSpec extends Properties("HashTypeSpec") {
|
||||
private val logger = BitcoinSLogger.logger
|
||||
property("serialization symmetry") = {
|
||||
Prop.forAll(NumberGenerator.int32s) { i32 =>
|
||||
val hashType = HashType.fromBytes(i32.bytes)
|
||||
|
||||
hashType.num == i32 &&
|
||||
i32.bytes.last == hashType.byte &&
|
||||
//this check cannot check the other 3 bytes in
|
||||
//hash type as they are discarded from inclusion
|
||||
//on a bitcoin digital signature. Not sure why satoshi
|
||||
//would have just used a uint8_t to represent a hash type
|
||||
//instead of a uint32_t.
|
||||
HashType.fromByte(hashType.byte).byte == hashType.byte
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -42,12 +42,12 @@ class HashTypeTest extends FlatSpec with MustMatchers {
|
||||
}
|
||||
|
||||
it must "determine if a given number is of hashType SIGHASH_ALL" in {
|
||||
HashType.isSIGHASH_ALL(Int32.zero) must be(true)
|
||||
HashType.isSIGHASH_ALL(Int32.one) must be(true)
|
||||
HashType.isSIGHASH_ALL(Int32(5)) must be(true)
|
||||
HashType.isSigHashAll(Int32.zero) must be(true)
|
||||
HashType.isSigHashAll(Int32.one) must be(true)
|
||||
HashType.isSigHashAll(Int32(5)) must be(true)
|
||||
|
||||
HashType.isSIGHASH_ALL(HashType.sigHashNone.num) must be(false)
|
||||
HashType.isSIGHASH_ALL(HashType.sigHashSingle.num) must be(false)
|
||||
HashType.isSigHashAll(HashType.sigHashNone.num) must be(false)
|
||||
HashType.isSigHashAll(HashType.sigHashSingle.num) must be(false)
|
||||
}
|
||||
|
||||
it must "return the correct byte for a given hashtype" in {
|
||||
|
@ -136,8 +136,8 @@ sealed abstract class TransactionSignatureSerializer {
|
||||
}
|
||||
case SigVersionWitnessV0 =>
|
||||
val isNotAnyoneCanPay = !HashType.isAnyoneCanPay(hashType)
|
||||
val isNotSigHashSingle = !(HashType.isSIGHASH_SINGLE(hashType.num))
|
||||
val isNotSigHashNone = !(HashType.isSIGHASH_NONE(hashType.num))
|
||||
val isNotSigHashSingle = !(HashType.isSigHashSingle(hashType.num))
|
||||
val isNotSigHashNone = !(HashType.isSigHashNone(hashType.num))
|
||||
val inputIndexInt = inputIndex.toInt
|
||||
val emptyHash = CryptoUtil.emptyDoubleSha256Hash
|
||||
|
||||
@ -154,7 +154,8 @@ sealed abstract class TransactionSignatureSerializer {
|
||||
val outputHash: Seq[Byte] = if (isNotSigHashSingle && isNotSigHashNone) {
|
||||
val bytes = spendingTransaction.outputs.flatMap(o => o.bytes)
|
||||
CryptoUtil.doubleSHA256(bytes).bytes
|
||||
} else if (HashType.isSIGHASH_SINGLE(hashType.num) && inputIndex < UInt32(spendingTransaction.outputs.size)) {
|
||||
} else if (HashType.isSigHashSingle(hashType.num) &&
|
||||
inputIndex < UInt32(spendingTransaction.outputs.size)) {
|
||||
val output = spendingTransaction.outputs(inputIndexInt)
|
||||
val bytes = CryptoUtil.doubleSHA256(RawTransactionOutputParser.write(output)).bytes
|
||||
bytes
|
||||
|
@ -9,7 +9,7 @@ import org.bitcoins.core.util.Factory
|
||||
*/
|
||||
sealed trait HashType {
|
||||
def num: Int32
|
||||
def byte: Byte
|
||||
def byte: Byte = num.bytes.last
|
||||
}
|
||||
|
||||
object HashType extends Factory[HashType] {
|
||||
@ -21,14 +21,23 @@ object HashType extends Factory[HashType] {
|
||||
def fromByte(byte: Byte): HashType = fromBytes(Seq(byte))
|
||||
|
||||
def fromNumber(num: Int32): HashType = {
|
||||
if (isSIGHASH_NONE(num)) {
|
||||
if (isSIGHASH_NONE_ANYONECANPAY(num)) SIGHASH_NONE_ANYONECANPAY(num) else SIGHASH_NONE(num)
|
||||
} else if (isSIGHASH_SINGLE(num)) {
|
||||
if (isSIGHASH_SINGLE_ANYONECANPAY(num)) SIGHASH_SINGLE_ANYONECANPAY(num) else SIGHASH_SINGLE(num)
|
||||
} else if (isSIGHASH_ANYONECANPAY(num)) {
|
||||
if (isSIGHASH_ALL_ANYONECANPAY(num)) SIGHASH_ALL_ANYONECANPAY(num)
|
||||
else {
|
||||
require(isONLY_ANYONE_CANPAY(num))
|
||||
if (isSigHashNone(num)) {
|
||||
if (isSigHashNoneAnyoneCanPay(num)) {
|
||||
SIGHASH_NONE_ANYONECANPAY(num)
|
||||
} else {
|
||||
SIGHASH_NONE(num)
|
||||
}
|
||||
} else if (isSigHashSingle(num)) {
|
||||
if (isSigHashAnyoneCanPay(num)) {
|
||||
SIGHASH_SINGLE_ANYONECANPAY(num)
|
||||
} else {
|
||||
SIGHASH_SINGLE(num)
|
||||
}
|
||||
} else if (isSigHashAnyoneCanPay(num)) {
|
||||
if (isSigHashAllAnyoneCanPay(num)) {
|
||||
SIGHASH_ALL_ANYONECANPAY(num)
|
||||
} else {
|
||||
require(isOnlyAnyoneCanPay(num))
|
||||
SIGHASH_ANYONECANPAY(num)
|
||||
}
|
||||
} else SIGHASH_ALL(num)
|
||||
@ -40,21 +49,40 @@ object HashType extends Factory[HashType] {
|
||||
case h: HashType => h.byte
|
||||
}
|
||||
|
||||
def isSIGHASH_ALL_ONE(num: Int32): Boolean = (num & Int32(0x1f)) == Int32(1)
|
||||
def isSIGHASH_NONE(num: Int32): Boolean = (num & Int32(0x1f)) == Int32(2)
|
||||
def isSIGHASH_SINGLE(num: Int32): Boolean = (num & Int32(0x1f)) == Int32(3)
|
||||
def isSIGHASH_ANYONECANPAY(num: Int32): Boolean = (num & Int32(0x80)) == Int32(0x80)
|
||||
def isSIGHASH_ALL_ANYONECANPAY(num: Int32): Boolean = isSIGHASH_ALL_ONE(num) && isSIGHASH_ANYONECANPAY(num)
|
||||
def isSIGHASH_NONE_ANYONECANPAY(num: Int32): Boolean = isSIGHASH_NONE(num) && isSIGHASH_ANYONECANPAY(num)
|
||||
def isSIGHASH_SINGLE_ANYONECANPAY(num: Int32): Boolean = isSIGHASH_SINGLE(num) && isSIGHASH_ANYONECANPAY(num)
|
||||
def isSIGHASH_ALL(num: Int32): Boolean = {
|
||||
if (!(isSIGHASH_NONE(num) || isSIGHASH_SINGLE(num) || isSIGHASH_ANYONECANPAY(num) || isSIGHASH_ALL_ANYONECANPAY(num) ||
|
||||
isSIGHASH_SINGLE_ANYONECANPAY(num) || isSIGHASH_NONE_ANYONECANPAY(num))) true
|
||||
def isSigHashAllOne(num: Int32): Boolean = (num & Int32(0x1f)) == Int32(1)
|
||||
|
||||
def isSigHashNone(num: Int32): Boolean = (num & Int32(0x1f)) == Int32(2)
|
||||
|
||||
def isSigHashSingle(num: Int32): Boolean = (num & Int32(0x1f)) == Int32(3)
|
||||
|
||||
def isSigHashAnyoneCanPay(num: Int32): Boolean = (num & Int32(0x80)) == Int32(0x80)
|
||||
|
||||
def isSigHashAllAnyoneCanPay(num: Int32): Boolean = {
|
||||
isSigHashAllOne(num) && isSigHashAnyoneCanPay(num)
|
||||
}
|
||||
|
||||
def isSigHashNoneAnyoneCanPay(num: Int32): Boolean = {
|
||||
isSigHashNone(num) && isSigHashAnyoneCanPay(num)
|
||||
}
|
||||
|
||||
def isSigHashSingleAnyoneCanPay(num: Int32): Boolean = {
|
||||
isSigHashSingle(num) && isSigHashAnyoneCanPay(num)
|
||||
}
|
||||
|
||||
def isSigHashAll(num: Int32): Boolean = {
|
||||
if (!(isSigHashNone(num) ||
|
||||
isSigHashSingle(num) ||
|
||||
isSigHashAnyoneCanPay(num) ||
|
||||
isSigHashAllAnyoneCanPay(num) ||
|
||||
isSigHashSingleAnyoneCanPay(num) ||
|
||||
isSigHashNoneAnyoneCanPay(num))) true
|
||||
else false
|
||||
}
|
||||
def isONLY_ANYONE_CANPAY(num: Int32): Boolean = {
|
||||
!(HashType.isSIGHASH_ALL_ANYONECANPAY(num) || HashType.isSIGHASH_NONE_ANYONECANPAY(num) ||
|
||||
HashType.isSIGHASH_SINGLE_ANYONECANPAY(num))
|
||||
|
||||
def isOnlyAnyoneCanPay(num: Int32): Boolean = {
|
||||
!(HashType.isSigHashAllAnyoneCanPay(num) ||
|
||||
HashType.isSigHashNoneAnyoneCanPay(num) ||
|
||||
HashType.isSigHashSingleAnyoneCanPay(num))
|
||||
}
|
||||
|
||||
/** Checks if the given hash type has the ANYONECANPAY bit set */
|
||||
@ -69,6 +97,7 @@ object HashType extends Factory[HashType] {
|
||||
|
||||
lazy val hashTypeBytes: Seq[Byte] = Seq(sigHashAllByte, sigHashSingleByte, sigHashNoneByte, sigHashAnyoneCanPayByte,
|
||||
sigHashNoneAnyoneCanPayByte, sigHashSingleAnyoneCanPayByte, sigHashAllAnyoneCanPayByte)
|
||||
|
||||
def apply(num: Int32): HashType = fromNumber(num)
|
||||
|
||||
def apply(int: Int): HashType = HashType(Int32(int))
|
||||
@ -140,40 +169,33 @@ object HashType extends Factory[HashType] {
|
||||
* https://en.bitcoin.it/wiki/OP_CHECKSIG
|
||||
*/
|
||||
case class SIGHASH_ALL(override val num: Int32) extends HashType {
|
||||
require(HashType.isSIGHASH_ALL(num), "SIGHASH_ALL acts as a 'catch-all' for undefined hashtypes, and has a default " +
|
||||
require(HashType.isSigHashAll(num), "SIGHASH_ALL acts as a 'catch-all' for undefined hashtypes, and has a default " +
|
||||
"value of one. Your input was: " + num + ", which is of hashType: " + HashType(num))
|
||||
override def byte = HashType.sigHashAllByte
|
||||
}
|
||||
object SIGHASH_ALL {
|
||||
def apply(byte: Byte): SIGHASH_ALL = SIGHASH_ALL(Int32(byte))
|
||||
}
|
||||
|
||||
case class SIGHASH_NONE(override val num: Int32) extends HashType {
|
||||
require(HashType.isSIGHASH_NONE(num), "The given number is not a SIGHASH_NONE number: " + num)
|
||||
override def byte: Byte = HashType.sigHashNoneByte
|
||||
require(HashType.isSigHashNone(num), "The given number is not a SIGHASH_NONE number: " + num)
|
||||
}
|
||||
|
||||
case class SIGHASH_SINGLE(override val num: Int32) extends HashType {
|
||||
require(HashType.isSIGHASH_SINGLE(num), "The given number is not a SIGHASH_SINGLE number: " + num)
|
||||
override def byte: Byte = HashType.sigHashSingleByte
|
||||
require(HashType.isSigHashSingle(num), "The given number is not a SIGHASH_SINGLE number: " + num)
|
||||
}
|
||||
|
||||
case class SIGHASH_ANYONECANPAY(override val num: Int32) extends HashType {
|
||||
require(HashType.isSIGHASH_ANYONECANPAY(num), "The given number was not a SIGHASH_ANYONECANPAY number: " + num)
|
||||
override def byte: Byte = HashType.sigHashAnyoneCanPayByte
|
||||
require(HashType.isSigHashAnyoneCanPay(num), "The given number was not a SIGHASH_ANYONECANPAY number: " + num)
|
||||
}
|
||||
|
||||
case class SIGHASH_ALL_ANYONECANPAY(override val num: Int32) extends HashType {
|
||||
require(HashType.isSIGHASH_ALL_ANYONECANPAY(num), "The given number was not a SIGHASH_ALL_ANYONECANPAY number: " + num)
|
||||
override def byte: Byte = HashType.sigHashAllAnyoneCanPayByte
|
||||
require(HashType.isSigHashAllAnyoneCanPay(num), "The given number was not a SIGHASH_ALL_ANYONECANPAY number: " + num)
|
||||
}
|
||||
|
||||
case class SIGHASH_NONE_ANYONECANPAY(override val num: Int32) extends HashType {
|
||||
require(HashType.isSIGHASH_NONE_ANYONECANPAY(num), "The given number was not a SIGHASH_NONE_ANYONECANPAY number: " + num)
|
||||
override def byte: Byte = HashType.sigHashNoneAnyoneCanPayByte
|
||||
require(HashType.isSigHashNoneAnyoneCanPay(num), "The given number was not a SIGHASH_NONE_ANYONECANPAY number: " + num)
|
||||
}
|
||||
|
||||
case class SIGHASH_SINGLE_ANYONECANPAY(override val num: Int32) extends HashType {
|
||||
require(HashType.isSIGHASH_SINGLE_ANYONECANPAY(num), "The given number was not a SIGHASH_SINGLE_ANYONECANPAY number: " + num)
|
||||
override def byte: Byte = HashType.sigHashSingleAnyoneCanPayByte
|
||||
require(HashType.isSigHashSingleAnyoneCanPay(num), "The given number was not a SIGHASH_SINGLE_ANYONECANPAY number: " + num)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user