private static Arena CreateArena(WabiSabiConfig cfg, IRPCClient rpc) { var arenaBuilder = ArenaBuilder.From(cfg).With(rpc); arenaBuilder.Period = TimeSpan.FromSeconds(1); return(arenaBuilder.Create()); }
public IndexBuilderService(IRPCClient rpc, BlockNotifier blockNotifier, string indexFilePath) { RpcClient = Guard.NotNull(nameof(rpc), rpc); BlockNotifier = Guard.NotNull(nameof(blockNotifier), blockNotifier); IndexFilePath = Guard.NotNullOrEmptyOrWhitespace(nameof(indexFilePath), indexFilePath); Index = new List <FilterModel>(); IndexLock = new AsyncLock(); StartingHeight = SmartHeader.GetStartingHeader(RpcClient.Network).Height; _serviceStatus = NotStarted; IoHelpers.EnsureContainingDirectoryExists(IndexFilePath); if (File.Exists(IndexFilePath)) { if (RpcClient.Network == Network.RegTest) { File.Delete(IndexFilePath); // RegTest is not a global ledger, better to delete it. } else { foreach (var line in File.ReadAllLines(IndexFilePath)) { var filter = FilterModel.FromLine(line); Index.Add(filter); } } } BlockNotifier.OnBlock += BlockNotifier_OnBlock; }
public BlockNotifier(TimeSpan period, IRPCClient rpcClient, P2pNode?p2pNode = null) : base(period) { RpcClient = Guard.NotNull(nameof(rpcClient), rpcClient); P2pNode = p2pNode; ProcessedBlocks = new List <uint256>(); if (p2pNode is { })
/// <summary> /// Gets the transactions that are unconfirmed using getrawmempool. /// This is efficient when many transaction ids are provided. /// </summary> public static async Task <IEnumerable <uint256> > GetUnconfirmedAsync(this IRPCClient rpc, IEnumerable <uint256> transactionHashes) { uint256[] unconfirmedTransactionHashes = await rpc.GetRawMempoolAsync(); // If there are common elements, then there's unconfirmed. return(transactionHashes.Intersect(unconfirmedTransactionHashes)); }
public WabiSabiCoordinator(CoordinatorParameters parameters, IRPCClient rpc, ICoinJoinIdStore coinJoinIdStore) { Parameters = parameters; Warden = new(parameters.UtxoWardenPeriod, parameters.PrisonFilePath, Config); ConfigWatcher = new(parameters.ConfigChangeMonitoringPeriod, Config, () => Logger.LogInfo("WabiSabi configuration has changed.")); CoinJoinIdStore = coinJoinIdStore; CoinJoinTransactionArchiver transactionArchiver = new(Path.Combine(parameters.CoordinatorDataDir, "CoinJoinTransactions")); CoinJoinFeeRateStatStore = CoinJoinFeeRateStatStore.LoadFromFile(parameters.CoinJoinFeeRateStatStoreFilePath, Config, rpc); IoHelpers.EnsureContainingDirectoryExists(Parameters.CoinJoinFeeRateStatStoreFilePath); CoinJoinFeeRateStatStore.NewStat += FeeRateStatStore_NewStat; var coinJoinScriptStore = CoinJoinScriptStore.LoadFromFile(parameters.CoinJoinScriptStoreFilePath); IoHelpers.EnsureContainingDirectoryExists(Parameters.CoinJoinScriptStoreFilePath); RoundParameterFactory roundParameterFactory = new RoundParameterFactory(Config, rpc.Network); Arena = new( parameters.RoundProgressSteppingPeriod, rpc.Network, Config, rpc, Warden.Prison, coinJoinIdStore, roundParameterFactory, transactionArchiver, coinJoinScriptStore); IoHelpers.EnsureContainingDirectoryExists(Parameters.CoinJoinIdStoreFilePath); Arena.CoinJoinBroadcast += Arena_CoinJoinBroadcast; }
/// <summary> /// Constructs a client to read & write data from/to a Loom DAppChain. /// </summary> /// <param name="writeClient">RPC client to use for submitting txs.</param> /// <param name="readClient">RPC client to use for querying DAppChain state.</param> public DAppChainClient(IRPCClient writeClient, IRPCClient readClient) { this.writeClient = writeClient; this.readClient = readClient; this.Logger = NullLogger.Instance; this.NonceRetries = 5; }
/// <summary> /// Recursively gathers all the dependents of the mempool transactions provided. /// </summary> /// <param name="transactionHashes">Mempool transactions to gather their dependents.</param> /// <param name="includingProvided">Should it include in the result the unconfirmed ones from the provided transactionHashes.</param> /// <param name="likelyProvidedManyConfirmedOnes">If many provided transactionHashes are not confirmed then it optimizes by doing a check in the beginning of which ones are unconfirmed.</param> /// <returns>All the dependents of the provided transactionHashes.</returns> public static async Task <ISet <uint256> > GetAllDependentsAsync(this IRPCClient rpc, IEnumerable <uint256> transactionHashes, bool includingProvided, bool likelyProvidedManyConfirmedOnes) { IEnumerable <uint256> workingTxHashes = likelyProvidedManyConfirmedOnes // If confirmed txIds are provided, then do a big check first. ? await rpc.GetUnconfirmedAsync(transactionHashes) : transactionHashes; var hashSet = new HashSet <uint256>(); foreach (var txId in workingTxHashes) { // Go through all the txIds provided and getmempoolentry to get the dependents and the confirmation status. var entry = await rpc.GetMempoolEntryAsync(txId, throwIfNotFound : false); if (entry != null) { // If we asked to include the provided transaction hashes into the result then check which ones are confirmed and do so. if (includingProvided) { hashSet.Add(txId); } // Get all the dependents of all the dependents except the ones we already know of. var except = entry.Depends.Except(hashSet); var dependentsOfDependents = await rpc.GetAllDependentsAsync(except, includingProvided : true, likelyProvidedManyConfirmedOnes : false); // Add them to the hashset. hashSet.UnionWith(dependentsOfDependents); } } return(hashSet); }
private static async Task <Dictionary <int, int> > EstimateHalfFeesAsync(this IRPCClient rpc, IDictionary <int, int> estimations, int smallTarget, int smallFee, int largeTarget, int largeFee, EstimateSmartFeeMode estimateMode = EstimateSmartFeeMode.Conservative, bool simulateIfRegTest = false, bool tolerateBitcoinCoreBrainfuck = true) { var newEstimations = new Dictionary <int, int>(); foreach (var est in estimations) { newEstimations.TryAdd(est.Key, est.Value); } if (Math.Abs(smallTarget - largeTarget) <= 1) { return(newEstimations); } if (smallFee == 0) { var smallFeeResult = await rpc.EstimateSmartFeeAsync(smallTarget, estimateMode, simulateIfRegTest, tolerateBitcoinCoreBrainfuck); smallFee = (int)Math.Ceiling(smallFeeResult.FeeRate.SatoshiPerByte); newEstimations.TryAdd(smallTarget, smallFee); } if (largeFee == 0) { var largeFeeResult = await rpc.EstimateSmartFeeAsync(largeTarget, estimateMode, simulateIfRegTest, tolerateBitcoinCoreBrainfuck); largeFee = (int)Math.Ceiling(largeFeeResult.FeeRate.SatoshiPerByte); largeTarget = largeFeeResult.Blocks; newEstimations.TryAdd(largeTarget, largeFee); } int halfTarget = (smallTarget + largeTarget) / 2; var halfFeeResult = await rpc.EstimateSmartFeeAsync(halfTarget, estimateMode, simulateIfRegTest, tolerateBitcoinCoreBrainfuck); int halfFee = (int)Math.Ceiling(halfFeeResult.FeeRate.SatoshiPerByte); halfTarget = halfFeeResult.Blocks; newEstimations.TryAdd(halfTarget, halfFee); if (smallFee != halfFee) { var smallEstimations = await rpc.EstimateHalfFeesAsync(newEstimations, smallTarget, smallFee, halfTarget, halfFee, estimateMode, simulateIfRegTest, tolerateBitcoinCoreBrainfuck); foreach (var est in smallEstimations) { newEstimations.TryAdd(est.Key, est.Value); } } if (largeFee != halfFee) { var largeEstimations = await rpc.EstimateHalfFeesAsync(newEstimations, halfTarget, halfFee, largeTarget, largeFee, estimateMode, simulateIfRegTest, tolerateBitcoinCoreBrainfuck); foreach (var est in largeEstimations) { newEstimations.TryAdd(est.Key, est.Value); } } return(newEstimations); }
public static async Task <Coin> OutpointToCoinAsync( InputRegistrationRequest request, Prison prison, IRPCClient rpc, WabiSabiConfig config) { OutPoint input = request.Input; if (prison.TryGet(input, out var inmate) && (!config.AllowNotedInputRegistration || inmate.Punishment != Punishment.Noted)) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputBanned); } var txOutResponse = await rpc.GetTxOutAsync(input.Hash, (int)input.N, includeMempool : true).ConfigureAwait(false); if (txOutResponse is null) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputSpent); } if (txOutResponse.Confirmations == 0) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputUnconfirmed); } if (txOutResponse.IsCoinBase && txOutResponse.Confirmations <= 100) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputImmature); } return(new Coin(input, txOutResponse.TxOut)); }
public CoinJoinFeeRateStatStore(WabiSabiConfig config, IRPCClient rpc, IEnumerable <CoinJoinFeeRateStat> feeRateStats) : base(TimeSpan.FromMinutes(10)) { Config = config; Rpc = rpc; CoinJoinFeeRateStats = new(feeRateStats.OrderBy(x => x.DateTimeOffset)); }
public static async Task <EstimateSmartFeeResponse> EstimateSmartFeeAsync(this IRPCClient rpc, int confirmationTarget, EstimateSmartFeeMode estimateMode = EstimateSmartFeeMode.Conservative, bool simulateIfRegTest = false, bool tryOtherFeeRates = true) { if (simulateIfRegTest && rpc.Network == Network.RegTest) { return(SimulateRegTestFeeEstimation(confirmationTarget, estimateMode)); } if (tryOtherFeeRates) { try { return(await rpc.EstimateSmartFeeAsync(confirmationTarget, estimateMode)); } catch (Exception ex) when(ex is RPCException || ex is NoEstimationException) { Logger.LogTrace(ex); // Hopefully Bitcoin Core brainfart: https://github.com/bitcoin/bitcoin/issues/14431 for (int i = 2; i <= Constants.SevenDaysConfirmationTarget; i++) { try { return(await rpc.EstimateSmartFeeAsync(i, estimateMode)); } catch (Exception ex2) when(ex2 is RPCException || ex2 is NoEstimationException) { Logger.LogTrace(ex2); } } } // Let's try one more time, whatever. } return(await rpc.EstimateSmartFeeAsync(confirmationTarget, estimateMode)); }
public static async Task <EstimateSmartFeeResponse> TryEstimateSmartFeeAsync(this IRPCClient rpc, int confirmationTarget, EstimateSmartFeeMode estimateMode = EstimateSmartFeeMode.Conservative, bool simulateIfRegTest = false, bool tryOtherFeeRates = false) { if (simulateIfRegTest && rpc.Network == Network.RegTest) { return(SimulateRegTestFeeEstimation(confirmationTarget, estimateMode)); } if (tryOtherFeeRates) { EstimateSmartFeeResponse response = await rpc.TryEstimateSmartFeeAsync(confirmationTarget, estimateMode); if (response != null) { return(response); } else { // Hopefully Bitcoin Core brainfart: https://github.com/bitcoin/bitcoin/issues/14431 for (int i = 2; i <= Constants.SevenDaysConfirmationTarget; i++) { response = await rpc.TryEstimateSmartFeeAsync(i, estimateMode); if (response != null) { return(response); } } } // Let's try one more time, whatever. } return(await rpc.TryEstimateSmartFeeAsync(confirmationTarget, estimateMode)); }
/// <summary> /// Estimates fees for 1w, 3d, 1d, 12h, 6h, 3h, 1h, 30m, 20m. /// </summary> public static async Task <AllFeeEstimate> EstimateAllFeeAsync(this IRPCClient rpc, EstimateSmartFeeMode estimateMode = EstimateSmartFeeMode.Conservative, bool simulateIfRegTest = false, CancellationToken cancel = default) { var smartEstimations = (simulateIfRegTest && rpc.Network == Network.RegTest) ? SimulateRegTestFeeEstimation(estimateMode) : await GetFeeEstimationsAsync(rpc, estimateMode, cancel).ConfigureAwait(false); var mempoolInfo = await rpc.GetMempoolInfoAsync(cancel).ConfigureAwait(false); var sanityFeeRate = mempoolInfo.GetSanityFeeRate(); var rpcStatus = await rpc.GetRpcStatusAsync(cancel).ConfigureAwait(false); var minEstimations = GetFeeEstimationsFromMempoolInfo(mempoolInfo); var fixedEstimations = smartEstimations .GroupJoin( minEstimations, outer => outer.Key, inner => inner.Key, (outer, inner) => new { Estimation = outer, MinimumFromMemPool = inner }) .SelectMany(x => x.MinimumFromMemPool.DefaultIfEmpty(), (a, b) => ( Target: a.Estimation.Key, FeeRate: Math.Max((int)sanityFeeRate.SatoshiPerByte, Math.Max(a.Estimation.Value, b.Value)))) .ToDictionary(x => x.Target, x => x.FeeRate); return(new AllFeeEstimate( estimateMode, fixedEstimations, rpcStatus.Synchronized)); }
private static async Task <Dictionary <int, int> > GetFeeEstimationsAsync(IRPCClient rpc, EstimateSmartFeeMode estimateMode, CancellationToken cancel = default) { var batchClient = rpc.PrepareBatch(); var rpcFeeEstimationTasks = Constants.ConfirmationTargets .Select(target => batchClient.EstimateSmartFeeAsync(target, estimateMode)) .ToList(); await batchClient.SendBatchAsync(cancel).ConfigureAwait(false); cancel.ThrowIfCancellationRequested(); try { await Task.WhenAll(rpcFeeEstimationTasks).ConfigureAwait(false); } catch { if (rpcFeeEstimationTasks.All(x => x.IsFaulted)) { throw rpcFeeEstimationTasks[0].Exception?.InnerExceptions[0] ?? new Exception($"{nameof(GetFeeEstimationsAsync)} failed to fetch fee estimations."); } } // EstimateSmartFeeAsync returns the block number where estimate was found - not always what the requested one. return(rpcFeeEstimationTasks .Zip(Constants.ConfirmationTargets, (task, target) => (task, target)) .Where(x => x.task.IsCompletedSuccessfully) .Select(x => (x.target, feeRate: x.task.Result.FeeRate)) .ToDictionary(x => x.target, x => (int)Math.Ceiling(x.feeRate.SatoshiPerByte))); }
/// <summary> /// Processes a RPCRequest. /// </summary> /// <param name="request">The request to process.</param> /// <returns></returns> private RPCResponse SendRPCRequest(RPCRequest request) { // Find the binding to 127.0.0.1 or the first available. The logic in RPC settings ensures there will be at least 1. IPEndPoint nodeEndPoint = this.rpcSettings.Bind.Where(b => b.Address.ToString() == "127.0.0.1").FirstOrDefault() ?? this.rpcSettings.Bind[0]; IRPCClient rpcClient = this.rpcClientFactory.Create($"{this.rpcSettings.RpcUser}:{this.rpcSettings.RpcPassword}", new Uri($"http://{nodeEndPoint}"), this.fullNode.Network); return(rpcClient.SendCommand(request)); }
public Arena(TimeSpan period, Network network, WabiSabiConfig config, IRPCClient rpc, Prison prison) : base(period) { Network = network; Config = config; Rpc = rpc; Prison = prison; Random = new SecureRandom(); }
public Participant(IRPCClient rpc, IBackendHttpClientFactory httpClientFactory) { Rpc = rpc; HttpClientFactory = httpClientFactory; KeyManager = KeyManager.CreateNew(out var _, password: "", Network.Main); KeyManager.AssertCleanKeysIndexed(); }
/// <summary> /// Constructs a client to read & write data from/to a Loom DAppChain. /// </summary> /// <param name="writeClient">RPC client to use for submitting txs.</param> /// <param name="readClient">RPC client to use for querying DAppChain state.</param> public DAppChainClient(IRPCClient writeClient, IRPCClient readClient) { this.eventSubs = new Dictionary <EventHandler <ChainEventArgs>, EventHandler <EventData> >(); this.writeClient = writeClient; this.readClient = readClient; this.Logger = NullLogger.Instance; this.NonceRetries = 5; }
private volatile bool _disposedValue = false; // To detect redundant calls public CoinJoinProcessor(WasabiSynchronizer synchronizer, WalletManager walletManager, IRPCClient rpc) { Synchronizer = Guard.NotNull(nameof(synchronizer), synchronizer); WalletManager = Guard.NotNull(nameof(walletManager), walletManager); RpcClient = rpc; ProcessLock = new AsyncLock(); Synchronizer.ResponseArrived += Synchronizer_ResponseArrivedAsync; }
public BlockParser(IRPCClient client, IBlockRepository blockRepository, ITransactionParser transactionParser) { this.client = client; this.blockRepository = blockRepository; this.transactionParser = transactionParser; this.parsedHash = new Dictionary <string, uint>(); this.parsedHeight = new Dictionary <uint, bool>(); }
public ArenaRequestHandler(WabiSabiConfig config, Prison prison, Arena arena, IRPCClient rpc) { Config = config; Prison = prison; Arena = arena; Rpc = rpc; Network = rpc.Network; }
public Participant(IRPCClient rpc, WabiSabiHttpApiClient apiClient) { Rpc = rpc; ApiClient = apiClient; KeyManager = KeyManager.CreateNew(out var _, password: ""); KeyManager.AssertCleanKeysIndexed(); }
public IndexBuilderService(IRPCClient rpc, BlockNotifier blockNotifier, string indexFilePath, string bech32UtxoSetFilePath) { RpcClient = Guard.NotNull(nameof(rpc), rpc); BlockNotifier = Guard.NotNull(nameof(blockNotifier), blockNotifier); IndexFilePath = Guard.NotNullOrEmptyOrWhitespace(nameof(indexFilePath), indexFilePath); Bech32UtxoSetFilePath = Guard.NotNullOrEmptyOrWhitespace(nameof(bech32UtxoSetFilePath), bech32UtxoSetFilePath); Bech32UtxoSet = new Dictionary <OutPoint, UtxoEntry>(); Bech32UtxoSetHistory = new List <ActionHistoryHelper>(capacity: 100); Index = new List <FilterModel>(); IndexLock = new AsyncLock(); StartingHeight = SmartHeader.GetStartingHeader(RpcClient.Network).Height; _running = 0; IoHelpers.EnsureContainingDirectoryExists(IndexFilePath); if (File.Exists(IndexFilePath)) { if (RpcClient.Network == Network.RegTest) { File.Delete(IndexFilePath); // RegTest is not a global ledger, better to delete it. } else { foreach (var line in File.ReadAllLines(IndexFilePath)) { var filter = FilterModel.FromLine(line); Index.Add(filter); } } } IoHelpers.EnsureContainingDirectoryExists(bech32UtxoSetFilePath); if (File.Exists(bech32UtxoSetFilePath)) { if (RpcClient.Network == Network.RegTest) { File.Delete(bech32UtxoSetFilePath); // RegTest is not a global ledger, better to delete it. } else { foreach (var line in File.ReadAllLines(Bech32UtxoSetFilePath)) { var parts = line.Split(':'); var txHash = new uint256(parts[0]); var nIn = int.Parse(parts[1]); var script = new Script(ByteHelpers.FromHex(parts[2]), true); OutPoint outPoint = new OutPoint(txHash, nIn); var utxoEntry = new UtxoEntry(outPoint, script); Bech32UtxoSet.Add(outPoint, utxoEntry); } } } BlockNotifier.OnBlock += BlockNotifier_OnBlock; }
public TransactionBroadcaster(Network network, BitcoinStore bitcoinStore, WasabiSynchronizer synchronizer, NodesGroup nodes, WalletManager walletManager, IRPCClient rpc) { Nodes = Guard.NotNull(nameof(nodes), nodes); Network = Guard.NotNull(nameof(network), network); BitcoinStore = Guard.NotNull(nameof(bitcoinStore), bitcoinStore); Synchronizer = Guard.NotNull(nameof(synchronizer), synchronizer); WalletManager = Guard.NotNull(nameof(walletManager), walletManager); RpcClient = rpc; }
public BitcoindRpcProcessBridge(IRPCClient rpc, string dataDir, bool printToConsole) : base() { RpcClient = Guard.NotNull(nameof(rpc), rpc); Network = RpcClient.Network; DataDir = Guard.NotNullOrEmptyOrWhitespace(nameof(dataDir), dataDir); PrintToConsole = printToConsole; PidFile = new PidFile(Path.Combine(DataDir, NetworkTranslator.GetDataDirPrefix(Network)), PidFileName); CachedPid = null; }
public static async Task <EstimateSmartFeeResponse> EstimateSmartFeeAsync(this IRPCClient rpc, int confirmationTarget, EstimateSmartFeeMode estimateMode = EstimateSmartFeeMode.Conservative, bool simulateIfRegTest = false) { if (simulateIfRegTest && rpc.Network == Network.RegTest) { return(SimulateRegTestFeeEstimation(confirmationTarget, estimateMode)); } return(await rpc.EstimateSmartFeeAsync(confirmationTarget, estimateMode)); }
public BitcoindRpcProcessBridge(IRPCClient rpcClient, string dataDir, bool printToConsole) { RpcClient = rpcClient; Network = RpcClient.Network; DataDir = dataDir; PrintToConsole = printToConsole; PidFile = new PidFile(Path.Combine(DataDir, NetworkTranslator.GetDataDirPrefix(Network)), PidFileName); CachedPid = null; Process = null; }
public static async Task <AllFeeEstimate> EstimateAllFeeAsync(this IRPCClient rpc, EstimateSmartFeeMode estimateMode = EstimateSmartFeeMode.Conservative, bool simulateIfRegTest = false, bool tolerateBitcoinCoreBrainfuck = true) { var rpcStatus = await rpc.GetRpcStatusAsync(CancellationToken.None).ConfigureAwait(false); var estimations = await rpc.EstimateHalfFeesAsync(new Dictionary <int, int>(), 2, 0, Constants.SevenDaysConfirmationTarget, 0, estimateMode, simulateIfRegTest, tolerateBitcoinCoreBrainfuck).ConfigureAwait(false); var allFeeEstimate = new AllFeeEstimate(estimateMode, estimations, rpcStatus.Synchronized); return(allFeeEstimate); }
public WabiSabiCoordinator(CoordinatorParameters parameters, IRPCClient rpc) { Parameters = parameters; Rpc = rpc; Warden = new(parameters.UtxoWardenPeriod, parameters.PrisonFilePath, Config); ConfigWatcher = new(parameters.ConfigChangeMonitoringPeriod, Config, () => Logger.LogInfo("WabiSabi configuration has changed.")); Rounds = new(); Postman = new(Config, Prison, Rounds, Rpc); }
public WabiSabiCoordinator(CoordinatorParameters parameters, IRPCClient rpc) { Parameters = parameters; Warden = new(parameters.UtxoWardenPeriod, parameters.PrisonFilePath, Config); ConfigWatcher = new(parameters.ConfigChangeMonitoringPeriod, Config, () => Logger.LogInfo("WabiSabi configuration has changed.")); CoinJoinTransactionArchiver transactionArchiver = new(Path.Combine(parameters.CoordinatorDataDir, "CoinJoinTransactions")); Arena = new(parameters.RoundProgressSteppingPeriod, rpc.Network, Config, rpc, Warden.Prison, transactionArchiver); }