mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 01:40:55 +01:00
Add getmediantimepast
RPC call (#3921)
* Add `getmediantimepast` RPC call * update docs * respond to the PR comments
This commit is contained in:
parent
0c625346e7
commit
4b07629d56
@ -87,6 +87,9 @@ object ConsoleCli {
|
||||
case gbh: GetBlockHeader => gbh.copy(hash = hash)
|
||||
case other => other
|
||||
}))),
|
||||
cmd("getmediantimepast")
|
||||
.action((_, conf) => conf.copy(command = GetMedianTimePast))
|
||||
.text(s"Get the median time past"),
|
||||
cmd("decoderawtransaction")
|
||||
.action((_, conf) =>
|
||||
conf.copy(command = DecodeRawTransaction(EmptyTransaction)))
|
||||
@ -1692,6 +1695,8 @@ object ConsoleCli {
|
||||
val requestParam: RequestParam = command match {
|
||||
case GetInfo =>
|
||||
RequestParam("getinfo")
|
||||
case GetMedianTimePast =>
|
||||
RequestParam("getmediantimepast")
|
||||
case GetUtxos =>
|
||||
RequestParam("getutxos")
|
||||
case ListReservedUtxos =>
|
||||
@ -2341,6 +2346,8 @@ object CliCommand {
|
||||
case class GetBlockHeader(hash: DoubleSha256DigestBE)
|
||||
extends AppServerCliCommand
|
||||
|
||||
case object GetMedianTimePast extends AppServerCliCommand
|
||||
|
||||
case class DecodeRawTransaction(transaction: Transaction)
|
||||
extends AppServerCliCommand
|
||||
|
||||
|
@ -268,6 +268,20 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
}
|
||||
}
|
||||
|
||||
"return the median time past" in {
|
||||
(mockChainApi.getMedianTimePast: () => Future[Long])
|
||||
.expects()
|
||||
.returning(Future.successful(1234567890L))
|
||||
|
||||
val route =
|
||||
chainRoutes.handleCommand(ServerCommand("getmediantimepast", Arr()))
|
||||
|
||||
Get() ~> route ~> check {
|
||||
assert(contentType == `application/json`)
|
||||
assert(responseAs[String] == """{"result":1234567890,"error":null}""")
|
||||
}
|
||||
}
|
||||
|
||||
"return the wallet's balance" in {
|
||||
(mockWalletApi
|
||||
.getBalance()(_: ExecutionContext))
|
||||
|
@ -95,6 +95,13 @@ case class ChainRoutes(chain: ChainApi, network: BitcoinNetwork)(implicit
|
||||
Server.httpSuccess(info.toJson)
|
||||
}
|
||||
}
|
||||
|
||||
case ServerCommand("getmediantimepast", _) =>
|
||||
complete {
|
||||
chain.getMedianTimePast().map { mtp =>
|
||||
Server.httpSuccess(mtp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -236,6 +236,16 @@ class BlockchainRpcTest extends BitcoindFixturesCachedPairV17 {
|
||||
}
|
||||
}
|
||||
|
||||
it should "calculate median time past" in { nodePair =>
|
||||
val client = nodePair.node1
|
||||
for {
|
||||
medianTime <- client.getMedianTimePast()
|
||||
} yield {
|
||||
val oneHourAgo = (System.currentTimeMillis() / 1000) - 60 * 60
|
||||
assert(medianTime > oneHourAgo)
|
||||
}
|
||||
}
|
||||
|
||||
override def afterAll(): Unit = {
|
||||
val stoppedF = pruneClientF.flatMap(BitcoindRpcTestUtil.stopServer)
|
||||
val _ = Await.result(stoppedF, duration)
|
||||
|
@ -107,7 +107,8 @@ class BitcoindRpcClient(override val instance: BitcoindInstance)(implicit
|
||||
}
|
||||
|
||||
/** Gets the number of compact filters in the database */
|
||||
override def getFilterCount(): Future[Int] = ???
|
||||
override def getFilterCount(): Future[Int] = Future.failed(
|
||||
new UnsupportedOperationException(s"Not implemented: getFilterCount"))
|
||||
|
||||
/** Returns the block height of the given block stamp */
|
||||
override def getHeightByBlockStamp(blockStamp: BlockStamp): Future[Int] =
|
||||
@ -129,6 +130,12 @@ class BitcoindRpcClient(override val instance: BitcoindInstance)(implicit
|
||||
override def epochSecondToBlockHeight(time: Long): Future[Int] =
|
||||
Future.successful(0)
|
||||
|
||||
override def getMedianTimePast(): Future[Long] = {
|
||||
for {
|
||||
info <- getBlockChainInfo
|
||||
} yield info.mediantime.toLong
|
||||
}
|
||||
|
||||
// Node Api
|
||||
|
||||
override def broadcastTransactions(
|
||||
|
@ -966,6 +966,56 @@ class ChainHandler(
|
||||
def toChainHandlerCached: Future[ChainHandlerCached] = {
|
||||
ChainHandler.toChainHandlerCached(this)
|
||||
}
|
||||
|
||||
/** calculates the median time passed */
|
||||
override def getMedianTimePast(): Future[Long] = {
|
||||
/*
|
||||
static constexpr int nMedianTimeSpan = 11;
|
||||
|
||||
int64_t GetMedianTimePast() const
|
||||
{
|
||||
int64_t pmedian[nMedianTimeSpan];
|
||||
int64_t* pbegin = &pmedian[nMedianTimeSpan];
|
||||
int64_t* pend = &pmedian[nMedianTimeSpan];
|
||||
|
||||
const CBlockIndex* pindex = this;
|
||||
for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev)
|
||||
*(--pbegin) = pindex->GetBlockTime();
|
||||
|
||||
std::sort(pbegin, pend);
|
||||
return pbegin[(pend - pbegin)/2];
|
||||
}
|
||||
*/
|
||||
val nMedianTimeSpan = 11
|
||||
|
||||
@tailrec
|
||||
def getNTopHeaders(
|
||||
n: Int,
|
||||
acc: Vector[Future[Option[BlockHeaderDb]]]): Vector[
|
||||
Future[Option[BlockHeaderDb]]] = {
|
||||
if (n == 1)
|
||||
acc
|
||||
else {
|
||||
val prev: Future[Option[BlockHeaderDb]] = acc.last.flatMap {
|
||||
case None => Future.successful(None)
|
||||
case Some(last) => getHeader(last.previousBlockHashBE)
|
||||
}
|
||||
getNTopHeaders(n - 1, acc :+ prev)
|
||||
}
|
||||
}
|
||||
|
||||
val top11 = getNTopHeaders(nMedianTimeSpan,
|
||||
Vector(getBestBlockHeader().map(Option.apply)))
|
||||
|
||||
Future
|
||||
.sequence(top11)
|
||||
.map(_.collect { case Some(header) =>
|
||||
header.time.toLong
|
||||
})
|
||||
.map { times =>
|
||||
times.sorted.apply(times.size / 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ChainHandler {
|
||||
|
@ -41,6 +41,8 @@ trait ChainQueryApi {
|
||||
/** Gets the block height of the closest block to the given time */
|
||||
def epochSecondToBlockHeight(time: Long): Future[Int]
|
||||
|
||||
/** calculates the median time passed */
|
||||
def getMedianTimePast(): Future[Long]
|
||||
}
|
||||
|
||||
object ChainQueryApi {
|
||||
|
@ -152,6 +152,7 @@ the `-p 9999:9999` port mapping on the docker container to adjust for this.
|
||||
- `hash` - The block hash
|
||||
- `decoderawtransaction` `tx` - `Decode the given raw hex transaction`
|
||||
- `tx` - Transaction encoded in hex to decode
|
||||
- `getmediantimepast` - Returns the median time past
|
||||
|
||||
### Wallet
|
||||
- `rescan` `[options]` - Rescan for wallet UTXOs
|
||||
|
@ -59,6 +59,8 @@ trait ChainQueryApi {
|
||||
def getFiltersBetweenHeights(
|
||||
startHeight: Int,
|
||||
endHeight: Int): Future[Vector[FilterResponse]]
|
||||
|
||||
def getMedianTimePast(): Future[Long]
|
||||
}
|
||||
```
|
||||
|
||||
@ -180,6 +182,8 @@ val chainApi = new ChainQueryApi {
|
||||
|
||||
Future.sequence(filterFs)
|
||||
}
|
||||
|
||||
override def getMedianTimePast(): Future[Long] = bitcoind.getMedianTimePast()
|
||||
}
|
||||
|
||||
// Finally, we can initialize our wallet with our own node api
|
||||
|
@ -81,6 +81,7 @@ val chainApi = new ChainQueryApi {
|
||||
override def getFilterCount(): Future[Int] = Future.successful(0)
|
||||
override def getHeightByBlockStamp(blockStamp: BlockStamp): Future[Int] = Future.successful(0)
|
||||
override def getFiltersBetweenHeights(startHeight: Int, endHeight: Int): Future[Vector[FilterResponse]] = Future.successful(Vector.empty)
|
||||
override def getMedianTimePast(): Future[Long] = Future.successful(0L)
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -121,12 +121,16 @@ class NeutrinoNodeTest extends NodeTestWithCachedBitcoindPair {
|
||||
//the send headers message.
|
||||
for {
|
||||
_ <- NodeTestUtil.awaitSync(node, bitcoind)
|
||||
} yield {
|
||||
val isCancelled = cancellable.cancel()
|
||||
if (!isCancelled) {
|
||||
logger.warn(s"Failed to cancel generating blocks on bitcoind")
|
||||
_ = {
|
||||
val isCancelled = cancellable.cancel()
|
||||
if (!isCancelled) {
|
||||
logger.warn(s"Failed to cancel generating blocks on bitcoind")
|
||||
}
|
||||
}
|
||||
succeed
|
||||
mtp1 <- bitcoind.getMedianTimePast()
|
||||
mtp2 <- node.chainApiFromDb().flatMap(_.getMedianTimePast())
|
||||
} yield {
|
||||
assert(mtp1 == mtp2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -342,4 +342,7 @@ trait Node extends NodeApi with ChainQueryApi with P2PLogger {
|
||||
override def epochSecondToBlockHeight(time: Long): Future[Int] =
|
||||
chainApiFromDb().flatMap(_.epochSecondToBlockHeight(time))
|
||||
|
||||
override def getMedianTimePast(): Future[Long] =
|
||||
chainApiFromDb().flatMap(_.getMedianTimePast())
|
||||
|
||||
}
|
||||
|
@ -165,5 +165,9 @@ trait BaseNodeTest extends BitcoinSFixture with EmbeddedPg {
|
||||
|
||||
override def epochSecondToBlockHeight(time: Long): Future[Int] =
|
||||
Future.successful(0)
|
||||
|
||||
/** calculates the median time passed */
|
||||
override def getMedianTimePast(): Future[Long] =
|
||||
Future.successful(0L)
|
||||
}
|
||||
}
|
||||
|
@ -129,6 +129,10 @@ trait BaseWalletTest extends EmbeddedPg { _: Suite with BitcoinSAkkaAsyncTest =>
|
||||
|
||||
override def epochSecondToBlockHeight(time: Long): Future[Int] =
|
||||
Future.successful(0)
|
||||
|
||||
/** calculates the median time passed */
|
||||
override def getMedianTimePast(): Future[Long] =
|
||||
Future.successful(0L)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -299,6 +299,10 @@ object BitcoinSWalletTest extends WalletLogger {
|
||||
|
||||
override def epochSecondToBlockHeight(time: Long): Future[Int] =
|
||||
Future.successful(0)
|
||||
|
||||
/** calculates the median time passed */
|
||||
override def getMedianTimePast(): Future[Long] =
|
||||
Future.successful(0L)
|
||||
}
|
||||
|
||||
private[bitcoins] class RandomFeeProvider extends FeeRateApi {
|
||||
|
Loading…
Reference in New Issue
Block a user