예제 #1
0
        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;
        }
예제 #2
0
        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;
        }
예제 #3
0
        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();
        }
예제 #5
0
        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
            };
        }
예제 #6
0
        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");
            }
        }
예제 #7
0
        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();
        }
예제 #8
0
        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();
        }
예제 #9
0
        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
            };
        }
예제 #10
0
        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();
        }
예제 #11
0
        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
            };
        }
예제 #12
0
        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
            };
        }
예제 #13
0
        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();
        }
예제 #14
0
        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
            };
        }
예제 #15
0
        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();
        }
예제 #17
0
        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
            };
        }