Add getblockheader cli command (#2424)

This commit is contained in:
Ben Carman 2020-12-23 07:15:35 -06:00 committed by GitHub
parent d6f27f5bbe
commit f10bdce179
5 changed files with 121 additions and 1 deletions

View File

@ -72,6 +72,19 @@ object ConsoleCli {
cmd("getbestblockhash")
.action((_, conf) => conf.copy(command = GetBestBlockHash))
.text(s"Get the best block hash"),
cmd("getblockheader")
.action((_, conf) =>
conf.copy(command = GetBlockHeader(DoubleSha256DigestBE.empty)))
.text("Returns information about block header <hash>")
.children(
arg[DoubleSha256DigestBE]("hash")
.text("The block hash")
.required()
.action((hash, conf) =>
conf.copy(command = conf.command match {
case gbh: GetBlockHeader => gbh.copy(hash = hash)
case other => other
}))),
cmd("decoderawtransaction")
.action((_, conf) =>
conf.copy(command = DecodeRawTransaction(EmptyTransaction)))
@ -1499,6 +1512,8 @@ object ConsoleCli {
up.writeJs(xprv),
up.writeJs(passwordOpt)))
case GetBlockHeader(hash) =>
RequestParam("getblockheader", Seq(up.writeJs(hash)))
// height
case GetBlockCount => RequestParam("getblockcount")
// filter count
@ -1842,6 +1857,7 @@ object CliCommand {
case object GetBlockCount extends CliCommand
case object GetFilterCount extends CliCommand
case object GetFilterHeaderCount extends CliCommand
case class GetBlockHeader(hash: DoubleSha256DigestBE) extends CliCommand
case class DecodeRawTransaction(transaction: Transaction) extends CliCommand
case class Rescan(

View File

@ -1,12 +1,12 @@
package org.bitcoins.server
import java.time.{ZoneId, ZonedDateTime}
import akka.http.scaladsl.model.ContentTypes._
import akka.http.scaladsl.server.ValidationRejection
import akka.http.scaladsl.testkit.ScalatestRouteTest
import org.bitcoins.core.Core
import org.bitcoins.core.api.chain.ChainApi
import org.bitcoins.core.api.chain.db._
import org.bitcoins.core.api.wallet.db._
import org.bitcoins.core.api.wallet.{AddressInfo, CoinSelectionAlgo}
import org.bitcoins.core.config.RegTest
@ -20,6 +20,7 @@ import org.bitcoins.core.protocol.BlockStamp.{
BlockTime,
InvalidBlockStamp
}
import org.bitcoins.core.protocol.blockchain.BlockHeader
import org.bitcoins.core.protocol.script.EmptyScriptWitness
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp, P2PKHAddress}
@ -217,6 +218,44 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
}
}
val blockHeader = BlockHeader(
"00e0002094c6692e100ed20d14f8c325c897259749e781d55ed1b7eb1000000000000000309a90b49f5f5a14ffdb2857557f6f27a136943603fb29e65e283dcb27fd886124fee25f57e53019886c0e8b")
val blockHeaderDb =
BlockHeaderDbHelper.fromBlockHeader(height = 1899697,
chainWork = BigInt(12345),
bh = blockHeader)
"get a block header" in {
val chainworkStr = {
val bytes = ByteVector(blockHeaderDb.chainWork.toByteArray)
val padded = if (bytes.length <= 32) {
bytes.padLeft(32)
} else bytes
padded.toHex
}
(mockChainApi
.getHeader(_: DoubleSha256DigestBE))
.expects(blockHeader.hashBE)
.returning(Future.successful(Some(blockHeaderDb)))
(mockChainApi
.getNumberOfConfirmations(_: DoubleSha256DigestBE))
.expects(blockHeader.hashBE)
.returning(Future.successful(Some(1)))
val route =
chainRoutes.handleCommand(
ServerCommand("getblockheader", Arr(Str(blockHeader.hashBE.hex))))
Get() ~> route ~> check {
assert(contentType == `application/json`)
assert(responseAs[
String] == s"""{"result":{"raw":"${blockHeader.hex}","hash":"${blockHeader.hashBE.hex}","confirmations":1,"height":1899697,"version":${blockHeader.version.toLong},"versionHex":"${blockHeader.version.hex}","merkleroot":"${blockHeader.merkleRootHashBE.hex}","time":${blockHeader.time.toLong},"nonce":${blockHeader.nonce.toLong},"bits":"${blockHeader.nBits.hex}","difficulty":${blockHeader.difficulty.toDouble},"chainwork":"$chainworkStr","previousblockhash":"${blockHeader.previousBlockHashBE.hex}"},"error":null}""")
}
}
"return the wallet's balance" in {
(mockWalletApi
.getBalance()(_: ExecutionContext))

View File

@ -7,6 +7,11 @@ import org.bitcoins.commons.jsonmodels.BitcoinSServerInfo
import org.bitcoins.commons.serializers.Picklers._
import org.bitcoins.core.api.chain.ChainApi
import org.bitcoins.core.config.BitcoinNetwork
import scodec.bits.ByteVector
import ujson._
import scala.concurrent.Future
import scala.util.{Failure, Success}
case class ChainRoutes(chain: ChainApi, network: BitcoinNetwork)(implicit
system: ActorSystem)
@ -39,6 +44,51 @@ case class ChainRoutes(chain: ChainApi, network: BitcoinNetwork)(implicit
}
}
case ServerCommand("getblockheader", arr) =>
GetBlockHeader.fromJsArr(arr) match {
case Failure(exception) =>
reject(ValidationRejection("failure", Some(exception)))
case Success(GetBlockHeader(hash)) =>
complete {
chain.getHeader(hash).flatMap {
case None => Future.successful(Server.httpSuccess(ujson.Null))
case Some(header) =>
chain.getNumberOfConfirmations(hash).map {
case None =>
throw new RuntimeException(
s"Got unconfirmed header, ${header.hashBE.hex}")
case Some(confs) =>
val chainworkStr = {
val bytes = ByteVector(header.chainWork.toByteArray)
val padded = if (bytes.length <= 32) {
bytes.padLeft(32)
} else bytes
padded.toHex
}
val json = Obj(
"raw" -> Str(header.blockHeader.hex),
"hash" -> Str(header.hashBE.hex),
"confirmations" -> Num(confs),
"height" -> Num(header.height),
"version" -> Num(header.version.toLong.toDouble),
"versionHex" -> Str(header.version.hex),
"merkleroot" -> Str(header.merkleRootHashBE.hex),
"time" -> Num(header.time.toBigInt.toDouble),
"nonce" -> Num(header.nonce.toBigInt.toDouble),
"bits" -> Str(header.nBits.hex),
"difficulty" -> Num(header.difficulty.toDouble),
"chainwork" -> Str(chainworkStr),
"previousblockhash" -> Str(header.previousBlockHashBE.hex)
)
Server.httpSuccess(json)
}
}
}
}
case ServerCommand("getinfo", _) =>
complete {
chain.getBestBlockHeader().map { header =>

View File

@ -418,6 +418,19 @@ object ConvertToPSBT extends ServerJsonModels {
}
}
case class GetBlockHeader(hash: DoubleSha256DigestBE)
object GetBlockHeader extends ServerJsonModels {
def fromJsArr(jsArr: ujson.Arr): Try[GetBlockHeader] =
Try {
require(jsArr.arr.size == 1,
s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1")
GetBlockHeader(DoubleSha256DigestBE(jsArr.arr.head.str))
}
}
case class DecodeRawTransaction(tx: Transaction)
object DecodeRawTransaction extends ServerJsonModels {

View File

@ -28,6 +28,8 @@ case class BlockHeaderDb(
blockHeader
}
lazy val difficulty: BigInt = blockHeader.difficulty
lazy val hash: DoubleSha256Digest = hashBE.flip
override def toString: String = {