bitcoin-s/docs/node/node-api.md
Chris Stewart 132479d271
Implement ability to provide external entropy to bitcoin-s (#3672)
* Encapsulate initialization of DLCOracle.start() method

* Use internal WalletAppConfig.kmConf rather than passing in custom key manager parameters

* Add KeyManagerAppConfig.defaultAccountType

* Get all tests passing besides TrezorAddressTest

* Get TrezorAddressTest passing with provided entropy

* Add unit test to make sure we can always derive the seed

* Get docs compiling

* Fix dlcWalletTest test cases

* Add more test cases to keymanager

* Add the new configuration to the example configuration

* Add more test cases

* Remove coverage on 2.12 as it isn't accurate

* Rework DLCOracleAppConfig.start() to call kmConf.start() so the oracle can use entropy provided via bitcoin-s.conf
2021-09-18 09:49:11 -05:00

4 KiB


id: node-api title: Node API

import akka.actor.ActorSystem
import org.bitcoins.core.api.node._
import org.bitcoins.crypto._
import org.bitcoins.core.protocol.blockchain.Block
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.wallet.fee._
import org.bitcoins.core.util._
import org.bitcoins.feeprovider._
import org.bitcoins.node._
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
import org.bitcoins.rpc.config._
import org.bitcoins.testkit.BitcoinSTestAppConfig
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
import org.bitcoins.wallet.Wallet
import org.bitcoins.wallet.config.WalletAppConfig


import scala.concurrent.{ExecutionContextExecutor, Future}

NodeAPI

The NodeApi is how the wallet project retrieves relevant node data like blocks. This allows the wallet for example to retrieve blocks for finding its relevant transactions.

Since this is an API it can be hooked up to the node module of bitcoin-s but it can also be linked to any other implementation of your choosing. This allows you to use the bitcoin-s wallet in any schema that you want.

The functions that the NodeApi supports are:

trait NodeApi {

  /** Request the underlying node to download the given blocks from its peers and feed the blocks to [[org.bitcoins.node.NodeCallbacks]] */
    def downloadBlocks(blockHashes: Vector[DoubleSha256Digest]): Future[Unit]
}

Downloading blocks with bitcoind

As an example, we will show you how to use the NodeApi and bitcoind to download blocks for a wallet.

implicit val system: ActorSystem = ActorSystem(s"node-api-example")
implicit val ec: ExecutionContextExecutor = system.dispatcher
implicit val walletConf: WalletAppConfig =
    BitcoinSTestAppConfig.getSpvTestConfig().walletConf

// let's use a helper method to get a v19 bitcoind
// and a ChainApi
val instance = BitcoindInstanceLocal.fromConfigFile(BitcoindConfig.DEFAULT_CONF_FILE)
val bitcoind = BitcoindV19RpcClient(instance)
val chainApi = BitcoinSWalletTest.MockChainQueryApi
val aesPasswordOpt = Some(AesPassword.fromString("password"))

// This function can be used to create a callback for when our node api calls downloadBlocks,
// more specifically it will call the function every time we receive a block, the returned
// NodeCallbacks will contain the necessary items to initialize the callbacks
def createCallback(processBlock: Block => Future[Unit]): NodeCallbacks = {
    lazy val onBlock: OnBlockReceived = { block =>
      processBlock(block)
    }
    NodeCallbacks(onBlockReceived = Vector(onBlock))
  }

// Here is a super simple example of a callback, this could be replaced with anything, from
// relaying the block on the network, finding relevant wallet transactions, verifying the block,
// or writing it to disk
val exampleProcessBlock = (block: Block) =>
    Future.successful(println(s"Received block: ${block.blockHeader.hashBE}"))
val exampleCallback = createCallback(exampleProcessBlock)

// Here is where we are defining our actual node api, Ideally this could be it's own class
// but for the examples sake we will keep it small.
  val nodeApi = new NodeApi {

    override def broadcastTransactions(transactions: Vector[Transaction]): Future[Unit] = {
            FutureUtil.sequentially(transactions)(bitcoind.sendRawTransaction(_)).map(_ => ())
    }

    override def downloadBlocks(
        blockHashes: Vector[DoubleSha256Digest]): Future[Unit] = {
      val blockFs = blockHashes.map(hash => bitcoind.getBlockRaw(hash))
      Future.sequence(blockFs).map(_ => ())
    }
  }

// Finally, we can initialize our wallet with our own node api
val wallet =
    Wallet(nodeApi = nodeApi, chainQueryApi = chainApi, feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one))

// Then to trigger the event we can run
val exampleBlock = DoubleSha256Digest(
    "000000000010dc23dc0d5acad64667a7a2b3010b6e02da4868bf392c90b6431d")
wallet.nodeApi.downloadBlocks(Vector(exampleBlock))