1414using Blockcore . Interfaces ;
1515using Blockcore . P2P ;
1616using Blockcore . P2P . Peer ;
17+ using Blockcore . Primitives ;
1718using Blockcore . Utilities ;
1819using Blockcore . Utilities . JsonErrors ;
1920using Blockcore . Utilities . ModelStateErrors ;
@@ -84,6 +85,8 @@ public class NodeController : Controller
8485
8586 private readonly ISelfEndpointTracker selfEndpointTracker ;
8687
88+ private readonly IConsensusManager consensusManager ;
89+
8790 public NodeController (
8891 ChainIndexer chainIndexer ,
8992 IChainState chainState ,
@@ -99,7 +102,8 @@ public NodeController(
99102 IGetUnspentTransaction getUnspentTransaction = null ,
100103 INetworkDifficulty networkDifficulty = null ,
101104 IPooledGetUnspentTransaction pooledGetUnspentTransaction = null ,
102- IPooledTransaction pooledTransaction = null )
105+ IPooledTransaction pooledTransaction = null ,
106+ IConsensusManager consensusManager = null )
103107 {
104108 Guard . NotNull ( fullNode , nameof ( fullNode ) ) ;
105109 Guard . NotNull ( network , nameof ( network ) ) ;
@@ -111,6 +115,7 @@ public NodeController(
111115 Guard . NotNull ( dateTimeProvider , nameof ( dateTimeProvider ) ) ;
112116 Guard . NotNull ( asyncProvider , nameof ( asyncProvider ) ) ;
113117 Guard . NotNull ( selfEndpointTracker , nameof ( selfEndpointTracker ) ) ;
118+ Guard . NotNull ( consensusManager , nameof ( consensusManager ) ) ;
114119
115120 this . chainIndexer = chainIndexer ;
116121 this . chainState = chainState ;
@@ -128,6 +133,7 @@ public NodeController(
128133 this . networkDifficulty = networkDifficulty ;
129134 this . pooledGetUnspentTransaction = pooledGetUnspentTransaction ;
130135 this . pooledTransaction = pooledTransaction ;
136+ this . consensusManager = consensusManager ;
131137 }
132138
133139 /// <summary>
@@ -247,45 +253,75 @@ public IActionResult GetBlockHeader([FromQuery] string hash, bool isJsonFormat =
247253 /// </summary>
248254 /// <param name="trxid">The transaction ID (a hash of the trancaction).</param>
249255 /// <param name="verbose">A flag that specifies whether to return verbose information about the transaction.</param>
256+ /// <param name="blockHash">The hash of the block in which to look for the transaction.</param>
250257 /// <returns>Json formatted <see cref="TransactionBriefModel"/> or <see cref="TransactionVerboseModel"/>. <c>null</c> if transaction not found. Returns <see cref="IActionResult"/> formatted error if otherwise fails.</returns>
251258 /// <exception cref="ArgumentNullException">Thrown if fullNode, network, or chain are not available.</exception>
252259 /// <exception cref="ArgumentException">Thrown if trxid is empty or not a valid<see cref="uint256"/>.</exception>
253260 /// <remarks>Requires txindex=1, otherwise only txes that spend or create UTXOs for a wallet can be returned.</remarks>
254261 [ Route ( "getrawtransaction" ) ]
255262 [ HttpGet ]
256- public async Task < IActionResult > GetRawTransactionAsync ( [ FromQuery ] string trxid , bool verbose = false )
263+ public async Task < IActionResult > GetRawTransactionAsync ( [ FromQuery ] string txid , bool verbose = false , string blockHash = null )
257264 {
258265 try
259266 {
260- Guard . NotEmpty ( trxid , nameof ( trxid ) ) ;
267+ Guard . NotEmpty ( txid , nameof ( txid ) ) ;
261268
262- uint256 txid ;
263- if ( ! uint256 . TryParse ( trxid , out txid ) )
269+ if ( ! uint256 . TryParse ( txid , out uint256 trxid ) )
264270 {
265271 throw new ArgumentException ( nameof ( trxid ) ) ;
266272 }
267273
268- // First tries to find a pooledTransaction. If can't, will retrieve it from the blockstore if it exists.
269- Transaction trx = this . pooledTransaction != null ? await this . pooledTransaction . GetTransaction ( txid ) . ConfigureAwait ( false ) : null ;
270- if ( trx == null )
274+ uint256 hash = null ;
275+ if ( ! string . IsNullOrEmpty ( blockHash ) && ! uint256 . TryParse ( blockHash , out hash ) )
271276 {
272- trx = this . blockStore ? . GetTransactionById ( txid ) ;
277+ throw new ArgumentException ( nameof ( blockHash ) ) ;
273278 }
274279
275- if ( trx == null )
280+ // Special exception for the genesis block coinbase transaction.
281+ if ( trxid == this . network . GetGenesis ( ) . GetMerkleRoot ( ) . Hash )
276282 {
277- return this . Json ( null ) ;
283+ throw new Exception ( "The genesis block coinbase is not considered an ordinary transaction and cannot be retrieved." ) ;
284+ }
285+
286+ Transaction trx = null ;
287+ ChainedHeaderBlock chainedHeaderBlock = null ;
288+
289+ if ( hash == null )
290+ {
291+ // Look for the transaction in the mempool, and if not found, look in the indexed transactions.
292+ trx = ( this . pooledTransaction == null ? null : await this . pooledTransaction . GetTransaction ( trxid ) . ConfigureAwait ( false ) ) ??
293+ this . blockStore . GetTransactionById ( trxid ) ;
294+
295+ if ( trx == null )
296+ {
297+ throw new Exception ( "No such mempool transaction. Use -txindex to enable blockchain transaction queries." ) ;
298+ }
299+ }
300+ else
301+ {
302+ // Retrieve the block specified by the block hash.
303+ chainedHeaderBlock = this . consensusManager . GetBlockData ( hash ) ;
304+
305+ if ( chainedHeaderBlock == null )
306+ {
307+ throw new Exception ( "Block hash not found." ) ;
308+ }
309+
310+ trx = chainedHeaderBlock . Block . Transactions . SingleOrDefault ( t => t . GetHash ( ) == trxid ) ;
311+
312+ if ( trx == null )
313+ {
314+ return this . Json ( null ) ;
315+ }
278316 }
279317
280318 if ( verbose )
281319 {
282- ChainedHeader block = this . GetTransactionBlock ( txid , this . fullNode , this . chainIndexer ) ;
320+ ChainedHeader block = chainedHeaderBlock != null ? chainedHeaderBlock . ChainedHeader : this . GetTransactionBlock ( trxid ) ;
283321 return this . Json ( new TransactionVerboseModel ( trx , this . network , block , this . chainState ? . ConsensusTip ) ) ;
284322 }
285323 else
286- {
287324 return this . Json ( new TransactionBriefModel ( trx ) ) ;
288- }
289325 }
290326 catch ( Exception e )
291327 {
@@ -634,5 +670,23 @@ internal static Target GetNetworkDifficulty(INetworkDifficulty networkDifficulty
634670 {
635671 return networkDifficulty ? . GetNetworkDifficulty ( ) ;
636672 }
673+
674+ /// <summary>
675+ /// Retrieves the block that the transaction is in.
676+ /// </summary>
677+ /// <param name="trxid">The transaction id.</param>
678+ /// <returns>Returns the <see cref="ChainedHeader"/> that the transaction is in. Returns <c>null</c> if not found.</returns>
679+ private ChainedHeader GetTransactionBlock ( uint256 trxid )
680+ {
681+ ChainedHeader block = null ;
682+
683+ uint256 blockid = this . blockStore ? . GetBlockIdByTransactionId ( trxid ) ;
684+ if ( blockid != null )
685+ {
686+ block = this . chainIndexer ? . GetHeader ( blockid ) ;
687+ }
688+
689+ return block ;
690+ }
637691 }
638692}
0 commit comments