private void QueryBlockTest_WithNonExistingGenerationTransaction_ShouldBeOrphaned() { // test case: coin reports generation transaction hash as invalid. var block = new PersistedBlock(1, false, false, false, "BLOCK_HASH", "TX_HASH", 0, 0, DateTime.Now); _daemonClient.GetBlock("BLOCK_HASH").Returns(info => new Block { Tx = new List <string> { "TX_HASH" } }); _daemonClient.GetTransaction("TX_HASH").Returns(x => { throw new RpcErrorException(new RpcErrorResponse { Error = new RpcError { Code = -5 // 'Invalid or non-wallet transaction id' } }); }); // query the block. var exposed = Exposed.From(new BlockProcessor(_poolConfig, _daemonClient, _storageLayer)); exposed.QueryBlock(block); // block should be marked as orphaned. block.Status.Should().Equal(BlockStatus.Orphaned); }
public Transaction GetGenerationTransaction(Block block) { try { return(_daemonClient.GetTransaction(block.Tx.First())); // query the transaction } catch (RpcException e) { _logger.Error("Queried transaction does not exist {0:l} - {1:l}", block.Tx.First(), e.Message); return(null); } }
private Transaction GetGenerationTx(IPersistedBlock block) { try { return(_daemonClient.GetTransaction(block.TransactionHash)); // query the transaction } catch (RpcTimeoutException e) { _logger.Error(e, "RpcTimeoutException while GetGenerationTx"); // on rpc-timeout exception, let block stay in pending status so we can query it again later. block.Status = BlockStatus.Pending; return(null); } catch (RpcConnectionException e) { _logger.Error(e, "RpcConnectionException while GetGenerationTx"); // on rpc-connection exception, let block stay in pending status so we can query it again later. block.Status = BlockStatus.Pending; return(null); } catch (RpcErrorException e) { _logger.Error(e, "RpcErrorException while GetGenerationTx"); // Invalid or non-wallet transaction id if (e.Code == (int)RpcErrorCode.RPC_INVALID_ADDRESS_OR_KEY) { block.Status = BlockStatus.Orphaned; // orphan the block if block does not exist. } // if we got an error that we do not handle else { block.Status = BlockStatus.Pending; // let the block in pending status so we can query it again later. _logger.Error("Unhandled rpc-error: {0:l}", e.Message); } return(null); } catch (RpcException e) { block.Status = BlockStatus.Pending; // and let block stay in pending status so we can query it again later. _logger.Error("Unhandled rpc-exception: {0:l}", e.Message); return(null); } }
private bool SubmitBlock(IShare share) { // TODO: we should try different submission techniques and probably more then once: https://github.com/ahmedbodi/stratum-mining/blob/master/lib/bitcoin_rpc.py#L65-123 try { if (_poolConfig.Coin.Options.SubmitBlockSupported) // see if submitblock() is available. { _daemonClient.SubmitBlock(share.BlockHex.ToHexString()); // submit the block. } else { _daemonClient.GetBlockTemplate(share.BlockHex.ToHexString(), _poolConfig.Wallet.Address); // use getblocktemplate() if submitblock() is not supported. } var block = _daemonClient.GetBlock(share.BlockHash.ToHexString()); // query the block. if (block == null) // make sure the block exists { _logger.Debug("Submitted block [{0}] cannot be found with getblock; [{1}]", share.BlockHash.ToHexString(), block.Hash); return(false); } if (block.Confirmations == -1) // make sure the block is accepted. { _logger.Debug("Submitted block [{0}] is orphaned; [{1:l}]", block.Height, block.Hash); return(false); } var expectedTxHash = share.CoinbaseHash.Bytes.ReverseBuffer().ToHexString(); // calculate our expected generation transactions's hash var genTxHash = block.Tx.First(); // read the hash of very first (generation transaction) of the block if (expectedTxHash != genTxHash) // make sure our calculated generated transaction and one reported by coin daemon matches. { _logger.Debug("Submitted block [{0}] doesn't seem to belong us as reported generation transaction hash [{1:l}] doesn't match our expected one [{2:l}]", block.Height, genTxHash, expectedTxHash); return(false); } var genTx = _daemonClient.GetTransaction(block.Tx.First()); // get the generation transaction. // make sure we were able to read the generation transaction if (genTx == null) { _logger.Debug("Submitted block [{0}] doesn't seem to belong us as we can't read the generation transaction on our records [{1:l}]", block.Height, block.Tx.First()); return(false); } var poolOutput = genTx.GetPoolOutput(_poolConfig.Wallet.Address, _poolAccount); // get the output that targets pool's central address. // make sure the blocks generation transaction contains our central pool wallet address if (poolOutput == null) { _logger.Debug("Submitted block [{0}] doesn't seem to belong us as generation transaction doesn't contain an output for pool's central wallet address: {0:}", block.Height, _poolConfig.Wallet.Address); return(false); } // if the code flows here, then it means the block was succesfully submitted and belongs to us. share.SetFoundBlock(block, genTx); // assign the block to share. _logger.Information("Found block [{0}] with hash [{1:l}]", share.Height, share.BlockHash.ToHexString()); return(true); } catch (RpcException e) { // unlike BlockProcessor's detailed exception handling and decision making based on the error, // here in share-manager we only one-shot submissions. If we get an error, basically we just don't care about the rest // and flag the submission as failed. _logger.Debug("We thought a block was found but it was rejected by the coin daemon; [{0:l}] - reason; {1:l}", share.BlockHash.ToHexString(), e.Message); return(false); } catch (Exception e) { _logger.Debug("We thought a block was found but it was rejected by the coin daemon; [{0:l}] - reason; {1:l}", share.BlockHash.ToHexString(), e.Message); return(false); } }