Skip to content

Commit

Permalink
made integration tests pass without txindex
Browse files Browse the repository at this point in the history
Impacted parts are:
  - `WatchConfirmed`: can't use `getrawtransaction` anymore; instead we
  use `importaddress`+`rescanblockchain`+`gettransaction`.
  - `WatchSpent`: untouched (there is no indexation for that in bitcoind
  so we were looking in the mempool and in the blockchain, and this
  still works)
  - `ValidateRequest`: doesn't work anymore

Note that we still use `getrawtransaction` when looking for spending
txes in the mempool. The `gettransaction`/`getrawtransaction`
differenciation is quite messy currently.

Left to do:
- handle restart with a brand new bitcoin core (which doesn't watch the
relevant scripts)
- handle (disable? delegate to electrum servers?) authentication of public
channels

Regarding pruning, we can probably just prune everything below
block height 500 000.
  • Loading branch information
pm47 committed Oct 16, 2019
1 parent b9252cd commit ee3bedf
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 11 deletions.
Expand Up @@ -139,7 +139,10 @@ class ZmqWatcher(blockCount: AtomicLong, client: ExtendedBitcoinClient)(implicit
}
}

case w: WatchConfirmed => checkConfirmed(w) // maybe the tx is already tx, in that case the watch will be triggered and removed immediately
case w: WatchConfirmed =>
for {
_ <- client.watchScript(w.publicKeyScript.toHex, 0)
} yield checkConfirmed(w) // maybe the tx is already tx, in that case the watch will be triggered and removed immediately

case _: WatchLost => () // TODO: not implemented

Expand Down
Expand Up @@ -22,7 +22,7 @@ import fr.acinq.eclair.TxCoordinates
import fr.acinq.eclair.blockchain.{GetTxWithMetaResponse, UtxoStatus, ValidateResult}
import fr.acinq.eclair.wire.ChannelAnnouncement
import kamon.Kamon
import org.json4s.JsonAST._
import org.json4s.JsonAST.{JValue, _}

import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try
Expand All @@ -34,15 +34,21 @@ class ExtendedBitcoinClient(val rpcClient: BitcoinJsonRPCClient) {

implicit val formats = org.json4s.DefaultFormats

def watchScript(script: String, rescanSinceHeight: Int)(implicit ec: ExecutionContext): Future[Unit] =
for {
_ <- rpcClient.invoke("importaddress", script, "", false)
_ <- rpcClient.invoke("rescanblockchain", rescanSinceHeight)
} yield Unit

def getTxConfirmations(txId: String)(implicit ec: ExecutionContext): Future[Option[Int]] =
rpcClient.invoke("getrawtransaction", txId, 1) // we choose verbose output to get the number of confirmations
rpcClient.invoke("gettransaction", txId) // we choose verbose output to get the number of confirmations
.map(json => Some((json \ "confirmations").extractOrElse[Int](0)))
.recover {
case t: JsonRPCError if t.error.code == -5 => None
}

def getTxBlockHash(txId: String)(implicit ec: ExecutionContext): Future[Option[String]] =
rpcClient.invoke("getrawtransaction", txId, 1) // we choose verbose output to get the number of confirmations
rpcClient.invoke("gettransaction", txId) // we choose verbose output to get the number of confirmations
.map(json => (json \ "blockhash").extractOpt[String])
.recover {
case t: JsonRPCError if t.error.code == -5 => None
Expand All @@ -66,21 +72,24 @@ class ExtendedBitcoinClient(val rpcClient: BitcoinJsonRPCClient) {
def getMempool()(implicit ec: ExecutionContext): Future[Seq[Transaction]] =
for {
txids <- rpcClient.invoke("getrawmempool").map(json => json.extract[List[String]])
txs <- Future.sequence(txids.map(getTransaction(_)))
txs <- Future.sequence(txids.map(getRawTransaction(_)))
} yield txs

/**
* @param txId
* @param ec
* @return
*/
def getRawTransaction(txId: String)(implicit ec: ExecutionContext): Future[String] =
def getRawTransaction(txId: String)(implicit ec: ExecutionContext): Future[Transaction] =
rpcClient.invoke("getrawtransaction", txId) collect {
case JString(raw) => raw
case JString(raw) => Transaction.read(raw)
}

def getTransaction(txId: String)(implicit ec: ExecutionContext): Future[Transaction] =
getRawTransaction(txId).map(raw => Transaction.read(raw))
for {
json <- rpcClient.invoke("gettransaction", txId)
JString(hex) = json \ "hex"
} yield Transaction.read(hex)

def getTransactionMeta(txId: String)(implicit ec: ExecutionContext): Future[GetTxWithMetaResponse] =
for {
Expand Down Expand Up @@ -165,7 +174,7 @@ class ExtendedBitcoinClient(val rpcClient: BitcoinJsonRPCClient) {
}
_ = span1.finish()
span2 = Kamon.spanBuilder("getrawtx").start()
tx <- getRawTransaction(txid)
tx <- getTransaction(txid)
_ = span2.finish()
span3 = Kamon.spanBuilder("utxospendable-mempool").start()
unspent <- isTransactionOutputSpendable(txid, outputIndex, includeMempool = true)
Expand All @@ -179,7 +188,7 @@ class ExtendedBitcoinClient(val rpcClient: BitcoinJsonRPCClient) {
}
}
_ = span.finish()
} yield ValidateResult(c, Right((Transaction.read(tx), fundingTxStatus)))
} yield ValidateResult(c, Right((tx, fundingTxStatus)))

} recover { case t: Throwable => ValidateResult(c, Left(t)) }

