2020 03 13 node md (#1229)

* Implement example of starting a neutrino node and example of adding a callback to node.md

Fix port number

* Clean up some names in the example

* Make sure we get the correct bitcoind binary with neutrino p2p support in the example
This commit is contained in:
Chris Stewart 2020-03-14 08:49:39 -05:00 committed by GitHub
parent 5bb31f4951
commit e598d4c37e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -3,11 +3,147 @@ id: node
title: Light Client
---
Bitcoin-S comes bundled with a light client Bitcoin node. this client
is capable of doing verification of some parts of the blockchain,
and can act as a starting point for building Bitcoin applications
that need to connect to the P2P network.
Bitcoin-s has node module that allows you to connect to the p2p network.
This node is currently only released as a library, and not as a binary.
This is because it (nor the documentation) is not deemed production
ready. Use at your own risk, and without too much money depending on it.
### 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](https://suredbits.com/neutrino-what-is-it-and-why-we-need-it/).
#### Callbacks
Bitcoin-S support call backs for the following events that happen on the bitcoin p2p network:
1. onTxReceived
2. onBlockReceived
3. onMerkleBlockReceived
4. 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](https://github.com/bitcoin/bitcoin/pull/16442)) 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.
```scala mdoc:invisible
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
```
```scala mdoc:compile-only
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)
```