mirror of
https://github.com/ACINQ/eclair.git
synced 2024-11-20 10:39:19 +01:00
Added an optional seed to Setup (#424)
If this seed is not provided, it is generated and stored in a seed.dat file. The electrum watcher uses this seed for its key.
This commit is contained in:
parent
7a6fa8a619
commit
6719c2d8f1
@ -84,17 +84,21 @@ object NodeParams {
|
||||
.withFallback(overrideDefaults)
|
||||
.withFallback(ConfigFactory.load()).getConfig("eclair")
|
||||
|
||||
def makeNodeParams(datadir: File, config: Config): NodeParams = {
|
||||
def makeNodeParams(datadir: File, config: Config, seed_opt: Option[BinaryData] = None): NodeParams = {
|
||||
|
||||
datadir.mkdirs()
|
||||
|
||||
val seedPath = new File(datadir, "seed.dat")
|
||||
val seed: BinaryData = seedPath.exists() match {
|
||||
case true => Files.readAllBytes(seedPath.toPath)
|
||||
case false =>
|
||||
val seed = randomKey.toBin
|
||||
Files.write(seedPath.toPath, seed)
|
||||
seed
|
||||
val seed: BinaryData = seed_opt match {
|
||||
case Some(s) => s
|
||||
case None =>
|
||||
val seedPath = new File(datadir, "seed.dat")
|
||||
seedPath.exists() match {
|
||||
case true => Files.readAllBytes(seedPath.toPath)
|
||||
case false =>
|
||||
val seed = randomKey.toBin
|
||||
Files.write(seedPath.toPath, seed)
|
||||
seed
|
||||
}
|
||||
}
|
||||
val master = DeterministicWallet.generate(seed)
|
||||
val extendedPrivateKey = DeterministicWallet.derivePrivateKey(master, DeterministicWallet.hardened(46) :: DeterministicWallet.hardened(0) :: Nil)
|
||||
|
@ -30,15 +30,22 @@ import scala.concurrent.duration._
|
||||
import scala.concurrent.{Await, ExecutionContext, Future, Promise}
|
||||
|
||||
/**
|
||||
* Setup eclair from a datadir.
|
||||
* <p>
|
||||
* Created by PM on 25/01/2016.
|
||||
*
|
||||
* @param datadir directory where eclair-core will write/read its data
|
||||
* @param overrideDefaults
|
||||
* @param actorSystem
|
||||
* @param seed_opt optional seed, if set eclair will use it instead of generating one and won't create a seed.dat file.
|
||||
*/
|
||||
class Setup(datadir: File, overrideDefaults: Config = ConfigFactory.empty(), actorSystem: ActorSystem = ActorSystem()) extends Logging {
|
||||
class Setup(datadir: File, overrideDefaults: Config = ConfigFactory.empty(), actorSystem: ActorSystem = ActorSystem(), seed_opt: Option[BinaryData] = None) extends Logging {
|
||||
|
||||
logger.info(s"hello!")
|
||||
logger.info(s"version=${getClass.getPackage.getImplementationVersion} commit=${getClass.getPackage.getSpecificationVersion}")
|
||||
|
||||
val config: Config = NodeParams.loadConfiguration(datadir, overrideDefaults)
|
||||
val nodeParams: NodeParams = NodeParams.makeNodeParams(datadir, config)
|
||||
val nodeParams: NodeParams = NodeParams.makeNodeParams(datadir, config, seed_opt)
|
||||
val chain: String = config.getString("chain")
|
||||
|
||||
// early checks
|
||||
@ -140,10 +147,11 @@ class Setup(datadir: File, overrideDefaults: Config = ConfigFactory.empty(), act
|
||||
val wallet = bitcoin match {
|
||||
case Bitcoind(bitcoinClient) => new BitcoinCoreWallet(bitcoinClient.rpcClient)
|
||||
case Bitcoinj(bitcoinj) => new BitcoinjWallet(bitcoinj.initialized.map(_ => bitcoinj.wallet()))
|
||||
case Electrum(electrumClient) =>
|
||||
val electrumSeedPath = new File(datadir, "electrum_seed.dat")
|
||||
val electrumWallet = system.actorOf(ElectrumWallet.props(electrumSeedPath, electrumClient, ElectrumWallet.WalletParameters(Block.RegtestGenesisBlock.hash, allowSpendUnconfirmed = true)), "electrum-wallet")
|
||||
new ElectrumEclairWallet(electrumWallet)
|
||||
case Electrum(electrumClient) => seed_opt match {
|
||||
case Some(seed) => val electrumWallet = system.actorOf(ElectrumWallet.props(seed, electrumClient, ElectrumWallet.WalletParameters(Block.TestnetGenesisBlock.hash)), "electrum-wallet")
|
||||
new ElectrumEclairWallet(electrumWallet)
|
||||
case _ => throw new RuntimeException("electrum wallet requires a seed to set up")
|
||||
}
|
||||
}
|
||||
wallet.getFinalAddress.map {
|
||||
case address => logger.info(s"initial wallet address=$address")
|
||||
|
@ -63,7 +63,5 @@ class ElectrumEclairWallet(val wallet: ActorRef)(implicit system: ActorSystem, e
|
||||
}
|
||||
}
|
||||
|
||||
def getMnemonics: Future[Seq[String]] = (wallet ? GetMnemonicCode).mapTo[GetMnemonicCodeResponse].map(_.mnemonics)
|
||||
|
||||
override def rollback(tx: Transaction): Future[Boolean] = (wallet ? CancelTransaction(tx)).map(_ => true)
|
||||
}
|
||||
|
@ -1,15 +1,11 @@
|
||||
package fr.acinq.eclair.blockchain.electrum
|
||||
|
||||
import java.io.File
|
||||
|
||||
import akka.actor.{ActorRef, LoggingFSM, Props}
|
||||
import com.google.common.io.Files
|
||||
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
|
||||
import fr.acinq.bitcoin.DeterministicWallet.{ExtendedPrivateKey, derivePrivateKey, hardened}
|
||||
import fr.acinq.bitcoin.{Base58, Base58Check, BinaryData, Block, Crypto, DeterministicWallet, MnemonicCode, OP_PUSHDATA, OutPoint, SIGHASH_ALL, Satoshi, Script, ScriptFlags, ScriptWitness, SigVersion, Transaction, TxIn, TxOut}
|
||||
import fr.acinq.bitcoin.{Base58, Base58Check, BinaryData, Block, Crypto, DeterministicWallet, OP_PUSHDATA, OutPoint, SIGHASH_ALL, Satoshi, Script, ScriptWitness, SigVersion, Transaction, TxIn, TxOut}
|
||||
import fr.acinq.eclair.blockchain.bitcoind.rpc.Error
|
||||
import fr.acinq.eclair.blockchain.electrum.ElectrumClient.{GetTransaction, GetTransactionResponse, TransactionHistoryItem, computeScriptHash}
|
||||
import fr.acinq.eclair.randomBytes
|
||||
import fr.acinq.eclair.transactions.Transactions
|
||||
import grizzled.slf4j.Logging
|
||||
|
||||
@ -28,16 +24,15 @@ import scala.util.{Failure, Success, Try}
|
||||
* client <--- ask tx ----- wallet
|
||||
* client ---- tx ----> wallet
|
||||
*
|
||||
* @param mnemonics
|
||||
* @param seed
|
||||
* @param client
|
||||
* @param params
|
||||
*/
|
||||
class ElectrumWallet(mnemonics: Seq[String], client: ActorRef, params: ElectrumWallet.WalletParameters) extends LoggingFSM[ElectrumWallet.State, ElectrumWallet.Data] {
|
||||
class ElectrumWallet(seed: BinaryData, client: ActorRef, params: ElectrumWallet.WalletParameters) extends LoggingFSM[ElectrumWallet.State, ElectrumWallet.Data] {
|
||||
|
||||
import ElectrumWallet._
|
||||
import params._
|
||||
|
||||
val seed = MnemonicCode.toSeed(mnemonics, "")
|
||||
val master = DeterministicWallet.generate(seed)
|
||||
|
||||
val accountMaster = accountKey(master)
|
||||
@ -241,7 +236,6 @@ class ElectrumWallet(mnemonics: Seq[String], client: ActorRef, params: ElectrumW
|
||||
}
|
||||
|
||||
whenUnhandled {
|
||||
case Event(GetMnemonicCode, _) => stay replying GetMnemonicCodeResponse(mnemonics)
|
||||
|
||||
case Event(GetCurrentReceiveAddress, data) => stay replying GetCurrentReceiveAddressResponse(data.currentReceiveAddress)
|
||||
|
||||
@ -263,20 +257,7 @@ object ElectrumWallet {
|
||||
// use 32 bytes seed, which will generate a 24 words mnemonic code
|
||||
val SEED_BYTES_LENGTH = 32
|
||||
|
||||
def props(mnemonics: Seq[String], client: ActorRef, params: WalletParameters): Props = Props(new ElectrumWallet(mnemonics, client, params))
|
||||
|
||||
def props(file: File, client: ActorRef, params: WalletParameters): Props = {
|
||||
val entropy: BinaryData = (file.exists(), file.canRead(), file.isFile) match {
|
||||
case (true, true, true) => Files.toByteArray(file)
|
||||
case (false, _, _) =>
|
||||
val buffer = randomBytes(SEED_BYTES_LENGTH)
|
||||
Files.write(buffer, file)
|
||||
buffer
|
||||
case _ => throw new IllegalArgumentException(s"cannot create wallet:$file exist but cannot read from")
|
||||
}
|
||||
val mnemonics = MnemonicCode.toMnemonics(entropy)
|
||||
Props(new ElectrumWallet(mnemonics, client, params))
|
||||
}
|
||||
def props(seed: BinaryData, client: ActorRef, params: WalletParameters): Props = Props(new ElectrumWallet(seed, client, params))
|
||||
|
||||
case class WalletParameters(chainHash: BinaryData, minimumFee: Satoshi = Satoshi(2000), dustLimit: Satoshi = Satoshi(546), swipeRange: Int = 10, allowSpendUnconfirmed: Boolean = true)
|
||||
|
||||
@ -289,9 +270,6 @@ object ElectrumWallet {
|
||||
sealed trait Request
|
||||
sealed trait Response
|
||||
|
||||
case object GetMnemonicCode extends RuntimeException
|
||||
case class GetMnemonicCodeResponse(mnemonics: Seq[String]) extends Response
|
||||
|
||||
case object GetBalance extends Request
|
||||
case class GetBalanceResponse(confirmed: Satoshi, unconfirmed: Satoshi) extends Response
|
||||
|
||||
|
@ -6,8 +6,9 @@ import fr.acinq.bitcoin.{BinaryData, Block, MnemonicCode, Satoshi}
|
||||
import fr.acinq.eclair.blockchain.electrum.ElectrumClient.{ScriptHashSubscription, ScriptHashSubscriptionResponse}
|
||||
import fr.acinq.eclair.blockchain.electrum.ElectrumWallet.{NewWalletReceiveAddress, WalletEvent, WalletParameters, WalletReady}
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.{BeforeAndAfterAll, FunSuite, FunSuiteLike}
|
||||
import org.scalatest.FunSuiteLike
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
@ -26,7 +27,7 @@ class ElectrumWalletSimulatedClientSpec extends TestKit(ActorSystem("test")) wit
|
||||
|
||||
val listener = TestProbe()
|
||||
system.eventStream.subscribe(listener.ref, classOf[WalletEvent])
|
||||
val wallet = TestFSMRef(new ElectrumWallet(mnemonics, system.actorOf(Props(new SimulatedClient())), WalletParameters(Block.RegtestGenesisBlock.hash, minimumFee = Satoshi(5000))))
|
||||
val wallet = TestFSMRef(new ElectrumWallet(seed, system.actorOf(Props(new SimulatedClient())), WalletParameters(Block.RegtestGenesisBlock.hash, minimumFee = Satoshi(5000))))
|
||||
|
||||
// wallet sends a receive address notification as soon as it is created
|
||||
listener.expectMsgType[NewWalletReceiveAddress]
|
||||
|
@ -21,7 +21,7 @@ class ElectrumWalletSpec extends IntegrationSpec {
|
||||
var wallet: ActorRef = _
|
||||
|
||||
test("wait until wallet is ready") {
|
||||
wallet = system.actorOf(Props(new ElectrumWallet(mnemonics, electrumClient, WalletParameters(Block.RegtestGenesisBlock.hash, minimumFee = Satoshi(5000)))), "wallet")
|
||||
wallet = system.actorOf(Props(new ElectrumWallet(seed, electrumClient, WalletParameters(Block.RegtestGenesisBlock.hash, minimumFee = Satoshi(5000)))), "wallet")
|
||||
val probe = TestProbe()
|
||||
awaitCond({
|
||||
probe.send(wallet, GetData)
|
||||
|
Loading…
Reference in New Issue
Block a user