mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-01-19 05:43:51 +01:00
Add getblockheader cli command (#2424)
This commit is contained in:
parent
d6f27f5bbe
commit
f10bdce179
@ -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(
|
||||
|
@ -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))
|
||||
|
@ -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 =>
|
||||
|
@ -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 {
|
||||
|
@ -28,6 +28,8 @@ case class BlockHeaderDb(
|
||||
blockHeader
|
||||
}
|
||||
|
||||
lazy val difficulty: BigInt = blockHeader.difficulty
|
||||
|
||||
lazy val hash: DoubleSha256Digest = hashBE.flip
|
||||
|
||||
override def toString: String = {
|
||||
|
Loading…
Reference in New Issue
Block a user