mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-01-19 05:43:51 +01:00
Merge pull request #1351 from benthecarman/sendrawtx
This commit is contained in:
commit
3d58c66b79
@ -208,6 +208,21 @@ object ConsoleCli {
|
||||
cmd("stop")
|
||||
.action((_, conf) => conf.copy(command = Stop))
|
||||
.text("Request a graceful shutdown of Bitcoin-S"),
|
||||
cmd("sendrawtransaction")
|
||||
.action((_, conf) =>
|
||||
conf.copy(command = SendRawTransaction(EmptyTransaction)))
|
||||
.text("Broadcasts the raw transaction")
|
||||
.children(
|
||||
arg[Transaction]("tx")
|
||||
.text("Transaction serialized in hex")
|
||||
.required()
|
||||
.action((tx, conf) =>
|
||||
conf.copy(command = conf.command match {
|
||||
case sendRawTransaction: SendRawTransaction =>
|
||||
sendRawTransaction.copy(tx = tx)
|
||||
case other => other
|
||||
}))
|
||||
),
|
||||
note(sys.props("line.separator") + "=== PSBT ==="),
|
||||
cmd("combinepsbts")
|
||||
.action((_, conf) => conf.copy(command = CombinePSBTs(Seq.empty)))
|
||||
@ -356,6 +371,8 @@ object ConsoleCli {
|
||||
// peers
|
||||
case GetPeers => RequestParam("getpeers")
|
||||
case Stop => RequestParam("stop")
|
||||
case SendRawTransaction(tx) =>
|
||||
RequestParam("sendrawtransaction", Seq(up.writeJs(tx)))
|
||||
// PSBTs
|
||||
case CombinePSBTs(psbts) =>
|
||||
RequestParam("combinepsbts", Seq(up.writeJs(psbts)))
|
||||
@ -477,6 +494,7 @@ object CliCommand {
|
||||
// Node
|
||||
case object GetPeers extends CliCommand
|
||||
case object Stop extends CliCommand
|
||||
case class SendRawTransaction(tx: Transaction) extends CliCommand
|
||||
|
||||
// Chain
|
||||
case object GetBestBlockHash extends CliCommand
|
||||
|
@ -393,6 +393,26 @@ class RoutesSpec
|
||||
}
|
||||
}
|
||||
|
||||
"send a raw transaction" in {
|
||||
val tx = Transaction(
|
||||
"020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000")
|
||||
|
||||
(mockNode
|
||||
.broadcastTransaction(_: Transaction))
|
||||
.expects(tx)
|
||||
.returning(FutureUtil.unit)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
val route =
|
||||
nodeRoutes.handleCommand(
|
||||
ServerCommand("sendrawtransaction", Arr(Str(tx.hex))))
|
||||
|
||||
Get() ~> route ~> check {
|
||||
contentType shouldEqual `application/json`
|
||||
responseAs[String] shouldEqual s"""{"result":"Broadcasted ${tx.txIdBE}","error":null}"""
|
||||
}
|
||||
}
|
||||
|
||||
"send to an address" in {
|
||||
// positive cases
|
||||
|
||||
|
@ -6,6 +6,8 @@ import akka.http.scaladsl.server._
|
||||
import akka.stream.ActorMaterializer
|
||||
import org.bitcoins.node.Node
|
||||
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
case class NodeRoutes(node: Node)(implicit system: ActorSystem)
|
||||
extends ServerRoute {
|
||||
import system.dispatcher
|
||||
@ -25,5 +27,17 @@ case class NodeRoutes(node: Node)(implicit system: ActorSystem)
|
||||
system.terminate()
|
||||
nodeStopping
|
||||
}
|
||||
|
||||
case ServerCommand("sendrawtransaction", arr) =>
|
||||
SendRawTransaction.fromJsArr(arr) match {
|
||||
case Failure(exception) =>
|
||||
reject(ValidationRejection("failure", Some(exception)))
|
||||
case Success(SendRawTransaction(tx)) =>
|
||||
complete {
|
||||
node.broadcastTransaction(tx).map { _ =>
|
||||
Server.httpSuccess(s"Broadcasted ${tx.txIdBE}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,18 @@ object GetAddressInfo extends ServerJsonModels {
|
||||
}
|
||||
}
|
||||
|
||||
case class SendRawTransaction(tx: Transaction)
|
||||
|
||||
object SendRawTransaction extends ServerJsonModels {
|
||||
|
||||
def fromJsArr(jsArr: ujson.Arr): Try[SendRawTransaction] = {
|
||||
require(jsArr.arr.size == 1,
|
||||
s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1")
|
||||
|
||||
Try(SendRawTransaction(jsToTx(jsArr.arr.head)))
|
||||
}
|
||||
}
|
||||
|
||||
case class CombinePSBTs(psbts: Seq[PSBT])
|
||||
|
||||
object CombinePSBTs extends ServerJsonModels {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.bitcoins.core.api
|
||||
|
||||
import org.bitcoins.core.crypto.DoubleSha256Digest
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
@ -9,6 +10,11 @@ import scala.concurrent.Future
|
||||
*/
|
||||
trait NodeApi {
|
||||
|
||||
/**
|
||||
* Broadcasts the given transaction over the P2P network
|
||||
*/
|
||||
def broadcastTransaction(transaction: Transaction): Future[Unit]
|
||||
|
||||
/**
|
||||
* Request the underlying node to download the given blocks from its peers and feed the blocks to [[org.bitcoins.node.NodeCallbacks]].
|
||||
*/
|
||||
|
@ -8,6 +8,7 @@ import akka.actor.ActorSystem
|
||||
import org.bitcoins.core.api.NodeApi
|
||||
import org.bitcoins.core.crypto.DoubleSha256Digest
|
||||
import org.bitcoins.core.protocol.blockchain.Block
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
||||
import org.bitcoins.node.NodeCallbacks
|
||||
import org.bitcoins.node.networking.peer.DataMessageHandler._
|
||||
@ -85,6 +86,11 @@ val exampleCallback = createCallback(exampleProcessBlock)
|
||||
// Here is where we are defining our actual node api, Ideally this could be it's own class
|
||||
// but for the examples sake we will keep it small.
|
||||
val nodeApi = new NodeApi {
|
||||
|
||||
override def broadcastTransaction(transaction: Transaction): Future[Unit] = {
|
||||
bitcoind.sendRawTransaction(transaction).map(_ => ())
|
||||
}
|
||||
|
||||
override def downloadBlocks(
|
||||
blockHashes: Vector[DoubleSha256Digest]): Future[Unit] = {
|
||||
val blockFs = blockHashes.map(hash => bitcoind.getBlockRaw(hash))
|
||||
|
@ -141,6 +141,7 @@ val keyManager = BIP39KeyManager.initialize(walletConfig.kmParams, bip39Password
|
||||
// once this future completes, we have a initialized
|
||||
// wallet
|
||||
val wallet = Wallet(keyManager, new NodeApi {
|
||||
override def broadcastTransaction(tx: Transaction): Future[Unit] = Future.successful(())
|
||||
override def downloadBlocks(blockHashes: Vector[DoubleSha256Digest]): Future[Unit] = Future.successful(())
|
||||
}, new ChainQueryApi {
|
||||
override def getBlockHeight(blockHash: DoubleSha256DigestBE): Future[Option[Int]] = Future.successful(None)
|
||||
|
@ -189,7 +189,7 @@ trait Node extends NodeApi with ChainQueryApi with P2PLogger {
|
||||
}
|
||||
|
||||
/** Broadcasts the given transaction over the P2P network */
|
||||
def broadcastTransaction(transaction: Transaction): Future[Unit] = {
|
||||
override def broadcastTransaction(transaction: Transaction): Future[Unit] = {
|
||||
val broadcastTx = BroadcastAbleTransaction(transaction)
|
||||
|
||||
txDAO.create(broadcastTx).onComplete {
|
||||
|
@ -4,13 +4,14 @@ import org.bitcoins.chain.blockchain.sync.FilterWithHeaderHash
|
||||
import org.bitcoins.core.api.ChainQueryApi.FilterResponse
|
||||
import org.bitcoins.core.api.{ChainQueryApi, NodeApi, NodeChainQueryApi}
|
||||
import org.bitcoins.core.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
|
||||
import org.bitcoins.core.gcs.{FilterType}
|
||||
import org.bitcoins.core.gcs.FilterType
|
||||
import org.bitcoins.core.protocol.BlockStamp
|
||||
import org.bitcoins.core.protocol.blockchain.{Block, BlockHeader}
|
||||
import org.bitcoins.core.util.{BitcoinSLogger, FutureUtil}
|
||||
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
||||
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
|
||||
import org.bitcoins.commons.jsonmodels.bitcoind.GetBlockFilterResult
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.wallet.Wallet
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
@ -117,6 +118,11 @@ abstract class SyncUtil extends BitcoinSLogger {
|
||||
implicit ec: ExecutionContext): NodeApi = {
|
||||
new NodeApi {
|
||||
|
||||
override def broadcastTransaction(
|
||||
transaction: Transaction): Future[Unit] = {
|
||||
bitcoindRpcClient.sendRawTransaction(transaction).map(_ => ())
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the underlying node to download the given blocks from its peers and feed the blocks to [[org.bitcoins.node.NodeCallbacks]].
|
||||
*/
|
||||
@ -153,9 +159,6 @@ abstract class SyncUtil extends BitcoinSLogger {
|
||||
walletF: Future[Wallet])(implicit ec: ExecutionContext): NodeApi = {
|
||||
new NodeApi {
|
||||
|
||||
/**
|
||||
* Request the underlying node to download the given blocks from its peers and feed the blocks to [[org.bitcoins.node.NodeCallbacks]].
|
||||
*/
|
||||
/**
|
||||
* Request the underlying node to download the given blocks from its peers and feed the blocks to [[org.bitcoins.node.NodeCallbacks]].
|
||||
*/
|
||||
@ -196,6 +199,14 @@ abstract class SyncUtil extends BitcoinSLogger {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the given transaction over the P2P network
|
||||
*/
|
||||
override def broadcastTransaction(
|
||||
transaction: Transaction): Future[Unit] = {
|
||||
bitcoindRpcClient.sendRawTransaction(transaction).map(_ => ())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import org.bitcoins.core.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
|
||||
import org.bitcoins.core.currency._
|
||||
import org.bitcoins.core.gcs.BlockFilter
|
||||
import org.bitcoins.core.protocol.BlockStamp
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.util.FutureUtil
|
||||
import org.bitcoins.db.AppConfig
|
||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
||||
@ -249,6 +250,9 @@ object BitcoinSWalletTest extends WalletLogger {
|
||||
|
||||
object MockNodeApi extends NodeApi {
|
||||
|
||||
override def broadcastTransaction(transaction: Transaction): Future[Unit] =
|
||||
FutureUtil.unit
|
||||
|
||||
override def downloadBlocks(
|
||||
blockHashes: Vector[DoubleSha256Digest]): Future[Unit] = FutureUtil.unit
|
||||
|
||||
|
@ -48,6 +48,9 @@ sealed trait WalletApi {
|
||||
*/
|
||||
trait LockedWalletApi extends WalletApi with WalletLogger {
|
||||
|
||||
def broadcastTransaction(transaction: Transaction): Future[Unit] =
|
||||
nodeApi.broadcastTransaction(transaction)
|
||||
|
||||
/**
|
||||
* Retrieves a bloom filter that that can be sent to a P2P network node
|
||||
* to get information about our transactions, pubkeys and scripts.
|
||||
|
Loading…
Reference in New Issue
Block a user