2019-08-23 20:53:00 +02:00
---
2019-09-02 15:16:44 +02:00
title: Wallet
id: wallet
2019-08-23 20:53:00 +02:00
---
2019-12-26 19:12:08 -06:00
## Bitcoin-s wallet
Bitcoin-s comes bundled with a rudimentary Bitcoin wallet. This wallet
2019-09-02 15:16:44 +02:00
is capable of managing private keys, generating addresses, constructing
and signing transactions, among other things. It is BIP32/BIP44/BIP49/BIP84
compatible.
This wallet 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.
2020-03-15 12:39:01 -05:00
### Disclaimer
The wallet api will changing significantly in the next release of bitcoin-s. EXPECT API BREAKING CHANGES and
surprising behavior from the current wallet..
2019-12-26 19:12:08 -06:00
### How is the bitcoin-s wallet implemented
2019-09-02 15:16:44 +02:00
2019-12-26 19:12:08 -06:00
The bitcoin-s wallet is a scalable way for individuals up to large bitcoin exchanges to safely and securely store their bitcoin in a scalable way.
All key interactions are delegated to the [key-manager ](key-manager.md ) which is a minimal dependecy library to store and use key material.
By default, we store the encrypted root key in `$HOME/.bitcoin-s/encrypted-bitcoin-s-seed.json` . This is the seed that is used for each of the wallets on each bitcoin network.
The wallet itself is used to manage the utxo life cycle, create transactions, and update wallet balances to show how much money you have the on a bitcoin network.
We use [slick ](https://scala-slick.org/doc/3.3.1/ ) as middleware to support different database types. Depending on your use case, you can use something as simple as sqlite, or something much more scalable like postgres.
### Example
This guide shows how to create a Bitcoin-s wallet and then
2019-08-23 20:53:00 +02:00
peer it with a `bitcoind` instance that relays
information about what is happening on the blockchain
through the P2P network.
This is useful if you want more flexible signing procedures in
the JVM ecosystem and more granular control over your
UTXOs with popular database like Postgres, SQLite, etc.
This code snippet you have a running `bitcoind` instance, locally
on regtest.
2020-03-10 12:49:22 -05:00
```scala mdoc:invisible
import org.bitcoins.chain.blockchain.ChainHandler
import org.bitcoins.chain.blockchain.sync.ChainSync
import org.bitcoins.chain.config.ChainAppConfig
import org.bitcoins.chain.api.ChainApi
import org.bitcoins.chain.models._
import org.bitcoins.core.api._
import ChainQueryApi._
import org.bitcoins.core.crypto._
import org.bitcoins.core.protocol._
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.currency._
import org.bitcoins.keymanager.bip39._
import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.rpc.config.BitcoindInstance
import org.bitcoins.wallet.config.WalletAppConfig
2020-04-28 13:58:14 -05:00
import org.bitcoins.wallet.api.WalletApi
2020-03-10 12:49:22 -05:00
import org.bitcoins.wallet.Wallet
import com.typesafe.config.ConfigFactory
import java.nio.file.Files
2020-04-29 09:49:41 -05:00
import java.time.Instant
2020-03-10 12:49:22 -05:00
import scala.concurrent._
```
2019-08-23 20:53:00 +02:00
```scala mdoc:compile-only
2019-08-30 22:12:18 +02:00
implicit val ec = scala.concurrent.ExecutionContext.global
2019-08-23 20:53:00 +02:00
2020-03-10 12:49:22 -05:00
2019-08-23 20:53:00 +02:00
val config = ConfigFactory.parseString {
"""
| bitcoin-s {
| network = regtest
| }
""".stripMargin
}
2020-03-10 12:49:22 -05:00
2019-08-23 20:53:00 +02:00
val datadir = Files.createTempDirectory("bitcoin-s-wallet")
2020-03-10 12:49:22 -05:00
2019-08-23 20:53:00 +02:00
implicit val walletConfig = WalletAppConfig(datadir, config)
// we also need to store chain state for syncing purposes
implicit val chainConfig = ChainAppConfig(datadir, config)
// when this future completes, we have
// created the necessary directories and
// databases for managing both chain state
// and wallet state
val configF: Future[Unit] = for {
_ < - walletConfig . initialize ( )
_ < - chainConfig . initialize ( )
} yield ()
val bitcoindInstance = BitcoindInstance.fromDatadir()
2019-08-30 22:12:18 +02:00
val bitcoind = BitcoindRpcClient(bitcoindInstance)
2019-08-23 20:53:00 +02:00
// when this future completes, we have
// synced our chain handler to our bitcoind
// peer
val syncF: Future[ChainApi] = configF.flatMap { _ =>
val getBestBlockHashFunc = { () =>
bitcoind.getBestBlockHash
}
2020-03-10 12:49:22 -05:00
2019-08-23 20:53:00 +02:00
val getBlockHeaderFunc = { hash: DoubleSha256DigestBE =>
bitcoind.getBlockHeader(hash).map(_.blockHeader)
}
val blockHeaderDAO = BlockHeaderDAO()
2019-09-25 11:18:51 -07:00
val compactFilterHeaderDAO = CompactFilterHeaderDAO()
val compactFilterDAO = CompactFilterDAO()
2019-08-23 20:53:00 +02:00
val chainHandler = ChainHandler(
blockHeaderDAO,
2019-09-25 11:18:51 -07:00
compactFilterHeaderDAO,
compactFilterDAO,
blockchains = Vector.empty,
blockFilterCheckpoints = Map.empty)
2019-08-23 20:53:00 +02:00
ChainSync.sync(chainHandler, getBlockHeaderFunc, getBestBlockHashFunc)
}
2019-12-26 19:12:08 -06:00
//initialize our key manager, where we store our keys
2020-01-03 11:03:45 -06:00
//you can add a password here if you want
//val bip39PasswordOpt = Some("my-password-here")
val bip39PasswordOpt = None
val keyManager = BIP39KeyManager.initialize(walletConfig.kmParams, bip39PasswordOpt).getOrElse {
2019-12-26 19:12:08 -06:00
throw new RuntimeException(s"Failed to initalize key manager")
}
2019-08-23 20:53:00 +02:00
// once this future completes, we have a initialized
// wallet
2020-01-02 11:18:41 -08:00
val wallet = Wallet(keyManager, new NodeApi {
2020-04-21 13:14:02 -05:00
override def broadcastTransaction(tx: Transaction): Future[Unit] = Future.successful(())
2020-01-02 11:18:41 -08:00
override def downloadBlocks(blockHashes: Vector[DoubleSha256Digest]): Future[Unit] = Future.successful(())
}, new ChainQueryApi {
2020-04-29 09:49:41 -05:00
override def epochSecondToBlockHeight(time: Long): Future[Int] = Future.successful(0)
2020-01-02 11:18:41 -08:00
override def getBlockHeight(blockHash: DoubleSha256DigestBE): Future[Option[Int]] = Future.successful(None)
override def getBestBlockHash(): Future[DoubleSha256DigestBE] = Future.successful(DoubleSha256DigestBE.empty)
override def getNumberOfConfirmations(blockHashOpt: DoubleSha256DigestBE): Future[Option[Int]] = Future.successful(None)
override def getFilterCount: Future[Int] = Future.successful(0)
override def getHeightByBlockStamp(blockStamp: BlockStamp): Future[Int] = Future.successful(0)
override def getFiltersBetweenHeights(startHeight: Int, endHeight: Int): Future[Vector[FilterResponse]] = Future.successful(Vector.empty)
2020-04-29 09:49:41 -05:00
}, creationTime = Instant.now)
2020-04-28 13:58:14 -05:00
val walletF: Future[WalletApi] = configF.flatMap { _ =>
2020-01-03 11:03:45 -06:00
Wallet.initialize(wallet,bip39PasswordOpt)
2019-08-23 20:53:00 +02:00
}
// when this future completes, ww have sent a transaction
// from bitcoind to the Bitcoin-S wallet
2019-12-14 12:06:22 -08:00
val transactionF: Future[(Transaction, Option[DoubleSha256DigestBE])] = for {
2019-08-23 20:53:00 +02:00
wallet < - walletF
address < - wallet . getNewAddress ( )
txid < - bitcoind . sendToAddress ( address , 3 . bitcoin )
transaction < - bitcoind . getRawTransaction ( txid )
2019-12-14 12:06:22 -08:00
} yield (transaction.hex, transaction.blockhash)
2019-08-23 20:53:00 +02:00
// when this future completes, we have processed
// the transaction from bitcoind, and we have
// queried our balance for the current balance
val balanceF: Future[CurrencyUnit] = for {
wallet < - walletF
2019-12-14 12:06:22 -08:00
(tx, blockhash) < - transactionF
_ < - wallet . processTransaction ( tx , blockhash )
2019-08-23 20:53:00 +02:00
balance < - wallet . getBalance
} yield balance
balanceF.foreach { balance =>
println(s"Bitcoin-S wallet balance: $balance")
}
```