Expand Down
2 changes: 1 addition & 1 deletion eclair-core/src/test/resources/integration/bitcoin.conf
Expand Up @@ -3,7 +3,7 @@ noprinttoconsole=1
server=1
rpcuser=foo
rpcpassword=bar
txindex=1
#txindex=1
zmqpubrawblock=tcp://127.0.0.1:28334
zmqpubrawtx=tcp://127.0.0.1:28335
rpcworkqueue=64
Expand Down
Expand Up @@ -122,4 +122,39 @@ class ExtendedBitcoinClientSpec extends TestKit(ActorSystem("test")) with Bitcoi
client.publishTransaction(Transaction.read("02000000000101b9e2a3f518fd74e696d258fed3c78c43f84504e76c99212e01cf225083619acf00000000000d0199800136b34b00000000001600145464ce1e5967773922506e285780339d72423244040047304402206795df1fd93c285d9028c384aacf28b43679f1c3f40215fd7bd1abbfb816ee5a022047a25b8c128e692d4717b6dd7b805aa24ecbbd20cfd664ab37a5096577d4a15d014730440220770f44121ed0e71ec4b482dded976f2febd7500dfd084108e07f3ce1e85ec7f5022025b32dc0d551c47136ce41bfb80f5a10de95c0babb22a3ae2d38e6688b32fcb20147522102c2662ab3e4fa18a141d3be3317c6ee134aff10e6cd0a91282a25bf75c0481ebc2102e952dd98d79aa796289fa438e4fdeb06ed8589ff2a0f032b0cfcb4d7b564bc3252aea58d1120")).pipeTo(sender.ref)
sender.expectMsgType[Failure]
}

test("test bitcoin client methods") {
val sender = TestProbe()
val bitcoinClient = new BasicBitcoinJsonRPCClient(
user = config.getString("bitcoind.rpcuser"),
password = config.getString("bitcoind.rpcpassword"),
host = config.getString("bitcoind.host"),
port = config.getInt("bitcoind.rpcport"))

val client = new ExtendedBitcoinClient(bitcoinClient)

bitcoinClient.invoke("sendtoaddress", "bcrt1q395dtsrzkj0dqqk08ajda8ew6x9sudm6ces2j26gn0k5mjmwuhjsukr0lg", 1).pipeTo(sender.ref)
val JString(txid) = sender.expectMsgType[JValue]

client.getTxConfirmations(txid).pipeTo(sender.ref)
sender.expectMsg(Some(0))

client.getTransaction(txid).pipeTo(sender.ref)
sender.expectMsgType[Transaction]

client.getTxBlockHash(txid).pipeTo(sender.ref)
sender.expectMsg(None)

bitcoinClient.invoke("generatetoaddress", 1, "bcrt1qhl44kwek8pzktjypm4dzq9r4w60xu34877sfnx9yc4ut20f6z2kswv2f2c").pipeTo(sender.ref)
sender.expectMsgType[JValue]

client.getTxBlockHash(txid).pipeTo(sender.ref)
assert(sender.expectMsgType[Option[String]].isDefined)

client.getTransactionShortId(txid).pipeTo(sender.ref)
sender.expectMsgType[(Int, Int)]


}

}

0 comments on commit ee3bedf

Please sign in to comment.