4.8 KiB
id | title |
---|---|
node | Light Client |
Bitcoin-s has node module that allows you to connect to the p2p network.
Neutrino Node
Bitcoin-s has experimental support for neutrino which is a new lite client proposal on the bitcoin p2p network. You can read more about how neutrino works here.
Callbacks
Bitcoin-S support call backs for the following events that happen on the bitcoin p2p network:
- onTxReceived
- onBlockReceived
- onMerkleBlockReceived
- onCompactFilterReceived
That means every time one of these events happens on the p2p network, we will call your callback so that you can be notified of the event. Let's make a easy one
Example
Here is an example of constructing a neutrino node and registering a callback so you can be notified of an event.
To run the example, we need a bitcoind binary that has neutrino support. Unforunately bitcoin core has not merged neutrino
p2p network support yet (pr here) which means that we have built a custom binary and host it ourselves. You need
to make sure to run sbt downloadBitcoind
and then look for the bitcoind
binary with neutrino support in
$HOME/.bitcoin-s/binaries/bitcoind/bitcoin-0.18.99/
. This binary is built from the open PR on bitcoin core.
import akka.actor.ActorSystem
import org.bitcoins.core.protocol.blockchain.Block
import org.bitcoins.node._
import org.bitcoins.rpc.client.common.BitcoindVersion
import org.bitcoins.testkit.node._
import org.bitcoins.testkit.node.fixture._
import org.bitcoins.testkit.rpc._
import org.bitcoins.server.BitcoinSAppConfig
import org.bitcoins.testkit._
import org.bitcoins.testkit.chain._
import scala.concurrent._
import scala.concurrent.duration._
import java.nio.file.Files
import com.typesafe.config.ConfigFactory
implicit val system = ActorSystem(s"node-example")
implicit val ec = system.dispatcher
//we also require a bitcoind instance to connect to
//so let's start one (make sure you ran 'sbt downloadBitcoind')
val instance = BitcoindRpcTestUtil.instance(versionOpt = Some(BitcoindVersion.Experimental))
val p2pPort = instance.p2pPort
val bitcoindF = BitcoindRpcTestUtil.startedBitcoindRpcClient(instance)
//contains information on how to connect to bitcoin's p2p info
val peerF = bitcoindF.map(b => NodeUnitTest.createPeer(b))
// set a data directory
val prefix = s"node-example-${System.currentTimeMillis()}"
val datadir = Files.createTempDirectory(prefix)
val tmpDir = BitcoinSTestAppConfig.tmpDir()
// set the current network to regtest
val config = ConfigFactory.parseString {
s"""
| bitcoin-s {
| network = regtest
| node {
| mode = neutrino # neutrino, spv
|
| peers = ["127.0.0.1:$p2pPort"] # a list of peer addresses in form "hostname:portnumber"
| # (e.g. "neutrino.testnet3.suredbits.com:18333")
| # Port number is optional, the default value is 8333 for mainnet,
| # 18333 for testnet and 18444 for regtest.
| }
| }
|""".stripMargin
}
implicit val appConfig = BitcoinSAppConfig(datadir, config)
implicit val chainConfig = appConfig.chainConf
implicit val nodeConfig = appConfig.nodeConf
val initNodeF = nodeConfig.initialize()
//the node requires a chainHandler to store block information
//use a helper method in our testkit to create the chain project
val chainApiF = for {
chainHandler <- ChainUnitTest.createChainHandler()
} yield chainHandler
//yay! All setup done, let's create a node and then start it!
val nodeF = for {
_ <- chainApiF
peer <- peerF
} yield {
NeutrinoNode(nodePeer = peer,
nodeConfig = nodeConfig,
chainConfig = chainConfig,
actorSystem = system)
}
//let's start it
val startedNodeF = nodeF.flatMap(_.start())
//let's make a simple callback that print's the
//blockhash everytime we receive a block on the network
val blockReceivedFunc = { block: Block =>
println(s"Received blockhash=${block.blockHeader.hashBE}")
}
val nodeCallbacks = NodeCallbacks.onBlockReceived(blockReceivedFunc)
//ok, now we need to add this allback to our running node
val nodeWithCallbackF = for {
node <- startedNodeF
withCallback = node.addCallbacks(nodeCallbacks)
} yield withCallback
//let's test it out by generating a block with bitcoind!
val genBlockF = for {
bitcoind <- bitcoindF
addr <- bitcoind.getNewAddress
hashes <- bitcoind.generateToAddress(1,addr)
} yield ()
//you should see our callback print a block hash
//when running this code
//cleanup
val cleanupF = for {
_ <- genBlockF
bitcoind <- bitcoindF
node <- nodeWithCallbackF
x = NeutrinoNodeConnectedWithBitcoind(node.asInstanceOf[NeutrinoNode],bitcoind)
_ <- NodeUnitTest.destroyNodeConnectedWithBitcoind(x)
} yield ()
Await.result(cleanupF, 60.seconds)