public MoneroJob(GetBlockTemplateResponse blockTemplate, byte[] instanceId, string jobId, PoolConfig poolConfig, ClusterConfig clusterConfig) { Contract.RequiresNonNull(blockTemplate, nameof(blockTemplate)); Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); Contract.RequiresNonNull(instanceId, nameof(instanceId)); Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(jobId), $"{nameof(jobId)} must not be empty"); switch (poolConfig.Coin.Type) { case CoinType.AEON: hashSlow = LibCryptonote.CryptonightHashSlowLite; break; case CoinType.XMR: hashSlow = buf => { // PoW variant var variant = buf[0] >= 7 ? buf[0] - 6 : 0; return(LibCryptonote.CryptonightHashSlow(buf, variant)); }; break; default: hashSlow = buf => LibCryptonote.CryptonightHashSlow(buf, 0); break; } BlockTemplate = blockTemplate; PrepareBlobTemplate(instanceId); }
protected override async Task PostStartInitAsync() { var infoResponse = await daemon.ExecuteCmdAnyAsync(MC.GetInfo); if (infoResponse.Error != null) { logger.ThrowLogPoolStartupException($"Init RPC failed: {infoResponse.Error.Message} (Code {infoResponse.Error.Code})", LogCat); } if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) { var addressResponse = await walletDaemon.ExecuteCmdAnyAsync <GetAddressResponse>(MWC.GetAddress); // ensure pool owns wallet if (clusterConfig.PaymentProcessing?.Enabled == true && addressResponse.Response?.Address != poolConfig.Address) { logger.ThrowLogPoolStartupException($"Wallet-Daemon does not own pool-address '{poolConfig.Address}'", LogCat); } } var info = infoResponse.Response.ToObject <GetInfoResponse>(); // chain detection networkType = info.IsTestnet ? MoneroNetworkType.Test : MoneroNetworkType.Main; // address validation poolAddressBase58Prefix = LibCryptonote.DecodeAddress(poolConfig.Address); if (poolAddressBase58Prefix == 0) { logger.ThrowLogPoolStartupException("Unable to decode pool-address", LogCat); } switch (networkType) { case MoneroNetworkType.Main: if (poolAddressBase58Prefix != MoneroConstants.AddressPrefix[poolConfig.Coin.Type]) { logger.ThrowLogPoolStartupException($"Invalid pool address prefix. Expected {MoneroConstants.AddressPrefix[poolConfig.Coin.Type]}, got {poolAddressBase58Prefix}", LogCat); } break; case MoneroNetworkType.Test: if (poolAddressBase58Prefix != MoneroConstants.AddressPrefixTestnet[poolConfig.Coin.Type]) { logger.ThrowLogPoolStartupException($"Invalid pool address prefix. Expected {MoneroConstants.AddressPrefix[poolConfig.Coin.Type]}, got {poolAddressBase58Prefix}", LogCat); } break; } // if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) // ConfigureRewards(); // update stats BlockchainStats.RewardType = "POW"; BlockchainStats.NetworkType = networkType.ToString(); await UpdateNetworkStatsAsync(); SetupJobUpdates(); }
public void Crytonote_DecodeIntegratedAddress() { var address = "4BrL51JCc9NGQ71kWhnYoDRffsDZy7m1HUU7MRU4nUMXAHNFBEJhkTZV9HdaL4gfuNBxLPc3BeMkLGaPbF5vWtANQsGwTGg55Kq4p3ENE7"; var result = LibCryptonote.DecodeIntegratedAddress(address); Assert.Equal(19ul, result); }
public void Crytonote_ConvertBlob() { var blob = "0106e5b3afd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b421c0300a401d90101ff9d0106d6d6a88702023c62e43372a58cb588147e20be53a27083f5c522f33c722b082ab7518c48cda280b4c4c32102609ec96e2499ee267d70efefc49f26e330526d3ef455314b7b5ba268a6045f8c80c0fc82aa0202fe5cc0fa56c4277d1a47827edce4725571529d57f33c73ada481ef84c323f30a8090cad2c60e02d88bf5e72a611c8b8464ce29e3b1adbfe1ae163886d9150fe511171cada98fcb80e08d84ddcb0102441915aaf9fbaf70ff454c701a6ae2bd59bb94dc0b888bf7e5d06274ee9238ca80c0caf384a302024078526e2132def44bde2806242652f5944e632f7d94290dd6ee5dda1929f5ee2b016e29f25f07ec2a8df59f0e118a6c9a4b769b745dc0c729071f6e0399d2585745020800000000012e7f76000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".HexToByteArray(); var result = LibCryptonote.ConvertBlob(blob, 330).ToHexString(); Assert.Equal("0106e5b3afd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b421c0300a4487286e262e95b8d2163a0c8b73527e8c9425adbdc4e532cf0ef4241f9ffbe9e01", result); }
public void Crytonote_DecodeAddress() { var address = "48nhyWcSey31ngSEhV8j8NPm6B8PistCQJBjjDjmTvRSTWYg6iocAw131vE2JPh3ps33vgQDKLrUx3fcErusYWcMJBxpm1d"; var result = LibCryptonote.DecodeAddress(address); Assert.Equal(18ul, result); }
public void Crytonote_Hash_Fast() { var blobConverted = "0106a2aaafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42580100a4b1e2f4baf6ab7109071ab59bc52dba740d1de99fa0ae0c4afd6ea9f40c5d87ec01".HexToByteArray(); var result = LibCryptonote.CryptonightHashFast(blobConverted).ToHexString(); Assert.Equal("ddc0e3a33b605ce39fa2d16a98d7634e33399ab1e4b56b3bdd3414b655fe9a98", result); }
public void Crytonote_Hash_Slow_Lite() { var blobConverted = "0106f1adafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42597710c48c6d885e2622f40f82ecd9b9fd538f28df9b0557e07cd3237a31c76569ada98001".HexToByteArray(); var result = LibCryptonote.CryptonightHashSlowLite(blobConverted).ToHexString(); Assert.Equal("0769caee428a232cffb76fa200f174ff962734f24e7b3bf8d1b0d4e8ba6ceebf", result); }
public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig) { Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); logger = LogUtil.GetPoolScopedLogger(typeof(JobManagerBase <MoneroJob>), poolConfig); this.poolConfig = poolConfig; this.clusterConfig = clusterConfig; poolAddressBase58Prefix = LibCryptonote.DecodeAddress(poolConfig.Address); if (poolAddressBase58Prefix == 0) { logger.ThrowLogPoolStartupException("Unable to decode pool-address", LogCat); } // extract standard daemon endpoints daemonEndpoints = poolConfig.Daemons .Where(x => string.IsNullOrEmpty(x.Category)) .ToArray(); // extract wallet daemon endpoints walletDaemonEndpoints = poolConfig.Daemons .Where(x => x.Category?.ToLower() == MoneroConstants.WalletDaemonCategory) .ToArray(); if (walletDaemonEndpoints.Length == 0) { logger.ThrowLogPoolStartupException("Wallet-RPC daemon is not configured (Daemon configuration for monero-pools require an additional entry of category \'wallet' pointing to the wallet daemon)", LogCat); } ConfigureDaemons(); }
public void Crytonote_Hash_Slow() { var blobConverted = "0106a2aaafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42580100a4b1e2f4baf6ab7109071ab59bc52dba740d1de99fa0ae0c4afd6ea9f40c5d87ec01".HexToByteArray(); var result = LibCryptonote.CryptonightHashSlow(blobConverted, 0).ToHexString(); Assert.Equal("a845ffbdf83ae9a8ffa504a1011efbd5ed2294bb9da591d3b583740568402c00", result); }
public bool ValidateAddress(string address) { Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(address), $"{nameof(address)} must not be empty"); var addressPrefix = LibCryptonote.DecodeAddress(address); var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(address); var coin = poolConfig.Template.As <CryptonoteCoinTemplate>(); switch (networkType) { case CryptonoteNetworkType.Main: if (addressPrefix != coin.AddressPrefix && addressIntegratedPrefix != coin.AddressPrefixIntegrated) { return(false); } break; case CryptonoteNetworkType.Test: if (addressPrefix != coin.AddressPrefixTestnet && addressIntegratedPrefix != coin.AddressPrefixIntegratedTestnet) { return(false); } break; } return(true); }
public void Crytonote_Hash_Slow_Variant_1() { var blobConverted = "0106a2aaafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42580100a4b1e2f4baf6ab7109071ab59bc52dba740d1de99fa0ae0c4afd6ea9f40c5d87ec01".HexToByteArray(); var result = LibCryptonote.CryptonightHashSlow(blobConverted, 1).ToHexString(); Assert.Equal("c41ec6434df8b2307ff3105ae15206f3fbdf5a99b35879c0a27b8b85a8e2704f", result); }
public bool ValidateAddress(string address) { Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(address), $"{nameof(address)} must not be empty"); var addressPrefix = LibCryptonote.DecodeAddress(address); var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(address); switch (networkType) { case MoneroNetworkType.Main: if (addressPrefix != MoneroConstants.AddressPrefix[poolConfig.Coin.Type] && addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type]) { return(false); } break; case MoneroNetworkType.Test: if (addressPrefix != MoneroConstants.AddressPrefixTestnet[poolConfig.Coin.Type] && addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type]) { return(false); } break; } return(true); }
public void Cryptonote_ConvertBlob_Should_Match() { var blob = "0105bfdfcecd05583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b420000000001d90101ff9d0106d6d6a88702020a79e36c5f5ac69abb68daa616b70e4dc911ed2edf50133fc121447cc403cd6780b4c4c32102b3adc5521c68a35e2dd1934e30b5fada872b384dbbf8c4e8130e43bd0097b8b680c0fc82aa0202b186f6745517ec23a87df7811849d71914a222c937da3e3a39c7bde6f27d2dc98090cad2c60e02df3a6eed49d05b0163986888ebe7da3fae808a72f3beec97346e0a18a960a7b180e08d84ddcb0102f37220a0c601e2dfe78cfab584cabeecf59079b3b2ee045561fb83ebf67941ba80c0caf384a30202b5e50c62333f3237d497eac37b26bd1217b6996eeb7d45e099b71b0f0b5399162b011c2515730ca7e8bb9b79e177557a1fa8b41e9aee544b25d69dc46f12f66b13f102080000000001ff0d7500".HexToByteArray(); var result = LibCryptonote.ConvertBlob(blob, blob.Length).ToHexString(); var expected = "0105bfdfcecd05583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b4200000000f262fa431f692fa1d8a6e89fb809487a2133dd6fd999d95c664b964df354ac4701"; Assert.Equal(expected, result); }
private PooledArraySegment <byte> ComputeBlockHash(byte[] blobConverted) { // blockhash is computed from the converted blob data prefixed with its length var bytes = new[] { (byte)blobConverted.Length } .Concat(blobConverted) .ToArray(); return(LibCryptonote.CryptonightHashFast(bytes)); }
protected override async Task RunPreInitChecksAsync() { // decode configured pool address. _poolAddressBase58Prefix = LibCryptonote.DecodeAddress(PoolContext.PoolAddress); if (_poolAddressBase58Prefix == 0) { throw new PoolStartupAbortedException("unable to decode configured pool address!"); } }
private void ComputeBlockHash(byte[] blobConverted, Span <byte> result) { // blockhash is computed from the converted blob data prefixed with its length var bytes = new[] { (byte)blobConverted.Length } .Concat(blobConverted) .ToArray(); LibCryptonote.CryptonightHashFast(bytes, result); }
private void ComputeBlockHash(ReadOnlySpan <byte> blobConverted, Span <byte> result) { // blockhash is computed from the converted blob data prefixed with its length Span <byte> block = stackalloc byte[blobConverted.Length + 1]; block[0] = (byte)blobConverted.Length; blobConverted.CopyTo(block.Slice(1)); LibCryptonote.CryptonightHashFast(block, result); }
private string EncodeBlob(uint workerExtraNonce) { Span <byte> blob = stackalloc byte[blobTemplate.Length]; blobTemplate.CopyTo(blob); // inject extranonce (big-endian at the beginning of the reserved area of the blob) var extraNonceBytes = BitConverter.GetBytes(workerExtraNonce.ToBigEndian()); extraNonceBytes.CopyTo(blob.Slice(BlockTemplate.ReservedOffset, extraNonceBytes.Length)); return(LibCryptonote.ConvertBlob(blob, blobTemplate.Length).ToHexString()); }
private string EncodeBlob(uint workerExtraNonce) { // clone template using (var blob = new PooledArraySegment <byte>(_blobTemplate.Length)) { Buffer.BlockCopy(_blobTemplate, 0, blob.Array, 0, _blobTemplate.Length); // inject extranonce (big-endian at the beginning of the reserved area of the blob) var extraNonceBytes = BitConverter.GetBytes(workerExtraNonce.ToBigEndian()); Buffer.BlockCopy(extraNonceBytes, 0, blob.Array, (int)BlockTemplate.ReservedOffset, extraNonceBytes.Length); return(LibCryptonote.ConvertBlob(blob.Array, _blobTemplate.Length).ToHexString()); } }
private string EncodeBlob(uint workerExtraNonce) { // clone template var blob = new byte[blobTemplate.Length]; Buffer.BlockCopy(blobTemplate, 0, blob, 0, blobTemplate.Length); // inject extranonce (big-endian at the beginning of the reserved area of the blob) var extraNonceBytes = BitConverter.GetBytes(workerExtraNonce.ToBigEndian()); Buffer.BlockCopy(extraNonceBytes, 0, blob, (int)BlockTemplate.ReservedOffset, extraNonceBytes.Length); var result = LibCryptonote.ConvertBlob(blob).ToHexString(); return(result); }
private bool ValidateAddress(string address) { // check address length. if (address.Length != MoneroConstants.AddressLength[CoinType.XMR]) { return(false); } var addressPrefix = LibCryptonote.DecodeAddress(address); if (addressPrefix != _poolAddressBase58Prefix) { return(false); } return(true); }
public bool ValidateAddress(string address) { Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(address), $"{nameof(address)} must not be empty"); if (address.Length != MoneroConstants.AddressLength) { return(false); } var addressPrefix = LibCryptonote.DecodeAddress(address); if (addressPrefix != poolAddressBase58Prefix) { return(false); } return(true); }
public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig) { poolAddressBase58Prefix = LibCryptonote.DecodeAddress(poolConfig.Address); if (poolAddressBase58Prefix == 0) { logger.ThrowLogPoolStartupException("Unable to decode pool-address)", LogCat); } // extract standard daemon endpoints daemonEndpoints = poolConfig.Daemons .Where(x => string.IsNullOrEmpty(x.Category)) .ToArray(); // extract wallet daemon endpoints walletDaemonEndpoints = poolConfig.Daemons .Where(x => x.Category?.ToLower() == MoneroConstants.WalletDaemonCategory) .ToArray(); base.Configure(poolConfig, clusterConfig); }
private static void TouchNativeLibs() { Console.WriteLine(LibCryptonote.CryptonightHashSlow(Encoding.UTF8.GetBytes("test"), 0).ToHexString()); Console.WriteLine(LibCryptonote.CryptonightHashFast(Encoding.UTF8.GetBytes("test")).ToHexString()); Console.WriteLine(new Blake().Digest(Encoding.UTF8.GetBytes("test"), 0).ToHexString()); }
protected override async Task PostStartInitAsync(CancellationToken ct) { var coin = poolConfig.Template.As <CryptonoteCoinTemplate>(); var infoResponse = await daemon.ExecuteCmdAnyAsync(logger, CryptonoteCommands.GetInfo); if (infoResponse.Error != null) { logger.ThrowLogPoolStartupException($"Init RPC failed: {infoResponse.Error.Message} (Code {infoResponse.Error.Code})"); } if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) { var addressResponse = await walletDaemon.ExecuteCmdAnyAsync <GetAddressResponse>(logger, ct, CryptonoteWalletCommands.GetAddress); // ensure pool owns wallet if (clusterConfig.PaymentProcessing?.Enabled == true && addressResponse.Response?.Address != poolConfig.Address) { logger.ThrowLogPoolStartupException($"Wallet-Daemon does not own pool-address '{poolConfig.Address}'"); } } var info = infoResponse.Response.ToObject <GetInfoResponse>(); // chain detection networkType = info.IsTestnet ? CryptonoteNetworkType.Test : CryptonoteNetworkType.Main; // address validation poolAddressBase58Prefix = LibCryptonote.DecodeAddress(poolConfig.Address); if (poolAddressBase58Prefix == 0) { logger.ThrowLogPoolStartupException("Unable to decode pool-address"); } switch (networkType) { case CryptonoteNetworkType.Main: if (poolAddressBase58Prefix != coin.AddressPrefix) { logger.ThrowLogPoolStartupException($"Invalid pool address prefix. Expected {coin.AddressPrefix}, got {poolAddressBase58Prefix}"); } break; case CryptonoteNetworkType.Test: if (poolAddressBase58Prefix != coin.AddressPrefixTestnet) { logger.ThrowLogPoolStartupException($"Invalid pool address prefix. Expected {coin.AddressPrefix}, got {poolAddressBase58Prefix}"); } break; } if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) { ConfigureRewards(); } // update stats BlockchainStats.RewardType = "POW"; BlockchainStats.NetworkType = networkType.ToString(); await UpdateNetworkStatsAsync(); // Periodically update network stats Observable.Interval(TimeSpan.FromMinutes(1)) .Select(via => Observable.FromAsync(async() => { try { await UpdateNetworkStatsAsync(); } catch (Exception ex) { logger.Error(ex); } })) .Concat() .Subscribe(); SetupJobUpdates(); }
public (Share Share, string BlobHex) ProcessShare(string nonce, uint workerExtraNonce, string workerHash, StratumClient worker) { Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(nonce), $"{nameof(nonce)} must not be empty"); Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(workerHash), $"{nameof(workerHash)} must not be empty"); Contract.Requires <ArgumentException>(workerExtraNonce != 0, $"{nameof(workerExtraNonce)} must not be empty"); var context = worker.ContextAs <CryptonoteWorkerContext>(); // validate nonce if (!CryptonoteConstants.RegexValidNonce.IsMatch(nonce)) { throw new StratumException(StratumError.MinusOne, "malformed nonce"); } // clone template Span <byte> blob = stackalloc byte[blobTemplate.Length]; blobTemplate.CopyTo(blob); // inject extranonce var extraNonceBytes = BitConverter.GetBytes(workerExtraNonce.ToBigEndian()); extraNonceBytes.CopyTo(blob.Slice(BlockTemplate.ReservedOffset, extraNonceBytes.Length)); // inject nonce var nonceBytes = nonce.HexToByteArray(); nonceBytes.CopyTo(blob.Slice(CryptonoteConstants.BlobNonceOffset, nonceBytes.Length)); // convert var blobConverted = LibCryptonote.ConvertBlob(blob, blobTemplate.Length); if (blobConverted == null) { throw new StratumException(StratumError.MinusOne, "malformed blob"); } Console.WriteLine($"Coin: {coin.Name} Symbol: {coin.Symbol} Family: {coin.Family} Hash: {coin.Hash} Variant: {coin.HashVariant}"); Console.WriteLine("------------------------------------------------------------------------------------------------------------"); Console.WriteLine($"blob Converted: {blobConverted[0]}"); // -------- NEED TO CHANGE ---------->> // determine variant CryptonightVariant variant = CryptonightVariant.VARIANT_0; if (coin.HashVariant != 0) { variant = (CryptonightVariant)coin.HashVariant; } else { switch (coin.Hash) { case CryptonightHashType.Normal: variant = (blobConverted[0] >= 10) ? CryptonightVariant.VARIANT_4 : ((blobConverted[0] >= 8) ? CryptonightVariant.VARIANT_2 : ((blobConverted[0] == 7) ? CryptonightVariant.VARIANT_1 : CryptonightVariant.VARIANT_0)); break; case CryptonightHashType.Lite: variant = CryptonightVariant.VARIANT_1; break; case CryptonightHashType.Heavy: variant = CryptonightVariant.VARIANT_0; break; case CryptonightHashType.RandomX: variant = CryptonightVariant.VARIANT_0; break; default: break; } } // <<------------- Console.WriteLine($""); // hash it Span <byte> headerHash = stackalloc byte[32]; hashFunc(blobConverted, BlockTemplate.SeedHash, headerHash, variant, BlockTemplate.Height); var headerHashString = headerHash.ToHexString(); if (headerHashString != workerHash) { throw new StratumException(StratumError.MinusOne, "bad hash"); } // check difficulty var headerValue = headerHash.ToBigInteger(); var shareDiff = (double)new BigRational(CryptonoteConstants.Diff1b, headerValue); var stratumDifficulty = context.Difficulty; var ratio = shareDiff / stratumDifficulty; var isBlockCandidate = shareDiff >= BlockTemplate.Difficulty; // test if share meets at least workers current difficulty if (!isBlockCandidate && ratio < 0.99) { // check if share matched the previous difficulty from before a vardiff retarget if (context.VarDiff?.LastUpdate != null && context.PreviousDifficulty.HasValue) { ratio = shareDiff / context.PreviousDifficulty.Value; if (ratio < 0.99) { throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})"); } // use previous difficulty stratumDifficulty = context.PreviousDifficulty.Value; } else { throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})"); } } var result = new Share { BlockHeight = BlockTemplate.Height, Difficulty = stratumDifficulty, }; if (isBlockCandidate) { // Compute block hash Span <byte> blockHash = stackalloc byte[32]; ComputeBlockHash(blobConverted, blockHash); // Fill in block-relevant fields result.IsBlockCandidate = true; result.BlockHash = blockHash.ToHexString(); } return(result, blob.ToHexString()); }
public void Cryptonote_DecodeAddress_Should_Throw_On_Null_Or_Empty_Argument() { Assert.Throws <ArgumentException>(() => LibCryptonote.DecodeAddress(null)); Assert.Throws <ArgumentException>(() => LibCryptonote.DecodeAddress("")); }
public void Cryptonote_ConvertBlob_Should_Throw_On_Null_Argument() { Assert.Throws <ArgumentNullException>(() => LibCryptonote.ConvertBlob(null, 0)); }
public void Cryptonote_FastHash_Should_Throw_On_Null_Argument() { Assert.Throws <ArgumentNullException>(() => LibCryptonote.CryptonightHashFast(null)); }
public async Task PayoutAsync(Balance[] balances) { Contract.RequiresNonNull(balances, nameof(balances)); // ensure we have peers var infoResponse = await daemon.ExecuteCmdAnyAsync <GetInfoResponse>(MC.GetInfo); if (infoResponse.Error != null || infoResponse.Response == null || infoResponse.Response.IncomingConnectionsCount + infoResponse.Response.OutgoingConnectionsCount < 3) { #if !DEBUG logger.Warn(() => $"[{LogCategory}] Payout aborted. Not enough peers (4 required)"); return; #endif } // validate addresses balances = balances .Where(x => { ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId); var addressPrefix = LibCryptonote.DecodeAddress(address); var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(address); switch (networkType) { case MoneroNetworkType.Main: if (addressPrefix != MoneroConstants.AddressPrefix[poolConfig.Coin.Type] && addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type]) { logger.Warn(() => $"[{LogCategory}] Excluding payment to invalid address {x.Address}"); return(false); } break; case MoneroNetworkType.Test: if (addressPrefix != MoneroConstants.AddressPrefixTestnet[poolConfig.Coin.Type] && addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type]) { logger.Warn(() => $"[{LogCategory}] Excluding payment to invalid address {x.Address}"); return(false); } break; } return(true); }) .ToArray(); // simple balances first var simpleBalances = balances .Where(x => { ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId); var hasPaymentId = paymentId != null; var isIntegratedAddress = false; var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(address); switch (networkType) { case MoneroNetworkType.Main: if (addressIntegratedPrefix == MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type]) { isIntegratedAddress = true; } break; case MoneroNetworkType.Test: if (addressIntegratedPrefix == MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type]) { isIntegratedAddress = true; } break; } return(!hasPaymentId && !isIntegratedAddress); }) .ToArray(); if (simpleBalances.Length > 0) { await PayoutBatch(simpleBalances); } // balances with paymentIds var minimumPaymentToPaymentId = extraConfig?.MinimumPaymentToPaymentId ?? poolConfig.PaymentProcessing.MinimumPayment; var paymentIdBalances = balances.Except(simpleBalances) .Where(x => x.Amount >= minimumPaymentToPaymentId) .ToArray(); foreach (var balance in paymentIdBalances) { await PayoutToPaymentId(balance); } }