public async Task GetMaturedBlockDeposits_Fails_When_Block_Not_In_Chain_Async() { FederationGatewayController controller = this.CreateController(); ChainedHeader tip = ChainedHeadersHelper.CreateConsecutiveHeaders(3, null, true)[2]; this.consensusManager.Tip.Returns(tip); IActionResult result = await controller.GetMaturedBlockDepositsAsync(new MaturedBlockRequestModel(1, 1000)).ConfigureAwait(false); result.Should().BeOfType <ErrorResult>(); var error = result as ErrorResult; error.Should().NotBeNull(); var errorResponse = error.Value as ErrorResponse; errorResponse.Should().NotBeNull(); errorResponse.Errors.Should().HaveCount(1); errorResponse.Errors.Should().Contain( e => e.Status == (int)HttpStatusCode.BadRequest); errorResponse.Errors.Should().Contain( e => e.Message.Contains("Unable to get deposits for block at height")); }
public async Task GetMaturedBlockDeposits_Gets_All_Matured_Block_Deposits_Async() { ChainedHeader tip = ChainedHeadersHelper.CreateConsecutiveHeaders(10, null, true).Last(); this.consensusManager.Tip.Returns(tip); FederationGatewayController controller = this.CreateController(); ChainedHeader earlierBlock = tip.GetAncestor(2); int minConfirmations = 2; this.depositExtractor.MinimumDepositConfirmations.Returns((uint)minConfirmations); int depositExtractorCallCount = 0; this.depositExtractor.ExtractBlockDeposits(Arg.Any <ChainedHeaderBlock>()).Returns(new MaturedBlockDepositsModel(null, null)); this.depositExtractor.When(x => x.ExtractBlockDeposits(Arg.Any <ChainedHeaderBlock>())).Do(info => { depositExtractorCallCount++; }); IActionResult result = await controller.GetMaturedBlockDepositsAsync(new MaturedBlockRequestModel(earlierBlock.Height, 1000)).ConfigureAwait(false); result.Should().BeOfType <JsonResult>(); // If the minConfirmations == 0 and this.chain.Height == earlierBlock.Height then expectedCallCount must be 1. int expectedCallCount = (tip.Height - minConfirmations) - earlierBlock.Height + 1; depositExtractorCallCount.Should().Be(expectedCallCount); }
public void GetMaturedBlockDeposits_Fails_When_Block_Height_Greater_Than_Minimum_Deposit_Confirmations_Async() { ChainedHeader tip = ChainedHeadersHelper.CreateConsecutiveHeaders(5, null, true).Last(); this.consensusManager.Tip.Returns(tip); FederationGatewayController controller = this.CreateController(); // Minimum deposit confirmations : 2 this.depositExtractor.MinimumDepositConfirmations.Returns((uint)2); int maturedHeight = (int)(tip.Height - this.depositExtractor.MinimumDepositConfirmations); // Back online at block height : 3 // 0 - 1 - 2 - 3 ChainedHeader earlierBlock = tip.GetAncestor(maturedHeight + 1); // Mature height = 2 (Chain header height (4) - Minimum deposit confirmations (2)) IActionResult result = controller.GetMaturedBlockDeposits(new MaturedBlockRequestModel(earlierBlock.Height, 1000)); // Block height (3) > Mature height (2) - returns error message var maturedBlockDepositsResult = (result as JsonResult).Value as SerializableResult <List <MaturedBlockDepositsModel> >; maturedBlockDepositsResult.Should().NotBeNull(); maturedBlockDepositsResult.IsSuccess.Should().Be(false); maturedBlockDepositsResult.Message.Should().Be(string.Format(MaturedBlocksProvider.RetrieveBlockHeightHigherThanMaturedTipMessage, earlierBlock.Height, maturedHeight)); }
public void ReceiveCurrentBlockTip_Should_Call_LeaderProdvider_Update() { var controller = new FederationGatewayController( this.loggerFactory, this.maturedBlockReceiver, this.maturedBlocksRequester, this.leaderProvider, this.chain, GetMaturedBlocksProvider(), this.depositExtractor, this.leaderReceiver); var model = new BlockTipModel(TestingValues.GetUint256(), TestingValues.GetPositiveInt(), TestingValues.GetPositiveInt()); var leaderProviderCallCount = 0; this.leaderProvider.When(x => x.Update(Arg.Any <BlockTipModel>())).Do(info => { leaderProviderCallCount++; }); IActionResult result = controller.ReceiveCurrentBlockTip(model); result.Should().BeOfType <OkResult>(); leaderProviderCallCount.Should().Be(1); }
public void ReceiveMaturedBlock_Should_Call_ReceivedMatureBlockDeposits() { var controller = new FederationGatewayController( this.loggerFactory, this.maturedBlockReceiver, this.maturedBlocksRequester, this.leaderProvider, this.chain, GetMaturedBlocksProvider(), this.depositExtractor, this.leaderReceiver); HashHeightPair hashHeightPair = TestingValues.GetHashHeightPair(); var deposits = new MaturedBlockDepositsModel(new MaturedBlockModel() { BlockHash = hashHeightPair.Hash, BlockHeight = hashHeightPair.Height }, new[] { new Deposit(0, Money.COIN * 10000, "TTMM7qGGxD5c77pJ8puBg7sTLAm2zZNBwK", hashHeightPair.Height, hashHeightPair.Hash) }); var callCount = 0; this.maturedBlockReceiver.When(x => x.ReceiveMaturedBlockDeposits(Arg.Any <IMaturedBlockDeposits[]>())).Do(info => { callCount++; }); controller.ReceiveMaturedBlock(deposits); callCount.Should().Be(1); }
public async Task GetMaturedBlockDeposits_Fails_When_Block_Height_Greater_Than_Minimum_Deposit_Confirmations_Async() { ChainedHeader tip = ChainedHeadersHelper.CreateConsecutiveHeaders(5, null, true).Last(); this.consensusManager.Tip.Returns(tip); // Minimum deposit confirmations : 2 IFederatedPegSettings federatedPegSettings = Substitute.For <IFederatedPegSettings>(); federatedPegSettings.MinimumConfirmationsNormalDeposits.Returns(2); FederationGatewayController controller = this.CreateController(federatedPegSettings); int maturedHeight = (int)(tip.Height - 2); // Back online at block height : 3 // 0 - 1 - 2 - 3 ChainedHeader earlierBlock = tip.GetAncestor(maturedHeight + 1); // Mature height = 2 (Chain header height (4) - Minimum deposit confirmations (2)) IActionResult result = await controller.GetMaturedBlockDepositsAsync(earlierBlock.Height); // Block height (3) > Mature height (2) - returns error message var maturedBlockDepositsResult = (result as JsonResult).Value as SerializableResult <List <MaturedBlockDepositsModel> >; maturedBlockDepositsResult.Should().NotBeNull(); maturedBlockDepositsResult.Value.Count().Should().Be(0); Assert.NotNull(maturedBlockDepositsResult.Message); }
public void GetMaturedBlockDeposits_Fails_When_Block_Height_Greater_Than_Minimum_Deposit_Confirmations_Async() { ChainedHeader tip = ChainedHeadersHelper.CreateConsecutiveHeaders(5, null, true).Last(); this.consensusManager.Tip.Returns(tip); // Minimum deposit confirmations : 2 IFederatedPegSettings federatedPegSettings = Substitute.For <IFederatedPegSettings>(); federatedPegSettings.MinimumConfirmationsNormalDeposits.Returns(2); FederationGatewayController controller = this.CreateController(federatedPegSettings); int maturedHeight = (int)(tip.Height - 2); // Back online at block height : 3 // 0 - 1 - 2 - 3 ChainedHeader earlierBlock = tip.GetAncestor(maturedHeight + 1); // Mature height = 2 (Chain header height (4) - Minimum deposit confirmations (2)) IActionResult result = controller.GetMaturedBlockDeposits(earlierBlock.Height); // Block height (3) > Mature height (2) - returns error message var maturedBlockDepositsResult = (result as JsonResult).Value as SerializableResult <List <MaturedBlockDepositsModel> >; maturedBlockDepositsResult.Should().NotBeNull(); maturedBlockDepositsResult.Value.Count().Should().Be(0); maturedBlockDepositsResult.Message.Should().Contain(string.Format("The submitted block height of {0} is not mature enough for '{1}' deposits, blocks below {2} can be returned.", earlierBlock.Height, DepositRetrievalType.Normal, maturedHeight)); }
private FederationGatewayController CreateController(IFederatedPegSettings federatedPegSettings) { var controller = new FederationGatewayController( this.loggerFactory, this.GetMaturedBlocksProvider(federatedPegSettings), this.federatedPegSettings, this.federationWalletManager, this.federationManager); return(controller); }
public void Call_Mainchain_Gateway_Get_Info() { var federation = new Federation(new[] { new PubKey("02fad5f3c4fdf4c22e8be4cfda47882fff89aaa0a48c1ccad7fa80dc5fee9ccec3"), new PubKey("02503f03243d41c141172465caca2f5cef7524f149e965483be7ce4e44107d7d35"), new PubKey("03be943c3a31359cd8e67bedb7122a0898d2c204cf2d0119e923ded58c429ef97c") }); Network sideChainNetwork = new TestNetwork(federation); string redeemScript = PayToFederationTemplate.Instance.GenerateScriptPubKey(federation.Id).ToString(); string federationIps = "127.0.0.1:36201,127.0.0.1:36202,127.0.0.1:36203"; string multisigPubKey = federation.GetFederationDetails().transactionSigningKeys.TakeLast(1).First().ToHex(); string[] args = new[] { "-mainchain", "-testnet", $"-federationips={federationIps}", $"-redeemscript={redeemScript}", $"-publickey={multisigPubKey}", "-mincoinmaturity=1", "-mindepositconfirmations=1" }; var nodeSettings = new NodeSettings(sideChainNetwork, ProtocolVersion.ALT_PROTOCOL_VERSION, args: args); this.federationWalletManager.IsFederationWalletActive().Returns(true); var settings = new FederatedPegSettings(nodeSettings, new CounterChainNetworkWrapper(KnownNetworks.StraxRegTest)); var controller = new FederationGatewayController( Substitute.For <IAsyncProvider>(), new ChainIndexer(), Substitute.For <IConnectionManager>(), this.crossChainTransferStore, this.GetMaturedBlocksProvider(settings), this.network, settings, this.federationWalletManager, Substitute.For <IFullNode>(), Substitute.For <IPeerBanning>(), this.federationManager); IActionResult result = controller.GetInfo(); result.Should().BeOfType <JsonResult>(); ((JsonResult)result).Value.Should().BeOfType <FederationGatewayInfoModel>(); var model = ((JsonResult)result).Value as FederationGatewayInfoModel; model.IsMainChain.Should().BeTrue(); model.FederationMiningPubKeys.Should().BeNull(); model.MiningPublicKey.Should().BeNull(); model.MultiSigRedeemScript.Should().Be(redeemScript); string.Join(",", model.FederationNodeIpEndPoints).Should().Be(federationIps); model.IsActive.Should().BeTrue(); model.MinimumDepositConfirmationsSmallDeposits.Should().Be(25); model.MinimumDepositConfirmationsNormalDeposits.Should().Be(80); model.MultisigPublicKey.Should().Be(multisigPubKey); }
private FederationGatewayController CreateController(IFederatedPegSettings federatedPegSettings) { var controller = new FederationGatewayController( this.crossChainTransferStore, this.loggerFactory, this.GetMaturedBlocksProvider(federatedPegSettings), this.network, this.federatedPegSettings, this.federationWalletManager, this.signedMultisigTransactionBroadcaster, this.federationManager); return(controller); }
private FederationGatewayController CreateController() { var controller = new FederationGatewayController( this.loggerFactory, this.network, this.leaderProvider, this.GetMaturedBlocksProvider(), this.leaderReceiver, this.federationGatewaySettings, this.federationWalletManager, this.federationManager); return(controller); }
public void GetMaturedBlockDeposits_Gets_All_Matured_Block_Deposits() { ChainedHeader tip = ChainedHeadersHelper.CreateConsecutiveHeaders(10, null, true).Last(); this.consensusManager.Tip.Returns(tip); int minConfirmations = 2; // Minimum deposit confirmations : 2 IFederatedPegSettings federatedPegSettings = Substitute.For <IFederatedPegSettings>(); federatedPegSettings.MinimumConfirmationsNormalDeposits.Returns(minConfirmations); FederationGatewayController controller = this.CreateController(federatedPegSettings); ChainedHeader earlierBlock = tip.GetAncestor(minConfirmations); int depositExtractorCallCount = 0; this.depositExtractor.ExtractDepositsFromBlock(Arg.Any <Block>(), Arg.Any <int>(), Arg.Any <DepositRetrievalType[]>()).Returns(new List <IDeposit>()); this.depositExtractor.When(x => x.ExtractDepositsFromBlock(Arg.Any <Block>(), Arg.Any <int>(), Arg.Any <DepositRetrievalType[]>())).Do(info => { depositExtractorCallCount++; }); this.consensusManager.GetBlocksAfterBlock(Arg.Any <ChainedHeader>(), MaturedBlocksProvider.MaturedBlocksBatchSize, Arg.Any <CancellationTokenSource>()).Returns(delegate(CallInfo info) { var chainedHeader = (ChainedHeader)info[0]; var blocks = new List <ChainedHeaderBlock>(); int startHeight = (chainedHeader == null) ? 0 : (chainedHeader.Height + 1); for (int i = startHeight; i <= this.consensusManager.Tip.Height; i++) { blocks.Add(new ChainedHeaderBlock(new Block(), tip.GetAncestor(i))); } return(blocks); }); IActionResult result = controller.GetMaturedBlockDeposits(earlierBlock.Height); result.Should().BeOfType <JsonResult>(); var maturedBlockDepositsResult = (result as JsonResult).Value as SerializableResult <List <MaturedBlockDepositsModel> >; maturedBlockDepositsResult.Should().NotBeNull(); maturedBlockDepositsResult.Message.Should().Be(string.Empty); // Heights 0 to 10. depositExtractorCallCount.Should().Be(11); }
private FederationGatewayController CreateController(IFederatedPegSettings federatedPegSettings) { var controller = new FederationGatewayController( Substitute.For <IAsyncProvider>(), new ChainIndexer(), Substitute.For <IConnectionManager>(), this.crossChainTransferStore, this.GetMaturedBlocksProvider(federatedPegSettings), this.network, this.federatedPegSettings, this.federationWalletManager, Substitute.For <IFullNode>(), this.federationManager); return(controller); }
public async void GetMaturedBlockDeposits_Fails_When_Block_Height_Greater_Than_Minimum_Deposit_Confirmations_Async() { // Chain header height : 4 // 0 - 1 - 2 - 3 - 4 this.chain = this.BuildChain(5); var controller = new FederationGatewayController( this.loggerFactory, this.maturedBlockReceiver, this.maturedBlocksRequester, this.leaderProvider, this.chain, this.GetMaturedBlocksProvider(), this.depositExtractor, this.leaderReceiver); // Minimum deposit confirmations : 2 this.depositExtractor.MinimumDepositConfirmations.Returns((uint)2); int maturedHeight = (int)(this.chain.Tip.Height - this.depositExtractor.MinimumDepositConfirmations); // Back online at block height : 3 // 0 - 1 - 2 - 3 ChainedHeader earlierBlock = this.chain.GetBlock(maturedHeight + 1); // Mature height = 2 (Chain header height (4) - Minimum deposit confirmations (2)) IActionResult result = await controller.GetMaturedBlockDepositsAsync(new MaturedBlockRequestModel(earlierBlock.Height)).ConfigureAwait(false); // Block height (3) > Mature height (2) - returns error message result.Should().BeOfType <ErrorResult>(); var error = result as ErrorResult; error.Should().NotBeNull(); var errorResponse = error.Value as ErrorResponse; errorResponse.Should().NotBeNull(); errorResponse.Errors.Should().HaveCount(1); errorResponse.Errors.Should().Contain( e => e.Status == (int)HttpStatusCode.BadRequest); errorResponse.Errors.Should().Contain( e => e.Message.Contains($"Block height {earlierBlock.Height} submitted is not mature enough. Blocks less than a height of {maturedHeight} can be processed.")); }
public void ReceiveCurrentBlockTip_Should_Call_LeaderProdvider_Update() { FederationGatewayController controller = this.CreateController(); var model = new BlockTipModel(TestingValues.GetUint256(), TestingValues.GetPositiveInt(), TestingValues.GetPositiveInt()); int leaderProviderCallCount = 0; this.leaderProvider.When(x => x.Update(Arg.Any <BlockTipModel>())).Do(info => { leaderProviderCallCount++; }); IActionResult result = controller.PushCurrentBlockTip(model); result.Should().BeOfType <OkResult>(); leaderProviderCallCount.Should().Be(1); }
public void GetMaturedBlockDeposits_Gets_All_Matured_Block_Deposits() { ChainedHeader tip = ChainedHeadersHelper.CreateConsecutiveHeaders(10, null, true).Last(); this.consensusManager.Tip.Returns(tip); int minConfirmations = 2; // Minimum deposit confirmations : 2 IFederatedPegSettings federatedPegSettings = Substitute.For <IFederatedPegSettings>(); federatedPegSettings.MinimumConfirmationsNormalDeposits.Returns(minConfirmations); FederationGatewayController controller = this.CreateController(federatedPegSettings); ChainedHeader earlierBlock = tip.GetAncestor(minConfirmations); int depositExtractorCallCount = 0; this.depositExtractor.ExtractBlockDeposits(Arg.Any <ChainedHeaderBlock>(), DepositRetrievalType.Normal).Returns(new MaturedBlockDepositsModel(null, null)); this.depositExtractor.When(x => x.ExtractBlockDeposits(Arg.Any <ChainedHeaderBlock>(), DepositRetrievalType.Normal)).Do(info => { depositExtractorCallCount++; }); this.consensusManager.GetBlockData(Arg.Any <List <uint256> >()).ReturnsForAnyArgs((x) => { List <uint256> hashes = x.ArgAt <List <uint256> >(0); return(hashes.Select((h) => new ChainedHeaderBlock(new Block(), earlierBlock)).ToArray()); }); IActionResult result = controller.GetMaturedBlockDeposits(earlierBlock.Height); result.Should().BeOfType <JsonResult>(); var maturedBlockDepositsResult = (result as JsonResult).Value as SerializableResult <List <MaturedBlockDepositsModel> >; maturedBlockDepositsResult.Should().NotBeNull(); maturedBlockDepositsResult.Message.Should().Be(string.Empty); // If the minConfirmations == 0 and this.chain.Height == earlierBlock.Height then expectedCallCount must be 1. int expectedCallCount = (tip.Height - minConfirmations) - earlierBlock.Height + 1; depositExtractorCallCount.Should().Be(expectedCallCount); }
public void Call_Sidechain_Gateway_Get_Info() { string redeemScript = "2 02fad5f3c4fdf4c22e8be4cfda47882fff89aaa0a48c1ccad7fa80dc5fee9ccec3 02503f03243d41c141172465caca2f5cef7524f149e965483be7ce4e44107d7d35 03be943c3a31359cd8e67bedb7122a0898d2c204cf2d0119e923ded58c429ef97c 3 OP_CHECKMULTISIG"; string federationIps = "127.0.0.1:36201,127.0.0.1:36202,127.0.0.1:36203"; string multisigPubKey = "03be943c3a31359cd8e67bedb7122a0898d2c204cf2d0119e923ded58c429ef97c"; string[] args = new[] { "-sidechain", "-regtest", $"-federationips={federationIps}", $"-redeemscript={redeemScript}", $"-publickey={multisigPubKey}", "-mincoinmaturity=1", "-mindepositconfirmations=1" }; NodeSettings nodeSettings = new NodeSettings(FederatedPegNetwork.NetworksSelector.Regtest(), ProtocolVersion.ALT_PROTOCOL_VERSION, args: args); this.federationWalletManager.IsFederationActive().Returns(true); this.federationManager.Initialize(); FederationGatewaySettings settings = new FederationGatewaySettings(nodeSettings); var controller = new FederationGatewayController( this.loggerFactory, this.network, this.leaderProvider, this.GetMaturedBlocksProvider(), this.leaderReceiver, settings, this.federationWalletManager, this.federationManager); IActionResult result = controller.GetInfo(); result.Should().BeOfType <JsonResult>(); ((JsonResult)result).Value.Should().BeOfType <FederationGatewayInfoModel>(); FederationGatewayInfoModel model = ((JsonResult)result).Value as FederationGatewayInfoModel; model.IsMainChain.Should().BeFalse(); model.FederationMiningPubKeys.Should().Equal(((PoAConsensusOptions)FederatedPegNetwork.NetworksSelector.Regtest().Consensus.Options).GenesisFederationPublicKeys.Select(keys => keys.ToString())); model.MultiSigRedeemScript.Should().Be(redeemScript); string.Join(",", model.FederationNodeIpEndPoints).Should().Be(federationIps); model.IsActive.Should().BeTrue(); model.MinCoinMaturity.Should().Be(1); model.MinimumDepositConfirmations.Should().Be(1); model.MultisigPublicKey.Should().Be(multisigPubKey); }
public async Task GetMaturedBlockDeposits_Fails_When_Block_Height_Greater_Than_Minimum_Deposit_Confirmations_Async() { ChainedHeader tip = ChainedHeadersHelper.CreateConsecutiveHeaders(5, null, true).Last(); this.consensusManager.Tip.Returns(tip); FederationGatewayController controller = this.CreateController(); // Minimum deposit confirmations : 2 this.depositExtractor.MinimumDepositConfirmations.Returns((uint)2); int maturedHeight = (int)(tip.Height - this.depositExtractor.MinimumDepositConfirmations); // Back online at block height : 3 // 0 - 1 - 2 - 3 ChainedHeader earlierBlock = tip.GetAncestor(maturedHeight + 1); // Mature height = 2 (Chain header height (4) - Minimum deposit confirmations (2)) IActionResult result = await controller.GetMaturedBlockDepositsAsync(new MaturedBlockRequestModel(earlierBlock.Height, 1000)).ConfigureAwait(false); // Block height (3) > Mature height (2) - returns error message result.Should().BeOfType <ErrorResult>(); var error = result as ErrorResult; error.Should().NotBeNull(); var errorResponse = error.Value as ErrorResponse; errorResponse.Should().NotBeNull(); errorResponse.Errors.Should().HaveCount(1); errorResponse.Errors.Should().Contain( e => e.Status == (int)HttpStatusCode.BadRequest); errorResponse.Errors.Should().Contain( e => e.Message.Contains($"Block height {earlierBlock.Height} submitted is not mature enough. Blocks less than a height of {maturedHeight} can be processed.")); }
public async void GetMaturedBlockDeposits_Gets_All_Matured_Block_Deposits_Async() { this.chain = this.BuildChain(10); var controller = new FederationGatewayController( this.loggerFactory, this.maturedBlockReceiver, this.maturedBlocksRequester, this.leaderProvider, this.chain, GetMaturedBlocksProvider(), this.depositExtractor, this.leaderReceiver); ChainedHeader earlierBlock = this.chain.GetBlock(2); var minConfirmations = 2; this.depositExtractor.MinimumDepositConfirmations.Returns((uint)minConfirmations); var depositExtractorCallCount = 0; this.depositExtractor.ExtractBlockDeposits(Arg.Any <ChainedHeader>()).Returns(new MaturedBlockDepositsModel(null, null)); this.depositExtractor.When(x => x.ExtractBlockDeposits(Arg.Any <ChainedHeader>())).Do(info => { depositExtractorCallCount++; }); IActionResult result = await controller.GetMaturedBlockDepositsAsync(new MaturedBlockRequestModel(earlierBlock.Height)).ConfigureAwait(false); result.Should().BeOfType <JsonResult>(); // If the minConfirmations == 0 and this.chain.Height == earlierBlock.Height then expectedCallCount must be 1. var expectedCallCount = (this.chain.Height - minConfirmations) - earlierBlock.Height + 1; depositExtractorCallCount.Should().Be(expectedCallCount); }
public async void GetMaturedBlockDeposits_Fails_When_Block_Not_In_Chain_Async() { this.chain = Substitute.For <ConcurrentChain>(); var controller = new FederationGatewayController( this.loggerFactory, this.maturedBlockReceiver, this.maturedBlocksRequester, this.leaderProvider, this.chain, this.GetMaturedBlocksProvider(), this.depositExtractor, this.leaderReceiver); ChainedHeader chainedHeader = this.BuildChain(3).GetBlock(2); this.chain.Tip.Returns(chainedHeader); IActionResult result = await controller.GetMaturedBlockDepositsAsync(new MaturedBlockRequestModel(1)).ConfigureAwait(false); result.Should().BeOfType <ErrorResult>(); var error = result as ErrorResult; error.Should().NotBeNull(); var errorResponse = error.Value as ErrorResponse; errorResponse.Should().NotBeNull(); errorResponse.Errors.Should().HaveCount(1); errorResponse.Errors.Should().Contain( e => e.Status == (int)HttpStatusCode.BadRequest); errorResponse.Errors.Should().Contain( e => e.Message.Contains("was not found on the block chain")); }