mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-03 10:46:42 +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)
|
||||
|
||||
## 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.
|
||||
See our [contributing guide](https://bitcoin-s.org/docs/contributing) for information
|
||||
on how to benchmark, test and develop your code.
|
||||
|
|
|
@ -2,14 +2,12 @@ package org.bitcoins.core.crypto
|
|||
|
||||
import org.bitcoinj.core.Sha256Hash
|
||||
import org.bitcoins.testkit.core.gen.CryptoGenerators
|
||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||
import org.scalatest.prop.PropertyChecks
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
import scodec.bits.ByteVector
|
||||
import scodec.bits._
|
||||
|
||||
/**
|
||||
* Created by chris on 2/29/16.
|
||||
*/
|
||||
class ECPublicKeyTest extends FlatSpec with MustMatchers {
|
||||
class ECPublicKeyTest extends BitcoinSUnitTest {
|
||||
|
||||
"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)
|
||||
}
|
||||
|
||||
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 {
|
||||
PropertyChecks.forAll(CryptoGenerators.publicKey) { pubKey =>
|
||||
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
|
||||
|
||||
import org.bitcoins.testkit.gen.ControlMessageGenerator
|
||||
import org.bitcoins.testkit.core.gen.p2p.ControlMessageGenerator
|
||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||
|
||||
/**
|
||||
* Created by chris on 8/26/16.
|
||||
*/
|
||||
class FilterAddMessageSpec extends BitcoinSUnitTest {
|
||||
class FilterAddMessageTest extends BitcoinSUnitTest {
|
||||
|
||||
it must "have serialization symmetry" in {
|
||||
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
|
||||
|
||||
import org.bitcoins.testkit.gen.DataMessageGenerator
|
||||
import org.bitcoins.testkit.core.gen.p2p.DataMessageGenerator
|
||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||
|
||||
class HeadersMessageSpec extends BitcoinSUnitTest {
|
||||
class HeadersMessageTest extends BitcoinSUnitTest {
|
||||
|
||||
it must "have serialization symmetry" in {
|
||||
forAll(DataMessageGenerator.headersMessage) { headersMsg =>
|
|
@ -1,9 +1,9 @@
|
|||
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
|
||||
|
||||
class InventoryMessageSpec extends BitcoinSUnitTest {
|
||||
class InventoryMessageTest extends BitcoinSUnitTest {
|
||||
|
||||
it must " have serialization symmetry" in {
|
||||
forAll(DataMessageGenerator.inventoryMessages) { invMessage =>
|
|
@ -1,9 +1,9 @@
|
|||
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
|
||||
|
||||
class InventorySpec extends BitcoinSUnitTest {
|
||||
class InventoryTest extends BitcoinSUnitTest {
|
||||
|
||||
it must "have serialization symmetry" in {
|
||||
forAll(DataMessageGenerator.inventory) { inventory =>
|
|
@ -1,9 +1,9 @@
|
|||
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
|
||||
|
||||
class MerkleBlockMessageSpec extends BitcoinSUnitTest {
|
||||
class MerkleBlockMessageTest extends BitcoinSUnitTest {
|
||||
it must "have serialization symmetry" in {
|
||||
forAll(DataMessageGenerator.merkleBlockMessage) { 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.testkit.node.NodeTestUtil
|
||||
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 {
|
||||
|
||||
"MessageHeader" must "must create a message header for a message" in {
|
||||
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.payloadSize must be(
|
||||
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 {
|
||||
val messageHeader = NetworkHeader(TestNet3, VerAckMessage)
|
||||
messageHeader.network must be(TestNet3.magicBytes)
|
||||
messageHeader.network must be(TestNet3)
|
||||
messageHeader.commandName must be(VerAckMessage.commandName)
|
||||
messageHeader.payloadSize must be(UInt32.zero)
|
||||
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.util.BitcoinSUnitTest
|
||||
import org.bitcoins.testkit.core.gen.p2p.P2PGenerator
|
||||
|
||||
class NetworkPayloadTest extends BitcoinSUnitTest {
|
||||
|
||||
|
@ -14,4 +15,14 @@ class NetworkPayloadTest extends BitcoinSUnitTest {
|
|||
payload.isInstanceOf[VersionMessage] must be(true)
|
||||
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
|
||||
|
||||
import org.bitcoins.testkit.gen.ControlMessageGenerator
|
||||
import org.bitcoins.testkit.core.gen.p2p.ControlMessageGenerator
|
||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||
|
||||
class PingMessageSpec extends BitcoinSUnitTest {
|
||||
class PingMessageTest extends BitcoinSUnitTest {
|
||||
|
||||
it must "have symmetry serialization" in {
|
||||
forAll(ControlMessageGenerator.pingMessage) { pingMessage =>
|
|
@ -1,9 +1,9 @@
|
|||
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
|
||||
|
||||
class PongMessageSpec extends BitcoinSUnitTest {
|
||||
class PongMessageTest extends BitcoinSUnitTest {
|
||||
|
||||
it must "have serialization symmetry" in {
|
||||
forAll(ControlMessageGenerator.pongMessage) { pongMsg =>
|
|
@ -1,9 +1,9 @@
|
|||
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
|
||||
|
||||
class RejectMessageSpec extends BitcoinSUnitTest {
|
||||
class RejectMessageTest extends BitcoinSUnitTest {
|
||||
|
||||
it must "have serialization symmetry" in {
|
||||
forAll(ControlMessageGenerator.rejectMessage) { rejectMsg =>
|
|
@ -1,9 +1,9 @@
|
|||
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
|
||||
|
||||
class TransactionMessageSpec extends BitcoinSUnitTest {
|
||||
class TransactionMessageTest extends BitcoinSUnitTest {
|
||||
|
||||
it must "have serialization symmetry" in {
|
||||
forAll(DataMessageGenerator.transactionMessage) { txMsg =>
|
|
@ -4,11 +4,18 @@ import java.net.InetAddress
|
|||
|
||||
import org.bitcoins.core.config.MainNet
|
||||
import org.bitcoins.core.number.{Int32, UInt64}
|
||||
import org.bitcoins.testkit.core.gen.p2p.ControlMessageGenerator
|
||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||
import org.joda.time.DateTime
|
||||
|
||||
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 {
|
||||
val versionMessage = VersionMessage(MainNet, InetAddress.getLocalHost)
|
||||
versionMessage.addressReceiveServices must be(UnnamedService)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package org.bitcoins.core.serializers.p2p.headers
|
||||
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.util.BitcoinSUtil
|
||||
import org.bitcoins.core.p2p.NetworkPayload
|
||||
import org.bitcoins.testkit.node.NodeTestUtil
|
||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||
import scodec.bits._
|
||||
|
||||
class RawNetworkHeaderSerializerTest extends BitcoinSUnitTest {
|
||||
val hex = "f9beb4d976657261636b000000000000000000005df6e0e2"
|
||||
|
@ -14,13 +14,13 @@ class RawNetworkHeaderSerializerTest extends BitcoinSUnitTest {
|
|||
|
||||
val messageHeader = RawNetworkHeaderSerializer.read(hex)
|
||||
//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.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 {
|
||||
|
@ -31,11 +31,11 @@ class RawNetworkHeaderSerializerTest extends BitcoinSUnitTest {
|
|||
it must "read a network header from a node on the network" in {
|
||||
val hex = NodeTestUtil.rawNetworkMessage.take(48)
|
||||
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 must be(NetworkPayload.versionCommandName)
|
||||
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.p2p.NodeNetwork
|
||||
|
|
|
@ -162,6 +162,13 @@ object Networks {
|
|||
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(
|
||||
MainNet.p2shNetworkByte -> MainNet,
|
||||
MainNet.p2pkhNetworkByte -> MainNet,
|
||||
|
|
|
@ -16,10 +16,10 @@ sealed trait NetworkHeader extends NetworkElement {
|
|||
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.
|
||||
*/
|
||||
def network: ByteVector
|
||||
def network: NetworkParameters
|
||||
|
||||
/**
|
||||
* 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] {
|
||||
|
||||
private case class NetworkHeaderImpl(
|
||||
network: ByteVector,
|
||||
network: NetworkParameters,
|
||||
commandName: String,
|
||||
payloadSize: UInt32,
|
||||
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
|
||||
*/
|
||||
def apply(
|
||||
network: ByteVector,
|
||||
network: NetworkParameters,
|
||||
commandName: String,
|
||||
payloadSize: UInt32,
|
||||
checksum: ByteVector): NetworkHeader = {
|
||||
|
@ -81,7 +81,7 @@ object NetworkHeader extends Factory[NetworkHeader] {
|
|||
network: NetworkParameters,
|
||||
payload: NetworkPayload): NetworkHeader = {
|
||||
val checksum = CryptoUtil.doubleSHA256(payload.bytes)
|
||||
NetworkHeader(network.magicBytes,
|
||||
NetworkHeader(network,
|
||||
payload.commandName,
|
||||
UInt32(payload.bytes.size),
|
||||
checksum.bytes.take(4))
|
||||
|
|
|
@ -4,7 +4,7 @@ import java.net.{InetAddress, InetSocketAddress}
|
|||
|
||||
import org.bitcoins.core.number.UInt32
|
||||
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 scodec.bits._
|
||||
|
||||
|
|
|
@ -35,13 +35,15 @@ sealed trait NetworkPayload extends NetworkElement {
|
|||
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
|
@ -252,8 +254,8 @@ object GetHeadersMessage extends Factory[GetHeadersMessage] {
|
|||
GetHeadersMessage(hashes, hashStop)
|
||||
}
|
||||
|
||||
def apply(hashes: DoubleSha256Digest): GetHeadersMessage = {
|
||||
GetHeadersMessage(Vector(hashes))
|
||||
def apply(hash: DoubleSha256Digest): GetHeadersMessage = {
|
||||
GetHeadersMessage(Vector(hash))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -357,7 +359,8 @@ object InventoryMessage extends Factory[InventoryMessage] {
|
|||
* as valid but which have not yet appeared in a block.
|
||||
* 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.
|
||||
* [[https://bitcoin.org/en/developer-reference#mempool]]
|
||||
*
|
||||
* @see [[https://bitcoin.org/en/developer-reference#mempool]]
|
||||
*/
|
||||
case object MemPoolMessage extends DataPayload {
|
||||
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
|
||||
* 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.)
|
||||
*
|
||||
* @see [[https://bitcoin.org/en/developer-reference#notfound]]
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @see https://bitcoin.org/en/developer-reference#notfound
|
||||
*/
|
||||
object NotFoundMessage extends Factory[NotFoundMessage] {
|
||||
|
@ -425,6 +430,11 @@ object NotFoundMessage extends Factory[NotFoundMessage] {
|
|||
def fromBytes(bytes: ByteVector): NotFoundMessage =
|
||||
RawNotFoundMessageSerializer.read(bytes)
|
||||
|
||||
def apply(inventories: Seq[Inventory]): NotFoundMessage = {
|
||||
val count = CompactSizeUInt(UInt64(inventories.length))
|
||||
apply(count, inventories)
|
||||
}
|
||||
|
||||
def apply(
|
||||
inventoryCount: CompactSizeUInt,
|
||||
inventories: Seq[Inventory]): NotFoundMessage = {
|
||||
|
@ -499,6 +509,11 @@ object AddrMessage extends Factory[AddrMessage] {
|
|||
def fromBytes(bytes: ByteVector): AddrMessage =
|
||||
RawAddrMessageSerializer.read(bytes)
|
||||
|
||||
def apply(addresses: Seq[NetworkIpAddress]): AddrMessage = {
|
||||
val count = CompactSizeUInt(UInt64(addresses.length))
|
||||
apply(count, addresses)
|
||||
}
|
||||
|
||||
def apply(
|
||||
ipCount: CompactSizeUInt,
|
||||
addresses: Seq[NetworkIpAddress]): AddrMessage =
|
||||
|
@ -1086,13 +1101,14 @@ object NetworkPayload {
|
|||
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
|
||||
* required in [[NetworkHeader]]
|
||||
* [[https://bitcoin.org/en/developer-reference#message-headers]]
|
||||
* required by the network header specification.
|
||||
*
|
||||
* @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(_) },
|
||||
getBlocksCommandName -> { RawGetBlocksMessageSerializer.read(_) },
|
||||
getHeadersCommandName -> { RawGetHeadersMessageSerializer.read(_) },
|
||||
|
@ -1117,9 +1133,7 @@ object NetworkPayload {
|
|||
},
|
||||
pingCommandName -> { RawPingMessageSerializer.read(_) },
|
||||
pongCommandName -> { RawPongMessageSerializer.read(_) },
|
||||
rejectCommandName -> { _: ByteVector =>
|
||||
???
|
||||
},
|
||||
rejectCommandName -> { RawRejectMessageSerializer.read(_) },
|
||||
sendHeadersCommandName -> { _: ByteVector =>
|
||||
SendHeadersMessage
|
||||
},
|
||||
|
@ -1129,6 +1143,9 @@ object NetworkPayload {
|
|||
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]]
|
||||
* to determine what type of [[NetworkPayload]] this is
|
||||
|
@ -1139,7 +1156,7 @@ object NetworkPayload {
|
|||
networkHeader: NetworkHeader,
|
||||
payloadBytes: ByteVector): NetworkPayload = {
|
||||
//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)
|
||||
deserializer(payloadBytes)
|
||||
}
|
||||
|
|
|
@ -30,7 +30,8 @@ object ProtocolVersion extends Factory[ProtocolVersion] {
|
|||
ProtocolVersion60002,
|
||||
ProtocolVersion70001,
|
||||
ProtocolVersion70002,
|
||||
ProtocolVersion70012
|
||||
ProtocolVersion70012,
|
||||
ProtocolVersion70013
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
|
@ -21,7 +21,7 @@ trait RawNetworkIpAddressSerializer
|
|||
val services = ServiceIdentifier(bytes.slice(4, 12))
|
||||
val ipBytes = bytes.slice(12, 28)
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -29,8 +29,9 @@ trait RawNetworkIpAddressSerializer
|
|||
val time = networkIpAddress.time.bytes.reverse
|
||||
val services = networkIpAddress.services.bytes
|
||||
val ipAddress = NetworkIpAddress.writeAddress(networkIpAddress.address)
|
||||
//uint16s are only 4 hex characters
|
||||
val port = ByteVector.fromShort(networkIpAddress.port.toShort)
|
||||
// uint16s are only 4 hex characters
|
||||
// cannot do fromShort,
|
||||
val port = ByteVector.fromInt(networkIpAddress.port, size = 2)
|
||||
time ++ services ++ ipAddress ++ port
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package org.bitcoins.core.serializers.p2p.headers
|
||||
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.config._
|
||||
import org.bitcoins.core.p2p.NetworkHeader
|
||||
import org.bitcoins.core.serializers.RawBitcoinSerializer
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
|
@ -20,7 +21,7 @@ trait RawNetworkHeaderSerializer
|
|||
* @return the native object for the MessageHeader
|
||||
*/
|
||||
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
|
||||
val commandName = bytes.slice(4, 16).toArray.map(_.toChar).mkString.trim
|
||||
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
|
||||
val commandName = ByteVector(commandNameNoPadding).padRight(12)
|
||||
val checksum = messageHeader.checksum
|
||||
network ++
|
||||
network.magicBytes ++
|
||||
commandName ++
|
||||
messageHeader.payloadSize.bytes.reverse ++
|
||||
checksum
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.bitcoins.core.serializers.p2p.messages
|
|||
|
||||
import org.bitcoins.core.protocol.CompactSizeUInt
|
||||
import org.bitcoins.core.serializers.{RawBitcoinSerializer, RawSerializerHelper}
|
||||
import org.bitcoins.core.serializers.p2p._
|
||||
import org.bitcoins.core.p2p._
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.bitcoins.core.serializers.p2p.messages
|
||||
|
||||
import org.bitcoins.core.currency.Satoshis
|
||||
import org.bitcoins.core.number.Int64
|
||||
import org.bitcoins.core.serializers.RawBitcoinSerializer
|
||||
import org.bitcoins.core.wallet.fee.SatoshisPerKiloByte
|
||||
import org.bitcoins.core.p2p._
|
||||
|
@ -11,14 +10,14 @@ sealed abstract class RawFeeFilterMessageSerializer
|
|||
extends RawBitcoinSerializer[FeeFilterMessage] {
|
||||
|
||||
override def read(bytes: ByteVector): FeeFilterMessage = {
|
||||
val i64 = Int64.fromBytes(bytes.take(8).reverse)
|
||||
val sat = Satoshis(i64)
|
||||
val satBytes = bytes.take(8).reverse
|
||||
val sat = Satoshis(satBytes)
|
||||
val satPerKb = SatoshisPerKiloByte(sat)
|
||||
FeeFilterMessage(satPerKb)
|
||||
}
|
||||
|
||||
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
|
||||
* [[https://en.bitcoin.it/wiki/Weight_units]]
|
||||
* @see [[https://en.bitcoin.it/wiki/Weight_units]]
|
||||
*/
|
||||
sealed abstract class BitcoinFeeUnit extends FeeUnit
|
||||
|
||||
case class SatoshisPerByte(currencyUnit: CurrencyUnit) extends BitcoinFeeUnit {
|
||||
|
||||
def toSatPerKb: SatoshisPerKiloByte = {
|
||||
SatoshisPerKiloByte(currencyUnit.satoshis * Satoshis(Int64(1000)))
|
||||
}
|
||||
}
|
||||
|
||||
case class SatoshisPerKiloByte(currencyUnit: CurrencyUnit) extends BitcoinFeeUnit {
|
||||
case class SatoshisPerKiloByte(currencyUnit: CurrencyUnit)
|
||||
extends BitcoinFeeUnit {
|
||||
|
||||
def toSatPerByte: SatoshisPerByte = {
|
||||
val conversionOpt = (currencyUnit.toBigDecimal * 0.001).toBigIntExact()
|
||||
conversionOpt match {
|
||||
|
@ -36,11 +39,13 @@ case class SatoshisPerKiloByte(currencyUnit: CurrencyUnit) extends BitcoinFeeUni
|
|||
SatoshisPerByte(sat)
|
||||
|
||||
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
|
||||
* 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
|
||||
[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
|
||||
|
||||
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.protocol.ln.currency._
|
||||
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 {
|
||||
|
||||
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] =
|
||||
for {
|
||||
int64 <- NumberGenerator.int64s
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package org.bitcoins.testkit.gen
|
||||
package org.bitcoins.testkit.core.gen.p2p
|
||||
|
||||
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.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 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]]
|
||||
* [[https://bitcoin.org/en/developer-reference#version]]
|
||||
*
|
||||
* @return
|
||||
* @see [[https://bitcoin.org/en/developer-reference#version]]
|
||||
*/
|
||||
def versionMessage: Gen[VersionMessage] =
|
||||
for {
|
||||
|
@ -53,9 +86,8 @@ trait ControlMessageGenerator {
|
|||
|
||||
/**
|
||||
* Generates a [[PingMessage]]
|
||||
* [[https://bitcoin.org/en/developer-reference#ping]]
|
||||
*
|
||||
* @return
|
||||
* @see [[https://bitcoin.org/en/developer-reference#ping]]
|
||||
*/
|
||||
def pingMessage: Gen[PingMessage] =
|
||||
for {
|
||||
|
@ -64,20 +96,24 @@ trait ControlMessageGenerator {
|
|||
|
||||
/**
|
||||
* Generates a [[PongMessage]]
|
||||
* [[https://bitcoin.org/en/developer-reference#pong]]
|
||||
*
|
||||
* @return
|
||||
* @see [[https://bitcoin.org/en/developer-reference#pong]]
|
||||
*/
|
||||
def pongMessage: Gen[PongMessage] =
|
||||
for {
|
||||
uInt64 <- NumberGenerator.uInt64s
|
||||
} yield PongMessage(uInt64)
|
||||
|
||||
def addrMessage: Gen[AddrMessage] = {
|
||||
for {
|
||||
addresses <- Gen.listOf(P2PGenerator.networkIpAddress)
|
||||
} yield AddrMessage(addresses)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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] =
|
||||
for {
|
||||
|
@ -86,9 +122,8 @@ trait ControlMessageGenerator {
|
|||
|
||||
/**
|
||||
* Generates a [[ServiceIdentifier]]
|
||||
* [[https://bitcoin.org/en/developer-reference#version]]
|
||||
*
|
||||
* @return
|
||||
* @see [[https://bitcoin.org/en/developer-reference#version]]
|
||||
*/
|
||||
def serviceIdentifier: Gen[ServiceIdentifier] =
|
||||
for {
|
||||
|
@ -110,9 +145,8 @@ trait ControlMessageGenerator {
|
|||
|
||||
/**
|
||||
* Creates a [[FilterLoadMessage]]
|
||||
* [[https://bitcoin.org/en/developer-reference#filterload]]
|
||||
*
|
||||
* @return
|
||||
* @see [[https://bitcoin.org/en/developer-reference#filterload]]
|
||||
*/
|
||||
def filterLoadMessage: Gen[FilterLoadMessage] =
|
||||
for {
|
||||
|
@ -125,9 +159,8 @@ trait ControlMessageGenerator {
|
|||
|
||||
/**
|
||||
* Creates a [[FilterAddMessage]]
|
||||
* [[https://bitcoin.org/en/developer-reference#filteradd]]
|
||||
*
|
||||
* @return
|
||||
* @see [[https://bitcoin.org/en/developer-reference#filteradd]]
|
||||
*/
|
||||
def filterAddMessage: Gen[FilterAddMessage] =
|
||||
for {
|
||||
|
@ -137,9 +170,8 @@ trait ControlMessageGenerator {
|
|||
|
||||
/**
|
||||
* Creates a [[RejectMessage]]
|
||||
* [[https://bitcoin.org/en/developer-reference#reject]]
|
||||
*
|
||||
* @return
|
||||
* @see [[https://bitcoin.org/en/developer-reference#reject]]
|
||||
*/
|
||||
def rejectMessage: Gen[RejectMessage] =
|
||||
for {
|
||||
|
@ -149,5 +181,3 @@ trait ControlMessageGenerator {
|
|||
extra <- CryptoGenerators.doubleSha256Digest
|
||||
} 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.p2p._
|
||||
|
@ -11,13 +11,44 @@ import org.bitcoins.testkit.core.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]]
|
||||
*/
|
||||
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]]
|
||||
*
|
||||
* @see [[https://bitcoin.org/en/developer-reference#getheaders]]
|
||||
*/
|
||||
def getHeaderMessages: Gen[GetHeadersMessage] =
|
||||
|
@ -28,6 +59,15 @@ trait DataMessageGenerator {
|
|||
hashStop <- CryptoGenerators.doubleSha256Digest
|
||||
} 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] =
|
||||
for {
|
||||
randomNum <- Gen.choose(1, 10)
|
||||
|
@ -39,7 +79,8 @@ trait DataMessageGenerator {
|
|||
|
||||
/**
|
||||
* 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] =
|
||||
for {
|
||||
|
@ -66,6 +107,12 @@ trait DataMessageGenerator {
|
|||
inventories <- Gen.listOfN(numInventories, inventory)
|
||||
} yield InventoryMessage(inventories)
|
||||
|
||||
def notFoundMessage: Gen[NotFoundMessage] = {
|
||||
for {
|
||||
inventories <- Gen.nonEmptyListOf(inventory)
|
||||
} yield NotFoundMessage(inventories)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random [[GetDataMessage]]
|
||||
* @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)
|
||||
|
||||
/** 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 = {
|
||||
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(
|
||||
minSuccessful = PosInt.from(executions).get,
|
||||
minSize = PosInt.from(executions).get,
|
||||
|
@ -33,7 +34,7 @@ abstract class BitcoinSUnitTest
|
|||
* @return
|
||||
*/
|
||||
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
|
||||
|
@ -41,7 +42,7 @@ abstract class BitcoinSUnitTest
|
|||
* @return
|
||||
*/
|
||||
def generatorDrivenConfigNewCode: PropertyCheckConfiguration = {
|
||||
buildConfig(BitcoinSUnitTest.NEW_CODE_EXECUTIONS)
|
||||
customGenDrivenConfig(BitcoinSUnitTest.NEW_CODE_EXECUTIONS)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue