Exemplo n.º 1
0
        // transactions need to be already sorted
        private Block(long ledgerHeight, SortedList <TransactionHash, SignedTransaction> transactions, short?index)
        {
            LedgerHeight        = ledgerHeight;
            FeeTransactionIndex = index;
            Transactions        = transactions.Values.ToList();

            // TODO fee transaction index?
            // TODO LedgerHeight?
            merkleRoot = MerkleNode <TransactionHash> .GetRoot(Transactions.Select(t => t.Hash));

            Hash = new BlockHash(merkleRoot.Hash.Bytes);
        }
        private LedgerMerkleRoot(SortedList <AccountHash, Account> accounts, SortedList <TxDeclarationHash, TxDeclaration> declarations, ILogger logger)
        {
            // TODO logger here looks ugly
            this.logger   = logger;
            this.accounts = MerkleNode <AccountHash> .GetRoot(accounts.Keys);

            this.declarations = MerkleNode <TxDeclarationHash> .GetRoot(declarations.Keys);

            Hash = GetHash();

#if DEBUG
            DumpTree(accounts, declarations);
#endif
        }
Exemplo n.º 3
0
        public ViewResult MAST(MASTModel model)
        {
            try
            {
                var script = new Script(Sanitize(model.Script));
                model.DecomposedScripts = new List <DecomposedScript>();
                var subScripts = script.Decompose();
                var hashes     = subScripts.Select(s => Hashes.Hash256(s.ToBytes())).ToArray();
                model.MerkleRoot = MerkleNode.GetRoot(hashes).Hash;
                int fullScriptSize = script.ToBytes().Length;
                model.FullScriptSize = fullScriptSize;
                int i = 0;
                foreach (var subScript in subScripts)
                {
                    bool[] matches = new bool[hashes.Length];
                    matches[i] = true;
                    PartialMerkleTree partial = new PartialMerkleTree(hashes, matches);
                    DecomposedScript  s       = new DecomposedScript();
                    s.Script            = subScript.ToString();
                    s.Bytes             = Encoders.Hex.EncodeData(subScript.ToBytes());
                    s.Hash              = Hashes.Hash256(subScript.ToBytes());
                    s.PartialMerkleTree = Encoders.Hex.EncodeData(partial.ToBytes());
                    model.DecomposedScripts.Add(s);
                    s.Size   = subScript.ToBytes().Length + partial.ToBytes().Length;
                    s.Saving = (int)(((decimal)(fullScriptSize - s.Size) / fullScriptSize) * 100m);

                    s.Size160   = (int)(subScript.ToBytes().Length + (decimal)partial.ToBytes().Length * 0.625m);
                    s.Saving160 = (int)(((decimal)(fullScriptSize - s.Size160) / fullScriptSize) * 100m);
                    i++;
                }
            }
            catch (FormatException ex)
            {
                ModelState.AddModelError("Script", "Invalid script (" + ex.Message + ")");
                return(View(model));
            }
            return(View(model));
        }
Exemplo n.º 4
0
    public override void Init(EquihashBlockTemplate blockTemplate, string jobId,
                              PoolConfig poolConfig, ClusterConfig clusterConfig, IMasterClock clock,
                              IDestination poolAddressDestination, Network network,
                              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;
        coin         = poolConfig.Template.As <EquihashCoinTemplate>();
        this.network = network;
        var equihashTemplate = poolConfig.Template.As <EquihashCoinTemplate>();

        networkParams = coin.GetNetwork(network.ChainName);
        BlockTemplate = blockTemplate;
        JobId         = jobId;
        Difficulty    = (double)new BigRational(networkParams.Diff1BValue, BlockTemplate.Target.HexToReverseByteArray().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()
                                       .ReverseInPlace()
                                       .ToHexString();

        BuildCoinbase();

        // build tx hashes
        var txHashes = new List <uint256> {
            new(coinbaseInitialHash)
        };

        txHashes.AddRange(BlockTemplate.Transactions.Select(tx => new uint256(tx.TxId.HexToReverseByteArray())));

        // build merkle root
        merkleRoot            = MerkleNode.GetRoot(txHashes).Hash.ToBytes().ReverseInPlace();
        merkleRootReversed    = merkleRoot.ReverseInPlace();
        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.HexToReverseByteArray().ToHexString(),
            false
        };
    }
}
Exemplo n.º 5
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();
        }
Exemplo n.º 6
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
            };
        }
Exemplo n.º 7
0
        public virtual void Init(EquihashBlockTemplate blockTemplate, string jobId,
                                 PoolConfig poolConfig, ClusterConfig clusterConfig, IMasterClock clock,
                                 IDestination poolAddressDestination, Network network,
                                 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>();
            networkParams = coin.GetNetwork(network.NetworkType);
            this.network  = network;
            BlockTemplate = blockTemplate;
            JobId         = jobId;
            Difficulty    = (double)new BigRational(networkParams.Diff1BValue, BlockTemplate.Target.HexToReverseByteArray().AsSpan().ToBigInteger());

            // ZCash Sapling & Overwinter support
            isSaplingActive = networkParams.SaplingActivationHeight.HasValue &&
                              networkParams.SaplingTxVersion.HasValue &&
                              networkParams.SaplingTxVersionGroupId.HasValue &&
                              networkParams.SaplingActivationHeight.Value > 0 &&
                              blockTemplate.Height >= networkParams.SaplingActivationHeight.Value;

            isOverwinterActive = isSaplingActive ||
                                 networkParams.OverwinterTxVersion.HasValue &&
                                 networkParams.OverwinterTxVersionGroupId.HasValue &&
                                 networkParams.OverwinterActivationHeight.HasValue &&
                                 networkParams.OverwinterActivationHeight.Value > 0 &&
                                 blockTemplate.Height >= networkParams.OverwinterActivationHeight.Value;

            if (isSaplingActive)
            {
                txVersion        = networkParams.SaplingTxVersion.Value;
                txVersionGroupId = networkParams.SaplingTxVersionGroupId.Value;
            }

            else if (isOverwinterActive)
            {
                txVersion        = networkParams.OverwinterTxVersion.Value;
                txVersionGroupId = networkParams.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 (networkParams?.vFundingStreams == true)
            {
                decimal fundingstreamTotal = 0;
                fundingstreamTotal = blockTemplate.Subsidy.FundingStreams.Sum(x => x.Value);
                blockReward        = (blockTemplate.Subsidy.Miner + fundingstreamTotal) * BitcoinConstants.SatoshisPerBitcoin;
            }
            else if (networkParams?.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();

            var solutionIn = !string.IsNullOrEmpty(blockTemplate.Solution) ?
                             blockTemplate.Solution.HexToByteArray().ToHexString() :
                             null;

            jobParams = new object[]
            {
                JobId,
                BlockTemplate.Version.ReverseByteOrder().ToStringHex8(),
                previousBlockHashReversedHex,
                merkleRootReversedHex,
                hashReserved,
                BlockTemplate.CurTime.ReverseByteOrder().ToStringHex8(),
                BlockTemplate.Bits.HexToReverseByteArray().ToHexString(),
                false,
                solutionIn
            };
        }
Exemplo n.º 8
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().AsSpan().ToBigInteger());
            txExpiryHeight = blockTemplate.Height + 100;

            // 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.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
            };
        }
Exemplo n.º 9
0
 public MerkleNode GetMerkleRoot()
 {
     return(MerkleNode.GetRoot(this.Transactions.Select(t => t.GetHash())));
 }
Exemplo n.º 10
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
            };
        }