mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-03 18:47:38 +01:00
Fix bounds checking for MultiSignatureScriptPubKey.maxSigsRequired (#5365)
This commit is contained in:
parent
83a27e2775
commit
0f95a1f7bb
2 changed files with 43 additions and 12 deletions
|
@ -412,6 +412,13 @@ class TransactionTest extends BitcoinSUnitTest {
|
||||||
assert(tx.hex == hex)
|
assert(tx.hex == hex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it must "parse decb09c41bc76bad8e006d92cf9f8c7ad4114e441a9cc6daf149e1495593a82f" in {
|
||||||
|
val hex =
|
||||||
|
"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff5303de1304040007762f44124d696e656420627920425443204775696c642cfabe6d6d4da12b2799cc8b6bba921e104cc9054fd1da40ed6c1b6287efd8460475f2a0440100000000000000080247961b56ae0000ffffffff018759b396000000001976a91427a1f12771de5cc3b73941664b2537c15316be4388ac00000000"
|
||||||
|
val tx = Transaction.fromHex(hex)
|
||||||
|
assert(tx.hex == hex)
|
||||||
|
}
|
||||||
|
|
||||||
private def findInput(
|
private def findInput(
|
||||||
tx: Transaction,
|
tx: Transaction,
|
||||||
outPoint: TransactionOutPoint): Option[(TransactionInput, Int)] = {
|
outPoint: TransactionOutPoint): Option[(TransactionInput, Int)] = {
|
||||||
|
|
|
@ -118,9 +118,16 @@ sealed trait MultiSignatureScriptPubKey extends RawScriptPubKey {
|
||||||
val numSigsRequired = asmWithoutPushOps(opCheckMultiSigIndex - maxSigs - 2)
|
val numSigsRequired = asmWithoutPushOps(opCheckMultiSigIndex - maxSigs - 2)
|
||||||
numSigsRequired match {
|
numSigsRequired match {
|
||||||
case x: ScriptNumber => x.toInt
|
case x: ScriptNumber => x.toInt
|
||||||
case c: ScriptConstant
|
case c: ScriptConstant =>
|
||||||
if ScriptNumber(c.hex).toLong <= Consensus.maxPublicKeysPerMultiSig =>
|
val sn = ScriptNumber(c.bytes).toInt
|
||||||
ScriptNumber(c.hex).toInt
|
val inBounds =
|
||||||
|
sn >= 0 && sn <= Consensus.maxPublicKeysPerMultiSig
|
||||||
|
if (inBounds) {
|
||||||
|
sn
|
||||||
|
} else {
|
||||||
|
sys.error(
|
||||||
|
s"Negative numSignaturesRequired given for MultiSignatureSPK, got=$sn")
|
||||||
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
"The first element of the multisignature pubkey must be a script number operation\n" +
|
"The first element of the multisignature pubkey must be a script number operation\n" +
|
||||||
|
@ -137,10 +144,16 @@ sealed trait MultiSignatureScriptPubKey extends RawScriptPubKey {
|
||||||
} else {
|
} else {
|
||||||
asm(checkMultiSigIndex - 1) match {
|
asm(checkMultiSigIndex - 1) match {
|
||||||
case x: ScriptNumber => x.toInt
|
case x: ScriptNumber => x.toInt
|
||||||
case c: ScriptConstant
|
case c: ScriptConstant =>
|
||||||
if ScriptNumber(
|
val maxSigs = ScriptNumber(c.bytes).toInt
|
||||||
c.hex).toLong <= Consensus.maxPublicKeysPerMultiSig =>
|
val inBounds =
|
||||||
ScriptNumber(c.hex).toInt
|
maxSigs >= 0 && maxSigs <= Consensus.maxPublicKeysPerMultiSig
|
||||||
|
if (inBounds) {
|
||||||
|
maxSigs
|
||||||
|
} else {
|
||||||
|
sys.error(
|
||||||
|
s"Negative maxSigs given for MultiSignatureSPK, got=$maxSigs")
|
||||||
|
}
|
||||||
case x =>
|
case x =>
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
"The element preceding a OP_CHECKMULTISIG operation in a multisignature pubkey must be a script number operation, got: " + x)
|
"The element preceding a OP_CHECKMULTISIG operation in a multisignature pubkey must be a script number operation, got: " + x)
|
||||||
|
@ -225,8 +238,10 @@ object MultiSignatureScriptPubKey
|
||||||
|
|
||||||
/** Determines if the given script tokens are a multisignature `scriptPubKey` */
|
/** Determines if the given script tokens are a multisignature `scriptPubKey` */
|
||||||
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
|
override def isValidAsm(asm: Seq[ScriptToken]): Boolean = {
|
||||||
val containsMultiSigOp =
|
val cmsIdx = if (asm.contains(OP_CHECKMULTISIG)) {
|
||||||
asm.contains(OP_CHECKMULTISIG) || asm.contains(OP_CHECKMULTISIGVERIFY)
|
asm.indexOf(OP_CHECKMULTISIG)
|
||||||
|
} else asm.indexOf(OP_CHECKMULTISIGVERIFY)
|
||||||
|
val containsMultiSigOp = cmsIdx != -1
|
||||||
|
|
||||||
if (asm.nonEmpty && containsMultiSigOp) {
|
if (asm.nonEmpty && containsMultiSigOp) {
|
||||||
//we need either the first or second asm operation to indicate how many signatures are required
|
//we need either the first or second asm operation to indicate how many signatures are required
|
||||||
|
@ -242,9 +257,18 @@ object MultiSignatureScriptPubKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//the second to last asm operation should be the maximum amount of public keys
|
//the second to last asm operation should be the maximum amount of public keys
|
||||||
val hasMaximumSignaturesTry = Try {
|
val hasMaximumSignaturesTry = {
|
||||||
asm(asm.length - 2) match {
|
val maxSigsIdx = asm.length - 2
|
||||||
case token: ScriptToken => isValidPubKeyNumber(token)
|
if (maxSigsIdx >= cmsIdx) {
|
||||||
|
val exn = new IllegalAccessException(
|
||||||
|
s"maxSigsIdx is after OP_CHECKMULTISIG/OP_CHECKMULTISIGVERIFY, maxSigsIx=$maxSigsIdx")
|
||||||
|
Failure(exn)
|
||||||
|
} else {
|
||||||
|
Try {
|
||||||
|
asm(maxSigsIdx) match {
|
||||||
|
case token: ScriptToken => isValidPubKeyNumber(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue