diff --git a/src/Features/Blockcore.Features.BlockStore/Controllers/BlockStoreClient.cs b/src/Features/Blockcore.Features.BlockStore/Api/BlockStoreClient.cs similarity index 94% rename from src/Features/Blockcore.Features.BlockStore/Controllers/BlockStoreClient.cs rename to src/Features/Blockcore.Features.BlockStore/Api/BlockStoreClient.cs index 86348d372..1caa32286 100644 --- a/src/Features/Blockcore.Features.BlockStore/Controllers/BlockStoreClient.cs +++ b/src/Features/Blockcore.Features.BlockStore/Api/BlockStoreClient.cs @@ -4,9 +4,11 @@ using System.Threading.Tasks; using Blockcore.Controllers; using Blockcore.Controllers.Models; +using Blockcore.Features.BlockStore.Api.Contollers; +using Blockcore.Features.BlockStore.Api.Models; using Microsoft.Extensions.Logging; -namespace Blockcore.Features.BlockStore.Controllers +namespace Blockcore.Features.BlockStore.Api { /// Rest client for . public interface IBlockStoreClient : IRestApiClientBase diff --git a/src/Features/Blockcore.Features.BlockStore/Controllers/BlockStoreController.cs b/src/Features/Blockcore.Features.BlockStore/Api/Controllers/BlockStoreController.cs similarity index 95% rename from src/Features/Blockcore.Features.BlockStore/Controllers/BlockStoreController.cs rename to src/Features/Blockcore.Features.BlockStore/Api/Controllers/BlockStoreController.cs index e4f053d92..705f7544b 100644 --- a/src/Features/Blockcore.Features.BlockStore/Controllers/BlockStoreController.cs +++ b/src/Features/Blockcore.Features.BlockStore/Api/Controllers/BlockStoreController.cs @@ -3,6 +3,7 @@ using Blockcore.Base; using Blockcore.Controllers.Models; using Blockcore.Features.BlockStore.AddressIndexing; +using Blockcore.Features.BlockStore.Api.Models; using Blockcore.Features.BlockStore.Models; using Blockcore.Interfaces; using Blockcore.Utilities; @@ -12,16 +13,8 @@ using Microsoft.Extensions.Logging; using NBitcoin; -namespace Blockcore.Features.BlockStore.Controllers +namespace Blockcore.Features.BlockStore.Api.Contollers { - public static class BlockStoreRouteEndPoint - { - public const string GetAddressesBalances = "getaddressesbalances"; - public const string GetVerboseAddressesBalances = "getverboseaddressesbalances"; - public const string GetAddressIndexerTip = "addressindexertip"; - public const string GetBlock = "block"; - public const string GetBlockCount = "GetBlockCount"; - } /// Controller providing operations on a blockstore. [ApiController] diff --git a/src/Features/Blockcore.Features.BlockStore/Api/Controllers/BlockStoreRPCController.cs b/src/Features/Blockcore.Features.BlockStore/Api/Controllers/BlockStoreRPCController.cs new file mode 100644 index 000000000..33ca4aa90 --- /dev/null +++ b/src/Features/Blockcore.Features.BlockStore/Api/Controllers/BlockStoreRPCController.cs @@ -0,0 +1,107 @@ +using System.Collections.Generic; +using Blockcore.Consensus; +using Blockcore.Controllers; +using Blockcore.Features.RPC; +using Blockcore.Features.RPC.Exceptions; +using Blockcore.Interfaces; +using Blockcore.Primitives; +using Microsoft.AspNetCore.Mvc; +using NBitcoin; + +namespace Blockcore.Features.BlockStore.Api.Controllers +{ + /// + /// Controller providing RPC operations on a watch-only wallet. + /// + public class BlockStoreRPCController : FeatureController + { + /// Consensus manager class. + private readonly IConsensusManager consensusManager; + + /// Thread safe access to the best chain of block headers from genesis. + private readonly ChainIndexer chainIndexer; + + /// Provides access to the block store database. + private readonly IBlockStore blockStore; + + /// + public BlockStoreRPCController( + IFullNode fullNode, + IConsensusManager consensusManager, + ChainIndexer chainIndexer, + Network network, + IBlockStore blockStore) : base(fullNode: fullNode, consensusManager: consensusManager, chainIndexer: chainIndexer, network: network) + { + this.consensusManager = consensusManager; + this.chainIndexer = chainIndexer; + this.blockStore = blockStore; + } + + /// + /// By default this function only works when there is an unspent output in the utxo for this transaction. + /// To make it work, you need to maintain a transaction index, using the -txindex command line option. + /// + /// The txids to filter + /// If specified, looks for txid in the block with this hash + /// + [ActionName("gettxoutproof")] + [ActionDescription("Checks if transactions are within block. Returns proof of transaction inclusion (raw MerkleBlock).")] + public MerkleBlock GetTxOutProof(string[] txids, string blockhash = "") + { + List transactionIds = new List(); + ChainedHeaderBlock block = null; + foreach (var txString in txids) + { + transactionIds.Add(uint256.Parse(txString)); + } + + if (!string.IsNullOrEmpty(blockhash)) + { + // Loop through txids and veryify that the transaction is in the block. + foreach (var transactionId in transactionIds) + { + var hashBlock = uint256.Parse(blockhash); + ChainedHeader chainedHeader = this.GetTransactionBlock(transactionId); + if (chainedHeader.HashBlock != hashBlock) + { + throw new RPCServerException(RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY, "Not all transactions found in specified or retrieved block"); + } + if (block == null && chainedHeader.BlockDataAvailability == BlockDataAvailabilityState.BlockAvailable) // Only get the block once. + { + block = this.consensusManager.GetBlockData(chainedHeader.HashBlock); + } + } + } + else + { + // Loop through txids and try to find which block they're in. Exit loop once a block is found. + foreach (var transactionId in transactionIds) + { + ChainedHeader chainedHeader = this.GetTransactionBlock(transactionId); + if (chainedHeader.BlockDataAvailability == BlockDataAvailabilityState.BlockAvailable) + { + block = this.consensusManager.GetBlockData(chainedHeader.HashBlock); + break; + } + } + } + if (block == null) + { + throw new RPCServerException(RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + } + var result = new MerkleBlock(block.Block, transactionIds.ToArray()); + return result; + } + + internal ChainedHeader GetTransactionBlock(uint256 trxid) + { + ChainedHeader block = null; + uint256 blockid = this.blockStore?.GetBlockIdByTransactionId(trxid); + if (blockid != null) + { + block = this.chainIndexer?.GetHeader(blockid); + } + return block; + } + } +} diff --git a/src/Features/Blockcore.Features.BlockStore/Api/Models/BlockStoreRouteEndPoint.cs b/src/Features/Blockcore.Features.BlockStore/Api/Models/BlockStoreRouteEndPoint.cs new file mode 100644 index 000000000..c0a1a7614 --- /dev/null +++ b/src/Features/Blockcore.Features.BlockStore/Api/Models/BlockStoreRouteEndPoint.cs @@ -0,0 +1,11 @@ +namespace Blockcore.Features.BlockStore.Api.Models +{ + public static class BlockStoreRouteEndPoint + { + public const string GetAddressesBalances = "getaddressesbalances"; + public const string GetVerboseAddressesBalances = "getverboseaddressesbalances"; + public const string GetAddressIndexerTip = "addressindexertip"; + public const string GetBlock = "block"; + public const string GetBlockCount = "GetBlockCount"; + } +} diff --git a/src/Features/Blockcore.Features.BlockStore/Blockcore.Features.BlockStore.csproj b/src/Features/Blockcore.Features.BlockStore/Blockcore.Features.BlockStore.csproj index b5489edeb..5cdef4b06 100644 --- a/src/Features/Blockcore.Features.BlockStore/Blockcore.Features.BlockStore.csproj +++ b/src/Features/Blockcore.Features.BlockStore/Blockcore.Features.BlockStore.csproj @@ -20,6 +20,7 @@ + diff --git a/src/Features/Blockcore.Features.RPC/RPCClient.cs b/src/Features/Blockcore.Features.RPC/RPCClient.cs index e8a6005f4..f498abad0 100644 --- a/src/Features/Blockcore.Features.RPC/RPCClient.cs +++ b/src/Features/Blockcore.Features.RPC/RPCClient.cs @@ -85,8 +85,8 @@ blockchain getchaintips blockchain getdifficulty blockchain getmempoolinfo blockchain getrawmempool Yes - blockchain gettxout Yes - blockchain gettxoutproof + blockchain gettxout Yes + blockchain gettxoutproof Yes blockchain verifytxoutproof blockchain gettxoutsetinfo blockchain verifychain diff --git a/src/Tests/Blockcore.Features.BlockStore.Tests/BlockStoreControllerTests.cs b/src/Tests/Blockcore.Features.BlockStore.Tests/BlockStoreControllerTests.cs index 66f062f40..2d95e9a22 100644 --- a/src/Tests/Blockcore.Features.BlockStore.Tests/BlockStoreControllerTests.cs +++ b/src/Tests/Blockcore.Features.BlockStore.Tests/BlockStoreControllerTests.cs @@ -3,7 +3,7 @@ using Blockcore.Base; using Blockcore.Controllers.Models; using Blockcore.Features.BlockStore.AddressIndexing; -using Blockcore.Features.BlockStore.Controllers; +using Blockcore.Features.BlockStore.Api.Contollers; using Blockcore.Features.BlockStore.Models; using Blockcore.Interfaces; using Blockcore.Tests.Common;