Get all of Core working with JS (#2826)

* core-test-js-json

* Add SigPointComputer cross compile
Co-authored-by: Nadav Kohen <nadavk25@gmail.com>

* Fix java.time for scalajs

* InetAddress for scalajs

* Fix linking errors in coreTestJS

* Easy fixes

* InetAddress Tests

* Update micropickle, fix exact functions

* Get BigSizeUIntTest working with upickle

* Fix exact funcs

* Update Base58Test with upickle

* Update ScriptSignatureTest with upickle

* Update TransactionTest with upickle

* Update BlockFilterTest with upickle

* Update ScriptInterpreterTest with upickle

* Update MnemonicCodeTest with upickle

* Add InetAddress unit tests

* Fix compile errors

* Get core tests running, not passing

* Sign with js

* Fix PSBTUnitTest

* Fix PBKDF2 for JS

* Use bcrypto's secp256k1 instead of ecdsa

* Fix 2.12.12 compile

* Core tests passing :)

* Test corejs
This commit is contained in:
benthecarman 2021-03-31 15:04:30 -05:00 committed by GitHub
parent 6e574931c6
commit fa80f36d2f
66 changed files with 889 additions and 446 deletions

View File

@ -27,4 +27,4 @@ jobs:
~/.bitcoin-s/binaries
key: ${{ runner.os }}-cache
- name: run tests
run: sbt ++2.13.5 cryptoTestJS/test 'set scalaJSStage in Global := FullOptStage' cryptoTestJS/test
run: sbt ++2.13.5 cryptoTestJS/test coreJS/test 'set scalaJSStage in Global := FullOptStage' cryptoTestJS/test coreJS/test

View File

@ -1,3 +1,3 @@
name := "bitcoin-s-app-commons"
libraryDependencies ++= Deps.appCommons(scalaVersion.value)
libraryDependencies ++= Deps.appCommons.value

View File

@ -1,6 +1,6 @@
name := "bitcoin-s-cli"
libraryDependencies ++= Deps.cli(scalaVersion.value)
libraryDependencies ++= Deps.cli.value
nativeImageOptions ++= Seq(
"-H:+ReportExceptionStackTraces",

View File

@ -59,7 +59,7 @@ class DLCPaneModel(resultArea: TextArea, oracleInfoArea: TextArea)
ConsoleCli.exec(GetDLCs, Config.empty) match {
case Failure(exception) => throw exception
case Success(dlcsStr) =>
ujson.read(dlcsStr).arr.map(read[DLCStatus]).toVector
ujson.read(dlcsStr).arr.map(read[DLCStatus](_)).toVector
}
}

View File

@ -4,7 +4,7 @@ name := "bitcoin-s-oracle-server"
// when server is quit
Compile / fork := true
libraryDependencies ++= Deps.oracleServer
libraryDependencies ++= Deps.oracleServer.value
mainClass := Some("org.bitcoins.oracle.server.OracleServerMain")

View File

@ -4,7 +4,7 @@ name := "bitcoin-s-server"
// when server is quit
Compile / fork := true
libraryDependencies ++= Deps.server(scalaVersion.value)
libraryDependencies ++= Deps.server.value
mainClass := Some("org.bitcoins.server.BitcoinSServerMain")

View File

@ -89,6 +89,7 @@ lazy val core = crossProject(JVMPlatform, JSPlatform)
lazy val coreJVM = core.jvm
lazy val coreJS = core.js
.settings(libraryDependencies ++= Deps.coreJs.value)
lazy val asyncUtils = crossProject(JVMPlatform, JSPlatform)
.crossType(CrossType.Pure)
@ -405,8 +406,10 @@ lazy val coreTest = crossProject(JVMPlatform, JSPlatform)
)
lazy val coreTestJVM = coreTest.jvm
.settings(libraryDependencies ++= Deps.coreTestJVM.value)
lazy val coreTestJS = coreTest.js
.enablePlugins(ScalaJSBundlerPlugin)
lazy val appCommons = project
.in(file("app-commons"))

View File

@ -0,0 +1,31 @@
package org.bitcoins.core.p2p
import org.bitcoins.testkitcore.gen.p2p.P2PGenerator
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
import java.net.{InetAddress => JvmAddress}
class InetAddressJVMTest extends BitcoinSUnitTest {
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
generatorDrivenConfigNewCode
behavior of "InetAddress"
it must "have serialization symmetry with java's InetAddress" in {
forAll(P2PGenerator.inetAddress) { inet =>
assert(
NetworkIpAddress.writeAddress(
JvmAddress.getByAddress(inet.getAddress).getAddress) == inet.bytes)
}
}
it must "have serialization symmetry with java's InetAddress with IPv4" in {
forAll(P2PGenerator.inetAddress) { inet =>
assert(
JvmAddress
.getByAddress(inet.ipv4Bytes.toArray)
.getAddress sameElements inet.ipv4Bytes.toArray)
}
}
}

View File

