1
0
Fork 0
mirror of https://github.com/ACINQ/eclair.git synced 2025-03-15 12:20:13 +01:00

Merge branch 'master' into wip-android

This commit is contained in:
pm47 2018-04-03 20:42:37 +02:00
commit 35670424e6
No known key found for this signature in database
GPG key ID: E434ED292E85643A
20 changed files with 240 additions and 140 deletions

View file

@ -35,6 +35,7 @@ eclair {
72 = 20
}
}
min-feerate = 1 // minimum feerate in satoshis per byte (same default value as bitcoin core's minimum relay fee)
node-alias = "eclair"
node-color = "49daaa"

View file

@ -34,7 +34,7 @@ import fr.acinq.eclair.router._
import grizzled.slf4j.Logging
import scala.concurrent.duration._
import scala.concurrent.{ExecutionContext, Future}
import scala.concurrent.{ExecutionContext, Future, Promise}
/**
@ -67,9 +67,12 @@ class Setup(datadir: File, wallet_opt: Option[EclairWallet] = None, overrideDefa
implicit val formats = org.json4s.DefaultFormats
implicit val ec = ExecutionContext.Implicits.global
def bootstrap: Future[Kit] = Future {
def bootstrap: Future[Kit] =
for {
_ <- Future.successful(true)
feeratesRetrieved = Promise[Boolean]()
val bitcoin = nodeParams.watcherType match {
bitcoin = nodeParams.watcherType match {
case ELECTRUM =>
logger.warn("EXPERIMENTAL ELECTRUM MODE ENABLED!!!")
val addressesFile = nodeParams.chainHash match {
@ -84,52 +87,58 @@ class Setup(datadir: File, wallet_opt: Option[EclairWallet] = None, overrideDefa
case _ => ???
}
val defaultFeerates = FeeratesPerByte(block_1 = config.getLong("default-feerates.delay-blocks.1"), blocks_2 = config.getLong("default-feerates.delay-blocks.2"), blocks_6 = config.getLong("default-feerates.delay-blocks.6"), blocks_12 = config.getLong("default-feerates.delay-blocks.12"), blocks_36 = config.getLong("default-feerates.delay-blocks.36"), blocks_72 = config.getLong("default-feerates.delay-blocks.72"))
Globals.feeratesPerByte.set(defaultFeerates)
Globals.feeratesPerKw.set(FeeratesPerKw(defaultFeerates))
logger.info(s"initial feeratesPerByte=${Globals.feeratesPerByte.get()}")
val feeProvider = (nodeParams.chainHash, bitcoin) match {
defaultFeerates = FeeratesPerByte(
block_1 = config.getLong("default-feerates.delay-blocks.1"),
blocks_2 = config.getLong("default-feerates.delay-blocks.2"),
blocks_6 = config.getLong("default-feerates.delay-blocks.6"),
blocks_12 = config.getLong("default-feerates.delay-blocks.12"),
blocks_36 = config.getLong("default-feerates.delay-blocks.36"),
blocks_72 = config.getLong("default-feerates.delay-blocks.72")
)
minFeeratePerByte = config.getLong("min-feerate")
feeProvider = (nodeParams.chainHash, bitcoin) match {
case (Block.RegtestGenesisBlock.hash, _) => new ConstantFeeProvider(defaultFeerates)
case _ => new FallbackFeeProvider(new BitgoFeeProvider(nodeParams.chainHash) :: new EarnDotComFeeProvider() :: new ConstantFeeProvider(defaultFeerates) :: Nil) // order matters!
case _ => new FallbackFeeProvider(new BitgoFeeProvider(nodeParams.chainHash) :: new EarnDotComFeeProvider() :: new ConstantFeeProvider(defaultFeerates) :: Nil, minFeeratePerByte) // order matters!
}
system.scheduler.schedule(0 seconds, 10 minutes)(feeProvider.getFeerates.map {
_ = system.scheduler.schedule(0 seconds, 10 minutes)(feeProvider.getFeerates.map {
case feerates: FeeratesPerByte =>
Globals.feeratesPerByte.set(feerates)
Globals.feeratesPerKw.set(FeeratesPerKw(feerates))
system.eventStream.publish(CurrentFeerates(Globals.feeratesPerKw.get))
logger.info(s"current feeratesPerByte=${Globals.feeratesPerByte.get()}")
feeratesRetrieved.trySuccess(true)
})
_ <- feeratesRetrieved.future
val watcher = bitcoin match {
watcher = bitcoin match {
case Electrum(electrumClient) =>
system.actorOf(SimpleSupervisor.props(Props(new ElectrumWatcher(electrumClient)), "watcher", SupervisorStrategy.Resume))
case _ => ???
}
val wallet = bitcoin match {
wallet = bitcoin match {
case _ if wallet_opt.isDefined => wallet_opt.get
case Electrum(electrumClient) =>
val electrumWallet = system.actorOf(ElectrumWallet.props(seed, electrumClient, ElectrumWallet.WalletParameters(nodeParams.chainHash)), "electrum-wallet")
new ElectrumEclairWallet(electrumWallet, nodeParams.chainHash)
case _ => ???
}
wallet.getFinalAddress.map {
_ = wallet.getFinalAddress.map {
case address => logger.info(s"initial wallet address=$address")
}
val paymentHandler = system.actorOf(SimpleSupervisor.props(config.getString("payment-handler") match {
paymentHandler = system.actorOf(SimpleSupervisor.props(config.getString("payment-handler") match {
case "local" => LocalPaymentHandler.props(nodeParams)
case "noop" => Props[NoopPaymentHandler]
}, "payment-handler", SupervisorStrategy.Resume))
val register = system.actorOf(SimpleSupervisor.props(Props(new Register), "register", SupervisorStrategy.Resume))
val relayer = system.actorOf(SimpleSupervisor.props(Relayer.props(nodeParams, register, paymentHandler), "relayer", SupervisorStrategy.Resume))
val router = system.actorOf(SimpleSupervisor.props(Router.props(nodeParams, watcher), "router", SupervisorStrategy.Resume))
val authenticator = system.actorOf(SimpleSupervisor.props(Authenticator.props(nodeParams), "authenticator", SupervisorStrategy.Resume))
val switchboard = system.actorOf(SimpleSupervisor.props(Switchboard.props(nodeParams, authenticator, watcher, router, relayer, wallet), "switchboard", SupervisorStrategy.Resume))
val paymentInitiator = system.actorOf(SimpleSupervisor.props(PaymentInitiator.props(nodeParams.privateKey.publicKey, router, register), "payment-initiator", SupervisorStrategy.Restart))
register = system.actorOf(SimpleSupervisor.props(Props(new Register), "register", SupervisorStrategy.Resume))
relayer = system.actorOf(SimpleSupervisor.props(Relayer.props(nodeParams, register, paymentHandler), "relayer", SupervisorStrategy.Resume))
router = system.actorOf(SimpleSupervisor.props(Router.props(nodeParams, watcher), "router", SupervisorStrategy.Resume))
authenticator = system.actorOf(SimpleSupervisor.props(Authenticator.props(nodeParams), "authenticator", SupervisorStrategy.Resume))
switchboard = system.actorOf(SimpleSupervisor.props(Switchboard.props(nodeParams, authenticator, watcher, router, relayer, wallet), "switchboard", SupervisorStrategy.Resume))
paymentInitiator = system.actorOf(SimpleSupervisor.props(PaymentInitiator.props(nodeParams.privateKey.publicKey, router, register), "payment-initiator", SupervisorStrategy.Restart))
val kit = Kit(
kit = Kit(
nodeParams = nodeParams,
system = system,
watcher = watcher,
@ -140,9 +149,7 @@ class Setup(datadir: File, wallet_opt: Option[EclairWallet] = None, overrideDefa
switchboard = switchboard,
paymentInitiator = paymentInitiator,
wallet = wallet)
kit
}
} yield kit
}

View file

@ -18,7 +18,8 @@ package fr.acinq.eclair.blockchain.electrum
import akka.actor.{ActorRef, ActorSystem}
import akka.pattern.ask
import fr.acinq.bitcoin.{Base58, Base58Check, BinaryData, Block, OP_EQUAL, OP_HASH160, OP_PUSHDATA, Satoshi, Script, Transaction, TxOut}
import fr.acinq.bitcoin.{BinaryData, Satoshi, Script, Transaction, TxOut}
import fr.acinq.eclair.addressToPublicKeyScript
import fr.acinq.eclair.blockchain.electrum.ElectrumClient.BroadcastTransaction
import fr.acinq.eclair.blockchain.electrum.ElectrumWallet._
import fr.acinq.eclair.blockchain.{EclairWallet, MakeFundingTxResponse}
@ -62,13 +63,7 @@ class ElectrumEclairWallet(val wallet: ActorRef, chainHash: BinaryData)(implicit
}
def sendPayment(amount: Satoshi, address: String, feeRatePerKw: Long): Future[String] = {
val publicKeyScript = Base58Check.decode(address) match {
case (Base58.Prefix.PubkeyAddressTestnet, pubKeyHash) if chainHash == Block.RegtestGenesisBlock.hash || chainHash == Block.TestnetGenesisBlock.hash => Script.pay2pkh(pubKeyHash)
case (Base58.Prefix.PubkeyAddress, pubKeyHash) if chainHash == Block.LivenetGenesisBlock.hash => Script.pay2pkh(pubKeyHash)
case (Base58.Prefix.ScriptAddressTestnet, scriptHash) if chainHash == Block.RegtestGenesisBlock.hash || chainHash == Block.TestnetGenesisBlock.hash => OP_HASH160 :: OP_PUSHDATA(scriptHash) :: OP_EQUAL :: Nil
case (Base58.Prefix.ScriptAddress, scriptHash) if chainHash == Block.LivenetGenesisBlock.hash => OP_HASH160 :: OP_PUSHDATA(scriptHash) :: OP_EQUAL :: Nil
case _ => throw new RuntimeException("payment address does not match our blockchain")
}
val publicKeyScript = Script.write(addressToPublicKeyScript(address, chainHash))
val tx = Transaction(version = 2, txIn = Nil, txOut = TxOut(amount, publicKeyScript) :: Nil, lockTime = 0)
(wallet ? CompleteTransaction(tx, feeRatePerKw))
@ -83,11 +78,8 @@ class ElectrumEclairWallet(val wallet: ActorRef, chainHash: BinaryData)(implicit
}
def sendAll(address: String, feeRatePerKw: Long): Future[(Transaction, Satoshi)] = {
val publicKeyScript = Base58Check.decode(address) match {
case (Base58.Prefix.PubkeyAddressTestnet, pubKeyHash) => Script.pay2pkh(pubKeyHash)
case (Base58.Prefix.ScriptAddressTestnet, scriptHash) => OP_HASH160 :: OP_PUSHDATA(scriptHash) :: OP_EQUAL :: Nil
}
(wallet ? SendAll(Script.write(publicKeyScript), feeRatePerKw))
val publicKeyScript = Script.write(addressToPublicKeyScript(address, chainHash))
(wallet ? SendAll(publicKeyScript, feeRatePerKw))
.mapTo[SendAllResponse]
.map {
case SendAllResponse(tx, fee) => (tx, fee)

View file

@ -19,6 +19,7 @@ package fr.acinq.eclair.blockchain.fee
import akka.actor.ActorSystem
import fr.acinq.bitcoin.{BinaryData, Block}
import fr.acinq.eclair.HttpHelper.get
import fr.acinq.eclair.feerateKbToByte
import org.json4s.JsonAST.{JInt, JValue}
import scala.concurrent.{ExecutionContext, Future}
@ -47,7 +48,7 @@ object BitgoFeeProvider {
val blockTargets = json \ "feeByBlockTarget"
blockTargets.foldField(Seq.empty[BlockTarget]) {
// we divide by 1024 because bitgo returns estimates in Satoshi/Kb and we use estimates in Satoshi/Byte
case (list, (strBlockTarget, JInt(feePerKb))) => list :+ BlockTarget(strBlockTarget.toInt, feePerKb.longValue() / 1024)
case (list, (strBlockTarget, JInt(feePerKb))) => list :+ BlockTarget(strBlockTarget.toInt, feerateKbToByte(feePerKb.longValue()))
}
}

View file

@ -55,8 +55,8 @@ object EarnDotComFeeProvider {
def extractFeerate(feeRanges: Seq[FeeRange], maxBlockDelay: Int): Long = {
// first we keep only fee ranges with a max block delay below the limit
val belowLimit = feeRanges.filter(_.maxDelay <= maxBlockDelay)
// out of all the remaining fee ranges, we select the one with the minimum higher bound
belowLimit.minBy(_.maxFee).maxFee
// out of all the remaining fee ranges, we select the one with the minimum higher bound and make sure it is > 0
Math.max(belowLimit.minBy(_.maxFee).maxFee, 1)
}
def extractFeerates(feeRanges: Seq[FeeRange]): FeeratesPerByte =

View file

@ -20,10 +20,14 @@ import scala.concurrent.{ExecutionContext, Future}
/**
* This provider will try all child providers in sequence, until one of them works
*
* @param providers a sequence of providers; they will be tried one after the others until one of them succeeds
* @param minFeeratePerByte a configurable minimum value for feerates
*/
class FallbackFeeProvider(providers: Seq[FeeProvider])(implicit ec: ExecutionContext) extends FeeProvider {
class FallbackFeeProvider(providers: Seq[FeeProvider], minFeeratePerByte: Long)(implicit ec: ExecutionContext) extends FeeProvider {
require(providers.size >= 1, "need at least one fee provider")
require(minFeeratePerByte > 0, "minimum fee rate must be strictly greater than 0")
def getFeerates(fallbacks: Seq[FeeProvider]): Future[FeeratesPerByte] =
fallbacks match {
@ -31,6 +35,19 @@ class FallbackFeeProvider(providers: Seq[FeeProvider])(implicit ec: ExecutionCon
case head +: remaining => head.getFeerates.recoverWith { case _ => getFeerates(remaining) }
}
override def getFeerates: Future[FeeratesPerByte] = getFeerates(providers)
override def getFeerates: Future[FeeratesPerByte] = getFeerates(providers).map(FallbackFeeProvider.enforceMinimumFeerate(_, minFeeratePerByte))
}
object FallbackFeeProvider {
def enforceMinimumFeerate(feeratesPerByte: FeeratesPerByte, minFeeratePerByte: Long) : FeeratesPerByte = feeratesPerByte.copy(
block_1 = Math.max(feeratesPerByte.block_1, minFeeratePerByte),
blocks_2 = Math.max(feeratesPerByte.blocks_2, minFeeratePerByte),
blocks_6 = Math.max(feeratesPerByte.blocks_6, minFeeratePerByte),
blocks_12 = Math.max(feeratesPerByte.blocks_12, minFeeratePerByte),
blocks_36 = Math.max(feeratesPerByte.blocks_36, minFeeratePerByte),
blocks_72 = Math.max(feeratesPerByte.blocks_72, minFeeratePerByte)
)
}

View file

@ -29,9 +29,13 @@ trait FeeProvider {
}
case class FeeratesPerByte(block_1: Long, blocks_2: Long, blocks_6: Long, blocks_12: Long, blocks_36: Long, blocks_72: Long)
case class FeeratesPerByte(block_1: Long, blocks_2: Long, blocks_6: Long, blocks_12: Long, blocks_36: Long, blocks_72: Long) {
require(block_1 > 0 && blocks_2 > 0 && blocks_6 > 0 && blocks_12 > 0 && blocks_36 > 0 && blocks_72 > 0, "all feerates must be strictly greater than 0")
}
case class FeeratesPerKw(block_1: Long, blocks_2: Long, blocks_6: Long, blocks_12: Long, blocks_36: Long, blocks_72: Long)
case class FeeratesPerKw(block_1: Long, blocks_2: Long, blocks_6: Long, blocks_12: Long, blocks_36: Long, blocks_72: Long) {
require(block_1 > 0 && blocks_2 > 0 && blocks_6 > 0 && blocks_12 > 0 && blocks_36 > 0 && blocks_72 > 0, "all feerates must be strictly greater than 0")
}
object FeeratesPerKw {
def apply(feerates: FeeratesPerByte): FeeratesPerKw = FeeratesPerKw(

View file

@ -82,7 +82,7 @@ class Channel(val nodeParams: NodeParams, wallet: EclairWallet, remoteNodeId: Pu
// this will be used to make sure the current commitment fee is up-to-date
context.system.eventStream.subscribe(self, classOf[CurrentFeerates])
// we need to periodically re-send channel updates, otherwise channel will be considered stale and get pruned by network
setTimer(TickRefreshChannelUpdate.toString, TickRefreshChannelUpdate, 1 day, repeat = true)
setTimer(TickRefreshChannelUpdate.toString, TickRefreshChannelUpdate, 7 days, repeat = true)
/*
8888888 888b 888 8888888 88888888888

View file

@ -27,7 +27,7 @@ import fr.acinq.eclair.transactions.Scripts._
import fr.acinq.eclair.transactions.Transactions._
import fr.acinq.eclair.transactions._
import fr.acinq.eclair.wire._
import fr.acinq.eclair.{Globals, NodeParams, ShortChannelId}
import fr.acinq.eclair.{Globals, NodeParams, ShortChannelId, addressToPublicKeyScript}
import scala.concurrent.Await
import scala.util.{Failure, Success, Try}
@ -96,8 +96,7 @@ object Helpers {
Math.abs((2.0 * (remoteFeeratePerKw - localFeeratePerKw)) / (localFeeratePerKw + remoteFeeratePerKw))
def shouldUpdateFee(commitmentFeeratePerKw: Long, networkFeeratePerKw: Long, updateFeeMinDiffRatio: Double): Boolean =
// negative feerate can happen in regtest mode
networkFeeratePerKw > 0 && feeRateMismatch(networkFeeratePerKw, commitmentFeeratePerKw) > updateFeeMinDiffRatio
feeRateMismatch(networkFeeratePerKw, commitmentFeeratePerKw) > updateFeeMinDiffRatio
/**
*
@ -107,10 +106,8 @@ object Helpers {
* @return true if the difference between local and remote fee rates is too high.
* the actual check is |remote - local| / avg(local, remote) > mismatch ratio
*/
def isFeeDiffTooHigh(remoteFeeratePerKw: Long, localFeeratePerKw: Long, maxFeerateMismatchRatio: Double): Boolean = {
// negative feerate can happen in regtest mode
remoteFeeratePerKw > 0 && feeRateMismatch(remoteFeeratePerKw, localFeeratePerKw) > maxFeerateMismatchRatio
}
def isFeeDiffTooHigh(remoteFeeratePerKw: Long, localFeeratePerKw: Long, maxFeerateMismatchRatio: Double): Boolean =
feeRateMismatch(remoteFeeratePerKw, localFeeratePerKw) > maxFeerateMismatchRatio
def makeAnnouncementSignatures(nodeParams: NodeParams, commitments: Commitments, shortChannelId: ShortChannelId) = {
val features = BinaryData.empty // empty features for now
@ -118,16 +115,11 @@ object Helpers {
AnnouncementSignatures(commitments.channelId, shortChannelId, localNodeSig, localBitcoinSig)
}
def getFinalScriptPubKey(wallet: EclairWallet): BinaryData = {
def getFinalScriptPubKey(wallet: EclairWallet, chainHash: BinaryData): BinaryData = {
import scala.concurrent.duration._
val finalAddress = Await.result(wallet.getFinalAddress, 40 seconds)
val finalScriptPubKey = Base58Check.decode(finalAddress) match {
case (Base58.Prefix.PubkeyAddress, hash) => Script.write(OP_DUP :: OP_HASH160 :: OP_PUSHDATA(hash) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil)
case (Base58.Prefix.PubkeyAddressTestnet, hash) => Script.write(OP_DUP :: OP_HASH160 :: OP_PUSHDATA(hash) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil)
case (Base58.Prefix.ScriptAddress, hash) => Script.write(OP_HASH160 :: OP_PUSHDATA(hash) :: OP_EQUAL :: Nil)
case (Base58.Prefix.ScriptAddressTestnet, hash) => Script.write(OP_HASH160 :: OP_PUSHDATA(hash) :: OP_EQUAL :: Nil)
}
finalScriptPubKey
Script.write(addressToPublicKeyScript(finalAddress, chainHash))
}
object Funding {

View file

@ -28,8 +28,8 @@ import fr.acinq.eclair.channel._
import fr.acinq.eclair.crypto.TransportHandler
import fr.acinq.eclair.crypto.TransportHandler.Listener
import fr.acinq.eclair.router._
import fr.acinq.eclair.{wire, _}
import fr.acinq.eclair.wire._
import fr.acinq.eclair.{wire, _}
import scala.concurrent.duration._
import scala.util.Random
@ -338,7 +338,7 @@ class Peer(nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: Actor
}
def createNewChannel(nodeParams: NodeParams, funder: Boolean, fundingSatoshis: Long, origin_opt: Option[ActorRef]): (ActorRef, LocalParams) = {
val defaultFinalScriptPubKey = Helpers.getFinalScriptPubKey(wallet)
val defaultFinalScriptPubKey = Helpers.getFinalScriptPubKey(wallet, nodeParams.chainHash)
val localParams = makeChannelParams(nodeParams, defaultFinalScriptPubKey, funder, fundingSatoshis)
val channel = spawnChannel(nodeParams, origin_opt)
(channel, localParams)

View file

@ -23,6 +23,8 @@ import fr.acinq.bitcoin.{BinaryData, _}
import scodec.Attempt
import scodec.bits.BitVector
import scala.util.{Failure, Success, Try}
package object eclair {
/**
@ -51,6 +53,8 @@ package object eclair {
case Attempt.Failure(cause) => throw new RuntimeException(s"serialization error: $cause")
}
def feerateKbToByte(feeratePerKb: Long): Long = Math.max(feeratePerKb / 1024, 1)
/**
* Converts feerate in satoshi-per-bytes to feerate in satoshi-per-kw
*
@ -77,4 +81,31 @@ package object eclair {
* @return the fee (in msat) that a node should be paid to forward an HTLC of 'amount' millisatoshis
*/
def nodeFee(baseMsat: Long, proportional: Long, msat: Long): Long = baseMsat + (proportional * msat) / 1000000
/**
*
* @param address base58 of bech32 address
* @param chainHash hash of the chain we're on, which will be checked against the input address
* @return the public key script that matches the input address.
*/
def addressToPublicKeyScript(address: String, chainHash: BinaryData): Seq[ScriptElt] = {
Try(Base58Check.decode(address)) match {
case Success((Base58.Prefix.PubkeyAddressTestnet, pubKeyHash)) if chainHash == Block.TestnetGenesisBlock.hash || chainHash == Block.RegtestGenesisBlock.hash => Script.pay2pkh(pubKeyHash)
case Success((Base58.Prefix.PubkeyAddress, pubKeyHash)) if chainHash == Block.LivenetGenesisBlock.hash => Script.pay2pkh(pubKeyHash)
case Success((Base58.Prefix.ScriptAddressTestnet, scriptHash)) if chainHash == Block.TestnetGenesisBlock.hash || chainHash == Block.RegtestGenesisBlock.hash => OP_HASH160 :: OP_PUSHDATA(scriptHash) :: OP_EQUAL :: Nil
case Success((Base58.Prefix.ScriptAddress, scriptHash)) if chainHash == Block.LivenetGenesisBlock.hash => OP_HASH160 :: OP_PUSHDATA(scriptHash) :: OP_EQUAL :: Nil
case Success(_) => throw new IllegalArgumentException("base58 address does not match our blockchain")
case Failure(base58error) =>
Try(Bech32.decodeWitnessAddress(address)) match {
case Success((_, version, _)) if version != 0.toByte => throw new IllegalArgumentException(s"invalid version $version in bech32 address")
case Success((_, _, bin)) if bin.length != 20 && bin.length != 32 => throw new IllegalArgumentException("hash length in bech32 address must be either 20 or 32 bytes")
case Success(("bc", _, bin)) if chainHash == Block.LivenetGenesisBlock.hash => OP_0 :: OP_PUSHDATA(bin) :: Nil
case Success(("tb", _, bin)) if chainHash == Block.TestnetGenesisBlock.hash => OP_0 :: OP_PUSHDATA(bin) :: Nil
case Success(("bcrt", _, bin)) if chainHash == Block.RegtestGenesisBlock.hash => OP_0 :: OP_PUSHDATA(bin) :: Nil
case Success(_) => throw new IllegalArgumentException("bech32 address does not match our blockchain")
case Failure(bech32error) => throw new IllegalArgumentException(s"$address is neither a valid Base58 address ($base58error) nor a valid Bech32 address ($bech32error)")
}
}
}
}

View file

@ -8,4 +8,4 @@ txindex=1
zmqpubrawblock=tcp://127.0.0.1:28334
zmqpubrawtx=tcp://127.0.0.1:28334
rpcworkqueue=64
addresstype=p2sh-segwit
addresstype=bech32

View file

@ -16,11 +16,14 @@
package fr.acinq.eclair
import fr.acinq.bitcoin.BinaryData
import fr.acinq.bitcoin.Crypto.PrivateKey
import fr.acinq.bitcoin.{Base58, Base58Check, Bech32, BinaryData, Block, Crypto, Script}
import org.junit.runner.RunWith
import org.scalatest.FunSuite
import org.scalatest.junit.JUnitRunner
import scala.util.Try
/**
* Created by PM on 27/01/2017.
*/
@ -36,4 +39,66 @@ class PackageSpec extends FunSuite {
data.foreach(x => assert(toLongId(x._1, x._2) === x._3))
}
test("decode base58 addresses") {
val priv = PrivateKey(BinaryData("01" * 32), compressed = true)
val pub = priv.publicKey
// p2pkh
// valid chain
assert(addressToPublicKeyScript(Base58Check.encode(Base58.Prefix.PubkeyAddressTestnet, pub.hash160), Block.TestnetGenesisBlock.hash) == Script.pay2pkh(pub))
assert(addressToPublicKeyScript(Base58Check.encode(Base58.Prefix.PubkeyAddressTestnet, pub.hash160), Block.RegtestGenesisBlock.hash) == Script.pay2pkh(pub))
assert(addressToPublicKeyScript(Base58Check.encode(Base58.Prefix.PubkeyAddress, pub.hash160), Block.LivenetGenesisBlock.hash) == Script.pay2pkh(pub))
// wrong chain
intercept[RuntimeException] {
addressToPublicKeyScript(Base58Check.encode(Base58.Prefix.PubkeyAddress, pub.hash160), Block.TestnetGenesisBlock.hash)
}
assert(Try(addressToPublicKeyScript(Base58Check.encode(Base58.Prefix.PubkeyAddress, pub.hash160), Block.TestnetGenesisBlock.hash)).isFailure)
assert(Try(addressToPublicKeyScript(Base58Check.encode(Base58.Prefix.PubkeyAddress, pub.hash160), Block.RegtestGenesisBlock.hash)).isFailure)
// p2sh
val script = Script.write(Script.pay2wpkh(pub))
// valid chain
assert(addressToPublicKeyScript(Base58Check.encode(Base58.Prefix.ScriptAddressTestnet, Crypto.hash160(script)), Block.TestnetGenesisBlock.hash) == Script.pay2sh(script))
assert(addressToPublicKeyScript(Base58Check.encode(Base58.Prefix.ScriptAddressTestnet, Crypto.hash160(script)), Block.RegtestGenesisBlock.hash) == Script.pay2sh(script))
assert(addressToPublicKeyScript(Base58Check.encode(Base58.Prefix.ScriptAddress, Crypto.hash160(script)), Block.LivenetGenesisBlock.hash) == Script.pay2sh(script))
// wrong chain
assert(Try(addressToPublicKeyScript(Base58Check.encode(Base58.Prefix.ScriptAddressTestnet, Crypto.hash160(script)), Block.LivenetGenesisBlock.hash)).isFailure)
assert(Try(addressToPublicKeyScript(Base58Check.encode(Base58.Prefix.ScriptAddress, Crypto.hash160(script)), Block.TestnetGenesisBlock.hash)).isFailure)
assert(Try(addressToPublicKeyScript(Base58Check.encode(Base58.Prefix.ScriptAddress, Crypto.hash160(script)), Block.RegtestGenesisBlock.hash)).isFailure)
}
test("decode bech32 addresses") {
val priv = PrivateKey(BinaryData("01" * 32), compressed = true)
val pub = priv.publicKey
// p2wpkh
assert(addressToPublicKeyScript(Bech32.encodeWitnessAddress("bc", 0, pub.hash160), Block.LivenetGenesisBlock.hash) == Script.pay2wpkh(pub))
assert(addressToPublicKeyScript(Bech32.encodeWitnessAddress("tb", 0, pub.hash160), Block.TestnetGenesisBlock.hash) == Script.pay2wpkh(pub))
assert(addressToPublicKeyScript(Bech32.encodeWitnessAddress("bcrt", 0, pub.hash160), Block.RegtestGenesisBlock.hash) == Script.pay2wpkh(pub))
// wrong version
assert(Try(addressToPublicKeyScript(Bech32.encodeWitnessAddress("bc", 1, pub.hash160), Block.LivenetGenesisBlock.hash)).isFailure)
assert(Try(addressToPublicKeyScript(Bech32.encodeWitnessAddress("tb", 1, pub.hash160), Block.TestnetGenesisBlock.hash)).isFailure)
assert(Try(addressToPublicKeyScript(Bech32.encodeWitnessAddress("bcrt", 1, pub.hash160), Block.RegtestGenesisBlock.hash)).isFailure)
// wrong chain
assert(Try(addressToPublicKeyScript(Bech32.encodeWitnessAddress("bc", 0, pub.hash160), Block.TestnetGenesisBlock.hash)).isFailure)
assert(Try(addressToPublicKeyScript(Bech32.encodeWitnessAddress("tb", 0, pub.hash160), Block.LivenetGenesisBlock.hash)).isFailure)
assert(Try(addressToPublicKeyScript(Bech32.encodeWitnessAddress("bcrt", 0, pub.hash160), Block.LivenetGenesisBlock.hash)).isFailure)
val script = Script.write(Script.pay2wpkh(pub))
assert(addressToPublicKeyScript(Bech32.encodeWitnessAddress("bc", 0, Crypto.sha256(script)), Block.LivenetGenesisBlock.hash) == Script.pay2wsh(script))
assert(addressToPublicKeyScript(Bech32.encodeWitnessAddress("tb", 0, Crypto.sha256(script)), Block.TestnetGenesisBlock.hash) == Script.pay2wsh(script))
assert(addressToPublicKeyScript(Bech32.encodeWitnessAddress("bcrt", 0, Crypto.sha256(script)), Block.RegtestGenesisBlock.hash) == Script.pay2wsh(script))
}
test("fail to decode invalid addresses") {
val e = intercept[RuntimeException] {
addressToPublicKeyScript("1Qbbbbb", Block.LivenetGenesisBlock.hash)
}
assert(e.getMessage.contains("is neither a valid Base58 address") && e.getMessage.contains("nor a valid Bech32 address"))
}
}

View file

@ -44,7 +44,7 @@ abstract class TestkitBaseClass extends TestKit(ActorSystem("test")) with fixtur
override def afterAll {
TestKit.shutdownActorSystem(system)
Globals.feeratesPerKw.set(FeeratesPerKw.single(0))
Globals.feeratesPerKw.set(FeeratesPerKw.single(1))
}
}

View file

@ -24,11 +24,11 @@ import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.pattern.pipe
import akka.testkit.{TestKit, TestProbe}
import com.typesafe.config.ConfigFactory
import fr.acinq.bitcoin.{MilliBtc, Satoshi, Script}
import fr.acinq.bitcoin.{Block, MilliBtc, Satoshi, Script}
import fr.acinq.eclair.blockchain._
import fr.acinq.eclair.blockchain.bitcoind.rpc.BasicBitcoinJsonRPCClient
import fr.acinq.eclair.randomKey
import fr.acinq.eclair.transactions.Scripts
import fr.acinq.eclair.{addressToPublicKeyScript, randomKey}
import grizzled.slf4j.Logging
import org.json4s.JsonAST.JValue
import org.json4s.{DefaultFormats, JString}
@ -39,6 +39,7 @@ import org.scalatest.{BeforeAndAfterAll, FunSuiteLike}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.sys.process.{Process, _}
import scala.util.Try
@RunWith(classOf[JUnitRunner])
class BitcoinCoreWalletSpec extends TestKit(ActorSystem("test")) with FunSuiteLike with BeforeAndAfterAll with Logging {
@ -110,7 +111,8 @@ class BitcoinCoreWalletSpec extends TestKit(ActorSystem("test")) with FunSuiteLi
assert(sender.expectMsgType[Satoshi] > Satoshi(0))
wallet.getFinalAddress.pipeTo(sender.ref)
assert(sender.expectMsgType[String].startsWith("2"))
val address = sender.expectMsgType[String]
assert(Try(addressToPublicKeyScript(address, Block.RegtestGenesisBlock.hash)).isSuccess)
val fundingTxes = for (i <- 0 to 3) yield {
val pubkeyScript = Script.write(Script.pay2wsh(Scripts.multiSig2of2(randomKey.publicKey, randomKey.publicKey)))

View file

@ -56,7 +56,7 @@ class FallbackFeeProviderSpec extends FunSuite {
val provider5 = new FailingFeeProvider(5, dummyFeerates) // fails after 5 tries
val provider7 = new FailingFeeProvider(Int.MaxValue, dummyFeerates) // "never" fails
val fallbackFeeProvider = new FallbackFeeProvider(provider0 :: provider1 :: provider3 :: provider5 :: provider7 :: Nil)
val fallbackFeeProvider = new FallbackFeeProvider(provider0 :: provider1 :: provider3 :: provider5 :: provider7 :: Nil, 1)
assert(await(fallbackFeeProvider.getFeerates) === provider1.feeratesPerByte)
@ -74,5 +74,11 @@ class FallbackFeeProviderSpec extends FunSuite {
}
test("ensure minimum feerate") {
val constantFeeProvider = new ConstantFeeProvider(FeeratesPerByte(1, 1, 1, 1, 1, 1))
val fallbackFeeProvider = new FallbackFeeProvider(constantFeeProvider :: Nil, 2)
assert(await(fallbackFeeProvider.getFeerates) === FeeratesPerByte(2, 2, 2, 2, 2, 2))
}
}

View file

@ -1576,16 +1576,6 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
}
}
test("recv CurrentFeerate (ignore negative feerate)") { case (alice, _, alice2bob, _, _, _, _) =>
within(30 seconds) {
val sender = TestProbe()
// this happens when in regtest mode
val event = CurrentFeerates(FeeratesPerKw.single(-1))
sender.send(alice, event)
alice2bob.expectNoMsg(500 millis)
}
}
test("recv BITCOIN_FUNDING_SPENT (their commit w/ htlc)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain, _) =>
within(30 seconds) {
val sender = TestProbe()

View file

@ -616,16 +616,6 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
}
}
test("recv CurrentFeerate (ignore negative feerate)") { case (alice, _, alice2bob, _, _, _, _) =>
within(30 seconds) {
val sender = TestProbe()
// this happens when in regtest mode
val event = CurrentFeerates(FeeratesPerKw.single(-1))
sender.send(alice, event)
alice2bob.expectNoMsg(500 millis)
}
}
test("recv BITCOIN_FUNDING_SPENT (their commit)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _, _) =>
within(30 seconds) {
// bob publishes his current commit tx, which contains two pending htlcs alice->bob

View file

@ -25,8 +25,8 @@ import akka.pattern.pipe
import akka.testkit.{TestKit, TestProbe}
import com.google.common.net.HostAndPort
import com.typesafe.config.{Config, ConfigFactory}
import fr.acinq.bitcoin.Crypto.{PublicKey, sha256}
import fr.acinq.bitcoin.{Base58, Base58Check, BinaryData, Block, Crypto, MilliSatoshi, OP_CHECKSIG, OP_DUP, OP_EQUAL, OP_EQUALVERIFY, OP_HASH160, OP_PUSHDATA, Satoshi, Script, ScriptFlags, Transaction}
import fr.acinq.bitcoin.Crypto.PublicKey
import fr.acinq.bitcoin.{Base58, Base58Check, Bech32, BinaryData, Block, Crypto, MilliSatoshi, OP_0, OP_CHECKSIG, OP_DUP, OP_EQUAL, OP_EQUALVERIFY, OP_HASH160, OP_PUSHDATA, Satoshi, Script, ScriptFlags, Transaction}
import fr.acinq.eclair.blockchain.bitcoind.rpc.{BasicBitcoinJsonRPCClient, BitcoinJsonRPCClient, ExtendedBitcoinClient}
import fr.acinq.eclair.blockchain.{Watch, WatchConfirmed}
import fr.acinq.eclair.channel.Register.Forward
@ -388,6 +388,8 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with FunSuiteLike wit
Base58Check.encode(Base58.Prefix.PubkeyAddressTestnet, pubKeyHash)
case OP_HASH160 :: OP_PUSHDATA(scriptHash, _) :: OP_EQUAL :: Nil =>
Base58Check.encode(Base58.Prefix.ScriptAddressTestnet, scriptHash)
case OP_0 :: OP_PUSHDATA(pubKeyHash, _) :: Nil if pubKeyHash.length == 20 => Bech32.encodeWitnessAddress("bcrt", 0, pubKeyHash)
case OP_0 :: OP_PUSHDATA(scriptHash, _) :: Nil if scriptHash.length == 32 => Bech32.encodeWitnessAddress("bcrt", 0, scriptHash)
case _ => ???
}

View file

@ -83,7 +83,7 @@ class RustyTestsSpec extends TestKit(ActorSystem("test")) with Matchers with fix
}
override def afterAll {
Globals.feeratesPerKw.set(FeeratesPerKw.single(0))
Globals.feeratesPerKw.set(FeeratesPerKw.single(1))
TestKit.shutdownActorSystem(system)
}