mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 01:40:55 +01:00
This reverts commit 8e7bde0ed9
.
This commit is contained in:
parent
8e7bde0ed9
commit
78448b277c
@ -376,7 +376,10 @@ lazy val cryptoTest = crossProject(JVMPlatform, JSPlatform)
|
||||
name := "bitcoin-s-crypto-test",
|
||||
libraryDependencies ++= Deps.cryptoTest.value
|
||||
)
|
||||
.dependsOn(crypto)
|
||||
.dependsOn(
|
||||
crypto,
|
||||
testkitCore
|
||||
)
|
||||
|
||||
lazy val cryptoTestJVM = cryptoTest.jvm
|
||||
|
||||
|
@ -1,19 +1,20 @@
|
||||
package org.bitcoins.core.util
|
||||
|
||||
import java.math.BigInteger
|
||||
|
||||
import org.bitcoins.core.number._
|
||||
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
||||
import org.bitcoins.core.protocol.blockchain.BlockHeader.TargetDifficultyHelper
|
||||
import org.bitcoins.crypto.CryptoNumberUtil
|
||||
import org.bitcoins.crypto.FieldElement
|
||||
import scodec.bits.{BitVector, ByteVector}
|
||||
|
||||
import java.math.BigInteger
|
||||
import scala.annotation.tailrec
|
||||
import scala.math.BigInt
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
/** Created by chris on 2/8/16.
|
||||
*/
|
||||
sealed abstract class NumberUtil extends CryptoNumberUtil {
|
||||
sealed abstract class NumberUtil extends BitcoinSLogger {
|
||||
|
||||
/** Takes 2^^num. */
|
||||
def pow2(exponent: Int): BigInt = {
|
||||
@ -23,6 +24,43 @@ sealed abstract class NumberUtil extends CryptoNumberUtil {
|
||||
BigInt(1) << exponent
|
||||
}
|
||||
|
||||
/** Converts a sequence of bytes to a **big endian** unsigned integer */
|
||||
def toUnsignedInt(bytes: ByteVector): BigInt = {
|
||||
toUnsignedInt(bytes.toArray)
|
||||
}
|
||||
|
||||
def uintToFieldElement(bytes: ByteVector): FieldElement = {
|
||||
FieldElement(toUnsignedInt(bytes))
|
||||
}
|
||||
|
||||
/** Converts a sequence of bytes to a **big endian** unsigned integer */
|
||||
def toUnsignedInt(bytes: Array[Byte]): BigInt = {
|
||||
BigInt(new BigInteger(1, bytes))
|
||||
}
|
||||
|
||||
/** Takes a hex string and parses it to a [[scala.math.BigInt BigInt]]. */
|
||||
def toBigInt(hex: String): BigInt = toBigInt(BytesUtil.decodeHex(hex))
|
||||
|
||||
/** Converts a sequence of bytes to twos complement signed number. */
|
||||
def toBigInt(bytes: ByteVector): BigInt = {
|
||||
//BigInt interprets the number as an unsigned number then applies the given
|
||||
//sign in front of that number, therefore if we have a negative number we need to invert it
|
||||
//since twos complement is an inverted number representation for negative numbers
|
||||
//see [[https://en.wikipedia.org/wiki/Two%27s_complement]]
|
||||
if (bytes.isEmpty) BigInt(0)
|
||||
//check if sign bit is set
|
||||
else if ((0x80.toByte & bytes.head) != 0) {
|
||||
val invertedBytes = bytes.tail.map(b => (b ^ 0xff.toByte).toByte)
|
||||
val firstByteInverted = (bytes.head ^ 0xff.toByte).toByte
|
||||
val num = firstByteInverted +: invertedBytes
|
||||
BigInt(-1, num.toArray) - 1
|
||||
} else {
|
||||
val firstBitOff = (0x7f & bytes.head).toByte
|
||||
val num = firstBitOff +: bytes.tail
|
||||
BigInt(num.toArray)
|
||||
}
|
||||
}
|
||||
|
||||
/** Converts a sequence of [[scala.Byte Byte]] to a [[scala.Int Int]]. */
|
||||
def toInt(bytes: ByteVector): Int = toBigInt(bytes).toInt
|
||||
|
||||
|
@ -1,16 +0,0 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
class BCryptoECDigitalSignatureTest extends BitcoinSCryptoTest {
|
||||
|
||||
behavior of "BCryptoECDigitalSignatureTest"
|
||||
|
||||
it must "be able to generate valid signatures with bcrypto" in {
|
||||
forAll(CryptoGenerators.privateKey, CryptoGenerators.sha256Digest) {
|
||||
case (privKey: ECPrivateKey, hash: Sha256Digest) =>
|
||||
val sig = BCryptoCryptoRuntime.sign(privKey, hash.bytes)
|
||||
val pubKey = privKey.publicKey
|
||||
|
||||
assert(pubKey.verify(hash, sig))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.must.Matchers
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
class HashingTest extends BitcoinSCryptoTest {
|
||||
class HashingTest extends AnyFlatSpec with Matchers {
|
||||
|
||||
private lazy val lines = Vector(
|
||||
// rnd,sha1(rnd),sha256(rnd),ripeMd160(rnd),sha256Hash160(rnd),hmac(rnd,sha256)
|
||||
|
@ -1,8 +1,9 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import scodec.bits.ByteVector
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.must.Matchers
|
||||
|
||||
class KeysTest extends BitcoinSCryptoTest {
|
||||
class KeysTest extends AnyFlatSpec with Matchers {
|
||||
|
||||
it must "generate keys" in {
|
||||
val privkey = ECPrivateKey.freshPrivateKey
|
||||
@ -17,62 +18,4 @@ class KeysTest extends BitcoinSCryptoTest {
|
||||
assert(!BCryptoCryptoRuntime.isValidPubKey(privkey.bytes))
|
||||
}
|
||||
|
||||
it must "be able to compress/decompress public keys" in {
|
||||
val privkey = ECPrivateKey.freshPrivateKey
|
||||
assert(BCryptoCryptoRuntime.secKeyVerify(privkey.bytes))
|
||||
assert(privkey.isCompressed)
|
||||
|
||||
val pubkey = BCryptoCryptoRuntime.toPublicKey(privkey, isCompressed = false)
|
||||
assert(BCryptoCryptoRuntime.isValidPubKey(pubkey.bytes))
|
||||
assert(!pubkey.isCompressed)
|
||||
|
||||
val compressed = privkey.publicKey
|
||||
assert(BCryptoCryptoRuntime.isValidPubKey(compressed.bytes))
|
||||
assert(compressed.isCompressed)
|
||||
|
||||
val converted = ECPublicKey.fromBytes(
|
||||
BCryptoCryptoRuntime.publicKeyConvert(pubkey.bytes, compressed = true))
|
||||
assert(BCryptoCryptoRuntime.isValidPubKey(converted.bytes))
|
||||
assert(converted.isCompressed)
|
||||
|
||||
val decompressed = ECPublicKey.fromBytes(
|
||||
BCryptoCryptoRuntime.publicKeyConvert(compressed.bytes,
|
||||
compressed = false))
|
||||
assert(BCryptoCryptoRuntime.isValidPubKey(decompressed.bytes))
|
||||
assert(!decompressed.isCompressed)
|
||||
|
||||
assert(pubkey.bytes != converted.bytes)
|
||||
assert(compressed.bytes == converted.bytes)
|
||||
assert(compressed.bytes != decompressed.bytes)
|
||||
assert(pubkey.bytes == decompressed.bytes)
|
||||
}
|
||||
|
||||
private val inf = ECPublicKey.fromHex("00")
|
||||
|
||||
it must "be able to add infinity points" in {
|
||||
val privkey = ECPrivateKey.freshPrivateKey
|
||||
val pubkey1 = privkey.publicKey
|
||||
val firstByte: Byte =
|
||||
if (pubkey1.bytes.head == 0x02) 0x03
|
||||
else if (pubkey1.bytes.head == 0x03) 0x02
|
||||
else pubkey1.bytes.head
|
||||
val pubkey2 =
|
||||
ECPublicKey.fromBytes(ByteVector(firstByte) ++ pubkey1.bytes.tail)
|
||||
|
||||
val res1 = BCryptoCryptoRuntime.add(pubkey1, pubkey2)
|
||||
|
||||
assert(res1 == inf)
|
||||
|
||||
val decompressedPubkey1 = ECPublicKey.fromBytes(
|
||||
BCryptoCryptoRuntime.publicKeyConvert(pubkey1.bytes, compressed = false))
|
||||
|
||||
val decompressedPubkey2 = ECPublicKey.fromBytes(
|
||||
BCryptoCryptoRuntime.publicKeyConvert(pubkey2.bytes, compressed = false))
|
||||
|
||||
val res2 =
|
||||
BCryptoCryptoRuntime.add(decompressedPubkey1, decompressedPubkey2)
|
||||
|
||||
assert(res2 == inf)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
class RandomTest extends BitcoinSCryptoTest {
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.must.Matchers
|
||||
|
||||
class RandomTest extends AnyFlatSpec with Matchers {
|
||||
|
||||
it should "generate random bytes" in {
|
||||
val rnd = 1.to(16).map(_ => BCryptoCryptoRuntime.randomBytes(32))
|
||||
|
@ -1,98 +0,0 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.scalatest.Assertion
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
class SigningTest extends BitcoinSCryptoTest {
|
||||
|
||||
it must "be able to sign and verify signatures" in {
|
||||
val privkey = ECPrivateKey.freshPrivateKey
|
||||
val pubkey = privkey.publicKey
|
||||
val msg = BCryptoCryptoRuntime.randomBytes(32)
|
||||
val sig = privkey.sign(msg)
|
||||
assert(pubkey.verify(msg, sig))
|
||||
}
|
||||
|
||||
it must "be able to sign and verify Schnorr signatures" in {
|
||||
val privkey = ECPrivateKey.freshPrivateKey
|
||||
val pubkey = privkey.publicKey
|
||||
val msg = BCryptoCryptoRuntime.randomBytes(32)
|
||||
val sig = privkey.schnorrSign(msg)
|
||||
assert(pubkey.schnorrVerify(msg, sig))
|
||||
}
|
||||
|
||||
it must "pass the BIP 340 test-vectors with bcrypto" in {
|
||||
BIP340TestVectors.vectors.foreach {
|
||||
case (index, secKeyOpt, pubKey, auxRandOpt, msg, sig, result, comment) =>
|
||||
test(index = index,
|
||||
secKeyOpt = secKeyOpt,
|
||||
pubKey = pubKey,
|
||||
auxRandOpt = auxRandOpt,
|
||||
msg = msg,
|
||||
sig = sig,
|
||||
result = result,
|
||||
comment = comment)
|
||||
}
|
||||
}
|
||||
|
||||
def test(
|
||||
index: Int,
|
||||
secKeyOpt: Option[String],
|
||||
pubKey: String,
|
||||
auxRandOpt: Option[String],
|
||||
msg: String,
|
||||
sig: String,
|
||||
result: Boolean,
|
||||
comment: String): Assertion = {
|
||||
val pkT = Try(SchnorrPublicKey(pubKey))
|
||||
val msgBytes = ByteVector.fromHex(msg).get
|
||||
val schnorrSigT = Try(SchnorrDigitalSignature(sig))
|
||||
|
||||
(pkT, schnorrSigT) match {
|
||||
case (Success(pk), Success(schnorrSig)) =>
|
||||
(secKeyOpt, auxRandOpt) match {
|
||||
case (Some(secKeyStr), Some(auxRandStr)) =>
|
||||
val secKey = ECPrivateKey(secKeyStr)
|
||||
assert(secKey.schnorrPublicKey == pk)
|
||||
val auxRand = ByteVector.fromHex(auxRandStr).get
|
||||
testSign(index, secKey, auxRand, msgBytes, schnorrSig)
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
testVerify(index, pk, msgBytes, schnorrSig, result, comment)
|
||||
case (Failure(_), _) |
|
||||
(_, Failure(_)) => // Must be verify only test resulting in false
|
||||
assert(secKeyOpt.isEmpty)
|
||||
assert(auxRandOpt.isEmpty)
|
||||
assert(!result, s"Test $index failed to parse signature: $comment")
|
||||
}
|
||||
}
|
||||
|
||||
def testSign(
|
||||
index: Int,
|
||||
secKey: ECPrivateKey,
|
||||
auxRand: ByteVector,
|
||||
msg: ByteVector,
|
||||
expectedSig: SchnorrDigitalSignature): Assertion = {
|
||||
val bcryptoSig =
|
||||
BCryptoCryptoRuntime.schnorrSign(msg, secKey, auxRand)
|
||||
assert(bcryptoSig == expectedSig,
|
||||
s"Test $index failed signing for Bouncy Castle")
|
||||
}
|
||||
|
||||
def testVerify(
|
||||
index: Int,
|
||||
pubKey: SchnorrPublicKey,
|
||||
msg: ByteVector,
|
||||
sig: SchnorrDigitalSignature,
|
||||
expectedResult: Boolean,
|
||||
comment: String): Assertion = {
|
||||
val bcryptoResult =
|
||||
BCryptoCryptoRuntime.schnorrVerify(msg, pubKey, sig)
|
||||
assert(bcryptoResult == expectedResult,
|
||||
s"Test $index failed verification for Bouncy Castle: $comment")
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoins.testkitcore.gen.{CryptoGenerators, NumberGenerator}
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
import org.scalacheck.Gen
|
||||
import org.scalatest.compatible.Assertion
|
||||
import scodec.bits.{ByteVector, HexStringSyntax}
|
||||
|
||||
class AesCryptTest extends BitcoinSCryptoTest {
|
||||
class AesCryptTest extends BitcoinSUnitTest {
|
||||
behavior of "AesEncrypt"
|
||||
|
||||
val password = AesPassword.fromNonEmptyString("PASSWORD")
|
||||
|
@ -1,12 +1,13 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
import org.scalatest.Assertion
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
/** Tests from https://github.com/sipa/bips/blob/bip-taproot/bip-0340/test-vectors.csv */
|
||||
class BouncyCastleBIP340Test extends BitcoinSCryptoTest {
|
||||
class BouncyCastleBIP340Test extends BitcoinSUnitTest {
|
||||
behavior of "Schnorr Signing"
|
||||
|
||||
def testSign(
|
||||
@ -75,9 +76,35 @@ class BouncyCastleBIP340Test extends BitcoinSCryptoTest {
|
||||
}
|
||||
}
|
||||
|
||||
private def toOpt(str: String): Option[String] = {
|
||||
if (str.isEmpty) {
|
||||
None
|
||||
} else {
|
||||
Some(str)
|
||||
}
|
||||
}
|
||||
|
||||
it must "pass the BIP 340 test-vectors with both secp256k1 bindings and bouncy castle" in {
|
||||
BIP340TestVectors.vectors.foreach {
|
||||
case (index, secKeyOpt, pubKey, auxRandOpt, msg, sig, result, comment) =>
|
||||
val bufferedSource =
|
||||
io.Source.fromURL(getClass.getResource("/bip340-test-vectors.csv"))
|
||||
try {
|
||||
val lines = bufferedSource.getLines()
|
||||
val _ = lines.next()
|
||||
for (line <- bufferedSource.getLines()) {
|
||||
val testVec = line.split(",").map(_.trim)
|
||||
val index = testVec.head.toInt
|
||||
val secKeyOpt = toOpt(testVec(1))
|
||||
val pubKey = testVec(2)
|
||||
val auxRandOpt = toOpt(testVec(3))
|
||||
val msg = testVec(4)
|
||||
val sig = testVec(5)
|
||||
val result = testVec(6).toBoolean
|
||||
val comment = if (testVec.length > 7) {
|
||||
testVec(7)
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
||||
test(index = index,
|
||||
secKeyOpt = secKeyOpt,
|
||||
pubKey = pubKey,
|
||||
@ -86,6 +113,9 @@ class BouncyCastleBIP340Test extends BitcoinSCryptoTest {
|
||||
sig = sig,
|
||||
result = result,
|
||||
comment = comment)
|
||||
}
|
||||
} finally {
|
||||
bufferedSource.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
class BouncyCastleECDigitalSignatureTest extends BitcoinSCryptoTest {
|
||||
import org.bitcoins.testkitcore.gen.CryptoGenerators
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
|
||||
class BouncyCastleECDigitalSignatureTest extends BitcoinSUnitTest {
|
||||
|
||||
behavior of "BouncyCastleECDigitalSignatureTest"
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoins.testkitcore.gen.{CryptoGenerators, NumberGenerator}
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
import org.scalacheck.Gen
|
||||
import org.scalatest.{Outcome, Succeeded}
|
||||
|
||||
class BouncyCastleSecp256k1Test extends BitcoinSCryptoTest {
|
||||
class BouncyCastleSecp256k1Test extends BitcoinSUnitTest {
|
||||
|
||||
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
|
||||
generatorDrivenConfigNewCode
|
||||
@ -14,6 +16,7 @@ class BouncyCastleSecp256k1Test extends BitcoinSCryptoTest {
|
||||
CryptoContext.default match {
|
||||
case CryptoContext.LibSecp256k1 => super.withFixture(test)
|
||||
case CryptoContext.BouncyCastle | CryptoContext.BCrypto =>
|
||||
logger.warn(s"Test ${test.name} skipped as Secp256k1 is not available.")
|
||||
Succeeded
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoins.core.util.NumberUtil
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.math.BigInt
|
||||
|
||||
/** Created by chris on 3/23/16.
|
||||
*/
|
||||
class DERSignatureUtilTest extends BitcoinSCryptoTest {
|
||||
class DERSignatureUtilTest extends BitcoinSUnitTest {
|
||||
|
||||
val p2shSignature = ECDigitalSignature(
|
||||
"304402205b7d2c2f177ae76cfbbf14d589c113b0b35db753d305d5562dd0b61cbf366cfb02202e56f93c4f08a27f986cd424ffc48a462c3202c4902104d4d0ff98ed28f4bf8001")
|
||||
@ -37,30 +37,30 @@ class DERSignatureUtilTest extends BitcoinSCryptoTest {
|
||||
it must "retrieve the (r,s) values for a p2sh signature in bitcoin" in {
|
||||
val (r, s) = DERSignatureUtil.decodeSignature(p2shSignature)
|
||||
r must be(
|
||||
CryptoNumberUtil.toBigInt(
|
||||
NumberUtil.toBigInt(
|
||||
"5b7d2c2f177ae76cfbbf14d589c113b0b35db753d305d5562dd0b61cbf366cfb"))
|
||||
s must be(
|
||||
CryptoNumberUtil.toBigInt(
|
||||
NumberUtil.toBigInt(
|
||||
"2e56f93c4f08a27f986cd424ffc48a462c3202c4902104d4d0ff98ed28f4bf80"))
|
||||
}
|
||||
|
||||
it must "retrieve the (r,s) values for a p2pkh signature in bitcoin" in {
|
||||
val (r, s) = DERSignatureUtil.decodeSignature(p2pkhSignature)
|
||||
r must be(
|
||||
CryptoNumberUtil.toBigInt(
|
||||
NumberUtil.toBigInt(
|
||||
"16ffdbb7c57634903c5e018fcfc48d59f4e37dc4bc3bbc9ba4e6ee39150bca03"))
|
||||
s must be(
|
||||
CryptoNumberUtil.toBigInt(
|
||||
NumberUtil.toBigInt(
|
||||
"119c2241a931819bc1a75d3596e4029d803d1cd6de123bf8a1a1a2c3665e1fac"))
|
||||
}
|
||||
|
||||
it must "retrieve the (r,s) values from a p2pk signature in bitcoin" in {
|
||||
val (r, s) = DERSignatureUtil.decodeSignature(p2pkSignature)
|
||||
r must be(
|
||||
CryptoNumberUtil.toBigInt(
|
||||
NumberUtil.toBigInt(
|
||||
"0a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa1"))
|
||||
s must be(
|
||||
CryptoNumberUtil.toBigInt(
|
||||
NumberUtil.toBigInt(
|
||||
"1fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b7420"))
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoins.testkitcore.gen.CryptoGenerators
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/** Created by chris on 3/22/16.
|
||||
*/
|
||||
class ECDigitalSignatureTest extends BitcoinSCryptoTest {
|
||||
class ECDigitalSignatureTest extends BitcoinSUnitTest {
|
||||
|
||||
implicit override val generatorDrivenConfig = generatorDrivenConfigNewCode
|
||||
|
||||
|
@ -1,6 +1,15 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
class ECPrivateKeyTest extends BitcoinSCryptoTest {
|
||||
import org.bitcoins.core.config.{MainNet, RegTest, SigNet, TestNet3}
|
||||
import org.bitcoins.core.crypto.ECPrivateKeyUtil
|
||||
import org.bitcoins.testkitcore.gen.{
|
||||
ChainParamsGenerator,
|
||||
CryptoGenerators,
|
||||
NumberGenerator
|
||||
}
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
|
||||
class ECPrivateKeyTest extends BitcoinSUnitTest {
|
||||
it must "create a private key from its hex representation" in {
|
||||
val privateKeyHex =
|
||||
"180cb41c7c600be951b5d3d0a7334acc7506173875834f7a6c4c786a28fcbb19"
|
||||
@ -8,10 +17,70 @@ class ECPrivateKeyTest extends BitcoinSCryptoTest {
|
||||
key.hex must be(privateKeyHex)
|
||||
}
|
||||
|
||||
it must "determine if a private key corresponds to a compressed public key or not" in {
|
||||
val compressedKey = "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi"
|
||||
val uncompressedKey = "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj"
|
||||
ECPrivateKeyUtil.isCompressed(compressedKey) must be(true)
|
||||
ECPrivateKeyUtil.isCompressed(uncompressedKey) must be(false)
|
||||
}
|
||||
|
||||
it must "create a fresh private key" in {
|
||||
ECPrivateKey() must not equal (ECPrivateKey())
|
||||
}
|
||||
|
||||
it must "serialize a private key to WIF and then be able to deserialize it" in {
|
||||
|
||||
val hex = "2cecbfb72f8d5146d7fe7e5a3f80402c6dd688652c332dff2e44618d2d3372"
|
||||
val privKey = ECPrivateKey(hex)
|
||||
val wif = ECPrivateKeyUtil.toWIF(privKey, TestNet3)
|
||||
val privKeyFromWIF = ECPrivateKeyUtil.fromWIFToPrivateKey(wif)
|
||||
privKeyFromWIF must be(privKey)
|
||||
|
||||
val privKeyDecompressed = ECPrivateKey.fromHex(hex, isCompressed = false)
|
||||
val wifDecompressed = ECPrivateKeyUtil.toWIF(privKeyDecompressed, TestNet3)
|
||||
val privKeyDecompressedFromWIF =
|
||||
ECPrivateKeyUtil.fromWIFToPrivateKey(wifDecompressed)
|
||||
privKeyDecompressedFromWIF must be(privKeyDecompressed)
|
||||
}
|
||||
|
||||
it must "serialize a private key to WIF when the private key is prefixed with 0 bytes" in {
|
||||
val hex =
|
||||
"00fc391adf4d6063a16a2e38b14d2be10133c4dacd4348b49d23ee0ce5ff4f1701"
|
||||
val privKey = ECPrivateKey(hex)
|
||||
val wif = ECPrivateKeyUtil.toWIF(privKey, TestNet3)
|
||||
val privKeyFromWIF = ECPrivateKeyUtil.fromWIFToPrivateKey(wif)
|
||||
privKeyFromWIF must be(privKey)
|
||||
}
|
||||
|
||||
it must "correctly decode a private key from WIF" in {
|
||||
val privateKey = ECPrivateKeyUtil.fromWIFToPrivateKey(
|
||||
"cTPg4Zc5Jis2EZXy3NXShgbn487GWBTapbU63BerLDZM3w2hQSjC")
|
||||
//derived hex on bitcore's playground
|
||||
privateKey.hex must be(
|
||||
"ad59fb6aadf617fb0f93469741fcd9a9f48700f1d1f465ddc0f26fa7f7bfa1ac")
|
||||
}
|
||||
|
||||
it must "decode a WIF private key corresponding to uncompressed public key" in {
|
||||
val wif = "5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C"
|
||||
val privKey = ECPrivateKeyUtil.fromWIFToPrivateKey(wif)
|
||||
privKey.publicKey.hex must be(
|
||||
"045b81f0017e2091e2edcd5eecf10d5bdd120a5514cb3ee65b8447ec18bfc4575c6d5bf415e54e03b1067934a0f0ba76b01c6b9ab227142ee1d543764b69d901e0")
|
||||
}
|
||||
|
||||
it must "have serialization symmetry for WIF format" in {
|
||||
forAll(CryptoGenerators.privateKey, ChainParamsGenerator.networkParams) {
|
||||
(privKey, network) =>
|
||||
val wif = ECPrivateKeyUtil.toWIF(privKey, network)
|
||||
network match {
|
||||
case MainNet =>
|
||||
assert(ECPrivateKeyUtil.parseNetworkFromWIF(wif).get == network)
|
||||
case TestNet3 | RegTest | SigNet =>
|
||||
assert(ECPrivateKeyUtil.parseNetworkFromWIF(wif).get == TestNet3)
|
||||
}
|
||||
assert(ECPrivateKeyUtil.fromWIFToPrivateKey(wif) == privKey)
|
||||
}
|
||||
}
|
||||
|
||||
it must "have serialization symmetry" in {
|
||||
forAll(CryptoGenerators.privateKey) { privKey =>
|
||||
assert(ECPrivateKey(privKey.hex) == privKey)
|
||||
@ -26,6 +95,16 @@ class ECPrivateKeyTest extends BitcoinSCryptoTest {
|
||||
}
|
||||
}
|
||||
|
||||
it must "fail to parse unknown WIF networks" in {
|
||||
// Litecoin privkey
|
||||
val wif = "6uSDaezGtedUbYk4F9CNVXbDWw9DuEuw7czU596t1CzmeAJ77P8"
|
||||
assert(ECPrivateKeyUtil.parseNetworkFromWIF(wif).isFailure)
|
||||
}
|
||||
|
||||
it must "fail to parse non-WIF strings" in {
|
||||
assert(ECPrivateKeyUtil.parseNetworkFromWIF("hello there").isFailure)
|
||||
}
|
||||
|
||||
it must "not serialize a ECPrivateKey toString" in {
|
||||
ECPrivateKey().toString must be("Masked(ECPrivateKeyImpl)")
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoins.testkitcore.gen.CryptoGenerators
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
import scodec.bits._
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
class ECPublicKeyTest extends BitcoinSCryptoTest {
|
||||
class ECPublicKeyTest extends BitcoinSUnitTest {
|
||||
|
||||
it must "be able to decompress keys" in {
|
||||
val uncompressed =
|
||||
|
@ -1,6 +1,9 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
class FieldElementTest extends BitcoinSCryptoTest {
|
||||
import org.bitcoins.testkitcore.gen.CryptoGenerators
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
|
||||
class FieldElementTest extends BitcoinSUnitTest {
|
||||
|
||||
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
|
||||
generatorDrivenConfigNewCode
|
@ -1,8 +1,10 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoins.testkitcore.gen.{CryptoGenerators, NumberGenerator}
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
import scodec.bits._
|
||||
|
||||
class HashDigestTest extends BitcoinSCryptoTest {
|
||||
class HashDigestTest extends BitcoinSUnitTest {
|
||||
behavior of "DoubleSha256Digest"
|
||||
|
||||
it must "be constructable from 32 bytes" in {
|
@ -1,7 +1,10 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoins.testkitcore.gen.CryptoGenerators
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
|
||||
/** Public key tests specific to the JVM */
|
||||
class JvmECPublicKeyTest extends BitcoinSCryptoTest {
|
||||
class JvmECPublicKeyTest extends BitcoinSUnitTest {
|
||||
|
||||
behavior of "JVMECPublicKeytest"
|
||||
|
||||
|
@ -1,6 +1,10 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
class SchnorrDigitalSignatureTest extends BitcoinSCryptoTest {
|
||||
import org.bitcoins.core.util.NumberUtil
|
||||
import org.bitcoins.testkitcore.gen.{CryptoGenerators, NumberGenerator}
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
|
||||
class SchnorrDigitalSignatureTest extends BitcoinSUnitTest {
|
||||
|
||||
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
|
||||
generatorDrivenConfigNewCode
|
||||
@ -105,8 +109,8 @@ class SchnorrDigitalSignatureTest extends BitcoinSCryptoTest {
|
||||
sig2.rx.bytes ++ privKey.schnorrPublicKey.bytes ++ message2
|
||||
val e2Bytes = CryptoUtil.sha256SchnorrChallenge(bytesToHash2).bytes
|
||||
|
||||
val e1 = CryptoNumberUtil.uintToFieldElement(e1Bytes)
|
||||
val e2 = CryptoNumberUtil.uintToFieldElement(e2Bytes)
|
||||
val e1 = NumberUtil.uintToFieldElement(e1Bytes)
|
||||
val e2 = NumberUtil.uintToFieldElement(e2Bytes)
|
||||
|
||||
val k = nonce.nonceKey.fieldElement
|
||||
val x = privKey.schnorrKey.fieldElement
|
@ -1,6 +1,9 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
class SchnorrNonceTest extends BitcoinSCryptoTest {
|
||||
import org.bitcoins.testkitcore.gen.CryptoGenerators
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
|
||||
class SchnorrNonceTest extends BitcoinSUnitTest {
|
||||
|
||||
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
|
||||
generatorDrivenConfigNewCode
|
@ -1,6 +1,9 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
class SchnorrPublicKeyTest extends BitcoinSCryptoTest {
|
||||
import org.bitcoins.testkitcore.gen.CryptoGenerators
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
|
||||
class SchnorrPublicKeyTest extends BitcoinSUnitTest {
|
||||
|
||||
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
|
||||
generatorDrivenConfigNewCode
|
@ -1,6 +1,9 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
class SignTest extends BitcoinSCryptoAsyncTest {
|
||||
import org.bitcoins.testkitcore.gen.CryptoGenerators
|
||||
import org.bitcoins.testkitcore.util.BitcoinSJvmTest
|
||||
|
||||
class SignTest extends BitcoinSJvmTest {
|
||||
|
||||
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
|
||||
generatorDrivenConfigNewCode
|
||||
@ -22,6 +25,17 @@ class SignTest extends BitcoinSCryptoAsyncTest {
|
||||
}
|
||||
}
|
||||
|
||||
it must "sign arbitrary pieces of data with arbitrary entropy correctly" in {
|
||||
forAllAsync(CryptoGenerators.sha256Digest, CryptoGenerators.sha256Digest) {
|
||||
case (hash, entropy) =>
|
||||
val sigF = privKey.signWithEntropyFunction(hash.bytes, entropy.bytes)
|
||||
|
||||
sigF.map { sig =>
|
||||
assert(pubKey.verify(hash.bytes, sig))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it must "sign arbitrary data correctly with low R values" in {
|
||||
forAllAsync(CryptoGenerators.sha256Digest) { hash =>
|
||||
val bytes = hash.bytes
|
@ -1,26 +0,0 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
class SignWithEntropyTest extends BitcoinSCryptoAsyncTest {
|
||||
|
||||
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
|
||||
generatorDrivenConfigNewCode
|
||||
|
||||
//ECPrivateKey implements the sign interface
|
||||
//so just use it for testing purposes
|
||||
val privKey: Sign = ECPrivateKey.freshPrivateKey
|
||||
val pubKey: ECPublicKey = privKey.publicKey
|
||||
|
||||
behavior of "SignWithEntropy"
|
||||
|
||||
it must "sign arbitrary pieces of data with arbitrary entropy correctly" in {
|
||||
forAllAsync(CryptoGenerators.sha256Digest, CryptoGenerators.sha256Digest) {
|
||||
case (hash, entropy) =>
|
||||
val sigF = privKey.signWithEntropyFunction(hash.bytes, entropy.bytes)
|
||||
|
||||
sigF.map { sig =>
|
||||
assert(pubKey.verify(hash.bytes, sig))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
object BIP340TestVectors {
|
||||
|
||||
val vectors: Vector[
|
||||
(
|
||||
Int,
|
||||
Option[String],
|
||||
String,
|
||||
Option[String],
|
||||
String,
|
||||
String,
|
||||
Boolean,
|
||||
String)] = Vector(
|
||||
"index,secret key,public key,aux_rand,message,signature,verification result,comment",
|
||||
"0,0000000000000000000000000000000000000000000000000000000000000003,F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9,0000000000000000000000000000000000000000000000000000000000000000,0000000000000000000000000000000000000000000000000000000000000000,E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0,TRUE,",
|
||||
"1,B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,0000000000000000000000000000000000000000000000000000000000000001,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A,TRUE,",
|
||||
"2,C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9,DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8,C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906,7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C,5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7,TRUE,",
|
||||
"3,0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710,25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3,TRUE,test fails if msg is reduced modulo p or n",
|
||||
"4,,D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9,,4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703,00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4,TRUE,",
|
||||
"5,,EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,public key not on the curve",
|
||||
"6,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2,FALSE,has_even_y(R) is false",
|
||||
"7,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD,FALSE,negated message",
|
||||
"8,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6,FALSE,negated s value",
|
||||
"9,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051,FALSE,sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 0",
|
||||
"10,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197,FALSE,sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 1",
|
||||
"11,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,sig[0:32] is not an X coordinate on the curve",
|
||||
"12,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,sig[0:32] is equal to field size",
|
||||
"13,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,FALSE,sig[32:64] is equal to curve order",
|
||||
"14,,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,public key is not a valid X coordinate because it exceeds the field size"
|
||||
).tail.map { line =>
|
||||
val testVec = line.split(",").map(_.trim)
|
||||
val index = testVec.head.toInt
|
||||
val secKeyOpt = toOpt(testVec(1))
|
||||
val pubKey = testVec(2)
|
||||
val auxRandOpt = toOpt(testVec(3))
|
||||
val msg = testVec(4)
|
||||
val sig = testVec(5)
|
||||
val result = testVec(6).toBoolean
|
||||
val comment = if (testVec.length > 7) {
|
||||
testVec(7)
|
||||
} else {
|
||||
""
|
||||
}
|
||||
(index, secKeyOpt, pubKey, auxRandOpt, msg, sig, result, comment)
|
||||
}
|
||||
|
||||
private def toOpt(str: String): Option[String] = {
|
||||
if (str.isEmpty) {
|
||||
None
|
||||
} else {
|
||||
Some(str)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.scalacheck.Gen
|
||||
import org.scalactic.anyvals.PosInt
|
||||
import org.scalatest.flatspec.{AnyFlatSpec, AsyncFlatSpec}
|
||||
import org.scalatest.matchers.must.Matchers
|
||||
import org.scalatest.{Assertion, BeforeAndAfter, BeforeAndAfterAll, Succeeded}
|
||||
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
trait BitcoinSCryptoTest
|
||||
extends AnyFlatSpec
|
||||
with BeforeAndAfter
|
||||
with BeforeAndAfterAll
|
||||
with Matchers
|
||||
with ScalaCheckPropertyChecks {
|
||||
|
||||
implicit def executionContext: ExecutionContext =
|
||||
scala.concurrent.ExecutionContext.global
|
||||
|
||||
def generatorDrivenConfigNewCode: PropertyCheckConfiguration = {
|
||||
customGenDrivenConfig(BitcoinSCryptoTest.NEW_CODE_EXECUTIONS)
|
||||
}
|
||||
|
||||
/** Sets the generator driven tests to perform the given amount of execs */
|
||||
def customGenDrivenConfig(executions: Int): PropertyCheckConfiguration = {
|
||||
PropertyCheckConfiguration(
|
||||
minSuccessful = PosInt.from(executions).get,
|
||||
minSize = PosInt.from(executions).get,
|
||||
workers = 1
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
trait BitcoinSCryptoAsyncTest
|
||||
extends AsyncFlatSpec
|
||||
with BeforeAndAfter
|
||||
with BeforeAndAfterAll
|
||||
with Matchers
|
||||
with ScalaCheckPropertyChecks {
|
||||
|
||||
implicit override def executionContext =
|
||||
scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
||||
def generatorDrivenConfigNewCode: PropertyCheckConfiguration = {
|
||||
customGenDrivenConfig(BitcoinSCryptoTest.NEW_CODE_EXECUTIONS)
|
||||
}
|
||||
|
||||
/** Sets the generator driven tests to perform the given amount of execs */
|
||||
def customGenDrivenConfig(executions: Int): PropertyCheckConfiguration = {
|
||||
PropertyCheckConfiguration(
|
||||
minSuccessful = PosInt.from(executions).get,
|
||||
minSize = PosInt.from(executions).get,
|
||||
workers = 1
|
||||
)
|
||||
}
|
||||
|
||||
def forAllAsync[A](gen: Gen[A])(
|
||||
func: A => Future[Assertion]): Future[Assertion] = {
|
||||
|
||||
val samples = 1
|
||||
.to(generatorDrivenConfig.minSize)
|
||||
.map(_ => gen.sample)
|
||||
.toVector
|
||||
.flatten
|
||||
|
||||
val testRunsF = Future.sequence(samples.map(func))
|
||||
|
||||
checkRunResults(testRunsF)
|
||||
}
|
||||
|
||||
def forAllAsync[A, B](genA: Gen[A], genB: Gen[B])(
|
||||
func: (A, B) => Future[Assertion]): Future[Assertion] = {
|
||||
|
||||
val samples = 1
|
||||
.to(generatorDrivenConfig.minSize)
|
||||
.map(_ => (genA.sample, genB.sample))
|
||||
.toVector
|
||||
.collect { case (Some(a), Some(b)) =>
|
||||
(a, b)
|
||||
}
|
||||
|
||||
val testRunsF = Future.sequence(samples.map(x => func(x._1, x._2)))
|
||||
|
||||
checkRunResults(testRunsF)
|
||||
}
|
||||
|
||||
private def checkRunResults(testRunsF: Future[Vector[Assertion]]) = {
|
||||
for {
|
||||
testRuns <- testRunsF
|
||||
} yield {
|
||||
val succeeded = testRuns.filter(_ == Succeeded)
|
||||
val failed = testRuns.filterNot(_ == Succeeded)
|
||||
if (succeeded.size < generatorDrivenConfig.minSuccessful) {
|
||||
failed.headOption.getOrElse(fail())
|
||||
} else {
|
||||
succeed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object BitcoinSCryptoTest {
|
||||
|
||||
/** The number of times new code
|
||||
* should be executed in a property based test
|
||||
*/
|
||||
val NEW_CODE_EXECUTIONS = 100
|
||||
|
||||
/** The number of times old code should be executed
|
||||
* in a property based test
|
||||
*/
|
||||
val OLD_CODE_EXECUTIONS = 20
|
||||
|
||||
}
|
@ -1,175 +0,0 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoins.crypto
|
||||
import org.scalacheck.Gen
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/** Created by chris on 6/22/16.
|
||||
*/
|
||||
sealed abstract class CryptoGenerators {
|
||||
|
||||
def privateKey: Gen[ECPrivateKey] = Gen.delay(ECPrivateKey())
|
||||
|
||||
def fieldElement: Gen[FieldElement] = privateKey.map(_.fieldElement)
|
||||
|
||||
def smallFieldElement: Gen[FieldElement] =
|
||||
NumberGenerator
|
||||
.bytevector(30)
|
||||
.map(bytes => FieldElement(ByteVector.fill(2)(0) ++ bytes))
|
||||
|
||||
def reallySmallFieldElement: Gen[FieldElement] =
|
||||
NumberGenerator
|
||||
.bytevector(15)
|
||||
.map(bytes => FieldElement(ByteVector.fill(17)(0) ++ bytes))
|
||||
|
||||
def largeFieldElement: Gen[FieldElement] =
|
||||
NumberGenerator
|
||||
.bytevector(30)
|
||||
.map(bytes => FieldElement(ByteVector.fill(2)(Byte.MinValue) ++ bytes))
|
||||
|
||||
def nonZeroFieldElement: Gen[FieldElement] =
|
||||
nonZeroPrivKey.map(_.fieldElement)
|
||||
|
||||
/** Generates a random non-zero private key */
|
||||
def nonZeroPrivKey: Gen[ECPrivateKey] =
|
||||
privateKey.filter(_.bytes.toArray.exists(_ != 0.toByte))
|
||||
|
||||
def schnorrNonce: Gen[SchnorrNonce] =
|
||||
nonZeroPrivKey.map(_.publicKey.bytes.tail).map(SchnorrNonce.fromBytes)
|
||||
|
||||
def schnorrPublicKey: Gen[SchnorrPublicKey] =
|
||||
publicKey.map(_.schnorrPublicKey)
|
||||
|
||||
/** Generate a sequence of private keys
|
||||
* @param num maximum number of keys to generate
|
||||
* @return
|
||||
*/
|
||||
def privateKeySeq(num: Int): Gen[Seq[ECPrivateKey]] =
|
||||
Gen.listOfN(num, privateKey)
|
||||
|
||||
/** Generates a sequence of private keys, and determines an amount of 'required' private keys
|
||||
* that a transaction needs to be signed with
|
||||
* @param num the maximum number of keys to generate
|
||||
* @return
|
||||
*/
|
||||
def privateKeySeqWithRequiredSigs(num: Int): Gen[(Seq[ECPrivateKey], Int)] = {
|
||||
if (num <= 0) {
|
||||
Gen.const((Nil, 0))
|
||||
} else {
|
||||
val privateKeys = privateKeySeq(num)
|
||||
for {
|
||||
keys <- privateKeys
|
||||
requiredSigs <- Gen.choose(0, keys.size - 1)
|
||||
} yield (keys, requiredSigs)
|
||||
}
|
||||
}
|
||||
|
||||
/** Generates a random number of private keys less than 15.
|
||||
* Also generates a random 'requiredSigs' number that a transaction needs to be signed with
|
||||
*/
|
||||
def privateKeySeqWithRequiredSigs: Gen[(Seq[ECPrivateKey], Int)] =
|
||||
for {
|
||||
num <- Gen.choose(0, 15)
|
||||
keysAndRequiredSigs <- privateKeySeqWithRequiredSigs(num)
|
||||
} yield keysAndRequiredSigs
|
||||
|
||||
/** A generator with 7 or less private keys -- useful for creating smaller scripts */
|
||||
def smallPrivateKeySeqWithRequiredSigs: Gen[(Seq[ECPrivateKey], Int)] =
|
||||
for {
|
||||
num <- Gen.choose(0, 7)
|
||||
keysAndRequiredSigs <- privateKeySeqWithRequiredSigs(num)
|
||||
} yield keysAndRequiredSigs
|
||||
|
||||
/** Generates a random public key */
|
||||
def publicKey: Gen[ECPublicKey] =
|
||||
for {
|
||||
privKey <- privateKey
|
||||
} yield privKey.publicKey
|
||||
|
||||
/** Generates a random digital signature */
|
||||
def digitalSignature: Gen[ECDigitalSignature] =
|
||||
for {
|
||||
privKey <- privateKey
|
||||
hash <- CryptoGenerators.doubleSha256Digest
|
||||
} yield privKey.sign(hash)
|
||||
|
||||
def schnorrDigitalSignature: Gen[SchnorrDigitalSignature] = {
|
||||
for {
|
||||
privKey <- privateKey
|
||||
hash <- CryptoGenerators.doubleSha256Digest
|
||||
} yield privKey.schnorrSign(hash.bytes)
|
||||
}
|
||||
|
||||
def adaptorSignature: Gen[ECAdaptorSignature] = {
|
||||
for {
|
||||
tweakedNonce <- publicKey
|
||||
untweakedNonce <- publicKey
|
||||
adaptedS <- fieldElement
|
||||
proofS <- fieldElement
|
||||
proofE <- fieldElement
|
||||
} yield {
|
||||
ECAdaptorSignature(tweakedNonce, adaptedS, untweakedNonce, proofS, proofE)
|
||||
}
|
||||
}
|
||||
|
||||
def sha256Digest: Gen[Sha256Digest] =
|
||||
for {
|
||||
bytes <- NumberGenerator.bytevector
|
||||
digest = CryptoUtil.sha256(bytes)
|
||||
} yield digest
|
||||
|
||||
def sha256DigestBE: Gen[Sha256DigestBE] = {
|
||||
sha256Digest.map(_.flip)
|
||||
}
|
||||
|
||||
/** Generates a random [[DoubleSha256Digest DoubleSha256Digest]] */
|
||||
def doubleSha256Digest: Gen[DoubleSha256Digest] =
|
||||
for {
|
||||
key <- privateKey
|
||||
digest = CryptoUtil.doubleSHA256(key.bytes)
|
||||
} yield digest
|
||||
|
||||
def doubleSha256DigestBE: Gen[DoubleSha256DigestBE] = {
|
||||
doubleSha256Digest.map(_.flip)
|
||||
}
|
||||
|
||||
/** Generates a sequence of [[DoubleSha256Digest DoubleSha256Digest]]
|
||||
* @param num the number of digets to generate
|
||||
* @return
|
||||
*/
|
||||
def doubleSha256DigestSeq(num: Int): Gen[Seq[DoubleSha256Digest]] =
|
||||
Gen.listOfN(num, doubleSha256Digest)
|
||||
|
||||
/** Generates a random [[Sha256Hash160Digest Sha256Hash160Digest]] */
|
||||
def sha256Hash160Digest: Gen[Sha256Hash160Digest] =
|
||||
for {
|
||||
pubKey <- publicKey
|
||||
hash = CryptoUtil.sha256Hash160(pubKey.bytes)
|
||||
} yield hash
|
||||
|
||||
def aesKey128Bit: Gen[AesKey] = Gen.delay(AesKey.get128Bit())
|
||||
def aesKey192Bit: Gen[AesKey] = Gen.delay(AesKey.get192Bit())
|
||||
def aesKey256Bit: Gen[AesKey] = Gen.delay(AesKey.get256Bit())
|
||||
|
||||
def aesKey: Gen[AesKey] =
|
||||
Gen.oneOf(aesKey128Bit, aesKey192Bit, aesKey256Bit)
|
||||
|
||||
def aesPassword: Gen[AesPassword] =
|
||||
Gen.alphaStr.suchThat(_.nonEmpty).map(AesPassword.fromNonEmptyString(_))
|
||||
|
||||
def aesIV: Gen[AesIV] = Gen.delay(AesIV.random)
|
||||
|
||||
def aesEncryptedData: Gen[AesEncryptedData] =
|
||||
for {
|
||||
cipher <- NumberGenerator.bytevector.suchThat(_.nonEmpty)
|
||||
iv <- aesIV
|
||||
} yield crypto.AesEncryptedData(cipherText = cipher, iv)
|
||||
|
||||
def genKey: Gen[SipHashKey] =
|
||||
Gen
|
||||
.listOfN(16, NumberGenerator.byte)
|
||||
.map(ByteVector(_))
|
||||
.map(SipHashKey(_))
|
||||
}
|
||||
|
||||
object CryptoGenerators extends CryptoGenerators
|
@ -1,11 +1,19 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoins.core.util.BytesUtil
|
||||
import org.bitcoins.testkitcore.gen.{CryptoGenerators, NumberGenerator}
|
||||
import org.scalacheck.Gen
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.must.Matchers
|
||||
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
|
||||
import scodec.bits._
|
||||
|
||||
/** Created by chris on 1/26/16.
|
||||
*/
|
||||
class CryptoUtilTest extends BitcoinSCryptoTest {
|
||||
class CryptoUtilTest
|
||||
extends AnyFlatSpec
|
||||
with Matchers
|
||||
with ScalaCheckPropertyChecks {
|
||||
|
||||
"CryptoUtil" must "perform a SHA-1 hash" in {
|
||||
val hash = CryptoUtil.sha1(hex"")
|
||||
@ -41,7 +49,7 @@ class CryptoUtilTest extends BitcoinSCryptoTest {
|
||||
|
||||
it must "perform a single SHA256 hash on a bit vector" in {
|
||||
val binary = bin"010001101110010001101110"
|
||||
val strBytes = CryptoBytesUtil.decodeHex(binary.toHex)
|
||||
val strBytes = BytesUtil.decodeHex(binary.toHex)
|
||||
|
||||
val shaStrBytes = CryptoUtil.sha256(strBytes)
|
||||
val shaBinary = CryptoUtil.sha256(binary)
|
||||
|
@ -1,84 +0,0 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.scalacheck.Arbitrary.arbitrary
|
||||
import org.scalacheck.Gen
|
||||
import scodec.bits.{BitVector, ByteVector}
|
||||
|
||||
/** Created by chris on 6/16/16.
|
||||
*/
|
||||
trait NumberGenerator {
|
||||
|
||||
def positiveShort: Gen[Short] = {
|
||||
Gen.chooseNum[Short](0, Short.MaxValue)
|
||||
}
|
||||
|
||||
/** Creates a generator that generates positive long numbers */
|
||||
def positiveLongs: Gen[Long] = Gen.choose(0, Long.MaxValue)
|
||||
|
||||
/** Integers between 0 and Int.MaxValue
|
||||
*/
|
||||
val positiveInts: Gen[Int] = Gen.choose(0, Int.MaxValue)
|
||||
|
||||
/** Integers between Int.MinValue and -1
|
||||
*/
|
||||
val negativeInts: Gen[Int] = Gen.choose(Int.MinValue, -1)
|
||||
|
||||
/** Random integers
|
||||
*/
|
||||
val ints: Gen[Int] = Gen.choose(Int.MinValue, Int.MaxValue)
|
||||
|
||||
/** Creates a generator for positive longs without the number zero */
|
||||
def positiveLongsNoZero: Gen[Long] = Gen.choose(1, Long.MaxValue)
|
||||
|
||||
/** Creates a number generator that generates negative long numbers */
|
||||
def negativeLongs: Gen[Long] = Gen.choose(Long.MinValue, -1)
|
||||
|
||||
/** Chooses a BigInt in the ranges of 0 <= bigInt < 2^^64 */
|
||||
def bigInts: Gen[BigInt] =
|
||||
Gen
|
||||
.chooseNum(Long.MinValue, Long.MaxValue)
|
||||
.map(x => BigInt(x) + BigInt(2).pow(63))
|
||||
|
||||
def positiveBigInts: Gen[BigInt] = bigInts.filter(_ >= 0)
|
||||
|
||||
def bigIntsUInt64Range: Gen[BigInt] =
|
||||
positiveBigInts.filter(_ < (BigInt(1) << 64))
|
||||
|
||||
/** Generates an arbitrary [[scala.Byte Byte]] in Scala */
|
||||
def byte: Gen[Byte] = arbitrary[Byte]
|
||||
|
||||
/** Generates an arbitrary [[scodec.bits.ByteVector ByteVector]] */
|
||||
def bytevector: Gen[ByteVector] = Gen.listOf(byte).map(ByteVector(_))
|
||||
|
||||
def bytevector(length: Int): Gen[ByteVector] =
|
||||
Gen.listOfN(length, byte).map(ByteVector(_))
|
||||
|
||||
/** Generates a 100 byte sequence */
|
||||
def bytes: Gen[List[Byte]] =
|
||||
for {
|
||||
num <- Gen.choose(0, 100)
|
||||
b <- bytes(num)
|
||||
} yield b
|
||||
|
||||
/** Generates the number of bytes specified by num
|
||||
* @param num
|
||||
* @return
|
||||
*/
|
||||
def bytes(num: Int): Gen[List[Byte]] = Gen.listOfN(num, byte)
|
||||
|
||||
/** Generates a random boolean */
|
||||
def bool: Gen[Boolean] =
|
||||
for {
|
||||
num <- Gen.choose(0, 1)
|
||||
} yield num == 1
|
||||
|
||||
/** Generates a bit vector */
|
||||
def bitVector: Gen[BitVector] =
|
||||
for {
|
||||
n <- Gen.choose(0, 100)
|
||||
vector <- Gen.listOfN(n, bool)
|
||||
} yield BitVector.bits(vector)
|
||||
|
||||
}
|
||||
|
||||
object NumberGenerator extends NumberGenerator
|
@ -4,9 +4,9 @@ import scodec.bits.ByteVector
|
||||
|
||||
import java.math.BigInteger
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.JSStringOps._
|
||||
import scala.scalajs.js.typedarray._
|
||||
import scala.scalajs.js.{JavaScriptException, UnicodeNormalizationForm}
|
||||
import scala.scalajs.js.JSStringOps._
|
||||
import scala.scalajs.js.UnicodeNormalizationForm
|
||||
|
||||
/** This is an implementation of [[CryptoRuntime]] that defaults to
|
||||
* Bcrypto (https://github.com/bcoin-org/bcrypto) when possible.
|
||||
@ -23,9 +23,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
|
||||
private lazy val sha1 = new SHA1
|
||||
private lazy val sha256 = SHA256Factory.create()
|
||||
private lazy val hmac = SHA512.hmac.apply().asInstanceOf[HMAC]
|
||||
|
||||
private lazy val ecdsa =
|
||||
new ECDSA("SECP256K1", sha256, js.constructorOf[SHA256], null)
|
||||
private lazy val ecdsa = new ECDSA("SECP256K1", sha256, sha256, null)
|
||||
|
||||
private lazy val randomBytesFunc: Int => ByteVector =
|
||||
try {
|
||||
@ -151,57 +149,49 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
|
||||
|
||||
override def tweakMultiply(
|
||||
publicKey: ECPublicKey,
|
||||
tweak: FieldElement): ECPublicKey = {
|
||||
val keyBuffer = ecdsa.publicKeyTweakMul(publicKey.bytes, tweak.bytes, true)
|
||||
ECPublicKey.fromBytes(keyBuffer)
|
||||
}
|
||||
tweak: FieldElement): ECPublicKey = ???
|
||||
|
||||
def publicKeyConvert(buffer: ByteVector, compressed: Boolean): ByteVector =
|
||||
publicKeyConvert(toNodeBuffer(buffer), compressed)
|
||||
override def add(pk1: ECPrivateKey, pk2: ECPrivateKey): ECPrivateKey = ???
|
||||
|
||||
def publicKeyConvert(buffer: Buffer, compressed: Boolean): Buffer =
|
||||
ecdsa.publicKeyConvert(buffer, compressed)
|
||||
override def add(bytes: ByteVector, pk2: ECPrivateKey): ByteVector = ???
|
||||
|
||||
override def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey = {
|
||||
try {
|
||||
val keyBuffer =
|
||||
ecdsa.publicKeyCombine(js.Array(pk1.bytes, pk2.bytes), true)
|
||||
ECPublicKey.fromBytes(keyBuffer)
|
||||
} catch {
|
||||
case ex: JavaScriptException =>
|
||||
// check for infinity
|
||||
val k1: Buffer =
|
||||
if (pk1.isCompressed) pk1.bytes
|
||||
else publicKeyConvert(pk1.bytes, compressed = true)
|
||||
|
||||
val k2: Buffer =
|
||||
if (pk2.isCompressed) pk2.bytes
|
||||
else publicKeyConvert(pk2.bytes, compressed = true)
|
||||
|
||||
if (
|
||||
((k1.head == 0x02 && k2.head == 0x03) ||
|
||||
(k1.head == 0x03 && k2.head == 0x02)) &&
|
||||
k1.tail == k2.tail
|
||||
) {
|
||||
ECPublicKey.fromHex("00")
|
||||
} else {
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
}
|
||||
override def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey = ???
|
||||
|
||||
override def pubKeyTweakAdd(
|
||||
pubkey: ECPublicKey,
|
||||
privkey: ECPrivateKey): ECPublicKey = {
|
||||
val keyBuffer = ecdsa.publicKeyTweakAdd(pubkey.bytes, privkey.bytes, true)
|
||||
ECPublicKey.fromBytes(keyBuffer)
|
||||
}
|
||||
privkey: ECPrivateKey): ECPublicKey = ???
|
||||
|
||||
override def isValidPubKey(bytes: ByteVector): Boolean =
|
||||
ecdsa.publicKeyVerify(bytes)
|
||||
|
||||
override def isFullyValidWithBouncyCastle(bytes: ByteVector): Boolean = ???
|
||||
|
||||
override def schnorrSign(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
auxRand: ByteVector): SchnorrDigitalSignature = {
|
||||
val buffer = ecdsa.schnorrSign(dataToSign, privateKey.bytes) //, auxRand)
|
||||
SchnorrDigitalSignature.fromBytes(buffer)
|
||||
}
|
||||
|
||||
override def schnorrSignWithNonce(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
nonceKey: ECPrivateKey): SchnorrDigitalSignature = ???
|
||||
|
||||
override def schnorrVerify(
|
||||
data: ByteVector,
|
||||
schnorrPubKey: SchnorrPublicKey,
|
||||
signature: SchnorrDigitalSignature): Boolean = {
|
||||
ecdsa.schnorrVerify(data, signature.bytes, schnorrPubKey.bytes)
|
||||
}
|
||||
|
||||
override def schnorrComputeSigPoint(
|
||||
data: ByteVector,
|
||||
nonce: SchnorrNonce,
|
||||
pubKey: SchnorrPublicKey,
|
||||
compressed: Boolean): ECPublicKey = ???
|
||||
|
||||
override def adaptorSign(
|
||||
key: ECPrivateKey,
|
||||
adaptorPoint: ECPublicKey,
|
||||
@ -262,22 +252,6 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
|
||||
s"Need $len bytes for buffer -> bytevector conversion")
|
||||
ByteVector(accum.map(_.toByte))
|
||||
}
|
||||
|
||||
override def decodePoint(bytes: ByteVector): ECPoint = {
|
||||
if (bytes.size == 1 && bytes(0) == 0x00) {
|
||||
ECPointInfinity
|
||||
} else {
|
||||
val decoded = ecdsa.curve
|
||||
.applyDynamic("decodePoint")(toNodeBuffer(bytes))
|
||||
.asInstanceOf[Point]
|
||||
|
||||
if (decoded.isInfinity())
|
||||
ECPointInfinity
|
||||
else
|
||||
ECPoint(new BigInteger(decoded.getX().toString()),
|
||||
new BigInteger(decoded.getY().toString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object BCryptoCryptoRuntime extends BCryptoCryptoRuntime
|
||||
|
@ -11,7 +11,7 @@ import scala.scalajs.js.annotation._
|
||||
class ECDSA(
|
||||
name: String = "SECP256K1",
|
||||
hash: SHA256 = new SHA256,
|
||||
xof: js.Dynamic = js.constructorOf[SHA256],
|
||||
xof: SHA256 = new SHA256,
|
||||
pre: String = null)
|
||||
extends js.Object {
|
||||
|
||||
@ -19,24 +19,10 @@ class ECDSA(
|
||||
|
||||
def privateKeyVerify(key: Buffer): Boolean = js.native
|
||||
|
||||
def privateKeyTweakMul(key: Buffer, tweak: Buffer): Buffer =
|
||||
js.native
|
||||
|
||||
def publicKeyCreate(key: Buffer, compressed: Boolean): Buffer = js.native
|
||||
|
||||
def publicKeyVerify(key: Buffer): Boolean = js.native
|
||||
|
||||
def publicKeyConvert(key: Buffer, compressed: Boolean): Buffer = js.native
|
||||
|
||||
def publicKeyTweakMul(key: Buffer, tweak: Buffer, compress: Boolean): Buffer =
|
||||
js.native
|
||||
|
||||
def publicKeyTweakAdd(key: Buffer, tweak: Buffer, compress: Boolean): Buffer =
|
||||
js.native
|
||||
|
||||
def publicKeyCombine(keys: js.Array[Buffer], compress: Boolean): Buffer =
|
||||
js.native
|
||||
|
||||
def sign(msg: Buffer, key: Buffer): Buffer = js.native
|
||||
|
||||
def verify(msg: Buffer, sig: Buffer, key: Buffer): Boolean = js.native
|
||||
@ -47,5 +33,10 @@ class ECDSA(
|
||||
param: Byte,
|
||||
compress: Boolean): Buffer = js.native
|
||||
|
||||
val curve: js.Dynamic = js.native
|
||||
var schnorr: Schnorr = js.native
|
||||
|
||||
def schnorrSign(msg: Buffer, key: Buffer): Buffer = js.native
|
||||
|
||||
def schnorrVerify(msg: Buffer, sig: Buffer, key: Buffer): Boolean =
|
||||
js.native
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.annotation.JSImport
|
||||
|
||||
@js.native
|
||||
@JSImport("bcrypto/lib/js/elliptic.js", JSImport.Default)
|
||||
class Point extends js.Object {
|
||||
def getX(): js.BigInt = js.native
|
||||
def getY(): js.BigInt = js.native
|
||||
def isInfinity(): Boolean = js.native
|
||||
}
|
@ -6,7 +6,7 @@ import org.bouncycastle.crypto.params.{
|
||||
ECPublicKeyParameters
|
||||
}
|
||||
import org.bouncycastle.crypto.signers.{ECDSASigner, HMacDSAKCalculator}
|
||||
import org.bouncycastle.math.ec.ECCurve
|
||||
import org.bouncycastle.math.ec.{ECCurve, ECPoint}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import java.math.BigInteger
|
||||
@ -15,7 +15,7 @@ import scala.util.Try
|
||||
object BouncyCastleUtil {
|
||||
|
||||
private val curve: ECCurve = BouncyCastleCryptoParams.curve.getCurve
|
||||
private val G = BouncyCastleCryptoParams.curve.getG
|
||||
private val G: ECPoint = BouncyCastleCryptoParams.curve.getG
|
||||
|
||||
private def getBigInteger(bytes: ByteVector): BigInteger = {
|
||||
new BigInteger(1, bytes.toArray)
|
||||
@ -26,16 +26,16 @@ object BouncyCastleUtil {
|
||||
decodePubKey(point, publicKey.isCompressed)
|
||||
}
|
||||
|
||||
def decodePoint(bytes: ByteVector): org.bouncycastle.math.ec.ECPoint = {
|
||||
def decodePoint(bytes: ByteVector): ECPoint = {
|
||||
curve.decodePoint(bytes.toArray)
|
||||
}
|
||||
|
||||
def decodePoint(pubKey: ECPublicKey): org.bouncycastle.math.ec.ECPoint = {
|
||||
def decodePoint(pubKey: ECPublicKey): ECPoint = {
|
||||
decodePoint(pubKey.bytes)
|
||||
}
|
||||
|
||||
def decodePubKey(
|
||||
point: org.bouncycastle.math.ec.ECPoint,
|
||||
point: ECPoint,
|
||||
isCompressed: Boolean = true): ECPublicKey = {
|
||||
val bytes = point.getEncoded(isCompressed)
|
||||
ECPublicKey.fromBytes(ByteVector(bytes))
|
||||
|
@ -1,6 +1,5 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoins.crypto
|
||||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair
|
||||
import org.bouncycastle.crypto.digests.{RIPEMD160Digest, SHA512Digest}
|
||||
import org.bouncycastle.crypto.generators.ECKeyPairGenerator
|
||||
@ -10,10 +9,12 @@ import org.bouncycastle.crypto.params.{
|
||||
ECPrivateKeyParameters,
|
||||
KeyParameter
|
||||
}
|
||||
import org.bouncycastle.math.ec.ECPoint
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import java.math.BigInteger
|
||||
import java.security.{MessageDigest, SecureRandom}
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
/** This is an implementation of [[CryptoRuntime]] that defaults to Bouncy Castle (https://bouncycastle.org/)
|
||||
* and [[java.security]].
|
||||
@ -41,9 +42,7 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
|
||||
* @return a tuple (p1, p2) where p1 and p2 are points on the curve and p1.x = p2.x = x
|
||||
* p1.y is even, p2.y is odd
|
||||
*/
|
||||
def recoverPoint(x: BigInteger): (
|
||||
org.bouncycastle.math.ec.ECPoint,
|
||||
org.bouncycastle.math.ec.ECPoint) = {
|
||||
def recoverPoint(x: BigInteger): (ECPoint, ECPoint) = {
|
||||
val bytes = ByteVector(x.toByteArray)
|
||||
|
||||
val bytes32 = if (bytes.length < 32) {
|
||||
@ -151,6 +150,9 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
|
||||
signature: ECDigitalSignature): Boolean =
|
||||
BouncyCastleUtil.verifyDigitalSignature(data, publicKey, signature)
|
||||
|
||||
override def decompressed(publicKey: ECPublicKey): ECPublicKey =
|
||||
BouncyCastleUtil.decompressPublicKey(publicKey)
|
||||
|
||||
override def publicKey(privateKey: ECPrivateKey): ECPublicKey =
|
||||
BouncyCastleUtil.computePublicKey(privateKey)
|
||||
|
||||
@ -159,10 +161,18 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
|
||||
tweak: FieldElement): ECPublicKey =
|
||||
BouncyCastleUtil.pubKeyTweakMul(publicKey, tweak.bytes)
|
||||
|
||||
override def add(pk1: ECPrivateKey, pk2: ECPrivateKey): ECPrivateKey =
|
||||
pk1.fieldElement.add(pk2.fieldElement).toPrivateKey
|
||||
|
||||
override def add(pk1: ByteVector, pk2: ECPrivateKey): ByteVector = {
|
||||
val sum = pk2.fieldElement.add(FieldElement(pk1))
|
||||
sum.bytes
|
||||
}
|
||||
|
||||
override def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey = {
|
||||
val p1 = BouncyCastleUtil.decodePoint(pk1)
|
||||
val p2 = BouncyCastleUtil.decodePoint(pk2)
|
||||
val sumPoint = p1.add(p2)
|
||||
val sumPoint =
|
||||
BouncyCastleUtil.decodePoint(pk1).add(BouncyCastleUtil.decodePoint(pk2))
|
||||
|
||||
BouncyCastleUtil.decodePubKey(sumPoint)
|
||||
}
|
||||
|
||||
@ -179,6 +189,77 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
|
||||
override def isFullyValidWithBouncyCastle(bytes: ByteVector): Boolean =
|
||||
bytes.nonEmpty && isValidPubKey(bytes)
|
||||
|
||||
override def schnorrSign(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
auxRand: ByteVector): SchnorrDigitalSignature = {
|
||||
val nonceKey =
|
||||
SchnorrNonce.kFromBipSchnorr(privateKey, dataToSign, auxRand)
|
||||
|
||||
schnorrSignWithNonce(dataToSign, privateKey, nonceKey)
|
||||
}
|
||||
|
||||
override def schnorrSignWithNonce(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
nonceKey: ECPrivateKey): SchnorrDigitalSignature = {
|
||||
val rx = nonceKey.schnorrNonce
|
||||
val k = nonceKey.nonceKey.fieldElement
|
||||
val x = privateKey.schnorrKey.fieldElement
|
||||
val e = sha256SchnorrChallenge(
|
||||
rx.bytes ++ privateKey.schnorrPublicKey.bytes ++ dataToSign).bytes
|
||||
|
||||
val challenge = x.multiply(FieldElement(e))
|
||||
val sig = k.add(challenge)
|
||||
|
||||
SchnorrDigitalSignature(rx, sig)
|
||||
}
|
||||
|
||||
override def schnorrVerify(
|
||||
data: ByteVector,
|
||||
schnorrPubKey: SchnorrPublicKey,
|
||||
signature: SchnorrDigitalSignature): Boolean = {
|
||||
val rx = signature.rx
|
||||
val sT = Try(signature.sig.toPrivateKey)
|
||||
|
||||
sT match {
|
||||
case Success(s) =>
|
||||
val eBytes = sha256SchnorrChallenge(
|
||||
rx.bytes ++ schnorrPubKey.bytes ++ data).bytes
|
||||
|
||||
val e = FieldElement(eBytes)
|
||||
val negE = e.negate
|
||||
|
||||
val sigPoint = s.publicKey
|
||||
val challengePoint = schnorrPubKey.publicKey.tweakMultiply(negE)
|
||||
val computedR = challengePoint.add(sigPoint)
|
||||
val yCoord = BouncyCastleUtil.decodePoint(computedR).getRawYCoord
|
||||
|
||||
yCoord != null && !yCoord.testBitZero() && computedR.schnorrNonce == rx
|
||||
case Failure(_) => false
|
||||
}
|
||||
}
|
||||
|
||||
override def schnorrComputeSigPoint(
|
||||
data: ByteVector,
|
||||
nonce: SchnorrNonce,
|
||||
pubKey: SchnorrPublicKey,
|
||||
compressed: Boolean): ECPublicKey = {
|
||||
val eBytes = sha256SchnorrChallenge(
|
||||
nonce.bytes ++ pubKey.bytes ++ data).bytes
|
||||
|
||||
val e = FieldElement(eBytes)
|
||||
|
||||
val compressedSigPoint =
|
||||
nonce.publicKey.add(pubKey.publicKey.tweakMultiply(e))
|
||||
|
||||
if (compressed) {
|
||||
compressedSigPoint
|
||||
} else {
|
||||
compressedSigPoint.decompressed
|
||||
}
|
||||
}
|
||||
|
||||
override def adaptorSign(
|
||||
key: ECPrivateKey,
|
||||
adaptorPoint: ECPublicKey,
|
||||
@ -230,15 +311,6 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
|
||||
sh.doFinal()
|
||||
}
|
||||
|
||||
override def decodePoint(bytes: ByteVector): crypto.ECPoint = {
|
||||
val decoded = BouncyCastleUtil.decodePoint(bytes)
|
||||
|
||||
if (decoded.isInfinity)
|
||||
crypto.ECPointInfinity
|
||||
else
|
||||
crypto.ECPoint(decoded.getRawXCoord.getEncoded,
|
||||
decoded.getRawYCoord.getEncoded)
|
||||
}
|
||||
}
|
||||
|
||||
object BouncycastleCryptoRuntime extends BouncycastleCryptoRuntime
|
||||
|
@ -274,9 +274,6 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
|
||||
|
||||
override def sipHash(item: ByteVector, key: SipHashKey): Long =
|
||||
BouncycastleCryptoRuntime.sipHash(item, key)
|
||||
|
||||
override def decodePoint(bytes: ByteVector): ECPoint =
|
||||
BouncycastleCryptoRuntime.decodePoint(bytes)
|
||||
}
|
||||
|
||||
object LibSecp256k1CryptoRuntime extends LibSecp256k1CryptoRuntime
|
||||
|
@ -1,49 +0,0 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import java.math.BigInteger
|
||||
import scala.math.BigInt
|
||||
|
||||
trait CryptoNumberUtil {
|
||||
|
||||
/** Converts a sequence of bytes to a **big endian** unsigned integer */
|
||||
def toUnsignedInt(bytes: ByteVector): BigInt = {
|
||||
toUnsignedInt(bytes.toArray)
|
||||
}
|
||||
|
||||
/** Converts a sequence of bytes to a **big endian** unsigned integer */
|
||||
def toUnsignedInt(bytes: Array[Byte]): BigInt = {
|
||||
BigInt(new BigInteger(1, bytes))
|
||||
}
|
||||
|
||||
def uintToFieldElement(bytes: ByteVector): FieldElement = {
|
||||
FieldElement(toUnsignedInt(bytes))
|
||||
}
|
||||
|
||||
/** Takes a hex string and parses it to a [[scala.math.BigInt BigInt]]. */
|
||||
def toBigInt(hex: String): BigInt = toBigInt(CryptoBytesUtil.decodeHex(hex))
|
||||
|
||||
/** Converts a sequence of bytes to twos complement signed number. */
|
||||
def toBigInt(bytes: ByteVector): BigInt = {
|
||||
//BigInt interprets the number as an unsigned number then applies the given
|
||||
//sign in front of that number, therefore if we have a negative number we need to invert it
|
||||
//since twos complement is an inverted number representation for negative numbers
|
||||
//see [[https://en.wikipedia.org/wiki/Two%27s_complement]]
|
||||
if (bytes.isEmpty) BigInt(0)
|
||||
//check if sign bit is set
|
||||
else if ((0x80.toByte & bytes.head) != 0) {
|
||||
val invertedBytes = bytes.tail.map(b => (b ^ 0xff.toByte).toByte)
|
||||
val firstByteInverted = (bytes.head ^ 0xff.toByte).toByte
|
||||
val num = firstByteInverted +: invertedBytes
|
||||
BigInt(-1, num.toArray) - 1
|
||||
} else {
|
||||
val firstBitOff = (0x7f & bytes.head).toByte
|
||||
val num = firstBitOff +: bytes.tail
|
||||
BigInt(num.toArray)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object CryptoNumberUtil extends CryptoNumberUtil
|
@ -2,8 +2,6 @@ package org.bitcoins.crypto
|
||||
|
||||
import scodec.bits.{BitVector, ByteVector}
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
/** Trait that should be extended by specific runtimes like javascript
|
||||
* or the JVM to support crypto functions needed for bitcoin-s
|
||||
*/
|
||||
@ -148,29 +146,13 @@ trait CryptoRuntime {
|
||||
data: ByteVector,
|
||||
signature: ECDigitalSignature): Boolean
|
||||
|
||||
def decompressed(publicKey: ECPublicKey): ECPublicKey = {
|
||||
if (publicKey.isCompressed) {
|
||||
decodePoint(publicKey.bytes) match {
|
||||
case ECPointInfinity => ECPublicKey.fromHex("00")
|
||||
case point: ECPointImpl =>
|
||||
val decompressedBytes =
|
||||
ByteVector.fromHex("04").get ++
|
||||
point.x.bytes ++
|
||||
point.y.bytes
|
||||
ECPublicKey(decompressedBytes)
|
||||
}
|
||||
} else publicKey
|
||||
}
|
||||
def decompressed(publicKey: ECPublicKey): ECPublicKey
|
||||
|
||||
def tweakMultiply(publicKey: ECPublicKey, tweak: FieldElement): ECPublicKey
|
||||
|
||||
def add(pk1: ECPrivateKey, pk2: ECPrivateKey): ECPrivateKey =
|
||||
pk1.fieldElement.add(pk2.fieldElement).toPrivateKey
|
||||
def add(pk1: ECPrivateKey, pk2: ECPrivateKey): ECPrivateKey
|
||||
|
||||
def add(pk1: ByteVector, pk2: ECPrivateKey): ByteVector = {
|
||||
val sum = pk2.fieldElement.add(FieldElement(pk1))
|
||||
sum.bytes
|
||||
}
|
||||
def add(bytes: ByteVector, pk2: ECPrivateKey): ByteVector
|
||||
|
||||
def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey
|
||||
|
||||
@ -180,84 +162,26 @@ trait CryptoRuntime {
|
||||
|
||||
def isFullyValidWithBouncyCastle(bytes: ByteVector): Boolean
|
||||
|
||||
def decodePoint(bytes: ByteVector): ECPoint
|
||||
|
||||
def decodePoint(pubKey: ECPublicKey): ECPoint = {
|
||||
decodePoint(pubKey.bytes)
|
||||
}
|
||||
|
||||
def schnorrSign(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
auxRand: ByteVector): SchnorrDigitalSignature = {
|
||||
val nonceKey =
|
||||
SchnorrNonce.kFromBipSchnorr(privateKey, dataToSign, auxRand)
|
||||
|
||||
schnorrSignWithNonce(dataToSign, privateKey, nonceKey)
|
||||
}
|
||||
auxRand: ByteVector): SchnorrDigitalSignature
|
||||
|
||||
def schnorrSignWithNonce(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
nonceKey: ECPrivateKey): SchnorrDigitalSignature = {
|
||||
val rx = nonceKey.schnorrNonce
|
||||
val k = nonceKey.nonceKey.fieldElement
|
||||
val x = privateKey.schnorrKey.fieldElement
|
||||
val e = sha256SchnorrChallenge(
|
||||
rx.bytes ++ privateKey.schnorrPublicKey.bytes ++ dataToSign).bytes
|
||||
|
||||
val challenge = x.multiply(FieldElement(e))
|
||||
val sig = k.add(challenge)
|
||||
|
||||
SchnorrDigitalSignature(rx, sig)
|
||||
}
|
||||
nonceKey: ECPrivateKey): SchnorrDigitalSignature
|
||||
|
||||
def schnorrVerify(
|
||||
data: ByteVector,
|
||||
schnorrPubKey: SchnorrPublicKey,
|
||||
signature: SchnorrDigitalSignature): Boolean = {
|
||||
val rx = signature.rx
|
||||
val sT = Try(signature.sig.toPrivateKey)
|
||||
|
||||
sT match {
|
||||
case Success(s) =>
|
||||
val eBytes = sha256SchnorrChallenge(
|
||||
rx.bytes ++ schnorrPubKey.bytes ++ data).bytes
|
||||
|
||||
val e = FieldElement(eBytes)
|
||||
val negE = e.negate
|
||||
|
||||
val sigPoint = s.publicKey
|
||||
val challengePoint = schnorrPubKey.publicKey.tweakMultiply(negE)
|
||||
val computedR = challengePoint.add(sigPoint)
|
||||
decodePoint(computedR) match {
|
||||
case ECPointInfinity => false
|
||||
case ECPointImpl(_, yCoord) =>
|
||||
!yCoord.toBigInteger.testBit(0) && computedR.schnorrNonce == rx
|
||||
}
|
||||
case Failure(_) => false
|
||||
}
|
||||
}
|
||||
signature: SchnorrDigitalSignature): Boolean
|
||||
|
||||
def schnorrComputeSigPoint(
|
||||
data: ByteVector,
|
||||
nonce: SchnorrNonce,
|
||||
pubKey: SchnorrPublicKey,
|
||||
compressed: Boolean): ECPublicKey = {
|
||||
val eBytes = sha256SchnorrChallenge(
|
||||
nonce.bytes ++ pubKey.bytes ++ data).bytes
|
||||
|
||||
val e = FieldElement(eBytes)
|
||||
|
||||
val compressedSigPoint =
|
||||
nonce.publicKey.add(pubKey.publicKey.tweakMultiply(e))
|
||||
|
||||
if (compressed) {
|
||||
compressedSigPoint
|
||||
} else {
|
||||
compressedSigPoint.decompressed
|
||||
}
|
||||
}
|
||||
compressed: Boolean): ECPublicKey
|
||||
|
||||
def adaptorSign(
|
||||
key: ECPrivateKey,
|
||||
|
@ -195,9 +195,6 @@ trait CryptoUtil extends CryptoRuntime {
|
||||
|
||||
override def sipHash(item: ByteVector, key: SipHashKey): Long =
|
||||
cryptoRuntime.sipHash(item, key)
|
||||
|
||||
override def decodePoint(bytes: ByteVector): ECPoint =
|
||||
cryptoRuntime.decodePoint(bytes)
|
||||
}
|
||||
|
||||
object CryptoUtil extends CryptoUtil
|
||||
|
@ -1,37 +0,0 @@
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import java.math.BigInteger
|
||||
|
||||
/** Represents a point on an elliptic curve.
|
||||
*/
|
||||
sealed trait ECPoint
|
||||
|
||||
/** The infinity point.
|
||||
*/
|
||||
case object ECPointInfinity extends ECPoint
|
||||
|
||||
/** A point on an elliptic curve.
|
||||
* @param x
|
||||
* @param y
|
||||
*/
|
||||
case class ECPointImpl(x: FieldElement, y: FieldElement) extends ECPoint
|
||||
|
||||
object ECPoint {
|
||||
|
||||
def apply(x: ByteVector, y: ByteVector): ECPoint =
|
||||
ECPointImpl(FieldElement.fromBytes(x), FieldElement.fromBytes(y))
|
||||
|
||||
def apply(x: Array[Byte], y: Array[Byte]): ECPoint =
|
||||
ECPointImpl(FieldElement.fromByteArray(x), FieldElement.fromByteArray(y))
|
||||
|
||||
def apply(x: BigInteger, y: BigInteger): ECPoint =
|
||||
ECPointImpl(FieldElement(x), FieldElement(y))
|
||||
|
||||
def apply(x: BigInt, y: BigInt): ECPoint =
|
||||
ECPointImpl(FieldElement(x), FieldElement(y))
|
||||
|
||||
def apply(x: String, y: String): ECPoint =
|
||||
ECPointImpl(FieldElement.fromHex(x), FieldElement.fromHex(y))
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
import Deps.{Compile, Test}
|
||||
import sbt._
|
||||
import org.portablescala.sbtplatformdeps.PlatformDepsPlugin.autoImport._
|
||||
|
||||
@ -270,7 +269,8 @@ object Deps {
|
||||
def crypto: Def.Initialize[Seq[ModuleID]] = {
|
||||
Def.setting {
|
||||
List(
|
||||
Compile.scodec.value
|
||||
Compile.scodec.value,
|
||||
Test.scalaTest.value
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -294,8 +294,7 @@ object Deps {
|
||||
def cryptoTest = Def.setting {
|
||||
List(
|
||||
Test.scalaTest.value,
|
||||
Test.scalacheck.value,
|
||||
Compile.scalaTestPlus.value
|
||||
Test.scalacheck.value
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -6,22 +6,22 @@ import org.bitcoins.core.protocol.blockchain.ChainParams
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.scalacheck.{Gen, Shrink}
|
||||
import org.scalactic.anyvals.PosInt
|
||||
import org.scalatest.concurrent.AsyncTimeLimitedTests
|
||||
import org.scalatest.flatspec.AsyncFlatSpec
|
||||
import org.scalatest.matchers.must.Matchers
|
||||
import org.scalatest.time.Span
|
||||
import org.scalatest.{
|
||||
Assertion,
|
||||
AsyncTestSuite,
|
||||
BeforeAndAfter,
|
||||
BeforeAndAfterAll
|
||||
}
|
||||
import org.scalatest.concurrent.AsyncTimeLimitedTests
|
||||
import org.scalatest.flatspec.AsyncFlatSpec
|
||||
import org.scalatest.matchers.must.Matchers
|
||||
import org.scalatest.time.Span
|
||||
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
|
||||
|
||||
import scala.annotation.nowarn
|
||||
import scala.collection.JavaConverters.iterableAsScalaIterableConverter
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/** This is a base trait in bitcoin-s for async tests
|
||||
*/
|
||||
@ -30,7 +30,8 @@ trait BaseAsyncTest
|
||||
with BeforeAndAfterAll
|
||||
with Matchers
|
||||
with ScalaCheckPropertyChecks
|
||||
with AsyncTimeLimitedTests { this: AsyncTestSuite =>
|
||||
with AsyncTimeLimitedTests
|
||||
with BitcoinSLogger { this: AsyncTestSuite =>
|
||||
|
||||
implicit def np: NetworkParameters = RegTest
|
||||
|
||||
@ -290,10 +291,7 @@ trait BaseAsyncTest
|
||||
* uses the default scala execution context to run
|
||||
* the tests on
|
||||
*/
|
||||
trait BitcoinSJvmTest
|
||||
extends AsyncFlatSpec
|
||||
with BaseAsyncTest
|
||||
with BitcoinSLogger {
|
||||
trait BitcoinSJvmTest extends AsyncFlatSpec with BaseAsyncTest {
|
||||
|
||||
implicit override def executionContext: ExecutionContext =
|
||||
scala.concurrent.ExecutionContext.global
|
||||
|
@ -14,7 +14,6 @@ import org.bitcoins.core.gcs.FilterHeader
|
||||
import org.bitcoins.core.p2p.CompactFilterMessage
|
||||
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
||||
import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp}
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.crypto.DoubleSha256DigestBE
|
||||
import org.bitcoins.db.AppConfig
|
||||
import org.bitcoins.node._
|
||||
@ -56,7 +55,7 @@ import org.scalatest.FutureOutcome
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
trait NodeUnitTest extends BitcoinSFixture with EmbeddedPg with BitcoinSLogger {
|
||||
trait NodeUnitTest extends BitcoinSFixture with EmbeddedPg {
|
||||
|
||||
override def beforeAll(): Unit = {
|
||||
AppConfig.throwIfDefaultDatadir(getFreshConfig.nodeConf)
|
||||
|
@ -2,14 +2,13 @@ package org.bitcoins.testkit.util
|
||||
|
||||
import java.nio.file.Files
|
||||
import org.bitcoins.core.config.NetworkParameters
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
||||
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.Future
|
||||
|
||||
abstract class BitcoindRpcTest extends BitcoinSAsyncTest with BitcoinSLogger {
|
||||
abstract class BitcoindRpcTest extends BitcoinSAsyncTest {
|
||||
|
||||
private val dirExists =
|
||||
Files.exists(BitcoindRpcTestClient.sbtBinaryDirectory)
|
||||
|
Loading…
Reference in New Issue
Block a user