From 0c29c8cfdaffd92cac3f60e543bbece1834ed060 Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Sun, 22 Jul 2018 12:55:20 -0500 Subject: [PATCH] Add invariant that doesn't allow segwit scripts to be created with uncompressed public keys reset scalafmt for rpc/ Removing unit test that used uncompressed public key for P2WSH, removing extra debug --- .../TransactionSignatureSerializerTest.scala | 20 ------------- .../script/P2WPKHWitnessSPKV0Test.scala | 15 ++++++++++ .../script/P2WSHWitnessSPKV0Test.scala | 30 +++++++++++++++++++ .../core/protocol/script/ScriptPubKey.scala | 3 ++ .../core/util/BitcoinScriptUtil.scala | 23 ++++++++++++++ 5 files changed, 71 insertions(+), 20 deletions(-) create mode 100644 core-test/src/test/scala/org/bitcoins/core/protocol/script/P2WPKHWitnessSPKV0Test.scala create mode 100644 core-test/src/test/scala/org/bitcoins/core/protocol/script/P2WSHWitnessSPKV0Test.scala diff --git a/core-test/src/test/scala/org/bitcoins/core/crypto/TransactionSignatureSerializerTest.scala b/core-test/src/test/scala/org/bitcoins/core/crypto/TransactionSignatureSerializerTest.scala index 9e89672ed7..213ed48281 100644 --- a/core-test/src/test/scala/org/bitcoins/core/crypto/TransactionSignatureSerializerTest.scala +++ b/core-test/src/test/scala/org/bitcoins/core/crypto/TransactionSignatureSerializerTest.scala @@ -412,26 +412,6 @@ class TransactionSignatureSerializerTest extends FlatSpec with MustMatchers { } - it must "serialize a basic p2wsh transaction correctly" in { - val expected = "01000000ff78d7a91d1d9f2defd4b9d7e17c8b2182565453e83ceaacc78dd2ee095681f13bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e7066504490491d88b9f0dc24d271f0f67179bce5914afe1ac0f83f6cd205f8b807436d6f0000000043410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac0100000000000000ffffffffe5d196bfb21caca9dbd654cafb3b4dc0c4882c8927d2eb300d9539dd0b9342280000000001000000" - val hex = "0100000000010190491d88b9f0dc24d271f0f67179bce5914afe1ac0f83f6cd205f8b807436d6f0000000000ffffffff010100000000000000000247304402200d461c140cfdfcf36b94961db57ae8c18d1cb80e9d95a9e47ac22470c1bf125502201c8dc1cbfef6a3ef90acbbb992ca22fe9466ee6f9d4898eda277a7ac3ab4b2510143410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac00000000" - - val spendingTx: WitnessTransaction = WitnessTransaction(hex) - spendingTx.hex must be(hex) - val inputIndex = UInt32.zero - val witnessRedeemScript = ScriptPubKey("43410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac") - val p2wsh = P2WSHWitnessSPKV0(witnessRedeemScript) - val amount = Satoshis(Int64(1)) - val txSigComponent = WitnessTxSigComponentRaw( - transaction = spendingTx, - inputIndex = inputIndex, - output = TransactionOutput(amount, p2wsh), - flags = Policy.standardFlags) - val serializedForSig = TransactionSignatureSerializer.serializeForSignature(txSigComponent, HashType.sigHashAll) - - BitcoinSUtil.encodeHex(serializedForSig) must be(expected) - } - it must "serialize a p2wpkh with SIGHASH_SINGLE|SIGHASH_ANYONECANPAY" in { val rawTx = "0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff05540b0000000000000151d0070000000000000151840300000000000001513c0f00000000000001512c010000000000000151000248304502210092f4777a0f17bf5aeb8ae768dec5f2c14feabf9d1fe2c89c78dfed0f13fdb86902206da90a86042e252bcd1e80a168c719e4a1ddcc3cebea24b9812c5453c79107e9832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71000000000000" val inputIndex = UInt32(1) diff --git a/core-test/src/test/scala/org/bitcoins/core/protocol/script/P2WPKHWitnessSPKV0Test.scala b/core-test/src/test/scala/org/bitcoins/core/protocol/script/P2WPKHWitnessSPKV0Test.scala new file mode 100644 index 0000000000..f224e1ad47 --- /dev/null +++ b/core-test/src/test/scala/org/bitcoins/core/protocol/script/P2WPKHWitnessSPKV0Test.scala @@ -0,0 +1,15 @@ +package org.bitcoins.core.protocol.script + +import org.bitcoins.core.crypto.ECPrivateKey +import org.scalatest.{ FlatSpec, MustMatchers } + +class P2WPKHWitnessSPKV0Test extends FlatSpec with MustMatchers { + + "P2WPKHWitnessSPKV0" must "fail to be created with an uncompressed public key" in { + val uncompressed = ECPrivateKey(false).publicKey + intercept[IllegalArgumentException] { + P2WPKHWitnessSPKV0(uncompressed) + } + } +} + diff --git a/core-test/src/test/scala/org/bitcoins/core/protocol/script/P2WSHWitnessSPKV0Test.scala b/core-test/src/test/scala/org/bitcoins/core/protocol/script/P2WSHWitnessSPKV0Test.scala new file mode 100644 index 0000000000..feef4e9410 --- /dev/null +++ b/core-test/src/test/scala/org/bitcoins/core/protocol/script/P2WSHWitnessSPKV0Test.scala @@ -0,0 +1,30 @@ +package org.bitcoins.core.protocol.script + +import org.bitcoins.core.crypto.ECPrivateKey +import org.bitcoins.core.script.constant.ScriptNumber +import org.scalatest.{ FlatSpec, MustMatchers } + +class P2WSHWitnessSPKV0Test extends FlatSpec with MustMatchers { + val uncompressed = ECPrivateKey(false).publicKey + val p2pk = P2PKScriptPubKey(uncompressed) + val multisig = MultiSignatureScriptPubKey(1, Vector(uncompressed)) + "P2WPKHWitnessSPKV0" must "fail to be created with an uncompressed public key" in { + intercept[IllegalArgumentException] { + P2WSHWitnessSPKV0(p2pk) + } + } + + it must "fail for a multisig script with an uncompressed public key" in { + + intercept[IllegalArgumentException] { + P2WSHWitnessSPKV0(multisig) + } + } + + it must "fail with a locktime script" in { + val cltv = CLTVScriptPubKey(ScriptNumber.zero, multisig) + intercept[IllegalArgumentException] { + P2WSHWitnessSPKV0(cltv) + } + } +} diff --git a/core/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala b/core/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala index 08b78bbb15..076f25c60b 100644 --- a/core/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala +++ b/core/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala @@ -575,6 +575,8 @@ object P2WPKHWitnessSPKV0 extends ScriptFactory[P2WPKHWitnessSPKV0] { /** Creates a P2WPKH witness script pubkey */ def apply(pubKey: ECPublicKey): P2WPKHWitnessSPKV0 = { + //https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#restrictions-on-public-key-type + require(pubKey.isCompressed, s"Public key must be compressed to be used in a segwit script, see BIP143") val hash = CryptoUtil.sha256Hash160(pubKey.bytes) val pushop = BitcoinScriptUtil.calculatePushOp(hash.bytes) fromAsm(Seq(OP_0) ++ pushop ++ Seq(ScriptConstant(hash.bytes))) @@ -603,6 +605,7 @@ object P2WSHWitnessSPKV0 extends ScriptFactory[P2WSHWitnessSPKV0] { } def apply(spk: ScriptPubKey): P2WSHWitnessSPKV0 = { + require(BitcoinScriptUtil.isOnlyCompressedPubKey(spk), s"Public key must be compressed to be used in a segwit script, see BIP143") val hash = CryptoUtil.sha256(spk.asmBytes) val pushop = BitcoinScriptUtil.calculatePushOp(hash.bytes) fromAsm(Seq(OP_0) ++ pushop ++ Seq(ScriptConstant(hash.bytes))) diff --git a/core/src/main/scala/org/bitcoins/core/util/BitcoinScriptUtil.scala b/core/src/main/scala/org/bitcoins/core/util/BitcoinScriptUtil.scala index 2d170dc15a..cac4043249 100644 --- a/core/src/main/scala/org/bitcoins/core/util/BitcoinScriptUtil.scala +++ b/core/src/main/scala/org/bitcoins/core/util/BitcoinScriptUtil.scala @@ -391,6 +391,29 @@ trait BitcoinScriptUtil extends BitcoinSLogger { else asm } + /** + * Checks that all the [[org.bitcoins.core.crypto.ECPublicKey]] in this script + * is compressed public keys, this is required for BIP143 + * @param spk + * @return + */ + def isOnlyCompressedPubKey(spk: ScriptPubKey): Boolean = { + spk match { + case p2pk: P2PKScriptPubKey => p2pk.publicKey.isCompressed + case m: MultiSignatureScriptPubKey => + !m.publicKeys.exists(k => !k.isCompressed) + case l: LockTimeScriptPubKey => + isOnlyCompressedPubKey(l.nestedScriptPubKey) + case e: EscrowTimeoutScriptPubKey => + isOnlyCompressedPubKey(e.escrow) && isOnlyCompressedPubKey(e.timeout) + case _: P2PKHScriptPubKey | _: P2SHScriptPubKey + | _: P2WPKHWitnessSPKV0 | _: P2WSHWitnessSPKV0 | _: UnassignedWitnessScriptPubKey + | _: NonStandardScriptPubKey | _: WitnessCommitment | EmptyScriptPubKey => + true + + } + } + } object BitcoinScriptUtil extends BitcoinScriptUtil