mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-13 11:35:40 +01:00
Delete files from the last iteration of our spv node (#656)
This commit is contained in:
parent
2994e82d02
commit
aef741adf7
6 changed files with 0 additions and 699 deletions
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
package org.bitcoins.node.networking
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.testkit.{ImplicitSender, TestActorRef, TestKit, TestProbe}
|
||||
import org.bitcoins.core.crypto.DoubleSha256Digest
|
||||
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
||||
import org.bitcoins.core.util.{BitcoinSLogger, BitcoinSUtil}
|
||||
import org.bitcoins.node.db.UnitTestDbConfig
|
||||
import org.bitcoins.core.p2p.BlockMessage
|
||||
import org.bitcoins.core.p2p.BlockMessage
|
||||
import org.bitcoins.node.util.TestUtil
|
||||
import org.scalatest.{BeforeAndAfter, BeforeAndAfterAll, FlatSpecLike, MustMatchers}
|
||||
|
||||
import scala.concurrent.duration.DurationInt
|
||||
|
||||
/**
|
||||
* Created by chris on 7/10/16.
|
||||
*/
|
||||
class BlockActorTest
|
||||
extends TestKit(ActorSystem("BlockActorTest"))
|
||||
with FlatSpecLike
|
||||
with MustMatchers
|
||||
with ImplicitSender
|
||||
with BeforeAndAfter
|
||||
with BeforeAndAfterAll
|
||||
with BitcoinSLogger {
|
||||
|
||||
def blockActor = {
|
||||
val peerMsgHandler = TestUtil.peer(self)
|
||||
TestActorRef(
|
||||
props = BlockActor.props(peerMsgHandler = peerMsgHandler,
|
||||
dbConfig = TestUtil.dbConfig),
|
||||
supervisor = self
|
||||
)
|
||||
}
|
||||
|
||||
val blockHash = DoubleSha256Digest.fromHex(
|
||||
BitcoinSUtil.flipEndianness(
|
||||
"00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206"))
|
||||
|
||||
"BlockActor" must "be able to send a GetBlocksMessage then receive that block back" in {
|
||||
blockActor ! blockHash
|
||||
val blockMsg = expectMsgType[BlockMessage](10.seconds)
|
||||
blockMsg.block.blockHeader.hash must be(blockHash)
|
||||
|
||||
}
|
||||
|
||||
it must "be able to request a block from it's block header" in {
|
||||
val blockHeader = BlockHeader(
|
||||
"0100000043497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000bac8b0fa927c0ac8234287e33c5f74d38d354820e24756ad709d7038fc5f31f020e7494dffff001d03e4b672")
|
||||
blockActor ! blockHeader
|
||||
val blockMsg = expectMsgType[BlockMessage](10.seconds)
|
||||
blockMsg.block.blockHeader.hash must be(blockHash)
|
||||
}
|
||||
|
||||
override def afterAll = {
|
||||
TestKit.shutdownActorSystem(system)
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
package org.bitcoins.node.networking
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.testkit.{ImplicitSender, TestActorRef, TestKit}
|
||||
import org.bitcoins.core.crypto.{DoubleSha256Digest, Sha256Hash160Digest}
|
||||
import org.bitcoins.core.number.{Int32, UInt32}
|
||||
import org.bitcoins.core.protocol.P2PKHAddress
|
||||
import org.bitcoins.core.protocol.blockchain.{BlockHeader, MerkleBlock, PartialMerkleTree}
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.node.constant.Constants
|
||||
import org.bitcoins.core.p2p.data.{Inventory, InventoryMessage, MerkleBlockMessage, TransactionMessage}
|
||||
import org.bitcoins.core.p2p.{MsgBlock, MsgTx}
|
||||
import org.bitcoins.node.constant.Constants
|
||||
import org.bitcoins.node.db.UnitTestDbConfig
|
||||
import org.bitcoins.core.p2p.data.{Inventory, InventoryMessage, MerkleBlockMessage, TransactionMessage}
|
||||
import org.bitcoins.core.p2p.{MsgBlock, MsgTx}
|
||||
import org.bitcoins.node.util.TestUtil
|
||||
import org.scalatest._
|
||||
import scodec.bits.BitVector
|
||||
|
||||
import scala.concurrent.duration.DurationInt
|
||||
|
||||
/**
|
||||
* Created by chris on 9/1/16.
|
||||
*/
|
||||
class PaymentActorTest
|
||||
extends TestKit(ActorSystem("PaymentActorTest"))
|
||||
with ImplicitSender
|
||||
with FlatSpecLike
|
||||
with MustMatchers
|
||||
with BeforeAndAfterAll {
|
||||
|
||||
val txId = DoubleSha256Digest.fromHex(
|
||||
"0d507a29efb362ce93687f524e7e3a668689e335ba20374c93710efdf7597c5f")
|
||||
|
||||
val transaction = Transaction.fromHex(
|
||||
"0100000001f78d02e5d2e37319a4cec31331babea9f0c6b9efb75060e27cf23997c6e560b3010000006a47304402207f6d19701c0e58bdedbc5073c17ac36e3493326c8c916db7dd224961fa8c8c9f02201ba78149c12a9754f7ceab1bcfe4c6afb8fb5ee38078f47065d316cddaa932b40121023de7008d781aa60ed8b0cdf92ece1d3e6eca2a0fd958d883114129a450ab05f2feffffff02bf9fb700000000001976a914a82d2cefa38fe32eb90c5d31d2063dde716c90df88ac009f2400000000001976a914415a05d63df2c212e1c750a70eba49d6d8af196d88accb210e00")
|
||||
"PaymentActor" must "monitor an address, then send SuccessfulPayment or FailedPayment message if that address is not paid in the next block" in {
|
||||
val paymentActor = paymentActorRef
|
||||
val pubKeyHash =
|
||||
Sha256Hash160Digest("415a05d63df2c212e1c750a70eba49d6d8af196d")
|
||||
val addr = P2PKHAddress(pubKeyHash, Constants.networkParameters)
|
||||
paymentActor ! addr
|
||||
|
||||
//TODO: Remove this thread.sleep call
|
||||
//wait for connection to be made so we have the right context
|
||||
Thread.sleep(3000)
|
||||
//build an inventory message, then send it to the payment actor
|
||||
val inventory = Inventory(MsgTx, txId)
|
||||
val txIdInvMsg = InventoryMessage(Seq(inventory))
|
||||
paymentActor ! txIdInvMsg
|
||||
|
||||
//now the payment actor switches to waiting for the full transaction
|
||||
//so send the actor the full transaction
|
||||
val txMsg = TransactionMessage(transaction)
|
||||
paymentActor ! txMsg
|
||||
|
||||
//after seeing the tx message, our payment actor waits for a block to be announced on the network
|
||||
val blockMsg = Inventory(
|
||||
MsgBlock,
|
||||
DoubleSha256Digest(
|
||||
"62862488a791bf863ea840f8b9e4ded91ef5625e73b4f56940d6050000000000"))
|
||||
val blockInvMsg = InventoryMessage(Seq(blockMsg))
|
||||
paymentActor ! blockInvMsg
|
||||
|
||||
val partialMerkleTree = PartialMerkleTree(
|
||||
transactionCount = UInt32(36),
|
||||
hashes = List(
|
||||
DoubleSha256Digest(
|
||||
"27f706c39b2ea48d9316d85f513080da35329f3629ecf5f22869e191d38f3553"),
|
||||
DoubleSha256Digest(
|
||||
"0d507a29efb362ce93687f524e7e3a668689e335ba20374c93710efdf7597c5f"),
|
||||
DoubleSha256Digest(
|
||||
"b80117bee395e816a26e807dcb5858403142dcb8d5edfc3eaa6dde700a9198a2"),
|
||||
DoubleSha256Digest(
|
||||
"d297f7e4e712967f77f87c65fc698fc6ff8fc0fb056b07ebd459567d0a1c36f8"),
|
||||
DoubleSha256Digest(
|
||||
"114b915455ad5cb314e77c648e243f71d9b4895ab96c38cc3c7e27fd151d112b"),
|
||||
DoubleSha256Digest(
|
||||
"c83ce4bd870c2d791d73d1ce3fd7b96f61c94d3ce3af270af22938c0d15b683a"),
|
||||
DoubleSha256Digest(
|
||||
"ec89457fd619020e11727f01d7f5518b7c3114aaa70376611efe9efd41c5c099")
|
||||
),
|
||||
bits = BitVector.fromValidBin("11011111" + "00000000")
|
||||
)
|
||||
|
||||
//after seeing a new block announcement on the network we request a merkle block message from the peer on the network
|
||||
//this merkle block message is taken from a node on the network
|
||||
val header = BlockHeader(
|
||||
version = Int32(805306368),
|
||||
previousBlockHash = DoubleSha256Digest(
|
||||
"1d73fa2ffbdf79c2e78e3312066833c4a264a19b958faf450100000000000000"),
|
||||
merkleRootHash = DoubleSha256Digest(
|
||||
"9b47cf5d64aa52d7536e2b469891a79ea8488092dc3c2e0ed26dbe9b508cce16"),
|
||||
time = UInt32(1472661981),
|
||||
nBits = UInt32(486604799),
|
||||
nonce = UInt32(4219144207L)
|
||||
)
|
||||
val merkleBlockMsg = MerkleBlockMessage(
|
||||
merkleBlock = MerkleBlock(blockHeader = header,
|
||||
txCount = UInt32(36),
|
||||
partialMerkleTree = partialMerkleTree))
|
||||
paymentActor ! merkleBlockMsg
|
||||
expectMsgType[PaymentActor.SuccessfulPayment](10.seconds)
|
||||
}
|
||||
|
||||
|
||||
def paymentActorRef: TestActorRef[PaymentActor] = {
|
||||
val peerMsgHandler = TestUtil.peer(self)
|
||||
val paymentProps = PaymentActor.props(
|
||||
peerMsgHandler = peerMsgHandler,
|
||||
dbConfig = TestUtil.dbConfig)
|
||||
|
||||
TestActorRef(paymentProps, self)
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -1,242 +0,0 @@
|
|||
/*
|
||||
package org.bitcoins.node.networking.sync
|
||||
|
||||
import akka.actor.{ActorSystem, PoisonPill}
|
||||
import akka.testkit.{ImplicitSender, TestActorRef, TestKit, TestProbe}
|
||||
import org.bitcoins.core.config.{MainNet, TestNet3}
|
||||
import org.bitcoins.core.gen.BlockchainElementsGenerator
|
||||
import org.bitcoins.core.protocol.blockchain.{
|
||||
BlockHeader,
|
||||
MainNetChainParams,
|
||||
TestNetChainParams
|
||||
}
|
||||
import org.bitcoins.node.constant.{Constants, TestConstants}
|
||||
import org.bitcoins.node.db.NodeDbManagement
|
||||
import org.bitcoins.core.p2p.data.HeadersMessage
|
||||
import org.bitcoins.node.models.BlockHeaderTable
|
||||
import org.bitcoins.node.util.TestUtil
|
||||
import org.scalatest.{
|
||||
BeforeAndAfter,
|
||||
BeforeAndAfterAll,
|
||||
FlatSpecLike,
|
||||
MustMatchers
|
||||
}
|
||||
import slick.jdbc.PostgresProfile.api._
|
||||
|
||||
import scala.concurrent.{Await, ExecutionContext}
|
||||
import scala.concurrent.duration.DurationInt
|
||||
|
||||
/**
|
||||
* Created by chris on 9/13/16.
|
||||
*/
|
||||
class BlockHeaderSyncActorTest
|
||||
extends TestKit(ActorSystem("BlockHeaderSyncActorSpec"))
|
||||
with ImplicitSender
|
||||
with FlatSpecLike
|
||||
with MustMatchers
|
||||
with BeforeAndAfter
|
||||
with BeforeAndAfterAll {
|
||||
implicit val ec: ExecutionContext =
|
||||
scala.concurrent.ExecutionContext.Implicits.global
|
||||
val timeout = 10.seconds
|
||||
val genesisBlockHash = TestNetChainParams.genesisBlock.blockHeader.hash
|
||||
|
||||
before {
|
||||
Await.result(
|
||||
NodeDbManagement.createBlockHeaderTable(TestConstants.dbConfig),
|
||||
timeout)
|
||||
}
|
||||
|
||||
"BlockHeaderSyncActor" must "send us an error if we receive two block headers that are not connected" in {
|
||||
val (b, probe) = blockHeaderSyncActor
|
||||
val blockHeader1 = BlockchainElementsGenerator.blockHeader.sample.get
|
||||
val blockHeader2 = BlockchainElementsGenerator.blockHeader.sample.get
|
||||
val headersMsg = HeadersMessage(List(blockHeader2))
|
||||
b ! BlockHeaderSyncActor.StartHeaders(List(blockHeader1))
|
||||
b ! headersMsg
|
||||
val errorMsg =
|
||||
probe.expectMsgType[BlockHeaderSyncActor.BlockHeadersDoNotConnect]
|
||||
errorMsg must be(
|
||||
BlockHeaderSyncActor.BlockHeadersDoNotConnect(blockHeader1.hash,
|
||||
blockHeader2.hash))
|
||||
b ! PoisonPill
|
||||
}
|
||||
|
||||
it must "sync the first 5 headers on testnet" in {
|
||||
//genesis block hash is 43497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000
|
||||
val genesisBlockHash = TestNetChainParams.genesisBlock.blockHeader.hash
|
||||
val firstBlockHash = TestUtil.firstFiveTestNetBlockHeaders.head.hash
|
||||
val secondBlockHash = TestUtil.firstFiveTestNetBlockHeaders(1).hash
|
||||
val thirdBlockHash = TestUtil.firstFiveTestNetBlockHeaders(2).hash
|
||||
val fourthBlockHash = TestUtil.firstFiveTestNetBlockHeaders(3).hash
|
||||
//5th block hash on testnet
|
||||
val fifthBlockHash = TestUtil.firstFiveTestNetBlockHeaders.last.hash
|
||||
val (b, probe) = blockHeaderSyncActor
|
||||
|
||||
b ! BlockHeaderSyncActor.GetHeaders(genesisBlockHash, fifthBlockHash)
|
||||
val headersReply =
|
||||
probe.expectMsgType[BlockHeaderSyncActor.GetHeadersReply](5.seconds)
|
||||
//note the hash we started the sync at is not included in the expected blockheaders we recevie from our peer
|
||||
val expectedHashes = List(firstBlockHash,
|
||||
secondBlockHash,
|
||||
thirdBlockHash,
|
||||
fourthBlockHash,
|
||||
fifthBlockHash)
|
||||
val actualHashes = headersReply.headers.map(_.hash)
|
||||
|
||||
actualHashes.size must be(expectedHashes.size)
|
||||
actualHashes must be(expectedHashes)
|
||||
b ! PoisonPill
|
||||
}
|
||||
|
||||
it must "fail to sync with a GetHeaders message if they are not connected" in {
|
||||
val (b, probe) = blockHeaderSyncActor
|
||||
val fifthBlockHash = TestUtil.firstFiveTestNetBlockHeaders.last.hash
|
||||
b ! BlockHeaderSyncActor.GetHeaders(genesisBlockHash, fifthBlockHash)
|
||||
|
||||
val headers = TestUtil.firstFiveTestNetBlockHeaders
|
||||
.slice(0, 2) ++ TestUtil.firstFiveTestNetBlockHeaders
|
||||
.slice(3, TestUtil.firstFiveTestNetBlockHeaders.size)
|
||||
val headersMsgMissingHeader = HeadersMessage(headers)
|
||||
b ! headersMsgMissingHeader
|
||||
|
||||
probe.expectMsgType[BlockHeaderSyncActor.BlockHeadersDoNotConnect]
|
||||
b ! PoisonPill
|
||||
}
|
||||
|
||||
it must "stop syncing when we do not receive 2000 block headers from our peer" in {
|
||||
val (b, probe) = blockHeaderSyncActor
|
||||
b ! BlockHeaderSyncActor.StartHeaders(
|
||||
List(TestNetChainParams.genesisBlock.blockHeader))
|
||||
val headersMsg = HeadersMessage(TestUtil.firstFiveTestNetBlockHeaders)
|
||||
b ! headersMsg
|
||||
val reply =
|
||||
probe.expectMsgType[BlockHeaderSyncActor.SuccessfulSyncReply](7.seconds)
|
||||
reply.lastHeader must be(TestUtil.firstFiveTestNetBlockHeaders.last)
|
||||
b ! PoisonPill
|
||||
}
|
||||
|
||||
it must "start syncing at the genesis block when there are no headers in the database" in {
|
||||
val (b, probe) = blockHeaderSyncActor
|
||||
b ! BlockHeaderSyncActor.StartAtLastSavedHeader
|
||||
val lastSavedHeaderReply =
|
||||
probe.expectMsgType[BlockHeaderSyncActor.StartAtLastSavedHeaderReply]
|
||||
lastSavedHeaderReply.header must be(
|
||||
Constants.chainParams.genesisBlock.blockHeader)
|
||||
b ! PoisonPill
|
||||
}
|
||||
|
||||
it must "successfully check two block headers if their difficulty is the same" in {
|
||||
val firstHeader = BlockchainElementsGenerator.blockHeader.sample.get
|
||||
//note that this header properly references the previous header, but nBits are different
|
||||
val secondHeader = BlockchainElementsGenerator
|
||||
.blockHeader(firstHeader.hash, firstHeader.nBits)
|
||||
.sample
|
||||
.get
|
||||
val checkHeaderResult =
|
||||
BlockHeaderSyncActor.checkHeaders(Some(firstHeader),
|
||||
List(secondHeader),
|
||||
0,
|
||||
MainNet)
|
||||
|
||||
checkHeaderResult.error.isDefined must be(false)
|
||||
checkHeaderResult.headers must be(List(secondHeader))
|
||||
}
|
||||
|
||||
it must "successfully check the header of ONLY the genesis block" in {
|
||||
val genesisBlockHeader = MainNetChainParams.genesisBlock.blockHeader
|
||||
val checkHeaderResult =
|
||||
BlockHeaderSyncActor.checkHeaders(None,
|
||||
List(genesisBlockHeader),
|
||||
0,
|
||||
MainNet)
|
||||
checkHeaderResult.error.isDefined must be(false)
|
||||
checkHeaderResult.headers must be(List(genesisBlockHeader))
|
||||
}
|
||||
|
||||
it must "successfully check a sequence of headers if their is a difficulty change on the 2016 block" in {
|
||||
val firstHeaders = genValidHeaderChain(2015)
|
||||
val lastHeader =
|
||||
BlockchainElementsGenerator.blockHeader(firstHeaders.last.hash).sample.get
|
||||
val headers = firstHeaders ++ List(lastHeader)
|
||||
val checkHeaderResult =
|
||||
BlockHeaderSyncActor.checkHeaders(None, headers, 0, MainNet)
|
||||
checkHeaderResult.error must be(None)
|
||||
checkHeaderResult.headers must be(headers)
|
||||
}
|
||||
|
||||
it must "fail a checkHeader on a sequence of headers if their is a difficulty change on the 2015 or 2017 block" in {
|
||||
val firstHeaders = genValidHeaderChain(2014)
|
||||
|
||||
val lastHeader =
|
||||
BlockchainElementsGenerator.blockHeader(firstHeaders.last.hash).sample.get
|
||||
val headers = firstHeaders ++ List(lastHeader)
|
||||
val checkHeaderResult =
|
||||
BlockHeaderSyncActor.checkHeaders(None, headers, 0, MainNet)
|
||||
checkHeaderResult.error.isDefined must be(true)
|
||||
checkHeaderResult.headers must be(headers)
|
||||
|
||||
val firstHeaders2 =
|
||||
BlockchainElementsGenerator.validHeaderChain(2016).sample.get
|
||||
val lastHeader2 = BlockchainElementsGenerator
|
||||
.blockHeader(firstHeaders2.last.hash)
|
||||
.sample
|
||||
.get
|
||||
val headers2 = firstHeaders ++ List(lastHeader2)
|
||||
val checkHeaderResult2 =
|
||||
BlockHeaderSyncActor.checkHeaders(None, headers2, 0, MainNet)
|
||||
checkHeaderResult2.error.isDefined must be(true)
|
||||
checkHeaderResult2.headers must be(headers2)
|
||||
}
|
||||
|
||||
it must "fail to check two block headers if the network difficulty isn't correct" in {
|
||||
val firstHeader = BlockchainElementsGenerator.blockHeader.sample.get
|
||||
//note that this header properly references the previous header, but nBits are different
|
||||
val secondHeader =
|
||||
BlockchainElementsGenerator.blockHeader(firstHeader.hash).sample.get
|
||||
val checkHeaderResult =
|
||||
BlockHeaderSyncActor.checkHeaders(Some(firstHeader),
|
||||
List(secondHeader),
|
||||
0,
|
||||
MainNet)
|
||||
|
||||
val errorMsg = checkHeaderResult.error.get
|
||||
.asInstanceOf[BlockHeaderSyncActor.BlockHeaderDifficultyFailure]
|
||||
|
||||
errorMsg.previousBlockHeader must be(firstHeader)
|
||||
errorMsg.blockHeader must be(secondHeader)
|
||||
}
|
||||
|
||||
/** The [[TestActorRef]] for a [[BlockHeaderSyncActor]] we use for testing */
|
||||
private def blockHeaderSyncActor: (
|
||||
TestActorRef[BlockHeaderSyncActor],
|
||||
TestProbe) = {
|
||||
val probe = TestProbe()
|
||||
|
||||
val peerMsgHandler = TestUtil.peer(self)
|
||||
|
||||
val syncActorProps = BlockHeaderSyncActor.props(
|
||||
peerMsgHandler = peerMsgHandler,
|
||||
dbConfig = TestConstants.dbConfig,
|
||||
networkParameters = TestNet3)
|
||||
val blockHeaderSyncActor: TestActorRef[BlockHeaderSyncActor] = {
|
||||
TestActorRef(syncActorProps, probe.ref)
|
||||
}
|
||||
|
||||
(blockHeaderSyncActor, probe)
|
||||
}
|
||||
|
||||
private def genValidHeaderChain(num: Long): List[BlockHeader] = {
|
||||
BlockchainElementsGenerator.validHeaderChain(num).sample.get.toList
|
||||
}
|
||||
|
||||
after {
|
||||
Await.result(NodeDbManagement.dropBlockHeaderTable(TestConstants.dbConfig),
|
||||
timeout)
|
||||
}
|
||||
|
||||
override def afterAll = {
|
||||
TestKit.shutdownActorSystem(system)
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -1,51 +0,0 @@
|
|||
package org.bitcoins.node.networking
|
||||
|
||||
import akka.actor.{Actor, ActorContext, ActorRef, Props}
|
||||
import akka.event.LoggingReceive
|
||||
import org.bitcoins.core.crypto.DoubleSha256Digest
|
||||
import org.bitcoins.core.p2p.NetworkMessage
|
||||
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.core.p2p._
|
||||
|
||||
/**
|
||||
* Created by chris on 7/10/16.
|
||||
*/
|
||||
sealed abstract class BlockActor extends Actor with BitcoinSLogger {
|
||||
|
||||
def peerMsgHandler: ActorRef
|
||||
|
||||
def receive: Receive = LoggingReceive {
|
||||
case hash: DoubleSha256Digest =>
|
||||
val inv = Inventory(TypeIdentifier.MsgBlock, hash)
|
||||
val getDataMessage = GetDataMessage(inv)
|
||||
val networkMessage =
|
||||
NetworkMessage(network = ???, getDataMessage)
|
||||
peerMsgHandler ! networkMessage
|
||||
context.become(awaitBlockMsg)
|
||||
case blockHeader: BlockHeader =>
|
||||
self.forward(blockHeader.hash)
|
||||
}
|
||||
|
||||
def awaitBlockMsg: Receive = LoggingReceive {
|
||||
case blockMsg: BlockMessage =>
|
||||
context.parent ! blockMsg
|
||||
context.stop(self)
|
||||
}
|
||||
}
|
||||
|
||||
object BlockActor {
|
||||
private case class BlockActorImpl(
|
||||
peerMsgHandler: ActorRef
|
||||
) extends BlockActor
|
||||
|
||||
def props(peerMsgHandler: ActorRef): Props = {
|
||||
Props(classOf[BlockActorImpl], peerMsgHandler)
|
||||
}
|
||||
|
||||
def apply(peerMsgHandler: ActorRef)(
|
||||
implicit context: ActorContext): ActorRef = {
|
||||
context.actorOf(props(peerMsgHandler))
|
||||
}
|
||||
|
||||
}
|
|
@ -1,175 +0,0 @@
|
|||
package org.bitcoins.node.networking
|
||||
|
||||
import akka.actor.{Actor, ActorRef, ActorRefFactory, Props}
|
||||
import akka.event.LoggingReceive
|
||||
import akka.io.Tcp
|
||||
import org.bitcoins.core.bloom.{BloomFilter, BloomUpdateNone}
|
||||
import org.bitcoins.core.crypto.{DoubleSha256Digest, Sha256Hash160Digest}
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.p2p.NetworkMessage
|
||||
import org.bitcoins.core.protocol.Address
|
||||
import org.bitcoins.core.protocol.blockchain.MerkleBlock
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.core.p2p._
|
||||
import org.bitcoins.node.util.BitcoinSpvNodeUtil
|
||||
|
||||
/**
|
||||
* Created by chris on 8/30/16.
|
||||
* Responsible for checking if a payment to a address was made
|
||||
* Verifying that the transaction that made the payment was included
|
||||
* inside of a block on the blockchain
|
||||
*
|
||||
* 1.) Creates a bloom filter
|
||||
* 2.) Sends the bloom filter to a node on the network
|
||||
* 3.) Nodes matches the bloom filter, sends a txid that matched the filter back to us
|
||||
* 4.) We request the full transaction using a [[GetDataMessage]]
|
||||
* 5.) We verify the transaction given to us has an output that matches the address we expected a payment to
|
||||
* 6.) When another block is announced on the network, we send a MsgMerkleBlock
|
||||
* to our peer on the network to see if the tx was included on that block
|
||||
* 7.) If it was, send the actor that that requested this message back
|
||||
*/
|
||||
sealed abstract class PaymentActor extends Actor with BitcoinSLogger {
|
||||
|
||||
def peerMsgHandler: ActorRef
|
||||
|
||||
def receive = LoggingReceive {
|
||||
case hash: Sha256Hash160Digest =>
|
||||
paymentToHash(hash)
|
||||
case address: Address =>
|
||||
self.forward(address.hash)
|
||||
}
|
||||
|
||||
/** Constructs a bloom filter that matches the given hash,
|
||||
* then sends that bloom filter to a peer on the network */
|
||||
def paymentToHash(hash: Sha256Hash160Digest) = {
|
||||
val bloomFilter =
|
||||
BloomFilter(10, 0.0001, UInt32.zero, BloomUpdateNone).insert(hash)
|
||||
val filterLoadMsg = FilterLoadMessage(bloomFilter)
|
||||
val bloomFilterNetworkMsg =
|
||||
NetworkMessage(network = ???, filterLoadMsg)
|
||||
peerMsgHandler ! bloomFilterNetworkMsg
|
||||
}
|
||||
|
||||
/** Awaits for a [[GetDataMessage]] that requested a transaction. We can also fire off more [[GetDataMessage]] inside of this context */
|
||||
def awaitTransactionGetDataMessage(
|
||||
hash: Sha256Hash160Digest,
|
||||
peerMessageHandler: ActorRef): Receive = LoggingReceive {
|
||||
case txMsg: TransactionMessage =>
|
||||
//check to see if any of the outputs on this tx match our hash
|
||||
val outputs = txMsg.transaction.outputs.filter(o =>
|
||||
o.scriptPubKey.asm.filter(_.bytes == hash.bytes).nonEmpty)
|
||||
|
||||
if (outputs.nonEmpty) {
|
||||
logger.debug(
|
||||
"matched transaction inside of awaitTransactionGetDataMsg: " + txMsg.transaction.hex)
|
||||
logger.debug("Matched txid: " + txMsg.transaction.txId.hex)
|
||||
logger.debug("Switching to awaitBlockAnnouncement")
|
||||
context.become(
|
||||
awaitBlockAnnouncement(hash,
|
||||
txMsg.transaction.txId,
|
||||
peerMessageHandler))
|
||||
}
|
||||
//otherwise we do nothing and wait for another transaction message
|
||||
case invMsg: InventoryMessage =>
|
||||
//txs are broadcast by nodes on the network when they are seen by a node
|
||||
//filter out the txs we do not care about
|
||||
val txInventories =
|
||||
invMsg.inventories.filter(_.typeIdentifier == TypeIdentifier.MsgTx)
|
||||
handleTransactionInventoryMessages(txInventories, peerMessageHandler)
|
||||
}
|
||||
|
||||
/** Sends a [[GetDataMessage]] to get the full transaction for a transaction inventory message */
|
||||
private def handleTransactionInventoryMessages(
|
||||
inventory: Seq[Inventory],
|
||||
peerMessageHandler: ActorRef): Unit = {
|
||||
for {
|
||||
txInv <- inventory
|
||||
inventory = GetDataMessage(txInv)
|
||||
} yield peerMessageHandler ! inventory
|
||||
|
||||
()
|
||||
}
|
||||
|
||||
/** This context waits for a block announcement on the network,
|
||||
* then constructs a [[MerkleBlockMessage]] to check
|
||||
* if the txid was included in that block */
|
||||
def awaitBlockAnnouncement(
|
||||
hash: Sha256Hash160Digest,
|
||||
txId: DoubleSha256Digest,
|
||||
peerMessageHandler: ActorRef): Receive = LoggingReceive {
|
||||
case invMsg: InventoryMessage =>
|
||||
val blockHashes =
|
||||
invMsg.inventories
|
||||
.filter(_.typeIdentifier == TypeIdentifier.MsgBlock)
|
||||
.map(_.hash)
|
||||
if (blockHashes.nonEmpty) {
|
||||
//construct a merkle block message to verify that the txIds was in the block
|
||||
val merkleBlockInventory =
|
||||
Inventory(TypeIdentifier.MsgFilteredBlock, blockHashes.head)
|
||||
val getDataMsg = GetDataMessage(merkleBlockInventory)
|
||||
val getDataNetworkMessage =
|
||||
NetworkMessage(network = ???, getDataMsg)
|
||||
peerMessageHandler ! getDataNetworkMessage
|
||||
logger.debug("Switching to awaitMerkleBlockMessage")
|
||||
context.become(
|
||||
awaitMerkleBlockMessage(hash, txId, blockHashes, peerMessageHandler))
|
||||
}
|
||||
//else do nothing and wait for another block announcement
|
||||
|
||||
}
|
||||
|
||||
/** This context waits for a [[MerkleBlockMessage]] from our peer on the network, then checks
|
||||
* if the given txid is contained inside of the block. If it is included, send a [[PaymentActor.SuccessfulPayment]]
|
||||
* message back to the actor that created this actor, else send a [[PaymentActor.FailedPayment]] message back to
|
||||
* the actor that created this actor
|
||||
* @param hash
|
||||
* @param txId
|
||||
* @param blockHashes
|
||||
* @param peerMessageHandler
|
||||
* @return
|
||||
*/
|
||||
def awaitMerkleBlockMessage(
|
||||
hash: Sha256Hash160Digest,
|
||||
txId: DoubleSha256Digest,
|
||||
blockHashes: Seq[DoubleSha256Digest],
|
||||
peerMessageHandler: ActorRef): Receive = LoggingReceive {
|
||||
case merkleBlockMsg: MerkleBlockMessage =>
|
||||
val result = merkleBlockMsg.merkleBlock.partialMerkleTree.extractMatches
|
||||
.contains(txId)
|
||||
if (result) {
|
||||
val successfulPayment =
|
||||
PaymentActor.SuccessfulPayment(hash,
|
||||
txId,
|
||||
blockHashes,
|
||||
merkleBlockMsg.merkleBlock)
|
||||
logger.info("Received successful payment: " + successfulPayment)
|
||||
context.parent ! successfulPayment
|
||||
} else context.parent ! PaymentActor.FailedPayment(hash)
|
||||
peerMessageHandler ! Tcp.Close
|
||||
context.stop(self)
|
||||
}
|
||||
}
|
||||
|
||||
object PaymentActor {
|
||||
private case class PaymentActorImpl(peerMsgHandler: ActorRef)
|
||||
extends PaymentActor
|
||||
|
||||
def props(peerMsgHandler: ActorRef): Props =
|
||||
Props(classOf[PaymentActorImpl], peerMsgHandler)
|
||||
|
||||
def apply(peerMsgHandler: ActorRef)(
|
||||
implicit context: ActorRefFactory): ActorRef =
|
||||
context.actorOf(props(peerMsgHandler),
|
||||
BitcoinSpvNodeUtil.createActorName(this.getClass))
|
||||
|
||||
sealed trait PaymentActorMessage
|
||||
case class SuccessfulPayment(
|
||||
hash: Sha256Hash160Digest,
|
||||
txId: DoubleSha256Digest,
|
||||
blockHash: Seq[DoubleSha256Digest],
|
||||
merkleBlock: MerkleBlock)
|
||||
extends PaymentActorMessage
|
||||
|
||||
case class FailedPayment(hash: Sha256Hash160Digest)
|
||||
extends PaymentActorMessage
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package org.bitcoins.node.store
|
||||
|
||||
import java.io.FileOutputStream
|
||||
|
||||
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
||||
import org.bitcoins.node.constant.Constants
|
||||
import org.bitcoins.node.constant.Constants
|
||||
|
||||
import scala.io.Source
|
||||
|
||||
/**
|
||||
* Created by chris on 9/5/16.
|
||||
*/
|
||||
trait BlockHeaderStore {
|
||||
|
||||
/** Appends block headers to the given file */
|
||||
def append(headers: Seq[BlockHeader], file: java.io.File): Unit = {
|
||||
printToFile(file) { p =>
|
||||
headers.map(_.hex).foreach(p.println)
|
||||
}
|
||||
}
|
||||
|
||||
/** Appends block headers to the default blockheader file */
|
||||
def append(headers: Seq[BlockHeader]): Unit =
|
||||
append(headers, Constants.blockHeaderFile)
|
||||
|
||||
/** Reads block headers from the given file */
|
||||
def read(file: java.io.File): Seq[BlockHeader] =
|
||||
(for {
|
||||
line <- Source.fromFile(file).getLines()
|
||||
} yield BlockHeader(line)).toSeq
|
||||
|
||||
/** Reads block headers from the default [[BlockHeader]] file */
|
||||
def read: Seq[BlockHeader] = read(Constants.blockHeaderFile)
|
||||
|
||||
/** Returns the last [[BlockHeader]] in the block header store */
|
||||
def lastHeader: Option[BlockHeader] = lastHeader(Constants.blockHeaderFile)
|
||||
|
||||
/** Returns the last [[BlockHeader]] in the block header store */
|
||||
def lastHeader(file: java.io.File): Option[BlockHeader] = {
|
||||
val headers = read(file)
|
||||
if (headers.isEmpty) None else Some(headers.last)
|
||||
}
|
||||
|
||||
private def printToFile(f: java.io.File)(
|
||||
op: java.io.PrintWriter => Unit): Unit = {
|
||||
val p = new java.io.PrintWriter(new FileOutputStream(f, true))
|
||||
try { op(p) } finally { p.close() }
|
||||
}
|
||||
}
|
||||
|
||||
object BlockHeaderStore extends BlockHeaderStore
|
Loading…
Add table
Reference in a new issue