mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-21 14:14:28 +01:00
Pubkey Refactor (#2936)
* Removed ExecutionContext from ECKey * Refactored ECPublicKey to remove compression state and introduced ECPublicKeyBytes to handle cases where serialization of input is important * Fixed the rest of bitcoin-s so that it passes all tests * Made all ECKeys into case classes * Successfully added isFullyValid invariant to ECPublicKey! * Fixed docs * Added scaladocs and fixed a RpcPsbtResult bug * Reject private keys of length < 32 and fix WIF parsing bug
This commit is contained in:
parent
b854f7b16a
commit
78f4dfb8c6
57 changed files with 811 additions and 493 deletions
|
@ -12,7 +12,7 @@ import org.bitcoins.core.script.constant.{
|
|||
import org.bitcoins.crypto.{
|
||||
DoubleSha256DigestBE,
|
||||
ECDigitalSignature,
|
||||
ECPublicKey
|
||||
ECPublicKeyBytes
|
||||
}
|
||||
import play.api.libs.json._
|
||||
import scodec.bits.ByteVector
|
||||
|
@ -43,7 +43,7 @@ case class SerializedTransactionWitness(
|
|||
hex: String,
|
||||
scriptType: Option[String],
|
||||
script: Option[Vector[ScriptToken]],
|
||||
pubKey: Option[ECPublicKey],
|
||||
pubKey: Option[ECPublicKeyBytes],
|
||||
signature: Option[ECDigitalSignature],
|
||||
stack: Option[Vector[ByteVector]])
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import org.bitcoins.core.protocol.transaction.{
|
|||
TransactionOutPoint
|
||||
}
|
||||
import org.bitcoins.commons.serializers.JsonWriters._
|
||||
import org.bitcoins.crypto.{DoubleSha256DigestBE, ECPrivateKey}
|
||||
import org.bitcoins.crypto.{DoubleSha256DigestBE, ECPrivateKeyBytes}
|
||||
import play.api.libs.json.{Json, Writes}
|
||||
import ujson.{Num, Str, Value}
|
||||
|
||||
|
@ -108,7 +108,7 @@ object RpcOpts {
|
|||
timestamp: UInt32,
|
||||
redeemscript: Option[ScriptPubKey] = None,
|
||||
pubkeys: Option[Vector[ScriptPubKey]] = None,
|
||||
keys: Option[Vector[ECPrivateKey]] = None,
|
||||
keys: Option[Vector[ECPrivateKeyBytes]] = None,
|
||||
internal: Option[Boolean] = None,
|
||||
watchonly: Option[Boolean] = None,
|
||||
label: Option[String] = None)
|
||||
|
|
|
@ -7,7 +7,7 @@ import org.bitcoins.core.protocol.transaction.Transaction
|
|||
import org.bitcoins.core.psbt.PSBT
|
||||
import org.bitcoins.core.script.ScriptType
|
||||
import org.bitcoins.core.script.crypto.HashType
|
||||
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey}
|
||||
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey, ECPublicKeyBytes}
|
||||
|
||||
sealed abstract class RpcPsbtResult
|
||||
|
||||
|
@ -89,7 +89,7 @@ final case class AnalyzePsbtInput(
|
|||
) extends RpcPsbtResult
|
||||
|
||||
final case class PsbtMissingData(
|
||||
pubkeys: Option[Vector[ECPublicKey]],
|
||||
pubkeys: Option[Vector[ECPublicKeyBytes]],
|
||||
signatures: Option[Vector[ECDigitalSignature]],
|
||||
redeemscript: Option[RpcPsbtScript],
|
||||
witnessscript: Option[RpcPsbtScript]
|
||||
|
|
|
@ -302,6 +302,13 @@ object JsonReaders {
|
|||
SerializerUtil.processJsString[ECPublicKey](ECPublicKey.fromHex)(json)
|
||||
}
|
||||
|
||||
implicit object ECPublicKeyBytesReads extends Reads[ECPublicKeyBytes] {
|
||||
|
||||
override def reads(json: JsValue): JsResult[ECPublicKeyBytes] =
|
||||
SerializerUtil.processJsString[ECPublicKeyBytes](
|
||||
ECPublicKeyBytes.fromHex)(json)
|
||||
}
|
||||
|
||||
implicit object SchnorrPublicKeyReads extends Reads[SchnorrPublicKey] {
|
||||
|
||||
override def reads(json: JsValue): JsResult[SchnorrPublicKey] =
|
||||
|
|
|
@ -68,6 +68,9 @@ object JsonSerializers {
|
|||
implicit val sha256Hash160DigestReads: Reads[Sha256Hash160Digest] =
|
||||
Sha256Hash160DigestReads
|
||||
implicit val eCPublicKeyReads: Reads[ECPublicKey] = ECPublicKeyReads
|
||||
|
||||
implicit val eCPublicKeyBytesReads: Reads[ECPublicKeyBytes] =
|
||||
ECPublicKeyBytesReads
|
||||
implicit val p2PKHAddressReads: Reads[P2PKHAddress] = P2PKHAddressReads
|
||||
implicit val p2SHAddressReads: Reads[P2SHAddress] = P2SHAddressReads
|
||||
|
||||
|
@ -631,6 +634,9 @@ object JsonSerializers {
|
|||
implicit val ecPublicKeyWrites: Writes[ECPublicKey] =
|
||||
Writes[ECPublicKey](pubKey => JsString(pubKey.hex))
|
||||
|
||||
implicit val ecPublicKeyBytesWrites: Writes[ECPublicKeyBytes] =
|
||||
Writes[ECPublicKeyBytes](pubKey => JsString(pubKey.hex))
|
||||
|
||||
implicit val scriptTokenWrites: Writes[ScriptToken] =
|
||||
Writes[ScriptToken](token => JsString(tokenToString(token)))
|
||||
|
||||
|
|
|
@ -37,7 +37,8 @@ class MessageRpcTest extends BitcoindRpcTest {
|
|||
|
||||
for {
|
||||
client <- clientF
|
||||
signature <- client.signMessageWithPrivKey(privKey, message)
|
||||
signature <- client.signMessageWithPrivKey(privKey.toPrivateKeyBytes(),
|
||||
message)
|
||||
validity <- client.verifyMessage(address, signature, message)
|
||||
} yield assert(validity)
|
||||
}
|
||||
|
|
|
@ -287,7 +287,7 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairV19 {
|
|||
val address = P2PKHAddress(publicKey, networkParam)
|
||||
|
||||
for {
|
||||
_ <- client.importPrivKey(ecPrivateKey,
|
||||
_ <- client.importPrivKey(ecPrivateKey.toPrivateKeyBytes(),
|
||||
rescan = false,
|
||||
walletNameOpt = Some(walletName))
|
||||
key <- client.dumpPrivKey(address, Some(walletName))
|
||||
|
@ -297,11 +297,15 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairV19 {
|
|||
client.getDaemon.datadir.getAbsolutePath + "/wallet_dump.dat",
|
||||
Some(walletName))
|
||||
} yield {
|
||||
assert(key == ecPrivateKey)
|
||||
assert(key.toPrivateKey == ecPrivateKey)
|
||||
val reader = new Scanner(result.filename)
|
||||
var found = false
|
||||
while (reader.hasNext) {
|
||||
if (reader.next == ECPrivateKeyUtil.toWIF(ecPrivateKey, networkParam)) {
|
||||
if (
|
||||
reader.next == ECPrivateKeyUtil.toWIF(
|
||||
ecPrivateKey.toPrivateKeyBytes(),
|
||||
networkParam)
|
||||
) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -399,18 +399,23 @@ class WalletRpcTest extends BitcoindFixturesCachedPairV21 {
|
|||
val address = P2PKHAddress(publicKey, networkParam)
|
||||
|
||||
for {
|
||||
_ <- client.importPrivKey(ecPrivateKey, rescan = false)
|
||||
_ <- client.importPrivKey(ecPrivateKey.toPrivateKeyBytes(),
|
||||
rescan = false)
|
||||
key <- client.dumpPrivKey(address)
|
||||
result <-
|
||||
client
|
||||
.dumpWallet(
|
||||
client.getDaemon.datadir.getAbsolutePath + "/wallet_dump.dat")
|
||||
} yield {
|
||||
assert(key == ecPrivateKey)
|
||||
assert(key.toPrivateKey == ecPrivateKey)
|
||||
val reader = new Scanner(result.filename)
|
||||
var found = false
|
||||
while (reader.hasNext) {
|
||||
if (reader.next == ECPrivateKeyUtil.toWIF(ecPrivateKey, networkParam)) {
|
||||
if (
|
||||
reader.next == ECPrivateKeyUtil.toWIF(
|
||||
ecPrivateKey.toPrivateKeyBytes(),
|
||||
networkParam)
|
||||
) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
@ -580,13 +585,15 @@ class WalletRpcTest extends BitcoindFixturesCachedPairV21 {
|
|||
BitcoinAddress.fromScriptPubKey(output.scriptPubKey, RegTest))
|
||||
} yield {
|
||||
val partialSig = BitcoinSigner.signSingle(
|
||||
ECSignatureParams(
|
||||
P2WPKHV0InputInfo(outPoint, output.value, privKey.publicKey),
|
||||
prevTx,
|
||||
privKey,
|
||||
HashType.sigHashAll),
|
||||
ECSignatureParams(P2WPKHV0InputInfo(outPoint,
|
||||
output.value,
|
||||
privKey.toPrivateKey.publicKey),
|
||||
prevTx,
|
||||
privKey.toPrivateKey,
|
||||
HashType.sigHashAll),
|
||||
transaction,
|
||||
isDummySignature = false)
|
||||
isDummySignature = false
|
||||
)
|
||||
|
||||
signedTx match {
|
||||
case btx: NonWitnessTransaction =>
|
||||
|
|
|
@ -12,7 +12,7 @@ import org.bitcoins.core.protocol.transaction.{
|
|||
TransactionInput,
|
||||
TransactionOutPoint
|
||||
}
|
||||
import org.bitcoins.crypto.{DoubleSha256DigestBE, ECPrivateKey}
|
||||
import org.bitcoins.crypto.{DoubleSha256DigestBE, ECPrivateKeyBytes}
|
||||
import org.bitcoins.rpc.client.common.BitcoindVersion
|
||||
import org.bitcoins.testkit.rpc.{
|
||||
BitcoindFixturesCachedPairV16,
|
||||
|
@ -85,7 +85,7 @@ class BitcoindV16RpcClientTest extends BitcoindFixturesCachedPairV16 {
|
|||
it should "be able to sign a raw transaction with private keys" in {
|
||||
nodePair: FixtureParam =>
|
||||
val client = nodePair.node1
|
||||
val privkeys: List[ECPrivateKey] =
|
||||
val privkeys: List[ECPrivateKeyBytes] =
|
||||
List("cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N",
|
||||
"cVKpPfVKSJxKqVpE9awvXNWuLHCa5j5tiE7K6zbUSptFpTEtiFrA")
|
||||
.map(ECPrivateKeyUtil.fromWIFToPrivateKey)
|
||||
|
@ -121,7 +121,10 @@ class BitcoindV16RpcClientTest extends BitcoindFixturesCachedPairV16 {
|
|||
|
||||
for {
|
||||
rawTx <- client.createRawTransaction(inputs, outputs)
|
||||
signed <- client.signRawTransaction(rawTx, utxoDeps, privkeys.toVector)
|
||||
signed <- client.signRawTransaction(
|
||||
rawTx,
|
||||
utxoDeps,
|
||||
privkeys.toVector.map(_.toPrivateKey))
|
||||
} yield assert(signed.complete)
|
||||
}
|
||||
|
||||
|
|
|
@ -109,7 +109,10 @@ class BitcoindV17RpcClientTest extends BitcoindFixturesCachedPairV17 {
|
|||
for {
|
||||
rawTx <- client.createRawTransaction(inputs, outputs)
|
||||
signed <-
|
||||
client.signRawTransactionWithKey(rawTx, privkeys.toVector, utxoDeps)
|
||||
client.signRawTransactionWithKey(
|
||||
rawTx,
|
||||
privkeys.toVector.map(_.toPrivateKey),
|
||||
utxoDeps)
|
||||
} yield assert(signed.complete)
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import org.bitcoins.commons.serializers.JsonSerializers._
|
|||
import org.bitcoins.core.config._
|
||||
import org.bitcoins.core.crypto.ECPrivateKeyUtil
|
||||
import org.bitcoins.core.util.StartStopAsync
|
||||
import org.bitcoins.crypto.ECPrivateKey
|
||||
import org.bitcoins.crypto.{ECPrivateKey, ECPrivateKeyBytes}
|
||||
import org.bitcoins.rpc.BitcoindException
|
||||
import org.bitcoins.rpc.config.BitcoindAuthCredentials.{
|
||||
CookieBased,
|
||||
|
@ -78,11 +78,23 @@ trait Client
|
|||
implicit object ECPrivateKeyWrites extends Writes[ECPrivateKey] {
|
||||
|
||||
override def writes(o: ECPrivateKey): JsValue =
|
||||
JsString(ECPrivateKeyUtil.toWIF(o, network))
|
||||
JsString(ECPrivateKeyUtil.toWIF(o.toPrivateKeyBytes(), network))
|
||||
}
|
||||
|
||||
implicit val eCPrivateKeyWrites: Writes[ECPrivateKey] = ECPrivateKeyWrites
|
||||
|
||||
/** This is here (and not in JsonWrriters)
|
||||
* so that the implicit network val is accessible
|
||||
*/
|
||||
implicit object ECPrivateKeyBytesWrites extends Writes[ECPrivateKeyBytes] {
|
||||
|
||||
override def writes(o: ECPrivateKeyBytes): JsValue =
|
||||
JsString(ECPrivateKeyUtil.toWIF(o, network))
|
||||
}
|
||||
|
||||
implicit val eCPrivateKeyBytesWrites: Writes[ECPrivateKeyBytes] =
|
||||
ECPrivateKeyBytesWrites
|
||||
|
||||
implicit val importMultiAddressWrites: Writes[RpcOpts.ImportMultiAddress] =
|
||||
Json.writes[RpcOpts.ImportMultiAddress]
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package org.bitcoins.rpc.client.common
|
|||
|
||||
import org.bitcoins.core.crypto.ECPrivateKeyUtil
|
||||
import org.bitcoins.core.protocol.P2PKHAddress
|
||||
import org.bitcoins.crypto.ECPrivateKey
|
||||
import org.bitcoins.crypto.ECPrivateKeyBytes
|
||||
import play.api.libs.json.JsString
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
@ -18,7 +18,7 @@ trait MessageRpc { self: Client =>
|
|||
}
|
||||
|
||||
def signMessageWithPrivKey(
|
||||
key: ECPrivateKey,
|
||||
key: ECPrivateKeyBytes,
|
||||
message: String): Future[String] = {
|
||||
bitcoindCall[String](
|
||||
"signmessagewithprivkey",
|
||||
|
|
|
@ -18,7 +18,7 @@ import org.bitcoins.core.script.crypto.HashType
|
|||
import org.bitcoins.crypto.{
|
||||
DoubleSha256Digest,
|
||||
DoubleSha256DigestBE,
|
||||
ECPrivateKey,
|
||||
ECPrivateKeyBytes,
|
||||
ECPublicKey
|
||||
}
|
||||
import org.bitcoins.rpc.client.common.BitcoindVersion._
|
||||
|
@ -41,7 +41,7 @@ trait WalletRpc { self: Client =>
|
|||
|
||||
def dumpPrivKey(
|
||||
address: BitcoinAddress,
|
||||
walletNameOpt: Option[String] = None): Future[ECPrivateKey] = {
|
||||
walletNameOpt: Option[String] = None): Future[ECPrivateKeyBytes] = {
|
||||
bitcoindCall[String]("dumpprivkey",
|
||||
List(JsString(address.value)),
|
||||
uriExtensionOpt = walletNameOpt.map(walletExtension))
|
||||
|
@ -197,7 +197,7 @@ trait WalletRpc { self: Client =>
|
|||
}
|
||||
|
||||
def importPrivKey(
|
||||
key: ECPrivateKey,
|
||||
key: ECPrivateKeyBytes,
|
||||
account: String = "",
|
||||
rescan: Boolean = true,
|
||||
walletNameOpt: Option[String] = None): Future[Unit] = {
|
||||
|
|
|
@ -72,7 +72,7 @@ class BloomFilterTest extends BitcoinSUnitTest {
|
|||
"5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C")
|
||||
assert(
|
||||
privKey.hex == "f49addfd726a59abde172c86452f5f73038a02f4415878dc14934175e8418aff")
|
||||
val pubKey = privKey.publicKey
|
||||
val pubKey = privKey.publicKeyBytes
|
||||
val filter1 = filter.insert(pubKey.bytes)
|
||||
//hex is from bitcoin core
|
||||
filter1.hex must be("0302c12b080000000000000001")
|
||||
|
@ -133,7 +133,7 @@ class BloomFilterTest extends BitcoinSUnitTest {
|
|||
|
||||
val filter6 = BloomFilter(10, 0.000001, UInt32.zero, BloomUpdateAll)
|
||||
//insert the pubkey of spendingTx in the bloom filter
|
||||
val pubKey = ECPublicKey(
|
||||
val pubKey = ECPublicKeyBytes(
|
||||
"046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339")
|
||||
val filter7 = filter6.insert(pubKey.bytes)
|
||||
filter7.isRelevant(creditingTx) must be(true)
|
||||
|
@ -204,7 +204,7 @@ class BloomFilterTest extends BitcoinSUnitTest {
|
|||
val block = Block(
|
||||
"0100000075616236cc2126035fadb38deb65b9102cc2c41c09cdf29fc051906800000000fe7d5e12ef0ff901f6050211249919b1c0653771832b3a80c66cea42847f0ae1d4d26e49ffff001d00f0a4410401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d029105ffffffff0100f2052a010000004341046d8709a041d34357697dfcb30a9d05900a6294078012bf3bb09c6f9b525f1d16d5503d7905db1ada9501446ea00728668fc5719aa80be2fdfc8a858a4dbdd4fbac00000000010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac0000000001000000025f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028000000004847304402205d6058484157235b06028c30736c15613a28bdb768ee628094ca8b0030d4d6eb0220328789c9a2ec27ddaec0ad5ef58efded42e6ea17c2e1ce838f3d6913f5e95db601ffffffff5f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028010000004a493046022100c45af050d3cea806cedd0ab22520c53ebe63b987b8954146cdca42487b84bdd6022100b9b027716a6b59e640da50a864d6dd8a0ef24c76ce62391fa3eabaf4d2886d2d01ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac000000000100000002e2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b0000000048473044022016e7a727a061ea2254a6c358376aaa617ac537eb836c77d646ebda4c748aac8b0220192ce28bf9f2c06a6467e6531e27648d2b3e2e2bae85159c9242939840295ba501ffffffffe2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b010000004a493046022100b7a1a755588d4190118936e15cd217d133b0e4a53c3c15924010d5648d8925c9022100aaef031874db2114f2d869ac2de4ae53908fbfea5b2b1862e181626bb9005c9f01ffffffff0200e1f505000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00000000")
|
||||
val txs = block.transactions
|
||||
val pubKey = ECPublicKey(
|
||||
val pubKey = ECPublicKeyBytes(
|
||||
"044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45af")
|
||||
val filter =
|
||||
BloomFilter(1, 0.00001, UInt32.zero, BloomUpdateNone).insert(pubKey.bytes)
|
||||
|
|
|
@ -8,7 +8,7 @@ import org.bitcoins.core.protocol.script.{
|
|||
}
|
||||
import org.bitcoins.core.protocol.transaction._
|
||||
import org.bitcoins.core.script.constant.ScriptToken
|
||||
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey}
|
||||
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey, ECPublicKeyBytes}
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
|
||||
/** Created by chris on 2/29/16.
|
||||
|
@ -27,7 +27,7 @@ class TransactionSignatureCheckerTest extends BitcoinSUnitTest {
|
|||
val p2pkOutput: TransactionOutput = TransactionOutput(
|
||||
"00f2052a0100000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac")
|
||||
|
||||
val p2pkPubKey: ECPublicKey = ECPublicKey(
|
||||
val p2pkPubKey: ECPublicKeyBytes = ECPublicKeyBytes(
|
||||
"0311db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c")
|
||||
|
||||
val p2pkSig: ECDigitalSignature = ECDigitalSignature(
|
||||
|
@ -55,7 +55,7 @@ class TransactionSignatureCheckerTest extends BitcoinSUnitTest {
|
|||
val p2pkhOutput: TransactionOutput = TransactionOutput(
|
||||
"00000000000000001976a914cd0385f813ec73f8fc340b7069daf566878a0d6b88ac")
|
||||
|
||||
val p2pkhPubKey: ECPublicKey = ECPublicKey(
|
||||
val p2pkhPubKey: ECPublicKeyBytes = ECPublicKeyBytes(
|
||||
"02a01aaa27b468ec3fb2ae0c2a9fa1d5dce9b79b35062178f479156d8daa6c0e50")
|
||||
|
||||
val p2pkhSig: ECDigitalSignature = ECDigitalSignature(
|
||||
|
@ -86,10 +86,10 @@ class TransactionSignatureCheckerTest extends BitcoinSUnitTest {
|
|||
MultiSignatureScriptPubKey(
|
||||
"47522102895a52495c4c370d50e6bef622ff28d87eec2df00c546b8921b6d07e844bfb9c210283fe2cf10b7dba0d635b3e408532183e27cd43adc11e125027107c095a2bfbc552ae")
|
||||
|
||||
val p2shMultiPubKey1: ECPublicKey = ECPublicKey(
|
||||
val p2shMultiPubKey1: ECPublicKeyBytes = ECPublicKeyBytes(
|
||||
"02895a52495c4c370d50e6bef622ff28d87eec2df00c546b8921b6d07e844bfb9c")
|
||||
|
||||
val p2shMultiPubKey2: ECPublicKey = ECPublicKey(
|
||||
val p2shMultiPubKey2: ECPublicKeyBytes = ECPublicKeyBytes(
|
||||
"0283fe2cf10b7dba0d635b3e408532183e27cd43adc11e125027107c095a2bfbc5")
|
||||
|
||||
val p2shMultiSig1: ECDigitalSignature = ECDigitalSignature(
|
||||
|
@ -176,7 +176,7 @@ class TransactionSignatureCheckerTest extends BitcoinSUnitTest {
|
|||
val p2shwpkhRedeemScript: ScriptPubKey =
|
||||
ScriptPubKey("16001422fe81d0b50f7a6407d4280e1603c10f5fc3fac8")
|
||||
|
||||
val p2shwpkhPubKey: ECPublicKey = ECPublicKey(
|
||||
val p2shwpkhPubKey: ECPublicKeyBytes = ECPublicKeyBytes(
|
||||
"029ca7faef43714b34508589f31394e0b0b6ab24dd4e440eaa03e72841d48e50c5")
|
||||
|
||||
val p2shwpkhSig: ECDigitalSignature = ECDigitalSignature(
|
||||
|
@ -211,10 +211,10 @@ class TransactionSignatureCheckerTest extends BitcoinSUnitTest {
|
|||
MultiSignatureScriptPubKey(
|
||||
"6952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae")
|
||||
|
||||
val p2wshPubKey1: ECPublicKey = ECPublicKey(
|
||||
val p2wshPubKey1: ECPublicKeyBytes = ECPublicKeyBytes(
|
||||
"0375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c")
|
||||
|
||||
val p2wshPubKey2: ECPublicKey = ECPublicKey(
|
||||
val p2wshPubKey2: ECPublicKeyBytes = ECPublicKeyBytes(
|
||||
"03a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff")
|
||||
|
||||
val p2wshSig1: ECDigitalSignature = ECDigitalSignature(
|
||||
|
@ -261,7 +261,7 @@ class TransactionSignatureCheckerTest extends BitcoinSUnitTest {
|
|||
}
|
||||
|
||||
// Negative Test Cases
|
||||
val incorrectPubKey: ECPublicKey = ECPublicKey.freshPublicKey
|
||||
val incorrectPubKey: ECPublicKeyBytes = ECPublicKeyBytes.freshPublicKey
|
||||
val incorrectOutput: EmptyTransactionOutput.type = EmptyTransactionOutput
|
||||
val incorrectTx: Transaction = EmptyTransaction
|
||||
val incorrectInputIndex: UInt32 = UInt32(100)
|
||||
|
|
|
@ -53,7 +53,7 @@ class TransactionSignatureCreatorTest extends BitcoinSJvmTest {
|
|||
"cTPg4Zc5Jis2EZXy3NXShgbn487GWBTapbU63BerLDZM3w2hQSjC")
|
||||
val txSignature =
|
||||
TransactionSignatureCreator.createSig(txSignatureComponent,
|
||||
privateKey,
|
||||
privateKey.toPrivateKey,
|
||||
HashType.sigHashAll)
|
||||
txSignature.r must be(expectedSig.r)
|
||||
txSignature.s must be(expectedSig.s)
|
||||
|
@ -81,7 +81,7 @@ class TransactionSignatureCreatorTest extends BitcoinSJvmTest {
|
|||
"cTTh7jNtZhg3vHTjvYK8zcHkLfsMAS8iqL7pfZ6eVAVHHF8fN1qy")
|
||||
val txSignature =
|
||||
TransactionSignatureCreator.createSig(txSignatureComponent,
|
||||
privateKey,
|
||||
privateKey.toPrivateKey,
|
||||
HashType.sigHashAll)
|
||||
txSignature.r must be(expectedSig.r)
|
||||
txSignature.s must be(expectedSig.s)
|
||||
|
@ -97,8 +97,10 @@ class TransactionSignatureCreatorTest extends BitcoinSJvmTest {
|
|||
val transaction = Transaction(rawTx)
|
||||
val prevTransaction = Transaction(
|
||||
"0100000001e4dbac0d73f4e3a9e99e70596a5f81b35a75f95b0474d051fbfd9dc249a5b67e000000006a4730440220486f112aee12997f6e484754d53d5c2158c18cc6d1d3f13aefcdf0ed19c47b290220136133d934d9e79a57408166c39fbce38e217ea9d417cabc20744134f04f06960121021f8cb5c3d611cf24dd665adff3fd540e4c155a05adaa6b672bfa7897c126d9b6feffffff0293d22a00000000001976a914cd0385f813ec73f8fc340b7069daf566878a0d6b88ac40420f000000000017a91480f7a6c14a8407da3546b4abfc3086876ca9a0668700000000")
|
||||
val privateKey = ECPrivateKeyUtil.fromWIFToPrivateKey(
|
||||
"cTTh7jNtZhg3vHTjvYK8zcHkLfsMAS8iqL7pfZ6eVAVHHF8fN1qy")
|
||||
val privateKey = ECPrivateKeyUtil
|
||||
.fromWIFToPrivateKey(
|
||||
"cTTh7jNtZhg3vHTjvYK8zcHkLfsMAS8iqL7pfZ6eVAVHHF8fN1qy")
|
||||
.toPrivateKey
|
||||
|
||||
val inputInfo =
|
||||
P2PKHInputInfo(TransactionOutPoint(prevTransaction.txId, UInt32.zero),
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
package org.bitcoins.core.crypto
|
||||
|
||||
import org.bitcoins.core.config.{MainNet, RegTest, SigNet, TestNet3}
|
||||
import org.bitcoins.crypto.ECPrivateKey
|
||||
import org.bitcoins.testkitcore.gen.{ChainParamsGenerator, CryptoGenerators}
|
||||
import org.bitcoins.crypto.{ECPrivateKey, ECPrivateKeyBytes}
|
||||
import org.bitcoins.testkitcore.gen.{
|
||||
ChainParamsGenerator,
|
||||
CryptoGenerators,
|
||||
NumberGenerator
|
||||
}
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
|
||||
class WIFEncodingTest extends BitcoinSUnitTest {
|
||||
|
@ -15,13 +19,14 @@ class WIFEncodingTest extends BitcoinSUnitTest {
|
|||
}
|
||||
it must "serialize a private key to WIF and then be able to deserialize it" in {
|
||||
|
||||
val hex = "2cecbfb72f8d5146d7fe7e5a3f80402c6dd688652c332dff2e44618d2d3372"
|
||||
val privKey = ECPrivateKey(hex)
|
||||
val hex = "002cecbfb72f8d5146d7fe7e5a3f80402c6dd688652c332dff2e44618d2d3372"
|
||||
val privKey = ECPrivateKeyBytes(hex)
|
||||
val wif = ECPrivateKeyUtil.toWIF(privKey, TestNet3)
|
||||
val privKeyFromWIF = ECPrivateKeyUtil.fromWIFToPrivateKey(wif)
|
||||
privKeyFromWIF must be(privKey)
|
||||
|
||||
val privKeyDecompressed = ECPrivateKey.fromHex(hex, isCompressed = false)
|
||||
val privKeyDecompressed =
|
||||
ECPrivateKey.fromHex(hex).toPrivateKeyBytes(isCompressed = false)
|
||||
val wifDecompressed = ECPrivateKeyUtil.toWIF(privKeyDecompressed, TestNet3)
|
||||
val privKeyDecompressedFromWIF =
|
||||
ECPrivateKeyUtil.fromWIFToPrivateKey(wifDecompressed)
|
||||
|
@ -31,7 +36,7 @@ class WIFEncodingTest extends BitcoinSUnitTest {
|
|||
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 privKey = ECPrivateKeyBytes(hex)
|
||||
val wif = ECPrivateKeyUtil.toWIF(privKey, TestNet3)
|
||||
val privKeyFromWIF = ECPrivateKeyUtil.fromWIFToPrivateKey(wif)
|
||||
privKeyFromWIF must be(privKey)
|
||||
|
@ -48,24 +53,42 @@ class WIFEncodingTest extends BitcoinSUnitTest {
|
|||
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(
|
||||
privKey.publicKeyBytes.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)
|
||||
forAll(CryptoGenerators.privateKey,
|
||||
ChainParamsGenerator.networkParams,
|
||||
NumberGenerator.bool) { (privKey, network, compressed) =>
|
||||
val wif =
|
||||
ECPrivateKeyUtil.toWIF(privKey.toPrivateKeyBytes(compressed), network)
|
||||
assert(ECPrivateKeyUtil.isCompressed(wif) == compressed)
|
||||
network match {
|
||||
case MainNet =>
|
||||
assert(ECPrivateKeyUtil.parseNetworkFromWIF(wif).get == MainNet)
|
||||
case TestNet3 | RegTest | SigNet =>
|
||||
assert(ECPrivateKeyUtil.parseNetworkFromWIF(wif).get == TestNet3)
|
||||
}
|
||||
assert(
|
||||
ECPrivateKeyUtil.fromWIFToPrivateKey(wif) == privKey.toPrivateKeyBytes(
|
||||
compressed))
|
||||
}
|
||||
}
|
||||
|
||||
it must "have serialization symmetry for WIF format when private key ends in 0x01" in {
|
||||
val privKey = ECPrivateKey(
|
||||
"710ed6c96012015f02e352cddd6f5a5b32499f2926ac7752b57d93b38be8c701")
|
||||
val wif =
|
||||
ECPrivateKeyUtil.toWIF(privKey.toPrivateKeyBytes(isCompressed = false),
|
||||
TestNet3)
|
||||
assert(!ECPrivateKeyUtil.isCompressed(wif))
|
||||
assert(ECPrivateKeyUtil.parseNetworkFromWIF(wif).get == TestNet3)
|
||||
assert(
|
||||
ECPrivateKeyUtil.fromWIFToPrivateKey(wif) == privKey
|
||||
.toPrivateKeyBytes(isCompressed = false))
|
||||
}
|
||||
|
||||
it must "fail to parse unknown WIF networks" in {
|
||||
// Litecoin privkey
|
||||
val wif = "6uSDaezGtedUbYk4F9CNVXbDWw9DuEuw7czU596t1CzmeAJ77P8"
|
||||
|
|
|
@ -387,8 +387,10 @@ class HDPathTest extends BitcoinSUnitTest {
|
|||
val spk = P2WPKHWitnessSPKV0(derivedPub)
|
||||
val address = Bech32Address(spk, MainNet)
|
||||
|
||||
val expectedPriv = ECPrivateKeyUtil.fromWIFToPrivateKey(
|
||||
"KyZpNDKnfs94vbrwhJneDi77V6jF64PWPF8x5cdJb8ifgg2DUc9d")
|
||||
val expectedPriv = ECPrivateKeyUtil
|
||||
.fromWIFToPrivateKey(
|
||||
"KyZpNDKnfs94vbrwhJneDi77V6jF64PWPF8x5cdJb8ifgg2DUc9d")
|
||||
.toPrivateKey
|
||||
val expectedPub = ECPublicKey(
|
||||
hex"0330d54fd0dd420a6e5f8d3624f5f3482cae350f79d5f0753bf5beef9c2d91af3c")
|
||||
val expectedAddress =
|
||||
|
@ -407,8 +409,10 @@ class HDPathTest extends BitcoinSUnitTest {
|
|||
val spk = P2WPKHWitnessSPKV0(derivedPub)
|
||||
val address = Bech32Address(spk, MainNet)
|
||||
|
||||
val expectedPriv = ECPrivateKeyUtil.fromWIFToPrivateKey(
|
||||
"Kxpf5b8p3qX56DKEe5NqWbNUP9MnqoRFzZwHRtsFqhzuvUJsYZCy")
|
||||
val expectedPriv = ECPrivateKeyUtil
|
||||
.fromWIFToPrivateKey(
|
||||
"Kxpf5b8p3qX56DKEe5NqWbNUP9MnqoRFzZwHRtsFqhzuvUJsYZCy")
|
||||
.toPrivateKey
|
||||
val expectedPub = ECPublicKey(
|
||||
hex"03e775fd51f0dfb8cd865d9ff1cca2a158cf651fe997fdc9fee9c1d3b5e995ea77")
|
||||
val expectedAddress =
|
||||
|
@ -427,8 +431,10 @@ class HDPathTest extends BitcoinSUnitTest {
|
|||
val spk = P2WPKHWitnessSPKV0(derivedPub)
|
||||
val address = Bech32Address(spk, MainNet)
|
||||
|
||||
val expectedPriv = ECPrivateKeyUtil.fromWIFToPrivateKey(
|
||||
"KxuoxufJL5csa1Wieb2kp29VNdn92Us8CoaUG3aGtPtcF3AzeXvF")
|
||||
val expectedPriv = ECPrivateKeyUtil
|
||||
.fromWIFToPrivateKey(
|
||||
"KxuoxufJL5csa1Wieb2kp29VNdn92Us8CoaUG3aGtPtcF3AzeXvF")
|
||||
.toPrivateKey
|
||||
val expectedPub = ECPublicKey(
|
||||
hex"03025324888e429ab8e3dbaf1f7802648b9cd01e9b418485c5fa4c1b9b5700e1a6")
|
||||
val expectedAddress =
|
||||
|
|
|
@ -4,7 +4,7 @@ import org.bitcoins.core.bloom._
|
|||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.protocol.transaction.TransactionOutPoint
|
||||
import org.bitcoins.core.util.BytesUtil
|
||||
import org.bitcoins.crypto.{DoubleSha256Digest, ECPublicKey}
|
||||
import org.bitcoins.crypto.{DoubleSha256Digest, ECPublicKeyBytes}
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
|
||||
/** Created by chris on 8/9/16.
|
||||
|
@ -136,7 +136,7 @@ class MerkleBlockTests extends BitcoinSUnitTest {
|
|||
// Match an output from the second transaction (the pubkey for address 1DZTzaBHUDM7T3QvUKBz4qXMRpkg8jsfB5)
|
||||
// This should not match the third transaction though it spends the output matched
|
||||
// It will match the fourth transaction, which has another pay-to-pubkey output to the same address
|
||||
val pubKey = ECPublicKey(
|
||||
val pubKey = ECPublicKeyBytes(
|
||||
"044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45af")
|
||||
val filterWithPubKey = filter.insert(pubKey.bytes)
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package org.bitcoins.core.protocol.script
|
||||
|
||||
import org.bitcoins.crypto.ECPublicKey
|
||||
import org.bitcoins.testkitcore.util.TestUtil
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
import org.bitcoins.crypto.ECPublicKeyBytes
|
||||
import org.bitcoins.testkitcore.util.{BitcoinSUnitTest, TestUtil}
|
||||
|
||||
/** Created by chris on 3/8/16.
|
||||
*/
|
||||
|
@ -36,11 +35,11 @@ class MultiSignatureScriptPubKeyTest extends BitcoinSUnitTest {
|
|||
|
||||
multiSigScriptPubKey.publicKeys must be(
|
||||
Seq(
|
||||
ECPublicKey(
|
||||
ECPublicKeyBytes(
|
||||
"025878e270211662a27181cf4d6ad4d2cf0e69a98a3815c086f587c7e9388d8718"),
|
||||
ECPublicKey(
|
||||
ECPublicKeyBytes(
|
||||
"03fc85980e3fac1f3d8a5c3223c3ef5bffc1bd42d2cc42add8c3899cc66e7f1906"),
|
||||
ECPublicKey(
|
||||
ECPublicKeyBytes(
|
||||
"0215b5bd050869166a70a7341b4f216e268b7c6c7504576dcea2cce7d11cc9a35f")
|
||||
))
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package org.bitcoins.core.protocol.script
|
||||
|
||||
import org.bitcoins.crypto.ECPublicKey
|
||||
import org.bitcoins.testkitcore.util.TestUtil
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
import org.bitcoins.crypto.ECPublicKeyBytes
|
||||
import org.bitcoins.testkitcore.util.{BitcoinSUnitTest, TestUtil}
|
||||
|
||||
/** Created by chris on 4/1/16.
|
||||
*/
|
||||
|
@ -15,7 +14,7 @@ class P2PKScriptPubKeyTest extends BitcoinSUnitTest {
|
|||
throw new RuntimeException("should have been p2pk script pub key")
|
||||
}
|
||||
|
||||
p2pkScriptPubKey.publicKey must be(ECPublicKey(
|
||||
p2pkScriptPubKey.publicKey must be(ECPublicKeyBytes(
|
||||
"0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import org.bitcoins.core.script.constant.{
|
|||
OP_0,
|
||||
ScriptConstant
|
||||
}
|
||||
import org.bitcoins.crypto.ECPublicKey
|
||||
import org.bitcoins.crypto.ECPublicKeyBytes
|
||||
import org.bitcoins.testkitcore.util.{BitcoinSJvmTest, TestUtil}
|
||||
|
||||
/** Created by chris on 3/8/16.
|
||||
|
@ -21,9 +21,9 @@ class P2SHScriptSignatureTest extends BitcoinSJvmTest {
|
|||
}
|
||||
p2shScriptSig.publicKeys must be(
|
||||
Seq(
|
||||
ECPublicKey(
|
||||
ECPublicKeyBytes(
|
||||
"0369d26ebd086523384a0f89f293d4c327a65fa73332d8efd1097cb35231295b83"),
|
||||
ECPublicKey(
|
||||
ECPublicKeyBytes(
|
||||
"02480863e5c4a4e9763f5380c44fcfe6a3b7787397076cf9ea1049303a9d34f721")
|
||||
))
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package org.bitcoins.core.protocol.script
|
||||
|
||||
import org.bitcoins.crypto.ECPrivateKey
|
||||
import org.bitcoins.crypto.ECPrivateKeyBytes
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
|
||||
class P2WPKHWitnessSPKV0Test extends BitcoinSUnitTest {
|
||||
|
||||
"P2WPKHWitnessSPKV0" must "fail to be created with an uncompressed public key" in {
|
||||
val uncompressed = ECPrivateKey(false).publicKey
|
||||
val uncompressed = ECPrivateKeyBytes.freshPrivateKey(false).publicKeyBytes
|
||||
intercept[IllegalArgumentException] {
|
||||
P2WPKHWitnessSPKV0(uncompressed)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,23 @@
|
|||
package org.bitcoins.core.protocol.script
|
||||
|
||||
import org.bitcoins.core.script.constant.ScriptNumber
|
||||
import org.bitcoins.crypto.ECPrivateKey
|
||||
import org.bitcoins.core.script.constant.{OP_1, ScriptConstant, ScriptNumber}
|
||||
import org.bitcoins.core.script.crypto.{OP_CHECKMULTISIG, OP_CHECKSIG}
|
||||
import org.bitcoins.core.util.BitcoinScriptUtil
|
||||
import org.bitcoins.crypto.ECPrivateKeyBytes
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
|
||||
class P2WSHWitnessSPKV0Test extends BitcoinSUnitTest {
|
||||
val uncompressed = ECPrivateKey(false).publicKey
|
||||
val p2pk = P2PKScriptPubKey(uncompressed)
|
||||
val multisig = MultiSignatureScriptPubKey(1, Vector(uncompressed))
|
||||
val uncompressed = ECPrivateKeyBytes.freshPrivateKey(false).publicKeyBytes
|
||||
val pushOps = BitcoinScriptUtil.calculatePushOp(uncompressed.bytes)
|
||||
val asmP2PK = pushOps ++ Seq(ScriptConstant(uncompressed.bytes), OP_CHECKSIG)
|
||||
val p2pk = P2PKScriptPubKey(asmP2PK)
|
||||
|
||||
val asmMultisig =
|
||||
Seq(OP_1) ++ pushOps ++ Seq(ScriptConstant(uncompressed.bytes),
|
||||
OP_1,
|
||||
OP_CHECKMULTISIG)
|
||||
val multisig = MultiSignatureScriptPubKey.fromAsm(asmMultisig)
|
||||
|
||||
"P2WPKHWitnessSPKV0" must "fail to be created with an uncompressed public key" in {
|
||||
intercept[IllegalArgumentException] {
|
||||
P2WSHWitnessSPKV0(p2pk)
|
||||
|
|
|
@ -339,10 +339,14 @@ class PSBTUnitTest extends BitcoinSUnitTest {
|
|||
|
||||
assert(unsignedPsbt.nextRole == PSBTRole.SignerPSBTRole)
|
||||
|
||||
val privKey0 = ECPrivateKeyUtil.fromWIFToPrivateKey(
|
||||
"cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr")
|
||||
val privKey1 = ECPrivateKeyUtil.fromWIFToPrivateKey(
|
||||
"cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d")
|
||||
val privKey0 = ECPrivateKeyUtil
|
||||
.fromWIFToPrivateKey(
|
||||
"cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr")
|
||||
.toPrivateKey
|
||||
val privKey1 = ECPrivateKeyUtil
|
||||
.fromWIFToPrivateKey(
|
||||
"cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d")
|
||||
.toPrivateKey
|
||||
|
||||
// BCrypto does not use low r signing
|
||||
val (expectedPsbt0, expectedPsbt1) = CryptoUtil.cryptoContext match {
|
||||
|
@ -361,11 +365,15 @@ class PSBTUnitTest extends BitcoinSUnitTest {
|
|||
|
||||
(psbt0, psbt1)
|
||||
}
|
||||
val privKey2 = ECPrivateKeyUtil.fromWIFToPrivateKey(
|
||||
"cT7J9YpCwY3AVRFSjN6ukeEeWY6mhpbJPxRaDaP5QTdygQRxP9Au")
|
||||
val privKey2 = ECPrivateKeyUtil
|
||||
.fromWIFToPrivateKey(
|
||||
"cT7J9YpCwY3AVRFSjN6ukeEeWY6mhpbJPxRaDaP5QTdygQRxP9Au")
|
||||
.toPrivateKey
|
||||
|
||||
val privKey3 = ECPrivateKeyUtil.fromWIFToPrivateKey(
|
||||
"cNBc3SWUip9PPm1GjRoLEJT6T41iNzCYtD7qro84FMnM5zEqeJsE")
|
||||
val privKey3 = ECPrivateKeyUtil
|
||||
.fromWIFToPrivateKey(
|
||||
"cNBc3SWUip9PPm1GjRoLEJT6T41iNzCYtD7qro84FMnM5zEqeJsE")
|
||||
.toPrivateKey
|
||||
|
||||
val expectedPubKeyHashes =
|
||||
Vector(privKey0, privKey1, privKey2, privKey3).map { key =>
|
||||
|
|
|
@ -8,10 +8,9 @@ import org.bitcoins.core.script.flag.ScriptVerifyWitnessPubKeyType
|
|||
import org.bitcoins.core.script.locktime.OP_CHECKLOCKTIMEVERIFY
|
||||
import org.bitcoins.core.script.reserved.{OP_NOP, OP_RESERVED}
|
||||
import org.bitcoins.core.script.result.ScriptErrorWitnessPubKeyType
|
||||
import org.bitcoins.crypto.{ECPrivateKey, ECPublicKey}
|
||||
import org.bitcoins.crypto.{ECPrivateKeyBytes, ECPublicKeyBytes}
|
||||
import org.bitcoins.testkitcore.gen.ScriptGenerators
|
||||
import org.bitcoins.testkitcore.util.TestUtil
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
import org.bitcoins.testkitcore.util.{BitcoinSUnitTest, TestUtil}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/** Created by chris on 3/2/16.
|
||||
|
@ -36,7 +35,7 @@ class BitcoinScriptUtilTest extends BitcoinSUnitTest {
|
|||
|
||||
// https://en.bitcoin.it/wiki/Genesis_block
|
||||
it must "filter out non-data from the genesis coinbase transaction" in {
|
||||
val genesisPK = ECPublicKey.fromHex(
|
||||
val genesisPK = ECPublicKeyBytes.fromHex(
|
||||
"04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f")
|
||||
val output = TransactionOutput.fromHex(
|
||||
"00f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac")
|
||||
|
@ -272,7 +271,7 @@ class BitcoinScriptUtilTest extends BitcoinSUnitTest {
|
|||
|
||||
it must "check a public key's encoding" in {
|
||||
//pubkeys must be compressed or uncompressed or else that are not validly encoded
|
||||
val key = ECPublicKey("00")
|
||||
val key = ECPublicKeyBytes("00")
|
||||
val program = TestUtil.testProgramExecutionInProgress
|
||||
BitcoinScriptUtil.checkPubKeyEncoding(key, program) must be(false)
|
||||
}
|
||||
|
@ -300,16 +299,16 @@ class BitcoinScriptUtilTest extends BitcoinSUnitTest {
|
|||
}
|
||||
|
||||
it must "determine if a segwit pubkey is compressed" in {
|
||||
val key = ECPrivateKey(false)
|
||||
val pubKey = key.publicKey
|
||||
val key = ECPrivateKeyBytes.freshPrivateKey(false)
|
||||
val pubKey = key.publicKeyBytes
|
||||
val flags = Seq(ScriptVerifyWitnessPubKeyType)
|
||||
BitcoinScriptUtil.isValidPubKeyEncoding(pubKey,
|
||||
SigVersionWitnessV0,
|
||||
flags) must be(
|
||||
Some(ScriptErrorWitnessPubKeyType))
|
||||
|
||||
val key2 = ECPrivateKey(false)
|
||||
val pubKey2 = key2.publicKey
|
||||
val key2 = ECPrivateKeyBytes.freshPrivateKey(false)
|
||||
val pubKey2 = key2.publicKeyBytes
|
||||
BitcoinScriptUtil.isValidPubKeyEncoding(pubKey2,
|
||||
SigVersionBase,
|
||||
flags) must be(None)
|
||||
|
|
|
@ -2,17 +2,17 @@ package org.bitcoins.core.crypto
|
|||
|
||||
import org.bitcoins.core.config.{NetworkParameters, Networks}
|
||||
import org.bitcoins.core.util.{Base58, BytesUtil}
|
||||
import org.bitcoins.crypto.{CryptoUtil, ECPrivateKey}
|
||||
import org.bitcoins.crypto.{CryptoUtil, ECPrivateKeyBytes}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
object ECPrivateKeyUtil {
|
||||
|
||||
/** Converts a [[org.bitcoins.crypto.ECPrivateKey ECPrivateKey]] to
|
||||
/** Converts a [[org.bitcoins.crypto.ECPrivateKeyBytes ECPrivateKey]] to
|
||||
* [[https://en.bitcoin.it/wiki/Wallet_import_format WIF]]
|
||||
*/
|
||||
def toWIF(privKey: ECPrivateKey, network: NetworkParameters): String = {
|
||||
def toWIF(privKey: ECPrivateKeyBytes, network: NetworkParameters): String = {
|
||||
val networkByte = network.privateKey
|
||||
//append 1 byte to the end of the priv key byte representation if we need a compressed pub key
|
||||
val fullBytes =
|
||||
|
@ -31,10 +31,10 @@ object ECPrivateKeyUtil {
|
|||
* @param WIF Wallet Import Format. Encoded in Base58
|
||||
* @return
|
||||
*/
|
||||
def fromWIFToPrivateKey(WIF: String): ECPrivateKey = {
|
||||
def fromWIFToPrivateKey(WIF: String): ECPrivateKeyBytes = {
|
||||
val isCompressed = ECPrivateKeyUtil.isCompressed(WIF)
|
||||
val privateKeyBytes = trimFunction(WIF)
|
||||
ECPrivateKey.fromBytes(privateKeyBytes, isCompressed)
|
||||
ECPrivateKeyBytes(privateKeyBytes, isCompressed)
|
||||
}
|
||||
|
||||
/** Takes in WIF private key as a sequence of bytes and determines if it corresponds to a compressed public key.
|
||||
|
@ -49,7 +49,7 @@ object ECPrivateKeyUtil {
|
|||
val validCompressedBytesInHex: Seq[String] =
|
||||
validCompressedBytes.map(b => BytesUtil.encodeHex(b))
|
||||
val firstByteHex = BytesUtil.encodeHex(bytes.head)
|
||||
if (validCompressedBytesInHex.contains(firstByteHex))
|
||||
if (validCompressedBytesInHex.contains(firstByteHex) && bytes.length == 38)
|
||||
bytes(bytes.length - 5) == 0x01.toByte
|
||||
else false
|
||||
}
|
||||
|
|
|
@ -9,7 +9,12 @@ import org.bitcoins.core.script.crypto._
|
|||
import org.bitcoins.core.script.flag.{ScriptFlag, ScriptFlagUtil}
|
||||
import org.bitcoins.core.script.result.ScriptErrorWitnessPubKeyType
|
||||
import org.bitcoins.core.util.BitcoinScriptUtil
|
||||
import org.bitcoins.crypto.{DERSignatureUtil, ECDigitalSignature, ECPublicKey}
|
||||
import org.bitcoins.crypto.{
|
||||
DERSignatureUtil,
|
||||
ECDigitalSignature,
|
||||
ECPublicKey,
|
||||
ECPublicKeyBytes
|
||||
}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
@ -20,6 +25,13 @@ import scala.annotation.tailrec
|
|||
*/
|
||||
trait TransactionSignatureChecker {
|
||||
|
||||
def checkSignature(
|
||||
txSignatureComponent: TxSigComponent,
|
||||
pubKeyBytes: ECPublicKeyBytes,
|
||||
signature: ECDigitalSignature): TransactionSignatureCheckerResult =
|
||||
checkSignature(txSignatureComponent,
|
||||
PartialSignature(pubKeyBytes, signature))
|
||||
|
||||
def checkSignature(
|
||||
txSignatureComponent: TxSigComponent,
|
||||
pubKey: ECPublicKey,
|
||||
|
@ -58,7 +70,7 @@ trait TransactionSignatureChecker {
|
|||
def checkSignature(
|
||||
txSignatureComponent: TxSigComponent,
|
||||
script: Seq[ScriptToken],
|
||||
pubKey: ECPublicKey,
|
||||
pubKey: ECPublicKeyBytes,
|
||||
signature: ECDigitalSignature,
|
||||
flags: Seq[ScriptFlag] =
|
||||
Policy.standardFlags): TransactionSignatureCheckerResult = {
|
||||
|
@ -147,7 +159,7 @@ trait TransactionSignatureChecker {
|
|||
txSignatureComponent: TxSigComponent,
|
||||
script: Seq[ScriptToken],
|
||||
sigs: List[ECDigitalSignature],
|
||||
pubKeys: List[ECPublicKey],
|
||||
pubKeys: List[ECPublicKeyBytes],
|
||||
flags: Seq[ScriptFlag],
|
||||
requiredSigs: Long): TransactionSignatureCheckerResult = {
|
||||
require(requiredSigs >= 0,
|
||||
|
|
|
@ -9,9 +9,6 @@ import scodec.bits.ByteVector
|
|||
* 33 byte compressed secp256k1 public key.
|
||||
*/
|
||||
case class NodeId(pubKey: ECPublicKey) extends NetworkElement {
|
||||
require(
|
||||
pubKey.isCompressed,
|
||||
s"Cannot create a nodeId from a public key that was not compressed ${pubKey.hex}")
|
||||
|
||||
override def toString: String = pubKey.hex
|
||||
|
||||
|
|
|
@ -25,9 +25,6 @@ case class LnRoute(
|
|||
cltvExpiryDelta: Short)
|
||||
extends NetworkElement {
|
||||
|
||||
require(pubkey.isCompressed,
|
||||
s"Can only use a compressed public key in routing")
|
||||
|
||||
override def bytes: ByteVector = {
|
||||
|
||||
val cltvExpiryDeltaHex = BytesUtil.encodeHex(cltvExpiryDelta)
|
||||
|
|
|
@ -154,11 +154,11 @@ sealed trait MultiSignatureScriptPubKey extends RawScriptPubKey {
|
|||
}
|
||||
|
||||
/** Returns the public keys encoded into the `scriptPubKey` */
|
||||
def publicKeys: Seq[ECPublicKey] = {
|
||||
def publicKeys: Seq[ECPublicKeyBytes] = {
|
||||
asm
|
||||
.filter(_.isInstanceOf[ScriptConstant])
|
||||
.slice(1, maxSigs + 1)
|
||||
.map(key => ECPublicKey(key.hex))
|
||||
.map(key => ECPublicKeyBytes(key.bytes))
|
||||
}
|
||||
|
||||
override def toString = s"multi($requiredSigs,${publicKeys.mkString(",")})"
|
||||
|
@ -343,8 +343,8 @@ object P2SHScriptPubKey extends ScriptFactory[P2SHScriptPubKey] {
|
|||
*/
|
||||
sealed trait P2PKScriptPubKey extends RawScriptPubKey {
|
||||
|
||||
def publicKey: ECPublicKey =
|
||||
ECPublicKey(BitcoinScriptUtil.filterPushOps(asm).head.bytes)
|
||||
def publicKey: ECPublicKeyBytes =
|
||||
ECPublicKeyBytes(BitcoinScriptUtil.filterPushOps(asm).head.bytes)
|
||||
|
||||
override def toString = s"pk(${publicKey.hex})"
|
||||
|
||||
|
@ -382,6 +382,21 @@ object P2PKScriptPubKey extends ScriptFactory[P2PKScriptPubKey] {
|
|||
case _ => false
|
||||
}
|
||||
|
||||
/** Builds a P2PKScriptPubKey from a specific branch of a P2PKWithTimeout.
|
||||
* Useful for when decomposing a script into spending branches.
|
||||
*/
|
||||
private[core] def fromP2PKWithTimeout(
|
||||
p2pkWithTimeout: P2PKWithTimeoutScriptPubKey,
|
||||
timeoutBranch: Boolean): P2PKScriptPubKey = {
|
||||
val pubKeyBytes =
|
||||
if (timeoutBranch) p2pkWithTimeout.timeoutPubKey
|
||||
else p2pkWithTimeout.pubKey
|
||||
val pushOps = BitcoinScriptUtil.calculatePushOp(pubKeyBytes.bytes)
|
||||
val asm = pushOps ++ Seq(ScriptConstant(pubKeyBytes.bytes), OP_CHECKSIG)
|
||||
|
||||
P2PKScriptPubKey(asm)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sealed trait LockTimeScriptPubKey extends RawScriptPubKey {
|
||||
|
@ -896,8 +911,8 @@ object NonStandardNotIfConditionalScriptPubKey
|
|||
*/
|
||||
sealed trait P2PKWithTimeoutScriptPubKey extends RawScriptPubKey {
|
||||
|
||||
lazy val pubKey: ECPublicKey =
|
||||
ECPublicKey.fromBytes(asm(2).bytes)
|
||||
lazy val pubKey: ECPublicKeyBytes =
|
||||
ECPublicKeyBytes(asm(2).bytes)
|
||||
|
||||
private lazy val smallCSVOpt: Option[Long] = {
|
||||
asm(4) match {
|
||||
|
@ -912,10 +927,10 @@ sealed trait P2PKWithTimeoutScriptPubKey extends RawScriptPubKey {
|
|||
.getOrElse(ScriptNumber(asm(5).bytes))
|
||||
}
|
||||
|
||||
lazy val timeoutPubKey: ECPublicKey = {
|
||||
lazy val timeoutPubKey: ECPublicKeyBytes = {
|
||||
smallCSVOpt match {
|
||||
case Some(_) => ECPublicKey.fromBytes(asm(8).bytes)
|
||||
case None => ECPublicKey.fromBytes(asm(9).bytes)
|
||||
case Some(_) => ECPublicKeyBytes(asm(8).bytes)
|
||||
case None => ECPublicKeyBytes(asm(9).bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -968,24 +983,28 @@ object P2PKWithTimeoutScriptPubKey
|
|||
}
|
||||
|
||||
if (asm.length == requiredSize) {
|
||||
val pubKey = ECPublicKey.fromBytes(asm(2).bytes)
|
||||
val pubKeyTry = Try(ECPublicKey.fromBytes(asm(2).bytes))
|
||||
|
||||
val lockTimeTry = smallCSVOpt match {
|
||||
case Some(num) => Success(ScriptNumber(num))
|
||||
case None => Try(ScriptNumber.fromBytes(asm(5).bytes))
|
||||
}
|
||||
|
||||
val timeoutPubKey = smallCSVOpt match {
|
||||
case Some(_) => ECPublicKey.fromBytes(asm(8).bytes)
|
||||
case None => ECPublicKey.fromBytes(asm(9).bytes)
|
||||
val timeoutPubKeyTry = Try {
|
||||
smallCSVOpt match {
|
||||
case Some(_) => ECPublicKey.fromBytes(asm(8).bytes)
|
||||
case None => ECPublicKey.fromBytes(asm(9).bytes)
|
||||
}
|
||||
}
|
||||
|
||||
lockTimeTry match {
|
||||
case Success(lockTime) =>
|
||||
(pubKeyTry, lockTimeTry, timeoutPubKeyTry) match {
|
||||
case (Failure(_), _, _) => false
|
||||
case (_, Failure(_), _) => false
|
||||
case (_, _, Failure(_)) => false
|
||||
case (Success(pubKey), Success(lockTime), Success(timeoutPubKey)) =>
|
||||
asm == P2PKWithTimeoutScriptPubKey(pubKey,
|
||||
lockTime,
|
||||
timeoutPubKey).asm
|
||||
case Failure(_) => false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
|
@ -1258,12 +1277,17 @@ object P2WPKHWitnessSPKV0 extends ScriptFactory[P2WPKHWitnessSPKV0] {
|
|||
fromAsm(Seq(OP_0) ++ pushop ++ Seq(ScriptConstant(hash.bytes)))
|
||||
}
|
||||
|
||||
/** Creates a P2WPKH witness script pubkey */
|
||||
def apply(pubKey: ECPublicKey): P2WPKHWitnessSPKV0 = {
|
||||
/** Builds a P2WPKH SPK from raw ECPublicKeyBytes (unsafe). */
|
||||
private[core] def apply(pubKey: ECPublicKeyBytes): 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")
|
||||
P2WPKHWitnessSPKV0(pubKey.toPublicKey)
|
||||
}
|
||||
|
||||
/** Creates a P2WPKH witness script pubkey */
|
||||
def apply(pubKey: ECPublicKey): P2WPKHWitnessSPKV0 = {
|
||||
val hash = CryptoUtil.sha256Hash160(pubKey.bytes)
|
||||
val pushop = BitcoinScriptUtil.calculatePushOp(hash.bytes)
|
||||
fromAsm(Seq(OP_0) ++ pushop ++ Seq(ScriptConstant(hash.bytes)))
|
||||
|
|
|
@ -4,7 +4,7 @@ import org.bitcoins.core.script.constant._
|
|||
import org.bitcoins.core.serializers.script.ScriptParser
|
||||
import org.bitcoins.core.util._
|
||||
import org.bitcoins.core.wallet.utxo.ConditionalPath
|
||||
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey}
|
||||
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey, ECPublicKeyBytes}
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.util.Try
|
||||
|
@ -79,7 +79,7 @@ sealed trait P2PKHScriptSignature extends ScriptSignature {
|
|||
def signature: ECDigitalSignature = signatures.head
|
||||
|
||||
/** Gives us the public key inside of a p2pkh script signature */
|
||||
def publicKey: ECPublicKey = ECPublicKey(asm.last.bytes)
|
||||
def publicKey: ECPublicKeyBytes = ECPublicKeyBytes(asm.last.bytes)
|
||||
|
||||
override def signatures: Seq[ECDigitalSignature] = {
|
||||
Seq(ECDigitalSignature(asm(1).hex))
|
||||
|
@ -103,20 +103,27 @@ object P2PKHScriptSignature extends ScriptFactory[P2PKHScriptSignature] {
|
|||
)
|
||||
}
|
||||
|
||||
/** Builds a P2PKH ScriptSig from a signature and raw ECPublicKeyBytes (may be invalid). */
|
||||
private[core] def apply(
|
||||
signature: ECDigitalSignature,
|
||||
pubKeyBytes: ECPublicKeyBytes): P2PKHScriptSignature = {
|
||||
val signatureBytesToPushOntoStack =
|
||||
BitcoinScriptUtil.calculatePushOp(signature.bytes)
|
||||
val pubKeyBytesToPushOntoStack =
|
||||
BitcoinScriptUtil.calculatePushOp(pubKeyBytes.bytes)
|
||||
val asm: Seq[ScriptToken] =
|
||||
signatureBytesToPushOntoStack ++ Seq(ScriptConstant(signature.bytes)) ++
|
||||
pubKeyBytesToPushOntoStack ++ Seq(ScriptConstant(pubKeyBytes.bytes))
|
||||
fromAsm(asm)
|
||||
}
|
||||
|
||||
/** Builds a script signature from a digital signature and a public key
|
||||
* this is a pay to public key hash script sig
|
||||
*/
|
||||
def apply(
|
||||
signature: ECDigitalSignature,
|
||||
pubKey: ECPublicKey): P2PKHScriptSignature = {
|
||||
val signatureBytesToPushOntoStack =
|
||||
BitcoinScriptUtil.calculatePushOp(signature.bytes)
|
||||
val pubKeyBytesToPushOntoStack =
|
||||
BitcoinScriptUtil.calculatePushOp(pubKey.bytes)
|
||||
val asm: Seq[ScriptToken] =
|
||||
signatureBytesToPushOntoStack ++ Seq(ScriptConstant(signature.hex)) ++
|
||||
pubKeyBytesToPushOntoStack ++ Seq(ScriptConstant(pubKey.hex))
|
||||
fromAsm(asm)
|
||||
P2PKHScriptSignature(signature, pubKey.toPublicKeyBytes())
|
||||
}
|
||||
|
||||
/** Determines if the given asm matches a [[P2PKHScriptSignature]] */
|
||||
|
@ -180,11 +187,11 @@ sealed trait P2SHScriptSignature extends ScriptSignature {
|
|||
}
|
||||
|
||||
/** Returns the public keys for the p2sh scriptSignature */
|
||||
def publicKeys: Seq[ECPublicKey] = {
|
||||
def publicKeys: Seq[ECPublicKeyBytes] = {
|
||||
val pubKeys: Seq[ScriptToken] = redeemScript.asm
|
||||
.filter(_.isInstanceOf[ScriptConstant])
|
||||
.filterNot(_.isInstanceOf[ScriptNumberOperation])
|
||||
pubKeys.map(k => ECPublicKey(k.hex))
|
||||
pubKeys.map(k => ECPublicKeyBytes(k.bytes))
|
||||
}
|
||||
|
||||
/** The digital signatures inside of the scriptSig */
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.bitcoins.core.util.{BitcoinScriptUtil, BytesUtil}
|
|||
import org.bitcoins.crypto.{
|
||||
ECDigitalSignature,
|
||||
ECPublicKey,
|
||||
ECPublicKeyBytes,
|
||||
EmptyDigitalSignature,
|
||||
Factory,
|
||||
NetworkElement
|
||||
|
@ -43,7 +44,7 @@ sealed abstract class ScriptWitnessV0 extends ScriptWitness {
|
|||
* Format: <pubKey> <signature>
|
||||
*/
|
||||
sealed abstract class P2WPKHWitnessV0 extends ScriptWitnessV0 {
|
||||
def pubKey: ECPublicKey = ECPublicKey(stack.head)
|
||||
def pubKey: ECPublicKeyBytes = ECPublicKeyBytes(stack.head)
|
||||
|
||||
def signature: ECDigitalSignature =
|
||||
stack(1) match {
|
||||
|
@ -63,14 +64,25 @@ object P2WPKHWitnessV0 {
|
|||
private def apply(stack: Seq[ByteVector]): P2WPKHWitnessV0 =
|
||||
P2WPKHWitnessV0Impl(stack)
|
||||
|
||||
private[bitcoins] def apply(
|
||||
pubKeyBytes: ECPublicKeyBytes): P2WPKHWitnessV0 = {
|
||||
P2WPKHWitnessV0(pubKeyBytes, EmptyDigitalSignature)
|
||||
}
|
||||
|
||||
def apply(pubKey: ECPublicKey): P2WPKHWitnessV0 = {
|
||||
P2WPKHWitnessV0(pubKey, EmptyDigitalSignature)
|
||||
}
|
||||
|
||||
private[bitcoins] def apply(
|
||||
publicKeyBytes: ECPublicKeyBytes,
|
||||
signature: ECDigitalSignature): P2WPKHWitnessV0 = {
|
||||
P2WPKHWitnessV0(Seq(publicKeyBytes.bytes, signature.bytes))
|
||||
}
|
||||
|
||||
def apply(
|
||||
publicKey: ECPublicKey,
|
||||
signature: ECDigitalSignature): P2WPKHWitnessV0 = {
|
||||
P2WPKHWitnessV0(Seq(publicKey.bytes, signature.bytes))
|
||||
P2WPKHWitnessV0(publicKey.toPublicKeyBytes(), signature)
|
||||
}
|
||||
|
||||
def fromP2PKHScriptSig(scriptSig: ScriptSignature): P2WPKHWitnessV0 =
|
||||
|
@ -176,11 +188,11 @@ object ScriptWitness extends Factory[ScriptWitness] {
|
|||
if (stack.isEmpty) {
|
||||
EmptyScriptWitness
|
||||
} else if (isPubKey && stack.size == 2) {
|
||||
val pubKey = ECPublicKey(stack.head)
|
||||
val pubKey = ECPublicKeyBytes(stack.head)
|
||||
val sig = ECDigitalSignature(stack(1))
|
||||
P2WPKHWitnessV0(pubKey, sig)
|
||||
} else if (isPubKey && stack.size == 1) {
|
||||
val pubKey = ECPublicKey(stack.head)
|
||||
val pubKey = ECPublicKeyBytes(stack.head)
|
||||
P2WPKHWitnessV0(pubKey)
|
||||
} else {
|
||||
//wont match a Vector if I don't convert to list
|
||||
|
|
|
@ -503,10 +503,14 @@ case class InputPSBTMap(elements: Vector[InputPSBTRecord])
|
|||
addLeaves(conditional.trueSPK, path :+ true)
|
||||
addLeaves(conditional.falseSPK, path :+ false)
|
||||
case p2pkWithTimeout: P2PKWithTimeoutScriptPubKey =>
|
||||
addLeaves(P2PKScriptPubKey(p2pkWithTimeout.pubKey), path :+ true)
|
||||
addLeaves(P2PKScriptPubKey.fromP2PKWithTimeout(p2pkWithTimeout,
|
||||
timeoutBranch =
|
||||
false),
|
||||
path :+ true)
|
||||
val timeoutSPK = CLTVScriptPubKey(
|
||||
p2pkWithTimeout.lockTime,
|
||||
P2PKScriptPubKey(p2pkWithTimeout.timeoutPubKey))
|
||||
P2PKScriptPubKey.fromP2PKWithTimeout(p2pkWithTimeout,
|
||||
timeoutBranch = true))
|
||||
addLeaves(timeoutSPK, path :+ false)
|
||||
case cltv: CLTVScriptPubKey =>
|
||||
addLeaves(cltv.nestedScriptPubKey, path)
|
||||
|
|
|
@ -157,7 +157,7 @@ object InputPSBTRecord extends Factory[InputPSBTRecord] {
|
|||
}
|
||||
|
||||
case class PartialSignature(
|
||||
pubKey: ECPublicKey,
|
||||
pubKey: ECPublicKeyBytes,
|
||||
signature: ECDigitalSignature)
|
||||
extends InputPSBTRecord {
|
||||
require(pubKey.byteSize == 33,
|
||||
|
@ -172,6 +172,12 @@ object InputPSBTRecord extends Factory[InputPSBTRecord] {
|
|||
|
||||
object PartialSignature extends Factory[PartialSignature] {
|
||||
|
||||
def apply(
|
||||
pubKey: ECPublicKey,
|
||||
signature: ECDigitalSignature): PartialSignature = {
|
||||
PartialSignature(pubKey.toPublicKeyBytes(), signature)
|
||||
}
|
||||
|
||||
def dummyPartialSig(
|
||||
pubKey: ECPublicKey = ECPublicKey.freshPublicKey): PartialSignature = {
|
||||
PartialSignature(pubKey, DummyECDigitalSignature)
|
||||
|
|
|
@ -14,7 +14,7 @@ import org.bitcoins.core.util.BitcoinScriptUtil
|
|||
import org.bitcoins.crypto.{
|
||||
CryptoUtil,
|
||||
ECDigitalSignature,
|
||||
ECPublicKey,
|
||||
ECPublicKeyBytes,
|
||||
HashDigest
|
||||
}
|
||||
import scodec.bits.ByteVector
|
||||
|
@ -77,7 +77,7 @@ sealed abstract class CryptoInterpreter {
|
|||
if (program.stack.size < 2) {
|
||||
program.failExecution(ScriptErrorInvalidStackOperation)
|
||||
} else {
|
||||
val pubKey = ECPublicKey(program.stack.head.bytes)
|
||||
val pubKey = ECPublicKeyBytes(program.stack.head.bytes)
|
||||
val signature = ECDigitalSignature(program.stack.tail.head.bytes)
|
||||
val flags = program.flags
|
||||
val restOfStack = program.stack.tail.tail
|
||||
|
@ -182,7 +182,8 @@ sealed abstract class CryptoInterpreter {
|
|||
program.stack.tail
|
||||
.slice(nPossibleSignatures.toInt, program.stack.tail.size))
|
||||
|
||||
val pubKeys = pubKeysScriptTokens.map(key => ECPublicKey(key.bytes))
|
||||
val pubKeys =
|
||||
pubKeysScriptTokens.map(key => ECPublicKeyBytes(key.bytes))
|
||||
|
||||
//+1 is for the fact that we have the # of sigs + the script token indicating the # of sigs
|
||||
val signaturesScriptTokens = program.stack.tail.slice(
|
||||
|
|
|
@ -35,7 +35,7 @@ import org.bitcoins.core.script.{
|
|||
}
|
||||
import org.bitcoins.core.serializers.script.ScriptParser
|
||||
import org.bitcoins.core.wallet.utxo._
|
||||
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey}
|
||||
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKeyBytes}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
@ -308,11 +308,13 @@ trait BitcoinScriptUtil {
|
|||
* [[https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L202]].
|
||||
*/
|
||||
def checkPubKeyEncoding(
|
||||
key: ECPublicKey,
|
||||
key: ECPublicKeyBytes,
|
||||
program: ExecutionInProgressScriptProgram): Boolean =
|
||||
checkPubKeyEncoding(key, program.flags)
|
||||
|
||||
def checkPubKeyEncoding(key: ECPublicKey, flags: Seq[ScriptFlag]): Boolean = {
|
||||
def checkPubKeyEncoding(
|
||||
key: ECPublicKeyBytes,
|
||||
flags: Seq[ScriptFlag]): Boolean = {
|
||||
if (
|
||||
ScriptFlagUtil.requireStrictEncoding(flags) &&
|
||||
!isCompressedOrUncompressedPubKey(key)
|
||||
|
@ -325,7 +327,7 @@ trait BitcoinScriptUtil {
|
|||
* @param key the public key that is being checked
|
||||
* @return true if the key is compressed/uncompressed otherwise false
|
||||
*/
|
||||
def isCompressedOrUncompressedPubKey(key: ECPublicKey): Boolean = {
|
||||
def isCompressedOrUncompressedPubKey(key: ECPublicKeyBytes): Boolean = {
|
||||
if (key.bytes.size < 33) {
|
||||
// Non-canonical public key: too short
|
||||
return false
|
||||
|
@ -345,7 +347,7 @@ trait BitcoinScriptUtil {
|
|||
}
|
||||
|
||||
/** Checks if the given public key is a compressed public key */
|
||||
def isCompressedPubKey(key: ECPublicKey): Boolean = {
|
||||
def isCompressedPubKey(key: ECPublicKeyBytes): Boolean = {
|
||||
(key.bytes.size == 33) && (key.bytes.head == 0x02 || key.bytes.head == 0x03)
|
||||
}
|
||||
|
||||
|
@ -361,7 +363,7 @@ trait BitcoinScriptUtil {
|
|||
* [[https://github.com/bitcoin/bitcoin/blob/528472111b4965b1a99c4bcf08ac5ec93d87f10f/src/script/interpreter.cpp#L214-L223]]
|
||||
*/
|
||||
def isValidPubKeyEncoding(
|
||||
pubKey: ECPublicKey,
|
||||
pubKey: ECPublicKeyBytes,
|
||||
sigVersion: SignatureVersion,
|
||||
flags: Seq[ScriptFlag]): Option[ScriptError] = {
|
||||
if (
|
||||
|
|
|
@ -256,7 +256,7 @@ object BitcoinSigner extends SignerUtils {
|
|||
psbt
|
||||
.inputMaps(inputIndex)
|
||||
.partialSignatures
|
||||
.exists(_.pubKey == signer.publicKey)
|
||||
.exists(_.pubKey.toPublicKey == signer.publicKey)
|
||||
) {
|
||||
throw new IllegalArgumentException(
|
||||
"Input has already been signed with this key")
|
||||
|
@ -331,7 +331,7 @@ sealed abstract class RawSingleKeyBitcoinSigner[-InputType <: RawInputInfo]
|
|||
signSingle(spendingInfo.toSingle(0), unsignedTx, isDummySignature)
|
||||
|
||||
val scriptSig =
|
||||
keyAndSigToScriptSig(partialSignature.pubKey,
|
||||
keyAndSigToScriptSig(partialSignature.pubKey.toPublicKey,
|
||||
partialSignature.signature,
|
||||
spendingInfoToSatisfy)
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.bitcoins.core.script.crypto.HashType
|
|||
import org.bitcoins.core.util.{BitcoinScriptUtil, BytesUtil}
|
||||
import org.bitcoins.crypto.{
|
||||
ECPublicKey,
|
||||
ECPublicKeyBytes,
|
||||
LowRDummyECDigitalSignature,
|
||||
NetworkElement,
|
||||
Sign
|
||||
|
@ -352,8 +353,10 @@ object RawInputInfo {
|
|||
case p2pk: P2PKScriptPubKey =>
|
||||
P2PKInputInfo(outPoint, amount, p2pk)
|
||||
case p2pkh: P2PKHScriptPubKey =>
|
||||
hashPreImages.collectFirst { case pubKey: ECPublicKey =>
|
||||
pubKey
|
||||
hashPreImages.collectFirst {
|
||||
case pubKey: ECPublicKey =>
|
||||
pubKey
|
||||
case pubKeyBytes: ECPublicKeyBytes => pubKeyBytes.toPublicKey
|
||||
} match {
|
||||
case None =>
|
||||
throw new IllegalArgumentException(
|
||||
|
@ -418,7 +421,8 @@ case class P2PKInputInfo(
|
|||
override def conditionalPath: ConditionalPath =
|
||||
ConditionalPath.NoCondition
|
||||
|
||||
override def pubKeys: Vector[ECPublicKey] = Vector(scriptPubKey.publicKey)
|
||||
override def pubKeys: Vector[ECPublicKey] = Vector(
|
||||
scriptPubKey.publicKey.toPublicKey)
|
||||
|
||||
override def requiredSigs: Int = 1
|
||||
}
|
||||
|
@ -454,7 +458,8 @@ case class P2PKWithTimeoutInputInfo(
|
|||
}
|
||||
|
||||
override def pubKeys: Vector[ECPublicKey] =
|
||||
Vector(scriptPubKey.pubKey, scriptPubKey.timeoutPubKey)
|
||||
Vector(scriptPubKey.pubKey.toPublicKey,
|
||||
scriptPubKey.timeoutPubKey.toPublicKey)
|
||||
|
||||
override def requiredSigs: Int = 1
|
||||
}
|
||||
|
@ -468,7 +473,8 @@ case class MultiSignatureInputInfo(
|
|||
override def conditionalPath: ConditionalPath =
|
||||
ConditionalPath.NoCondition
|
||||
|
||||
override def pubKeys: Vector[ECPublicKey] = scriptPubKey.publicKeys.toVector
|
||||
override def pubKeys: Vector[ECPublicKey] =
|
||||
scriptPubKey.publicKeys.map(_.toPublicKey).toVector
|
||||
|
||||
override def requiredSigs: Int = scriptPubKey.requiredSigs
|
||||
}
|
||||
|
@ -545,7 +551,7 @@ object SegwitV0NativeInputInfo {
|
|||
Vector.empty): SegwitV0NativeInputInfo = {
|
||||
scriptWitness match {
|
||||
case p2wpkh: P2WPKHWitnessV0 =>
|
||||
P2WPKHV0InputInfo(outPoint, amount, p2wpkh.pubKey)
|
||||
P2WPKHV0InputInfo(outPoint, amount, p2wpkh.pubKey.toPublicKey)
|
||||
case p2wsh: P2WSHWitnessV0 =>
|
||||
P2WSHV0InputInfo(outPoint,
|
||||
amount,
|
||||
|
|
|
@ -27,7 +27,7 @@ class ECPrivateKeyTest extends BitcoinSCryptoTest {
|
|||
}
|
||||
|
||||
it must "not serialize a ECPrivateKey toString" in {
|
||||
ECPrivateKey().toString must be("Masked(ECPrivateKeyImpl)")
|
||||
ECPrivateKey().toString must be("Masked(ECPrivateKey)")
|
||||
}
|
||||
|
||||
it must "successfully negate itself" in {
|
||||
|
|
|
@ -2,8 +2,6 @@ package org.bitcoins.crypto
|
|||
|
||||
import scodec.bits._
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
class ECPublicKeyTest extends BitcoinSCryptoTest {
|
||||
|
||||
it must "be able to decompress keys" in {
|
||||
|
@ -30,17 +28,17 @@ class ECPublicKeyTest extends BitcoinSCryptoTest {
|
|||
|
||||
it must "decompress keys correctly" in {
|
||||
forAll(CryptoGenerators.privateKey) { privKey =>
|
||||
val pubKey = privKey.publicKey
|
||||
val privKeyBytes = ECPrivateKeyBytes(privKey.bytes)
|
||||
val pubKey = privKeyBytes.publicKeyBytes
|
||||
|
||||
assert(privKey.isCompressed)
|
||||
assert(privKeyBytes.isCompressed)
|
||||
assert(pubKey.isCompressed)
|
||||
|
||||
val decompressedPrivKey =
|
||||
ECPrivateKey(privKey.bytes, isCompressed = false)(
|
||||
ExecutionContext.global)
|
||||
ECPrivateKeyBytes(privKey.bytes, isCompressed = false)
|
||||
val decompressedPubKey = pubKey.decompressed
|
||||
|
||||
assert(decompressedPrivKey.publicKey == decompressedPubKey)
|
||||
assert(decompressedPrivKey.publicKeyBytes == decompressedPubKey)
|
||||
assert(pubKey.bytes.tail == decompressedPubKey.bytes.splitAt(33)._1.tail)
|
||||
}
|
||||
}
|
||||
|
@ -53,27 +51,25 @@ class ECPublicKeyTest extends BitcoinSCryptoTest {
|
|||
}
|
||||
|
||||
it must "be able to compress/decompress public keys" in {
|
||||
val privkey = ECPrivateKey.freshPrivateKey
|
||||
val privkey = ECPrivateKeyBytes.freshPrivateKey
|
||||
assert(CryptoUtil.secKeyVerify(privkey.bytes))
|
||||
assert(privkey.isCompressed)
|
||||
|
||||
val notCompressedKey =
|
||||
ECPrivateKey(bytes = privkey.bytes, isCompressed = false)
|
||||
val pubkey = CryptoUtil.toPublicKey(notCompressedKey)
|
||||
ECPrivateKeyBytes(bytes = privkey.bytes, isCompressed = false)
|
||||
val pubkey = notCompressedKey.publicKeyBytes
|
||||
assert(CryptoUtil.isValidPubKey(pubkey.bytes))
|
||||
assert(!pubkey.isCompressed)
|
||||
|
||||
val compressed = privkey.publicKey
|
||||
val compressed = privkey.publicKeyBytes
|
||||
assert(CryptoUtil.isValidPubKey(compressed.bytes))
|
||||
assert(compressed.isCompressed)
|
||||
|
||||
val converted =
|
||||
CryptoUtil.publicKeyConvert(pubkey, compressed = true)
|
||||
val converted = pubkey.compressed
|
||||
assert(CryptoUtil.isValidPubKey(converted.bytes))
|
||||
assert(converted.isCompressed)
|
||||
|
||||
val decompressed =
|
||||
CryptoUtil.publicKeyConvert(compressed, compressed = false)
|
||||
val decompressed = compressed.decompressed
|
||||
assert(CryptoUtil.isValidPubKey(decompressed.bytes))
|
||||
assert(!decompressed.isCompressed)
|
||||
|
||||
|
@ -84,8 +80,7 @@ class ECPublicKeyTest extends BitcoinSCryptoTest {
|
|||
}
|
||||
|
||||
it must "not be able to add opposite public keys" in {
|
||||
val privkey = ECPrivateKey.freshPrivateKey
|
||||
val pubkey1 = privkey.publicKey
|
||||
val pubkey1 = ECPublicKey.freshPublicKey
|
||||
val firstByte: Byte =
|
||||
if (pubkey1.bytes.head == 0x02) 0x03
|
||||
else if (pubkey1.bytes.head == 0x03) 0x02
|
||||
|
@ -93,21 +88,37 @@ class ECPublicKeyTest extends BitcoinSCryptoTest {
|
|||
val pubkey2 =
|
||||
ECPublicKey.fromBytes(ByteVector(firstByte) ++ pubkey1.bytes.tail)
|
||||
|
||||
assertThrows[Exception] {
|
||||
val sumKey = CryptoUtil.add(pubkey1, pubkey2)
|
||||
if (sumKey == ECPublicKey.infinity) fail()
|
||||
}
|
||||
assertThrows[Exception](CryptoUtil.add(pubkey1, pubkey2))
|
||||
assertThrows[Exception](
|
||||
CryptoUtil.add(pubkey1.compressed, pubkey2.compressed))
|
||||
assertThrows[Exception](
|
||||
CryptoUtil.add(pubkey1.decompressed, pubkey2.decompressed))
|
||||
}
|
||||
|
||||
val decompressedPubkey1 =
|
||||
CryptoUtil.publicKeyConvert(pubkey1, compressed = false)
|
||||
it must "be able to add multiple public keys together with sub-sums of 0x00" in {
|
||||
val pubkey1 = ECPublicKey.freshPublicKey
|
||||
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 pubkey3 = ECPublicKey.freshPublicKey
|
||||
val firstByte2: Byte =
|
||||
if (pubkey3.bytes.head == 0x02) 0x03
|
||||
else if (pubkey3.bytes.head == 0x03) 0x02
|
||||
else pubkey3.bytes.head
|
||||
val pubkey4 =
|
||||
ECPublicKey.fromBytes(ByteVector(firstByte2) ++ pubkey3.bytes.tail)
|
||||
|
||||
val decompressedPubkey2 =
|
||||
CryptoUtil.publicKeyConvert(pubkey2, compressed = false)
|
||||
|
||||
assertThrows[Exception] {
|
||||
val sumKey = CryptoUtil.add(decompressedPubkey1, decompressedPubkey2)
|
||||
if (sumKey == ECPublicKey.infinity) fail()
|
||||
}
|
||||
assert(
|
||||
CryptoUtil.combinePubKeys(Vector(pubkey1, pubkey2, pubkey3)) == pubkey3)
|
||||
assert(
|
||||
CryptoUtil.combinePubKeys(Vector(pubkey3, pubkey1, pubkey2)) == pubkey3)
|
||||
assertThrows[Exception](
|
||||
CryptoUtil.combinePubKeys(Vector(pubkey1, pubkey2, pubkey3, pubkey4)))
|
||||
assertThrows[Exception](
|
||||
CryptoUtil.combinePubKeys(Vector(pubkey1, pubkey3, pubkey2, pubkey4)))
|
||||
}
|
||||
|
||||
it must "correctly compress keys" in {
|
||||
|
@ -116,16 +127,22 @@ class ECPublicKeyTest extends BitcoinSCryptoTest {
|
|||
val pubKeyCompressed = pubKey.compressed
|
||||
val pubKeyDecompressed = pubKey.decompressed
|
||||
|
||||
if (privKey.isCompressed) {
|
||||
assert(pubKey == pubKeyCompressed)
|
||||
} else {
|
||||
assert(pubKey == pubKeyDecompressed)
|
||||
}
|
||||
assert(!pubKey.isCompressed)
|
||||
assert(pubKeyCompressed.isFullyValid)
|
||||
assert(pubKeyDecompressed.isFullyValid)
|
||||
|
||||
assert(pubKeyCompressed.decompressed == pubKeyDecompressed)
|
||||
assert(pubKeyCompressed.compressed == pubKeyCompressed)
|
||||
assert(pubKeyDecompressed.compressed == pubKeyCompressed)
|
||||
assert(pubKeyDecompressed.decompressed == pubKeyDecompressed)
|
||||
assert(pubKeyCompressed == pubKeyDecompressed)
|
||||
assert(!pubKeyCompressed.decompressed.isCompressed)
|
||||
assert(pubKeyCompressed.compressed.isCompressed)
|
||||
assert(pubKeyDecompressed.compressed.isCompressed)
|
||||
assert(!pubKeyDecompressed.decompressed.isCompressed)
|
||||
assert(pubKeyCompressed == pubKey)
|
||||
assert(pubKeyDecompressed == pubKey)
|
||||
assert(
|
||||
pubKeyCompressed.bytes.tail == pubKeyDecompressed.decompressedBytes
|
||||
.splitAt(33)
|
||||
._1
|
||||
.tail)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,8 +63,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
|
|||
override def toPublicKey(privateKey: ECPrivateKey): ECPublicKey = {
|
||||
val buffer = CryptoJsUtil.toNodeBuffer(privateKey.bytes)
|
||||
val pubKeyBuffer =
|
||||
SECP256k1.publicKeyCreate(key = buffer,
|
||||
compressed = privateKey.isCompressed)
|
||||
SECP256k1.publicKeyCreate(key = buffer, compressed = false)
|
||||
val privKeyByteVec = CryptoJsUtil.toByteVector(pubKeyBuffer)
|
||||
ECPublicKey.fromBytes(privKeyByteVec)
|
||||
}
|
||||
|
@ -151,8 +150,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
|
|||
|
||||
override def publicKey(privateKey: ECPrivateKey): ECPublicKey = {
|
||||
val buffer = CryptoJsUtil.toNodeBuffer(privateKey.bytes)
|
||||
val bufferPubKey =
|
||||
SECP256k1.publicKeyCreate(buffer, privateKey.isCompressed)
|
||||
val bufferPubKey = SECP256k1.publicKeyCreate(buffer, compressed = false)
|
||||
val byteVec = CryptoJsUtil.toByteVector(bufferPubKey)
|
||||
ECPublicKey.fromBytes(byteVec)
|
||||
}
|
||||
|
@ -178,7 +176,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
|
|||
}
|
||||
|
||||
override def verify(
|
||||
publicKey: ECPublicKey,
|
||||
publicKey: PublicKey[_],
|
||||
data: ByteVector,
|
||||
signature: ECDigitalSignature): Boolean = {
|
||||
val dataBuffer = CryptoJsUtil.toNodeBuffer(data)
|
||||
|
@ -190,7 +188,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
|
|||
override def tweakMultiply(
|
||||
publicKey: ECPublicKey,
|
||||
tweak: FieldElement): ECPublicKey = {
|
||||
val pubKeyBuffer = CryptoJsUtil.toNodeBuffer(publicKey.bytes)
|
||||
val pubKeyBuffer = CryptoJsUtil.toNodeBuffer(publicKey.decompressedBytes)
|
||||
val tweakBuffer = CryptoJsUtil.toNodeBuffer(tweak.bytes)
|
||||
val keyBuffer =
|
||||
SECP256k1.publicKeyTweakMul(pubKeyBuffer, tweakBuffer, compress = true)
|
||||
|
@ -198,23 +196,9 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
|
|||
ECPublicKey.fromBytes(keyByteVec)
|
||||
}
|
||||
|
||||
def publicKeyConvert(buffer: ByteVector, compressed: Boolean): ByteVector = {
|
||||
val pubKeyBuffer =
|
||||
publicKeyConvert(CryptoJsUtil.toNodeBuffer(buffer), compressed)
|
||||
CryptoJsUtil.toByteVector(pubKeyBuffer)
|
||||
}
|
||||
|
||||
def publicKeyConvert(buffer: Buffer, compressed: Boolean): Buffer =
|
||||
SECP256k1.publicKeyConvert(buffer, compressed)
|
||||
|
||||
override def publicKeyConvert(
|
||||
key: ECPublicKey,
|
||||
compressed: Boolean): ECPublicKey =
|
||||
ECPublicKey(publicKeyConvert(key.bytes, compressed))
|
||||
|
||||
override def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey = {
|
||||
val pk1Buffer = CryptoJsUtil.toNodeBuffer(pk1.bytes)
|
||||
val pk2Buffer = CryptoJsUtil.toNodeBuffer(pk2.bytes)
|
||||
val pk1Buffer = CryptoJsUtil.toNodeBuffer(pk1.decompressedBytes)
|
||||
val pk2Buffer = CryptoJsUtil.toNodeBuffer(pk2.decompressedBytes)
|
||||
try {
|
||||
val keyBuffer =
|
||||
SECP256k1.publicKeyCombine(js.Array(pk1Buffer, pk2Buffer),
|
||||
|
@ -223,21 +207,13 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
|
|||
ECPublicKey.fromBytes(keyBytes)
|
||||
} catch {
|
||||
case ex: JavaScriptException =>
|
||||
val k1: ByteVector = pk1.bytes
|
||||
val k2: ByteVector = pk2.bytes
|
||||
|
||||
// check for infinity
|
||||
val k1: ByteVector =
|
||||
if (pk1.isCompressed) pk1.bytes
|
||||
else publicKeyConvert(pk1.bytes, compressed = true)
|
||||
|
||||
val k2: ByteVector =
|
||||
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.infinity
|
||||
if ((k1.head ^ k2.head) == 0x01 && k1.tail == k2.tail) {
|
||||
throw new IllegalArgumentException(
|
||||
s"Invalid public key sum, got 0x00 = $pk1 + $pk2")
|
||||
} else {
|
||||
throw ex
|
||||
}
|
||||
|
@ -247,7 +223,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
|
|||
override def pubKeyTweakAdd(
|
||||
pubkey: ECPublicKey,
|
||||
privkey: ECPrivateKey): ECPublicKey = {
|
||||
val pubKeyBuffer = CryptoJsUtil.toNodeBuffer(pubkey.bytes)
|
||||
val pubKeyBuffer = CryptoJsUtil.toNodeBuffer(pubkey.decompressedBytes)
|
||||
val privKeyBuffer = CryptoJsUtil.toNodeBuffer(privkey.bytes)
|
||||
val keyBuffer =
|
||||
SECP256k1.publicKeyTweakAdd(pubKeyBuffer, privKeyBuffer, compress = true)
|
||||
|
@ -269,19 +245,19 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
|
|||
hi | lo
|
||||
}
|
||||
|
||||
override def decodePoint(bytes: ByteVector): ECPoint = {
|
||||
override def decodePoint(bytes: ByteVector): SecpPoint = {
|
||||
if (bytes.size == 1 && bytes(0) == 0x00) {
|
||||
ECPointInfinity
|
||||
SecpPointInfinity
|
||||
} else {
|
||||
val decoded = SECP256k1.curve
|
||||
.applyDynamic("decodePoint")(CryptoJsUtil.toNodeBuffer(bytes))
|
||||
.asInstanceOf[Point]
|
||||
|
||||
if (decoded.isInfinity())
|
||||
ECPointInfinity
|
||||
SecpPointInfinity
|
||||
else
|
||||
ECPoint(new BigInteger(decoded.getX().toString()),
|
||||
new BigInteger(decoded.getY().toString()))
|
||||
SecpPoint(new BigInteger(decoded.getX().toString()),
|
||||
new BigInteger(decoded.getY().toString()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -26,25 +26,23 @@ object BouncyCastleUtil {
|
|||
decodePubKey(point, publicKey.isCompressed)
|
||||
}
|
||||
|
||||
private[crypto] def decodePoint(
|
||||
bytes: ByteVector): org.bouncycastle.math.ec.ECPoint = {
|
||||
private[crypto] def decodePoint(bytes: ByteVector): ECPoint = {
|
||||
curve.decodePoint(bytes.toArray)
|
||||
}
|
||||
|
||||
private[crypto] def decodePoint(
|
||||
pubKey: ECPublicKey): org.bouncycastle.math.ec.ECPoint = {
|
||||
decodePoint(pubKey.bytes)
|
||||
private[crypto] def decodePoint(pubKey: ECPublicKey): ECPoint = {
|
||||
decodePoint(pubKey.decompressedBytes)
|
||||
}
|
||||
|
||||
private[crypto] def decodePubKey(
|
||||
point: org.bouncycastle.math.ec.ECPoint,
|
||||
point: ECPoint,
|
||||
isCompressed: Boolean = true): ECPublicKey = {
|
||||
val bytes = point.getEncoded(isCompressed)
|
||||
ECPublicKey.fromBytes(ByteVector(bytes))
|
||||
}
|
||||
|
||||
def validatePublicKey(bytes: ByteVector): Boolean = {
|
||||
Try(decodePoint(bytes))
|
||||
bytes != ByteVector(0x00) && Try(decodePoint(bytes))
|
||||
.map(_.getCurve == curve)
|
||||
.getOrElse(false)
|
||||
}
|
||||
|
@ -68,7 +66,7 @@ object BouncyCastleUtil {
|
|||
def computePublicKey(privateKey: ECPrivateKey): ECPublicKey = {
|
||||
val priv = getBigInteger(privateKey.bytes)
|
||||
val point = G.multiply(priv)
|
||||
val pubBytes = ByteVector(point.getEncoded(privateKey.isCompressed))
|
||||
val pubBytes = ByteVector(point.getEncoded(false))
|
||||
require(
|
||||
ECPublicKey.isFullyValid(pubBytes),
|
||||
s"Bouncy Castle failed to generate a valid public key, got: ${CryptoBytesUtil
|
||||
|
@ -149,7 +147,7 @@ object BouncyCastleUtil {
|
|||
|
||||
def verifyDigitalSignature(
|
||||
data: ByteVector,
|
||||
publicKey: ECPublicKey,
|
||||
publicKey: PublicKey[_],
|
||||
signature: ECDigitalSignature): Boolean = {
|
||||
val resultTry = Try {
|
||||
val publicKeyParams =
|
||||
|
|
|
@ -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,6 +9,7 @@ import org.bouncycastle.crypto.params.{
|
|||
ECPrivateKeyParameters,
|
||||
KeyParameter
|
||||
}
|
||||
import org.bouncycastle.math.ec.ECPoint
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import java.math.BigInteger
|
||||
|
@ -34,16 +34,14 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
|
|||
keypair.getPrivate.asInstanceOf[ECPrivateKeyParameters]
|
||||
val priv: BigInteger = privParams.getD
|
||||
val bytes = ByteVector(priv.toByteArray)
|
||||
ECPrivateKey.fromBytes(bytes)
|
||||
ECPrivateKey.fromBytes(bytes.padLeft(33))
|
||||
}
|
||||
|
||||
/** @param x x coordinate
|
||||
* @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) {
|
||||
|
@ -148,7 +146,7 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
|
|||
}
|
||||
|
||||
override def verify(
|
||||
publicKey: ECPublicKey,
|
||||
publicKey: PublicKey[_],
|
||||
data: ByteVector,
|
||||
signature: ECDigitalSignature): Boolean =
|
||||
BouncyCastleUtil.verifyDigitalSignature(data, publicKey, signature)
|
||||
|
@ -156,11 +154,6 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
|
|||
override def publicKey(privateKey: ECPrivateKey): ECPublicKey =
|
||||
BouncyCastleUtil.computePublicKey(privateKey)
|
||||
|
||||
override def publicKeyConvert(
|
||||
key: ECPublicKey,
|
||||
compressed: Boolean): ECPublicKey =
|
||||
BouncyCastleUtil.publicKeyConvert(key, compressed)
|
||||
|
||||
override def tweakMultiply(
|
||||
publicKey: ECPublicKey,
|
||||
tweak: FieldElement): ECPublicKey =
|
||||
|
@ -200,14 +193,14 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
|
|||
sh.doFinal()
|
||||
}
|
||||
|
||||
override def decodePoint(bytes: ByteVector): crypto.ECPoint = {
|
||||
override def decodePoint(bytes: ByteVector): SecpPoint = {
|
||||
val decoded = BouncyCastleUtil.decodePoint(bytes)
|
||||
|
||||
if (decoded.isInfinity)
|
||||
crypto.ECPointInfinity
|
||||
SecpPointInfinity
|
||||
else
|
||||
crypto.ECPoint(decoded.getRawXCoord.getEncoded,
|
||||
decoded.getRawYCoord.getEncoded)
|
||||
SecpPoint(decoded.getRawXCoord.getEncoded,
|
||||
decoded.getRawYCoord.getEncoded)
|
||||
}
|
||||
|
||||
override def pbkdf2WithSha512(
|
||||
|
|
|
@ -44,8 +44,7 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
|
|||
|
||||
override def toPublicKey(privateKey: ECPrivateKey): ECPublicKey = {
|
||||
val pubKeyBytes: Array[Byte] =
|
||||
NativeSecp256k1.computePubkey(privateKey.bytes.toArray,
|
||||
privateKey.isCompressed)
|
||||
NativeSecp256k1.computePubkey(privateKey.bytes.toArray, false)
|
||||
val pubBytes = ByteVector(pubKeyBytes)
|
||||
require(
|
||||
ECPublicKey.isFullyValid(pubBytes),
|
||||
|
@ -77,7 +76,7 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
|
|||
NativeSecp256k1.secKeyVerify(privateKeyBytes.toArray)
|
||||
|
||||
override def verify(
|
||||
publicKey: ECPublicKey,
|
||||
publicKey: PublicKey[_],
|
||||
data: ByteVector,
|
||||
signature: ECDigitalSignature): Boolean = {
|
||||
val result =
|
||||
|
@ -96,17 +95,16 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
|
|||
} else result
|
||||
}
|
||||
|
||||
override def decompressed(publicKey: ECPublicKey): ECPublicKey = {
|
||||
override def decompressed[PK <: PublicKey[PK]](publicKey: PK): PK = {
|
||||
if (publicKey.isCompressed) {
|
||||
val decompressed = NativeSecp256k1.decompress(publicKey.bytes.toArray)
|
||||
ECPublicKey.fromBytes(ByteVector(decompressed))
|
||||
publicKey.fromBytes(ByteVector(decompressed))
|
||||
} else publicKey
|
||||
}
|
||||
|
||||
override def publicKey(privateKey: ECPrivateKey): ECPublicKey = {
|
||||
val pubKeyBytes: Array[Byte] =
|
||||
NativeSecp256k1.computePubkey(privateKey.bytes.toArray,
|
||||
privateKey.isCompressed)
|
||||
NativeSecp256k1.computePubkey(privateKey.bytes.toArray, false)
|
||||
val pubBytes = ByteVector(pubKeyBytes)
|
||||
require(
|
||||
ECPublicKey.isFullyValid(pubBytes),
|
||||
|
@ -115,17 +113,13 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
|
|||
ECPublicKey(pubBytes)
|
||||
}
|
||||
|
||||
override def publicKeyConvert(
|
||||
key: ECPublicKey,
|
||||
compressed: Boolean): ECPublicKey =
|
||||
BouncycastleCryptoRuntime.publicKeyConvert(key, compressed)
|
||||
|
||||
override def tweakMultiply(
|
||||
publicKey: ECPublicKey,
|
||||
tweak: FieldElement): ECPublicKey = {
|
||||
val mulBytes = NativeSecp256k1.pubKeyTweakMul(publicKey.bytes.toArray,
|
||||
tweak.bytes.toArray,
|
||||
publicKey.isCompressed)
|
||||
val mulBytes = NativeSecp256k1.pubKeyTweakMul(
|
||||
publicKey.decompressedBytes.toArray,
|
||||
tweak.bytes.toArray,
|
||||
false)
|
||||
ECPublicKey(ByteVector(mulBytes))
|
||||
}
|
||||
|
||||
|
@ -142,8 +136,16 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
|
|||
}
|
||||
|
||||
override def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey = {
|
||||
val summands = Array(pk1.bytes.toArray, pk2.bytes.toArray)
|
||||
val sumKey = NativeSecp256k1.pubKeyCombine(summands, pk1.isCompressed)
|
||||
val summands =
|
||||
Array(pk1.decompressedBytes.toArray, pk2.decompressedBytes.toArray)
|
||||
val sumKey = NativeSecp256k1.pubKeyCombine(summands, false)
|
||||
|
||||
ECPublicKey(ByteVector(sumKey))
|
||||
}
|
||||
|
||||
override def combinePubKeys(pubKeys: Vector[ECPublicKey]): ECPublicKey = {
|
||||
val summands = pubKeys.map(_.decompressedBytes.toArray).toArray
|
||||
val sumKey = NativeSecp256k1.pubKeyCombine(summands, false)
|
||||
|
||||
ECPublicKey(ByteVector(sumKey))
|
||||
}
|
||||
|
@ -151,9 +153,10 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
|
|||
override def pubKeyTweakAdd(
|
||||
pubkey: ECPublicKey,
|
||||
privkey: ECPrivateKey): ECPublicKey = {
|
||||
val tweaked = NativeSecp256k1.pubKeyTweakAdd(pubkey.bytes.toArray,
|
||||
privkey.bytes.toArray,
|
||||
privkey.isCompressed)
|
||||
val tweaked = NativeSecp256k1.pubKeyTweakAdd(
|
||||
pubkey.decompressedBytes.toArray,
|
||||
privkey.bytes.toArray,
|
||||
false)
|
||||
ECPublicKey(ByteVector(tweaked))
|
||||
}
|
||||
|
||||
|
@ -214,10 +217,11 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
|
|||
adaptorPoint: ECPublicKey,
|
||||
msg: ByteVector,
|
||||
auxRand: ByteVector): ECAdaptorSignature = {
|
||||
val sig = NativeSecp256k1.adaptorSign(key.bytes.toArray,
|
||||
adaptorPoint.bytes.toArray,
|
||||
msg.toArray,
|
||||
auxRand.toArray)
|
||||
val sig = NativeSecp256k1.adaptorSign(
|
||||
key.bytes.toArray,
|
||||
adaptorPoint.decompressedBytes.toArray,
|
||||
msg.toArray,
|
||||
auxRand.toArray)
|
||||
ECAdaptorSignature(ByteVector(sig))
|
||||
}
|
||||
|
||||
|
@ -237,7 +241,7 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
|
|||
val secretBytes = NativeSecp256k1.adaptorExtractSecret(
|
||||
signature.bytes.toArray,
|
||||
adaptorSignature.bytes.toArray,
|
||||
key.bytes.toArray)
|
||||
key.decompressedBytes.toArray)
|
||||
|
||||
ECPrivateKey(ByteVector(secretBytes))
|
||||
}
|
||||
|
@ -248,9 +252,9 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
|
|||
msg: ByteVector,
|
||||
adaptorPoint: ECPublicKey): Boolean = {
|
||||
NativeSecp256k1.adaptorVerify(adaptorSignature.bytes.toArray,
|
||||
key.bytes.toArray,
|
||||
key.decompressedBytes.toArray,
|
||||
msg.toArray,
|
||||
adaptorPoint.bytes.toArray)
|
||||
adaptorPoint.decompressedBytes.toArray)
|
||||
}
|
||||
|
||||
override def isValidSignatureEncoding(
|
||||
|
@ -263,16 +267,16 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
|
|||
override def sipHash(item: ByteVector, key: SipHashKey): Long =
|
||||
BouncycastleCryptoRuntime.sipHash(item, key)
|
||||
|
||||
override def decodePoint(bytes: ByteVector): ECPoint = {
|
||||
override def decodePoint(bytes: ByteVector): SecpPoint = {
|
||||
val infinityPt: ByteVector = ByteVector.fromByte(0x00)
|
||||
|
||||
if (bytes == infinityPt) {
|
||||
ECPointInfinity
|
||||
SecpPointInfinity
|
||||
} else {
|
||||
val pointBytes = NativeSecp256k1.decompress(bytes.toArray)
|
||||
val xBytes = pointBytes.tail.take(32)
|
||||
val yBytes = pointBytes.takeRight(32)
|
||||
ECPoint(xBytes, yBytes)
|
||||
SecpPoint(xBytes, yBytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ object AdaptorUtil {
|
|||
val randHash = CryptoUtil.sha256ECDSAAdaptorAux(auxRand).bytes
|
||||
val maskedKey = randHash.xor(privKey.bytes)
|
||||
|
||||
val bytesToHash = maskedKey ++ adaptorPoint.compressed.bytes ++ message
|
||||
val bytesToHash = maskedKey ++ adaptorPoint.bytes ++ message
|
||||
val nonceHash = algoName match {
|
||||
case "DLEQ" => CryptoUtil.sha256DLEQ(bytesToHash)
|
||||
case "ECDSAadaptor/non" => CryptoUtil.sha256ECDSAAdaptorNonce(bytesToHash)
|
||||
|
@ -55,10 +55,10 @@ object AdaptorUtil {
|
|||
r: ECPublicKey,
|
||||
privateKey: ECPrivateKey): FieldElement = {
|
||||
CryptoUtil.decodePoint(r) match {
|
||||
case ECPointInfinity =>
|
||||
case SecpPointInfinity =>
|
||||
throw new IllegalArgumentException(
|
||||
s"Invalid point, got=$ECPointInfinity")
|
||||
case point: ECPointImpl =>
|
||||
s"Invalid point, got=$SecpPointInfinity")
|
||||
case point: SecpPointFinite =>
|
||||
val rx = FieldElement(point.x.toBigInteger)
|
||||
val x = privateKey.fieldElement
|
||||
val m = FieldElement(dataToSign)
|
||||
|
@ -106,7 +106,7 @@ object AdaptorUtil {
|
|||
val untweakedPoint =
|
||||
m.getPublicKey.add(pubKey.tweakMultiply(rx)).tweakMultiply(s.inverse)
|
||||
|
||||
FieldElement(untweakedPoint.compressed.bytes.tail)
|
||||
FieldElement(untweakedPoint.bytes.tail)
|
||||
}
|
||||
|
||||
/** https://github.com/discreetlogcontracts/dlcspecs/blob/d01595b70269d4204b05510d19bba6a4f4fcff23/ECDSA-adaptor.md#encryption-verification */
|
||||
|
@ -163,9 +163,8 @@ object AdaptorUtil {
|
|||
|
||||
val secretOrNeg = adaptorSig.adaptedS.multInv(FieldElement(sig.s))
|
||||
|
||||
require(
|
||||
secretOrNeg.getPublicKey.compressed.bytes.tail == adaptor.compressed.bytes.tail,
|
||||
s"Invalid inputs: $sig, $adaptorSig, and $adaptor")
|
||||
require(secretOrNeg.getPublicKey.bytes.tail == adaptor.bytes.tail,
|
||||
s"Invalid inputs: $sig, $adaptorSig, and $adaptor")
|
||||
|
||||
if (secretOrNeg.getPublicKey == adaptor) {
|
||||
secretOrNeg.toPrivateKey
|
||||
|
|
|
@ -170,11 +170,6 @@ trait CryptoRuntime {
|
|||
|
||||
def publicKey(privateKey: ECPrivateKey): ECPublicKey
|
||||
|
||||
/** Converts the given public key from its current representation to compressed/not compressed
|
||||
* depending upon how [[compressed]] is set
|
||||
*/
|
||||
def publicKeyConvert(key: ECPublicKey, compressed: Boolean): ECPublicKey
|
||||
|
||||
def sign(privateKey: ECPrivateKey, dataToSign: ByteVector): ECDigitalSignature
|
||||
|
||||
def signWithEntropy(
|
||||
|
@ -185,20 +180,20 @@ trait CryptoRuntime {
|
|||
def secKeyVerify(privateKeybytes: ByteVector): Boolean
|
||||
|
||||
def verify(
|
||||
publicKey: ECPublicKey,
|
||||
publicKey: PublicKey[_],
|
||||
data: ByteVector,
|
||||
signature: ECDigitalSignature): Boolean
|
||||
|
||||
def decompressed(publicKey: ECPublicKey): ECPublicKey = {
|
||||
def decompressed[PK <: PublicKey[PK]](publicKey: PK): PK = {
|
||||
if (publicKey.isCompressed) {
|
||||
decodePoint(publicKey.bytes) match {
|
||||
case ECPointInfinity => ECPublicKey.fromHex("00")
|
||||
case point: ECPointImpl =>
|
||||
case SecpPointInfinity => publicKey.fromHex("00")
|
||||
case point: SecpPointFinite =>
|
||||
val decompressedBytes =
|
||||
ByteVector.fromHex("04").get ++
|
||||
point.x.bytes ++
|
||||
point.y.bytes
|
||||
ECPublicKey(decompressedBytes)
|
||||
publicKey.fromBytes(decompressedBytes)
|
||||
}
|
||||
} else publicKey
|
||||
}
|
||||
|
@ -213,16 +208,50 @@ trait CryptoRuntime {
|
|||
sum.bytes
|
||||
}
|
||||
|
||||
/** Adds two SecpPoints together and correctly handles the point at infinity (0x00). */
|
||||
def add(point1: SecpPoint, point2: SecpPoint): SecpPoint = {
|
||||
(point1, point2) match {
|
||||
case (SecpPointInfinity, p) => p
|
||||
case (p, SecpPointInfinity) => p
|
||||
case (p1: SecpPointFinite, p2: SecpPointFinite) =>
|
||||
val pk1 = p1.toPublicKey
|
||||
val pk2 = p2.toPublicKey
|
||||
|
||||
if (
|
||||
(pk1.bytes.head ^ pk2.bytes.head) == 0x01 && pk1.bytes.tail == pk2.bytes.tail
|
||||
) {
|
||||
SecpPointInfinity
|
||||
} else {
|
||||
add(pk1, pk2).toPoint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds two public keys together, failing if the sum is 0x00 (the point at infinity). */
|
||||
def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey
|
||||
|
||||
/** Adds a Vector of public keys together, failing only if the total sum is 0x00
|
||||
* (the point at infinity), but still succeeding if sub-sums are 0x00.
|
||||
*/
|
||||
def combinePubKeys(pubKeys: Vector[ECPublicKey]): ECPublicKey = {
|
||||
val summandPoints = pubKeys.map(_.toPoint)
|
||||
val sumPoint = summandPoints.reduce[SecpPoint](add(_, _))
|
||||
sumPoint match {
|
||||
case SecpPointInfinity =>
|
||||
throw new IllegalArgumentException(
|
||||
"Sum result was 0x00, an invalid public key.")
|
||||
case p: SecpPointFinite => p.toPublicKey
|
||||
}
|
||||
}
|
||||
|
||||
def pubKeyTweakAdd(pubkey: ECPublicKey, privkey: ECPrivateKey): ECPublicKey
|
||||
|
||||
def isValidPubKey(bytes: ByteVector): Boolean
|
||||
|
||||
def decodePoint(bytes: ByteVector): ECPoint
|
||||
def decodePoint(bytes: ByteVector): SecpPoint
|
||||
|
||||
def decodePoint(pubKey: ECPublicKey): ECPoint = {
|
||||
decodePoint(pubKey.bytes)
|
||||
def decodePoint(pubKey: ECPublicKey): SecpPoint = {
|
||||
decodePoint(pubKey.decompressedBytes)
|
||||
}
|
||||
|
||||
def schnorrSign(
|
||||
|
@ -268,11 +297,12 @@ trait CryptoRuntime {
|
|||
|
||||
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
|
||||
val computedR = challengePoint.toPoint.add(sigPoint.toPoint)
|
||||
computedR match {
|
||||
case SecpPointInfinity => false
|
||||
case point: SecpPointFinite =>
|
||||
!point.y.toBigInteger.testBit(
|
||||
0) && point.toPublicKey.schnorrNonce == rx
|
||||
}
|
||||
case Failure(_) => false
|
||||
}
|
||||
|
|
|
@ -83,11 +83,6 @@ trait CryptoUtil extends CryptoRuntime {
|
|||
override def publicKey(privateKey: ECPrivateKey): ECPublicKey =
|
||||
cryptoRuntime.publicKey(privateKey)
|
||||
|
||||
override def publicKeyConvert(
|
||||
key: ECPublicKey,
|
||||
compressed: Boolean): ECPublicKey =
|
||||
cryptoRuntime.publicKeyConvert(key, compressed)
|
||||
|
||||
override def sign(
|
||||
privateKey: ECPrivateKey,
|
||||
dataToSign: ByteVector): ECDigitalSignature =
|
||||
|
@ -103,12 +98,12 @@ trait CryptoUtil extends CryptoRuntime {
|
|||
cryptoRuntime.secKeyVerify(privateKeybytes)
|
||||
|
||||
override def verify(
|
||||
publicKey: ECPublicKey,
|
||||
publicKey: PublicKey[_],
|
||||
data: ByteVector,
|
||||
signature: ECDigitalSignature): Boolean =
|
||||
cryptoRuntime.verify(publicKey, data, signature)
|
||||
|
||||
override def decompressed(publicKey: ECPublicKey): ECPublicKey =
|
||||
override def decompressed[PK <: PublicKey[PK]](publicKey: PK): PK =
|
||||
cryptoRuntime.decompressed(publicKey)
|
||||
|
||||
override def tweakMultiply(
|
||||
|
@ -122,9 +117,15 @@ trait CryptoUtil extends CryptoRuntime {
|
|||
override def add(pk1: ByteVector, pk2: ECPrivateKey): ByteVector =
|
||||
cryptoRuntime.add(pk1, pk2)
|
||||
|
||||
override def add(point1: SecpPoint, point2: SecpPoint): SecpPoint =
|
||||
cryptoRuntime.add(point1, point2)
|
||||
|
||||
override def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey =
|
||||
cryptoRuntime.add(pk1, pk2)
|
||||
|
||||
override def combinePubKeys(pubKeys: Vector[ECPublicKey]): ECPublicKey =
|
||||
cryptoRuntime.combinePubKeys(pubKeys)
|
||||
|
||||
override def pubKeyTweakAdd(
|
||||
pubkey: ECPublicKey,
|
||||
privkey: ECPrivateKey): ECPublicKey =
|
||||
|
@ -197,7 +198,7 @@ trait CryptoUtil extends CryptoRuntime {
|
|||
override def sipHash(item: ByteVector, key: SipHashKey): Long =
|
||||
cryptoRuntime.sipHash(item, key)
|
||||
|
||||
override def decodePoint(bytes: ByteVector): ECPoint =
|
||||
override def decodePoint(bytes: ByteVector): SecpPoint =
|
||||
cryptoRuntime.decodePoint(bytes)
|
||||
|
||||
override def randomBytes(n: Int): ByteVector = cryptoRuntime.randomBytes(n)
|
||||
|
|
|
@ -45,7 +45,7 @@ object DLEQUtil {
|
|||
tweakedPoint: ECPublicKey,
|
||||
auxRand: ByteVector): FieldElement = {
|
||||
val hash = CryptoUtil
|
||||
.sha256(point.compressed.bytes ++ tweakedPoint.compressed.bytes)
|
||||
.sha256(point.bytes ++ tweakedPoint.bytes)
|
||||
.bytes
|
||||
|
||||
AdaptorUtil.adaptorNonce(hash,
|
||||
|
@ -66,8 +66,8 @@ object DLEQUtil {
|
|||
p2: ECPublicKey): ByteVector = {
|
||||
CryptoUtil
|
||||
.sha256DLEQ(
|
||||
p1.compressed.bytes ++ adaptorPoint.compressed.bytes ++
|
||||
p2.compressed.bytes ++ r1.compressed.bytes ++ r2.compressed.bytes)
|
||||
p1.bytes ++ adaptorPoint.bytes ++
|
||||
p2.bytes ++ r1.bytes ++ r2.bytes)
|
||||
.bytes
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ object ECAdaptorSignature extends Factory[ECAdaptorSignature] {
|
|||
dleqProofE: FieldElement,
|
||||
dleqProofS: FieldElement): ECAdaptorSignature = {
|
||||
fromBytes(
|
||||
tweakedNonce.compressed.bytes ++ untweakedNonce.compressed.bytes ++
|
||||
tweakedNonce.bytes ++ untweakedNonce.bytes ++
|
||||
adaptedS.bytes ++ dleqProofE.bytes ++ dleqProofS.bytes
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,19 +3,143 @@ package org.bitcoins.crypto
|
|||
import scodec.bits.ByteVector
|
||||
|
||||
import java.math.BigInteger
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
/** Represents the raw bytes which are meant to represent an ECKey without deserializing. */
|
||||
sealed trait ECKeyBytes extends NetworkElement
|
||||
|
||||
/** Represents a serialization sensitive ECPrivateKey (such as is used in WIF). */
|
||||
case class ECPrivateKeyBytes(bytes: ByteVector, isCompressed: Boolean = true)
|
||||
extends ECKeyBytes
|
||||
with MaskedToString {
|
||||
val toPrivateKey: ECPrivateKey = ECPrivateKey(bytes)
|
||||
|
||||
/** Returns the raw ECPublicKeyBytes serialized using isCompressed. */
|
||||
def publicKeyBytes: ECPublicKeyBytes = {
|
||||
val pubKey = toPrivateKey.publicKey
|
||||
if (isCompressed) {
|
||||
ECPublicKeyBytes(pubKey.bytes)
|
||||
} else {
|
||||
ECPublicKeyBytes(pubKey.decompressedBytes)
|
||||
}
|
||||
}
|
||||
|
||||
override def toStringSensitive: String = s"ECPrivateKeyBytes($hex)"
|
||||
}
|
||||
|
||||
object ECPrivateKeyBytes extends Factory[ECPrivateKeyBytes] {
|
||||
|
||||
override def fromBytes(bytes: ByteVector): ECPrivateKeyBytes = {
|
||||
val modifiedBytes = ECPrivateKey.fromBytes(bytes).bytes
|
||||
|
||||
new ECPrivateKeyBytes(modifiedBytes)
|
||||
}
|
||||
|
||||
def freshPrivateKey(isCompressed: Boolean): ECPrivateKeyBytes = {
|
||||
CryptoUtil.freshPrivateKey.toPrivateKeyBytes(isCompressed)
|
||||
}
|
||||
|
||||
def freshPrivateKey: ECPrivateKeyBytes = {
|
||||
CryptoUtil.freshPrivateKey.toPrivateKeyBytes()
|
||||
}
|
||||
}
|
||||
|
||||
/** Represents any type which wraps public key bytes which can be used for ECDSA verification.
|
||||
* Should always be instantiated with class X extends PublicKey[X].
|
||||
*/
|
||||
sealed trait PublicKey[PK <: PublicKey[PK]] extends NetworkElement {
|
||||
|
||||
/** The fromBytes function for the PK type. */
|
||||
private[crypto] def fromBytes(bytes: ByteVector): PK
|
||||
|
||||
private[crypto] def fromHex(hex: String): PK = {
|
||||
fromBytes(CryptoBytesUtil.decodeHex(hex))
|
||||
}
|
||||
|
||||
/** Returns this but as a PK. */
|
||||
private def thisAsPK: PK = {
|
||||
this.asInstanceOf[PK]
|
||||
}
|
||||
|
||||
def verify(hash: HashDigest, signature: ECDigitalSignature): Boolean =
|
||||
verify(hash.bytes, signature)
|
||||
|
||||
/** Verifies if a given piece of data is signed by the
|
||||
* [[org.bitcoins.crypto.ECPrivateKey ECPrivateKey]]'s corresponding
|
||||
* [[org.bitcoins.crypto.ECPublicKey ECPublicKey]].
|
||||
*/
|
||||
def verify(data: ByteVector, signature: ECDigitalSignature): Boolean = {
|
||||
CryptoUtil.verify(this, data, signature)
|
||||
}
|
||||
|
||||
def verify(hex: String, signature: ECDigitalSignature): Boolean =
|
||||
verify(CryptoBytesUtil.decodeHex(hex), signature)
|
||||
|
||||
/** Returns true if the underlying bytes being wrapped are compressed */
|
||||
def isCompressed: Boolean = bytes.size == 33
|
||||
|
||||
/** Returns true if the underlying bytes being wrapped are valid according to secp256k1 */
|
||||
def isFullyValid: Boolean = ECPublicKey.isFullyValid(bytes)
|
||||
|
||||
/** Returns the decompressed version of this PublicKey */
|
||||
lazy val decompressed: PK = {
|
||||
if (isCompressed) {
|
||||
CryptoUtil.decompressed(thisAsPK)
|
||||
} else thisAsPK
|
||||
}
|
||||
|
||||
/** Returns the compressed version of this PublicKey */
|
||||
lazy val compressed: PK = {
|
||||
if (isCompressed || bytes == ByteVector.fromByte(0x00)) {
|
||||
thisAsPK
|
||||
} else {
|
||||
val key = if (bytes.length == 65) this else decompressed
|
||||
val (x, y) = key.bytes.tail.splitAt(32)
|
||||
val leadByte = if (FieldElement(y).isEven) 2.toByte else 3.toByte
|
||||
fromBytes(x.+:(leadByte))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Wraps raw ECPublicKey bytes without doing any validation or deserialization (may be invalid). */
|
||||
case class ECPublicKeyBytes(bytes: ByteVector)
|
||||
extends ECKeyBytes
|
||||
with PublicKey[ECPublicKeyBytes] {
|
||||
|
||||
/** Parse these bytes into the bitcoin-s internal public key type. */
|
||||
def toPublicKey: ECPublicKey = ECPublicKey(bytes)
|
||||
|
||||
override private[crypto] def fromBytes(bytes: ByteVector): ECPublicKeyBytes =
|
||||
ECPublicKeyBytes(bytes)
|
||||
}
|
||||
|
||||
object ECPublicKeyBytes extends Factory[ECPublicKeyBytes] {
|
||||
|
||||
override def fromBytes(bytes: ByteVector): ECPublicKeyBytes = {
|
||||
new ECPublicKeyBytes(bytes)
|
||||
}
|
||||
|
||||
def freshPublicKey: ECPublicKeyBytes = {
|
||||
ECPrivateKeyBytes.freshPrivateKey.publicKeyBytes
|
||||
}
|
||||
}
|
||||
|
||||
/** Created by chris on 2/16/16.
|
||||
* Represents a fully parsed and validated ECDSA private or public key.
|
||||
*/
|
||||
sealed abstract class BaseECKey extends NetworkElement
|
||||
|
||||
/** Created by chris on 2/16/16.
|
||||
* A valid deserialized private key.
|
||||
*
|
||||
* Note that there is no notion of compressed vs. decompressed
|
||||
* as there is in Wallet Import Format (WIF), if dealing with
|
||||
* external wallets then ECPrivateKeyBytes may be needed.
|
||||
*/
|
||||
sealed abstract class ECPrivateKey
|
||||
case class ECPrivateKey(bytes: ByteVector)
|
||||
extends BaseECKey
|
||||
with AdaptorSign
|
||||
with MaskedToString {
|
||||
require(CryptoUtil.secKeyVerify(bytes), s"Invalid key, hex: ${bytes.toHex}")
|
||||
|
||||
/** Signs a given sequence of bytes with the signingKey
|
||||
* @param dataToSign the bytes to be signed
|
||||
|
@ -91,16 +215,13 @@ sealed abstract class ECPrivateKey
|
|||
|
||||
def negate: ECPrivateKey = {
|
||||
val negPrivKeyNum = N.subtract(new BigInteger(1, bytes.toArray))
|
||||
ECPrivateKey(ByteVector(negPrivKeyNum.toByteArray))
|
||||
ECPrivateKey(ByteVector(negPrivKeyNum.toByteArray).padLeft(33))
|
||||
}
|
||||
|
||||
def add(other: ECPrivateKey): ECPrivateKey = {
|
||||
CryptoUtil.add(this, other)
|
||||
}
|
||||
|
||||
/** Signifies if the this private key corresponds to a compressed public key */
|
||||
def isCompressed: Boolean
|
||||
|
||||
/** Derives the public for a the private key */
|
||||
override def publicKey: ECPublicKey =
|
||||
CryptoUtil.publicKey(this)
|
||||
|
@ -115,83 +236,61 @@ sealed abstract class ECPrivateKey
|
|||
|
||||
def fieldElement: FieldElement = FieldElement(bytes)
|
||||
|
||||
override def toStringSensitive: String = s"ECPrivateKey($hex,$isCompressed)"
|
||||
override def toStringSensitive: String = s"ECPrivateKey($hex)"
|
||||
|
||||
def toPrivateKeyBytes(isCompressed: Boolean = true): ECPrivateKeyBytes = {
|
||||
ECPrivateKeyBytes(bytes, isCompressed)
|
||||
}
|
||||
}
|
||||
|
||||
object ECPrivateKey extends Factory[ECPrivateKey] {
|
||||
|
||||
private case class ECPrivateKeyImpl(
|
||||
override val bytes: ByteVector,
|
||||
isCompressed: Boolean,
|
||||
ec: ExecutionContext)
|
||||
extends ECPrivateKey {
|
||||
require(CryptoUtil.secKeyVerify(bytes), s"Invalid key, hex: ${bytes.toHex}")
|
||||
}
|
||||
|
||||
def apply(bytes: ByteVector, isCompressed: Boolean)(implicit
|
||||
ec: ExecutionContext): ECPrivateKey = {
|
||||
ECPrivateKeyImpl(bytes, isCompressed, ec)
|
||||
}
|
||||
|
||||
override def fromBytes(bytes: ByteVector): ECPrivateKey =
|
||||
fromBytes(bytes, isCompressed = true)
|
||||
|
||||
@tailrec
|
||||
def fromBytes(bytes: ByteVector, isCompressed: Boolean): ECPrivateKey = {
|
||||
|
||||
override def fromBytes(bytes: ByteVector): ECPrivateKey = {
|
||||
if (bytes.size == 32)
|
||||
ECPrivateKeyImpl(bytes, isCompressed, ExecutionContext.global)
|
||||
else if (bytes.size < 32) {
|
||||
//means we need to pad the private key with 0 bytes so we have 32 bytes
|
||||
ECPrivateKey.fromBytes(bytes.padLeft(32), isCompressed)
|
||||
} //this is for the case when java serialies a BigInteger to 33 bytes to hold the signed num representation
|
||||
else if (bytes.size == 33)
|
||||
ECPrivateKey.fromBytes(bytes.slice(1, 33), isCompressed)
|
||||
else
|
||||
new ECPrivateKey(bytes)
|
||||
else if (bytes.size == 33 && bytes.head == 0x00) {
|
||||
new ECPrivateKey(bytes.slice(1, 33))
|
||||
} else
|
||||
throw new IllegalArgumentException(
|
||||
"Private keys cannot be greater than 33 bytes in size, got: " +
|
||||
"Private keys must be 32 in size, got: " +
|
||||
CryptoBytesUtil.encodeHex(bytes) + " which is of size: " + bytes.size)
|
||||
}
|
||||
|
||||
def fromHex(hex: String, isCompressed: Boolean): ECPrivateKey =
|
||||
fromBytes(CryptoBytesUtil.decodeHex(hex), isCompressed)
|
||||
|
||||
def fromFieldElement(fieldElement: FieldElement): ECPrivateKey = {
|
||||
fieldElement.toPrivateKey
|
||||
}
|
||||
|
||||
/** Generates a fresh [[org.bitcoins.crypto.ECPrivateKey ECPrivateKey]] that has not been used before. */
|
||||
def apply(): ECPrivateKey = ECPrivateKey(true)
|
||||
|
||||
def apply(isCompressed: Boolean): ECPrivateKey = freshPrivateKey(isCompressed)
|
||||
def apply(): ECPrivateKey = ECPrivateKey.freshPrivateKey
|
||||
|
||||
/** Generates a fresh [[org.bitcoins.crypto.ECPrivateKey ECPrivateKey]] that has not been used before. */
|
||||
def freshPrivateKey: ECPrivateKey = freshPrivateKey(true)
|
||||
|
||||
def freshPrivateKey(isCompressed: Boolean): ECPrivateKey = {
|
||||
val priv = CryptoUtil.freshPrivateKey
|
||||
ECPrivateKey.fromBytes(priv.bytes, isCompressed)
|
||||
}
|
||||
def freshPrivateKey: ECPrivateKey = CryptoUtil.freshPrivateKey
|
||||
}
|
||||
|
||||
/** Created by chris on 2/16/16.
|
||||
* A valid deserialized ECDSA public key.
|
||||
*
|
||||
* This class wraps some underlying _bytes but after checking that these _bytes are valid,
|
||||
* all serializations (compressed and decompressed) of this public key are (lazily) computed
|
||||
* where the decompressed version is used internally for computation and the compressed version
|
||||
* is provided by the NetworkElement::bytes member.
|
||||
*
|
||||
* Note that 0x00 is not a valid ECPublicKey but is a valid SecpPoint meaning that if you are
|
||||
* doing computations on public key (points) that may have intermediate 0x00 values, then you
|
||||
* should convert using toPoint, do computation, and then convert back toPublicKey in the end.
|
||||
*/
|
||||
sealed abstract class ECPublicKey extends BaseECKey {
|
||||
case class ECPublicKey(private val _bytes: ByteVector)
|
||||
extends BaseECKey
|
||||
with PublicKey[ECPublicKey] {
|
||||
require(isFullyValid, s"Invalid public key: ${_bytes}")
|
||||
|
||||
def verify(hash: HashDigest, signature: ECDigitalSignature): Boolean =
|
||||
verify(hash.bytes, signature)
|
||||
/** Converts this public key into the raw underlying point on secp256k1 for computation. */
|
||||
def toPoint: SecpPointFinite = SecpPoint.fromPublicKey(this)
|
||||
|
||||
/** Verifies if a given piece of data is signed by the
|
||||
* [[org.bitcoins.crypto.ECPrivateKey ECPrivateKey]]'s corresponding
|
||||
* [[org.bitcoins.crypto.ECPublicKey ECPublicKey]].
|
||||
*/
|
||||
def verify(data: ByteVector, signature: ECDigitalSignature): Boolean = {
|
||||
CryptoUtil.verify(this, data, signature)
|
||||
override private[crypto] def fromBytes(bytes: ByteVector): ECPublicKey = {
|
||||
ECPublicKey.fromBytes(bytes)
|
||||
}
|
||||
|
||||
def verify(hex: String, signature: ECDigitalSignature): Boolean =
|
||||
verify(CryptoBytesUtil.decodeHex(hex), signature)
|
||||
|
||||
def schnorrVerify(
|
||||
data: ByteVector,
|
||||
signature: SchnorrDigitalSignature): Boolean = {
|
||||
|
@ -200,9 +299,8 @@ sealed abstract class ECPublicKey extends BaseECKey {
|
|||
|
||||
def schnorrComputePoint(
|
||||
data: ByteVector,
|
||||
nonce: SchnorrNonce,
|
||||
compressed: Boolean = isCompressed): ECPublicKey = {
|
||||
schnorrPublicKey.computeSigPoint(data, nonce, compressed)
|
||||
nonce: SchnorrNonce): ECPublicKey = {
|
||||
schnorrPublicKey.computeSigPoint(data, nonce)
|
||||
}
|
||||
|
||||
def schnorrPublicKey: SchnorrPublicKey = SchnorrPublicKey(bytes)
|
||||
|
@ -224,31 +322,56 @@ sealed abstract class ECPublicKey extends BaseECKey {
|
|||
|
||||
override def toString: String = "ECPublicKey(" + hex + ")"
|
||||
|
||||
/** Checks if the [[org.bitcoins.crypto.ECPublicKey ECPublicKey]] is compressed */
|
||||
def isCompressed: Boolean = bytes.size == 33
|
||||
/** Returns true only if the underlying wrapped _bytes are compressed */
|
||||
override def isCompressed: Boolean = _bytes.size == 33
|
||||
|
||||
/** Checks if the [[org.bitcoins.crypto.ECPublicKey ECPublicKey]] is valid according to secp256k1 */
|
||||
def isFullyValid: Boolean = ECPublicKey.isFullyValid(bytes)
|
||||
/** @inheritdoc */
|
||||
override def isFullyValid: Boolean = ECPublicKey.isFullyValid(_bytes)
|
||||
|
||||
/** Returns the decompressed version of this [[org.bitcoins.crypto.ECPublicKey ECPublicKey]] */
|
||||
def decompressed: ECPublicKey =
|
||||
CryptoUtil.decompressed(this)
|
||||
|
||||
def compressed: ECPublicKey = {
|
||||
if (isCompressed || bytes == ByteVector.fromByte(0x00)) {
|
||||
/** Returns this same ECPublicKey wrapping the underlying compressed _bytes.
|
||||
* This function doesn't really have any use, don't use it probably.
|
||||
*/
|
||||
override lazy val compressed: ECPublicKey = {
|
||||
if (isCompressed || _bytes == ByteVector.fromByte(0x00)) {
|
||||
this
|
||||
} else {
|
||||
val key = if (bytes.length == 65) this else decompressed
|
||||
val (x, y) = key.bytes.tail.splitAt(32)
|
||||
val key = if (_bytes.length == 65) this else decompressed
|
||||
val (x, y) = key._bytes.tail.splitAt(32)
|
||||
val leadByte = if (FieldElement(y).isEven) 2.toByte else 3.toByte
|
||||
ECPublicKey(x.+:(leadByte))
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the compressed representation of this ECPublicKey */
|
||||
override def bytes: ByteVector = {
|
||||
compressed._bytes
|
||||
}
|
||||
|
||||
/** Returns the decompressed representation of this ECPublicKey */
|
||||
def decompressedBytes: ByteVector = {
|
||||
decompressed._bytes
|
||||
}
|
||||
|
||||
def decompressedHex: String = {
|
||||
decompressedBytes.toHex
|
||||
}
|
||||
|
||||
/** Converts this ECPublicKey to raw ECPublicKeyBytes using the specified serialization. */
|
||||
def toPublicKeyBytes(isCompressed: Boolean = true): ECPublicKeyBytes = {
|
||||
val bs = if (isCompressed) bytes else decompressedBytes
|
||||
|
||||
ECPublicKeyBytes(bs)
|
||||
}
|
||||
|
||||
override def equals(obj: Any): Boolean = {
|
||||
obj match {
|
||||
case pubKey: ECPublicKey => bytes == pubKey.bytes
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds this ECPublicKey to another as points and returns the resulting ECPublicKey.
|
||||
*
|
||||
* Note: if this ever becomes a bottleneck, secp256k1_ec_pubkey_combine should
|
||||
* get wrapped in NativeSecp256k1 to speed things up.
|
||||
* If you are adding more than two points together use CryptoUtil.combinePubKeys instead.
|
||||
*/
|
||||
def add(otherKey: ECPublicKey): ECPublicKey =
|
||||
CryptoUtil.add(this, otherKey)
|
||||
|
@ -260,28 +383,15 @@ sealed abstract class ECPublicKey extends BaseECKey {
|
|||
|
||||
object ECPublicKey extends Factory[ECPublicKey] {
|
||||
|
||||
private case class ECPublicKeyImpl(
|
||||
override val bytes: ByteVector,
|
||||
ec: ExecutionContext)
|
||||
extends ECPublicKey {
|
||||
//unfortunately we cannot place ANY invariants here
|
||||
//because of old transactions on the blockchain that have weirdly formatted public keys. Look at example in script_tests.json
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/test/data/script_tests.json#L457
|
||||
//bitcoin core only checks CPubKey::IsValid()
|
||||
//this means we can have public keys with only one byte i.e. 0x00 or no bytes.
|
||||
//Eventually we would like this to be CPubKey::IsFullyValid() but since we are remaining backwards compatible
|
||||
//we cannot do this. If there ever is a hard fork this would be a good thing to add.
|
||||
}
|
||||
|
||||
override def fromBytes(bytes: ByteVector): ECPublicKey = {
|
||||
ECPublicKeyImpl(bytes, ExecutionContext.global)
|
||||
new ECPublicKey(bytes)
|
||||
}
|
||||
|
||||
def apply(): ECPublicKey = freshPublicKey
|
||||
|
||||
val dummy: ECPublicKey = FieldElement.one.getPublicKey
|
||||
def apply(point: SecpPointFinite): ECPublicKey = point.toPublicKey
|
||||
|
||||
val infinity: ECPublicKey = ECPublicKey.fromBytes(ByteVector(0x00))
|
||||
val dummy: ECPublicKey = FieldElement.one.getPublicKey
|
||||
|
||||
/** Generates a fresh [[org.bitcoins.crypto.ECPublicKey ECPublicKey]] that has not been used before. */
|
||||
def freshPublicKey: ECPublicKey = ECPrivateKey.freshPrivateKey.publicKey
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import java.math.BigInteger
|
||||
|
||||
/** Represents a point on secp256k1 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))
|
||||
}
|
63
crypto/src/main/scala/org/bitcoins/crypto/SecpPoint.scala
Normal file
63
crypto/src/main/scala/org/bitcoins/crypto/SecpPoint.scala
Normal file
|
@ -0,0 +1,63 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import java.math.BigInteger
|
||||
|
||||
/** Represents a point on the secp256k1 elliptic curve. */
|
||||
sealed trait SecpPoint extends NetworkElement {
|
||||
|
||||
/** Returns the group sum of this point and the input. */
|
||||
def add(point: SecpPoint): SecpPoint = {
|
||||
CryptoUtil.add(this, point)
|
||||
}
|
||||
}
|
||||
|
||||
/** The point at infinity, this is the secp256k1 group identity element meaning
|
||||
* p + 0x00 = 0x00 + p = p for any point p and
|
||||
* p + (-p) = 0x00.
|
||||
*
|
||||
* Note that this does not correspond to a valid ECPublicKey just like
|
||||
* FieldElement.zero does not correspond to a valid private key (and in fact
|
||||
* 0x00 = FieldElement.zero*G).
|
||||
*/
|
||||
case object SecpPointInfinity extends SecpPoint {
|
||||
override val bytes: ByteVector = ByteVector(0x00)
|
||||
}
|
||||
|
||||
/** A non-identity point, (x, y), on the secp256k1 elliptic curve.
|
||||
*/
|
||||
case class SecpPointFinite(x: FieldElement, y: FieldElement) extends SecpPoint {
|
||||
|
||||
override def bytes: ByteVector = {
|
||||
ByteVector(0x04) ++ x.bytes ++ y.bytes
|
||||
}
|
||||
|
||||
def toPublicKey: ECPublicKey = {
|
||||
ECPublicKey(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
object SecpPoint {
|
||||
|
||||
def fromPublicKey(key: ECPublicKey): SecpPointFinite = {
|
||||
val (x, y) = key.decompressedBytes.tail.splitAt(32)
|
||||
SecpPointFinite(FieldElement.fromBytes(x), FieldElement.fromBytes(y))
|
||||
}
|
||||
|
||||
def apply(x: ByteVector, y: ByteVector): SecpPointFinite =
|
||||
SecpPointFinite(FieldElement.fromBytes(x), FieldElement.fromBytes(y))
|
||||
|
||||
def apply(x: Array[Byte], y: Array[Byte]): SecpPointFinite =
|
||||
SecpPointFinite(FieldElement.fromByteArray(x),
|
||||
FieldElement.fromByteArray(y))
|
||||
|
||||
def apply(x: BigInteger, y: BigInteger): SecpPointFinite =
|
||||
SecpPointFinite(FieldElement(x), FieldElement(y))
|
||||
|
||||
def apply(x: BigInt, y: BigInt): SecpPointFinite =
|
||||
SecpPointFinite(FieldElement(x), FieldElement(y))
|
||||
|
||||
def apply(x: String, y: String): SecpPointFinite =
|
||||
SecpPointFinite(FieldElement.fromHex(x), FieldElement.fromHex(y))
|
||||
}
|
|
@ -102,10 +102,10 @@ val psbtWithSigHashFlags = psbtWithUpdatedSecondInput
|
|||
// correctly in an application
|
||||
// Here we use the relevant private keys to sign the first input
|
||||
val privKey0 = ECPrivateKeyUtil.fromWIFToPrivateKey(
|
||||
"cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr")
|
||||
"cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr").toPrivateKey
|
||||
|
||||
val privKey1 = ECPrivateKeyUtil.fromWIFToPrivateKey(
|
||||
"cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d")
|
||||
"cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d").toPrivateKey
|
||||
|
||||
val psbtFirstSig =
|
||||
psbtWithSigHashFlags
|
||||
|
|
|
@ -6,7 +6,7 @@ import org.bitcoins.core.number.{Int32, UInt32}
|
|||
import org.bitcoins.core.protocol.script._
|
||||
import org.bitcoins.core.protocol.transaction._
|
||||
import org.bitcoins.core.psbt.PSBT
|
||||
import org.bitcoins.crypto.{DoubleSha256Digest, ECPublicKey}
|
||||
import org.bitcoins.crypto.{DoubleSha256Digest, ECPublicKeyBytes}
|
||||
|
||||
/** Created by chris on 2/12/16.
|
||||
*/
|
||||
|
@ -136,7 +136,7 @@ trait TransactionTestUtil {
|
|||
Transaction,
|
||||
Int,
|
||||
ScriptPubKey,
|
||||
Seq[ECPublicKey]) = {
|
||||
Seq[ECPublicKeyBytes]) = {
|
||||
val key1 = ECPrivateKeyUtil.fromWIFToPrivateKey(
|
||||
"cVLwRLTvz3BxDAWkvS3yzT9pUcTCup7kQnfT2smRjvmmm1wAP6QT")
|
||||
val key2 = ECPrivateKeyUtil.fromWIFToPrivateKey(
|
||||
|
@ -146,7 +146,7 @@ trait TransactionTestUtil {
|
|||
(signedMultiSignatureTx,
|
||||
0,
|
||||
multiSignatureScriptPubKey,
|
||||
Seq(key1.publicKey, key2.publicKey, key3.publicKey))
|
||||
Seq(key1.publicKeyBytes, key2.publicKeyBytes, key3.publicKeyBytes))
|
||||
}
|
||||
|
||||
/** Returns a p2sh transaction with its corresponding crediting output */
|
||||
|
|
|
@ -214,7 +214,8 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
|||
} yield {
|
||||
assert(signed != psbt)
|
||||
assert(
|
||||
signed.inputMaps.head.partialSignatures.exists(_.pubKey == walletKey))
|
||||
signed.inputMaps.head.partialSignatures
|
||||
.exists(_.pubKey.toPublicKey == walletKey))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,7 +237,8 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
|||
} yield {
|
||||
assert(signed != psbt)
|
||||
assert(
|
||||
signed.inputMaps.head.partialSignatures.exists(_.pubKey == walletKey))
|
||||
signed.inputMaps.head.partialSignatures
|
||||
.exists(_.pubKey.toPublicKey == walletKey))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,7 +260,8 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
|||
} yield {
|
||||
assert(signed != psbt)
|
||||
assert(
|
||||
signed.inputMaps.head.partialSignatures.exists(_.pubKey == walletKey))
|
||||
signed.inputMaps.head.partialSignatures
|
||||
.exists(_.pubKey.toPublicKey == walletKey))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,7 +284,8 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
|||
} yield {
|
||||
assert(signed != psbt)
|
||||
assert(
|
||||
signed.inputMaps.head.partialSignatures.exists(_.pubKey == walletKey))
|
||||
signed.inputMaps.head.partialSignatures
|
||||
.exists(_.pubKey.toPublicKey == walletKey))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -822,7 +822,10 @@ abstract class Wallet
|
|||
keyPaths.foldLeft(withData) { (accum, hdPath) =>
|
||||
val sign = keyManager.toSign(hdPath)
|
||||
// Only sign if that key doesn't have a signature yet
|
||||
if (!input.partialSignatures.exists(_.pubKey == sign.publicKey)) {
|
||||
if (
|
||||
!input.partialSignatures.exists(
|
||||
_.pubKey.toPublicKey == sign.publicKey)
|
||||
) {
|
||||
logger.debug(
|
||||
s"Signing input $index with key ${sign.publicKey.hex}")
|
||||
accum.sign(index, sign)
|
||||
|
|
Loading…
Add table
Reference in a new issue