public override async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolConfig) { await base.ConfigureAsync(clusterConfig, poolConfig); poolExtraConfig = poolConfig.Extra.SafeExtensionDataAs <ZCashPoolConfigExtra>(); // detect network var blockchainInfoResponse = await daemon.ExecuteCmdSingleAsync <BlockchainInfo>(BitcoinCommands.GetBlockchainInfo); if (blockchainInfoResponse.Response.Chain.ToLower() == "test") { networkType = BitcoinNetworkType.Test; } else if (blockchainInfoResponse.Response.Chain.ToLower() == "regtest") { networkType = BitcoinNetworkType.RegTest; } else { networkType = BitcoinNetworkType.Main; } // lookup config if (ZCashConstants.Chains.TryGetValue(poolConfig.Coin.Type, out var coinbaseTx)) { coinbaseTx.TryGetValue(networkType, out chainConfig); } // detect z_shieldcoinbase support var response = await daemon.ExecuteCmdSingleAsync <JObject>(ZCashCommands.ZShieldCoinbase); supportsNativeShielding = response.Error.Code != (int)BitcoinRPCErrorCode.RPC_METHOD_NOT_FOUND; }
public override async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolConfig) { await base.ConfigureAsync(clusterConfig, poolConfig); poolExtraConfig = poolConfig.Extra.SafeExtensionDataAs <EquihashPoolConfigExtra>(); // detect network var blockchainInfoResponse = await daemon.ExecuteCmdSingleAsync <BlockchainInfo>(logger, BitcoinCommands.GetBlockchainInfo); if (blockchainInfoResponse.Response.Chain.ToLower() == "test") { networkType = BitcoinNetworkType.Test; } else if (blockchainInfoResponse.Response.Chain.ToLower() == "regtest") { networkType = BitcoinNetworkType.RegTest; } else { networkType = BitcoinNetworkType.Main; } chainConfig = poolConfig.Template.As <EquihashCoinTemplate>().GetNetwork(networkType); // detect z_shieldcoinbase support var response = await daemon.ExecuteCmdSingleAsync <JObject>(logger, EquihashCommands.ZShieldCoinbase); supportsNativeShielding = response.Error.Code != (int)BitcoinRPCErrorCode.RPC_METHOD_NOT_FOUND; }
public virtual void Init(TBlockTemplate blockTemplate, string jobId, PoolConfig poolConfig, ClusterConfig clusterConfig, IMasterClock clock, IDestination poolAddressDestination, BitcoinNetworkType networkType, bool isPoS, double shareMultiplier, IHashAlgorithm coinbaseHasher, IHashAlgorithm headerHasher, IHashAlgorithm blockHasher) { Contract.RequiresNonNull(blockTemplate, nameof(blockTemplate)); Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); Contract.RequiresNonNull(clock, nameof(clock)); Contract.RequiresNonNull(poolAddressDestination, nameof(poolAddressDestination)); Contract.RequiresNonNull(coinbaseHasher, nameof(coinbaseHasher)); Contract.RequiresNonNull(headerHasher, nameof(headerHasher)); Contract.RequiresNonNull(blockHasher, nameof(blockHasher)); Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(jobId), $"{nameof(jobId)} must not be empty"); this.poolConfig = poolConfig; this.clusterConfig = clusterConfig; this.clock = clock; this.poolAddressDestination = poolAddressDestination; this.networkType = networkType; BlockTemplate = blockTemplate; JobId = jobId; Difficulty = new Target(new NBitcoin.BouncyCastle.Math.BigInteger(BlockTemplate.Target, 16)).Difficulty; extraNoncePlaceHolderLength = BitcoinExtraNonceProvider.PlaceHolderLength; this.isPoS = isPoS; this.shareMultiplier = shareMultiplier; this.coinbaseHasher = coinbaseHasher; this.headerHasher = headerHasher; this.blockHasher = blockHasher; blockTargetValue = BigInteger.Parse(BlockTemplate.Target, NumberStyles.HexNumber); previousBlockHashReversedHex = BlockTemplate.PreviousBlockhash .HexToByteArray() .ReverseByteOrder() .ToHexString(); BuildMerkleBranches(); BuildCoinbase(); jobParams = new object[] { JobId, previousBlockHashReversedHex, coinbaseInitialHex, coinbaseFinalHex, merkleBranchesHex, BlockTemplate.Version.ToStringHex8(), BlockTemplate.Bits, BlockTemplate.CurTime.ToStringHex8(), false }; }
public EquihashNetworkParams GetNetwork(BitcoinNetworkType networkType) { switch (networkType) { case BitcoinNetworkType.Main: return(Networks["main"]); case BitcoinNetworkType.Test: return(Networks["test"]); case BitcoinNetworkType.RegTest: return(Networks["regtest"]); } throw new NotSupportedException(); }
public virtual void Init(ExchangeCoinGetWork work, string jobId, PoolConfig poolConfig, ClusterConfig clusterConfig, IMasterClock clock, IDestination poolAddressDestination, BitcoinNetworkType networkType, double shareMultiplier, decimal blockrewardMultiplier, IHashAlgorithm headerHasher) { Contract.RequiresNonNull(work, nameof(work)); Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); Contract.RequiresNonNull(clock, nameof(clock)); Contract.RequiresNonNull(poolAddressDestination, nameof(poolAddressDestination)); Contract.RequiresNonNull(headerHasher, nameof(headerHasher)); Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(jobId), $"{nameof(jobId)} must not be empty"); this.clock = clock; if (ExchangeCoinConstants.Chains.TryGetValue(poolConfig.Coin.Type, out var chain)) { chain.TryGetValue(networkType, out chainConfig); } Work = work; BlockHeader = new ExchangeCoinBlockHeader(work.Data); JobId = jobId; this.shareMultiplier = shareMultiplier; this.headerHasher = headerHasher; this.equihash = chainConfig.Solver(); blockTarget = new Target(BlockHeader.Bits); Difficulty = chainConfig.Diff1.Divide(blockTarget.ToBigInteger()).LongValue; BuildCoinbase(); jobParams = new object[] { JobId, BlockHeader.PrevBlock.ToHexString(), coinbaseInitialHex, coinbaseFinalHex, "", BlockHeader.Version.ToStringHex8().HexToByteArray().ReverseArray().ToHexString(), BlockHeader.Bits.ReverseByteOrder().ToStringHex8(), BlockHeader.Timestamp.ReverseByteOrder().ToStringHex8(), false }; }
public static Network ToNetwork(this BitcoinNetworkType networkType) { switch (networkType) { case BitcoinNetworkType.Main: return(Network.Main); case BitcoinNetworkType.Test: return(Network.TestNet); case BitcoinNetworkType.RegTest: return(Network.RegTest); default: throw new NotSupportedException("unsupported network type"); } }
public void Init(TBlockTemplate blockTemplate, string jobId, PoolConfig poolConfig, ClusterConfig clusterConfig, IDestination poolAddressDestination, BitcoinNetworkType networkType, BitcoinExtraNonceProvider extraNonceProvider, bool isPoS, double shareMultiplier, IHashAlgorithm coinbaseHasher, IHashAlgorithm headerHasher, IHashAlgorithm blockHasher) { Contract.RequiresNonNull(blockTemplate, nameof(blockTemplate)); Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); Contract.RequiresNonNull(poolAddressDestination, nameof(poolAddressDestination)); Contract.RequiresNonNull(extraNonceProvider, nameof(extraNonceProvider)); Contract.RequiresNonNull(coinbaseHasher, nameof(coinbaseHasher)); Contract.RequiresNonNull(headerHasher, nameof(headerHasher)); Contract.RequiresNonNull(blockHasher, nameof(blockHasher)); Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(jobId), $"{nameof(jobId)} must not be empty"); this.poolConfig = poolConfig; this.clusterConfig = clusterConfig; this.poolAddressDestination = poolAddressDestination; this.networkType = networkType; BlockTemplate = blockTemplate; JobId = jobId; extraNoncePlaceHolderLength = extraNonceProvider.PlaceHolder.Length; this.isPoS = isPoS; this.shareMultiplier = shareMultiplier; this.coinbaseHasher = coinbaseHasher; this.headerHasher = headerHasher; this.blockHasher = blockHasher; blockTargetValue = BigInteger.Parse(BlockTemplate.Target, NumberStyles.HexNumber); previousBlockHashReversedHex = BlockTemplate.PreviousBlockhash .HexToByteArray() .ReverseByteOrder() .ToHexString(); BuildMerkleBranches(); BuildCoinbase(); }
public override void Init(ZCashBlockTemplate blockTemplate, string jobId, PoolConfig poolConfig, ClusterConfig clusterConfig, IMasterClock clock, IDestination poolAddressDestination, BitcoinNetworkType networkType, bool isPoS, double shareMultiplier, IHashAlgorithm coinbaseHasher, IHashAlgorithm headerHasher, IHashAlgorithm blockHasher) { Contract.RequiresNonNull(blockTemplate, nameof(blockTemplate)); Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); Contract.RequiresNonNull(clock, nameof(clock)); Contract.RequiresNonNull(poolAddressDestination, nameof(poolAddressDestination)); Contract.RequiresNonNull(coinbaseHasher, nameof(coinbaseHasher)); Contract.RequiresNonNull(headerHasher, nameof(headerHasher)); Contract.RequiresNonNull(blockHasher, nameof(blockHasher)); Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(jobId), $"{nameof(jobId)} must not be empty"); this.poolConfig = poolConfig; this.clusterConfig = clusterConfig; this.clock = clock; this.poolAddressDestination = poolAddressDestination; this.networkType = networkType; if (ZCashConstants.CoinbaseTxConfig.TryGetValue(poolConfig.Coin.Type, out var coinbaseTx)) { coinbaseTx.TryGetValue(networkType, out coinbaseTxConfig); } BlockTemplate = blockTemplate; JobId = jobId; Difficulty = new Target(new NBitcoin.BouncyCastle.Math.BigInteger(BlockTemplate.Target, 16)).Difficulty; this.isPoS = isPoS; this.shareMultiplier = shareMultiplier; this.headerHasher = headerHasher; this.blockHasher = blockHasher; blockTargetValue = BigInteger.Parse(BlockTemplate.Target, NumberStyles.HexNumber); previousBlockHashReversedHex = BlockTemplate.PreviousBlockhash .HexToByteArray() .ReverseArray() .ToHexString(); blockReward = blockTemplate.Subsidy.Miner * BitcoinConstants.SatoshisPerBitcoin; if (coinbaseTxConfig?.PayFoundersReward == true) { var founders = blockTemplate.Subsidy.Founders ?? blockTemplate.Subsidy.Community; if (!founders.HasValue) { throw new Exception("Error, founders reward missing for block template"); } blockReward = (blockTemplate.Subsidy.Miner + founders.Value) * BitcoinConstants.SatoshisPerBitcoin; } rewardFees = blockTemplate.Transactions.Sum(x => x.Fee); BuildCoinbase(); // build tx hashes var txHashes = new List <uint256> { new uint256(coinbaseInitialHash) }; txHashes.AddRange(BlockTemplate.Transactions.Select(tx => new uint256(tx.Hash.HexToByteArray().ReverseArray()))); // build merkle root merkleRoot = MerkleNode.GetRoot(txHashes).Hash.ToBytes().ReverseArray(); merkleRootReversed = merkleRoot.ReverseArray(); merkleRootReversedHex = merkleRootReversed.ToHexString(); }
public override void Init(ZCashBlockTemplate blockTemplate, string jobId, PoolConfig poolConfig, ClusterConfig clusterConfig, IMasterClock clock, IDestination poolAddressDestination, BitcoinNetworkType networkType, bool isPoS, double shareMultiplier, decimal?blockrewardMultiplier, IHashAlgorithm coinbaseHasher, IHashAlgorithm headerHasher, IHashAlgorithm blockHasher) { Contract.RequiresNonNull(blockTemplate, nameof(blockTemplate)); Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); Contract.RequiresNonNull(clock, nameof(clock)); Contract.RequiresNonNull(poolAddressDestination, nameof(poolAddressDestination)); Contract.RequiresNonNull(coinbaseHasher, nameof(coinbaseHasher)); Contract.RequiresNonNull(headerHasher, nameof(headerHasher)); Contract.RequiresNonNull(blockHasher, nameof(blockHasher)); Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(jobId), $"{nameof(jobId)} must not be empty"); this.poolConfig = poolConfig; this.clusterConfig = clusterConfig; this.clock = clock; this.poolAddressDestination = poolAddressDestination; this.networkType = networkType; if (ZCashConstants.Chains.TryGetValue(poolConfig.Coin.Type, out var coinbaseTx)) { coinbaseTx.TryGetValue(networkType, out chainConfig); } BlockTemplate = blockTemplate; JobId = jobId; Difficulty = (double)new BigRational(chainConfig.Diff1b, BlockTemplate.Target.HexToByteArray().ReverseArray().ToBigInteger()); this.isPoS = isPoS; this.shareMultiplier = shareMultiplier; this.headerHasher = headerHasher; this.blockHasher = blockHasher; this.equihash = chainConfig.Solver(); if (!string.IsNullOrEmpty(BlockTemplate.Target)) { blockTargetValue = new uint256(BlockTemplate.Target); } else { var tmp = new Target(BlockTemplate.Bits.HexToByteArray()); blockTargetValue = tmp.ToUInt256(); } previousBlockHashReversedHex = BlockTemplate.PreviousBlockhash .HexToByteArray() .ReverseArray() .ToHexString(); BuildCoinbase(); // build tx hashes var txHashes = new List <uint256> { new uint256(coinbaseInitialHash) }; txHashes.AddRange(BlockTemplate.Transactions.Select(tx => new uint256(tx.TxId.HexToByteArray().ReverseArray()))); // build merkle root merkleRoot = MerkleNode.GetRoot(txHashes).Hash.ToBytes().ReverseArray(); merkleRootReversed = merkleRoot.ReverseArray(); merkleRootReversedHex = merkleRootReversed.ToHexString(); jobParams = new object[] { JobId, BlockTemplate.Version.ReverseByteOrder().ToStringHex8(), previousBlockHashReversedHex, merkleRootReversedHex, BlockTemplate.Height.ReverseByteOrder().ToStringHex8() + sha256Empty.Take(28).ToHexString(), // height + hashReserved BlockTemplate.CurTime.ReverseByteOrder().ToStringHex8(), BlockTemplate.Bits.HexToByteArray().ReverseArray().ToHexString(), false }; }
protected override async Task PostStartInitAsync(CancellationToken ct) { var commands = new[] { new DaemonCmd(BitcoinCommands.ValidateAddress, new[] { poolConfig.Address }), new DaemonCmd(BitcoinCommands.SubmitBlock), new DaemonCmd(!hasLegacyDaemon ? BitcoinCommands.GetBlockchainInfo : BitcoinCommands.GetInfo), new DaemonCmd(BitcoinCommands.GetDifficulty), }; var results = await daemon.ExecuteBatchAnyAsync(commands); if (results.Any(x => x.Error != null)) { var resultList = results.ToList(); var errors = results.Where(x => x.Error != null && commands[resultList.IndexOf(x)].Method != BitcoinCommands.SubmitBlock) .ToArray(); if (errors.Any()) { logger.ThrowLogPoolStartupException($"Init RPC failed: {string.Join(", ", errors.Select(y => y.Error.Message))}", LogCat); } } var validateAddressResponse = results[0].Response.ToObject <ValidateAddressResponse>(); var submitBlockResponse = results[1]; var blockchainInfoResponse = !hasLegacyDaemon ? results[2].Response.ToObject <BlockchainInfo>() : null; var daemonInfoResponse = hasLegacyDaemon ? results[2].Response.ToObject <DaemonInfo>() : null; var difficultyResponse = results[3].Response.ToObject <JToken>(); if (!validateAddressResponse.IsValid) { logger.ThrowLogPoolStartupException($"Daemon reports pool-address '{poolConfig.Address}' as invalid", LogCat); } if (clusterConfig.PaymentProcessing?.Enabled == true && !validateAddressResponse.IsMine) { logger.ThrowLogPoolStartupException($"Daemon does not own pool-address '{poolConfig.Address}'", LogCat); } isPoS = difficultyResponse.Values().Any(x => x.Path == "proof-of-stake"); if (!isPoS) { if (!validateAddressResponse.Address.StartsWith("bitcoincash:")) { poolAddressDestination = AddressToDestination(validateAddressResponse.Address); } else { poolAddressDestination = AddressToDestination(poolConfig.Address); } } else { poolAddressDestination = new PubKey(validateAddressResponse.PubKey); } if (!hasLegacyDaemon) { if (blockchainInfoResponse.Chain.ToLower() == "test") { networkType = BitcoinNetworkType.Test; } else if (blockchainInfoResponse.Chain.ToLower() == "regtest") { networkType = BitcoinNetworkType.RegTest; } else { networkType = BitcoinNetworkType.Main; } } else { networkType = daemonInfoResponse.Testnet ? BitcoinNetworkType.Test : BitcoinNetworkType.Main; } BlockchainStats.NetworkType = networkType.ToString(); BlockchainStats.RewardType = isPoS ? "POS" : "POW"; if (submitBlockResponse.Error?.Message?.ToLower() == "method not found") { hasSubmitBlockMethod = false; } else if (submitBlockResponse.Error?.Code == -1) { hasSubmitBlockMethod = true; } else { logger.ThrowLogPoolStartupException($"Unable detect block submission RPC method", LogCat); } if (!hasLegacyDaemon) { await UpdateNetworkStatsAsync(); } else { await UpdateNetworkStatsLegacyAsync(); } Observable.Interval(TimeSpan.FromMinutes(10)) .Select(via => Observable.FromAsync(() => !hasLegacyDaemon ? UpdateNetworkStatsAsync() : UpdateNetworkStatsLegacyAsync())) .Concat() .Subscribe(); SetupCrypto(); SetupJobUpdates(); }
public void Init(BlockTemplate blockTemplate, string jobId, PoolConfig poolConfig, ClusterConfig clusterConfig, IMasterClock clock, IDestination poolAddressDestination, BitcoinNetworkType networkType, bool isPoS, double shareMultiplier, IHashAlgorithm coinbaseHasher, IHashAlgorithm headerHasher, IHashAlgorithm blockHasher) { Contract.RequiresNonNull(blockTemplate, nameof(blockTemplate)); Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); Contract.RequiresNonNull(clock, nameof(clock)); Contract.RequiresNonNull(poolAddressDestination, nameof(poolAddressDestination)); Contract.RequiresNonNull(coinbaseHasher, nameof(coinbaseHasher)); Contract.RequiresNonNull(headerHasher, nameof(headerHasher)); Contract.RequiresNonNull(blockHasher, nameof(blockHasher)); Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(jobId), $"{nameof(jobId)} must not be empty"); this.poolConfig = poolConfig; coin = poolConfig.Template.As <BitcoinTemplate>(); txVersion = coin.CoinbaseTxVersion; this.clock = clock; this.poolAddressDestination = poolAddressDestination; this.networkType = networkType; BlockTemplate = blockTemplate; JobId = jobId; Difficulty = new Target(new NBitcoin.BouncyCastle.Math.BigInteger(BlockTemplate.Target, 16)).Difficulty; extraNoncePlaceHolderLength = BitcoinConstants.ExtranoncePlaceHolderLength; this.isPoS = isPoS; this.shareMultiplier = shareMultiplier; if (coin.HasMasterNodes) { masterNodeParameters = BlockTemplate.Extra.SafeExtensionDataAs <MasterNodeBlockTemplateExtra>(); } this.coinbaseHasher = coinbaseHasher; this.headerHasher = headerHasher; this.blockHasher = blockHasher; if (!string.IsNullOrEmpty(BlockTemplate.Target)) { blockTargetValue = new uint256(BlockTemplate.Target); } else { var tmp = new Target(BlockTemplate.Bits.HexToByteArray()); blockTargetValue = tmp.ToUInt256(); } previousBlockHashReversedHex = BlockTemplate.PreviousBlockhash .HexToByteArray() .ReverseByteOrder() .ToHexString(); BuildMerkleBranches(); BuildCoinbase(); jobParams = new object[] { JobId, previousBlockHashReversedHex, coinbaseInitialHex, coinbaseFinalHex, merkleBranchesHex, BlockTemplate.Version.ToStringHex8(), BlockTemplate.Bits, BlockTemplate.CurTime.ToStringHex8(), false }; }
public virtual void Init(EquihashBlockTemplate blockTemplate, string jobId, PoolConfig poolConfig, ClusterConfig clusterConfig, IMasterClock clock, IDestination poolAddressDestination, BitcoinNetworkType networkType, EquihashSolver solver) { Contract.RequiresNonNull(blockTemplate, nameof(blockTemplate)); Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); Contract.RequiresNonNull(clock, nameof(clock)); Contract.RequiresNonNull(poolAddressDestination, nameof(poolAddressDestination)); Contract.RequiresNonNull(solver, nameof(solver)); Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(jobId), $"{nameof(jobId)} must not be empty"); this.clock = clock; this.poolAddressDestination = poolAddressDestination; coin = poolConfig.Template.As <EquihashCoinTemplate>(); coin.Networks.TryGetValue(networkType.ToString().ToLower(), out chainConfig); network = networkType.ToNetwork(); BlockTemplate = blockTemplate; JobId = jobId; Difficulty = (double)new BigRational(chainConfig.Diff1BValue, BlockTemplate.Target.HexToReverseByteArray().AsSpan().ToBigInteger()); // ZCash Sapling & Overwinter support isSaplingActive = chainConfig.SaplingActivationHeight.HasValue && chainConfig.SaplingTxVersion.HasValue && chainConfig.SaplingTxVersionGroupId.HasValue && chainConfig.SaplingActivationHeight.Value > 0 && blockTemplate.Height >= chainConfig.SaplingActivationHeight.Value; isOverwinterActive = isSaplingActive || chainConfig.OverwinterTxVersion.HasValue && chainConfig.OverwinterTxVersionGroupId.HasValue && chainConfig.OverwinterActivationHeight.HasValue && chainConfig.OverwinterActivationHeight.Value > 0 && blockTemplate.Height >= chainConfig.OverwinterActivationHeight.Value; if (isSaplingActive) { txVersion = chainConfig.SaplingTxVersion.Value; txVersionGroupId = chainConfig.SaplingTxVersionGroupId.Value; } else if (isOverwinterActive) { txVersion = chainConfig.OverwinterTxVersion.Value; txVersionGroupId = chainConfig.OverwinterTxVersionGroupId.Value; } // Misc this.solver = solver; if (!string.IsNullOrEmpty(BlockTemplate.Target)) { blockTargetValue = new uint256(BlockTemplate.Target); } else { var tmp = new Target(BlockTemplate.Bits.HexToByteArray()); blockTargetValue = tmp.ToUInt256(); } previousBlockHashReversedHex = BlockTemplate.PreviousBlockhash .HexToByteArray() .ReverseInPlace() .ToHexString(); if (blockTemplate.Subsidy != null) { blockReward = blockTemplate.Subsidy.Miner * BitcoinConstants.SatoshisPerBitcoin; } else { blockReward = BlockTemplate.CoinbaseValue; } if (chainConfig?.PayFoundersReward == true) { var founders = blockTemplate.Subsidy.Founders ?? blockTemplate.Subsidy.Community; if (!founders.HasValue) { throw new Exception("Error, founders reward missing for block template"); } blockReward = (blockTemplate.Subsidy.Miner + founders.Value) * BitcoinConstants.SatoshisPerBitcoin; } rewardFees = blockTemplate.Transactions.Sum(x => x.Fee); BuildCoinbase(); // build tx hashes var txHashes = new List <uint256> { new uint256(coinbaseInitialHash) }; txHashes.AddRange(BlockTemplate.Transactions.Select(tx => new uint256(tx.Hash.HexToReverseByteArray()))); // build merkle root merkleRoot = MerkleNode.GetRoot(txHashes).Hash.ToBytes().ReverseInPlace(); merkleRootReversed = merkleRoot.ReverseInPlace(); merkleRootReversedHex = merkleRootReversed.ToHexString(); // misc var hashReserved = isSaplingActive && !string.IsNullOrEmpty(blockTemplate.FinalSaplingRootHash) ? blockTemplate.FinalSaplingRootHash.HexToReverseByteArray().ToHexString() : sha256Empty.ToHexString(); jobParams = new object[] { JobId, BlockTemplate.Version.ReverseByteOrder().ToStringHex8(), previousBlockHashReversedHex, merkleRootReversedHex, hashReserved, BlockTemplate.CurTime.ReverseByteOrder().ToStringHex8(), BlockTemplate.Bits.HexToReverseByteArray().ToHexString(), false }; }
protected override async Task PostStartInitAsync() { var commands = new[] { new DaemonCmd(BitcoinCommands.ValidateAddress, new[] { poolConfig.Address }), new DaemonCmd(BitcoinCommands.GetDifficulty), new DaemonCmd(BitcoinCommands.SubmitBlock), new DaemonCmd(BitcoinCommands.GetBlockchainInfo) }; var results = await daemon.ExecuteBatchAnyAsync(commands); if (results.Any(x => x.Error != null)) { var resultList = results.ToList(); var errors = results.Where(x => x.Error != null && commands[resultList.IndexOf(x)].Method != BitcoinCommands.SubmitBlock) .ToArray(); if (errors.Any()) { logger.ThrowLogPoolStartupException($"Init RPC failed: {string.Join(", ", errors.Select(y => y.Error.Message))}", LogCat); } } // extract results var validateAddressResponse = results[0].Response.ToObject <ValidateAddressResponse>(); var difficultyResponse = results[1].Response.ToObject <JToken>(); var submitBlockResponse = results[2]; var blockchainInfoResponse = results[3].Response.ToObject <BlockchainInfo>(); // ensure pool owns wallet if (!validateAddressResponse.IsValid) { logger.ThrowLogPoolStartupException($"Daemon reports pool-address '{poolConfig.Address}' as invalid", LogCat); } if (!validateAddressResponse.IsMine) { logger.ThrowLogPoolStartupException($"Daemon does not own pool-address '{poolConfig.Address}'", LogCat); } isPoS = difficultyResponse.Values().Any(x => x.Path == "proof-of-stake"); // Create pool address script from response if (isPoS) { poolAddressDestination = new PubKey(validateAddressResponse.PubKey); } else { poolAddressDestination = BitcoinUtils.AddressToScript(validateAddressResponse.Address); } // chain detection if (blockchainInfoResponse.Chain.ToLower() == "test") { networkType = BitcoinNetworkType.Test; } else if (blockchainInfoResponse.Chain.ToLower() == "regtest") { networkType = BitcoinNetworkType.RegTest; } else { networkType = BitcoinNetworkType.Main; } ConfigureRewards(); // update stats BlockchainStats.NetworkType = networkType.ToString(); BlockchainStats.RewardType = isPoS ? "POS" : "POW"; // block submission RPC method if (submitBlockResponse.Error?.Message?.ToLower() == "method not found") { hasSubmitBlockMethod = false; } else if (submitBlockResponse.Error?.Code == -1) { hasSubmitBlockMethod = true; } else { logger.ThrowLogPoolStartupException($"Unable detect block submission RPC method", LogCat); } await UpdateNetworkStatsAsync(); SetupCrypto(); SetupJobUpdates(); }
public override void Init(ZCashBlockTemplate blockTemplate, string jobId, PoolConfig poolConfig, ClusterConfig clusterConfig, IMasterClock clock, IDestination poolAddressDestination, BitcoinNetworkType networkType, bool isPoS, double shareMultiplier, decimal blockrewardMultiplier, IHashAlgorithm coinbaseHasher, IHashAlgorithm headerHasher, IHashAlgorithm blockHasher) { Contract.RequiresNonNull(blockTemplate, nameof(blockTemplate)); Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); Contract.RequiresNonNull(clock, nameof(clock)); Contract.RequiresNonNull(poolAddressDestination, nameof(poolAddressDestination)); Contract.RequiresNonNull(coinbaseHasher, nameof(coinbaseHasher)); Contract.RequiresNonNull(headerHasher, nameof(headerHasher)); Contract.RequiresNonNull(blockHasher, nameof(blockHasher)); Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(jobId), $"{nameof(jobId)} must not be empty"); this.poolConfig = poolConfig; this.clusterConfig = clusterConfig; this.clock = clock; this.poolAddressDestination = poolAddressDestination; this.networkType = networkType; if (ZCashConstants.Chains.TryGetValue(poolConfig.Coin.Type, out var chain)) { chain.TryGetValue(networkType, out chainConfig); } BlockTemplate = blockTemplate; JobId = jobId; Difficulty = (double)new BigRational(chainConfig.Diff1b, BlockTemplate.Target.HexToByteArray().ReverseArray().ToBigInteger()); txExpiryHeight = blockTemplate.Height + 100; // ZCash Sapling & Overwinter support isSaplingActive = chainConfig.SaplingActivationHeight.HasValue && chainConfig.SaplingActivationHeight.Value > 0 && blockTemplate.Height >= chainConfig.SaplingActivationHeight.Value; isOverwinterActive = isSaplingActive || chainConfig.OverwinterActivationHeight.HasValue && chainConfig.OverwinterActivationHeight.Value > 0 && blockTemplate.Height >= chainConfig.OverwinterActivationHeight.Value; if (isSaplingActive) { txVersion = 4; txVersionGroupId = 0x892f2085; } else if (isOverwinterActive) { txVersion = 3; txVersionGroupId = 0x03C48270; } // Misc this.isPoS = isPoS; this.shareMultiplier = shareMultiplier; this.headerHasher = headerHasher; this.blockHasher = blockHasher; this.equihash = chainConfig.Solver(); if (!string.IsNullOrEmpty(BlockTemplate.Target)) { blockTargetValue = new uint256(BlockTemplate.Target); } else { var tmp = new Target(BlockTemplate.Bits.HexToByteArray()); blockTargetValue = tmp.ToUInt256(); } previousBlockHashReversedHex = BlockTemplate.PreviousBlockhash .HexToByteArray() .ReverseArray() .ToHexString(); blockReward = blockTemplate.Subsidy.Miner * BitcoinConstants.SatoshisPerBitcoin; if (chainConfig?.PayFoundersReward == true) { var founders = blockTemplate.Subsidy.Founders ?? blockTemplate.Subsidy.Community; if (!founders.HasValue) { throw new Exception("Error, founders reward missing for block template"); } blockReward = (blockTemplate.Subsidy.Miner + founders.Value) * BitcoinConstants.SatoshisPerBitcoin; } rewardFees = blockTemplate.Transactions.Sum(x => x.Fee); BuildCoinbase(); // build tx hashes var txHashes = new List <uint256> { new uint256(coinbaseInitialHash) }; txHashes.AddRange(BlockTemplate.Transactions.Select(tx => new uint256(tx.Hash.HexToByteArray().ReverseArray()))); // build merkle root merkleRoot = MerkleNode.GetRoot(txHashes).Hash.ToBytes().ReverseArray(); merkleRootReversed = merkleRoot.ReverseArray(); merkleRootReversedHex = merkleRootReversed.ToHexString(); // misc var hashReserved = isSaplingActive && !string.IsNullOrEmpty(blockTemplate.FinalSaplingRootHash) ? blockTemplate.FinalSaplingRootHash.HexToByteArray().ReverseArray().ToHexString() : sha256Empty.ToHexString(); jobParams = new object[] { JobId, BlockTemplate.Version.ReverseByteOrder().ToStringHex8(), previousBlockHashReversedHex, merkleRootReversedHex, hashReserved, BlockTemplate.CurTime.ReverseByteOrder().ToStringHex8(), BlockTemplate.Bits.HexToByteArray().ReverseArray().ToHexString(), false }; }
public override void Init(ZCashBlockTemplate blockTemplate, string jobId, PoolConfig poolConfig, XPoolConfig clusterConfig, IMasterClock clock, IDestination poolAddressDestination, BitcoinNetworkType networkType, bool isPoS, double shareMultiplier, decimal blockrewardMultiplier, IHashAlgorithm coinbaseHasher, IHashAlgorithm headerHasher, IHashAlgorithm blockHasher) { Assertion.RequiresNonNull(blockTemplate, nameof(blockTemplate)); Assertion.RequiresNonNull(poolConfig, nameof(poolConfig)); Assertion.RequiresNonNull(clusterConfig, nameof(clusterConfig)); Assertion.RequiresNonNull(clock, nameof(clock)); Assertion.RequiresNonNull(poolAddressDestination, nameof(poolAddressDestination)); Assertion.RequiresNonNull(coinbaseHasher, nameof(coinbaseHasher)); Assertion.RequiresNonNull(headerHasher, nameof(headerHasher)); Assertion.RequiresNonNull(blockHasher, nameof(blockHasher)); Assertion.Requires<ArgumentException>(!string.IsNullOrEmpty(jobId), $"{nameof(jobId)} must not be empty"); this.poolConfig = poolConfig; this.clusterConfig = clusterConfig; this.clock = clock; this.poolAddressDestination = poolAddressDestination; this.networkType = networkType; if (ZCashConstants.CoinbaseTxConfig.TryGetValue(poolConfig.Coin.Type, out var coinbaseTx)) coinbaseTx.TryGetValue(networkType, out coinbaseTxConfig); BlockTemplate = blockTemplate; JobId = jobId; Difficulty = (double) new BigRational(coinbaseTxConfig.Diff1b, BlockTemplate.Target.HexToByteArray().ReverseArray().ToBigInteger()); this.isPoS = isPoS; this.shareMultiplier = shareMultiplier; this.headerHasher = headerHasher; this.blockHasher = blockHasher; if (!string.IsNullOrEmpty(BlockTemplate.Target)) blockTargetValue = new uint256(BlockTemplate.Target); else { var tmp = new Target(BlockTemplate.Bits.HexToByteArray()); blockTargetValue = tmp.ToUInt256(); } previousBlockHashReversedHex = BlockTemplate.PreviousBlockhash .HexToByteArray() .ReverseArray() .ToHexString(); blockReward = blockTemplate.Subsidy.Miner * BitcoinConstants.SatoshisPerBitcoin; if (coinbaseTxConfig?.PayFoundersReward == true) { var founders = blockTemplate.Subsidy.Founders ?? blockTemplate.Subsidy.Community; if (!founders.HasValue) throw new Exception("Error, founders reward missing for block template"); blockReward = (blockTemplate.Subsidy.Miner + founders.Value) * BitcoinConstants.SatoshisPerBitcoin; } rewardFees = blockTemplate.Transactions.Sum(x => x.Fee); BuildCoinbase(); var txHashes = new List<uint256> { new uint256(coinbaseInitialHash) }; txHashes.AddRange(BlockTemplate.Transactions.Select(tx => new uint256(tx.Hash.HexToByteArray().ReverseArray()))); merkleRoot = MerkleNode.GetRoot(txHashes).Hash.ToBytes().ReverseArray(); merkleRootReversed = merkleRoot.ReverseArray(); merkleRootReversedHex = merkleRootReversed.ToHexString(); jobParams = new object[] { JobId, BlockTemplate.Version.ReverseByteOrder().ToStringHex8(), previousBlockHashReversedHex, merkleRootReversedHex, sha256Empty.ToHexString(), BlockTemplate.CurTime.ReverseByteOrder().ToStringHex8(), BlockTemplate.Bits.HexToByteArray().ReverseArray().ToHexString(), false }; }
protected override async Task PostStartInitAsync(CancellationToken ct) { var commands = new[] { new DaemonCmd(BitcoinCommands.ValidateAddress, new[] { poolConfig.Address }), new DaemonCmd(BitcoinCommands.GetInfo), new DaemonCmd(BitcoinCommands.GetDifficulty), }; var results = await _daemon.ExecuteBatchAnyAsync(commands); if (results.Any(x => x.Error != null)) { var resultList = results.ToList(); var errors = results.Where(x => x.Error != null && commands[resultList.IndexOf(x)].Method != BitcoinCommands.SubmitBlock) .ToArray(); if (errors.Any()) { logger.ThrowLogPoolStartupException($"Init RPC failed: {string.Join(", ", errors.Select(y => y.Error.Message))}", LogCat); } } // extract results var validateAddressResponse = results[0].Response.ToObject <ValidateAddressResponse>(); var daemonInfoResponse = results[1].Response.ToObject <DaemonInfo>(); var difficultyResponse = results[2].Response.ToObject <JToken>(); if (clusterConfig.PaymentProcessing?.Enabled == true) { var result = await _wallet.ExecuteCmdAnyAsync <ValidateAddressResponse>( BitcoinCommands.ValidateAddress, new[] { poolConfig.Address }); // extract results validateAddressResponse = result.Response; } // ensure pool owns wallet if (!validateAddressResponse.IsValid) { logger.ThrowLogPoolStartupException($"Daemon reports pool-address '{poolConfig.Address}' as invalid", LogCat); } if (clusterConfig.PaymentProcessing?.Enabled == true && !validateAddressResponse.IsMine) { logger.ThrowLogPoolStartupException($"Daemon does not own pool-address '{poolConfig.Address}'", LogCat); } // Create pool address script from response _poolAddressDestination = AddressToDestination(poolConfig.Address); // chain detection _networkType = daemonInfoResponse.Testnet ? BitcoinNetworkType.Test : BitcoinNetworkType.Main; // update stats BlockchainStats.NetworkType = _networkType.ToString(); BlockchainStats.RewardType = "POW"; await UpdateNetworkStatsAsync(); // Periodically update network stats Observable.Interval(TimeSpan.FromMinutes(10)) .Select(via => Observable.FromAsync(() => UpdateNetworkStatsAsync())) .Concat() .Subscribe(); SetupCrypto(); SetupJobUpdates(); }
public override void Init(EquihashBlockTemplate blockTemplate, string jobId, PoolConfig poolConfig, ClusterConfig clusterConfig, IMasterClock clock, IDestination poolAddressDestination, BitcoinNetworkType networkType, EquihashSolver solver) { Contract.RequiresNonNull(blockTemplate, nameof(blockTemplate)); Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); Contract.RequiresNonNull(clock, nameof(clock)); Contract.RequiresNonNull(poolAddressDestination, nameof(poolAddressDestination)); Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(jobId), $"{nameof(jobId)} must not be empty"); this.clock = clock; this.poolAddressDestination = poolAddressDestination; this.networkType = networkType; var equihashTemplate = poolConfig.Template.As <EquihashCoinTemplate>(); equihashTemplate.Networks.TryGetValue(networkType.ToString().ToLower(), out chainConfig); BlockTemplate = blockTemplate; JobId = jobId; Difficulty = (double)new BigRational(chainConfig.Diff1BValue, BlockTemplate.Target.HexToByteArray().ReverseArray().AsSpan().ToBigInteger()); this.solver = solver; if (!string.IsNullOrEmpty(BlockTemplate.Target)) { blockTargetValue = new uint256(BlockTemplate.Target); } else { var tmp = new Target(BlockTemplate.Bits.HexToByteArray()); blockTargetValue = tmp.ToUInt256(); } previousBlockHashReversedHex = BlockTemplate.PreviousBlockhash .HexToByteArray() .ReverseArray() .ToHexString(); BuildCoinbase(); // build tx hashes var txHashes = new List <uint256> { new uint256(coinbaseInitialHash) }; txHashes.AddRange(BlockTemplate.Transactions.Select(tx => new uint256(tx.TxId.HexToByteArray().ReverseArray()))); // build merkle root merkleRoot = MerkleNode.GetRoot(txHashes).Hash.ToBytes().ReverseArray(); merkleRootReversed = merkleRoot.ReverseArray(); merkleRootReversedHex = merkleRootReversed.ToHexString(); jobParams = new object[] { JobId, BlockTemplate.Version.ReverseByteOrder().ToStringHex8(), previousBlockHashReversedHex, merkleRootReversedHex, BlockTemplate.Height.ReverseByteOrder().ToStringHex8() + sha256Empty.Take(28).ToHexString(), // height + hashReserved BlockTemplate.CurTime.ReverseByteOrder().ToStringHex8(), BlockTemplate.Bits.HexToByteArray().ReverseArray().ToHexString(), false }; }