mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-03 18:47:38 +01:00
Bump P2P + crypto test coverage (#506)
* Rename P2P tests and move generators into correct package * Add more tests for P2P messages * Add note on coverage to contributing guide * Replace network in P2P header with typed network * Add more P2P tests * Add more tests for ECPriv and ECPub
This commit is contained in:
parent
2723ef6f5d
commit
6de0ba268e
41 changed files with 551 additions and 126 deletions
|
@ -1,25 +1,2 @@
|
||||||
[  ](https://bintray.com/bitcoin-s/bitcoin-s-core/bitcoin-s-core-test/_latestVersion)
|
See our [contributing guide](https://bitcoin-s.org/docs/contributing) for information
|
||||||
|
on how to benchmark, test and develop your code.
|
||||||
## Running tests
|
|
||||||
|
|
||||||
To run the entire `core` test suite:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
chris@chris:~/dev/bitcoins-core$ bloop test coreTest
|
|
||||||
```
|
|
||||||
|
|
||||||
## Coverage
|
|
||||||
|
|
||||||
To produce a report that quantifies how much of our code is covered by tests:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sbt
|
|
||||||
> coverage
|
|
||||||
> coreTest/test
|
|
||||||
> core/coverageReport
|
|
||||||
```
|
|
||||||
|
|
||||||
This generates three different reports: Cobertura, XML and HTML formats.
|
|
||||||
See the output of your sbt shell to find the location of them.
|
|
||||||
Open up the HTML file in your browser. You'll now see code coverage
|
|
||||||
of all files in `core` project.
|
|
||||||
|
|
|
@ -2,14 +2,12 @@ package org.bitcoins.core.crypto
|
||||||
|
|
||||||
import org.bitcoinj.core.Sha256Hash
|
import org.bitcoinj.core.Sha256Hash
|
||||||
import org.bitcoins.testkit.core.gen.CryptoGenerators
|
import org.bitcoins.testkit.core.gen.CryptoGenerators
|
||||||
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
import org.scalatest.prop.PropertyChecks
|
import org.scalatest.prop.PropertyChecks
|
||||||
import org.scalatest.{FlatSpec, MustMatchers}
|
import org.scalatest.{FlatSpec, MustMatchers}
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits._
|
||||||
|
|
||||||
/**
|
class ECPublicKeyTest extends BitcoinSUnitTest {
|
||||||
* Created by chris on 2/29/16.
|
|
||||||
*/
|
|
||||||
class ECPublicKeyTest extends FlatSpec with MustMatchers {
|
|
||||||
|
|
||||||
"ECPublicKey" must "verify that a arbitrary piece of data was signed by the private key corresponding to a public key" in {
|
"ECPublicKey" must "verify that a arbitrary piece of data was signed by the private key corresponding to a public key" in {
|
||||||
|
|
||||||
|
@ -58,6 +56,28 @@ class ECPublicKeyTest extends FlatSpec with MustMatchers {
|
||||||
bitcoinsSignature.bytes.toArray) must be(true)
|
bitcoinsSignature.bytes.toArray) must be(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it must "be able to decompress keys" in {
|
||||||
|
val uncompressed =
|
||||||
|
ECPublicKey(
|
||||||
|
hex"044f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa385b6b1b8ead809ca67454d9683fcf2ba03456d6fe2c4abe2b07f0fbdbb2f1c1")
|
||||||
|
val compressed =
|
||||||
|
ECPublicKey(
|
||||||
|
hex"034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa")
|
||||||
|
|
||||||
|
assert(uncompressed.isFullyValid)
|
||||||
|
assert(compressed.isFullyValid)
|
||||||
|
|
||||||
|
assert(compressed.isCompressed)
|
||||||
|
assert(!uncompressed.isCompressed)
|
||||||
|
|
||||||
|
assert(compressed.decompressed == uncompressed)
|
||||||
|
assert(uncompressed.decompressed == uncompressed)
|
||||||
|
}
|
||||||
|
|
||||||
|
it must "generate unique keys" in {
|
||||||
|
assert(ECPublicKey() != ECPublicKey())
|
||||||
|
}
|
||||||
|
|
||||||
it must "have serialization symmetry from ECPublicKey -> ECPoint -> ECPublicKey" in {
|
it must "have serialization symmetry from ECPublicKey -> ECPoint -> ECPublicKey" in {
|
||||||
PropertyChecks.forAll(CryptoGenerators.publicKey) { pubKey =>
|
PropertyChecks.forAll(CryptoGenerators.publicKey) { pubKey =>
|
||||||
val p = pubKey.toPoint
|
val p = pubKey.toPoint
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
import org.bitcoins.testkit.core.gen.p2p.DataMessageGenerator
|
||||||
|
import org.bitcoins.testkit.core.gen.p2p.ControlMessageGenerator
|
||||||
|
|
||||||
|
class AddrMessageTest extends BitcoinSUnitTest {
|
||||||
|
it must "have serialization symmetry" in {
|
||||||
|
forAll(ControlMessageGenerator.addrMessage) { addr =>
|
||||||
|
val fromBytes = AddrMessage.fromBytes(addr.bytes)
|
||||||
|
// assert(fromBytes == addr) todo
|
||||||
|
assert(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
import org.bitcoins.testkit.core.gen.p2p.DataMessageGenerator
|
||||||
|
|
||||||
|
class BlockMessageTest extends BitcoinSUnitTest {
|
||||||
|
it must "have serialization symmetry" in {
|
||||||
|
forAll(DataMessageGenerator.blockMessage) { block =>
|
||||||
|
assert(block == BlockMessage.fromBytes(block.bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
import org.bitcoins.testkit.core.gen.p2p.ControlMessageGenerator
|
||||||
|
|
||||||
|
class FeeFilterMessageTest extends BitcoinSUnitTest {
|
||||||
|
it must "have serialization symmetry" in {
|
||||||
|
forAll(ControlMessageGenerator.feeFilterMessage) { fee =>
|
||||||
|
assert(FeeFilterMessage.fromBytes(fee.bytes) == fee)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,9 @@
|
||||||
package org.bitcoins.core.p2p
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
import org.bitcoins.testkit.gen.ControlMessageGenerator
|
import org.bitcoins.testkit.core.gen.p2p.ControlMessageGenerator
|
||||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
|
||||||
/**
|
class FilterAddMessageTest extends BitcoinSUnitTest {
|
||||||
* Created by chris on 8/26/16.
|
|
||||||
*/
|
|
||||||
class FilterAddMessageSpec extends BitcoinSUnitTest {
|
|
||||||
|
|
||||||
it must "have serialization symmetry" in {
|
it must "have serialization symmetry" in {
|
||||||
forAll(ControlMessageGenerator.filterAddMessage) { filterAddMsg =>
|
forAll(ControlMessageGenerator.filterAddMessage) { filterAddMsg =>
|
|
@ -0,0 +1,68 @@
|
||||||
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
|
import org.bitcoins.testkit.core.gen.p2p.ControlMessageGenerator
|
||||||
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
import org.bitcoins.core.protocol.CompactSizeUInt
|
||||||
|
import org.bitcoins.core.number.UInt64
|
||||||
|
import scodec.bits._
|
||||||
|
import org.bitcoins.core.number.UInt32
|
||||||
|
import org.bitcoins.core.bloom.BloomUpdateAll
|
||||||
|
import org.bitcoins.core.bloom.BloomFlag
|
||||||
|
import org.bitcoins.core.bloom.BloomFilter
|
||||||
|
|
||||||
|
class FilterLoadMessageTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
|
it must "have serialization symmetry" in {
|
||||||
|
forAll(ControlMessageGenerator.filterLoadMessage) { filterMsg =>
|
||||||
|
assert(FilterLoadMessage(filterMsg.hex) == filterMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it must "throw on too large filters" in {
|
||||||
|
intercept[IllegalArgumentException] {
|
||||||
|
val size = CompactSizeUInt(UInt64(36001))
|
||||||
|
val data = ByteVector.empty
|
||||||
|
val hashFuncs = UInt32.one
|
||||||
|
val tweak = UInt32.zero
|
||||||
|
val flags = BloomUpdateAll
|
||||||
|
val bloom = BloomFilter(filterSize = size,
|
||||||
|
data = data,
|
||||||
|
hashFuncs = hashFuncs,
|
||||||
|
tweak = tweak,
|
||||||
|
flags = flags)
|
||||||
|
FilterLoadMessage(bloom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it must "throw on too many hashfuncs" in {
|
||||||
|
intercept[IllegalArgumentException] {
|
||||||
|
val size = CompactSizeUInt(UInt64(36000))
|
||||||
|
val data = ByteVector.empty
|
||||||
|
val hashFuncs = UInt32(51)
|
||||||
|
val tweak = UInt32.zero
|
||||||
|
val flags = BloomUpdateAll
|
||||||
|
val bloom = BloomFilter(filterSize = size,
|
||||||
|
data = data,
|
||||||
|
hashFuncs = hashFuncs,
|
||||||
|
tweak = tweak,
|
||||||
|
flags = flags)
|
||||||
|
FilterLoadMessage(bloom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it must "throw on size discrepancy" in {
|
||||||
|
intercept[IllegalArgumentException] {
|
||||||
|
val size = CompactSizeUInt(UInt64(36000))
|
||||||
|
val data = ByteVector.empty
|
||||||
|
val hashFuncs = UInt32.one
|
||||||
|
val tweak = UInt32.zero
|
||||||
|
val flags = BloomUpdateAll
|
||||||
|
val bloom = BloomFilter(filterSize = size,
|
||||||
|
data = data,
|
||||||
|
hashFuncs = hashFuncs,
|
||||||
|
tweak = tweak,
|
||||||
|
flags = flags)
|
||||||
|
FilterLoadMessage(bloom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
|
import org.bitcoins.testkit.core.gen.p2p.DataMessageGenerator
|
||||||
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
|
||||||
|
class GetBlocksMessageTest extends BitcoinSUnitTest {
|
||||||
|
it must "have serialization symmetry" in {
|
||||||
|
forAll(DataMessageGenerator.getBlocksMessage) { getBlocks =>
|
||||||
|
assert(getBlocks == GetBlocksMessage.fromBytes(getBlocks.bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
|
import org.bitcoins.testkit.core.gen.p2p.DataMessageGenerator
|
||||||
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
import org.scalacheck.Gen
|
||||||
|
import org.bitcoins.core.crypto.DoubleSha256Digest
|
||||||
|
|
||||||
|
class GetDataMessageTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
|
it must "have serialization symmetry" in {
|
||||||
|
forAll(DataMessageGenerator.getDataMessages) { dataMsg =>
|
||||||
|
assert(GetDataMessage(dataMsg.hex) == dataMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it must "be constructable from inventories" in {
|
||||||
|
forAll(DataMessageGenerator.getDataMessages) { getData =>
|
||||||
|
assert(GetDataMessage(getData.inventories) == getData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it must "be constructable from a single inventory" in {
|
||||||
|
val inventory = Inventory(TypeIdentifier.MsgBlock, DoubleSha256Digest.empty)
|
||||||
|
assert(GetDataMessage(inventory) == GetDataMessage(Seq(inventory)))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
|
import org.bitcoins.testkit.core.gen.p2p.DataMessageGenerator
|
||||||
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
import org.bitcoins.core.crypto.DoubleSha256Digest
|
||||||
|
import org.bitcoins.testkit.core.gen.CryptoGenerators
|
||||||
|
|
||||||
|
class GetHeadersMessageTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
|
it must "have serialization symmetry" in {
|
||||||
|
forAll(DataMessageGenerator.getHeaderMessages) { headerMsg =>
|
||||||
|
assert(GetHeadersMessage(headerMsg.hex) == headerMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it must "be constructable from just hashes" in {
|
||||||
|
forAll(DataMessageGenerator.getHeaderDefaultProtocolMessage) { getHeader =>
|
||||||
|
assert(
|
||||||
|
GetHeadersMessage(getHeader.hashes, getHeader.hashStop) == getHeader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it must "be constructable without a stop" in {
|
||||||
|
def getHash: DoubleSha256Digest =
|
||||||
|
CryptoGenerators.doubleSha256Digest.sample.getOrElse(getHash)
|
||||||
|
val msg = GetHeadersMessage(List.fill(10)(getHash))
|
||||||
|
assert(msg.hashStop == DoubleSha256Digest.empty)
|
||||||
|
|
||||||
|
val hash = getHash
|
||||||
|
val otherMsg = GetHeadersMessage(hash)
|
||||||
|
assert(otherMsg == GetHeadersMessage(Vector(hash)))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
package org.bitcoins.core.p2p
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
import org.bitcoins.testkit.gen.DataMessageGenerator
|
import org.bitcoins.testkit.core.gen.p2p.DataMessageGenerator
|
||||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
|
||||||
class HeadersMessageSpec extends BitcoinSUnitTest {
|
class HeadersMessageTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
it must "have serialization symmetry" in {
|
it must "have serialization symmetry" in {
|
||||||
forAll(DataMessageGenerator.headersMessage) { headersMsg =>
|
forAll(DataMessageGenerator.headersMessage) { headersMsg =>
|
|
@ -1,9 +1,9 @@
|
||||||
package org.bitcoins.core.p2p
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
import org.bitcoins.testkit.gen.DataMessageGenerator
|
import org.bitcoins.testkit.core.gen.p2p.DataMessageGenerator
|
||||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
|
||||||
class InventoryMessageSpec extends BitcoinSUnitTest {
|
class InventoryMessageTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
it must " have serialization symmetry" in {
|
it must " have serialization symmetry" in {
|
||||||
forAll(DataMessageGenerator.inventoryMessages) { invMessage =>
|
forAll(DataMessageGenerator.inventoryMessages) { invMessage =>
|
|
@ -1,9 +1,9 @@
|
||||||
package org.bitcoins.core.p2p
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
import org.bitcoins.testkit.gen.DataMessageGenerator
|
import org.bitcoins.testkit.core.gen.p2p.DataMessageGenerator
|
||||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
|
||||||
class InventorySpec extends BitcoinSUnitTest {
|
class InventoryTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
it must "have serialization symmetry" in {
|
it must "have serialization symmetry" in {
|
||||||
forAll(DataMessageGenerator.inventory) { inventory =>
|
forAll(DataMessageGenerator.inventory) { inventory =>
|
|
@ -1,9 +1,9 @@
|
||||||
package org.bitcoins.core.p2p
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
import org.bitcoins.testkit.gen.DataMessageGenerator
|
import org.bitcoins.testkit.core.gen.p2p.DataMessageGenerator
|
||||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
|
||||||
class MerkleBlockMessageSpec extends BitcoinSUnitTest {
|
class MerkleBlockMessageTest extends BitcoinSUnitTest {
|
||||||
it must "have serialization symmetry" in {
|
it must "have serialization symmetry" in {
|
||||||
forAll(DataMessageGenerator.merkleBlockMessage) { merkleBlockMsg =>
|
forAll(DataMessageGenerator.merkleBlockMessage) { merkleBlockMsg =>
|
||||||
assert(MerkleBlockMessage(merkleBlockMsg.hex) == merkleBlockMsg)
|
assert(MerkleBlockMessage(merkleBlockMsg.hex) == merkleBlockMsg)
|
|
@ -5,15 +5,15 @@ import org.bitcoins.core.number.UInt32
|
||||||
import org.bitcoins.core.util.{BitcoinSUtil, CryptoUtil}
|
import org.bitcoins.core.util.{BitcoinSUtil, CryptoUtil}
|
||||||
import org.bitcoins.testkit.node.NodeTestUtil
|
import org.bitcoins.testkit.node.NodeTestUtil
|
||||||
import org.scalatest.{FlatSpec, MustMatchers}
|
import org.scalatest.{FlatSpec, MustMatchers}
|
||||||
|
import org.bitcoins.core.config.MainNet
|
||||||
|
import scala.util.Random
|
||||||
|
import scodec.bits.ByteVector
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by chris on 6/10/16.
|
|
||||||
*/
|
|
||||||
class NetworkHeaderTest extends FlatSpec with MustMatchers {
|
class NetworkHeaderTest extends FlatSpec with MustMatchers {
|
||||||
|
|
||||||
"MessageHeader" must "must create a message header for a message" in {
|
"MessageHeader" must "must create a message header for a message" in {
|
||||||
val messageHeader = NetworkHeader(TestNet3, NodeTestUtil.versionMessage)
|
val messageHeader = NetworkHeader(TestNet3, NodeTestUtil.versionMessage)
|
||||||
messageHeader.network must be(TestNet3.magicBytes)
|
messageHeader.network must be(TestNet3)
|
||||||
messageHeader.commandName must be(NodeTestUtil.versionMessage.commandName)
|
messageHeader.commandName must be(NodeTestUtil.versionMessage.commandName)
|
||||||
messageHeader.payloadSize must be(
|
messageHeader.payloadSize must be(
|
||||||
UInt32(NodeTestUtil.versionMessage.bytes.size))
|
UInt32(NodeTestUtil.versionMessage.bytes.size))
|
||||||
|
@ -23,10 +23,20 @@ class NetworkHeaderTest extends FlatSpec with MustMatchers {
|
||||||
|
|
||||||
it must "build the correct message header for a verack message" in {
|
it must "build the correct message header for a verack message" in {
|
||||||
val messageHeader = NetworkHeader(TestNet3, VerAckMessage)
|
val messageHeader = NetworkHeader(TestNet3, VerAckMessage)
|
||||||
messageHeader.network must be(TestNet3.magicBytes)
|
messageHeader.network must be(TestNet3)
|
||||||
messageHeader.commandName must be(VerAckMessage.commandName)
|
messageHeader.commandName must be(VerAckMessage.commandName)
|
||||||
messageHeader.payloadSize must be(UInt32.zero)
|
messageHeader.payloadSize must be(UInt32.zero)
|
||||||
BitcoinSUtil.encodeHex(messageHeader.checksum) must be("5df6e0e2")
|
BitcoinSUtil.encodeHex(messageHeader.checksum) must be("5df6e0e2")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it must "throw on messages of bad length" in {
|
||||||
|
intercept[IllegalArgumentException] {
|
||||||
|
val commandName = Random.shuffle(NetworkPayload.commandNames).head
|
||||||
|
NetworkHeader(MainNet,
|
||||||
|
commandName,
|
||||||
|
payloadSize = UInt32.one,
|
||||||
|
checksum = ByteVector.empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
import org.bitcoins.testkit.core.gen.p2p.P2PGenerator
|
||||||
|
|
||||||
|
class NetworkIpAddressTest extends BitcoinSUnitTest {
|
||||||
|
it must "have serialization symmetry" in {
|
||||||
|
forAll(P2PGenerator.networkIpAddress) { ip =>
|
||||||
|
val fromBytes = NetworkIpAddress.fromBytes(ip.bytes)
|
||||||
|
assert(fromBytes == ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package org.bitcoins.core.p2p
|
||||||
|
|
||||||
import org.bitcoins.testkit.node.NodeTestUtil
|
import org.bitcoins.testkit.node.NodeTestUtil
|
||||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
import org.bitcoins.testkit.core.gen.p2p.P2PGenerator
|
||||||
|
|
||||||
class NetworkPayloadTest extends BitcoinSUnitTest {
|
class NetworkPayloadTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
|
@ -14,4 +15,14 @@ class NetworkPayloadTest extends BitcoinSUnitTest {
|
||||||
payload.isInstanceOf[VersionMessage] must be(true)
|
payload.isInstanceOf[VersionMessage] must be(true)
|
||||||
payload.commandName must be(NetworkPayload.versionCommandName)
|
payload.commandName must be(NetworkPayload.versionCommandName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this tests has a bunch of messages to choose between, so we set a high config value
|
||||||
|
implicit override val generatorDrivenConfig = customGenDrivenConfig(200)
|
||||||
|
it must "parse messages based on its command name" in {
|
||||||
|
forAll(P2PGenerator.message) { p2p =>
|
||||||
|
val bytes = p2p.bytes
|
||||||
|
val parser = NetworkPayload.readers(p2p.commandName)
|
||||||
|
assert(parser(bytes) == p2p)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
import org.bitcoins.testkit.core.gen.p2p.DataMessageGenerator
|
||||||
|
|
||||||
|
class NotFoundMessageTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
|
it must "have serialization symmetry" in {
|
||||||
|
forAll(DataMessageGenerator.notFoundMessage) { notFound =>
|
||||||
|
assert(NotFoundMessage.fromBytes(notFound.bytes) == notFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
package org.bitcoins.core.p2p
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
import org.bitcoins.testkit.gen.ControlMessageGenerator
|
import org.bitcoins.testkit.core.gen.p2p.ControlMessageGenerator
|
||||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
|
||||||
class PingMessageSpec extends BitcoinSUnitTest {
|
class PingMessageTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
it must "have symmetry serialization" in {
|
it must "have symmetry serialization" in {
|
||||||
forAll(ControlMessageGenerator.pingMessage) { pingMessage =>
|
forAll(ControlMessageGenerator.pingMessage) { pingMessage =>
|
|
@ -1,9 +1,9 @@
|
||||||
package org.bitcoins.core.p2p
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
import org.bitcoins.testkit.gen.ControlMessageGenerator
|
import org.bitcoins.testkit.core.gen.p2p.ControlMessageGenerator
|
||||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
|
||||||
class PongMessageSpec extends BitcoinSUnitTest {
|
class PongMessageTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
it must "have serialization symmetry" in {
|
it must "have serialization symmetry" in {
|
||||||
forAll(ControlMessageGenerator.pongMessage) { pongMsg =>
|
forAll(ControlMessageGenerator.pongMessage) { pongMsg =>
|
|
@ -1,9 +1,9 @@
|
||||||
package org.bitcoins.core.p2p
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
import org.bitcoins.testkit.gen.ControlMessageGenerator
|
import org.bitcoins.testkit.core.gen.p2p.ControlMessageGenerator
|
||||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
|
||||||
class RejectMessageSpec extends BitcoinSUnitTest {
|
class RejectMessageTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
it must "have serialization symmetry" in {
|
it must "have serialization symmetry" in {
|
||||||
forAll(ControlMessageGenerator.rejectMessage) { rejectMsg =>
|
forAll(ControlMessageGenerator.rejectMessage) { rejectMsg =>
|
|
@ -1,9 +1,9 @@
|
||||||
package org.bitcoins.core.p2p
|
package org.bitcoins.core.p2p
|
||||||
|
|
||||||
import org.bitcoins.testkit.gen.DataMessageGenerator
|
import org.bitcoins.testkit.core.gen.p2p.DataMessageGenerator
|
||||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
|
||||||
class TransactionMessageSpec extends BitcoinSUnitTest {
|
class TransactionMessageTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
it must "have serialization symmetry" in {
|
it must "have serialization symmetry" in {
|
||||||
forAll(DataMessageGenerator.transactionMessage) { txMsg =>
|
forAll(DataMessageGenerator.transactionMessage) { txMsg =>
|
|
@ -4,11 +4,18 @@ import java.net.InetAddress
|
||||||
|
|
||||||
import org.bitcoins.core.config.MainNet
|
import org.bitcoins.core.config.MainNet
|
||||||
import org.bitcoins.core.number.{Int32, UInt64}
|
import org.bitcoins.core.number.{Int32, UInt64}
|
||||||
|
import org.bitcoins.testkit.core.gen.p2p.ControlMessageGenerator
|
||||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
|
|
||||||
class VersionMessageTest extends BitcoinSUnitTest {
|
class VersionMessageTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
|
it must "have serialization symmetry" in {
|
||||||
|
forAll(ControlMessageGenerator.versionMessage) { versionMessage =>
|
||||||
|
assert(VersionMessage(versionMessage.hex) == versionMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"VersionMessage" must "create a new version message to be sent to another node on the network" in {
|
"VersionMessage" must "create a new version message to be sent to another node on the network" in {
|
||||||
val versionMessage = VersionMessage(MainNet, InetAddress.getLocalHost)
|
val versionMessage = VersionMessage(MainNet, InetAddress.getLocalHost)
|
||||||
versionMessage.addressReceiveServices must be(UnnamedService)
|
versionMessage.addressReceiveServices must be(UnnamedService)
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package org.bitcoins.core.serializers.p2p.headers
|
package org.bitcoins.core.serializers.p2p.headers
|
||||||
|
|
||||||
import org.bitcoins.core.number.UInt32
|
import org.bitcoins.core.number.UInt32
|
||||||
import org.bitcoins.core.util.BitcoinSUtil
|
|
||||||
import org.bitcoins.core.p2p.NetworkPayload
|
import org.bitcoins.core.p2p.NetworkPayload
|
||||||
import org.bitcoins.testkit.node.NodeTestUtil
|
import org.bitcoins.testkit.node.NodeTestUtil
|
||||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
import scodec.bits._
|
||||||
|
|
||||||
class RawNetworkHeaderSerializerTest extends BitcoinSUnitTest {
|
class RawNetworkHeaderSerializerTest extends BitcoinSUnitTest {
|
||||||
val hex = "f9beb4d976657261636b000000000000000000005df6e0e2"
|
val hex = "f9beb4d976657261636b000000000000000000005df6e0e2"
|
||||||
|
@ -14,13 +14,13 @@ class RawNetworkHeaderSerializerTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
val messageHeader = RawNetworkHeaderSerializer.read(hex)
|
val messageHeader = RawNetworkHeaderSerializer.read(hex)
|
||||||
//this is the mainnet id
|
//this is the mainnet id
|
||||||
BitcoinSUtil.encodeHex(messageHeader.network) must be("f9beb4d9")
|
messageHeader.network.magicBytes must be(hex"f9beb4d9")
|
||||||
|
|
||||||
messageHeader.commandName must be("verack")
|
messageHeader.commandName must be("verack")
|
||||||
|
|
||||||
messageHeader.payloadSize must be(UInt32.zero)
|
messageHeader.payloadSize must be(UInt32.zero)
|
||||||
|
|
||||||
BitcoinSUtil.encodeHex(messageHeader.checksum) must be("5df6e0e2")
|
messageHeader.checksum must be(hex"5df6e0e2")
|
||||||
}
|
}
|
||||||
|
|
||||||
it must "write an object that was just read and get the original input" in {
|
it must "write an object that was just read and get the original input" in {
|
||||||
|
@ -31,11 +31,11 @@ class RawNetworkHeaderSerializerTest extends BitcoinSUnitTest {
|
||||||
it must "read a network header from a node on the network" in {
|
it must "read a network header from a node on the network" in {
|
||||||
val hex = NodeTestUtil.rawNetworkMessage.take(48)
|
val hex = NodeTestUtil.rawNetworkMessage.take(48)
|
||||||
val header = RawNetworkHeaderSerializer.read(hex)
|
val header = RawNetworkHeaderSerializer.read(hex)
|
||||||
BitcoinSUtil.encodeHex(header.network) must be("0B110907".toLowerCase)
|
header.network.magicBytes must be(hex"0B110907")
|
||||||
header.commandName.size must be(NetworkPayload.versionCommandName.size)
|
header.commandName.size must be(NetworkPayload.versionCommandName.size)
|
||||||
header.commandName must be(NetworkPayload.versionCommandName)
|
header.commandName must be(NetworkPayload.versionCommandName)
|
||||||
header.payloadSize must be(UInt32(102))
|
header.payloadSize must be(UInt32(102))
|
||||||
BitcoinSUtil.encodeHex(header.checksum) must be("2f6743da")
|
header.checksum must be(hex"2f6743da")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.bitcoins.core.serializers.p2p.messages
|
package org.bitcoins.core.serializers.p2p
|
||||||
|
|
||||||
import org.bitcoins.core.number.UInt32
|
import org.bitcoins.core.number.UInt32
|
||||||
import org.bitcoins.core.p2p.NodeNetwork
|
import org.bitcoins.core.p2p.NodeNetwork
|
||||||
|
|
|
@ -162,6 +162,13 @@ object Networks {
|
||||||
case _: String => None
|
case _: String => None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Map of magic network bytes to the corresponding network */
|
||||||
|
def magicToNetwork: Map[ByteVector, NetworkParameters] = Map(
|
||||||
|
MainNet.magicBytes -> MainNet,
|
||||||
|
TestNet3.magicBytes -> TestNet3,
|
||||||
|
RegTest.magicBytes -> RegTest
|
||||||
|
)
|
||||||
|
|
||||||
def bytesToNetwork: Map[ByteVector, NetworkParameters] = Map(
|
def bytesToNetwork: Map[ByteVector, NetworkParameters] = Map(
|
||||||
MainNet.p2shNetworkByte -> MainNet,
|
MainNet.p2shNetworkByte -> MainNet,
|
||||||
MainNet.p2pkhNetworkByte -> MainNet,
|
MainNet.p2pkhNetworkByte -> MainNet,
|
||||||
|
|
|
@ -16,10 +16,10 @@ sealed trait NetworkHeader extends NetworkElement {
|
||||||
override def bytes: ByteVector = RawNetworkHeaderSerializer.write(this)
|
override def bytes: ByteVector = RawNetworkHeaderSerializer.write(this)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Magic bytes indicating the originating network;
|
* Each network has magic bytes indicating the originating network;
|
||||||
* used to seek to next message when stream state is unknown.
|
* used to seek to next message when stream state is unknown.
|
||||||
*/
|
*/
|
||||||
def network: ByteVector
|
def network: NetworkParameters
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ASCII string which identifies what message type is contained in the payload.
|
* ASCII string which identifies what message type is contained in the payload.
|
||||||
|
@ -46,7 +46,7 @@ sealed trait NetworkHeader extends NetworkElement {
|
||||||
object NetworkHeader extends Factory[NetworkHeader] {
|
object NetworkHeader extends Factory[NetworkHeader] {
|
||||||
|
|
||||||
private case class NetworkHeaderImpl(
|
private case class NetworkHeaderImpl(
|
||||||
network: ByteVector,
|
network: NetworkParameters,
|
||||||
commandName: String,
|
commandName: String,
|
||||||
payloadSize: UInt32,
|
payloadSize: UInt32,
|
||||||
checksum: ByteVector)
|
checksum: ByteVector)
|
||||||
|
@ -65,7 +65,7 @@ object NetworkHeader extends Factory[NetworkHeader] {
|
||||||
* @param checksum the checksum of the payload to ensure that the entire payload was sent
|
* @param checksum the checksum of the payload to ensure that the entire payload was sent
|
||||||
*/
|
*/
|
||||||
def apply(
|
def apply(
|
||||||
network: ByteVector,
|
network: NetworkParameters,
|
||||||
commandName: String,
|
commandName: String,
|
||||||
payloadSize: UInt32,
|
payloadSize: UInt32,
|
||||||
checksum: ByteVector): NetworkHeader = {
|
checksum: ByteVector): NetworkHeader = {
|
||||||
|
@ -81,7 +81,7 @@ object NetworkHeader extends Factory[NetworkHeader] {
|
||||||
network: NetworkParameters,
|
network: NetworkParameters,
|
||||||
payload: NetworkPayload): NetworkHeader = {
|
payload: NetworkPayload): NetworkHeader = {
|
||||||
val checksum = CryptoUtil.doubleSHA256(payload.bytes)
|
val checksum = CryptoUtil.doubleSHA256(payload.bytes)
|
||||||
NetworkHeader(network.magicBytes,
|
NetworkHeader(network,
|
||||||
payload.commandName,
|
payload.commandName,
|
||||||
UInt32(payload.bytes.size),
|
UInt32(payload.bytes.size),
|
||||||
checksum.bytes.take(4))
|
checksum.bytes.take(4))
|
||||||
|
|
|
@ -4,7 +4,7 @@ import java.net.{InetAddress, InetSocketAddress}
|
||||||
|
|
||||||
import org.bitcoins.core.number.UInt32
|
import org.bitcoins.core.number.UInt32
|
||||||
import org.bitcoins.core.protocol.NetworkElement
|
import org.bitcoins.core.protocol.NetworkElement
|
||||||
import org.bitcoins.core.serializers.p2p.messages.RawNetworkIpAddressSerializer
|
import org.bitcoins.core.serializers.p2p._
|
||||||
import org.bitcoins.core.util.Factory
|
import org.bitcoins.core.util.Factory
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
|
||||||
|
|
|
@ -35,13 +35,15 @@ sealed trait NetworkPayload extends NetworkElement {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a data message inside of bitcoin core
|
* Represents a data message inside of bitcoin core
|
||||||
* [[https://bitcoin.org/en/developer-reference#data-messages]]
|
*
|
||||||
|
* @see [[https://bitcoin.org/en/developer-reference#data-messages]]
|
||||||
*/
|
*/
|
||||||
sealed trait DataPayload extends NetworkPayload
|
sealed trait DataPayload extends NetworkPayload
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The block message transmits a single serialized block
|
* The block message transmits a single serialized block
|
||||||
* [[https://bitcoin.org/en/developer-reference#block]]
|
*
|
||||||
|
* @see [[https://bitcoin.org/en/developer-reference#block]]
|
||||||
*/
|
*/
|
||||||
trait BlockMessage extends DataPayload {
|
trait BlockMessage extends DataPayload {
|
||||||
|
|
||||||
|
@ -252,8 +254,8 @@ object GetHeadersMessage extends Factory[GetHeadersMessage] {
|
||||||
GetHeadersMessage(hashes, hashStop)
|
GetHeadersMessage(hashes, hashStop)
|
||||||
}
|
}
|
||||||
|
|
||||||
def apply(hashes: DoubleSha256Digest): GetHeadersMessage = {
|
def apply(hash: DoubleSha256Digest): GetHeadersMessage = {
|
||||||
GetHeadersMessage(Vector(hashes))
|
GetHeadersMessage(Vector(hash))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,7 +359,8 @@ object InventoryMessage extends Factory[InventoryMessage] {
|
||||||
* as valid but which have not yet appeared in a block.
|
* as valid but which have not yet appeared in a block.
|
||||||
* That is, transactions which are in the receiving node’s memory pool.
|
* That is, transactions which are in the receiving node’s memory pool.
|
||||||
* The response to the mempool message is one or more inv messages containing the TXIDs in the usual inventory format.
|
* The response to the mempool message is one or more inv messages containing the TXIDs in the usual inventory format.
|
||||||
* [[https://bitcoin.org/en/developer-reference#mempool]]
|
*
|
||||||
|
* @see [[https://bitcoin.org/en/developer-reference#mempool]]
|
||||||
*/
|
*/
|
||||||
case object MemPoolMessage extends DataPayload {
|
case object MemPoolMessage extends DataPayload {
|
||||||
override val commandName = NetworkPayload.memPoolCommandName
|
override val commandName = NetworkPayload.memPoolCommandName
|
||||||
|
@ -404,6 +407,7 @@ object MerkleBlockMessage extends Factory[MerkleBlockMessage] {
|
||||||
* node does not have available for relay. (Nodes are not expected to relay historic transactions
|
* node does not have available for relay. (Nodes are not expected to relay historic transactions
|
||||||
* which are no longer in the memory pool or relay set.
|
* which are no longer in the memory pool or relay set.
|
||||||
* Nodes may also have pruned spent transactions from older blocks, making them unable to send those blocks.)
|
* Nodes may also have pruned spent transactions from older blocks, making them unable to send those blocks.)
|
||||||
|
*
|
||||||
* @see [[https://bitcoin.org/en/developer-reference#notfound]]
|
* @see [[https://bitcoin.org/en/developer-reference#notfound]]
|
||||||
*/
|
*/
|
||||||
trait NotFoundMessage extends DataPayload with InventoryMessage {
|
trait NotFoundMessage extends DataPayload with InventoryMessage {
|
||||||
|
@ -413,6 +417,7 @@ trait NotFoundMessage extends DataPayload with InventoryMessage {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The companion object factory used to create NotFoundMessages on the p2p network
|
* The companion object factory used to create NotFoundMessages on the p2p network
|
||||||
|
*
|
||||||
* @see https://bitcoin.org/en/developer-reference#notfound
|
* @see https://bitcoin.org/en/developer-reference#notfound
|
||||||
*/
|
*/
|
||||||
object NotFoundMessage extends Factory[NotFoundMessage] {
|
object NotFoundMessage extends Factory[NotFoundMessage] {
|
||||||
|
@ -425,6 +430,11 @@ object NotFoundMessage extends Factory[NotFoundMessage] {
|
||||||
def fromBytes(bytes: ByteVector): NotFoundMessage =
|
def fromBytes(bytes: ByteVector): NotFoundMessage =
|
||||||
RawNotFoundMessageSerializer.read(bytes)
|
RawNotFoundMessageSerializer.read(bytes)
|
||||||
|
|
||||||
|
def apply(inventories: Seq[Inventory]): NotFoundMessage = {
|
||||||
|
val count = CompactSizeUInt(UInt64(inventories.length))
|
||||||
|
apply(count, inventories)
|
||||||
|
}
|
||||||
|
|
||||||
def apply(
|
def apply(
|
||||||
inventoryCount: CompactSizeUInt,
|
inventoryCount: CompactSizeUInt,
|
||||||
inventories: Seq[Inventory]): NotFoundMessage = {
|
inventories: Seq[Inventory]): NotFoundMessage = {
|
||||||
|
@ -499,6 +509,11 @@ object AddrMessage extends Factory[AddrMessage] {
|
||||||
def fromBytes(bytes: ByteVector): AddrMessage =
|
def fromBytes(bytes: ByteVector): AddrMessage =
|
||||||
RawAddrMessageSerializer.read(bytes)
|
RawAddrMessageSerializer.read(bytes)
|
||||||
|
|
||||||
|
def apply(addresses: Seq[NetworkIpAddress]): AddrMessage = {
|
||||||
|
val count = CompactSizeUInt(UInt64(addresses.length))
|
||||||
|
apply(count, addresses)
|
||||||
|
}
|
||||||
|
|
||||||
def apply(
|
def apply(
|
||||||
ipCount: CompactSizeUInt,
|
ipCount: CompactSizeUInt,
|
||||||
addresses: Seq[NetworkIpAddress]): AddrMessage =
|
addresses: Seq[NetworkIpAddress]): AddrMessage =
|
||||||
|
@ -1086,13 +1101,14 @@ object NetworkPayload {
|
||||||
val versionCommandName = "version"
|
val versionCommandName = "version"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains all the valid command names with their deserializer on the p2p protocol
|
* Contains all the valid command names with their deserializer on the p2p protocol.
|
||||||
* These commands all have the null bytes appended to the end of the string as
|
* These commands all have the null bytes appended to the end of the string as
|
||||||
* required in [[NetworkHeader]]
|
* required by the network header specification.
|
||||||
* [[https://bitcoin.org/en/developer-reference#message-headers]]
|
*
|
||||||
|
* @see [[https://bitcoin.org/en/developer-reference#message-headers]]
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
val commandNames: Map[String, ByteVector => NetworkPayload] = Map(
|
val readers: Map[String, ByteVector => NetworkPayload] = Map(
|
||||||
blockCommandName -> { RawBlockMessageSerializer.read(_) },
|
blockCommandName -> { RawBlockMessageSerializer.read(_) },
|
||||||
getBlocksCommandName -> { RawGetBlocksMessageSerializer.read(_) },
|
getBlocksCommandName -> { RawGetBlocksMessageSerializer.read(_) },
|
||||||
getHeadersCommandName -> { RawGetHeadersMessageSerializer.read(_) },
|
getHeadersCommandName -> { RawGetHeadersMessageSerializer.read(_) },
|
||||||
|
@ -1117,9 +1133,7 @@ object NetworkPayload {
|
||||||
},
|
},
|
||||||
pingCommandName -> { RawPingMessageSerializer.read(_) },
|
pingCommandName -> { RawPingMessageSerializer.read(_) },
|
||||||
pongCommandName -> { RawPongMessageSerializer.read(_) },
|
pongCommandName -> { RawPongMessageSerializer.read(_) },
|
||||||
rejectCommandName -> { _: ByteVector =>
|
rejectCommandName -> { RawRejectMessageSerializer.read(_) },
|
||||||
???
|
|
||||||
},
|
|
||||||
sendHeadersCommandName -> { _: ByteVector =>
|
sendHeadersCommandName -> { _: ByteVector =>
|
||||||
SendHeadersMessage
|
SendHeadersMessage
|
||||||
},
|
},
|
||||||
|
@ -1129,6 +1143,9 @@ object NetworkPayload {
|
||||||
versionCommandName -> { RawVersionMessageSerializer.read(_) }
|
versionCommandName -> { RawVersionMessageSerializer.read(_) }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/** All command names for P2P messages */
|
||||||
|
val commandNames: Vector[String] = readers.keys.toVector
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a [[NetworkPayload]] from the given bytes using the [[NetworkHeader]]
|
* Parses a [[NetworkPayload]] from the given bytes using the [[NetworkHeader]]
|
||||||
* to determine what type of [[NetworkPayload]] this is
|
* to determine what type of [[NetworkPayload]] this is
|
||||||
|
@ -1139,7 +1156,7 @@ object NetworkPayload {
|
||||||
networkHeader: NetworkHeader,
|
networkHeader: NetworkHeader,
|
||||||
payloadBytes: ByteVector): NetworkPayload = {
|
payloadBytes: ByteVector): NetworkPayload = {
|
||||||
//the commandName in the network header tells us what payload type this is
|
//the commandName in the network header tells us what payload type this is
|
||||||
val deserializer: ByteVector => NetworkPayload = commandNames(
|
val deserializer: ByteVector => NetworkPayload = readers(
|
||||||
networkHeader.commandName)
|
networkHeader.commandName)
|
||||||
deserializer(payloadBytes)
|
deserializer(payloadBytes)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,8 @@ object ProtocolVersion extends Factory[ProtocolVersion] {
|
||||||
ProtocolVersion60002,
|
ProtocolVersion60002,
|
||||||
ProtocolVersion70001,
|
ProtocolVersion70001,
|
||||||
ProtocolVersion70002,
|
ProtocolVersion70002,
|
||||||
ProtocolVersion70012
|
ProtocolVersion70012,
|
||||||
|
ProtocolVersion70013
|
||||||
)
|
)
|
||||||
|
|
||||||
def fromBytes(bytes: ByteVector): ProtocolVersion = {
|
def fromBytes(bytes: ByteVector): ProtocolVersion = {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.bitcoins.core.p2p.serializers.messages
|
package org.bitcoins.core.serializers.p2p
|
||||||
|
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ trait RawNetworkIpAddressSerializer
|
||||||
val services = ServiceIdentifier(bytes.slice(4, 12))
|
val services = ServiceIdentifier(bytes.slice(4, 12))
|
||||||
val ipBytes = bytes.slice(12, 28)
|
val ipBytes = bytes.slice(12, 28)
|
||||||
val ipAddress = InetAddress.getByAddress(ipBytes.toArray)
|
val ipAddress = InetAddress.getByAddress(ipBytes.toArray)
|
||||||
val port = NumberUtil.toLong(bytes.slice(28, 30)).toInt
|
val port = bytes.slice(28, 30).toInt(signed = false)
|
||||||
NetworkIpAddress(time, services, ipAddress, port)
|
NetworkIpAddress(time, services, ipAddress, port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,8 +29,9 @@ trait RawNetworkIpAddressSerializer
|
||||||
val time = networkIpAddress.time.bytes.reverse
|
val time = networkIpAddress.time.bytes.reverse
|
||||||
val services = networkIpAddress.services.bytes
|
val services = networkIpAddress.services.bytes
|
||||||
val ipAddress = NetworkIpAddress.writeAddress(networkIpAddress.address)
|
val ipAddress = NetworkIpAddress.writeAddress(networkIpAddress.address)
|
||||||
//uint16s are only 4 hex characters
|
// uint16s are only 4 hex characters
|
||||||
val port = ByteVector.fromShort(networkIpAddress.port.toShort)
|
// cannot do fromShort,
|
||||||
|
val port = ByteVector.fromInt(networkIpAddress.port, size = 2)
|
||||||
time ++ services ++ ipAddress ++ port
|
time ++ services ++ ipAddress ++ port
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.bitcoins.core.serializers.p2p.headers
|
package org.bitcoins.core.serializers.p2p.headers
|
||||||
|
|
||||||
import org.bitcoins.core.number.UInt32
|
import org.bitcoins.core.number.UInt32
|
||||||
|
import org.bitcoins.core.config._
|
||||||
import org.bitcoins.core.p2p.NetworkHeader
|
import org.bitcoins.core.p2p.NetworkHeader
|
||||||
import org.bitcoins.core.serializers.RawBitcoinSerializer
|
import org.bitcoins.core.serializers.RawBitcoinSerializer
|
||||||
import org.bitcoins.core.util.BitcoinSLogger
|
import org.bitcoins.core.util.BitcoinSLogger
|
||||||
|
@ -20,7 +21,7 @@ trait RawNetworkHeaderSerializer
|
||||||
* @return the native object for the MessageHeader
|
* @return the native object for the MessageHeader
|
||||||
*/
|
*/
|
||||||
def read(bytes: ByteVector): NetworkHeader = {
|
def read(bytes: ByteVector): NetworkHeader = {
|
||||||
val network = bytes.take(4)
|
val network = Networks.magicToNetwork(bytes.take(4))
|
||||||
//.trim removes the null characters appended to the command name
|
//.trim removes the null characters appended to the command name
|
||||||
val commandName = bytes.slice(4, 16).toArray.map(_.toChar).mkString.trim
|
val commandName = bytes.slice(4, 16).toArray.map(_.toChar).mkString.trim
|
||||||
val payloadSize = UInt32(bytes.slice(16, 20).reverse)
|
val payloadSize = UInt32(bytes.slice(16, 20).reverse)
|
||||||
|
@ -39,7 +40,7 @@ trait RawNetworkHeaderSerializer
|
||||||
//command name needs to be 12 bytes in size, or 24 chars in hex
|
//command name needs to be 12 bytes in size, or 24 chars in hex
|
||||||
val commandName = ByteVector(commandNameNoPadding).padRight(12)
|
val commandName = ByteVector(commandNameNoPadding).padRight(12)
|
||||||
val checksum = messageHeader.checksum
|
val checksum = messageHeader.checksum
|
||||||
network ++
|
network.magicBytes ++
|
||||||
commandName ++
|
commandName ++
|
||||||
messageHeader.payloadSize.bytes.reverse ++
|
messageHeader.payloadSize.bytes.reverse ++
|
||||||
checksum
|
checksum
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.bitcoins.core.serializers.p2p.messages
|
||||||
|
|
||||||
import org.bitcoins.core.protocol.CompactSizeUInt
|
import org.bitcoins.core.protocol.CompactSizeUInt
|
||||||
import org.bitcoins.core.serializers.{RawBitcoinSerializer, RawSerializerHelper}
|
import org.bitcoins.core.serializers.{RawBitcoinSerializer, RawSerializerHelper}
|
||||||
|
import org.bitcoins.core.serializers.p2p._
|
||||||
import org.bitcoins.core.p2p._
|
import org.bitcoins.core.p2p._
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package org.bitcoins.core.serializers.p2p.messages
|
package org.bitcoins.core.serializers.p2p.messages
|
||||||
|
|
||||||
import org.bitcoins.core.currency.Satoshis
|
import org.bitcoins.core.currency.Satoshis
|
||||||
import org.bitcoins.core.number.Int64
|
|
||||||
import org.bitcoins.core.serializers.RawBitcoinSerializer
|
import org.bitcoins.core.serializers.RawBitcoinSerializer
|
||||||
import org.bitcoins.core.wallet.fee.SatoshisPerKiloByte
|
import org.bitcoins.core.wallet.fee.SatoshisPerKiloByte
|
||||||
import org.bitcoins.core.p2p._
|
import org.bitcoins.core.p2p._
|
||||||
|
@ -11,14 +10,14 @@ sealed abstract class RawFeeFilterMessageSerializer
|
||||||
extends RawBitcoinSerializer[FeeFilterMessage] {
|
extends RawBitcoinSerializer[FeeFilterMessage] {
|
||||||
|
|
||||||
override def read(bytes: ByteVector): FeeFilterMessage = {
|
override def read(bytes: ByteVector): FeeFilterMessage = {
|
||||||
val i64 = Int64.fromBytes(bytes.take(8).reverse)
|
val satBytes = bytes.take(8).reverse
|
||||||
val sat = Satoshis(i64)
|
val sat = Satoshis(satBytes)
|
||||||
val satPerKb = SatoshisPerKiloByte(sat)
|
val satPerKb = SatoshisPerKiloByte(sat)
|
||||||
FeeFilterMessage(satPerKb)
|
FeeFilterMessage(satPerKb)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def write(feeFilterMessage: FeeFilterMessage): ByteVector = {
|
override def write(feeFilterMessage: FeeFilterMessage): ByteVector = {
|
||||||
feeFilterMessage.feeRate.currencyUnit.bytes.reverse
|
feeFilterMessage.feeRate.currencyUnit.satoshis.bytes.reverse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,17 +17,20 @@ sealed abstract class FeeUnit {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Meant to represent the different fee unit types for the bitcoin protocol
|
* Meant to represent the different fee unit types for the bitcoin protocol
|
||||||
* [[https://en.bitcoin.it/wiki/Weight_units]]
|
* @see [[https://en.bitcoin.it/wiki/Weight_units]]
|
||||||
*/
|
*/
|
||||||
sealed abstract class BitcoinFeeUnit extends FeeUnit
|
sealed abstract class BitcoinFeeUnit extends FeeUnit
|
||||||
|
|
||||||
case class SatoshisPerByte(currencyUnit: CurrencyUnit) extends BitcoinFeeUnit {
|
case class SatoshisPerByte(currencyUnit: CurrencyUnit) extends BitcoinFeeUnit {
|
||||||
|
|
||||||
def toSatPerKb: SatoshisPerKiloByte = {
|
def toSatPerKb: SatoshisPerKiloByte = {
|
||||||
SatoshisPerKiloByte(currencyUnit.satoshis * Satoshis(Int64(1000)))
|
SatoshisPerKiloByte(currencyUnit.satoshis * Satoshis(Int64(1000)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class SatoshisPerKiloByte(currencyUnit: CurrencyUnit) extends BitcoinFeeUnit {
|
case class SatoshisPerKiloByte(currencyUnit: CurrencyUnit)
|
||||||
|
extends BitcoinFeeUnit {
|
||||||
|
|
||||||
def toSatPerByte: SatoshisPerByte = {
|
def toSatPerByte: SatoshisPerByte = {
|
||||||
val conversionOpt = (currencyUnit.toBigDecimal * 0.001).toBigIntExact()
|
val conversionOpt = (currencyUnit.toBigDecimal * 0.001).toBigIntExact()
|
||||||
conversionOpt match {
|
conversionOpt match {
|
||||||
|
@ -36,11 +39,13 @@ case class SatoshisPerKiloByte(currencyUnit: CurrencyUnit) extends BitcoinFeeUni
|
||||||
SatoshisPerByte(sat)
|
SatoshisPerByte(sat)
|
||||||
|
|
||||||
case None =>
|
case None =>
|
||||||
throw new RuntimeException(s"Failed to convert sat/kb -> sat/byte for ${currencyUnit}")
|
throw new RuntimeException(
|
||||||
|
s"Failed to convert sat/kb -> sat/byte for ${currencyUnit}")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A 'virtual byte' (also known as virtual size) is a new weight measurement that
|
* A 'virtual byte' (also known as virtual size) is a new weight measurement that
|
||||||
* was created with segregated witness (BIP141). Now 1 'virtual byte'
|
* was created with segregated witness (BIP141). Now 1 'virtual byte'
|
||||||
|
|
|
@ -160,6 +160,22 @@ The command `sbt testQuick` can also be handy. It runs tests that either:
|
||||||
For more information on `testQuick`, see the offical
|
For more information on `testQuick`, see the offical
|
||||||
[sbt docs](https://www.scala-sbt.org/1.x/docs/Testing.html#testQuick).
|
[sbt docs](https://www.scala-sbt.org/1.x/docs/Testing.html#testQuick).
|
||||||
|
|
||||||
|
### Coverage
|
||||||
|
|
||||||
|
To produce a report that quantifies how much of our code is covered by tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sbt
|
||||||
|
> coverage
|
||||||
|
> coreTest/test
|
||||||
|
> core/coverageReport
|
||||||
|
```
|
||||||
|
|
||||||
|
This generates three different reports: Cobertura, XML and HTML formats.
|
||||||
|
See the output of your sbt shell to find the location of them.
|
||||||
|
Open up the HTML file in your browser. You'll now see code coverage
|
||||||
|
of all files in `core` project.
|
||||||
|
|
||||||
### CI
|
### CI
|
||||||
|
|
||||||
Bitcoin-S uses Travis to run tests and deploy library and website builds. Generally
|
Bitcoin-S uses Travis to run tests and deploy library and website builds. Generally
|
||||||
|
|
|
@ -9,12 +9,34 @@ import org.bitcoins.core.currency.{
|
||||||
import org.bitcoins.core.number.Int64
|
import org.bitcoins.core.number.Int64
|
||||||
import org.bitcoins.core.protocol.ln.currency._
|
import org.bitcoins.core.protocol.ln.currency._
|
||||||
import org.scalacheck.Gen
|
import org.scalacheck.Gen
|
||||||
|
import org.bitcoins.core.wallet.fee.FeeUnit
|
||||||
|
import org.bitcoins.core.wallet.fee.SatoshisPerByte
|
||||||
|
import org.bitcoins.core.wallet.fee.SatoshisPerKiloByte
|
||||||
|
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by chris on 6/23/16.
|
|
||||||
*/
|
|
||||||
trait CurrencyUnitGenerator {
|
trait CurrencyUnitGenerator {
|
||||||
|
|
||||||
|
def satsPerByte: Gen[SatoshisPerByte] = {
|
||||||
|
for {
|
||||||
|
curr <- positiveRealistic
|
||||||
|
} yield SatoshisPerByte(curr)
|
||||||
|
}
|
||||||
|
|
||||||
|
def satsPerKiloByte: Gen[SatoshisPerKiloByte] = {
|
||||||
|
for {
|
||||||
|
curr <- positiveRealistic
|
||||||
|
} yield SatoshisPerKiloByte(curr)
|
||||||
|
}
|
||||||
|
|
||||||
|
def satsPerVirtualByte: Gen[SatoshisPerVirtualByte] = {
|
||||||
|
for {
|
||||||
|
curr <- positiveRealistic
|
||||||
|
} yield SatoshisPerVirtualByte(curr)
|
||||||
|
}
|
||||||
|
|
||||||
|
def feeUnit: Gen[FeeUnit] =
|
||||||
|
Gen.oneOf(satsPerByte, satsPerKiloByte, satsPerVirtualByte)
|
||||||
|
|
||||||
def satoshis: Gen[Satoshis] =
|
def satoshis: Gen[Satoshis] =
|
||||||
for {
|
for {
|
||||||
int64 <- NumberGenerator.int64s
|
int64 <- NumberGenerator.int64s
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.bitcoins.testkit.gen
|
package org.bitcoins.testkit.core.gen.p2p
|
||||||
|
|
||||||
import java.net.{InetAddress, InetSocketAddress}
|
import java.net.{InetAddress, InetSocketAddress}
|
||||||
|
|
||||||
|
@ -7,17 +7,50 @@ import org.bitcoins.core.p2p.ProtocolVersion
|
||||||
import org.bitcoins.core.protocol.CompactSizeUInt
|
import org.bitcoins.core.protocol.CompactSizeUInt
|
||||||
import org.bitcoins.core.p2p._
|
import org.bitcoins.core.p2p._
|
||||||
import org.bitcoins.core.p2p._
|
import org.bitcoins.core.p2p._
|
||||||
import org.bitcoins.testkit.core.gen.{BloomFilterGenerator, CryptoGenerators, NumberGenerator, StringGenerators}
|
import org.bitcoins.testkit.core.gen.{
|
||||||
|
BloomFilterGenerator,
|
||||||
|
CryptoGenerators,
|
||||||
|
NumberGenerator,
|
||||||
|
StringGenerators
|
||||||
|
}
|
||||||
import org.scalacheck.Gen
|
import org.scalacheck.Gen
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
|
import org.bitcoins.testkit.core.gen.CurrencyUnitGenerator
|
||||||
|
import org.bitcoins.core.wallet.fee.SatoshisPerByte
|
||||||
|
import org.bitcoins.core.wallet.fee.SatoshisPerKiloByte
|
||||||
|
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
||||||
|
|
||||||
trait ControlMessageGenerator {
|
object ControlMessageGenerator {
|
||||||
|
|
||||||
|
/** Generates a valid P2P control message */
|
||||||
|
def controlMessage: Gen[ControlPayload] = Gen.oneOf(
|
||||||
|
addrMessage,
|
||||||
|
filterAddMessage,
|
||||||
|
filterLoadMessage,
|
||||||
|
feeFilterMessage,
|
||||||
|
pingMessage,
|
||||||
|
pongMessage,
|
||||||
|
rejectMessage,
|
||||||
|
versionMessage
|
||||||
|
)
|
||||||
|
|
||||||
|
def feeFilterMessage: Gen[FeeFilterMessage] = {
|
||||||
|
for {
|
||||||
|
fee <- CurrencyUnitGenerator.feeUnit.suchThat(
|
||||||
|
!_.isInstanceOf[SatoshisPerVirtualByte])
|
||||||
|
} yield
|
||||||
|
fee match {
|
||||||
|
case fee: SatoshisPerByte => FeeFilterMessage(fee)
|
||||||
|
case fee: SatoshisPerKiloByte => FeeFilterMessage(fee)
|
||||||
|
case SatoshisPerVirtualByte(_) =>
|
||||||
|
throw new RuntimeException(s"We cannot end up here")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a random [[VersionMessage]]
|
* Generates a random [[VersionMessage]]
|
||||||
* [[https://bitcoin.org/en/developer-reference#version]]
|
|
||||||
*
|
*
|
||||||
* @return
|
* @see [[https://bitcoin.org/en/developer-reference#version]]
|
||||||
*/
|
*/
|
||||||
def versionMessage: Gen[VersionMessage] =
|
def versionMessage: Gen[VersionMessage] =
|
||||||
for {
|
for {
|
||||||
|
@ -53,9 +86,8 @@ trait ControlMessageGenerator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a [[PingMessage]]
|
* Generates a [[PingMessage]]
|
||||||
* [[https://bitcoin.org/en/developer-reference#ping]]
|
|
||||||
*
|
*
|
||||||
* @return
|
* @see [[https://bitcoin.org/en/developer-reference#ping]]
|
||||||
*/
|
*/
|
||||||
def pingMessage: Gen[PingMessage] =
|
def pingMessage: Gen[PingMessage] =
|
||||||
for {
|
for {
|
||||||
|
@ -64,20 +96,24 @@ trait ControlMessageGenerator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a [[PongMessage]]
|
* Generates a [[PongMessage]]
|
||||||
* [[https://bitcoin.org/en/developer-reference#pong]]
|
|
||||||
*
|
*
|
||||||
* @return
|
* @see [[https://bitcoin.org/en/developer-reference#pong]]
|
||||||
*/
|
*/
|
||||||
def pongMessage: Gen[PongMessage] =
|
def pongMessage: Gen[PongMessage] =
|
||||||
for {
|
for {
|
||||||
uInt64 <- NumberGenerator.uInt64s
|
uInt64 <- NumberGenerator.uInt64s
|
||||||
} yield PongMessage(uInt64)
|
} yield PongMessage(uInt64)
|
||||||
|
|
||||||
|
def addrMessage: Gen[AddrMessage] = {
|
||||||
|
for {
|
||||||
|
addresses <- Gen.listOf(P2PGenerator.networkIpAddress)
|
||||||
|
} yield AddrMessage(addresses)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a random [[ProtocolVersion]]
|
* Generates a random [[ProtocolVersion]]
|
||||||
* [[https://bitcoin.org/en/developer-reference#protocol-versions]]
|
|
||||||
*
|
*
|
||||||
* @return
|
* @see [[https://bitcoin.org/en/developer-reference#protocol-versions]]
|
||||||
*/
|
*/
|
||||||
def protocolVersion: Gen[ProtocolVersion] =
|
def protocolVersion: Gen[ProtocolVersion] =
|
||||||
for {
|
for {
|
||||||
|
@ -86,9 +122,8 @@ trait ControlMessageGenerator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a [[ServiceIdentifier]]
|
* Generates a [[ServiceIdentifier]]
|
||||||
* [[https://bitcoin.org/en/developer-reference#version]]
|
|
||||||
*
|
*
|
||||||
* @return
|
* @see [[https://bitcoin.org/en/developer-reference#version]]
|
||||||
*/
|
*/
|
||||||
def serviceIdentifier: Gen[ServiceIdentifier] =
|
def serviceIdentifier: Gen[ServiceIdentifier] =
|
||||||
for {
|
for {
|
||||||
|
@ -110,9 +145,8 @@ trait ControlMessageGenerator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a [[FilterLoadMessage]]
|
* Creates a [[FilterLoadMessage]]
|
||||||
* [[https://bitcoin.org/en/developer-reference#filterload]]
|
|
||||||
*
|
*
|
||||||
* @return
|
* @see [[https://bitcoin.org/en/developer-reference#filterload]]
|
||||||
*/
|
*/
|
||||||
def filterLoadMessage: Gen[FilterLoadMessage] =
|
def filterLoadMessage: Gen[FilterLoadMessage] =
|
||||||
for {
|
for {
|
||||||
|
@ -125,9 +159,8 @@ trait ControlMessageGenerator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a [[FilterAddMessage]]
|
* Creates a [[FilterAddMessage]]
|
||||||
* [[https://bitcoin.org/en/developer-reference#filteradd]]
|
|
||||||
*
|
*
|
||||||
* @return
|
* @see [[https://bitcoin.org/en/developer-reference#filteradd]]
|
||||||
*/
|
*/
|
||||||
def filterAddMessage: Gen[FilterAddMessage] =
|
def filterAddMessage: Gen[FilterAddMessage] =
|
||||||
for {
|
for {
|
||||||
|
@ -137,9 +170,8 @@ trait ControlMessageGenerator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a [[RejectMessage]]
|
* Creates a [[RejectMessage]]
|
||||||
* [[https://bitcoin.org/en/developer-reference#reject]]
|
|
||||||
*
|
*
|
||||||
* @return
|
* @see [[https://bitcoin.org/en/developer-reference#reject]]
|
||||||
*/
|
*/
|
||||||
def rejectMessage: Gen[RejectMessage] =
|
def rejectMessage: Gen[RejectMessage] =
|
||||||
for {
|
for {
|
||||||
|
@ -149,5 +181,3 @@ trait ControlMessageGenerator {
|
||||||
extra <- CryptoGenerators.doubleSha256Digest
|
extra <- CryptoGenerators.doubleSha256Digest
|
||||||
} yield RejectMessage(message, code, reason, extra.bytes)
|
} yield RejectMessage(message, code, reason, extra.bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
object ControlMessageGenerator extends ControlMessageGenerator
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.bitcoins.testkit.gen
|
package org.bitcoins.testkit.core.gen.p2p
|
||||||
|
|
||||||
import org.bitcoins.core.number.UInt32
|
import org.bitcoins.core.number.UInt32
|
||||||
import org.bitcoins.core.p2p._
|
import org.bitcoins.core.p2p._
|
||||||
|
@ -11,13 +11,44 @@ import org.bitcoins.testkit.core.gen.{
|
||||||
import org.scalacheck.Gen
|
import org.scalacheck.Gen
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsible for generating random [[DataMessage]]
|
* Responsible for generating random data message
|
||||||
|
|
||||||
* @see [[https://bitcoin.org/en/developer-reference#data-messages]]
|
* @see [[https://bitcoin.org/en/developer-reference#data-messages]]
|
||||||
*/
|
*/
|
||||||
trait DataMessageGenerator {
|
trait DataMessageGenerator {
|
||||||
|
|
||||||
|
/** Generates a valid P2P data message */
|
||||||
|
def dataMessage: Gen[DataPayload] = Gen.oneOf(
|
||||||
|
blockMessage,
|
||||||
|
getBlocksMessage,
|
||||||
|
getDataMessages,
|
||||||
|
getHeaderMessages,
|
||||||
|
headersMessage,
|
||||||
|
inventoryMessages,
|
||||||
|
merkleBlockMessage,
|
||||||
|
notFoundMessage,
|
||||||
|
transactionMessage
|
||||||
|
)
|
||||||
|
|
||||||
|
def blockMessage: Gen[BlockMessage] = {
|
||||||
|
for {
|
||||||
|
block <- BlockchainElementsGenerator.block
|
||||||
|
} yield BlockMessage(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
def getBlocksMessage: Gen[GetBlocksMessage] = {
|
||||||
|
for {
|
||||||
|
protocol <- ControlMessageGenerator.protocolVersion
|
||||||
|
hashes <- Gen
|
||||||
|
.nonEmptyListOf(CryptoGenerators.doubleSha256Digest)
|
||||||
|
.suchThat(_.length <= 500)
|
||||||
|
stopHash <- CryptoGenerators.doubleSha256Digest
|
||||||
|
} yield GetBlocksMessage(protocol, hashes, stopHash)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a random [[GetHeadersMessage]]
|
* Generates a random [[GetHeadersMessage]]
|
||||||
|
*
|
||||||
* @see [[https://bitcoin.org/en/developer-reference#getheaders]]
|
* @see [[https://bitcoin.org/en/developer-reference#getheaders]]
|
||||||
*/
|
*/
|
||||||
def getHeaderMessages: Gen[GetHeadersMessage] =
|
def getHeaderMessages: Gen[GetHeadersMessage] =
|
||||||
|
@ -28,6 +59,15 @@ trait DataMessageGenerator {
|
||||||
hashStop <- CryptoGenerators.doubleSha256Digest
|
hashStop <- CryptoGenerators.doubleSha256Digest
|
||||||
} yield GetHeadersMessage(version, hashes, hashStop)
|
} yield GetHeadersMessage(version, hashes, hashStop)
|
||||||
|
|
||||||
|
/** Generates a `getheaders` message with the default protocol version */
|
||||||
|
def getHeaderDefaultProtocolMessage: Gen[GetHeadersMessage] = {
|
||||||
|
for {
|
||||||
|
numHashes <- Gen.choose(0, 2000)
|
||||||
|
hashes <- CryptoGenerators.doubleSha256DigestSeq(numHashes)
|
||||||
|
hashStop <- CryptoGenerators.doubleSha256Digest
|
||||||
|
} yield GetHeadersMessage(hashes, hashStop)
|
||||||
|
}
|
||||||
|
|
||||||
def headersMessage: Gen[HeadersMessage] =
|
def headersMessage: Gen[HeadersMessage] =
|
||||||
for {
|
for {
|
||||||
randomNum <- Gen.choose(1, 10)
|
randomNum <- Gen.choose(1, 10)
|
||||||
|
@ -39,7 +79,8 @@ trait DataMessageGenerator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a random [[TypeIdentifier]]
|
* Generates a random [[TypeIdentifier]]
|
||||||
* [[https://bitcoin.org/en/developer-reference#data-messages]]
|
*
|
||||||
|
* @see [[https://bitcoin.org/en/developer-reference#data-messages]]
|
||||||
*/
|
*/
|
||||||
def typeIdentifier: Gen[TypeIdentifier] =
|
def typeIdentifier: Gen[TypeIdentifier] =
|
||||||
for {
|
for {
|
||||||
|
@ -66,6 +107,12 @@ trait DataMessageGenerator {
|
||||||
inventories <- Gen.listOfN(numInventories, inventory)
|
inventories <- Gen.listOfN(numInventories, inventory)
|
||||||
} yield InventoryMessage(inventories)
|
} yield InventoryMessage(inventories)
|
||||||
|
|
||||||
|
def notFoundMessage: Gen[NotFoundMessage] = {
|
||||||
|
for {
|
||||||
|
inventories <- Gen.nonEmptyListOf(inventory)
|
||||||
|
} yield NotFoundMessage(inventories)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a random [[GetDataMessage]]
|
* Generate a random [[GetDataMessage]]
|
||||||
* @see [[https://bitcoin.org/en/developer-reference#getdata]]
|
* @see [[https://bitcoin.org/en/developer-reference#getdata]]
|
|
@ -0,0 +1,51 @@
|
||||||
|
package org.bitcoins.testkit.core.gen.p2p
|
||||||
|
import org.scalacheck.Gen
|
||||||
|
import org.bitcoins.core.p2p.NetworkIpAddress
|
||||||
|
import org.bitcoins.testkit.core.gen.NumberGenerator
|
||||||
|
import org.bitcoins.core.p2p.ServiceIdentifier
|
||||||
|
import org.bitcoins.core.p2p.NodeNetwork
|
||||||
|
import org.bitcoins.core.p2p.UnnamedService
|
||||||
|
import java.net.InetAddress
|
||||||
|
import org.bitcoins.core.p2p.NetworkPayload
|
||||||
|
|
||||||
|
object P2PGenerator {
|
||||||
|
|
||||||
|
/** Generates a valid P2P network message */
|
||||||
|
def message: Gen[NetworkPayload] =
|
||||||
|
Gen.oneOf(ControlMessageGenerator.controlMessage,
|
||||||
|
DataMessageGenerator.dataMessage)
|
||||||
|
|
||||||
|
def inetAddress: Gen[InetAddress] = {
|
||||||
|
def ipRangeNum = Gen.choose(0, 255)
|
||||||
|
for {
|
||||||
|
first <- ipRangeNum
|
||||||
|
second <- ipRangeNum
|
||||||
|
third <- ipRangeNum
|
||||||
|
fourth <- ipRangeNum
|
||||||
|
} 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def networkIpAddress: Gen[NetworkIpAddress] = {
|
||||||
|
for {
|
||||||
|
time <- NumberGenerator.uInt32s
|
||||||
|
services <- serviceIdentifier
|
||||||
|
address <- inetAddress
|
||||||
|
port <- Gen.choose(1025, 64000)
|
||||||
|
} yield NetworkIpAddress(time, services, address, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
def serviceIdentifier: Gen[ServiceIdentifier] = {
|
||||||
|
for {
|
||||||
|
unknown <- NumberGenerator.uInt64.suchThat(u64 =>
|
||||||
|
u64 != NodeNetwork.num || u64 != UnnamedService.num)
|
||||||
|
service <- Gen.oneOf(NodeNetwork,
|
||||||
|
UnnamedService,
|
||||||
|
ServiceIdentifier(unknown))
|
||||||
|
} yield service
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,13 +14,14 @@ abstract class BitcoinSUnitTest
|
||||||
protected lazy val logger: Logger = LoggerFactory.getLogger(getClass)
|
protected lazy val logger: Logger = LoggerFactory.getLogger(getClass)
|
||||||
|
|
||||||
/** The configuration for property based tests in our testing suite
|
/** The configuration for property based tests in our testing suite
|
||||||
* See: http://www.scalatest.org/user_guide/writing_scalacheck_style_properties
|
* @see http://www.scalatest.org/user_guide/writing_scalacheck_style_properties
|
||||||
*/
|
*/
|
||||||
implicit override val generatorDrivenConfig: PropertyCheckConfiguration = {
|
implicit override val generatorDrivenConfig: PropertyCheckConfiguration = {
|
||||||
generatorDriveConfigOldCode
|
generatorDriveConfigOldCode
|
||||||
}
|
}
|
||||||
|
|
||||||
private def buildConfig(executions: Int): PropertyCheckConfiguration = {
|
/** Sets the generator driven tests to perform the given amount of execs */
|
||||||
|
def customGenDrivenConfig(executions: Int): PropertyCheckConfiguration = {
|
||||||
PropertyCheckConfiguration(
|
PropertyCheckConfiguration(
|
||||||
minSuccessful = PosInt.from(executions).get,
|
minSuccessful = PosInt.from(executions).get,
|
||||||
minSize = PosInt.from(executions).get,
|
minSize = PosInt.from(executions).get,
|
||||||
|
@ -33,7 +34,7 @@ abstract class BitcoinSUnitTest
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
def generatorDriveConfigOldCode: PropertyCheckConfiguration = {
|
def generatorDriveConfigOldCode: PropertyCheckConfiguration = {
|
||||||
buildConfig(BitcoinSUnitTest.OLD_CODE_EXECUTIONS)
|
customGenDrivenConfig(BitcoinSUnitTest.OLD_CODE_EXECUTIONS)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Property based tests that are new have a higher chance of failing
|
/** Property based tests that are new have a higher chance of failing
|
||||||
|
@ -41,7 +42,7 @@ abstract class BitcoinSUnitTest
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
def generatorDrivenConfigNewCode: PropertyCheckConfiguration = {
|
def generatorDrivenConfigNewCode: PropertyCheckConfiguration = {
|
||||||
buildConfig(BitcoinSUnitTest.NEW_CODE_EXECUTIONS)
|
customGenDrivenConfig(BitcoinSUnitTest.NEW_CODE_EXECUTIONS)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue