mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 01:40:55 +01:00
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:
parent
6e574931c6
commit
fa80f36d2f
@ -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
|
@ -1,3 +1,3 @@
|
||||
name := "bitcoin-s-app-commons"
|
||||
|
||||
libraryDependencies ++= Deps.appCommons(scalaVersion.value)
|
||||
libraryDependencies ++= Deps.appCommons.value
|
||||
|
@ -1,6 +1,6 @@
|
||||
name := "bitcoin-s-cli"
|
||||
|
||||
libraryDependencies ++= Deps.cli(scalaVersion.value)
|
||||
libraryDependencies ++= Deps.cli.value
|
||||
|
||||
nativeImageOptions ++= Seq(
|
||||
"-H:+ReportExceptionStackTraces",
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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"))
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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]]
|
||||
|
@ -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}"
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
35
core/src/main/scala/org/bitcoins/core/p2p/InetAddress.scala
Normal file
35
core/src/main/scala/org/bitcoins/core/p2p/InetAddress.scala
Normal 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)
|
||||
}
|
||||
}
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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] {
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) =>
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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]]
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
```
|
||||
|
||||
|
@ -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._
|
||||
|
Loading…
Reference in New Issue
Block a user