1616
1717package fr .acinq .eclair .blockchain .bitcoind
1818
19+ import akka .actor .ActorRef
1920import akka .actor .Status .Failure
2021import akka .pattern .pipe
21- import akka .testkit .TestProbe
22+ import akka .testkit .{ TestActor , TestProbe }
2223import fr .acinq .bitcoin
24+ import fr .acinq .bitcoin .BlockHeader
2325import fr .acinq .bitcoin .scalacompat .Crypto .PublicKey
2426import fr .acinq .bitcoin .scalacompat .{Block , BtcDouble , ByteVector32 , MilliBtcDouble , OutPoint , Satoshi , SatoshiLong , Script , ScriptWitness , Transaction , TxIn , TxOut }
2527import fr .acinq .eclair .blockchain .OnChainWallet .{FundTransactionResponse , MakeFundingTxResponse , OnChainBalance , SignTransactionResponse }
2628import fr .acinq .eclair .blockchain .WatcherSpec .{createSpendManyP2WPKH , createSpendP2WPKH }
2729import fr .acinq .eclair .blockchain .bitcoind .BitcoindService .BitcoinReq
2830import fr .acinq .eclair .blockchain .bitcoind .rpc .BitcoinCoreClient ._
2931import fr .acinq .eclair .blockchain .bitcoind .rpc .BitcoinJsonRPCAuthMethod .UserPassword
30- import fr .acinq .eclair .blockchain .bitcoind .rpc .{BasicBitcoinJsonRPCClient , BitcoinCoreClient , JsonRPCError }
32+ import fr .acinq .eclair .blockchain .bitcoind .rpc .{BasicBitcoinJsonRPCClient , BitcoinCoreClient , BitcoinJsonRPCClient , JsonRPCError }
3133import fr .acinq .eclair .blockchain .fee .FeeratePerKw
3234import fr .acinq .eclair .transactions .{Scripts , Transactions }
3335import fr .acinq .eclair .{BlockHeight , TestConstants , TestKitBaseClass , addressToPublicKeyScript , randomKey }
@@ -806,4 +808,98 @@ class BitcoinCoreClientSpec extends TestKitBaseClass with BitcoindService with A
806808 assert(addressToPublicKeyScript(address, Block .RegtestGenesisBlock .hash) == Script .pay2wpkh(receiveKey))
807809 }
808810
811+ test(" get block header info" ) {
812+ import fr .acinq .bitcoin .scalacompat .KotlinUtils ._
813+ val sender = TestProbe ()
814+ val bitcoinClient = new BitcoinCoreClient (bitcoinrpcclient)
815+
816+ bitcoinClient.getBlockHeight().pipeTo(sender.ref)
817+ val height = sender.expectMsgType[BlockHeight ]
818+ bitcoinClient.getBlockHash(height.toInt).pipeTo(sender.ref)
819+ val lastBlockId = sender.expectMsgType[ByteVector32 ]
820+ bitcoinClient.getBlockHeaderInfo(lastBlockId).pipeTo(sender.ref)
821+ val lastBlockInfo = sender.expectMsgType[BlockHeaderInfo ]
822+ assert(lastBlockInfo.nextBlockHash.isEmpty)
823+
824+ bitcoinClient.getBlockHash(height.toInt - 1 ).pipeTo(sender.ref)
825+ val blockId = sender.expectMsgType[ByteVector32 ]
826+ bitcoinClient.getBlockHeaderInfo(blockId).pipeTo(sender.ref)
827+ val blockInfo = sender.expectMsgType[BlockHeaderInfo ]
828+ assert(lastBlockInfo.header.hashPreviousBlock == blockInfo.header.hash)
829+ assert(blockInfo.nextBlockHash.contains(kmp2scala(lastBlockInfo.header.hash)))
830+ }
831+
832+ test(" get chains of block header infos" ) {
833+ import fr .acinq .bitcoin .scalacompat .KotlinUtils ._
834+ val sender = TestProbe ()
835+ val bitcoinClient = new BitcoinCoreClient (bitcoinrpcclient)
836+
837+ def getHeaderInfos (height : Int , count : Int ): List [BlockHeaderInfo ] = {
838+ bitcoinClient.getBlockHash(height).pipeTo(sender.ref)
839+ val blockId = sender.expectMsgType[ByteVector32 ]
840+ bitcoinClient.getBlockHeaderInfos(blockId, count).pipeTo(sender.ref)
841+ sender.expectMsgType[List [BlockHeaderInfo ]]
842+ }
843+
844+ def check (blockInfos : List [BlockHeaderInfo ]): Unit = {
845+ for (i <- 0 until blockInfos.size - 1 ) {
846+ require(blockInfos(i).nextBlockHash.contains(kmp2scala(blockInfos(i + 1 ).header.hash)))
847+ require(blockInfos(i + 1 ).header.hashPreviousBlock == blockInfos(i).header.hash)
848+ }
849+ }
850+
851+ check(getHeaderInfos(140 , 5 ))
852+ check(getHeaderInfos(146 , 5 ))
853+ }
854+
855+ test(" get tx confirmation proof" ) {
856+ val sender = TestProbe ()
857+ val bitcoinClient = new BitcoinCoreClient (bitcoinrpcclient)
858+
859+ val address = getNewAddress(sender)
860+ val tx1 = sendToAddress(address, 5 btc, sender)
861+
862+ bitcoinClient.getTxConfirmations(tx1.txid).pipeTo(sender.ref)
863+ sender.expectMsg(Some (0 ))
864+
865+ bitcoinClient.checkTxConfirmations(tx1.txid, 1 ).pipeTo(sender.ref)
866+ sender.expectMsgType[Failure ]
867+
868+ generateBlocks(3 )
869+ bitcoinClient.getTxConfirmations(tx1.txid).pipeTo(sender.ref)
870+ sender.expectMsg(Some (3 ))
871+
872+ bitcoinClient.checkTxConfirmations(tx1.txid, 3 ).pipeTo(sender.ref)
873+ assert(sender.expectMsgType[Boolean ])
874+ }
875+
876+ test(" verify tx confirmation proof" ) {
877+ val sender = TestProbe ()
878+ // this is a valid proof for a random tx on testnet
879+ val validProof = " 0000c020d80e8834189edc6f67bb6683e0f1fc21608fb30f0c7148432b0000000000000078d01cf841039ee0d10e9e43cae12d54f08251fd96ac13688991a9af155f1c93b4f0d762f1da381903a82037140000000653f44c07bbd9acc8a9f9efa4ffaef176e0be7cfd7125b4985dc63b516191f81384d408a276c856d1816ffaf66f33dd17d4601a8e7c702ad98db859bd2c9d03ee2d982aad9cf8956835baa2011888a30a759548b3702b98c2266f4dd6a42b3dacefa0c54813f90ee2c7629f7600fcb782503c98a1df30368c568cc8c780c9d97b3c99c68fffea99cf505a02138e52df20e51c77e5784460f95bf3660f302b7046ceaa968b4e007fab9a717a9be9403ba5d866ec3ef81c5155e919aa27da49fa17025f00"
880+ val bitcoinClient = new BitcoinCoreClient (new BitcoinJsonRPCClient {
881+ override def invoke (method : String , params : Any * )(implicit ec : ExecutionContext ): Future [JValue ] = method match {
882+ case " gettxoutproof" => Future .successful(new JString (validProof)) // bitcoin core is lying to us
883+ case _ => bitcoinrpcclient.invoke(method, params : _* )(ec)
884+ }
885+ })
886+
887+ val address = getNewAddress(sender)
888+ val tx1 = sendToAddress(address, 5 btc, sender)
889+
890+ bitcoinClient.getTxConfirmations(tx1.txid).pipeTo(sender.ref)
891+ sender.expectMsg(Some (0 ))
892+
893+ bitcoinClient.checkTxConfirmations(tx1.txid, 1 ).pipeTo(sender.ref)
894+ sender.expectMsgType[Failure ]
895+
896+ generateBlocks(3 )
897+ bitcoinClient.getTxConfirmations(tx1.txid).pipeTo(sender.ref)
898+ sender.expectMsg(Some (3 ))
899+
900+ bitcoinClient.checkTxConfirmations(tx1.txid, 3 ).pipeTo(sender.ref)
901+ val failure = sender.expectMsgType[Failure ]
902+ assert(failure.cause.getMessage.contains(s " cannot find inclusion proof for ${tx1.txid}" ))
903+ }
904+
809905}
0 commit comments