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-09-02 15:16:44 +02:00
|
|
|
Bitcoin-S comes bundled with a rudimentary Bitcoin wallet. This wallet
|
|
|
|
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.
|
|
|
|
|
|
|
|
## Creating a wallet
|
|
|
|
|
2019-08-23 20:53:00 +02:00
|
|
|
This guide shows how to create a Bitcoin-S wallet and then
|
|
|
|
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.
|
|
|
|
|
|
|
|
```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
|
|
|
|
|
|
|
import com.typesafe.config.ConfigFactory
|
|
|
|
val config = ConfigFactory.parseString {
|
|
|
|
"""
|
|
|
|
| bitcoin-s {
|
|
|
|
| network = regtest
|
|
|
|
| }
|
|
|
|
""".stripMargin
|
|
|
|
}
|
|
|
|
|
|
|
|
import java.nio.file.Files
|
|
|
|
val datadir = Files.createTempDirectory("bitcoin-s-wallet")
|
|
|
|
|
|
|
|
import org.bitcoins.wallet.config.WalletAppConfig
|
|
|
|
implicit val walletConfig = WalletAppConfig(datadir, config)
|
|
|
|
|
|
|
|
// we also need to store chain state for syncing purposes
|
|
|
|
import org.bitcoins.chain.config.ChainAppConfig
|
|
|
|
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
|
|
|
|
import scala.concurrent._
|
|
|
|
val configF: Future[Unit] = for {
|
|
|
|
_ <- walletConfig.initialize()
|
|
|
|
_ <- chainConfig.initialize()
|
|
|
|
} yield ()
|
|
|
|
|
|
|
|
import org.bitcoins.rpc.config.BitcoindInstance
|
|
|
|
val bitcoindInstance = BitcoindInstance.fromDatadir()
|
|
|
|
|
|
|
|
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
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
|
|
|
|
import org.bitcoins.chain.api.ChainApi
|
|
|
|
val syncF: Future[ChainApi] = configF.flatMap { _ =>
|
|
|
|
val getBestBlockHashFunc = { () =>
|
|
|
|
bitcoind.getBestBlockHash
|
|
|
|
}
|
|
|
|
|
|
|
|
import org.bitcoins.core.crypto.DoubleSha256DigestBE
|
|
|
|
val getBlockHeaderFunc = { hash: DoubleSha256DigestBE =>
|
|
|
|
bitcoind.getBlockHeader(hash).map(_.blockHeader)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-25 11:18:51 -07:00
|
|
|
import org.bitcoins.chain.models._
|
2019-08-23 20:53:00 +02:00
|
|
|
import org.bitcoins.chain.blockchain.ChainHandler
|
|
|
|
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
|
|
|
|
|
|
|
import org.bitcoins.chain.blockchain.sync.ChainSync
|
|
|
|
ChainSync.sync(chainHandler, getBlockHeaderFunc, getBestBlockHashFunc)
|
|
|
|
}
|
|
|
|
|
|
|
|
// once this future completes, we have a initialized
|
|
|
|
// wallet
|
|
|
|
import org.bitcoins.wallet.api.LockedWalletApi
|
|
|
|
import org.bitcoins.wallet.api.InitializeWalletSuccess
|
|
|
|
import org.bitcoins.wallet.Wallet
|
2019-12-10 14:22:33 -08:00
|
|
|
import org.bitcoins.core.api._
|
2019-08-23 20:53:00 +02:00
|
|
|
val walletF: Future[LockedWalletApi] = configF.flatMap { _ =>
|
2019-12-10 14:22:33 -08:00
|
|
|
Wallet.initialize(NodeApi.NoOp, ChainQueryApi.NoOp).collect {
|
2019-08-23 20:53:00 +02:00
|
|
|
case InitializeWalletSuccess(wallet) => wallet
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// when this future completes, ww have sent a transaction
|
|
|
|
// from bitcoind to the Bitcoin-S wallet
|
2019-12-14 12:06:22 -08:00
|
|
|
import org.bitcoins.core.crypto._
|
|
|
|
import org.bitcoins.core.protocol.transaction._
|
2019-08-23 20:53:00 +02:00
|
|
|
import org.bitcoins.core.currency._
|
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")
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|