@ -3,8 +3,9 @@ package org.bitcoins.core.crypto
import org.bitcoins.testkitcore.gen.CryptoGenerators
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
import org.scalatest.Assertion
import play.api.libs.json._
import scodec.bits.{BinStringSyntax, BitVector, ByteVector}
import ujson._
import upickle.default._
import scala.util.{Failure, Try}
@ -221,28 +222,29 @@ class MnemonicCodeTest extends BitcoinSUnitTest {
)
}
implicit object TrezorReads extends Reads[RawTrezorTestVector] {
def reads(json: JsValue): JsResult[RawTrezorTestVector] = {
for {
arr <- json.validate[JsArray]
entropy <- arr(0).validate[String]
words <- arr(1).validate[String]
seed <- arr(2).validate[String]
xpriv <- arr(3).validate[String]
implicit val trezorReads: Reader[RawTrezorTestVector] = reader[Value].map {
value =>
val resOpt = for {
arr <- value.arrOpt
entropy <- arr(0).strOpt
words <- arr(1).strOpt
seed <- arr(2).strOpt
xpriv <- arr(3).strOpt
} yield RawTrezorTestVector(entropy, words, seed, xpriv)
}
resOpt.get
}
val rawJson = TrezorBIP39Vectors.str
val json = Json.parse(rawJson)
val testVectors =
(json \ "english")
.validate[Vector[RawTrezorTestVector]]
.map(_.map(trezorFromRaw))
.get
val english = ujson.read(rawJson).obj("english").arr
testVectors.map(testTrezorVector(_))
val testVectors =
upickle.default
.read[Vector[RawTrezorTestVector]](english)
.map(trezorFromRaw)
testVectors.map(testTrezorVector)
}
it must "not serialize a MnemonicCode toString" in {

View File

@ -4,7 +4,7 @@ import org.bitcoins.core.protocol.blockchain.Block
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.crypto.DoubleSha256DigestBE
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
import play.api.libs.json.{JsArray, Json}
import ujson._
class BlockFilterTest extends BitcoinSUnitTest {
behavior of "BlockFilter"
@ -43,29 +43,27 @@ class BlockFilterTest extends BitcoinSUnitTest {
object Bip158TestCase {
//["Block Height,Block Hash,Block,[Prev Output Scripts for Block],Previous Basic Header,Basic Filter,Basic Header,Notes"]
def fromJsArray(array: JsArray): Bip158TestCase = {
def fromArr(array: Arr): Bip158TestCase = {
val parseResult = for {
height <- array(0).validate[Int]
blockHash <- array(1).validate[String].map(DoubleSha256DigestBE.fromHex)
height <- array(0).numOpt.map(_.toInt)
blockHash <- array(1).strOpt.map(DoubleSha256DigestBE.fromHex)
block <- array(2).validate[String].map(Block.fromHex)
block <- array(2).strOpt.map(Block.fromHex)
scriptArray <- array(3).validate[JsArray]
scriptArray <- array(3).arrOpt
scripts = parseScripts(scriptArray)
prevHeader <-
array(4)
.validate[String]
array(4).strOpt
.map(DoubleSha256DigestBE.fromHex)
filter <-
array(5)
.validate[String]
array(5).strOpt
.map(BlockFilter.fromHex(_, blockHash.flip))
header <- array(6).validate[String].map(DoubleSha256DigestBE.fromHex)
header <- array(6).strOpt.map(DoubleSha256DigestBE.fromHex)
notes <- array(7).validate[String]
notes <- array(7).strOpt
} yield Bip158TestCase(height,
blockHash,
block,
@ -78,17 +76,18 @@ class BlockFilterTest extends BitcoinSUnitTest {
parseResult.get
}
private def parseScripts(array: JsArray): Vector[ScriptPubKey] = {
val hexScripts = array.validate[Vector[String]].get
private def parseScripts(array: Arr): Vector[ScriptPubKey] = {
val hexScripts = array.arr.map(_.str)
hexScripts.map(ScriptPubKey.fromAsmHex)
hexScripts.map(ScriptPubKey.fromAsmHex).toVector
}
}
it must "pass bip 158 test vectors" in {
val vec: Vector[JsArray] =
Json.parse(Testnet19.str.mkString).validate[Vector[JsArray]].get.tail
val testCases = vec.map(Bip158TestCase.fromJsArray)
val vec: Vector[Value] =
ujson.read(Testnet19.str.mkString).arr.toVector.tail
val testCases =
vec.map(value => Bip158TestCase.fromArr(Arr.from(value.arr)))
testCases.foreach(_.runTest())
}

View File

@ -5,15 +5,15 @@ import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.crypto.{DoubleSha256Digest, SipHashKey}
import org.bitcoins.testkitcore.gen.CryptoGenerators._
import org.bitcoins.testkitcore.gen.NumberGenerator
import org.bitcoins.testkitcore.util.BitcoinSJvmTest
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
import org.scalacheck.Gen
import scodec.bits.{ByteVector, _}
import scodec.bits._
class GolombFilterTest extends BitcoinSJvmTest {
class GolombFilterTest extends BitcoinSUnitTest {
behavior of "GolombFilter"
it must "match encoded data for arbitrary GCS parameters" in {
forAllParallel(genKey, genPMRand) { case (k, (p, m, rand)) =>
forAll(genKey, genPMRand) { case (k, (p, m, rand)) =>
val data1 = rand + UInt64.one
val data2 = data1 + UInt64.one
val data = Vector(data1, data2)
@ -55,22 +55,21 @@ class GolombFilterTest extends BitcoinSJvmTest {
val genRandHashes: Gen[Vector[UInt64]] =
Gen.listOfN(100, NumberGenerator.uInt64).map(_.toVector)
forAllParallel(genKey, genData, genRandHashes) {
case (k, data, randHashes) =>
val filter = GCS.buildBasicBlockFilter(data, k)
val binarySearchMatcher = BinarySearchFilterMatcher(filter)
val simpleMatcher = SimpleFilterMatcher(filter)
val hashes = binarySearchMatcher.decodedHashes
forAll(genKey, genData, genRandHashes) { case (k, data, randHashes) =>
val filter = GCS.buildBasicBlockFilter(data, k)
val binarySearchMatcher = BinarySearchFilterMatcher(filter)
val simpleMatcher = SimpleFilterMatcher(filter)
val hashes = binarySearchMatcher.decodedHashes
data.foreach(element => assert(binarySearchMatcher.matches(element)))
assert(binarySearchMatcher.matchesAny(data))
assert(simpleMatcher.matchesAny(data))
data.foreach(element => assert(binarySearchMatcher.matches(element)))
assert(binarySearchMatcher.matchesAny(data))
assert(simpleMatcher.matchesAny(data))
val hashesNotInData: Vector[UInt64] =
randHashes.filterNot(hashes.contains)
val hashesNotInData: Vector[UInt64] =
randHashes.filterNot(hashes.contains)
assert(hashesNotInData.forall(hash =>
!binarySearchMatcher.matchesHash(hash)))
assert(
hashesNotInData.forall(hash => !binarySearchMatcher.matchesHash(hash)))
}
}

View File

@ -1,11 +1,11 @@
package org.bitcoins.core.p2p
import org.bitcoins.testkitcore.gen.p2p.DataMessageGenerator
import org.bitcoins.testkitcore.util.BitcoinSJvmTest
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
class BlockMessageTest extends BitcoinSJvmTest {
class BlockMessageTest extends BitcoinSUnitTest {
it must "have serialization symmetry" in {
forAllParallel(DataMessageGenerator.blockMessage) { block =>
forAll(DataMessageGenerator.blockMessage) { block =>
assert(block == BlockMessage.fromBytes(block.bytes))
}
}

View File

@ -1,12 +1,12 @@
package org.bitcoins.core.p2p
import org.bitcoins.testkitcore.gen.p2p.DataMessageGenerator
import org.bitcoins.testkitcore.util.BitcoinSJvmTest
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
class HeadersMessageTest extends BitcoinSJvmTest {
class HeadersMessageTest extends BitcoinSUnitTest {
it must "have serialization symmetry" in {
forAllParallel(DataMessageGenerator.headersMessage) { headersMsg =>
forAll(DataMessageGenerator.headersMessage) { headersMsg =>
assert(HeadersMessage(headersMsg.hex) == headersMsg)
}
}

View File

@ -0,0 +1,28 @@
package org.bitcoins.core.p2p
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
import scodec.bits._
class InetAddressTest extends BitcoinSUnitTest {
behavior of "InetAddress"
it must "correctly read an IPv4 address" in {
val ipv6Bytes = hex"00000000000000000000ffff48c4a809"
val ipv4Bytes = hex"48c4a809"
val inetA = InetAddress(ipv6Bytes)
assert(inetA.ipv4Bytes == ipv4Bytes)
val inetB = InetAddress(ipv4Bytes)
assert(inetB.bytes == ipv6Bytes)
assert(inetA.getAddress sameElements ipv6Bytes.toArray)
}
it must "correctly read an IPv6 address" in {
val bytes = hex"20010db800002f3b02aa00fffe289c5a"
val inet = InetAddress(bytes)
assert(inet.bytes == bytes)
assert(inet.getAddress sameElements bytes.toArray)
assertThrows[IllegalArgumentException](inet.ipv4Bytes)
}
}

View File

@ -3,9 +3,9 @@ package org.bitcoins.core.p2p
import org.bitcoins.core.config.TestNet3
import org.bitcoins.testkitcore.gen.p2p.P2PGenerator
import org.bitcoins.testkitcore.node.P2PMessageTestUtil
import org.bitcoins.testkitcore.util.BitcoinSJvmTest
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
class NetworkPayloadTest extends BitcoinSJvmTest {
class NetworkPayloadTest extends BitcoinSUnitTest {
"NetworkMessage" must "create a payload object from it's network header and the payload bytes" in {
val rawNetworkMessage = P2PMessageTestUtil.rawNetworkMessage
@ -14,7 +14,9 @@ class NetworkPayloadTest extends BitcoinSJvmTest {
val payload = NetworkPayload(header, payloadHex)
payload.isInstanceOf[VersionMessage] must be(true)
payload.commandName must be(NetworkPayload.versionCommandName)
val testVersionMessage = VersionMessage("173.31.39.168", TestNet3)
val ipArr = Array(173.toByte, 31.toByte, 39.toByte, 168.toByte)
val inet = InetAddress(ipArr)
val testVersionMessage = VersionMessage(TestNet3, inet, inet)
payload.asInstanceOf[VersionMessage].addressReceiveIpAddress must be(
testVersionMessage.addressReceiveIpAddress)
payload.asInstanceOf[VersionMessage].addressReceivePort must be(
@ -24,7 +26,7 @@ class NetworkPayloadTest extends BitcoinSJvmTest {
// this tests has a bunch of messages to choose between, so we set a high config value
implicit override val generatorDrivenConfig = customGenDrivenConfig(100)
it must "parse messages based on its command name" in {
forAllParallel(P2PGenerator.message) { p2p =>
forAll(P2PGenerator.message) { p2p =>
val bytes = p2p.bytes
val parser = NetworkPayload.readers(p2p.commandName)
assert(parser(bytes) == p2p)

View File

@ -1,13 +1,13 @@
package org.bitcoins.core.p2p
import java.net.InetAddress
import java.time.Instant
import org.bitcoins.core.config.MainNet
import org.bitcoins.core.number.{Int32, UInt64}
import org.bitcoins.testkitcore.gen.p2p.ControlMessageGenerator
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
import scodec.bits._
import java.time.Instant
class VersionMessageTest extends BitcoinSUnitTest {
it must "have serialization symmetry" in {
@ -18,18 +18,21 @@ class VersionMessageTest extends BitcoinSUnitTest {
it must "have a meaningful toString message" in {
forAll(ControlMessageGenerator.versionMessage) { version =>
assert(version.toString.length < 350 + version.userAgent.length())
assert(version.toString().length < 400 + version.userAgent.length())
}
}
"VersionMessage" must "create a new version message to be sent to another node on the network" in {
val versionMessage = VersionMessage(MainNet, InetAddress.getLocalHost)
val ipArr = Array(173.toByte, 31.toByte, 39.toByte, 168.toByte)
val inet = InetAddress(ipArr)
val versionMessage = VersionMessage(MainNet, inet, inet)
assert(versionMessage.addressReceiveServices.nodeNone)
versionMessage.addressReceiveIpAddress must be(InetAddress.getLocalHost)
versionMessage.addressReceiveIpAddress must be(inet)
versionMessage.addressReceivePort must be(MainNet.port)
assert(versionMessage.addressTransServices.nodeNetwork)
versionMessage.addressTransIpAddress must be(InetAddress.getLocalHost)
versionMessage.addressTransIpAddress must be(inet)
versionMessage.addressTransPort must be(MainNet.port)
versionMessage.nonce must be(UInt64.zero)

View File

@ -1,9 +1,12 @@
package org.bitcoins.core.protocol
import org.bitcoins.core.number.UInt64
import org.bitcoins.core.protocol.BigSizeJsonTestVectors._
import org.bitcoins.testkitcore.gen.NumberGenerator
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
import scodec.bits.ByteVector
import ujson.Value
import upickle.default._
import scala.util.{Failure, Success, Try}
@ -27,32 +30,25 @@ class BigSizeUIntTest extends BitcoinSUnitTest {
}
it must "pass encoding tests" in {
val tests = ujson.read(BigSizeJsonTestVectors.encode).arr.toVector
val tests = read[Vector[EncodeTestVector]](BigSizeJsonTestVectors.encode)
tests.foreach { test =>
val obj = test.obj
val name = obj("name").str
val num = BigInt(obj("value").str)
val bytes = ByteVector.fromValidHex(obj("bytes").str)
assert(BigSizeUInt(num).bytes == bytes, name)
assert(BigSizeUInt(test.value).bytes == test.bytes, test.name)
}
}
it must "pass decoding tests" in {
val tests = ujson.read(BigSizeJsonTestVectors.decode).arr.toVector
val tests = read[Vector[DecodeTestVector]](BigSizeJsonTestVectors.decode)
tests.foreach { test =>
val obj = test.obj
val name = obj("name").str
val numStr = obj("value").str
val bytes = ByteVector.fromValidHex(obj("bytes").str)
if (numStr.nonEmpty) {
assert(BigSizeUInt(bytes).num == UInt64(BigInt(numStr)), name)
if (test.value.nonEmpty) {
assert(BigSizeUInt(test.bytes).num == UInt64(BigInt(test.value)),
test.name)
} else {
Try {
assertThrows[IllegalArgumentException] {
BigSizeUInt(bytes)
BigSizeUInt(test.bytes)
}
} match {
case Failure(err) => fail(obj("exp_error").str, err)
case Failure(err) => fail(test.expectedErrorOpt.get, err)
case Success(success) => success
}
}
@ -61,6 +57,19 @@ class BigSizeUIntTest extends BitcoinSUnitTest {
}
object BigSizeJsonTestVectors {
case class EncodeTestVector(name: String, value: BigInt, bytes: ByteVector)
implicit
val encodeTestVectorR: Reader[EncodeTestVector] = reader[Value].map { value =>
val obj = value.obj
val name = obj("name").str
val num = BigInt(obj("value").str)
val bytes = ByteVector.fromValidHex(obj("bytes").str)
EncodeTestVector(name, num, bytes)
}
val encode: String = """[
| {
| "name": "zero",
@ -104,6 +113,23 @@ object BigSizeJsonTestVectors {
| }
|]""".stripMargin
case class DecodeTestVector(
name: String,
value: String,
bytes: ByteVector,
expectedErrorOpt: Option[String])
implicit
val decodeTestVectorR: Reader[DecodeTestVector] = reader[Value].map { value =>
val obj = value.obj
val name = obj("name").str
val num = obj("value").str
val bytes = ByteVector.fromValidHex(obj("bytes").str)
val expectedErrorOpt = Try(obj("exp_error").str).toOption
DecodeTestVector(name, num, bytes, expectedErrorOpt)
}
val decode: String = """[
| {
| "name": "zero",

View File

@ -17,7 +17,7 @@ import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.{DoubleSha256Digest, ECDigitalSignature}
import org.bitcoins.testkitcore.util.{BitcoinSJvmTest, TestUtil}
import scodec.bits.ByteVector
import spray.json._
import upickle.default._
/** Created by chris on 2/17/16.
*/
@ -120,11 +120,11 @@ class ScriptSignatureTest extends BitcoinSJvmTest {
}
it must "read sighash.json and return result" in {
import org.bitcoins.core.protocol.script.testprotocol.SignatureHashTestCaseProtocol._
import org.bitcoins.core.protocol.script.testprotocol.SignatureHashTestCase._
val lines = SigHashJson.str
val testCases: Seq[SignatureHashTestCase] =
lines.parseJson.convertTo[Seq[SignatureHashTestCase]]
val testCases: Vector[SignatureHashTestCase] =
read[Vector[SignatureHashTestCase]](lines)
val allTests = for {
testCase <- testCases

View File

@ -4,24 +4,44 @@ import org.bitcoins.core.number.{Int32, UInt32}
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.core.serializers.script.ScriptParser
import org.bitcoins.crypto.DoubleSha256Digest
import ujson._
import upickle.default._
/** Created by tom on 7/21/16.
*/
trait SignatureHashTestCase {
def transaction: Transaction
def script: ScriptPubKey
def inputIndex: UInt32
def hashTypeNum: Int32
def hashType: HashType
def hash: DoubleSha256Digest
}
case class SignatureHashTestCaseImpl(
case class SignatureHashTestCase(
transaction: Transaction,
script: ScriptPubKey,
inputIndex: UInt32,
hashTypeNum: Int32,
hashType: HashType,
hash: DoubleSha256Digest)
extends SignatureHashTestCase
object SignatureHashTestCase {
implicit val signatureHashTestCaseR: Reader[SignatureHashTestCase] =
reader[Value].map { value =>
val Arr: Arr = value match {
case array: Arr => array
case _: Value =>
throw new RuntimeException(
"Script signature hash test case must be in Arr format")
}
val elements: Vector[Value] = Arr.value.toVector
val transaction: Transaction =
Transaction(elements.head.str)
val asm = ScriptParser.fromHex(elements.apply(1).str)
val script: ScriptPubKey = ScriptPubKey(asm)
val inputIndex: UInt32 = UInt32(elements(2).num.toInt)
val hashTypeNum: Int32 = Int32(elements(3).num.toInt)
val hashType: HashType = HashType(hashTypeNum)
val hash: DoubleSha256Digest =
DoubleSha256Digest(elements.last.str)
SignatureHashTestCase(transaction,
script,
inputIndex,
hashTypeNum,
hashType,
hash)
}
}

View File

@ -9,7 +9,6 @@ import org.bitcoins.core.currency.CurrencyUnits
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.testprotocol.CoreTransactionTestCase
import org.bitcoins.core.protocol.transaction.testprotocol.CoreTransactionTestCaseProtocol._
import org.bitcoins.core.script.PreExecutionScriptProgram
import org.bitcoins.core.script.interpreter.ScriptInterpreter
import org.bitcoins.core.script.result.ScriptOk
@ -17,7 +16,6 @@ import org.bitcoins.crypto.CryptoUtil
import org.bitcoins.testkitcore.gen.TransactionGenerators
import org.bitcoins.testkitcore.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits._
import spray.json._
class TransactionTest extends BitcoinSUnitTest {
behavior of "Transaction"
@ -151,9 +149,8 @@ class TransactionTest extends BitcoinSUnitTest {
}
it must "read all of the tx_valid.json's contents and return ScriptOk" in {
val lines = JsonTestVectors.valid
val json = lines.parseJson
val testCasesOpt: Seq[Option[CoreTransactionTestCase]] =
json.convertTo[Seq[Option[CoreTransactionTestCase]]]
val testCasesOpt =
upickle.default.read[Seq[Option[CoreTransactionTestCase]]](lines)
val testCases: Seq[CoreTransactionTestCase] = testCasesOpt.flatten
for {
testCase <- testCases
@ -226,9 +223,8 @@ class TransactionTest extends BitcoinSUnitTest {
it must "read all of the tx_invalid.json's contents and return a ScriptError" in {
val lines = JsonTestVectors.invalid
val json = lines.parseJson
val testCasesOpt: Seq[Option[CoreTransactionTestCase]] =
json.convertTo[Seq[Option[CoreTransactionTestCase]]]
val testCasesOpt =
upickle.default.read[Seq[Option[CoreTransactionTestCase]]](lines)
val testCases: Seq[CoreTransactionTestCase] = testCasesOpt.flatten
for {
testCase <- testCases

View File

@ -1,35 +1,93 @@
package org.bitcoins.core.protocol.transaction.testprotocol
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutPoint}
import org.bitcoins.core.script.flag.ScriptFlag
import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.script.flag.{ScriptFlag, ScriptFlagFactory}
import org.bitcoins.core.serializers.script.ScriptParser
import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.DoubleSha256Digest
import ujson._
import upickle.default._
/** Created by chris on 5/4/16.
* Used to represent the test cases found inside of tx_valid.json & tx_invalid.json
* from bitcoin core
*/
trait CoreTransactionTestCase {
def outPoints: Seq[TransactionOutPoint] = creditingTxsInfo.map(_._1)
def scriptPubKeys: Seq[ScriptPubKey] = creditingTxsInfo.map(_._2)
def creditingTxsInfo: Seq[
(TransactionOutPoint, ScriptPubKey, Option[CurrencyUnit])]
def spendingTx: Transaction
def flags: Seq[ScriptFlag]
def raw: String
}
case class CoreTransactionTestCaseImpl(
case class CoreTransactionTestCase(
creditingTxsInfo: Seq[
(TransactionOutPoint, ScriptPubKey, Option[CurrencyUnit])],
spendingTx: Transaction,
flags: Seq[ScriptFlag],
raw: String)
extends CoreTransactionTestCase
object CoreTransactionTestCase {
implicit val coreTransactionTestCaseR: Reader[
Option[CoreTransactionTestCase]] =
reader[Value].map { value =>
val arr: Arr = value match {
case array: Arr => array
case _: Value =>
throw new RuntimeException(
"Core test case must be in the format of js array")
}
val elements: Vector[Value] = arr.value.toVector
if (elements.size < 3) None
else {
val creditingTxsInfo: Seq[(
TransactionOutPoint,
ScriptPubKey,
Option[CurrencyUnit])] =
elements.head match {
case array: Arr => parseOutPointsScriptPubKeysAmount(array)
case _: Value =>
throw new RuntimeException("Needs to be a js array")
}
val spendingTx: Transaction = Transaction(elements(1).str)
val flags: Seq[ScriptFlag] =
ScriptFlagFactory.fromList(elements(2).str)
Some(
CoreTransactionTestCase(creditingTxsInfo.reverse,
spendingTx,
flags,
elements.toString))
}
}
private def parseOutPointsScriptPubKeysAmount(array: Arr): Seq[
(TransactionOutPoint, ScriptPubKey, Option[CurrencyUnit])] = {
val result = array.value.map {
case array: Arr =>
val prevoutHashHex =
BytesUtil.flipEndianness(array.value.head.str)
val prevoutHash = DoubleSha256Digest(prevoutHashHex)
val prevoutIndex = array.value(1).num.toLong match {
case -1 => UInt32("ffffffff")
case index
if index >= UInt32.min.toLong && index <= UInt32.max.toLong =>
UInt32(index)
}
val amount =
if (array.arr.size == 4)
Some(Satoshis(array.arr(3).num.toLong))
else None
val outPoint = TransactionOutPoint(prevoutHash, prevoutIndex)
val scriptTokens: Seq[ScriptToken] =
ScriptParser.fromString(array.arr(2).str)
val scriptPubKey = ScriptPubKey.fromAsm(scriptTokens)
(outPoint, scriptPubKey, amount)
case _: Value =>
throw new RuntimeException(
"All tx outpoint/scriptpubkey info must be array elements")
}
result.toVector
}
}

View File

@ -344,12 +344,23 @@ class PSBTUnitTest extends BitcoinSUnitTest {
val privKey1 = ECPrivateKeyUtil.fromWIFToPrivateKey(
"cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d")
val expectedPsbt0 = PSBT(
hex"70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000002202029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f000000800000008001000080000100f80200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000220203089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000")
// BCrypto does not use low r signing
val (expectedPsbt0, expectedPsbt1) = CryptoUtil.cryptoContext match {
case CryptoContext.LibSecp256k1 | CryptoContext.BouncyCastle =>
val psbt0 = PSBT(
hex"70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000002202029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f000000800000008001000080000100f80200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000220203089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000")
val psbt1 = PSBT(
hex"70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d74730440220631a989fe738a92ad01986023312c19214fe2802b39e5cbc1ac3678806c692c3022039db6c387bd267716dfdb3d4d8da50b8e85d213326ba7c7daaa4c0ce41eb922301010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f000000800000008001000080000100f80200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f79650000002202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000")
val expectedPsbt1 = PSBT(
hex"70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d74730440220631a989fe738a92ad01986023312c19214fe2802b39e5cbc1ac3678806c692c3022039db6c387bd267716dfdb3d4d8da50b8e85d213326ba7c7daaa4c0ce41eb922301010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f000000800000008001000080000100f80200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f79650000002202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000")
(psbt0, psbt1)
case CryptoContext.BCrypto =>
val psbt0 = PSBT(
hex"70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000002202029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f000000800000008001000080000100f80200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000220203089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000")
val psbt1 = PSBT(
hex"70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f000000800000008001000080000100f80200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f79650000002202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000")
(psbt0, psbt1)
}
val privKey2 = ECPrivateKeyUtil.fromWIFToPrivateKey(
"cT7J9YpCwY3AVRFSjN6ukeEeWY6mhpbJPxRaDaP5QTdygQRxP9Au")

View File

@ -15,9 +15,8 @@ import org.bitcoins.core.protocol.transaction.{
import org.bitcoins.core.script.PreExecutionScriptProgram
import org.bitcoins.core.script.flag.ScriptFlagFactory
import org.bitcoins.core.script.interpreter.testprotocol.CoreTestCase
import org.bitcoins.core.script.interpreter.testprotocol.CoreTestCaseProtocol._
import org.bitcoins.testkitcore.util.{BitcoinSUnitTest, TransactionTestUtil}
import spray.json._
import upickle.default._
import scala.util.Try
@ -26,9 +25,11 @@ import scala.util.Try
class ScriptInterpreterTest extends BitcoinSUnitTest {
"ScriptInterpreter" must "evaluate all the scripts from the bitcoin core script_tests.json" in {
val json = ScriptTestsJson.str.parseJson
import org.bitcoins.core.script.interpreter.testprotocol.CoreTestCase._
val json = ScriptTestsJson.str
val testCasesOpt: Seq[Option[CoreTestCase]] =
json.convertTo[Seq[Option[CoreTestCase]]]
read[Seq[Option[CoreTestCase]]](json)
val testCases: Seq[CoreTestCase] = testCasesOpt.flatten
for {
testCase <- testCases

View File

@ -1,29 +1,21 @@
package org.bitcoins.core.script.interpreter.testprotocol
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.protocol.script.{
ScriptPubKey,
ScriptSignature,
ScriptWitness
}
import org.bitcoins.core.currency._
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.script.result.ScriptResult
import org.bitcoins.core.serializers.script.ScriptParser
import org.bitcoins.core.util.{BitcoinScriptUtil, BytesUtil}
import scodec.bits._
import ujson._
import upickle.default._
/** Created by chris on 1/18/16.
* This represents a core test case for valid and invalid scripts
* the scripts can be seen in the script_tests.json file
* files.
*/
trait CoreTestCase {
def scriptSig: ScriptSignature
def scriptPubKey: ScriptPubKey
def flags: String
def expectedResult: ScriptResult
def comments: String
def raw: String
def witness: Option[(ScriptWitness, CurrencyUnit)]
}
case class CoreTestCaseImpl(
case class CoreTestCase(
scriptSig: ScriptSignature,
scriptPubKey: ScriptPubKey,
flags: String,
@ -31,4 +23,129 @@ case class CoreTestCaseImpl(
comments: String,
raw: String,
witness: Option[(ScriptWitness, CurrencyUnit)])
extends CoreTestCase
object CoreTestCase {
implicit val coreTestCaseR: Reader[Option[CoreTestCase]] = reader[Value].map {
value =>
val arr: Arr = value match {
case array: Arr => array
case _ =>
throw new RuntimeException(
"Core test case must be in the format of js array")
}
val elements: Vector[Value] = arr.value.toVector
if (elements.size < 3) {
//means that the line is probably a separator between different types of test cases i.e.
//["Equivalency of different numeric encodings"]
None
} else if (elements.size == 4) {
//means we are missing a comment
val scriptPubKeyBytes: ByteVector = parseScriptPubKey(elements(1))
val scriptPubKey = ScriptPubKey(scriptPubKeyBytes)
val scriptSignatureBytes: ByteVector =
parseScriptSignature(elements.head)
val scriptSignature: ScriptSignature =
ScriptSignature(scriptSignatureBytes)
val flags = elements(2).str
val expectedResult = ScriptResult(elements(3).str)
Some(
CoreTestCase(scriptSignature,
scriptPubKey,
flags,
expectedResult,
"",
elements.toString,
None))
} else if (elements.size == 5 && elements.head.isInstanceOf[Arr]) {
//means we have a witness as the first item in our array
val witnessArray = elements.head.asInstanceOf[Arr]
val amount = Satoshis((witnessArray.value.last.num * 100000000L).toLong)
val stack = witnessArray.value.toVector
.slice(0, witnessArray.value.size - 1)
.map(c => BytesUtil.decodeHex(c.str))
val witness = ScriptWitness(stack.reverse)
val scriptPubKeyBytes: ByteVector = parseScriptPubKey(elements(2))
val scriptPubKey = ScriptPubKey(scriptPubKeyBytes)
val scriptSignatureBytes: ByteVector = parseScriptSignature(elements(1))
val scriptSignature: ScriptSignature =
ScriptSignature(scriptSignatureBytes)
val flags = elements(3).str
val expectedResult = ScriptResult(elements(4).str)
Some(
CoreTestCase(scriptSignature,
scriptPubKey,
flags,
expectedResult,
"",
elements.toString,
Some((witness, amount))))
} else if (elements.size == 5) {
val scriptPubKeyBytes: ByteVector = parseScriptPubKey(elements(1))
val scriptPubKey = ScriptPubKey(scriptPubKeyBytes)
val scriptSignatureBytes: ByteVector =
parseScriptSignature(elements.head)
val scriptSignature: ScriptSignature =
ScriptSignature(scriptSignatureBytes)
val flags = elements(2).str
val expectedResult = ScriptResult(elements(3).str)
val comments = elements(4).str
Some(
CoreTestCase(scriptSignature,
scriptPubKey,
flags,
expectedResult,
comments,
elements.toString,
None))
} else if (elements.size == 6 && elements.head.arrOpt.isDefined) {
val witnessArray = elements.head.arr
val amount = Satoshis((witnessArray.value.last.num * 100000000L).toLong)
val stack = witnessArray.value.toVector
.slice(0, witnessArray.value.size - 1)
.map(c => BytesUtil.decodeHex(c.str))
val witness = ScriptWitness(stack.reverse)
val scriptPubKeyBytes: ByteVector = parseScriptPubKey(elements(2))
val scriptPubKey = ScriptPubKey(scriptPubKeyBytes)
val scriptSignatureBytes: ByteVector = parseScriptSignature(elements(1))
val scriptSignature: ScriptSignature =
ScriptSignature(scriptSignatureBytes)
val flags = elements(3).str
val expectedResult = ScriptResult(elements(4).str)
val comments = elements(5).str
Some(
CoreTestCase(scriptSignature,
scriptPubKey,
flags,
expectedResult,
comments,
elements.toString,
Some((witness, amount))))
} else None
}
/** Parses the script signature asm, it can come in multiple formats
* such as byte strings i.e. 0x02 0x01 0x00
* and numbers 1 2
* look at scirpt_valid.json file for example formats
*/
private def parseScriptSignature(element: Value): ByteVector = {
val asm = ScriptParser.fromString(element.str)
val bytes = BitcoinScriptUtil.asmToBytes(asm)
val compactSizeUInt = CompactSizeUInt.calculateCompactSizeUInt(bytes)
compactSizeUInt.bytes ++ bytes
}
/** Parses a script pubkey asm from the bitcoin core test cases,
* example formats:
* "2 EQUALVERIFY 1 EQUAL"
* "'Az' EQUAL"
* look at scirpt_valid.json file for more example formats
*/
private def parseScriptPubKey(element: Value): ByteVector = {
val asm = ScriptParser.fromString(element.str)
val bytes = BitcoinScriptUtil.asmToBytes(asm)
val compactSizeUInt = CompactSizeUInt.calculateCompactSizeUInt(bytes)
compactSizeUInt.bytes ++ bytes
}
}

View File

@ -8,8 +8,6 @@ import org.bitcoins.testkitcore.util.BitcoinSUnitTest
import org.scalacheck.Gen
import scodec.bits.{ByteVector, HexStringSyntax}
import java.net.InetAddress
class RawAddrV2MessageSerializerTest extends BitcoinSUnitTest {
def ipv4AddrV2MessageGen: Gen[IPv4AddrV2Message] = {
@ -98,10 +96,11 @@ class RawAddrV2MessageSerializerTest extends BitcoinSUnitTest {
}
it must "parse an IPv4AddrV2Message" in {
val msg = IPv4AddrV2Message(UInt32(4523),
CompactSizeUInt(UInt64(53453453L)),
InetAddress.getByAddress(hex"00000000".toArray),
UInt16(8333))
val msg: IPv4AddrV2Message =
IPv4AddrV2Message(UInt32(4523),
CompactSizeUInt(UInt64(53453453L)),
InetAddress.getByAddress(hex"00000000".toArray),
UInt16(8333))
assert("000011abfe8da22f030100000000208d" == msg.hex)
}

View File

@ -1,6 +1,7 @@
package org.bitcoins.core.serializers.p2p
package org.bitcoins.core.serializers.p2p.messages
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.serializers.p2p.RawNetworkIpAddressSerializer
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
class RawNetworkIpAddressSerializerTest extends BitcoinSUnitTest {
@ -16,7 +17,7 @@ class RawNetworkIpAddressSerializerTest extends BitcoinSUnitTest {
val ipAddress = RawNetworkIpAddressSerializer.read(hex)
ipAddress.time must be(UInt32(1414012889))
assert(ipAddress.services.nodeNetwork)
ipAddress.address.toString must be("/192.0.2.51")
ipAddress.address.bytes.toHex must be(address)
ipAddress.port must be(8333)
}

View File

@ -1,15 +1,15 @@
package org.bitcoins.core.serializers.p2p.messages
import java.net.InetSocketAddress
import org.bitcoins.core.number.{Int32, Int64, UInt64}
import org.bitcoins.core.p2p._
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
import scodec.bits._
class RawVersionMessageSerializerTest extends BitcoinSUnitTest {
//take from the bitcoin developer reference underneath this seciton
//take from the bitcoin developer reference underneath this section
//https://bitcoin.org/en/developer-reference#version
val protocolVersion = "72110100"
@ -100,12 +100,12 @@ class RawVersionMessageSerializerTest extends BitcoinSUnitTest {
assert(versionMessage.services.nodeNetwork)
versionMessage.timestamp must be(Int64(-4420735367386806222L))
versionMessage.addressReceiveIpAddress must be(
new InetSocketAddress(17057).getAddress)
InetAddress(hex"00000000000000000000ffff00000000"))
assert(versionMessage.addressReceiveServices.nodeNone)
versionMessage.addressReceivePort must be(17057)
assert(versionMessage.addressTransServices.nodeNone)
versionMessage.addressTransIpAddress must be(
new InetSocketAddress(41963).getAddress)
InetAddress(hex"00000000000000000000ffff00000000"))
versionMessage.addressTransPort must be(41963)
versionMessage.nonce must be(UInt64(BigInt("9223372036854775809")))
versionMessage.userAgentSize must be(CompactSizeUInt(UInt64(86), 1))

View File

@ -2,7 +2,7 @@ package org.bitcoins.core.util
import org.bitcoins.core.util.testprotocol._
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
import spray.json._
import upickle.default._
/** Created by tom on 5/17/16.
*/
@ -61,10 +61,8 @@ class Base58Test extends BitcoinSUnitTest {
}
it must "read base58_keys_valid.json and validate each case" in {
import org.bitcoins.core.util.testprotocol.Base58ValidTestCaseProtocol._
val json = Base58EncodingTestVectors.valid.parseJson
val testCases: Seq[Base58ValidTestCase] =
json.convertTo[Seq[Base58ValidTestCase]]
val testCases =
read[Seq[Base58ValidTestCase]](Base58EncodingTestVectors.valid)
for {
testCase <- testCases
} yield {
@ -81,15 +79,12 @@ class Base58Test extends BitcoinSUnitTest {
}
it must "read base58_keys_invalid.json and return each as an invalid base58 string" in {
import org.bitcoins.core.util.testprotocol.Base58InvalidTestCase
import org.bitcoins.core.util.testprotocol.Base58InvalidTestCaseProtocol._
val json = Base58EncodingTestVectors.invalid.parseJson
val testCases: Seq[Base58InvalidTestCase] =
json.convertTo[Seq[Base58InvalidTestCase]]
val testCases =
read[Seq[Base58InvalidTestCase]](Base58EncodingTestVectors.invalid)
for {
testCase <- testCases
} yield {
testCase must be(Base58InvalidTestCaseImpl(testCase.base58EncodedString))
testCase must be(Base58InvalidTestCase(testCase.base58EncodedString))
Base58.isValid(testCase.base58EncodedString) must be(false)
}
}

View File

@ -1,11 +1,18 @@
package org.bitcoins.core.util.testprotocol
/** Created by tom on 6/21/16.
*/
import ujson._
import upickle.default._
trait Base58InvalidTestCase {
def base58EncodedString: String
case class Base58InvalidTestCase(base58EncodedString: String)
object Base58InvalidTestCase {
implicit val base58InvalidTestCaseR: Reader[Base58InvalidTestCase] =
reader[Value].map {
case array: Arr =>
val str = array.value.head.str
Base58InvalidTestCase(str)
case error: Value =>
throw new RuntimeException("Expected array. Got: " + error)
}
}
case class Base58InvalidTestCaseImpl(base58EncodedString: String)
extends Base58InvalidTestCase

View File

@ -1,18 +1,51 @@
package org.bitcoins.core.util.testprotocol
import org.bitcoins.core.protocol.Address
import org.bitcoins.core.util.testprotocol.ConfigParams._
import org.bitcoins.crypto.{ECPrivateKey, Sha256Hash160Digest}
import ujson._
import upickle.default._
/** Created by tom on 6/14/16.
*/
trait Base58ValidTestCase {
def addressOrWIFPrivKey: Either[Address, String]
def hashOrPrivKey: Either[Sha256Hash160Digest, ECPrivateKey]
def configParams: ConfigParams
}
case class Base58ValidTestCaseImpl(
case class Base58ValidTestCase(
addressOrWIFPrivKey: Either[Address, String],
hashOrPrivKey: Either[Sha256Hash160Digest, ECPrivateKey],
configParams: ConfigParams)
extends Base58ValidTestCase
object Base58ValidTestCase {
implicit val base58ValidTestCaseR: Reader[Base58ValidTestCase] =
reader[Value].map { value =>
val jsArray: Arr = value match {
case array: Arr => array
case _: Value =>
throw new RuntimeException(
"Core test case must be in the format of js array")
}
val elements: Vector[Value] = jsArray.value.toVector
val configParams: ConfigParams =
upickle.default.read[ConfigParams](elements(2))
def addressOrPrivateKey(
elements: Vector[Value]): Either[Address, String] =
if (configParams.isPrivKey) {
Right(elements(0).str)
} else {
val addr = Address.fromString(elements(0).str)
Left(addr)
}
def isHashOrPrivKey(
elements: Vector[Value]): Either[Sha256Hash160Digest, ECPrivateKey] =
configParams.addrTypeOrIsCompressed match {
case a if a.isLeft =>
Left(Sha256Hash160Digest(elements(1).str))
case b if b.isRight =>
Right(ECPrivateKey(elements(1).str))
case _ =>
sys.error(s"Should be left or right")
}
Base58ValidTestCase(addressOrPrivateKey(elements),
isHashOrPrivKey(elements),
configParams)
}
}

View File

@ -1,44 +1,33 @@
package org.bitcoins.core.util.testprotocol
import spray.json._
import ujson._
import upickle.default._
/** Created by tom on 6/14/16.
*/
trait ConfigParams {
def addrTypeOrIsCompressed: Either[String, Boolean]
def isPrivKey: Boolean
def isTestNet: Boolean
}
case class ConfigParamsImpl(
case class ConfigParams(
addrTypeOrIsCompressed: Either[String, Boolean],
isPrivKey: Boolean,
isTestNet: Boolean)
extends ConfigParams
object ConfigParamsProtocol extends DefaultJsonProtocol {
object ConfigParams {
val addrTypeKey = "addrType"
val isCompressedKey = "isCompressed"
val isPrivKeyKey = "isPrivkey"
val isTestNetKey = "isTestnet"
implicit object ConfigParamsFormatter extends RootJsonFormat[ConfigParams] {
implicit val configParamsR: Reader[ConfigParams] = reader[Value].map {
value =>
val obj = value.obj
val addrTypeOrPrivKey: Either[String, Boolean] =
parseAddrTypeOrPrivKey(obj)
val isPrivKey = obj(isPrivKeyKey).bool
val isTestNet = obj(isTestNetKey).bool
override def read(value: JsValue): ConfigParams = {
val obj = value.asJsObject
val addrTypeOrPrivKey: Either[String, Boolean] = parseAddrTypeOrPrivKey(
obj)
val isPrivKey = obj.fields(isPrivKeyKey).convertTo[Boolean]
val isTestNet = obj.fields(isTestNetKey).convertTo[Boolean]
ConfigParamsImpl(addrTypeOrPrivKey, isPrivKey, isTestNet)
}
ConfigParams(addrTypeOrPrivKey, isPrivKey, isTestNet)
}
override def write(config: ConfigParams): JsValue = ???
def parseAddrTypeOrPrivKey(obj: JsObject): Either[String, Boolean] = {
if (obj.fields.contains("addrType")) {
Left(obj.fields(addrTypeKey).convertTo[String])
} else Right(obj.fields(isCompressedKey).convertTo[Boolean])
}
def parseAddrTypeOrPrivKey(obj: Obj): Either[String, Boolean] = {
if (obj.value.contains("addrType")) {
Left(obj(addrTypeKey).str)
} else Right(obj(isCompressedKey).bool)
}
}

View File

@ -0,0 +1,7 @@
package org.bitcoins.core.protocol.dlc
import org.bitcoins.crypto.ECPublicKey
case class SigPointComputer(private val computeSigPoint: () => ECPublicKey) {
lazy val compute: ECPublicKey = computeSigPoint()
}

View File

@ -0,0 +1,14 @@
package org.bitcoins.core.protocol.dlc
import org.bitcoins.crypto.ECPublicKey
import scala.concurrent._
import scala.concurrent.duration.DurationInt
case class SigPointComputer(private val computeSigPoint: () => ECPublicKey) {
private val sigPointF = Future(computeSigPoint())(ExecutionContext.global)
lazy val compute: ECPublicKey = {
Await.result(sigPointF, 20.seconds)
}
}

View File

@ -1,6 +1,6 @@
package org.bitcoins.core.crypto
import org.bitcoins.crypto.{Factory, MaskedToString, NetworkElement, PBKDF2}
import org.bitcoins.crypto._
import scodec.bits.ByteVector
sealed abstract class BIP39Seed extends NetworkElement with MaskedToString {
@ -48,11 +48,12 @@ object BIP39Seed extends Factory[BIP39Seed] {
val words = mnemonic.mkString(" ")
val encodedBytes = PBKDF2
.withSha512(words, salt, ITERATION_COUNT, DERIVED_KEY_LENGTH)
.getEncoded
val encodedBytes = CryptoUtil.pbkdf2WithSha512(words,
salt,
ITERATION_COUNT,
DERIVED_KEY_LENGTH)
BIP39Seed.fromBytes(ByteVector(encodedBytes))
BIP39Seed.fromBytes(encodedBytes)
}
/** Generates a [[https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32]]

View File

@ -1,7 +1,5 @@
package org.bitcoins.core.crypto
import java.security.SecureRandom
import org.bitcoins.core.crypto.words.EnglishWordsBip39
import org.bitcoins.core.util.SeqWrapper
import org.bitcoins.crypto.{CryptoUtil, MaskedToString}
@ -197,15 +195,12 @@ object MnemonicCode {
require(bits % 8 == 0,
s"Given amount if bits ($bits) must be a multiple of 8!")
val randomGenerator: SecureRandom = new SecureRandom
val byteArray: Array[Byte] = new Array[Byte](bits / 8)
randomGenerator.nextBytes(byteArray)
val bitVector = BitVector(byteArray)
val bytes = CryptoUtil.randomBytes(bits / 8)
val bitVector = BitVector(bytes)
bitVector.ensuring(
bitVector => bits == bitVector.length,
s"Did not generate enough bits of entropy! Exepcted=$bits, actual=${bitVector.length}"
s"Did not generate enough bits of entropy! Expected=$bits, actual=${bitVector.length}"
)
}

View File

@ -1,5 +1,6 @@
package org.bitcoins.core.number
import org.bitcoins.core._
import org.bitcoins.core.util.{BytesUtil, NumberUtil}
import org.bitcoins.crypto.{CryptoBytesUtil, Factory, NetworkElement}
import scodec.bits.{ByteOrdering, ByteVector}
@ -21,8 +22,8 @@ sealed abstract class Number[T <: Number[T]]
/** The underlying scala number used to to hold the number */
protected def underlying: A
def toInt: Int = toBigInt.bigInteger.intValueExact()
def toLong: Long = toBigInt.bigInteger.longValueExact()
def toInt: Int = toBigInt.bigInteger.intExact
def toLong: Long = toBigInt.bigInteger.longExact
def toBigInt: BigInt = underlying
/** This is used to determine the valid amount of bytes in a number

View File

@ -0,0 +1,35 @@
package org.bitcoins.core.p2p
import scodec.bits._
trait InetAddress {
def bytes: ByteVector
def ipv4Bytes: ByteVector = {
require(bytes.take(12) == hex"00000000000000000000ffff",
"Cannot call ipv4Bytes for an IPv6 address")
bytes.drop(12)
}
def getAddress: Array[Byte] = bytes.toArray
}
object InetAddress {
private case class InetAddressImpl(
bytes: ByteVector
) extends InetAddress
def apply(array: Array[Byte]): InetAddress = {
getByAddress(array)
}
def apply(bytes: ByteVector): InetAddress = {
getByAddress(bytes.toArray)
}
def getByAddress(array: Array[Byte]): InetAddress = {
val bytes = NetworkIpAddress.writeAddress(ByteVector(array))
InetAddressImpl(bytes)
}
}

View File

@ -1,7 +1,5 @@
package org.bitcoins.core.p2p
import java.net.{InetAddress, InetSocketAddress}
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.serializers.p2p._
import org.bitcoins.crypto.{Factory, NetworkElement}
@ -49,7 +47,27 @@ object NetworkIpAddress extends Factory[NetworkIpAddress] {
* @see https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
*/
def writeAddress(iNetAddress: InetAddress): ByteVector = {
if (iNetAddress.getAddress.size == 4) {
writeAddress(iNetAddress.getAddress)
}
/** Writes an IP address to the representation that the p2p network requires.
* An IPv6 address is in big endian byte order.
* An IPv4 address has to be mapped to an IPv6 address.
*
* @see https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
*/
def writeAddress(bytes: Array[Byte]): ByteVector = {
writeAddress(ByteVector(bytes))
}
/** Writes an IP address to the representation that the p2p network requires.
* An IPv6 address is in big endian byte order.
* An IPv4 address has to be mapped to an IPv6 address.
*
* @see https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
*/
def writeAddress(bytes: ByteVector): ByteVector = {
if (bytes.size == 4) {
//this means we need to convert the IPv4 address to an IPv6 address
//first we have an 80 bit prefix of zeros
val zeroBytes = ByteVector.fill(10)(0)
@ -57,11 +75,9 @@ object NetworkIpAddress extends Factory[NetworkIpAddress] {
val oneBytes = hex"ffff"
val prefix: ByteVector = zeroBytes ++ oneBytes
val addr = prefix ++ ByteVector(iNetAddress.getAddress)
val addr = prefix ++ bytes
addr
} else {
ByteVector(iNetAddress.getAddress)
}
} else bytes
}
private case class NetworkIpAddressImpl(
@ -81,18 +97,4 @@ object NetworkIpAddress extends Factory[NetworkIpAddress] {
def fromBytes(bytes: ByteVector): NetworkIpAddress =
RawNetworkIpAddressSerializer.read(bytes)
def fromInetSocketAddress(
socket: InetSocketAddress,
services: ServiceIdentifier): NetworkIpAddress = {
//TODO: this might be wrong, read this time documentation above
val timestamp = UInt32(System.currentTimeMillis() / 1000)
NetworkIpAddress(
time = timestamp,
services = services,
address = socket.getAddress,
port = socket.getPort
)
}
}

View File

@ -1,6 +1,5 @@
package org.bitcoins.core.p2p
import java.net.{InetAddress, InetSocketAddress}
import org.bitcoins.core.bloom.{BloomFilter, BloomFlag}
import org.bitcoins.core.config.NetworkParameters
import org.bitcoins.core.gcs.{FilterHeader, FilterType, GolombFilter}
@ -11,12 +10,8 @@ import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.serializers.p2p.messages._
import org.bitcoins.core.util.BytesUtil
import org.bitcoins.core.wallet.fee.{SatoshisPerByte, SatoshisPerKiloByte}
import org.bitcoins.crypto.{
DoubleSha256Digest,
Factory,
HashDigest,
NetworkElement
}
import org.bitcoins.crypto._
import scodec.bits.ByteVector
/** Trait that represents a payload for a message on the Bitcoin p2p network
@ -528,7 +523,7 @@ case class IPv4AddrV2Message(
extends AddrV2Message {
override val networkId: Byte = AddrV2Message.IPV4_NETWORK_BYTE
override val addrBytes: ByteVector = ByteVector(addr.getAddress)
override val addrBytes: ByteVector = addr.ipv4Bytes
require(addrBytes.size == AddrV2Message.IPV4_ADDR_LENGTH,
"Incorrect size of IPv4 message, consider using IPv6AddrV2Message")
@ -1332,7 +1327,7 @@ trait VersionMessage extends ControlPayload {
// TODO addressTransServices, addressTransIpAddress and addressTransPort
// what do these fields mean?
override def toString(): String =
s"VersionMessage($version, $services, epoch=${timestamp.toLong}, receiverServices=$addressReceiveIpAddress, receiverAddress=$addressReceiveIpAddress, receiverPort=$addressReceivePort), userAgent=$userAgent, startHeight=${startHeight.toInt}, relay=$relay)"
s"VersionMessage($version, $services, epoch=${timestamp.toLong}, receiverServices=${addressReceiveIpAddress.bytes.toHex}, receiverAddress=${addressReceiveIpAddress.bytes.toHex}, receiverPort=$addressReceivePort), userAgent=$userAgent, startHeight=${startHeight.toInt}, relay=$relay)"
}
/** @see https://bitcoin.org/en/developer-reference#version
@ -1393,13 +1388,6 @@ object VersionMessage extends Factory[VersionMessage] {
)
}
def apply(
network: NetworkParameters,
receivingIpAddress: InetAddress): VersionMessage = {
val transmittingIpAddress = InetAddress.getLocalHost
VersionMessage(network, receivingIpAddress, transmittingIpAddress)
}
def apply(
network: NetworkParameters,
receivingIpAddress: InetAddress,
@ -1435,18 +1423,6 @@ object VersionMessage extends Factory[VersionMessage] {
relay = relay
)
}
def apply(host: String, network: NetworkParameters): VersionMessage = {
//network.dnsSeeds(0)
val transmittingIpAddress = InetAddress.getByName(host)
VersionMessage(network, transmittingIpAddress)
}
def apply(
socket: InetSocketAddress,
network: NetworkParameters): VersionMessage = {
VersionMessage(network, socket.getAddress)
}
}
object NetworkPayload {

View File

@ -7,6 +7,7 @@ import org.bitcoins.core.protocol.transaction.{
import org.bitcoins.core.wallet.fee.SatoshisPerKiloByte
import scodec.bits._
import java.math.BigInteger
import scala.annotation.tailrec
import scala.math.Ordering
@ -31,6 +32,19 @@ package object core {
}
}
implicit class bigIntegerUtil(private val bigInt: BigInteger) extends AnyVal {
implicit def intExact: Int = {
if (bigInt.bitLength() <= 31) bigInt.intValue()
else throw new ArithmeticException("BigInteger out of int range");
}
implicit def longExact: Long = {
if (bigInt.bitLength() <= 63) bigInt.longValue()
else throw new ArithmeticException("BigInteger out of long range");
}
}
implicit val satoshisPerKiloByteOrdering: Ordering[SatoshisPerKiloByte] =
new Ordering[SatoshisPerKiloByte] {

View File

@ -5,11 +5,9 @@ import org.bitcoins.core.protocol.tlv.{
EnumOutcome,
UnsignedNumericOutcome
}
import org.bitcoins.core.protocol.transaction.WitnessTransaction
import org.bitcoins.crypto.{ECPublicKey, SchnorrNonce}
import scala.concurrent.duration.DurationInt
import scala.concurrent.{Await, ExecutionContext, Future}
/** OracleOutcomes are in one-to-one correspondence with Contract
* Execution Transactions (CETs) and are defined by a set of oracles
* needed to execute with a given CET, representing a certain outcome
@ -27,8 +25,12 @@ sealed trait OracleOutcome {
*/
def outcome: DLCOutcomeType
protected def computeSigPoint: ECPublicKey
/** The adaptor point used to encrypt the signatures for this corresponding CET. */
def sigPoint: ECPublicKey
def sigPoint: ECPublicKey = {
SigPointComputer(() => computeSigPoint).compute
}
/** The sum of all oracle nonces used in execution with this OracleOutcome. */
def aggregateNonce: SchnorrNonce
@ -42,12 +44,8 @@ case class EnumOracleOutcome(
outcome: EnumOutcome)
extends OracleOutcome {
private val sigPointF = Future {
override protected def computeSigPoint: ECPublicKey = {
oracles.map(_.sigPoint(outcome)).reduce(_.add(_))
}(ExecutionContext.global)
override lazy val sigPoint: ECPublicKey = {
Await.result(sigPointF, 10.seconds)
}
override lazy val aggregateNonce: SchnorrNonce = {
@ -77,16 +75,12 @@ case class NumericOracleOutcome(oraclesAndOutcomes: Vector[
def outcomes: Vector[UnsignedNumericOutcome] =
oraclesAndOutcomes.map(_._2)
private val sigPointF = Future {
override protected def computeSigPoint: ECPublicKey = {
oraclesAndOutcomes
.map { case (oracle, outcome) =>
oracle.sigPoint(outcome)
}
.reduce(_.add(_))
}(ExecutionContext.global)
override lazy val sigPoint: ECPublicKey = {
Await.result(sigPointF, 20.seconds)
}
override lazy val aggregateNonce: SchnorrNonce = {
@ -108,3 +102,6 @@ object NumericOracleOutcome {
NumericOracleOutcome(Vector((oracleInfo, outcome)))
}
}
/** An oracle outcome and it's corresponding CET */
case class OutcomeCETPair(outcome: OracleOutcome, wtx: WitnessTransaction)

View File

@ -1,5 +1,6 @@
package org.bitcoins.core.protocol.ln.currency
import org.bitcoins.core._
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.number._
import org.bitcoins.core.protocol.ln._
@ -51,9 +52,9 @@ sealed abstract class LnCurrencyUnit
def toBigInt: BigInt
def toLong: Long = toBigInt.bigInteger.longValueExact()
def toLong: Long = toBigInt.bigInteger.longExact
def toInt: Int = toBigInt.bigInteger.intValueExact()
def toInt: Int = toBigInt.bigInteger.intExact
protected def underlying: BigInt

View File

@ -1,5 +1,6 @@
package org.bitcoins.core.protocol.ln.currency
import org.bitcoins.core._
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
import org.bitcoins.core.number.{BasicArithmetic, UInt64}
import org.bitcoins.crypto.NetworkElement
@ -34,7 +35,7 @@ sealed abstract class MilliSatoshis
def toBigInt: BigInt = underlying
def toLong: Long = toBigInt.bigInteger.longValueExact
def toLong: Long = toBigInt.bigInteger.longExact
def toBigDecimal: BigDecimal = BigDecimal(toBigInt)

View File

@ -79,8 +79,8 @@ object LnRoute {
val (cltvExpiryDeltaBytes, _) = rest3.splitAt(CLTV_EXPIRTY_DELTA_LEN)
val cltvExpiryDelta = new BigInteger(
cltvExpiryDeltaBytes.toArray).shortValueExact
val cltvExpiryDelta =
new BigInteger(cltvExpiryDeltaBytes.toArray).shortValue()
LnRoute(pubKey,
shortChannelId,

View File

@ -1,7 +1,5 @@
package org.bitcoins.core.serializers.p2p
import java.net.InetAddress
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.p2p._
import org.bitcoins.core.serializers.RawBitcoinSerializer

View File

@ -1,7 +1,5 @@
package org.bitcoins.core.serializers.p2p.messages
import java.net.InetAddress
import org.bitcoins.core.number.{Int32, Int64, UInt32, UInt64}
import org.bitcoins.core.p2p._
import org.bitcoins.core.protocol.CompactSizeUInt

View File

@ -1,20 +1,7 @@
package org.bitcoins.crypto
import org.bitcoins.crypto.facade.{
Buffer,
ECDSA,
HMAC,
Hash160,
Random,
RandomBrowser,
RipeMd160,
SHA1,
SHA256,
SHA256Factory,
SHA512,
SipHash
}
import scodec.bits.ByteVector
import org.bitcoins.crypto.facade._
import scodec.bits._
import java.math.BigInteger
import scala.scalajs.js
@ -31,11 +18,9 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
private lazy val ripeMd160 = new RipeMd160
private lazy val sha1 = new SHA1
private lazy val sha256 = SHA256Factory.create()
private lazy val sha512 = SHA512Factory.create()
private lazy val hmac = SHA512.hmac.apply().asInstanceOf[HMAC]
private lazy val ecdsa =
new ECDSA("SECP256K1", sha256, js.constructorOf[SHA256], null)
private lazy val randomBytesFunc: Int => ByteVector = { int =>
try {
// try to call the native implementation
@ -50,7 +35,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
}
}
def randomBytes(n: Int): ByteVector = randomBytesFunc(n)
override def randomBytes(n: Int): ByteVector = randomBytesFunc(n)
override def ripeMd160(bytes: ByteVector): RipeMd160Digest = {
val buffer = CryptoJsUtil.toNodeBuffer(bytes)
@ -78,7 +63,8 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
override def toPublicKey(privateKey: ECPrivateKey): ECPublicKey = {
val buffer = CryptoJsUtil.toNodeBuffer(privateKey.bytes)
val pubKeyBuffer =
ecdsa.publicKeyCreate(key = buffer, compressed = privateKey.isCompressed)
SECP256k1.publicKeyCreate(key = buffer,
compressed = privateKey.isCompressed)
val privKeyByteVec = CryptoJsUtil.toByteVector(pubKeyBuffer)
ECPublicKey.fromBytes(privKeyByteVec)
}
@ -92,9 +78,17 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
Sha256Digest.fromBytes(hashByteVec)
}
def sha512(bytes: ByteVector): ByteVector = {
val buffer = CryptoJsUtil.toNodeBuffer(bytes)
sha512.init()
sha512.update(buffer)
val hashBuffer = sha512.`final`()
CryptoJsUtil.toByteVector(hashBuffer)
}
/** Generates a 32 byte private key */
override def freshPrivateKey: ECPrivateKey = {
val keyBytes = ecdsa.privateKeyGenerate()
val keyBytes = SECP256k1.privateKeyGenerate()
val byteVec = CryptoJsUtil.toByteVector(keyBytes)
ECPrivateKey.fromBytes(byteVec)
}
@ -136,18 +130,18 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
val sigBuffer = CryptoJsUtil.toNodeBuffer(signature.bytes)
val keyBytes =
if (signature.isDEREncoded)
ecdsa.recoverDER(msgBuffer, sigBuffer, param = 0, compress = true)
SECP256k1.recoverDER(msgBuffer, sigBuffer, param = 0, compress = true)
else
ecdsa.recover(msgBuffer, sigBuffer, param = 0, compress = true)
SECP256k1.recover(msgBuffer, sigBuffer, param = 0, compress = true)
val keyByteVec = CryptoJsUtil.toByteVector(keyBytes)
val key = ECPublicKey.fromBytes(keyByteVec)
val keyBytesWithSign =
if (signature.isDEREncoded)
ecdsa.recoverDER(msgBuffer, sigBuffer, param = 1, compress = true)
SECP256k1.recoverDER(msgBuffer, sigBuffer, param = 1, compress = true)
else
ecdsa.recover(msgBuffer, sigBuffer, param = 1, compress = true)
SECP256k1.recover(msgBuffer, sigBuffer, param = 1, compress = true)
val keyWithSignByteVec = CryptoJsUtil.toByteVector(keyBytesWithSign)
val keyWithSign = ECPublicKey.fromBytes(keyWithSignByteVec)
@ -158,7 +152,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
override def publicKey(privateKey: ECPrivateKey): ECPublicKey = {
val buffer = CryptoJsUtil.toNodeBuffer(privateKey.bytes)
val bufferPubKey =
ecdsa.publicKeyCreate(buffer, privateKey.isCompressed)
SECP256k1.publicKeyCreate(buffer, privateKey.isCompressed)
val byteVec = CryptoJsUtil.toByteVector(bufferPubKey)
ECPublicKey.fromBytes(byteVec)
}
@ -168,7 +162,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
dataToSign: ByteVector): ECDigitalSignature = {
val privBuffer = CryptoJsUtil.toNodeBuffer(privateKey.bytes)
val dataBuffer = CryptoJsUtil.toNodeBuffer(dataToSign)
val buffer = ecdsa.signDER(dataBuffer, privBuffer)
val buffer = SECP256k1.signDER(dataBuffer, privBuffer)
val byteVec = CryptoJsUtil.toByteVector(buffer)
ECDigitalSignature.fromFrontOfBytes(byteVec)
}
@ -180,7 +174,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
override def secKeyVerify(privateKeybytes: ByteVector): Boolean = {
val buffer = CryptoJsUtil.toNodeBuffer(privateKeybytes)
ecdsa.privateKeyVerify(buffer)
SECP256k1.privateKeyVerify(buffer)
}
override def verify(
@ -190,11 +184,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
val dataBuffer = CryptoJsUtil.toNodeBuffer(data)
val sigBuffer = CryptoJsUtil.toNodeBuffer(signature.bytes)
val pubKeyBuffer = CryptoJsUtil.toNodeBuffer(publicKey.bytes)
if (signature.isDEREncoded) {
ecdsa.verifyDER(dataBuffer, sigBuffer, pubKeyBuffer)
} else {
ecdsa.verify(dataBuffer, sigBuffer, pubKeyBuffer)
}
SECP256k1.verifyDER(dataBuffer, sigBuffer, pubKeyBuffer)
}
override def tweakMultiply(
@ -202,7 +192,8 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
tweak: FieldElement): ECPublicKey = {
val pubKeyBuffer = CryptoJsUtil.toNodeBuffer(publicKey.bytes)
val tweakBuffer = CryptoJsUtil.toNodeBuffer(tweak.bytes)
val keyBuffer = ecdsa.publicKeyTweakMul(pubKeyBuffer, tweakBuffer, true)
val keyBuffer =
SECP256k1.publicKeyTweakMul(pubKeyBuffer, tweakBuffer, compress = true)
val keyByteVec = CryptoJsUtil.toByteVector(keyBuffer)
ECPublicKey.fromBytes(keyByteVec)
}
@ -214,7 +205,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
}
def publicKeyConvert(buffer: Buffer, compressed: Boolean): Buffer =
ecdsa.publicKeyConvert(buffer, compressed)
SECP256k1.publicKeyConvert(buffer, compressed)
override def publicKeyConvert(
key: ECPublicKey,
@ -226,7 +217,8 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
val pk2Buffer = CryptoJsUtil.toNodeBuffer(pk2.bytes)
try {
val keyBuffer =
ecdsa.publicKeyCombine(js.Array(pk1Buffer, pk2Buffer), true)
SECP256k1.publicKeyCombine(js.Array(pk1Buffer, pk2Buffer),
compress = true)
val keyBytes = CryptoJsUtil.toByteVector(keyBuffer)
ECPublicKey.fromBytes(keyBytes)
} catch {
@ -257,14 +249,15 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
privkey: ECPrivateKey): ECPublicKey = {
val pubKeyBuffer = CryptoJsUtil.toNodeBuffer(pubkey.bytes)
val privKeyBuffer = CryptoJsUtil.toNodeBuffer(privkey.bytes)
val keyBuffer = ecdsa.publicKeyTweakAdd(pubKeyBuffer, privKeyBuffer, true)
val keyBuffer =
SECP256k1.publicKeyTweakAdd(pubKeyBuffer, privKeyBuffer, compress = true)
val keyByteVec = CryptoJsUtil.toByteVector(keyBuffer)
ECPublicKey.fromBytes(keyByteVec)
}
override def isValidPubKey(bytes: ByteVector): Boolean = {
val buffer = CryptoJsUtil.toNodeBuffer(bytes)
ecdsa.publicKeyVerify(buffer)
SECP256k1.publicKeyVerify(buffer)
}
override def sipHash(item: ByteVector, key: SipHashKey): Long = {
@ -280,7 +273,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
if (bytes.size == 1 && bytes(0) == 0x00) {
ECPointInfinity
} else {
val decoded = ecdsa.curve
val decoded = SECP256k1.curve
.applyDynamic("decodePoint")(CryptoJsUtil.toNodeBuffer(bytes))
.asInstanceOf[Point]
@ -291,6 +284,24 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
new BigInteger(decoded.getY().toString()))
}
}
override def pbkdf2WithSha512(
pass: ByteVector,
salt: ByteVector,
iterationCount: Int,
derivedKeyLength: Int): ByteVector = {
// bcrypto uses bytes instead of bits for length, so divide by 8
val keyLengthBytes = derivedKeyLength / 8
val buffer =
PBKDF2.derive(sha512,
CryptoJsUtil.toNodeBuffer(pass),
CryptoJsUtil.toNodeBuffer(salt),
iterationCount,
keyLengthBytes)
CryptoJsUtil.toByteVector(buffer)
}
}
object BCryptoCryptoRuntime extends BCryptoCryptoRuntime

View File

@ -9,7 +9,7 @@ import scala.scalajs.js.annotation._
* https://github.com/bcoin-org/bcrypto/blob/master/lib/js/hash160.js
*/
@js.native
@JSImport("bcrypto/lib/hash160.js", JSImport.Default)
@JSImport("bcrypto/lib/js/hash160.js", JSImport.Default)
class Hash160 extends Hasher {
override def init(): Unit = js.native

View File

@ -0,0 +1,21 @@
package org.bitcoins.crypto.facade
import org.bitcoins.crypto.Hasher
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
/** Scala wrapper for
* https://github.com/bcoin-org/bcrypto/blob/master/lib/js/pbkdf2.js
*/
@js.native
@JSImport("bcrypto/lib/pbkdf2.js", JSImport.Namespace)
object PBKDF2 extends js.Object {
def derive(
hash: Hasher,
pass: Buffer,
salt: Buffer,
iter: Int,
len: Int): Buffer = js.native
}

View File

@ -4,16 +4,11 @@ import scala.scalajs.js
import scala.scalajs.js.annotation._
/** Scala wrapper for
* https://github.com/bcoin-org/bcrypto/blob/master/lib/js/ecdsa.js
* https://github.com/bcoin-org/bcrypto/blob/master/lib/js/secp256k1.js
*/
@js.native
@JSImport("bcrypto/lib/js/ecdsa.js", JSImport.Default)
class ECDSA(
name: String = "SECP256K1",
hash: SHA256 = new SHA256,
xof: js.Dynamic = js.constructorOf[SHA256],
pre: String = null)
extends js.Object {
@JSImport("bcrypto/lib/js/secp256k1.js", JSImport.Namespace)
object SECP256k1 extends js.Object {
def privateKeyGenerate(): Buffer = js.native

View File

@ -206,6 +206,22 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
crypto.ECPoint(decoded.getRawXCoord.getEncoded,
decoded.getRawYCoord.getEncoded)
}
override def pbkdf2WithSha512(
pass: ByteVector,
salt: ByteVector,
iterationCount: Int,
derivedKeyLength: Int): ByteVector = {
val bytes =
PBKDF2.withSha512(pass, salt, iterationCount, derivedKeyLength).getEncoded
ByteVector(bytes)
}
override def randomBytes(n: Int): ByteVector = {
val array = new Array[Byte](n)
secureRandom.nextBytes(array)
ByteVector(array)
}
}
object BouncycastleCryptoRuntime extends BouncycastleCryptoRuntime

View File

@ -278,6 +278,21 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
override def decodePoint(bytes: ByteVector): ECPoint =
BouncycastleCryptoRuntime.decodePoint(bytes)
override def randomBytes(n: Int): ByteVector = {
BouncycastleCryptoRuntime.randomBytes(n)
}
override def pbkdf2WithSha512(
pass: ByteVector,
salt: ByteVector,
iterationCount: Int,
derivedKeyLength: Int): ByteVector = {
BouncycastleCryptoRuntime.pbkdf2WithSha512(pass,
salt,
iterationCount,
derivedKeyLength)
}
}
object LibSecp256k1CryptoRuntime extends LibSecp256k1CryptoRuntime

View File

@ -22,18 +22,6 @@ object PBKDF2 {
private val secretKeyFactory =
SecretKeyFactory.getInstance(PSEUDO_RANDOM_FUNCTION)
/** $keyStretch */
def withSha512(
string: String,
salt: String,
iterationCount: Int,
derivedKeyLength: Int): SecretKey = {
withSha512(ByteVector(string.getBytes),
ByteVector(salt.getBytes),
iterationCount,
derivedKeyLength)
}
/** $keyStretch */
def withSha512(
bytes: ByteVector,

View File

@ -2,7 +2,6 @@ package org.bitcoins.crypto
import scodec.bits.ByteVector
import java.security.SecureRandom
import javax.crypto.spec.{IvParameterSpec, SecretKeySpec}
import javax.crypto.{BadPaddingException, Cipher, SecretKey}
import scala.util.{Failure, Success, Try}
@ -77,10 +76,8 @@ object AesSalt extends Factory[AesSalt] {
* of 32 bytes
*/
def random: AesSalt = {
val rand = new SecureRandom
val array = new Array[Byte](32)
rand.nextBytes(array)
AesSalt(ByteVector(array))
val bytes = CryptoUtil.randomBytes(32)
AesSalt(bytes)
}
}
@ -110,12 +107,13 @@ final case class AesPassword private (private val value: String)
case Right(bytes) => bytes
}
val secretKey = PBKDF2.withSha512(passwordBytes,
salt.bytes,
iterationCount = AesPassword.ITERATIONS,
derivedKeyLength = AesPassword.KEY_SIZE)
val key = AesKey.fromSecretKey(secretKey)
key
val secretKey = CryptoUtil.pbkdf2WithSha512(
passwordBytes,
salt.bytes,
iterationCount = AesPassword.ITERATIONS,
derivedKeyLength = AesPassword.KEY_SIZE)
AesKey.fromValidBytes(secretKey)
}
override def toStringSensitive: String = value
@ -212,10 +210,7 @@ object AesKey {
/** Gets a AES key with the specified number of bytes */
private def get(length: Int): AesKey = {
val random = new SecureRandom
val arr = new Array[Byte](length)
random.nextBytes(arr)
AesKey(ByteVector(arr))
AesKey(CryptoUtil.randomBytes(length))
}
/** Gets a random 128 bit AES key */
@ -258,10 +253,7 @@ object AesIV {
/** Generates a random IV */
def random: AesIV = {
val random = new SecureRandom()
val bytes = new Array[Byte](AesIV.length)
random.nextBytes(bytes)
AesIV(ByteVector(bytes))
AesIV(CryptoUtil.randomBytes(AesIV.length))
}
}

View File

@ -300,4 +300,23 @@ trait CryptoRuntime {
/** https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki#hashing-data-objects */
def sipHash(item: ByteVector, key: SipHashKey): Long
def pbkdf2WithSha512(
pass: String,
salt: String,
iterationCount: Int,
derivedKeyLength: Int): ByteVector = {
pbkdf2WithSha512(ByteVector(pass.getBytes),
ByteVector(salt.getBytes),
iterationCount,
derivedKeyLength)
}
def pbkdf2WithSha512(
pass: ByteVector,
salt: ByteVector,
iterationCount: Int,
derivedKeyLength: Int): ByteVector
def randomBytes(n: Int): ByteVector
}

View File

@ -198,6 +198,15 @@ trait CryptoUtil extends CryptoRuntime {
override def decodePoint(bytes: ByteVector): ECPoint =
cryptoRuntime.decodePoint(bytes)
override def randomBytes(n: Int): ByteVector = cryptoRuntime.randomBytes(n)
override def pbkdf2WithSha512(
pass: ByteVector,
salt: ByteVector,
iterationCount: Int,
derivedKeyLength: Int): ByteVector =
cryptoRuntime.pbkdf2WithSha512(pass, salt, iterationCount, derivedKeyLength)
}
object CryptoUtil extends CryptoUtil

View File

@ -142,10 +142,15 @@ trait Sign extends AsyncSign {
signWithEntropy(bytes, startBytes)
}
if (sig.bytes.length <= 70) {
sig
} else {
signLowR(bytes, startAt + 1)
// If we are using BCrypto then we can't sign with entropy
CryptoUtil.cryptoContext match {
case CryptoContext.BCrypto => sig
case CryptoContext.LibSecp256k1 | CryptoContext.BouncyCastle =>
if (sig.bytes.length <= 70) {
sig
} else {
signLowR(bytes, startAt + 1)
}
}
}

View File

@ -156,7 +156,7 @@ object WalletStorage extends KeyManagerLogger {
logger.debug(s"Read raw mnemonic from $seedPath")
Try(ujson.read(rawJson)) match {
case Failure(ujson.ParseException(clue, _, _, _)) =>
case Failure(ujson.ParseException(clue, _)) =>
Left(ReadMnemonicError.JsonParsingError(clue))
case Failure(exception) => throw exception
case Success(value) =>

View File

@ -1,7 +1,5 @@
package org.bitcoins.node.networking.peer
import java.net.InetAddress
import akka.actor.ActorRef
import akka.io.Tcp
import akka.util.Timeout
@ -63,7 +61,11 @@ case class PeerMessageSender(client: P2PClient)(implicit conf: NodeAppConfig)
/** Sends a [[org.bitcoins.core.p2p.VersionMessage VersionMessage]] to our peer */
def sendVersionMessage(): Future[Unit] = {
val versionMsg = VersionMessage(client.peer.socket, conf.network)
val local = java.net.InetAddress.getLocalHost
val versionMsg = VersionMessage(
conf.network,
InetAddress(client.peer.socket.getAddress.getAddress),
InetAddress(local.getAddress))
logger.trace(s"Sending versionMsg=$versionMsg to peer=${client.peer}")
sendMsg(versionMsg)
}
@ -71,13 +73,14 @@ case class PeerMessageSender(client: P2PClient)(implicit conf: NodeAppConfig)
def sendVersionMessage(chainApi: ChainApi)(implicit
ec: ExecutionContext): Future[Unit] = {
chainApi.getBestHashBlockHeight().flatMap { height =>
val transmittingIpAddress = InetAddress.getLocalHost
val transmittingIpAddress = java.net.InetAddress.getLocalHost
val receivingIpAddress = client.peer.socket.getAddress
val versionMsg = VersionMessage(conf.network,
"/Bitcoin-S:0.5.0/",
Int32(height),
receivingIpAddress,
transmittingIpAddress)
val versionMsg =
VersionMessage(conf.network,
"/Bitcoin-S:0.5.0/",
Int32(height),
InetAddress(receivingIpAddress.getAddress),
InetAddress(transmittingIpAddress.getAddress))
logger.trace(s"Sending versionMsg=$versionMsg to peer=${client.peer}")
sendMsg(versionMsg)

View File

@ -1,6 +1,5 @@
import Deps.{Compile, Test}
import sbt._
import org.portablescala.sbtplatformdeps.PlatformDepsPlugin.autoImport._
import sbt._
object Deps {
@ -43,7 +42,7 @@ object Deps {
val breezeV = "1.1"
val newMicroPickleV = "0.8.0"
val newMicroPickleV = "1.3.8"
val newMicroJsonV = newMicroPickleV
// akka-http-upickle is not yet published
@ -62,6 +61,7 @@ object Deps {
val scoptV = "4.0.1"
val sttpV = "1.7.2"
val codehausV = "3.1.3"
val scalaJsTimeV = "2.2.0"
}
object Compile {
@ -163,7 +163,8 @@ object Deps {
val newMicroJson = "com.lihaoyi" %% "ujson" % V.newMicroJsonV
val newMicroPickle = "com.lihaoyi" %% "upickle" % V.newMicroPickleV
val newMicroPickle =
Def.setting("com.lihaoyi" %%% "upickle" % V.newMicroPickleV)
// get access to reflection data at compile-time
val sourcecode = "com.lihaoyi" %% "sourcecode" % V.sourcecodeV
@ -184,6 +185,10 @@ object Deps {
val scalaJsStubs =
"org.scala-js" %% "scalajs-stubs" % V.scalaJsStubsV % "provided"
val scalaJsTime =
Def.setting(
"io.github.cquiroz" %%% "scala-java-time" % V.scalaJsTimeV withSources () withJavadoc ())
val scalaTest =
Def.setting(
"org.scalatest" %%% "scalatest" % V.scalaTest withSources () withJavadoc ())
@ -246,13 +251,14 @@ object Deps {
Test.pgEmbedded
)
def appCommons(scalaVersion: String) =
val appCommons = Def.setting {
List(
Compile.newMicroPickle,
Compile.newMicroPickle.value,
Compile.playJson,
Compile.slf4j,
Compile.grizzledSlf4j
)
}
def core = Def.setting {
List(
@ -283,12 +289,25 @@ object Deps {
List(
Test.junitInterface,
Test.scalaTest.value,
Test.spray,
Test.playJson,
Test.scalaCollectionCompat,
Compile.newMicroPickle.value
)
}
val coreTestJVM = Def.setting {
List(
Test.junitInterface,
Test.scalaTest.value,
Test.scalaCollectionCompat
)
}
val coreJs = Def.setting {
List(
Compile.scalaJsTime.value
)
}
def cryptoTest = Def.setting {
List(
Test.scalaTest.value,
@ -350,10 +369,10 @@ object Deps {
)
}
def cli(scalaVersion: String) =
val cli = Def.setting {
List(
Compile.sttp,
Compile.newMicroPickle,
Compile.newMicroPickle.value,
Compile.logback,
Compile.scopt,
//we can remove this dependency when this is fixed
@ -361,26 +380,29 @@ object Deps {
//see https://github.com/bitcoin-s/bitcoin-s/issues/1100
Compile.codehaus
)
}
val gui = List(Compile.breezeViz, Compile.scalaFx) ++ Compile.javaFxDeps
def server(scalaVersion: String) =
val server = Def.setting {
List(
Compile.newMicroPickle,
Compile.newMicroPickle.value,
Compile.logback,
Compile.akkaActor,
Compile.akkaHttp,
Compile.akkaSlf4j
)
}
val oracleServer =
val oracleServer = Def.setting {
List(
Compile.newMicroPickle,
Compile.newMicroPickle.value,
Compile.logback,
Compile.akkaActor,
Compile.akkaHttp,
Compile.akkaSlf4j
)
}
val eclairRpc = List(
Compile.akkaHttp,
@ -435,7 +457,7 @@ object Deps {
def testkitCore = Def.setting {
List(
Compile.newMicroPickle,
Compile.newMicroPickle.value,
Compile.scalaCollectionCompat,
Compile.scalacheck.value,
Compile.scalaTest.value,

View File

@ -1,9 +1,7 @@
package org.bitcoins.testkitcore.gen.p2p
import java.net.{InetAddress, InetSocketAddress}
import org.bitcoins.core.number.{UInt32, UInt64}
import org.bitcoins.core.p2p.{ProtocolVersion, _}
import org.bitcoins.core.p2p._
import org.bitcoins.core.protocol.CompactSizeUInt
import org.bitcoins.core.wallet.fee.{
SatoshisPerByte,
@ -12,6 +10,7 @@ import org.bitcoins.core.wallet.fee.{
SatoshisPerVirtualByte
}
import org.bitcoins.testkitcore.gen._
import org.bitcoins.testkitcore.gen.p2p.P2PGenerator._
import org.scalacheck.Gen
import scodec.bits.ByteVector
@ -119,16 +118,6 @@ object ControlMessageGenerator {
randomNum <- Gen.choose(0, 1)
} yield ServiceIdentifier(randomNum)
def inetAddress: Gen[InetAddress] =
for {
socketAddress <- inetSocketAddress
} yield socketAddress.getAddress
def inetSocketAddress: Gen[InetSocketAddress] =
for {
p <- portNumber
} yield new InetSocketAddress(p)
def portNumber: Gen[Int] = Gen.choose(0, 65535)
/** Creates a [[org.bitcoins.core.p2p.FilterLoadMessage]]

View File

@ -1,11 +1,8 @@
package org.bitcoins.testkitcore.gen.p2p
import org.scalacheck.Gen
import org.bitcoins.core.p2p.NetworkIpAddress
import org.bitcoins.core.p2p._
import org.bitcoins.testkitcore.gen.NumberGenerator
import org.bitcoins.core.p2p.ServiceIdentifier
import java.net.InetAddress
import org.bitcoins.core.p2p.NetworkPayload
import org.scalacheck.Gen
object P2PGenerator {
@ -24,7 +21,11 @@ object P2PGenerator {
} yield {
// as long as we don't pass in a host name no IO is performed
// https://stackoverflow.com/questions/5571744/java-convert-a-string-representing-an-ip-to-inetaddress
InetAddress.getByName(s"$first.$second.$third.$fourth")
// fixme
val arr = Array(first.toByte, second.toByte, third.toByte, fourth.toByte)
val inet = InetAddress(arr)
InetAddress(NetworkIpAddress.writeAddress(inet))
}
}

View File

@ -165,7 +165,6 @@ Here is an example of a transaction spending a `scriptPubKey` which is correctly
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script._
import org.bitcoins.core.script.interpreter._
import org.bitcoins.core.policy._
import org.bitcoins.core.number._
import org.bitcoins.core.crypto._
@ -188,4 +187,3 @@ val preExecution = PreExecutionScriptProgram(btxsc)
val result = ScriptInterpreter.run(preExecution)
// result: org.bitcoins.core.script.result.ScriptResult = ScriptOk
```

View File

@ -84,7 +84,6 @@ Here is an example of a transaction spending a `scriptPubKey` which is correctly
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script._
import org.bitcoins.core.script.interpreter._
import org.bitcoins.core.policy._
import org.bitcoins.core.number._
import org.bitcoins.core.crypto._