From 5b7f09bd775d1ab092b66645fa60c86ebebac12a Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 10 Jan 2018 15:33:24 +0100 Subject: [PATCH 001/348] Log and record miner submitting block solution --- src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs | 2 +- src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs | 3 ++- src/MiningCore/Blockchain/Monero/MoneroJobManager.cs | 4 ++-- src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs | 2 +- src/MiningCore/Persistence/Model/Block.cs | 1 + src/MiningCore/Persistence/Postgres/Entities/Block.cs | 1 + .../Persistence/Postgres/Repositories/BlockRepository.cs | 4 ++-- src/MiningCore/Persistence/Postgres/Scripts/createdb.sql | 1 + 8 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 0bd3dd80d..80f6918c7 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -346,7 +346,7 @@ public virtual async Task SubmitShareAsync(StratumClient worker, o if (share.IsBlockCandidate) { - logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{share.BlockHash}]"); + logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{share.BlockHash}] submitted by {minerName}"); // persist the coinbase transaction-hash to allow the payment processor // to verify later on that the pool has received the reward for the block diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs index 84b0e43e8..c01dd3049 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs @@ -404,6 +404,7 @@ public async Task SubmitShareAsync(StratumClient worker, Contract.RequiresNonNull(request, nameof(request)); logger.LogInvoke(LogCat, new[] { worker.ConnectionId }); + var context = worker.GetContextAs(); // var miner = request[0]; var jobId = request[1]; @@ -429,7 +430,7 @@ public async Task SubmitShareAsync(StratumClient worker, if (share.IsBlockCandidate) { - logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight}"); + logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} submitted by {context.MinerName}"); } } diff --git a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs index db22ac65d..b2a6266e8 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs @@ -255,9 +255,9 @@ public async Task SubmitShareAsync(StratumClient worker, { Contract.RequiresNonNull(worker, nameof(worker)); Contract.RequiresNonNull(request, nameof(request)); - var context = worker.GetContextAs(); logger.LogInvoke(LogCat, new[] { worker.ConnectionId }); + var context = worker.GetContextAs(); var job = currentJob; if (workerJob.Height != job?.BlockTemplate.Height) @@ -275,7 +275,7 @@ public async Task SubmitShareAsync(StratumClient worker, if (share.IsBlockCandidate) { - logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{share.BlobHash.Substring(0, 6)}]"); + logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{share.BlobHash.Substring(0, 6)}] submitted by {context.MinerName}"); share.TransactionConfirmationData = share.BlobHash; } diff --git a/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs b/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs index c23756734..11cfebc16 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs @@ -163,7 +163,7 @@ public override async Task SubmitShareAsync(StratumClient worker, if (share.IsBlockCandidate) { - logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{share.BlockHash}]"); + logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{share.BlockHash}] submitted by {minerName}"); // persist the coinbase transaction-hash to allow the payment processor // to verify later on that the pool has received the reward for the block diff --git a/src/MiningCore/Persistence/Model/Block.cs b/src/MiningCore/Persistence/Model/Block.cs index 0e7e2cd98..f1bf0082c 100644 --- a/src/MiningCore/Persistence/Model/Block.cs +++ b/src/MiningCore/Persistence/Model/Block.cs @@ -33,6 +33,7 @@ public class Block public double ConfirmationProgress { get; set; } public double? Effort { get; set; } public string TransactionConfirmationData { get; set; } + public string Miner { get; set; } public decimal Reward { get; set; } public DateTime Created { get; set; } } diff --git a/src/MiningCore/Persistence/Postgres/Entities/Block.cs b/src/MiningCore/Persistence/Postgres/Entities/Block.cs index 9bb54dc45..01d23e58d 100644 --- a/src/MiningCore/Persistence/Postgres/Entities/Block.cs +++ b/src/MiningCore/Persistence/Postgres/Entities/Block.cs @@ -33,6 +33,7 @@ public class Block public double ConfirmationProgress { get; set; } public double? Effort { get; set; } public string TransactionConfirmationData { get; set; } + public string Miner { get; set; } public decimal Reward { get; set; } public DateTime Created { get; set; } } diff --git a/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs index 28608aea1..c15bd80dd 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs @@ -48,8 +48,8 @@ public void Insert(IDbConnection con, IDbTransaction tx, Block block) var mapped = mapper.Map(block); var query = - "INSERT INTO blocks(poolid, blockheight, networkdifficulty, status, type, transactionconfirmationdata, reward, effort, confirmationprogress, created) " + - "VALUES(@poolid, @blockheight, @networkdifficulty, @status, @type, @transactionconfirmationdata, @reward, @effort, @confirmationprogress, @created)"; + "INSERT INTO blocks(poolid, blockheight, networkdifficulty, status, type, transactionconfirmationdata, miner, reward, effort, confirmationprogress, created) " + + "VALUES(@poolid, @blockheight, @networkdifficulty, @status, @type, @transactionconfirmationdata, @miner, @reward, @effort, @confirmationprogress, @created)"; con.Execute(query, mapped, tx); } diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index 957000f34..1c940040a 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -31,6 +31,7 @@ CREATE TABLE blocks confirmationprogress FLOAT NOT NULL DEFAULT 0, effort FLOAT NULL, transactionconfirmationdata TEXT NOT NULL, + miner TEXT NULL, reward decimal(28,12) NULL, created TIMESTAMP NOT NULL ); From 629fba934dbb3969fdda1727ffc8d252b72e3a9c Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 10 Jan 2018 16:32:53 +0100 Subject: [PATCH 002/348] Fix top miner query --- .../Postgres/Repositories/StatsRepository.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index e83f3d7b5..ee2980390 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -229,9 +229,22 @@ public MinerWorkerPerformanceStats[] PagePoolMinersByHashrate(IDbConnection con, { logger.LogInvoke(new[] { (object) poolId, from, page, pageSize }); - var query = "SELECT miner, AVG(hashrate) AS hashrate, AVG(sharespersecond) AS sharespersecond " + - "FROM minerstats WHERE poolid = @poolid AND created >= @from GROUP BY miner " + - "ORDER BY hashrate DESC OFFSET @offset FETCH NEXT (@pageSize) ROWS ONLY"; + var query = "WITH tmp AS " + + "( " + + " SELECT " + + " ms.miner, " + + " ms.hashrate, " + + " ms.sharespersecond, " + + " ROW_NUMBER() OVER(PARTITION BY ms.miner ORDER BY ms.hashrate DESC) AS rk " + + " FROM (SELECT miner, SUM(hashrate) AS hashrate, SUM(sharespersecond) AS sharespersecond " + + " FROM minerstats " + + " WHERE poolid = @poolid AND created >= @from GROUP BY miner, created) ms " + + ") " + + "SELECT t.miner, t.hashrate, t.sharespersecond " + + "FROM tmp t " + + "WHERE t.rk = 1 " + + "ORDER by t.hashrate DESC " + + "OFFSET @offset FETCH NEXT (@pageSize) ROWS ONLY"; return con.Query(query, new { poolId, from, offset = page * pageSize, pageSize }) .Select(mapper.Map) From 7218c5bcc4caa95abfc9c08b906a02ab016cc0e3 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 10 Jan 2018 17:07:04 +0100 Subject: [PATCH 003/348] Don't expose disabled pools via API --- src/MiningCore/Api/ApiServer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Api/ApiServer.cs b/src/MiningCore/Api/ApiServer.cs index fc752f2d2..a0efde744 100644 --- a/src/MiningCore/Api/ApiServer.cs +++ b/src/MiningCore/Api/ApiServer.cs @@ -120,7 +120,7 @@ private PoolConfig GetPool(HttpContext context, Match m) if (!string.IsNullOrEmpty(poolId)) { - var pool = clusterConfig.Pools.FirstOrDefault(x => x.Id == poolId); + var pool = clusterConfig.Pools.FirstOrDefault(x => x.Id == poolId && x.Enabled); if (pool != null) return pool; @@ -218,7 +218,7 @@ private async Task GetPoolInfosAsync(HttpContext context, Match m) { var response = new GetPoolsResponse { - Pools = clusterConfig.Pools.Select(config => + Pools = clusterConfig.Pools.Where(x=> x.Enabled).Select(config => { // load stats var stats = cf.Run(con => statsRepo.GetLastPoolStats(con, config.Id)); From 5e8906a32048d32cbdcfa1b3b90f377aa3ce47e4 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 11 Jan 2018 10:31:53 +0100 Subject: [PATCH 004/348] External socket binding --- src/MiningCore/Mining/PoolBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index a94667b21..ae35b0130 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -158,7 +158,7 @@ private void StartExternalStratumPublisherListener() using (var subSocket = new SubscriberSocket()) { //subSocket.Options.ReceiveHighWatermark = 1000; - subSocket.Connect(poolConfig.ExternalStratumZmqSocket); + subSocket.Bind(poolConfig.ExternalStratumZmqSocket); subSocket.Subscribe(poolConfig.ExternalStratumZmqTopic); logger.Info($"Monitoring external stratum {poolConfig.ExternalStratumZmqSocket}/{poolConfig.ExternalStratumZmqTopic}"); From a505c83358247da9a502e6a3d3c53fec1c7395c7 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 11 Jan 2018 21:34:07 +0100 Subject: [PATCH 005/348] API Fix --- .../Postgres/Repositories/StatsRepository.cs | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index ee2980390..3bbcd66fd 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -121,39 +121,40 @@ public MinerStats GetMinerStats(IDbConnection con, IDbTransaction tx, string poo var lastUpdate = con.QuerySingleOrDefault(query, new { poolId, miner }, tx); - if (!lastUpdate.HasValue) - return null; + if (lastUpdate.HasValue) + { - // load rows rows by timestamp - query = "SELECT * FROM minerstats WHERE poolid = @poolId AND miner = @miner AND created = @created"; + // load rows rows by timestamp + query = "SELECT * FROM minerstats WHERE poolid = @poolId AND miner = @miner AND created = @created"; - var stats = con.Query(query, new { poolId, miner, created = lastUpdate }) - .Select(mapper.Map) - .ToArray(); + var stats = con.Query(query, new { poolId, miner, created = lastUpdate }) + .Select(mapper.Map) + .ToArray(); - if (stats.Any()) - { - // replace null worker with empty string - foreach(var stat in stats) + if (stats.Any()) { - if (stat.Worker == null) + // replace null worker with empty string + foreach(var stat in stats) { - stat.Worker = string.Empty; - break; + if (stat.Worker == null) + { + stat.Worker = string.Empty; + break; + } } - } - // transform to dictionary - result.Performance = new WorkerPerformanceStatsContainer - { - Workers = stats.ToDictionary(x => x.Worker, x => new WorkerPerformanceStats + // transform to dictionary + result.Performance = new WorkerPerformanceStatsContainer { - Hashrate = x.Hashrate, - SharesPerSecond = x.SharesPerSecond - }), - - Created = stats.First().Created - }; + Workers = stats.ToDictionary(x => x.Worker, x => new WorkerPerformanceStats + { + Hashrate = x.Hashrate, + SharesPerSecond = x.SharesPerSecond + }), + + Created = stats.First().Created + }; + } } } @@ -177,7 +178,7 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenHourly(IDbCon foreach (var entity in entities) entity.Worker = entity.Worker ?? string.Empty; - // group + // group var entitiesByDate = entities .GroupBy(x=> x.Created); From c8000ed642a3f0397d0ee8869b091c8f8d0c57ee Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 11 Jan 2018 22:11:04 +0100 Subject: [PATCH 006/348] WIP --- .../Persistence/Postgres/Repositories/StatsRepository.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index 3bbcd66fd..0b0cb87d2 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -62,6 +62,9 @@ public void InsertMinerWorkerPerformanceStats(IDbConnection con, IDbTransaction var mapped = mapper.Map(stats); + if (string.IsNullOrEmpty(mapped.Worker)) + mapped.Worker = string.Empty; + var query = "INSERT INTO minerstats(poolid, miner, worker, hashrate, sharespersecond, created) " + "VALUES(@poolid, @miner, @worker, @hashrate, @sharespersecond, @created)"; @@ -123,7 +126,6 @@ public MinerStats GetMinerStats(IDbConnection con, IDbTransaction tx, string poo if (lastUpdate.HasValue) { - // load rows rows by timestamp query = "SELECT * FROM minerstats WHERE poolid = @poolId AND miner = @miner AND created = @created"; @@ -146,7 +148,7 @@ public MinerStats GetMinerStats(IDbConnection con, IDbTransaction tx, string poo // transform to dictionary result.Performance = new WorkerPerformanceStatsContainer { - Workers = stats.ToDictionary(x => x.Worker, x => new WorkerPerformanceStats + Workers = stats.ToDictionary(x => x.Worker ?? string.Empty, x => new WorkerPerformanceStats { Hashrate = x.Hashrate, SharesPerSecond = x.SharesPerSecond From 26de06f55742d7a7db42ffbc4c9e954bde8cf116 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 11 Jan 2018 22:40:40 +0100 Subject: [PATCH 007/348] WIP --- src/MiningCore/Persistence/Postgres/Scripts/createdb.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index 1c940040a..9655093dd 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -85,7 +85,7 @@ CREATE TABLE minerstats id BIGSERIAL NOT NULL PRIMARY KEY, poolid TEXT NOT NULL, miner TEXT NOT NULL, - worker TEXT NULL, + worker TEXT NOT NULL, hashrate DOUBLE PRECISION NOT NULL DEFAULT 0, sharespersecond DOUBLE PRECISION NOT NULL DEFAULT 0, created TIMESTAMP NOT NULL From b3c63d2530780f455bffb45851e4b4e25eb890d3 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 12 Jan 2018 12:38:27 +0100 Subject: [PATCH 008/348] Revert connect change --- src/MiningCore/Mining/PoolBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index ae35b0130..a94667b21 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -158,7 +158,7 @@ private void StartExternalStratumPublisherListener() using (var subSocket = new SubscriberSocket()) { //subSocket.Options.ReceiveHighWatermark = 1000; - subSocket.Bind(poolConfig.ExternalStratumZmqSocket); + subSocket.Connect(poolConfig.ExternalStratumZmqSocket); subSocket.Subscribe(poolConfig.ExternalStratumZmqTopic); logger.Info($"Monitoring external stratum {poolConfig.ExternalStratumZmqSocket}/{poolConfig.ExternalStratumZmqTopic}"); From f3f40b72dcbf009ac66ab6da54b35fc2488610e4 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 12 Jan 2018 14:14:31 +0100 Subject: [PATCH 009/348] Monero address validation fixes --- .../Blockchain/Monero/MoneroConstants.cs | 28 ++++++++++ .../Blockchain/Monero/MoneroJobManager.cs | 43 +++++++++++--- .../Blockchain/Monero/MoneroPayoutHandler.cs | 56 ++++++++++++++++++- 3 files changed, 116 insertions(+), 11 deletions(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroConstants.cs b/src/MiningCore/Blockchain/Monero/MoneroConstants.cs index 113cd5b1e..f5d2dfb0e 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroConstants.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroConstants.cs @@ -45,6 +45,34 @@ public class MoneroConstants { CoinType.AEON, 97 }, }; + public static readonly Dictionary AddressPrefix = new Dictionary + { + { CoinType.XMR, 18 }, + { CoinType.ETN, 18018 }, + { CoinType.AEON, 178 }, + }; + + public static readonly Dictionary AddressPrefixTestnet = new Dictionary + { + { CoinType.XMR, 53 }, + { CoinType.ETN, 53 }, + { CoinType.AEON, 178 }, + }; + + public static readonly Dictionary AddressPrefixIntegrated = new Dictionary + { + { CoinType.XMR, 19 }, + { CoinType.ETN, 18019 }, + { CoinType.AEON, 178 }, + }; + + public static readonly Dictionary AddressPrefixIntegratedTestnet = new Dictionary + { + { CoinType.XMR, 54 }, + { CoinType.ETN, 54 }, + { CoinType.AEON, 178 }, + }; + public static readonly Dictionary SmallestUnit = new Dictionary { { CoinType.XMR, Piconero }, diff --git a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs index b2a6266e8..fe1b7d96a 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs @@ -26,6 +26,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Security.Cryptography; using System.Threading.Tasks; using Autofac; +using Microsoft.Extensions.DependencyInjection; using MiningCore.Blockchain.Bitcoin; using MiningCore.Blockchain.Monero.DaemonRequests; using MiningCore.Blockchain.Monero.DaemonResponses; @@ -198,10 +199,6 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi 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)) @@ -222,12 +219,22 @@ public bool ValidateAddress(string address) { Contract.Requires(!string.IsNullOrEmpty(address), $"{nameof(address)} must not be empty"); - if (address.Length != MoneroConstants.AddressLength[poolConfig.Coin.Type]) - return false; - var addressPrefix = LibCryptonote.DecodeAddress(address); - if (addressPrefix != poolAddressBase58Prefix) - return false; + + switch (networkType) + { + case MoneroNetworkType.Main: + if (addressPrefix != MoneroConstants.AddressPrefix[poolConfig.Coin.Type] && + addressPrefix != MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type]) + return false; + break; + + case MoneroNetworkType.Test: + if (addressPrefix != MoneroConstants.AddressPrefixTestnet[poolConfig.Coin.Type] && + addressPrefix != MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type]) + return false; + break; + } return true; } @@ -403,6 +410,24 @@ protected override async Task PostStartInitAsync() // 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; + } + ConfigureRewards(); // update stats diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index 2b71219bf..491ca2b21 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -31,6 +31,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Configuration; using MiningCore.DaemonInterface; using MiningCore.Extensions; +using MiningCore.Native; using MiningCore.Notifications; using MiningCore.Payments; using MiningCore.Persistence; @@ -413,9 +414,60 @@ public async Task PayoutAsync(Balance[] balances) #endif } + // validate addresses + balances = balances + .Where(x => + { + var addressPrefix = LibCryptonote.DecodeAddress(x.Address); + + switch (networkType) + { + case MoneroNetworkType.Main: + if (addressPrefix != MoneroConstants.AddressPrefix[poolConfig.Coin.Type] && + addressPrefix != 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] && + addressPrefix != 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 => !x.Address.Contains(PayoutConstants.PayoutInfoSeperator)) + .Where(x => + { + var hasPaymentId = x.Address.Contains(PayoutConstants.PayoutInfoSeperator); + var isIntegratedAddress = false; + var addressPrefix = LibCryptonote.DecodeAddress(x.Address); + + switch (networkType) + { + case MoneroNetworkType.Main: + if (addressPrefix == MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type]) + isIntegratedAddress = true; + break; + + case MoneroNetworkType.Test: + if (addressPrefix == MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type]) + isIntegratedAddress = true; + break; + } + + return !hasPaymentId && !isIntegratedAddress; + }) .ToArray(); if (simpleBalances.Length > 0) @@ -425,7 +477,7 @@ public async Task PayoutAsync(Balance[] balances) var minimumPaymentToPaymentId = extraConfig?.MinimumPaymentToPaymentId ?? poolConfig.PaymentProcessing.MinimumPayment; var paymentIdBalances = balances.Except(simpleBalances) - .Where(x => x.Address.Contains(PayoutConstants.PayoutInfoSeperator) && x.Amount >= minimumPaymentToPaymentId) + .Where(x => x.Amount >= minimumPaymentToPaymentId) .ToArray(); foreach(var balance in paymentIdBalances) From 01856f1a7e538d242472894f5c7dd323b59ac13e Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 12 Jan 2018 17:02:51 +0100 Subject: [PATCH 010/348] More Monero integrated address fixes --- .../Blockchain/Monero/MoneroConstants.cs | 11 +++---- .../Blockchain/Monero/MoneroJobManager.cs | 9 +++--- .../Blockchain/Monero/MoneroPayoutHandler.cs | 11 +++---- src/MiningCore/Native/LibCryptonote.cs | 19 ++++++++++-- .../runtimes/win-x64/native/libcryptonote.dll | Bin 411136 -> 412672 bytes .../runtimes/win-x86/native/libcryptonote.dll | Bin 366080 -> 366592 bytes .../cryptonote_basic/cryptonote_basic.h | 16 ++++++++++ src/Native/libcryptonote/exports.cpp | 28 ++++++++++++++---- 8 files changed, 73 insertions(+), 21 deletions(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroConstants.cs b/src/MiningCore/Blockchain/Monero/MoneroConstants.cs index f5d2dfb0e..e16ac7ba9 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroConstants.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroConstants.cs @@ -18,6 +18,7 @@ portions of the Software. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +using System; using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; @@ -38,35 +39,35 @@ public class MoneroConstants { public const string WalletDaemonCategory = "wallet"; - public static readonly Dictionary AddressLength = new Dictionary + public static readonly Dictionary AddressLength = new Dictionary { { CoinType.XMR, 95 }, { CoinType.ETN, 98 }, { CoinType.AEON, 97 }, }; - public static readonly Dictionary AddressPrefix = new Dictionary + public static readonly Dictionary AddressPrefix = new Dictionary { { CoinType.XMR, 18 }, { CoinType.ETN, 18018 }, { CoinType.AEON, 178 }, }; - public static readonly Dictionary AddressPrefixTestnet = new Dictionary + public static readonly Dictionary AddressPrefixTestnet = new Dictionary { { CoinType.XMR, 53 }, { CoinType.ETN, 53 }, { CoinType.AEON, 178 }, }; - public static readonly Dictionary AddressPrefixIntegrated = new Dictionary + public static readonly Dictionary AddressPrefixIntegrated = new Dictionary { { CoinType.XMR, 19 }, { CoinType.ETN, 18019 }, { CoinType.AEON, 178 }, }; - public static readonly Dictionary AddressPrefixIntegratedTestnet = new Dictionary + public static readonly Dictionary AddressPrefixIntegratedTestnet = new Dictionary { { CoinType.XMR, 54 }, { CoinType.ETN, 54 }, diff --git a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs index fe1b7d96a..a3de79acd 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs @@ -76,7 +76,7 @@ public MoneroJobManager( private readonly NotificationService notificationService; private readonly IMasterClock clock; private MoneroNetworkType networkType; - private uint poolAddressBase58Prefix; + private UInt64 poolAddressBase58Prefix; private DaemonEndpointConfig[] walletDaemonEndpoints; protected async Task UpdateJob() @@ -220,18 +220,19 @@ public bool ValidateAddress(string address) Contract.Requires(!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] && - addressPrefix != MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type]) + 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] && - addressPrefix != MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type]) + addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type]) return false; break; } diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index 491ca2b21..e28383e99 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -419,12 +419,13 @@ public async Task PayoutAsync(Balance[] balances) .Where(x => { var addressPrefix = LibCryptonote.DecodeAddress(x.Address); + var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(x.Address); switch (networkType) { case MoneroNetworkType.Main: if (addressPrefix != MoneroConstants.AddressPrefix[poolConfig.Coin.Type] && - addressPrefix != MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type]) + addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type]) { logger.Warn(() => $"[{LogCategory}] Excluding payment to invalid address {x.Address}"); return false; @@ -433,7 +434,7 @@ public async Task PayoutAsync(Balance[] balances) case MoneroNetworkType.Test: if (addressPrefix != MoneroConstants.AddressPrefixTestnet[poolConfig.Coin.Type] && - addressPrefix != MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type]) + addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type]) { logger.Warn(() => $"[{LogCategory}] Excluding payment to invalid address {x.Address}"); return false; @@ -451,17 +452,17 @@ public async Task PayoutAsync(Balance[] balances) { var hasPaymentId = x.Address.Contains(PayoutConstants.PayoutInfoSeperator); var isIntegratedAddress = false; - var addressPrefix = LibCryptonote.DecodeAddress(x.Address); + var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(x.Address); switch (networkType) { case MoneroNetworkType.Main: - if (addressPrefix == MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type]) + if (addressIntegratedPrefix == MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type]) isIntegratedAddress = true; break; case MoneroNetworkType.Test: - if (addressPrefix == MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type]) + if (addressIntegratedPrefix == MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type]) isIntegratedAddress = true; break; } diff --git a/src/MiningCore/Native/LibCryptonote.cs b/src/MiningCore/Native/LibCryptonote.cs index c24f2b19a..165fcd11e 100644 --- a/src/MiningCore/Native/LibCryptonote.cs +++ b/src/MiningCore/Native/LibCryptonote.cs @@ -33,7 +33,10 @@ public static unsafe class LibCryptonote private static extern bool convert_blob(byte* input, int inputSize, byte* output, ref int outputSize); [DllImport("libcryptonote", EntryPoint = "decode_address_export", CallingConvention = CallingConvention.Cdecl)] - private static extern uint decode_address(byte* input, int inputSize); + private static extern UInt64 decode_address(byte* input, int inputSize); + + [DllImport("libcryptonote", EntryPoint = "decode_integrated_address_export", CallingConvention = CallingConvention.Cdecl)] + private static extern UInt64 decode_integrated_address(byte* input, int inputSize); [DllImport("libcryptonote", EntryPoint = "cn_slow_hash_export", CallingConvention = CallingConvention.Cdecl)] private static extern int cn_slow_hash(byte* input, byte* output, uint inputLength); @@ -97,7 +100,7 @@ public static byte[] ConvertBlob(byte[] data, int size) } } - public static uint DecodeAddress(string address) + public static UInt64 DecodeAddress(string address) { Contract.Requires(!string.IsNullOrEmpty(address), $"{nameof(address)} must not be empty"); @@ -109,6 +112,18 @@ public static uint DecodeAddress(string address) } } + public static UInt64 DecodeIntegratedAddress(string address) + { + Contract.Requires(!string.IsNullOrEmpty(address), $"{nameof(address)} must not be empty"); + + var data = Encoding.UTF8.GetBytes(address); + + fixed (byte* input = data) + { + return decode_integrated_address(input, data.Length); + } + } + public static PooledArraySegment CryptonightHashSlow(byte[] data) { Contract.RequiresNonNull(data, nameof(data)); diff --git a/src/MiningCore/runtimes/win-x64/native/libcryptonote.dll b/src/MiningCore/runtimes/win-x64/native/libcryptonote.dll index 97c16654426f1e883e6b5723c0cd5237f8b83d9d..15a282c4992bd2ed2ed2c5d69885516ca1c028ce 100644 GIT binary patch delta 82074 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 delta 81041 zcmZ^M2UwKH^Z)I_!6BRn$|0Z!ND&mof*ny1(8GWlO9Xpji8UGpyP|^SJkOF>U6WX% zi6$n|s2I;KhzN?k*Vy$%ET~aIqxb*pzJTVN-=F8%_uZYDo!y zT#5g&jYE1eyYWP^Kx&(mciCWc~BW5_LZW6qs z9aP<$aX|UsVh2@=sw$PAltXGb%^R&&g#fWwxD6X`GwCe5;* z;7Qg<-`WRAE$xGRKJ#->&9iqi+OmxqrLD%^Hd?g5S?pBCj6=qvPA{aX_O*yTh})#+ z`8udf_Dd8(&G;e^wyPjK|D33fRs0ow;zd8CjvXtC{Gap)pMT-O$O(*!7MC+Aa{eFs zx1ONaRN-`F%7hfDpG$ckCC@*TUaJ47#MXKyZFZ>B zpn~{O2~ib|sPV4~74`m~w7HgK=NM9DVR?DWN~ETabt;pCOQgXcC7YH=%N#oo+;WBmSH6)17-BEZGYbA9zbYYhq}rpY%}E-Mw*bs(O`6QnkB1 zp%g~*NqNN+sjJg)8t5nOck-k|?n{@PI@a3n3l=K0{QCjpk31 zjkAxWI>>{eyNe@iM$?pJ0F4T;3kL#Z1gM1qiYf=S04PKO)hh>u0OYBFYLtUK0n#WS z`*M&5bugOyR0)iR*c$dP+Im9T>fD@`8l;ELP3X~L$o2oTwdZAQ9=S$niNiB3Ib=Q&q zn0)pDQ4P_JR?n3@Tt{pA3s{c_lEt;R=ASrD@ch11;@Zi*F$RXxg?#=HU3g#W>Nb-4 zpOUiO;vDbZ;}ytd*-fZk@~h@c`byE&zS1;{y5|I#k@{ccN@#= z!iH;8Uec=Gq|)mNu6uPJ9kk~ zg>vjQX-&;2e+^G3n2*YDuR<94@rzL_Rnck8nh68X^K5IR_cc4xK~<%WwfbnPZ?#QOIGFUK30+hlS>>fzsuzH^ke`cI~{b*RW-JO5LJZS^I3cFL33 zdpevFe2wNDrB%zX^NfqHONRrZYF(~IyLKDe8(wICYsgf5#!Uzc{@JgX!mmjy5ci*>OrZvPN(`mqqT|Vsewa{7W*}r z=X-Msi@nuoPK69jQL%#6)W8A2b&!Sx*Gc!}5KMb9{rdLKO+5i(Os{JCF;rgZsDjK^ zLu-%8E5y@3{A1mS?$)DcOiv*{v-;vcyoIGkbJTgnlop;9v8TpR*%(@2j6AKI6A8b^ zxYK5noI?iD_(JLH5cl>C3c1uu63m@7iRRqI&^+CKH>1TRT;A-&o2%CeE+-%dEn-{_ zFNu)DtG9|AIzkR_q{5JPA!ko;Iiv>qp*<03&l*l)v2SBEHC@1?ia6AZrKWWQ;{N7} zX0eY1T?$8H+{EHSen(KMWlV5F z#{HWKruMuGKRF_esOLv(tE7^8HK;}@|DzYbbC9_-$()~Pn(mrlK4&!b!!S81o8J>DFsv<|RwxY) zt6TN(IZ;LN>{wS?8HOqz{tvfL4of5I`+3xUhv{Jd(!d#9VJ$8VlFWzXP4?2-`Z0d* z(XQe^qxpiI{m*}i673}#-n6O#W9gV&Lp;|8OYz}v=<%&mOoN(q=vJv;10QPKDot-- zBy+Oz8?;u_jW4s*O|3)|0taHT?2{uMYBrhvnw=Hhd^VvEo=N#F_S4{JS#w)z3GKd5 zTHeZ^cHbcFZq=4P-7CFq)tv6wD>aGSrmwTU(xQl(Lkds#NN*$i>bL(?i5bJ&1kAKO z(x}$G^>H6C6U#B$J<|2oUweGF8+oE%9p(mVs6Z9Ub9PIEqK491yQLFR&GecdE4dCR z&wF~8Gx7%%nAK>TmOUc{qDyG?d)jT8mUoy zSGs;p*6jGN32nSuDsA72R@s=Gb5R zY!>fIf1V{d!p4o7CB^)5g07mG<+kw-rN+3dSHDfA^h!on>gHyI(kasKfB4hsv$9V8 zF^o{Vwo>HQ4&L7DcvH~6iEy~!DjJ1n<|5N|(#owN^wL`C@YYxw(MEFKR*UM}NOiZh zp*?<*hHb0uzAXw_GY&=f&P_B^qq(a_cAc2DdfRS7??y_!why4-v3I*Sy}wqvw0(Jx zPHTBdrN-IWY1J$J(Os<+9{n+*`(QGJ7wTs;r3NO;!JM2Y<)95vZ)^9!kX?LPh8~ga z1gjFcc%78{XWif}tBA^C9)A^&LUfzExJM z9c}B9k2%PWR^7(SWahlw|5tcO+3Im>ki>)I{hkiqBX1 z;brOQD{Dt)ZGAXGO=k?0d`b@3^|z^{nBif`l)aFC{$~}rB*niQNqgUsvfj0%nPJkCcY~b_VO;v^;eWG5 zEs38r^}UYvEt4$o+tQyxr4#Q5J4J;Gg845#5KJqTM%g^+#m}xgyJJrV(6WatUQLQLJ34dLzdvKm97sL-D3pESK#pp5cH+>@ zC9H`fX;078V^bZ;6lw}#WsYQ*rg0L-hCF4vHKYq|{D8SQk*#!52s_|J*6U_`!m;BY ztAtNq&pNIO*+FQA4-0i6G4y5-`^tr^_dadpRQV54Nprr@Y#9I#l~>p84y&&vH|P(w ztW~R$tAyryvtk{&5?S*BCp~X8pOE`F^QccE&sYd+As2daiQ4-bP3<+svD|(D9{GNJ zNRs)myxD8H8*z7E0h)vg8dv0%=XkLYHxf#h#IYW3WGcPt$xEump1P6Nq&5qyMtYIu z*16S)52}CK`g3*SZ%2J^u|f~hhTgczoNJKpX{(#etrqd1b=t7(8e|&%@dj(_NgC1A zC^ps;vis)>i>XOG*i8dbyIQfoaWXf$qhxE5Cy60+$92KSc^w>yavzm#CDe-z z@g}{g?{${zjjFzC#Y((MXPS12bFHE8gYB0}j!*A#jz#y_sG20vF&KIv*J;JFYl8mt zRi&2uTC%#e$m;ON7m&}A4Bza|P0Y{2Wj!fhMHQ-~C7q>a9$NYIrk;WV!5>34?vjQJT<5@HI(uZ`UY3ItNaqc7bPjB(O zMYq@}U#Oi!ImzR*ACb%uB$IEkYrbgAva<@8sUMT*1c}ovYmy&Hbujpz=Gn!bKs49Z z5l4{DIMf9Wyru#Bs}5O5k5yr_g2`6R*7{t*e?G|~Lr5)pFQ27^kfrYZouH{^j|4Nk zK1IHp2qSe#Q|jTwTGS=K(Rat$y}Bffo(;8DuSa?iyNT~rY(yxDvTN~PW&JIbRHJl+ zl^v*04iYn)6;8&|(%S4*IO#xxb6ML4#NV~sAr+j(5~C?DumvWDmDaBtkN{GB5LL+y z^ZD#x(U!izqM&~c`?(<*Mf;R7k4ES#-!j&%5vffJ_OtPgP?cg|7SNb{O=PyLF)`A) z2iT3q#NRC|`(Nz;@v&BmApWE}hOoRiSK;5i1pE)|V;!21QBHebLQ0k>r~S;(h%}+; zFO_0K_Ex5VF{bplJp~W*n^hjB;Ea#?QBK*(N`YKGm^}w?f0^uA~8O*A2a{f zm~h7LVX3W=E_uYBw$_?W($?$3Xbudt@IA-~v@bmkLF4%2_K*ntzIrpq0-$`LA*vbxnT4nlIN z`Mtt6M9wL;u8JpnTxiH&)?Qs9SX$!(W8ER?YwMUp4>F7PQL*_w$S-vAIo9Mel0^Hj zWf`B5QFec$uU~vd{-9OPvY&ba@pKJ)(G!TwcPch8nS2p>pp0)!5ws>v#A4tl$3z}2T9V~#LJINsDK1s^xh(3nA{GmF zv0x`G%4TLwAwxAeCwcd&$FtyGq^on*Yo5aafEbq9i^OR>74&g-suyWY@2_O`y-5pF z!lHVUNPX!E>;}Ke=i6@4u;zRxq3=p&=}j8xo@7>Z!%~d-@@~7^n^be(&pzshjNC0K z%Z^p)L+aN4^c64M(P-)H$$=t8jOGp%0&rv9`;gDc1h%6O=84J2S!o~AOaH}k-hhv0 zJB{3VIs39NX{_1vBcCPy{E@BgOD5^RTlOEqxyx9Cex$L>i*=mvF@IJ}Mg7P?`g#Q` z?uY5V;1%=fPu5ez3YOO&=_fB)X@3$yO%kg=fP6*0B|42o2GY zwomO;`LQi*_~Q<gd;UwFb(A2<81b>=`~a-ic6Gne7y z5S^OO&J0KXsC?!&0>mmbgtEJ40%rKt(*CumM-F;|DFBE?qsdU5fw!#( z$B|KlZk@#%r9qQs-n0%*BUlmNp2+%4Alqp8MCLjX)6%o8)_oJnCPK$gVHxZR;IG{86@Z=~~eLiN-9))bld~#nOg=JwP zwrLWuO_QK()6^Tv7MMvcT{41wx`42%BT?mJ@(}SXDX_j;K$a4E@JF^_Ax1#oAFa<8 zlHv9m(?L#^K8OvJ$PnWaMNnfWphGB{EjWhJyTN0o%Tc{7GXMuo0_CGA%M&^HvkRll$2DAmyeQzP)0K zvuCj5kdsQQ=aO~ zs_h{&=x43j(mkZlfRe~^HHd}^7!kUN$^>C65)rY1*t>`!B_I73BCp)5ziiCN86*Ey zO^_waX>KACExYu_BvYwzPuy6Sy~L36CC=ZB<}yC5%*^3+k&lZ47P<2H5-6t8vWQn+ zP7@p#!SPhdPJ-#8Y#v|Li}PLd3y;qf@XkZ!0xdhdm&~R8N3qWPVA9v5vOo3_J#n?> z?IUS~RvTw+eSi!lv~_c~Asd7K+%R?}n@pmXW~@gJNuiCKu`M~oFKA>qYB2NAJnU4( z^pEKm(>JD1@4mV6JBe%AYw>(0v6nfd0iC#%UCSl@bkNY{hlo4tcaU_9YG=%trc$Lw zqJJ>oCY8u%A@A)r`6?YVq#&ADzYZ%YGGVll9%QFwn zzzemH{f5^*ia+_E(R?t*d@9kLT|~oeHqnzaAE|f_vpTyWTbM_@X#7LAF^~AtCSmMo z9`U1N1K6WHGS7L%@b@-;`Xu*HX6c7X0Da@a8WmtMlYN+sbiO&P99tuqHP1)(4Fc=S zCghWa&W;t>%{`fG0kEqCR&q8DEzkC}Cp%dHrr(t_bv;5R(Dgl;=?Dq6+xdlxJ<^ey zwAGjF+!2yN<9o7{qa@7k!RO_OVMEyZqhtZSI+#Ti!s$5BoVVMn9$IGk_qO>-*{&WN z2O2t|3a`Hx+fs<-OHwyRj$s;!9l*!1fec~^$B2O*?#G56BZJ9V_UTDdgC3aA9vtJ1 zOJZyK5g+#E6bU1p6kMzMY~*p`O_wLJdB@2hx;<1`5dC0c4kt*nDyKR^d*Phy9M9UH zK;!Pz6V4tzI*(mDK~ia#dJ28(d2IMe;1ASQ(!b4RZ%>kq^dBsK;T+OCb6D;v%!-pk z6#S4mEc!HQLC*!U$)`zxVe}Wg(dsd?_@ogR7@Z#%=!YK03>@{-akl$3sa1W$*Y7d0 zU|}!ZutZB!%PdxOn$)YZYYazN)LV}+!x<77^8IL$?hZoeYl!UJz-Eit>Bm*me8GrK za~O=1vO1m(J41ZD?#1yII&#;}oZ8ZL_N%nU9DO2|{cr|CJ08Pw&cHc$9L!#yAwj+~ z3bBW5ia&+Xa!URYbHQTW4zAAPC^XlHMV=)=Y)mp#vfDfM+j+P?Vg9V^1!5sd?B)eH zUwvvb|BEorb84|$7lve@RZ2X1cz0JH})_^-$YuO#O-AXXSgsu!+ zEF>ciXdC8>$-5^;`zN0AH?7#-E5s-L%mNHM`IvYf7SC+)+%2Bl#dEWG{vw|1#B-&1 zE*HK+Km#8`*OhA~1kbz)A zScI?);d_L&2tOliLdZhcg>V2N51|m@Gy*F)kJbOULu|x#(zr%vZ+lgmkI`b-(AZve zKqK^_P)=jpuao%LZh+yOpg?XWwwEeSzxn2>hMxubWfa&-bUErmFMCx+DVfCi_M~w18Rug8M25uvRxnO;X4{yFq+3gKBXHz z8B6ti?dnShonYH;5PwfU9Qt5AAl6S+ceg07?vtAA(G3z1zN`kgizPnxOsnEJX(TV# zsDDyzcxzEiO_McH1wcvt3jhlE!h9FA><#eCV65tJOxlJ3BA#2;Z_ z-yuHq`cbyz4vd!S2-|T7wfN>JyLJcC-BrNxHJtV)fV)NAQjod3_@P3uz^Kura)PM} zLEUmbO$ykoyCks3UpPGCt29`595eF!5l{}J(R^CjgShw}wXx`v-(ZFpn-b%@aQpr* znsi=ci+*J#O;=X)9;{RI`mFgqG$Eo7>wS-`?3`CXR4p}VcSg1==$G>o)yRWsk5FxY z9GRhKqcype=h*VE3skDz6!J9L?c#oeJj%@F8H?*7*{Or&i>&>9GRSW%fEfc_1wu|h z7Mxr_VU&9%PI3l2dLPEdMIgqw%4ga8`=m+D!&o@MJ#s6)f#tU5m3=ZvJ#USz`ja+ME33y zgxXOD)wSpw3UDEm_(F(1GBi}MMO6jX`)oST#nIUUT}?q3DrlF=%)bik-}?pYO$zos zZucm8mkR6!1-nSWsw(qN7ucN&_G<gk5*!L3hb{6HhrLiK2(WbC(sKNbi9JyUx{5HuwNPDKkVpE5f;$SdyMjKhpg$DcL||X-6)pZl!S1YN?IW<4 z6zmcO`)eguBe1&_>^KGcyMj%J>OHT+MZHo%_fXKAD>Gjc*y#$kxq{tNi9IZ^0~D;6 zg56e$-6F8<6zp4P(cq_ zi<1TRsDjNB+tDV69&avqc>cI+n14oU_vgsEpnL8+0_^9QB%|m1!qhRYQ z*madymB4mYu+9p0WhM4u5SMfl1^c)PN=nD9T8Ta<&>jlbe*M?QzBAnK2@&Srs<)35ln# zcdRfBkF4Wk0=RWWAS7-1Ob#3G-+-O0jA z$d~qyO)3>@N$qN~Z6&a!fjilm5|U&8cpku8d)i@L_KfswJ{XsT%$G3Bt1+{~J+O2x znc4FCb(Jd3N#3zU`mz;&kU}O?LMM8BG)HS&zR-lk_YQ3QHkdI&eF#9jl_e zZ^cEWOOQmG7l4mpC2e}&N}w+DGWVh^`tC=CKH2on90yaZq;XrvrZ3GBpkWoDQk(uB z2Vr*;ZRyLKnjFYwja!jHy-^RKPRPMkMK$7(wqf<9A7^LJ!d{YEPGNw+sj2VBK7C0H zPT`!DN&Om&@0X;DcbYHnDC;)PU%LVce*ekArXReG-Fiu?dm06PGVmDzyqPUTdh9ml z@``vGJw>V`ux521ip&+*0bTkwj6q%VfLR4gam3(V>Qg0#sVj)-;?2!(G6v%D4;Ix07v zv>6;T`$$?tquKB?GQhS*7^VZ5ajUKIY$12uns6u2qWzRp3Lna^x6yXfrjHgkL$opF zyz2uR^q4dM;DH-6qWpnYnak2zpnwh)1$g+d0k4Ti^kIV#!2`T(XJ=>YmSq=5pr}81 zT3@t9$oLEr@G{I7ZTg=%#G+jc2=^Sax3C?rN$rRPXfApeH!TX~TYSBa#Dj8II^(S^ z%|SL}H}9A{m_PX?F}WwJQVO?ifPsaVk{UrZ;lLn|qx=~tioN8y8k-IKLyNZyus!ya z9pw&ecqs`EKM=?Fq0%_4n!JC%NI1&-04;vBg@}Ag28 z9uluWD3lK+^1kU_OY}`hk>g%unY@)F($sPfL@L=u9FgmTAx{^Dpe)6!((O=pK<3#; zFafGmy13kV>gGtx)zD$`?AXeRy30T9!=-;z+X;-0uq|bnhj!Lvr^|@G)nqgoHK{-Q zYFb^?W=%~n_-2oQIX2ENngPxL|G>dh6!4eD4=`M&zh{1LNW&&^z+m6uCW5SU74MSg zIyY@=z@O_}lS*`M>J}pIrj5q?uv%S{WxgSy)%w1BXG@!89H?H52!?dhJC^f?WJcCR z^&`%pPR^NE(;SU+_06yfRJv1X_l&6CfT-qQPPNZGlU8+pp{#-E@G4_7-V%L;<2$}L zdf9x`7@>c`7k^xpF^j&jrB&fNV%~+`!ushf8g>?V*8e{7VLJiS7nR(VF3IMjM0=Bh;7D4Ep}nWr;o)~K7|H}Iyi_=BeK*5OzV0yRd|NNEB9_RgO(ayn39<@`9&ARdmh1?JL!3w?; zsw)>+Inx7rdssG%(Z)8YPM6wPM?z=Pq8IEqp~<8Jt3&B~nplsm?LmFiP}NmGvjjUjikfP$op!V~ zRZn48@vbwVF>iSi3@g9dXf&^q(Hf&=*(Jyf+Aq4neLeHHr)}xgpV&Zq+K?P!OYOnp zkDr*;p89IX|HP%17#MBS5Bv%DmuM5Z?`N@!PtOkF%+yIGpr}}sW6$FlKVyx-e5j5U zm>3PBAtDM@&B;~P<6?dO-P`wkx`@48v0yK1pv>z=W&OTQS*%aFQh`4r@Yoi@RtsCC zrn;(6p4*8&ipb7a|Mi^xuBOB3wCBvzfwqdc;108yQ9$BM>Ow?|9piAf8IjqK)4XOr zQ0Yo^Gi@rj0jjaipt1+mdUrP4fj00=1(9y|efM!+<*N&xDFx)K4?bhZ9B9MvrGR7< zX!6w=h@cx)CiP6jVqWF?rB=@<@XJ>ZM-q!@Acj6;^&F|s7r(=MgiRFp1^s#$&5tmt z_+ehC1xn+d#_Mj>|27oTjLc0-HD(O<TLOOA;4~UvpTY>9#go{H>YEGAuU{Rb+7>hfS+TmeXbnggWrm4P ztc^G}RbbC*Y(k%8Xzb%P*?~bPFcopNiQY81vFdLtg zx3V!B+St3Jf{*$aG&rXFcG)+V{|ICFKjVi5&F_rf|$EfICmDxGb zKD7QL_PG;n%eFYt#;$8{Ul!9eZgfYS!Ln@)d+tQT$ZF` zMp^K92YHl2Ss)Lv&UB`Y2_bB|3-$B5`A{i&f3b_4ilKc>exC@Qaw&W4LhsYdek@o= zgV{e?s`Fif8#|XT@GV%2HjgKcl_zAL_@33PO5c*Xtd5R0QZEK%80({>fuxAd)Zw<{ zw|q30im!rAMN=Ts>Ftp*R(DEV=~yk|*IluV9T_>EJN-f9IIX;s4IDl(B=Z zw42wza(s$1-iR_ZjD@>V&+z5gF(1n}+;G~_#c1iERD$b)O@9VRbd>T113!d2!cyI6 z?Wz|sC~SJ%y28kCeqjC4jXty^-Pl4s-A;zHSP$B|mkDhhd_ov)t|t|yTj#3IxXJI~ zALM(5mQS1M+z4(!rvs*QOK=4`Q$%C@%uij9KcjDYmEW$S@w6?3XxV((R96D_=!)9)y`d3c&|VR1H2#J4*?sGumxc@V8`)(6U;jL z(JSdwgOvPQe5%KJaDLZ=sj36g{eeqFyaex=NdJg3?@z;gemjpDFyauV<gZ&%s9=e7c@4+idyquesYKT;cl}Y)cSr zL7K3lAevESz$6UCFZW|8vPN~NM^#i=+nU2-0b9&Q2T>okvko0XwlUve>e;I=oa6G+ z+w#)y9Yd3F@Vmm?O|@*@!E>Ry`G>d#H4j_E%2g#Ys@uty97Xw#XGbIgEzT zgS5*G>){aEn^N0FYd|QiO}twiEAP|UKfpUmG%qXVPLf4^>Nx8cMlZUJp30?W;Yi*( z`7mo=pWY-JSa3MaqDha~wQ$<8+u?$DwpjCmnH})KU4q>a+pWcj{DH6pVGY7ngqsK@ z2r7Khw*v2mct;~-Bi;pXPXzw8-wcF5n_DL}pj`<)lFyDeq~qz`B5Qmj`aAI-ScD3j z4Vig%{I)~H<&2wc=NIsW{_LyV92d$}S@j4yoh-6`6G5vHs!3xTn$UwmgYtMop6f!i z!x2j~KhxEX&(?*gM4&}XZ z>~vEaMK_OQuFdE`y8SLoYevIq=3TbF8J*jGb6cwV4dEif&j{NQ4k4UF_zB?}!cK$| zgqsLk5V8?YA^d<)ijZJFooLaH&*mb{oY}gqbw+bK9A`BL-r17OI9iqy%@02@FL<9F zz`yj=Pd>oxThd_z`j6oa6dO^UZTekbVgIJIXIeW;@)q3vjf1Q#`mlpg7M3p7#&s1? zU4Vd>XDZO@aE>F+0ASPW6n=b14Oaqc?`Owb(z>lqj7BL~YWFZ^RgZ;)M zi57JLBBNk5hPnTB6uwR@pzd+vjMRJ?!YazfVJsif z;By{v@LA6JY!rONQ6Bt)0{JCQ;Zd`T#Ts6}5u>ot_jQn+K1wiI#hLJh515z~CLKAG z9{O(WT}NLt@`AICxFE4&r_TuAWyaR&Hd_^D*m2S)Ck(fPwRTbwx$+W-nLC8Do3 z63xm9uybdVThl7FOZV_uGpaVstoh)sXfVQ%2`#{K!EtcF>m9J>b$FY~V zmwn!bwx*6F*qS!<~^=)S=+mR^i z-EQ;-3E#EOP9=KKeDI?_j5vrT&v<+!oLlY^VHjHU*AKI2J*a`+Kg?V|qk(m^K~QXf z1Q#JDM3j)@ag}3h+DcGr^lNG#Z0 zL)jz?!CJDY6W=5j%SJ8>$f3$E4?aSZk$k0|xf*Nd)Oup`3~Sa-Y;rHy+@Dvmi@j)= z&NK;G(8h*X&o`8D_p&z)qwhzt=-$+z`#Hx>mD-u3vGXvz6C2r^hWe(gLf2``xh6wr z#Q5^3g()f-l*JBab?a4ZM{hcjzGtjqAKHlcvM>732JTM>U>%-}4KC#e6;SD2iEMoz z`jGCsz?Sr-&G5D!=}Wf}GKo#?PdCL^+#a`GQ& zG87dp__-Bd!j~(#*|2zPxkmQ{$;TSK2y`~>*IU{3QPitdoevCpD-1->4eZCeLHi5G zVm<=PybCBVW;YvXJRUV`OY$O-IuPxa~N2G>yWQ zzaK}_ZsZMnJ({M`H4E5RW8fqCv(8`B5yAerrT`5I;Ul{h?r0)j3wxJ#JM3V+bH!rC zreD^a-TImadQZXS1ik~<&}g0~ZjWIn7V!f44f7pKzw4Oa@&AKwKah72-}8)iQRY|C zO7{Pw_h5G8Xmi?bHfueOrjY7v`8axpE^Wewr_!ZlJhP?JIMRs4rO{^Om32xQ^|Gt) zxfmnC^sNe$Kh|0}sONQ2&-z9{H3B-*mA@?N#}dn#NWICY?Ak-fiDotb2!AJ?6;7hoz+N+j zY=gL*$6{9ra>m6SZR92)XTFR)70xzHrbEdIrkg@NT>to;WANFTSjTP*XHBQjIyCcl z)^`dGOMfd7RZ_qyU1mpYR3=8MuOh9>wD5#ky8(&BfTP&SI)<3|+9xqmyI7DsT{?yA1ANH=Key^NQFO+0>Xfp6Oj5(Cr=kvlO!y9lr6x;{}~ zf;8dutt7jgL{nzq4D{d-UH2$AWJHz)0+5g+hYisc>k|dnVr?AWaIxOu1+(I?iZ~)q zgQycrS@u*KG0X`r4)Pj41s-p$mw2aKd9F)2if|C6J_P*cb6v8*AaKdrd!jZefC;(+ zj(a9x$?pKWhIApqN#v_P3~-dYEuZz7M(fn8r(}H(bUx6fc<16RqZ#V2xF+&3U<^=y z@eTWZ8XcWp8ymSe&B5JPFvivq?ymCMKl~A*n(Gd4kEb5-)U5kZ-RHXd4-zfUl1A&E3AtU5Bg4tbFP;W?%^6Az+57^k=o9lE#ytX1sQ4};XhW`ZGCJ;N@b6P@v?chOUIIrd+8r^T$t4CrIL6sk zk)O%I-Od8Za9_Nsfe@%~u%6}=xN;K|yi3jQWKwtRdf8)6o*cQdIEeYTJp zX5oE$A&Z(tz1;?*RP;K=LCSsT|AvEX=q&V{&qB6%7WGQcTEM%;@DE-|0R@;#)*+Ul zi_1oZ^Eob{AXSTje-&|E=1VlIXcAvOM1bH5XvT3GXTx4W%IBgl5HsrH4gxn<0G0gT zBd*K*6KAYkllNJ${n-e7SV#tSOAp|L&W4eKo-?V2SUHpC0$8A%?Srz17`^j(JXlR0in!-hXGI;q%Y<;%*C_)D|xL2ufPwgs{K}w z@H#9)Oqbb`+o4GPZ8N);K|Rxd0Wi+24Z=&F3PVxUMbu@6qUhR$%G_;5?joW8+FlAc zwLUma7l?*pv3nGPZNqVq+F%Y|kb=^RQt{x;Lu1P3qj_kVn9d)~C)HJ{y#PK7FknVq zyjq^m=48?)4xa%e?_=4S)Wdrr&!vk~^LOfA2sRH9WLrI-m1NR7-J2oFS?b~ppKww$ z@2t;x%vn8|lftOz;xFm&%_$ns1&cESy(`B!Yg6!2>SHri3N~;)8$O%Xqc=@#`E2T! zz8ok~LcHNOM2qWI4$~y;0+{vVEAVj!Gol~O7L~;f1RTZvUQQXQ?}}J?KThDdfKy4j z_{+N4ze2{`7l_yS#;JJcJewB?^lU>%P{rxuwGZcsQH32bs2=GI99qA| z1t68?97C*}|Fd~))f`&Wp$|av0k(e*t>e&y$0hb~4z1OEA4v-sPIVipCX~iBN#Dw4~+!5zEvgMb1n;?M?LBr(YCxd_h00dUDb#2cv*v{ z?hv^yb2>(s;x#NlF6g*rF8g{O)q4)%WY3DV{qTk>Py^JMlKhsi)$?fGo=vv{d$!M& zytl4e6Q_KelAU)a`NpB!MH{t40T7YS6_I8Y5iVqbvKNuEjfLP)|7R2p<|TWWkBR#E ze}!aTSx9N#+vHrl<-D@)>fLj=)D%0vc@7(3qQiQQ<7YtJx}!N!?UB&Uy@^r4Q%0Ve zwT-9V3hN%6%bQ{F!_FTBaqQ6TXZzH#Aj&KI$h7CZNYS=g!Tjdan*AC92}8^)t8R*T zq&6LgIU?B%$v9mn1=kV@e9dNq90gLOb8a;ei9V<`M04;^&Xkm}T>f-9XpGe6&1T=u zr@bTO*{G$k<#}bFsjuKD32$!8cU9POv_*XoROPn(=jANeOnvF$<;-ZNtz#xuW+^hD z@Bi19CjhP3@{`aif&G^)|2C85fn9oOCQ2^uqc7p_vXF;)MOXEih#$%+n|>7g~b-5u#T$iba?23No8-f*vG=kGO8>R|UZh!m{ zE4P6i1W?J}8*yD`8dL*XuI3^r>JL6_s0D`f4)zSeB&?Fj834+eyymz77}9XAR=3Jv zNNXZvxgpKrg#R+6OyPACFJ(fQgIM_l`5VV^L)y5K*GY8X$A&aTkno0dMogE5QcOpfSBv<&A?pOdIE$o9r<;>Gaz2xDNdESZJ^L)Iw^hSt$>9gZ6nxzFr@E6 zh|(T#`US&4Uj%KdFr>Qxtu&;+&!{k@62NF2H>AJJV22mNka_?lPg}l-derQMXlfJ8 zx=B0|3BhI}0_E&GgEd-21E}W=_Squp*Zo4eA|n{oVnh{#NdmuM7}WY)2o(l( z3rJz%%E~iG>enDvF5pAy?C2s|FMS3;MMtr7sPk1Rrn7xHxh~$&8`1xm);bmVI71|& z|Ced44mb+#Q%)JFr-+po9L#Z*rnMP*<6pLQ?R21L8{Xi>P3w1fLk(fQ@Pui_;D%{E z$2Km8X`KV4qB&C$E9bv*)ZeabV6NY;M`ryf&-FbY2UQ`3SrXI`= z7lP%PYfv+>SV_4BAP~tx2bDAyd&!*8;Da>sUKQZvT70d?S7f|@;}YH8C!@ECLH378n_@q|$~{fbey`*~XoP&WW{88J9K1plZ8 zf;OZO@fYBywOhn|m(oy57qP^p^q})LIP&;y&Qr4KYc_KQt!@olhASbCz2JSzO~(ij zo3WgZbi4%rR{mo&JG}zmifI@Pc3j3GBS$MV354(5;+<&bHNE_KL)vk4D(#tQ1r zLXp#99tZU101wt@1@&?0$^o@GAkZ;{$6t@)9NpQ071Y_G3WuC#r&iDgj@RLB$SVc5 zI`jILR(Je~Lq?ZF+J8%X(SJ;A-M7#JD=>Lw(1N^M(1N^MZw-HP$Qz*r9dSaG_oB~H zSaz2jsMl$zS5T4gJ)Uh0nhHXA0vm&LJjaWaV|jN{3}=Bw4=m=OJJ5l=JAE$0hQkvH zS04aQH&+iG;Hh0a)pZWP>XBELV({fT-rm21qR(-+0v^CV^=iP#hXM{@3We_v)Wu;V2aMr>1`gby%8?3k$`91dA&vudO7;yu z&@gX07u&x2pETl}poV-F_Xlv+W7BtZWRHHp2KW0U7PywKA=lZVwR8e$$y%?Y+vw^Z z){=F!9ihFtTbuku3kmHpiIEMoCf#Dsd^XVFs?EFN+^3M=xs5Q`O=8_P&{)#X$~MrJ z#P1)9OEyzni(Sn|`$UsblVHxa=^p@ypIO!5b4}&k&HfkKA<%CEY^-iJF7~Q+!hERa zs%_)>?maPUa9uqC4v(%28~qEdMK6zMmS3n>uR5HMMf(~Vi(N9xNE*L;Kqlf$e++IE zwDNXbw)M$!RP@bD>vGW8G`~_z+c8?PJRbT-xd83 zam1!y2O{jT`ik?2a=1<3hcg9O9I*CRp_*nKSMXCsoF{fbmhuw`T%FWjPGy!~ap`h% zs&&h+n9D=<4Fld{7%~>u3U}Ibn`=JHi#q-fk2ZZW8S*n3UQcGtH_;_ETwZyS_{7YyPDnEMBD0aPvR&6H0iHTVvRP_W^M*BPvz%)M!DBe9NDa56F1W! zC)?MYxBBMSY{O=nO9ryxf6%>j$QTx~1>5A0U$f7*&}iLnuSJ_1MV#Tqq_HhqXgi-@ zM}wnUK8x|E?7WMkA^sb`k=i++`O`{p{?EN;{lqSTprdc$Q2E?=8Sy(#Ics4Z)3V1h;eV2tZq~USQeLEfG z)cqBA=G5v+X+m;I^o4z9xcxKM5|TzT7G*lgl^ z1b(;n*fD&~oHGZPaw_;}JEOyt@1qI6jZT9aYFqO@v=~NV|Iip&h*Q`XMc{2R1ai1p zdwwLUb(4e4+SAEM;GEQC$mY;O1?n!8!0;yr?NmTc3h2!kfYt+qDwzy_BUP@)I55&r z8!txVoHY1+OIWm(hHB##P$v$$%qCiCH>$=xW-Dz+bDlG|o!G=3n8rHqq`tApp?8z=2fa=&t-;ev=fVw-l;pz8g&#^4@+gM9ZMN(2)PySJP6@-Yns0v}!IWRi?uf*&KBJ|vRmsUH#p z0yF^E&+*q19$|BB;!vo^Ig>$H!0c=l-x zr930QD<2A@#Mmyx8SK=3)Kl}*L(IewAW0Ysa@@i3c#J_>0M3CQ z?XfY8y6HsSY`V;`n8&yf4B_z{4lCLkGLX|#3T6Gx~Q=JY;z9v(4G4Y z#q-}(sDbVGKbo<#IW*k47(iUJK{qdBE(d8hx4OeI@^Wt4C)mY9BwGLBY|KH7xu;E; zbP%WZ^;z~o>R`nuM{O%CqRB$`o-{$&$p$)#R3tAO6pEeB&h6N0wsd)-Ih!!*45k&%3YECPX*$1M8NGS>Al?MrLlWm^x^#`N7ScB_!qT%J#B>R*n) z+TWtSkC(XK2J+j`B%tW*J1i-mc1vH(aa<2THx+S^JpLP6(HaAl*B6)Ylplga7uxeS zV2jIOvogj)pW z3;3e*78umP415*#&s^{Uk$(fUm)23 zTV~zCyh45<;P;`SqMa{?@yhZmh{cHQ!Y84e=|#llHk=!3sx_ZQSK_{56<$vdn;t(V zeCh~os?P$IxOcd&l1cV+=6#gbbpHOPB9q9!RP5G0Qa!TL2jlf0z>BJ`6qU&H;xkCR zxWkKi^O9VeXWo}u&h;OAYJ!k2TV8*Oit;%V zW!ynrK7&)7@)}m*-{!9VOO*5pO8UsSaF-U<+cgBau^{2O*CP&w_g|o!ES!n>?iU@y ze|D#weiUeMt2N!C9-vTvj+Z$tab`I7BAUfK~Lal zSS*ensGtXNw4H(;!_izrf$o#8;FCD_yZG7yh4YH+!upWolN59(yyQFu-HW5kRj0jz z?!eI-6!Zv=F7NK13Obpi=PKxt9PO?!=kRpM=@z!$<)I3`2gmCbd<^3MkFhs_tFrjw z#-IBP52Aux1O!C%f}kiasJL$zbGh!g6_yLGxfUuInF@xMaHViPw)_ms%4{PomsZRT zOA{-L$_mTs5@uLtRx1DRnRzZ;koW(-&xgx-=FFLM&YU^VY|qR*h~^x+hO20qbF+** za?YkLZvztU8LY(4RA=z6_Gl;b`oA{;?Lj?@THl(8XsnAQ@rm9h=1UCd>n?3|e#6gF zYy9NW)`=5n%qM!w%u$?B4%fM`u@_W^E3Z^l@h`_n;4oIg#sTCQoe(lNa^)Gp2w;)5 z5nw`#gifQOggi3Bus_80D_TCY@%}!ys_GzTx1y2L_ySqUnQB41Su^&N3Er+r@VaM$ zJ)EE(`rI1&Oi%iy6UcE!Ptjv1(4I4Tr_QbtxNi2x@)@W)Jh)ds|Mb5dRfk<^NYCRD zgU#mpkg1%0ACIR7C3-*0TQs;tKZWy|1J3FrC%v*BWr7!Dqfja~xpEvmC#nzmaDB+d z^&yk#jkEfc=|_?v{55Q4Kxg1f+~cz&g#cB-d%xsn933(FVS+kHyg`NhDCrmh7)M42KB;G(>tl` zGreWsxi7HJ*HP)n&G7rM-K3&y6yAo~zzrw=LeiJKel*sV{Fv<47~Hk}5(TrKdVj8W z4oH?8R5nN*copqIdhl~SvXc&>;Mp$E;qCfkI}gr`-^WOluj^9`L@KL{?-7>mCvU*{ zTwiE8M+xV!3Nn#qpTmH**Jt$XIeloO%1>qgx{2!q{c%q3nfc6U z2J~oid@8;`?|KY9Q;JE0Q*^pipQ!)Rnp%IQM`u1bg7wA`6?dTi^}$j74-uSPPh(O$A$FSp6(_+4tn zLU4e9->?PeaJTz*qXjfQiJ7o;W)qHF`47Y%gzF)xxHi4!tvmpzMG!^Mvu|=+)byx(ffN< zt-=fezNQdJw)1+31t;yF*Apz;Y4LeIykQyMsj}gdY2$fJD)*s}&+DBmjp^U>dUrfi z@!#s>?2lr?@3yLSX?@74^&uT}_*?y5%UD|d9cs0&!sw;%uugdd{qmhYt?dWs;-Jr> zR%T@nydyn+Ztn;5^!BI+wYylg&-jBRVYKLb{XL(D?vvdjEb}hYM`0)4>N0(#KK3Mi zT!ux__)-Rd{_kY+y`YC#zNa=9^!AoFY1{?9r{xCaT!0^qp#v9mo4rd$JsE0IAF_do zluqkzRsNvFAM_y1zB7x*B1rLQLsf7_qt|%4bR9K66+d)$v(V-ivLm1wd|pHe$>ZwJCh_whP=uEvsA|5 z@vs%{ZLVoNQIps4b;QGlvA|;s6ptZ?Q@@Lt^80QaWnDxiv^9yIzlhzqf6=Lndbs5) za$VG0c^!HOzETK8#FMzg_YC8Cx;)T%0%t#3#EEoFkBp3Wt)@GE!U+5cO8p7#(jQGJ z|0jKE!!Z!auGEqm|Ezb>pB_U4e%9k7J{kiRvQLDL>U(|lF-P`s9o5knW0~|5*EDke z3`V2r;LrMfmPXX}7yZ3v3Bm5tSyW?5fBN?qY#W?rrD&I)VtIwuxlrk+(G{28*OE*f z%dxri?~(LPxo!_!9DpY`FVBVsEb$=T0cbeJuhXykpgU7Wu-G#r^Sov|oL(Pdb4?L@ zUm7Or2V=i}&fvbWBb*z4iA6$J^nT7i7QGC2o*t32Rw4a?1a5H7FyHe1m@h|%U~p49 z{;O^a{rWAYAJsSBRf4-}Hb>~zuX<0f&+da=Bb$q`CTlLKn#ZvoL%a7Vr9$uF|HxcT z>)Ss8Gj9H4`*-X|&sXTt*6Hs{-C`Zi)3De-J?X0o{eFFYPa1JaA0N6FhtP0clIOrV zIsaIeqZ%r%3SRymy?F_*o8Iq1moDjp$IQ!uD}96Z_D9V2p{nq8U4z!HZ{Z{REb8!% zCqR;GPI-AucAp!ChSAo7bUf^U}BNrav-}Gpo2q3YVqJsAPrgyW%(RaV;%{vz1 zpi{Fa`PM?r`C`T-vm#RhN8kUJH+4NzL$HNnE@RumlQi6$`U|Z{?dE-`MTrN16$n?%7VK@Rx~9ikHd5^0dbHmnj$zv(CWR*dt*;l)Xo4>Ot#^%lc1?ATXoi`i0yK0o zM<}*Wspoa-s!>>g)8^b$z(r^mVfTgXg>CHCphGK2G2I8ol?AUSPqT;=g*Y z(6?XZyj;hl+IVp9IXKbDQFW%G2{tUf_Aj)$o4)*4-yA*$&7@PSn8Gv3uexwYKI1en22ayhf{>`a4^1Gq8ix_bOCqZ{R zhHBV0%K67=OldgJCh~JPT0^N%xS^+E&rdgos{aZ?{}zkiwP?0MsRW3Wx{0sd>7ptK#%CV6*Mjhcn;W%9w-n6usw>vCZS zBNqD#FTvJT!53bl$+ysS|3{DALYwv<9lwP(4U121Vb{!$G^Pr9+)8Oxc+2+lb$YrA zdtS!VfhyDoI709DD(w5Zrf(f3BK5x?#6d=Si>*%4sQ1g=tEz@L&kV)@0UwOQhycB_ zw;@&IeYb6P%Q~p}C}&<4P=j&yiSx5W-c{LQx6Eeg)^gF((vEyAVvyeBZyIG0F_xaR z)FKAzZC|8)7IBZIEBWi9jiod7&_zFe?hBNri%XU=nju6R{oCbI6?$HX$@=(R+q^_@ z?8{HXN|?&1dvxAr;CYI7w2!0PY1d!BQ-3ef%QBLddx^G|g|x#`0$|6v$?I<=bQLmZhRK7iSeG!&C8MHJRZjBh*{ zQ}ub%o8e&@OUoOHi`b2IPh&9(2L$hKj0{|&3yno|@QE$(h^iB#^D^mz2Lt2rUP*37F7l)^N&)wL8gmRUdEOVg~vhL0O%TE+Zk zvt!&OpU$f$!7+|j5shX;bM&^Pq3hoeuirZdh>qA9I5j}D9=>G^hGH@6nxP`k{_j?L z(-X#W+_Sf1x^z3L`QYsTZl(vFFs4_dmjLZ7tq95f??yV_CZfA_!bogJCj&$hzIM>O zsc3D%cWRo76w6QaOjA7h+vx44A`@E@`vi)vmQY$8h%)}3UI-M=>3@DtBZI_zoEZI9 zka)6bItobTF8houg+(+YScF36R(f z!tFGtnP~4{4q9%TRD#a5qnU_k+#jCGcJ#)|mg7j%2OT3VeH3~vqkaK>j-MfWO2zvF48r0=Y>YEb@D1MB8vFSMj$u4M zh+Ya6?K=9SR$lie`YYTr{sC5a@0&L!H1VJXrRJIvfg0i1=)BQZ40#9sOjV)ckrs)H zX~Y@60n>>0`36iQUQMK(VWPif8hsxoqWKtjWVdw_p30f30xc0RGl&rvRuWL=L_N%~ zR|R*;r^MzW#d;tQsb;y*6pcd9j1LlRF4}ik1uRf-xxa@yZ&irl&TBkm|6&~;CmB$$ z7V++G+2hn8Tnx1=qcPzk+%k|B05oiDpwj8vy@f%KA(;7#7iM61W4nfD+<)C$xB-KK zj^5qRAttSdYuZ*W*&_e7_^vx@tM2=OM*0%~{_EC&T)s7uah1p;0bjA$zVCM zCl-b~@x7W!s)jI7h2n&QwcI`EzQAjcV7ch*XP>UZ#}|;{-rc8yB0a7;vRjz$Nw-ULiio)Tr68yB?L8#5Q9VS1ClGU zk4NY9`)+tn+IK#qa=J$%c4>r&4ZSvj9gJU_r8)3Do8ftP;)9{ZuC}x{LbUDs;s&-l z`ZwR*13&odgw`H8=i2NGbWkcnTql5bWPf5lW6sOYzxc-m^A-iQ6dk?J^hLM)Y)3tW zrnD6Cx)%;Y0?hn$Jey^Bj@JNJ6}mISoxTIO3w-A&4CgUIcm4Z>Tp@#3=O-{bGEhr@ z6E*2MQ1Z)TybMMYo`&&+JeDb&KgN8-H4Ku_=a?OBcj{F6@Bb-C z{acA%aU<{M%E1>o!eB6wo0~>18qE2-j<5$r2|nSGNy7B8_vnoF0NQi_v5~d?@K-Kr|4!BD!bhj z*;)j)*rH-W*)!wLxYns4*W<1SDXF!H>bv7BRAs$~Ri-uPWgsZKrrEtUILZJ?+Qij^ zVRoiVTJU>X`N*671a>14DRGa-FDXYjhy?>bDoZaq&%@iu|&av+H6 zxraJZW2QsSm8k_B&fx4@IOwMh3<3XF1Y1F%s`KAzL5#Rd|K%JVi4lGECmq|`h)R84 zZ~CK+7;X87>}}DCB+%rxA|BHuxot(XzNZ%-G~6!p#1Ke%2P$aj=|%UA_~1WXOS{i8 z>pxVw@8BK-V>zL#S4?s`f)1DaT{~=%o0oUUNZ)^4<3^+Pyk3>&RfYKmq(a{sseG@i zS;L-Ltg&CJn>rISC?HDVaCY={U#;p$#Ut{ak!vH(jz!!23~h=POR!|ftDT4oSTb04 z4k8}>7YSw=3;rd0JF&}R#djga8%?fZkpRESMDOAYyn4s0mw7%}5{LPXpg7UTvVz9O ziMF;8Xz5RA4Ux@?LF-Otbn5Gugr5AeHjY^*F`6EHowmh^R(_4Esa#qo`Y=vJW%fG_ zdl;^7(^&11HmsZLsxTibOe`?53Uf$d(B}HJRG1ePrWY_l3bR#V?7%cqm~4e{0HZ0) z5`~!uOp^r)HC>@nf$~zA(F&6R%)h6kihU)<345cP;P;zCwoypgsp{)jrjP*&DgCXl z-)9PmO1Xm=ua|#JksU>|%*n7j-kvxsU5{dr!qvY|qVrfeAGY)XerQlmW4%f@ld8-$ zLsDH#Z^SLlLodDKRd?$4LX45!;lsnGgJ3oiUai(#sW@BvEM5{2A zCvT!{iFxA+T~Q!tT*{HaCnX+g4pI~p;rqH7`#CKodK|0 zjlnz`&0sh!V$hzPouO7kdWC_DPBS<`mph}LdXAdL17uPUfS7x5hEwAsY3(`Vvrxw~ zya!_jZhD`K{?kID(xahCjE3=mii7EGZJG2 zzi$*~gTh#WIjb-$6eby%1}9{yc?y#P%uPjeCoStLM(87t(+6GAA$yy?>?&4=h740) zQ0AJyn7!blDy}KCxEuPDKMYl|*Ify;wVUXW`6p(yhv$v)NtCZ?9XKGu#-_$ApLpjv zoLBQ1;z#G*XH7h0Rc0QQgE>7M$;1?mOI`aApD5n|n;2D^$?np;>e?tN&?=y~Wg111 zA}Pq8AdJxaK?3uJvsvG{iJszVY2igQH9-uEpM8{@v;T(Uqn%D;Is(J-nPl8fQMcUb zd_vtO<96Fo`aD4-2JOQn7VIzEq!)2ND(8I{ecwg2q+#8~NJ|ge++8HYOD=U6!y?z< z5T(YIL-NLAy~D6x`0BD8KT8eS4P7&6Vh?oK2h#E$A_~(#PxKJ|T6}+8j=SY_F_JK$ zOxC#4b(F635Yea?oArdBWl{H@A_2qP1wAp$ZB3hc3Ty^HPWw5sjL!80={xjqPxu>A z%U&4fEuq1^;EPE#rx)VlD3?Qx=w$}wbgGxg3%mbb&Y#o4P3A0hkJ(!dXL&E}Eyl-q zDb#LsAYIqzO435UzohOD@_iYV_ZA(SMxoiQ82@$=P;wmsj>(MeBie^82dR8xQPHN` z$rIV!R^kuqTxek*aYvJ;18aH--mU1}KEi=fk*! zF0Jkd6IRmBe&|AvBUeAPY+e!6q(7R7F|@D0*oQ&py#qv-ph_%TFyC+%viCTz;INoi z28a$AZ+|ramKaF?4iMd%QjUBx`6|CQ;`f|5-DKcEF&QJqCkBc=&DN~vX#cgFF$Ae5 zDLXLPF6xd z1V$O?Aje~H8eh?mBQ8-MBJOG~Bjoz-CLD>*3KkTlVAhj+shIUnLsu0OmDA;+xOhLD zeaVi8;KeshC9%vt@esG6JTGN~vZ!E_;GUMjA~d*Fy0pxeBi!fZb@dqMH>OU5#Wc*$ z=M5H1EqU99hynUXL&!cvBgi@Ak?-k5|_#m){z17V{pLq%kZUYr3?K(#3jnL|+me@8SzQ_dWu79$|Y|eFXf}o|HEhL(KQ6WUS~N?teE9 zy&6*Hl^!-YXHHo~;0fHr7pj6UPp0T3F+B9g$(%+1wJ(B!bM!@7 z4YP!c8bJDyifP@d^2RoB4G2ev*!jKFSd4Q<Tdwuv27W-;0JH0htL=M!?0_!}?YhMpv_jVk<;O!9D?OheKWD>uekp1EBO8yTt zlO|E{1Yv9P+~?>#KLmX-&q5`1#{@J*duibW5vTugCvBe~TKP6?>!5`ub#^tiD}#nl6jPf=olY)j>2<$9KX$R`jhvuI zCW)DP!L#%$VlA7g<((KNVF%BhVzEAZJiTzI$kq>xqqw^;YyBZTdKaFO59qbKFl&7+ zi7wqGV)Qdf6qt;b|65v=jM?k2QQ2c6oeGmhxE`EDr;{<@YD>2eZ2$T&^ojLu*Z6e= zJ^?ef<`Xbm5rY=rIXaf-vHyOO?wE`Rk!ZzakwMoci~fzfTC`FYU;7Wl;pp^+%Nn^B(apOt$&^H4 z_n-sPiTd7ynly+e-6Q&xBoaDqCm#2ZNHRfq4 z!P@c7SZY64v`$n4J7y?lu)%ur5BPnGAK(9qB*8b@u#DuNAoyp5yProGEU{nun~0QH zdTg#3aEBzB68x2f>L989OopA#H3v!E1AXK{QdcnYn46#865Rau$MC@f#_w&(|6b9| zM}^L{rH=QCkU)g6@1d`e$~T{2cR$6@UH6K(OoXvTm(N1!IFRrE>D;j=p#R!LRDZ|L z{{9;<$NV4p5k=4Er7UEc|E9$Jsc5@D1l|1CKBi**rb_HL5px(`SoWvo*oDte@=o32 zV_$s}^Ezn3E#_WM^DsV+1J}rObvRaD5&RY>!1*e#0>TB_R6rUwDjz+(3_F#9_=%JL zIr%Ve9R7!CaZbu%K;Ew$QEJ|FydO8PM480ym1L6QaDMls3iezlcpY{>&oUW7-tQ~z=x&J0# z!FJ=5ka3m|JBc|W1(d(k@#`46I#0B2CyA#VIrdF**|BdX`}JUE-%O4f!0^Y(h74oF zb<1e7&lfGDq^v2y!x)}j_Gtp2Lb9hd0{_H*$mfrI5=~k2MTiejDD>$u^vrzG#te0j zq0@+K6@{{Hi$X8q8!q&}aU-8lBoQ$r7KnuK$IOav$aTl8KX{ta`Na_D1vHTx4$=4p zXpHRi-~zk{Iz-PcKpPZ5Hx>wNzFJ6;3sJj&M|Ug~iKx%lEkxryfL>jQA<_W4x)A-a z0~E1HbPwo@?uC$Z)arEduW8mI(Xq?<_hpm9pJ7^wCLZhb`W!%)0v!f)?cIF_r`PeU zl|H?+RbSzomxmXLv6lW6uvoP94cdm9FRl#Zh7nG3+$U^$#(r9GA2Q`!jJ|6>I=@(i z2ls(!yn?~J-G*p8PnV(@=|aBuiFjY@RyTv8xy&FED|?ZCLszJakwH`Lb6SRZ^*yrG z-_iH4rRyuOIiec|3AgcUwvPPn!$;fhxL<_%R$|1a)M}GM^X>=NH!1gi@tma>bxOsH zxjr-_RYV5&tTxGp=oC}07l=~DsD_7qRQKg8x|AxyI%DLvEW_da(=^JcyqIHqxz{9j zXQNj&-05}n+F+kvmbCF`oBu}%ON7lg6+Bee2Rr9dmWbvND=wh%KT#3xiZt2xt|93} zv_wp3`!@Q2+;936(CpBh_~n*-1J({z0T#rK%IkRg4GLH)iW_%F28NUY@oynrTq^c9 zMvgGH!8a0~PoSO4L|lvOMzs#7<*k9|y*wP_4A)|EEfbx5Uz2Fw=7SHQMx}`%lOFsY z4LBbw9N-#Xot)dZiV1^RJTI5x6`NRd6eY){>Hby0ySt%JUF>?p3mqSv1I;u%PmC7^ zpJ6Fl)a&$ani%GL7|~{~_LG0QXcpM>E$%IR?7Z%}sqS2psaHB$><8%1bkUDiqK3&;8Tx$?krHD1^SmwEt zCN39|wz1Xdibd$xCie}Vgdym%BnQs^%QJ?*N;6i9K|wcR=!!ts&$^q3>zg0wgOwtL z^asQ}e)j_JUQ2TiEqFkTXkuj)_hT^@@cMy1d;oI{U()3V#FSPq^JLc9^an@d!C0g^ z#P?dN?Ky?!XNYHG4{6ny*UD~H9pnW*-g7q$#`PKySBQR=vn}86N!a4Mp9wn%dRiTQ&?niu0xEeO}$ZskjR@%kMAH z?<<8Z@$47aBZw9bk6o$jC<6FgPugKj5**}vE=3V1-T&39;D)$!mLl%+a~$_2isZ2u zC}|Z&<`-$nD!c|>ORujI;deyJ1GNV81oGQpjd#6vyhp@a)10;3eZ%_xtvtt(ebjD# z?l=}*yTvl@ANf6&cJ0Y;wdmdpD109TV`%wR$OTowSK9HL67TfsNZ{v@;3V+;E-;=) zwy)S|#cB~@GjTQ)@EK$U@{eTeXi+dwIENGPJJ5mEqGbbURu%Jgdpf^b%&}w~7?dfF zS$O@;8nFRj>l$XWXecMke6#``SpXWl^oEQmSs zu;?=L9cY*>zrj>(m(cP*4|>|gR^AO^l`T7S4MHL_7qK#&R~_!f;j71Gw&i{3ak?{G z^vK+NLw@dJ#xb`%4}M!f@5%2?5Xo@P#AvHqd(21`pYTY`=}psKy)Nb1ShfrIoR%-d zb5WNk9Ea)M0UzS80IG-NEvxlQ7wnY|QV-j@Qe*hz+|%<$++N$cSId1ev01>zYj z|NK>{P!gJ?@?zYdIz_o_MVmm7n#Rt{P2ouAt8`?oh>Cm0BO;VJ49$}gQuuy^;+DnI z-hYOy>qJ7=L)8hK(v#rM*79ZhW!{lHSuCF)m0HOA>qlt$I?-dqMpX>&Yy^8*?8;2C zsL~X@ymxG^m21gkq6fd&t#l3i+UF~NL7Qvylk``Q)W|R1LU%qQdbBj@U#LUx;Jod6 zl=eJ=x%wUS$s-~>;$_nb%tvLg;$~;cc^{JZqoRlHaV34gp4!q2nP&Ein^jbnD?&%) zjZT7jl3Xc&RvXPM|4yKB`@+L-@x9;QSdm%Y=|0cLCK>E7I`8F*qxT;b?L+?XV8a=e zRmt?LRQ0H6E^d=r;7Kk$ZH$N1Ka|vNEcHH|BvA9PR!dz-tJnXZq(1W}eXw41ZGFY9 zlJeBsxFosGQBaO(A6QLjR)giEAEa?PcyW+UY4yhYWzZWrBGj*_rYs#B>5ClE!?8hG z|ARxdt)Io#Py0go55(9x9JE%IW<8p1rHQ%FtkRtYw`P7g0Y6tnwS&stKE*1MoqKMf z3aM2kI7d5NecbVD=zOk-8Th*gA1ONbT#YNsM>vZW8vM`A8e==0lXwOv+12tXvO7gs z$c_3UFF#4C9C@q0$oHS50z_ukoAR+IJ(S{tEuZp;r&3VMnJe*wfj12Rw~8&l`d_sj zr9<#zUeI5*VrDqDyrf@EtCq>lT57i%ME2o%!eVVVRCuF!0Pz& zEs9qPD}8Xlt(vOPT@dkTSzx<(*Vj1JtXrlmsokC>RZVdBAP{i;n)>74cE``CKfcc$ zX8OeX;~NdRRpl)OL{h&z5u5o&wbfM}0*i*0XTJulQ`L|M3UP1NFYn-~bl3BM)s70^ zNJ0LRTzj@xvvcR2TU9fDp6khHZkpM95!pxjOx5g~&XwX?a61FLV!+OqKyz^)j3d&efjQ#DEWGGGtJHHtrw6RHq7l7d9sFIeLUh^ zZx1y~YzZ@|7F6vKeIQ;I*f{ci9KG8be$L!{_BcJ8`XK@{z$+ie@cD;r3?s3SbjRZ& zrpLDG?9?aTm3zwMzT|n9ZkF>r`sZ=cag@FgzFeIpSIGQZRk@?_U0ig9%70$qj#HXi zaAXc%;KxFW4f8yAp>-JL9FppsG!NyLf=@Ot%@+%X`QUV@Q;zvI@}(s=(?=UcbDtcvCzod3!j7d!L}-INRBcB$-JqgJG5q(-o@2w3 zCOok_HgTO5icbIVO~Pj1RBg_@+<9#M@xa(8a0d(y`l}-cTI8U{UB(~bbmnmZIdUAS z4(AX=hq|WFOPj=C?~xzes&X}@KR02ym5mZNV@|jWP1%fj$_@p$_yo`oa#(4*Ai zr;I&s~Fs^u+%M*Im>qmiaFJe@ZnrY4_ZwRZ57RvULJ&{a@9`eaIV`93T&K|G(&48 zc8<0ybliKj(~QPiN_NWW;6>B7h-O&Cc4Mn(lVWzd%{r_68#Kq}Jp<(bQNI^AVVH)6 zdJQnm{v(#Q^Lj?}LZR{*PE!|QDP1M{Q5Z_j$2|sh)}`GsC>y+p9^NK8Ozb-67LKaC z-}w26eA(ClH2CO@;SYG+xV{jbSg03U{wP?u8XbY1}Rh;hy4RgL9@VJqT|B7=1=A8SX2j>XHxhfx%I=91pHD@oyxl>n@^Uohjolh)K zoX-FrHxA;zsWVu(8huFT39mUe(LW@n15Dly@Hib(AnXY-w)uRP2lK*4CvjgbZ`XX& zMl($t6-jybf=1>95IMnrQyy5j8Wnlw1aYoZ#kn2st2uiq&J*^|Fm0qbpO|On1Q0pF zf5o{0bI!$sS8e@Oh;vmsrOr9HujV|P)@&0Ur*<_N|NMbexh+Uo<<5Y|jZpkIRR#xF zBgHt+Z+<#zl?1fH>^v3txS7RV&SN~Y9O9Y|=4aV&^KxqZl!)mJ6wj)_nJ~MVY~^SE zw|kiaABa<1<9YFuEM@g4Hz z&$9RC9C+nvyuQmkc!j4bHEov(OH3${__yV66+hF&k2m396Lv7+BPLvKLYoP%nb7wS z#V6W?{S?g9CYp%ZCfs1cr%kxagoP%2--M@3_=O28P3ZNf64cCuZA{q1griJ2O~On~ zyU#>qnQ)T{cbM=M6Be29YZG2JVWX=`V5|xInK0Rei%huMgxgK{8bip}j+%(CP5843 z|1hD~UrInz6Sg#Aya@-G@J{8W1DA_Xh0dKLvV@Cx#Z4>0e796u`B>+2NF`S0B{Hl`Uj)o6j8sfV8xAz{|# zhGcIGR%&V{>_t})Z8=B{pA$WMci5~-fiuKw%2Ur6nvDavI3~*29L7rT0^WTwT_GKw zYy?i?sJbl5{vTObOKL+mVyCkeB9MG9i6DL6 zXY}=0qfKVkXEkZv<;07I4g$}-Dgk-xiS2VkiwES(ErWG<1$g(xx)mX8GZ9R8@ZtI` z=~^T46Et^GxUv+XJM4y)533^Et`cbRYik#wwNChYA8P)a7|=V`X$o$no|4_!D!l%f z3cKC7)GTVPvBEoO<8LCQo%K;go;6INQ*u=}wNCPQI{TYw6_AmmqLUnoJc%rqMTlOS zOU*BfmiqOz)aSB*E7JVS*tl0ok(W>qmoB3qe9qC=mqnXQ+qoLUxeLM@1{@5$`{IJg zLO9k$xC?>{O#7mSt}%jkgp=yfG5u}M3Obvr;#J9(EH$$dS^F7r7l;<6@RjWpw6<5U zcvcrK>1vo7ZY=X4tvSt7n(|2UPJD*wph?nxVpF!&O*&P=$Sx zRM29g= z%}2V@d^l&DT#DBzx_)MOJ6%BXfGtneJVE?K?WQiHfv)?&q$u?DCu^v=A=B!oK1gf8 zgeu1xRxEU`xli`EkM+2}j+L=)dR`^#zTfU~pXzasL(j$o+&W3otI33e*)xLXcZGHz4wgJzVn;%#C(OV2Yx=&q!07*1sEDV=lN6vP7 zgV8B<(DNjkE!1MDTRgMBw5dis8~8fHHbB1I;#tiC4?0d!xQYI)z#5rQ8kb~XQ5J*1 zU@p*k8*4c73fltO35tN-|1%`RgMgf$GO~p`VY-=3Tc&EDeo?qAjESDJ9U`=>2CDX?@gBd5?BMq5GUzW6S- ze(ExcoXEW2uA$}vEL7;^H*2Fa6uPcRd`Wtu5!jzCcXWSE3J#YdYy;%WZ40K@@|G&{ zcL5tATuFynkA4WcFTP7w=!+hDNCHL&^3fn#T@?!{U{XC+S+CG_sZ;8wj;|l>Bhk0J z^}gHl$UT;RduuYoi&+ZDawnYdYGG}3${U7eM>Jn<6UnCnbR13qR8Pe?B)_e^OK4|( zXguRAZ0&0`!nk1b5w-*JJoa$?JMV7A4+=!D+QProSX)aaK>vIK|{Go@QFTPa8p${4O#;}Le$p=0{G<|%riMnM+E z^(ZTt0=)ZbpiR)U3?S?##x0mFk@0*Dp%vjGgj4I#)hF0ak(2B3AslW|AKFDr>O&ut z=-VEH{9W~D>gXq`bj|~EWs2c7bM1$q`(od~qj)#UyzF!hnyD)hwwrWr3$W?kwV?y? zoFWylR~8?{s`n!P4&jo0!L&v^w^D1+kQq)?gxVrNX1E=YU#art zR*>ml`A7*q3MexISRb92N!LQsq0Ak)u`xGXZieiYStn~O!wME6TsOy_mdP(xmN`f} z?}5GD1}izC(AVjTCpy_gXVpfR9aq#o65ZR)WVVUU2dt|DKL|<3YINlXsDKiupefIq zZg^q4l%_2!i^4u797&1aQv5b-FtiQJRT_63r^9+wP0IuRf!cAHE7m@Tm4gr$R1-%p zPceSBo03L~U(R%35%9?0ye;1fnk8HQoyROYpvqv-H-0Q8L#sf|k^zu;IQsRR|?bM=BT1m}Z1GE`h96L_3y9SvHt1c)dr`-JVH1 zTT7(2x%9Ix^$p@;Xhi4dVjSNOf z^ENDkN|gxWKuMV(&F~5KN%IhZ`)VPb4JI`U#_H#-Wgr$~YF14eE8in!p=ZhvDw~PJ zcR)}6(+#-8EW=C0uQX`jA|sfOQk-Rk$5g@-e9~)VyURG4aVrgNH@wwOYZkyu*3Uwz zS!w4i*e7(gp&+8DP)KtdYL^A!ws~l8e=nMW<&Qt;3i_t*crwI3X-P2a07-?>RNXd9XDLBW*q2;WxrZ7F}A;S~|f#x$>dSS}gAS~W>e zjAhEf6*dc)Z-iqTL&y0>YaXf1$2g2t_q=>42WS-B8%$e({S%;#<-COlqH0zRenBr8T{ir6mF z7m^m?;mV*%i|}xjrqPN;Mw_N3b;|AfO4U6)+URWQN)l=aNRL4NOuOeD=Kb(;%Jujb7xG5v=3)yJ&l$z*{A?$ajFp~G9EUo+rm^7K>@v$YP2>& zb0JMt{>6~M!Gm-YBR^7e_q@tx&@-uM9dOtj6U0-_Qdl^a z-e6#*uK+~ZS}9$+SSm$8eQ%&N%z+PBX0+E!vT4CGqorX5&LIUD2XSkahT9nLr8Kb7 zv1Jf|`eGSswRjk$m}ya&qXR(FcA?ZU4OaJo?mpCIDdKz|gj;Dr$PY{u&$vKZp9W9x zp`B@{{7X|&k7hk$3JWB-nN{+}bR*N=_!{kkEge&Jy|}nr8O+B@lhTd2R%}<#D``DS z0kzP2n6@BCn(kmaL}7vfDI+S^p}O&~x;tqyq}x^NVOwrkJ2D++Io;9K(1l#tAyU$F z&n}03OKO?A+=w-}#FWQ$DL?_nA{sBPs*}5Kl8}Kqn!4E}MHvBfy25DOC^isI4Oyk@ zspSJkdpt18hfzmF=Cd60>IFUXLC4uHfZoXVZOj^Gg+Ln+Wzc&Mz-+RCbu$30lPB3r z$$;CgXFDh}vg3#(r$JRyjbKrXL+i%gYc$gzG3d+^BRI}ozIY?!!B^eZ(T=&7F?U%S z@LlvVg4s)auF^pxJvbHXbXi| z(V^c?-#&;Z*-8yp;2}Y+x&jSYEF#=j5u{*pYXv+obp>2Vl7z^5c|ev2oy$X;@e>A5 zc=?qsiBrS1L5DU(e*19V6Z?6TbVr{lla|d{x@^k*a;E*46~;K~`H<16dz#QXrOnAm zi(ZVg(=n7^FSIg1H!eCpUMtKIS}KMb3YVeTb{u%}A!ENKvKR|rIxU?&BW(t1Gz5bvuECmXjeYWB$K4giXNVr$Vn<|;qI2BpzEHv ziHc6!=#h4!qQi(r@k?8@jColRM(dHOby}t{u|RO`+X=yFzz&|_yA_jo;IeS=L8lCr zufm@Z#`sc&qcfOs{BL+Dn9m$V<8#@}`+N>ZV^0`pfa9jC(|nsmc4MRi$WjdIIw?YI zDAP3C6?%5HG1M}V%2yl9(Ks#2G;HI0HPYo=PC%%a7MjplYqZomb#`}M%b00k^A_pGEVUiG>4* zi=|0fMqGexlc7a|qySksj-QP-W*LFiXe$oI>Vcn)-pVpsH{*nwshBNSDb^vEE@v4% zns8j{45hR^m%6Sox=I|{S1Ei-KU%uRXd`j!O}dVL7ltXF}l*DHW{#3XJBAYbjfXw-PEkvPa@Dp7_rZCb*sR2b@C z_0wwO;Gn?a)IgMCBab+GY^~9zUD7tCXbGIK7o1Lo;T{+91AR4~zFli{4k$3m3$S(c z6Z{G&VjbS56;S_ma1D)2i(wz>1Ndq5;5zhmG}^ijX_L*^f?k?-8oy*ZhqMwM=C5ns zS*Lkx z@k2CiGJb9v&#)(}RC?)AjMh0r4m#()wUK4L5$Da=>(i<0dZWFhD4`kajn49RH*H;S zrpuzkAeLB6FXJ|fD*>pj1q69%K?x1nda@FTpRP=1bJBnuqfboeQz`?tQJOX#KMt!j zKGkV_=zlpzXZfg>O>;kbSXhWF-bp{?7zRNemVaQdZuHH#(6&B}d#v}@M8g1o+A8Tq- z;G_{wqfH+s*MPH+H*2kyaHpkuJU>~bsC=-2TxwisYR;OncBo+XIE^0Cu7&iI(`eoF z)c62pv5zapM8a<^<1fGEof_=5hrW3 zO61#}zcN$HOD5!eaw#I8zvdYuWDQ>odcIQDnzr(I^a^OwV@5ZwM{^%DTJ~m*%0L>{ z+FJ{@glmn;d`i5FypkAS2z+F7Z>{;&P|Z3U60mcLWe_TiUG7alVLhpsPBN;9hI zvoMSdqohDyO={A3hLr(2xDC+fabr+a=uX9}a37`z-h>+?92ViF`CD3RIIk8eVZ(#b zEFj6Gi+@YgX6#3GM#mn{2ggpYsIq#FCSragk6I~DDzLDd?*CUpB|s2PqnA;=|tyczDdfv#;fc0@71Eb!|J2V$R2LOfqnOgi}Z{w>BJ zvyQyEMU{G+twvOHP8yG-dy#^ZrkY7sZlIZ4jr;m=ybt1Mn(@a>*7<-Ih&W`1FPrdR z6ME-SqisfRK>Ti1UP)N4xEsHCdVZVHy8Apgy17NW4?h`ZuR@=#4B=aD97I%y>E<@0 zRnsCfX(}4eL--Z#rcPwEAK=EPL|C+E@#Am+9)k$90RH0}%NC$)zS#ogDFd;Q_!^x@ z+sNoDtMY>;O$un(B8&3=?^XHdFBzI$a#H^?@+fM%F?O6OW*s04EHuNORXK}OjgGJ8 zy-77x`KAg*rV6J_h;saJyV>H*Gil1qxJydSHubeMwwG(GcRi2pd%_qw+EmAQOsN+S z$d{*jEG8`6TMJL{)BKh;ENhVJox)9u-4t&}8n)7?$E20&6@Rsk_~gfE(38fn1XJ7` zQ+$3s;#rJmKRcA(d(s#g@X9O7tN6T{D*Rrdu&3Y}=S_4<3{Hc=@BAw?;wiI3Tscn_ zeZgb2;wg+9um<2Mbj|0zsyMyd(V~sRio$vH6F4E(c-m;C7r#nvo;F&JFJ`JnURtB8 zKALf{q4s5Gi}p@DPWCfXdxmAjw@HuZ%EAt71_Ywr9`7ytZ^fn(g^$s;r;Vtl{B#un zhp$OVE0#S*A3bdh2-y9a(keBORZVLM!8sPJ8ba#uEjof9O>vy{@?DgJc(eN%Y?TjPOj^v4ApV&!{533dmQ70u`>? zHA(_4Uv@KoA|PKI%y3vsAFX9Vu;#b7VP%6dqe!Ic(sg1@BUGBMa|00hryt1(`2X)P z71nlRpT}w24r657$Ccb*K)$n^VNW+^nUqvu*fQDV8bW`7k{vh6q@DuE71|?acoQIV z!(KtzgWChh`8{EVuK@B3SbpBTOb8&u%$M=Cu0C*8Y{Gi+8RMi>qV(PxC9L@>3oQUy ziJ@kAJRrwR17sl!09lDg&F~w5|1U}u^q0b`S`J|D8vR~ioJOPe-cGnWnn{4nDf`^@ zP)+Bz5GwLjP@p{EO+dabnc=z~vEHQDb}ITHK)yzq;Q)_zGX=D^Axf<+LoD>rvqrG& z!1h7}8?Ypwc06lD$wtdc$DYN!E_&pL<|;~UqY@JV$oU(#k@`Gm^pP~% zO_~&wW|c|v;zrs8nqcOWYSI)TjwPR>E>9RC^;TsiZ`F3e(v8$|moYNhtfxc zH1c%8RLa|B^bBy1knQ+X+hqLW>D(@}>+Uf1ux+A7&l@8~SMKNM6l2u1Cg>OjqI=v_ zD@0WhIl-c-I5a69VGGw#o4hN%%Jg{IMVsxf6-=~fev@#57VUiA2nic}fLX$o(yU?Y#{%ogvab^yV8@U_rS>43iEPQ$uKNVK&t8tt% zT=MmFOdI9xHd?E$+Z@`z+h{M{vSf~Ow)jnSdAFgubEuYIGJP@2jJBI4KNXOD@zG5* z_a&pN+4cJ0OGZz58&Bi*7=cX-->L0R1)J#BOGfK>lgXsw|J6#VqA8-qZ@YW(Qoe_$TlAB}MY?a$GGi{^I^xYm~(4ZoS zhz#=4g3^32Kx?S^B*2d*V(R36OlhW~)5Y;Tn}~P~@f@dtrY4?dzHD@bk8FJz&Jq2d z@&aucHWK0&O(z+>!9=G%VA0;jZv))~x@nP#&dRW8|KV5k9(8>Mn=Y=J`1q9;ZQv@4 z#$k)M8h(T|_tKhA^wYdQ)l;}dPC}W;o<8O`d=dh*!iO!|Is6jdSDJW+!=k*kDA3)O z-UoeRq)pAXX#dN$^wCny#3n+8amc21HriuYz_!++Ex^y+8Lkdfz^g{wpwG?ZwsmOM z@l#%=BnZ5+>6;fDveb{wsK)rbnPe_&ekQHwSLKRdngsu9wR(P6C&%`YKZ^Rw7A z?^HSP`R-Mtx$OQQL|G4luX5FA+pH?C4x6dzYsPpPpO5&ZW_*$vKV>tme$5yoqc|EtcY@7H`>RHI;_%^ zY_Vuvx8k%!8CK73HIBY|9aY09`s;P0ZTEvr!G$u>s(Fz``vAXUlS|!j!8Q#25Py)y z6&itIGAUPxY}ca=w3_Huh44iChf0^ECvmJGelTgF(G!(Ud7;s&&q~CGb!?!uN{d7X z*@h8!3(X52#(fMstnw+GjjdW}TIF3pt`tf))1WtuX`R?13lR?B$pTDu_>T8UZIGg- zctVk$(_-7Ygf}+RCvO-ds)s+7FIlwSdw7^b?e`i{`lTZ@X0Oq#>7^q|<4VL8zhcpV zU%uC98Pn@1@CHU$7aMDZuOt8X4Q3qVm%V0@xL$NM0+Vx}sq&Q3>dx`3jdQ{`2li_r>R+hntf`drGWl0e$@ki&oDea(@1Ni#eZ(mHsa?d@(kCPgILcTKsYO~ ztGKr1Y-R?ub4uZl0QvgX40AlLOs!)*IbZ?q98@Af?=NbUs})8LjM?5=QmI9okDt5W zSSxJO*uO?!5YLJ7j)6-TsX75P@+#O$ zxrdDAneiXhw(`4Bg6DksDl?h+e5aIX3dnaY&2UFRR-DuDbrO&>pIH!WO4{Jl`5^H6wc15UT$kQg=U&cJO;>0eQt(-G~+o9Uk$f; zD792o9W1;)rPkM{lx>@`RMy9}E!7W%2jv(!?6V+$h*A2NWd8VQ;4%5C)Z+bF)+Q?->^k9c?Sm6$@Wn@Z?qN~tD*TudDR z`RWVELO3m7TLJmbvlv;Cy2|xhWYjLcLeo+ub(A{gp;VG7EW;H3Fd(b(m>K>bAS=UZ z`1%U4u1`U5z4f3KcwDLmW~Tg`a=NyKD#4JOokCO4iVMmLd4MdK;TBuVl!;PGHYM%#4e?-9~c9gb7s~T3oXNpf7GNu@*L%UV6?n*um-yJE`CAVmHYs#2lbzg z;b;P2*T$)Cx;rr-SdCxYc3NFzG_$$!7l3m%)U{zq&(Na7sp8Rn{JhBM?=P7(sAfji z!$xS+ut+RQnHf~pG&LZ_3ewr#Y0P1xM`r$~>f``+16GE!vI{snp!>>#7C3rm|6HIIL3xt;OsJ zOe3_w@{Q2y=Z+&r zGilB&WQ~}h>g)P3QQ0hzvAoKpNQ zK)zlD+B#(yxIn5zJmU;w`uVNE_6OeiB>7);n%ALa@FpM}4$jS{u zF|a#~0_4kcRxkz1YP(c1`vdaz`Y!tDm@%N4yJm^*hE**1t%lN$Zq)L)(fm%XYJiBXM2r|U zV#J8x^kDP^1|&F9B4oxM2QeyIQBk6UOoorKqk^^$N6`-<$So>rRMc>9kxK-WfLsE^ zF^nLHQP1;MRYK;hS#$pR*82WAto8D|wQE<^uBu(TD%Jb9OVb1M7}5Os=#EdU^Sd^c zZ&NdDr-$c`QRIi4fwcX>%Rb3*T^OHhND;_l3XV#+CtXMqBN>9KY z&qpVHYW;pdQ@%TI;qQc7-4#Cv=O9rV68RVXcGCQS92B9uQjdfOVef7}ymtWnMLM=^YRA-ZU8^za(1`{1VK{=oP8wxa9U zT2Dr$@7RHk7XR(eb1Cz$LackpT5DMIvew(2Ka}s4^j|Hz<}IK39I@<*wREU!+V1fW zsG}eL`(^KC{-6GbJnu69oBtutrOdz9%@h6YI;&4}A-1mJN0+re^T5B^fl<{QIMlS0 z2^IyP!lP6Yoy-c_^Z3C_lG%Ysg5Dx)nwPVBX`-}_PM$NEChsMIrpiCS%v#3}Kd5P? z9re60^z`$>%3VIQ0X;uAI&{4?G^jchqS7REYh$`i>A*u)d(({#bz?)_W_PZQ&RlPW zRO8-TZ*{rEElL}TLT~qUiZv<_=@x2hdZ+)v^iH_b1RC1cwu`r^a$n9lFHirmo>y)x z-(`cNlgi`Bj^T z#jV?#)33kKZ^ogK^P(%_^xbI6zxN`)nGo`uJ1MmOCA9PLJZXjh)s^xb`TQ5t8qSOM z`mDwCT?T3U(Q{4fXzw%aFKH8sCVy)6jOKo3HSQng!4_? zh)%B01gSdi8}mNAG4JKQG50!rF=kX78gcb3=5YP|ncH-A-)s%;rDX$J)>&^V>%D;W zrg-a({(iH2NM~)fh9o!b^5ijo^CoIKq0wWTtu&p0zMoqI$7qdl9f-4h=B(MR!%nFZ zo-)ple9UmR$epQAY8#Wf`f(GS@&ph0GfW`2l%FV`G$j-LW+y7y=f3GZJso}hbL-sm z?)01OXuUVPs>O7RK2rbq-0I(a?uOj&H}|2H^P&T{a2HY$3Ux3qJk#{ZE%WtApXKY3 z@RW~hCek+Fxmsp#vHCh)v-Kqy_gwyS{aN{t-+X{JHl4G?1e17sQ{I1mu0Kw`@WXy{ zU|#g+Emrql|Eg|$_L$#vobKe=vDxZA;7t88N~6M+*3_t&D`)VRFETHkbfVb13@UR_i8T9R$_GQ7gnm@9B~rn;=7g~q2v8w!c%bxt%)G~LRvI~bkFmwzPs@J_2s*HZE)e&en+AOBBlEt$$ejPJD4Mm|Lr-T3ur zCgm|*gi27=CVqcFL0-@62LQ7%!OSJS0s36}@?v^>C^wB$A-%-!n%k zb_I`WEz;^pGZlU!UH*5F%>NxE^Z(}{nZI(X>HoVyGFP73ZT-FViaIN?yB#J7q$FU> z2~>iQFuHRT)e)HygP(dn4o`UAbTZ~rq&!*hUeA}n3eStRo)6Gp z;kBsDFh9F@XoJ!Jz z@OGpR=KT!ypbQ>HTDSoY54t`KW1bfeqh#_oz=3@@ z|9G*nFYiq7V#jmoCx#EgGX^jZAAo-u!1;G78WXJv9g+{gLC870&0uS1i&O7aKEO>x4~VN8(+B=v_d%xEKX&0)=Fjqaa@V6s6;v zV~X&KIm)<{0?EUtQ88Y85tZVbgN^VCI!eEc(}5Rn!Zr7C#EC_*CeiY59TN}Wd@n0YgW#%IBoQ8~T>UUC~dMmSFvyrO`*C11ex z-xTN5G6qPoLF-TvnZ&>yv}ky7G%CaA!_^Zx68JcrR!Bv|m%#kHc=M|~@aB8?8CD*y zy^ozyet7W%9E}%g|C@t_UbLK`p(E6ZbiDWz%E6lwD$t`eD0qg=H9a14&wUX3rgLgY z55eCcHC&1sT88RKuZ5i+=N=K`h{8W0r-6b$*nj4_XND;wNb)(?$Kc_4tcdgm7^s*Z)B;JVTlP=zaD)Hh%RE-x8pC6B}9oMU`*^blTAM0c#DrNoQAO6n6n0QHzse0&66|ABi7 z!XI#cqf98$NDO>PyNwrzqH=r~-oK9V!0;t-;2z%U<3n)45jKvmfKCV!!9(JxdhUvN z@iCN!FM|ggxI*v^aMDrghfQ zT)a2|Rp8y2A=~|}T){)9(0XQw?RxUJBt8K1&Y_DnzGX0xf{ON{#Md~y@EGceH@zqj zO2b#d-MuLsz81a~WL>XQSK#0JaCGHuOnKh~6Z^j$Mz|BHaj1no&UJkdjzp1#>^=Ml z=@N~@dsA4R^b%N)bUK1qk8a)?iqMdf|>q? z&C@C-2bD8J9Dx?#!|+w4fFDtTJzMZC1AG+tb0uA=5*Jwx$Kiwce7FaN@C|UtAg%~} z7A#69v-&!bg!k=UqxyOuSfx)+-Zq!+R`;9R{<BM$h~E*rXd2%f-ot59 z*w1v7O1k(s%EC9t`rz&PBsQS2Oo&HO1aB^*)TjiX1#P5yPy*lgd=>opatfsULlVrh zS8}un%u$HR<=0T4H(3@=9m=U%%ChhTQhbSc&@L|}1it!GwCqJGc<;^mrN>>H1&v89t$D8S%FN0lg@UE;Ixc)~nQLUB2vxakB z;sfwXr1UKK6G~mqUfxK>89_nuA;>4XO?U-)pcBEtv%Re{(PhGZYlEY#dhvUOaLeR{>u95jEh&gz;{A0IophEzUPQ zH=hFG#f%Bm8@%`k%EXH^&yx963jllI2xI^sS|KK3gE>%PzpX`V_rhK%qn2`4z5vVh&8AXUkm5mNwbNM z!QDugVl7OX=+$I64^=A<+#pZojVry7)>P^66bipXq3>d+kX_6~1*T{cmlHk$`%HG5 z-VprJ^QI`l+>T0>2X01&2#;F$^6%V2RKWSa_bM{%aJO5~0DKP>lfMeCMQYCDcXR#k z@Fq@&h0r8NC>e^gfcWh_98$da9co2XM{}GAUhyK6?&XTWi_cD_&Bu$cp=tOE_zhCq z&;V2Klc)U;VG5lI_6#~vCM+OcRLqgUi+@BF_|qXLR7TS7zMslSy0{eW!&kx2ALIzB zeoRX+1CUNx%dixMr!2Jj&%DPa2P;ued=;GduzS0Vz^+tM=hg{7M!GA;;SQw3SPSDN z?qL^$k8&GkJuTx`6xVXYV_bHmiv=hjZ?}w7A%Ml4M5W9Slc&?T;G1JqcnVVN%3idP zbg|dt31+qOz@~r|vHS^+1nJ@-WZvfrg5RP5UOfH;=RbuBGsBBg!7Ezj7c>GdUQe@x+&xGL-LU_4qae2HIqz1 z7(qIur{hs9CsyV9IW#`x5#K`XKCm3VkMD^WKSm*Zb6g5uF)PcySME^`YhPAU+8%{(w^P=2>=1;y%(Gyh6Hy zSnO5&H-ZUqEh@mrVcX~3OCC~? zdU@b=S~~KGGtn;Phx?GOn$y8A40N|J!F>G&m7F|c-$gqAt1S%8ElR~VN5J4)M#m^h zCRjnkLb_Okij*JbEM+fwU}+AMAzk4zXD_2rc(DM*wCr+@5YiEi*_b&i+`W&%Wh-6Z z99Y8w3b6T2CG7)VOnQq4QLP9jzwPD^!gcSk*QCc`_PbtzVBULfo_v^E<)(+=$H;aX z6O0?K!-EDs^#iWgj~S2))}thR13dPjyCU-u6>hcb1Mn0|Ay4hcG+dujZ}1T~U!KdX z0`{(UFS#Hb<@x+-uK%4(D3gA=b7FTsVLa${oTCj~T6q1Ow{R0}J6=Eab&k7#L@GMx zCp3a|1^JIP!Cj9KL0Uh(blIEflEQW@DJHD7%{W88(H8h}%#u z>0%u!#4FT8(HX_~mQfmt-gp2tkS@+dt=3Qo*nhjb_aV60^A+$0yT+Z-GL%Ch9&1q& z3yZzK;FREl@C~Gvs{&rN!}TpgJ{0<~1Z9w4Y`v2sgb%>Skd{>#$DA)|lt{N@n9q5fT2URm{D_-B6W)!KKLQsc=aj)yNI%&$!Yk@Yw^=w7^Pnd1)8HpanX2Itq;|U= zcKF8i0eCY~{#>{h=?Jy^JM{)BeLXz-E!TdW_r9>-Q7SKh*h#kV}a z3Z9c-`tw=2Jy!Zxk&L8}#IJ(w=n1d< zV(&zHLXsYY3(*w3_|lm^GZ#M}ov8P%0A;l>Iy4Tr{}xrr(BCy_whz~@`!hb+zK9nmtRc5w^5(q9hdl=%ib=;{D5kh z(Fkv*({epN7tTT|Tp2uww3l`86w;nG!uIK|55QEU{9=~p#R->k+BmujJJ^Fl_{AB&%(F-fSHR;#IDe$`VLUUCp3X`S!R2Ty>8s!oFFlayGh>hnq%PridcF`o z=J|zi8`63j;B8kjNEYkKztX10Uga~>nFvFZ#o@vS;C2+l*T6+XNv9@Pz=UhvBhd=x zd0t%TdGYLQw+kU%X_(7!j(wrmC(v@_c#&uK}t`7yO1ilX!pCp-J3Kx87Wf{oaOm4xYF~La1U}0 zBOEZ?T{a!YkkZ8oH&WYaCko(Uq0DOfG=L<<+0IwLsN=eUzv&Oo6Uj{dha~r5*aL8?LfimGAJzoZwp~Nq^ zf?&;Uoc{n5wM^_mN%#hM6eZ)c##2#H5HH41Dt;O4Fo7l(pA0`iHQfEG;Q>^=lPd*w zz1{W6a6Hn{&4(*IAGa|@1@xL^MdDwPGL^$cNQX4(4qCT6@w{AI03Vs?p1zNKm+|QB2i{&VYhqgKE0e3|Kcs0_( zS@1)oL%JG%=cONm=RfG?;j4`%@CdhRR+I-{LCzk-YNYiz^n4v0_b68p`KQ3mD5Up4 zTQE00<}MtDd!FSylc@o&SU~S!d>o$j8hgEm%N6D#9d>c{>s-_1nFD`7&M6~Y;94Y~ z4U18nJTqbE3b#E-hGCokdQw@!Fm(xSH<{95gM=4{yy>ne6GlD18kQ_`+i>wqq>a_W zi7R|2i***lEl5X2{AwjPtCz?F`&6?0VxIp~Fegwh3!H?V-f|CJ5*+CH5S;A!A~@gk z3*kXjuG$XIeVgkWp8^lQ;})(CUi_|md1u3iQ4x8nAYT`?O#u^plha&{oO2KP%&6nV zmyq_1?|GUb@4G$>3y|`QQ#@Y^%RMhPYMQ23yu8Xi5}B|Ttzx^iRW=*`z+Gtxd>!f9 zT?n^%z6N&vkjhAUGQ0(4(}w54d8m=}82kkVNjD!6fPm<@YT~c~soseDSNoif33c#S zA5$Pb|A#SuLE6hXaQr83N%P?upStOZa68huZz=zOW3Q+s5PVhxAVLg{Rz7@20Y;e!bF4%vgyRkHwxQRU`lQ<7m<72Q6si5M(xa&i3s^^R0 z8l-}*hc|uZUdCZqj z9e%ad^?a7roVU%r)$(ata~IM&U+TARXE!{RDf@olU&XMx-fs%=;?)7halmK6)kvilmvr>=#)CZYJEU9lF&OHE z*vo7Wd~R`4GurwZ<}dM zj5yzK246r4@j38Iq;|U&<`ahC{5b|=NR=qDpWp02@`e5V=Boj2{=_uDxeBSdErfv! z-3^N+sDgBHKhm;w7utTac%a{GVMYauaH78d54(U^EwwBvnF zGTe`x!vY^2=KAUIvf+LcXQdgi?~T-Id5 z&1lwx&xT(h)uCN5Z48AaUEG4|@I8OyH+z1=`LAYT9}}a;y61a7{Lb^o;Kp(8B^QTd zCa{T zyO6Dk2&Tt9)M^&!2_K$9f$$~p+^GI9SHhL|bA;sK z@M)YH{0O)oslqnEKR)E1?kNv({cmGp7ny2c!NX*tbrX*xgFgvloF-LFv3m)Hqtf++ znMh5wxCLov#QI0PM&?oK2hxK~6|6znP2Agd03x3-{Xmm&{=?Px?(i z6vT(%G?aobgHe=VQ=Zpot{TZz>EiXC*9fi; zd0u?g^EI&7Y)&7~2O51-BX4S1afat(unIN2!x5TIg{ozua21sTw)sEq0%nVWp_@6IKc}U|NVDoka6jmcffbiQk9BEL3YVA){fpRMHsK$W`(# zxd-qQOf6{|JWA82(HH6YaRI;Aa2Q`Y3@{6`c77Fqc zj2MV=P2)uk2Bb_Hz~^?)iyFU2>EbJ%uYei~$5|vRn*I*nQQOOowL_m#Yp%sZ4p4YGu8X`jJ8g)UV zBLsL{6Ezxw(!~*;7gs;e`L83BMm5;s%@BX`yr{thw6J&)Qb9$9{VQEmFuJ^`hnHb&vTnh+JDEia~fUL5IpQ4!I~Bif!973HjSai!-)Mbau=Jm7gz z(XO^;$OMsmKHQDel-9!WFS}lv_y~r$1T%5nSaB=bC(#2(q P%NNI1PQIb_Uw!`tZM29H diff --git a/src/MiningCore/runtimes/win-x86/native/libcryptonote.dll b/src/MiningCore/runtimes/win-x86/native/libcryptonote.dll index 6973198b1e0267ac91d8b378e05105f27fc03012..1f4803a9d8fac460b99cfcbe58a468038c6eac97 100644 GIT binary patch delta 42590 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%0D{ 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 delta 42146 zcmbrnd3;UB|37|bPL>;q;3tvRO8#vn>-<7f=YF5KT)X;faU<++vEh= ziVT();5lC2PMbL^(s$abRGq;;vFHMgA~bicthQ-IxEyNJ)SWN4)#ODtxEhsH(_@}7 z?Z8e+!$)|?<7`^d57)|hHm$OrfFj+gG^W~wKi=D4rP?JxX~iHdRkTvlrBP~VV8)e3 zIf)Wm;I7cl8ut`MvB15dmNgz|_c|VI|2iI`76_j04voCCjBR^RRr%{bqt}1Zj=kT& zr(F}p32~>YloG*Ztm^&--gxIf=}VDs;M1ehioI<$$FHT?YWNWE1}arrwUBtnP5#cd zx!o>AkGaXUw#}4ip_}Y&r_K5R(7tX|m2+OLWGXUFE6#z0pqWIK7A5fh|G^vYgujuo z@zQ_NCwsktw<_j24PtwHK`+%Fh`r=WRdSV`mz_xLb(Nj$n<>#eS2@JKWwOIdimOa!RDEQ*k{X)u|Equ`y zzMTr+2Jl@IeD;D*;P^Oa*`V;Tvc5FBt(EWp$nUMj*8I*kl#?Ag(3S1vJco$J94)Mi zuSMhNE`(i9<$pWM-#WCR`yyr1AhgMkz@ijSW7H(WG2JPNuWBSmH`w7>hUzzSmD*_! zNg6Y6c?0?12EO!hFS)s6Q@U!69PSuITR)e_JC1DpFws_{k9nZwc`;O_P2xVa^4E?b z^uQYC;zqsN=^iC7BmvedoZYRhd*()nKusX@L_j zu~SN7THwlWX3D-OF>{i;xm=+M_OiXEMbp2`__R~XI5JSBNh?E)fB8g?)OgXc8S-dN z3#wWxFVUQ%x(0G&qY>1zo4m164_f0OUvCuBtxQ6D{4!Q|fAp75D*jytItQ#VN%u$} zYg1|$jTX}KKf2a$OV=d6#X;`ecsLEOkykZN@_eWkXjnn&*e`7`sF*A8XPXS7y?e<{u3p13hNDTN10mWI=D-CH+!h#J(zROp&>xsQ8j_;g3AN>?SNtCI6$_T`VL z;G-0PDf2;oOr`E`zn)nV+vwk~F=alKO0|4(CppHaD;-=bFZ1zi z)Cy?e5-mv2PVz@S{?z*|`D>qU%`g0`l&3USOxUW@SXURkF4(D3OL*B-xrOgqy0)wQ zg>MFJ+Es4p*NskpA*cFz(pgjFMSk+eagO_JRMhSG1tQ=j)i7`o;PcXoA4U^S@VRZE~xmTcPqql&z z;1YqX=p;`K^r7P>%I^hskhVW2Y<4Pfb6fdX(OXw0x&Sw!Rrrfn^8LVW&YwS3Fk`<| ztN57-a&XH8`#BGYn0uAhZB-y|ZP|gI{z)!x=}Wi%B>&m61->SjZeq1Sc4+@|) zkL1*#rsRRVD5wqX_dwng)GFwgyNXnO`~&Xr6I%Jg|7iYOf69-7+~iHcUb0`SH1|u@ zW|l<(Iloor_phyT)N(_$yrY$mzp#T{HF&f=ryZKiXZxYz6ywM_?}_G*g?&`E>gD~Z zxsZArLw+y8vRkm|ZFYS{I{9ayg^YCi6LH zWY~$`CQ_ldYAnj&{sx5Kf<5xe=TQHSc-$RR*R7OcPfD|nH(_mZY4%
q7aYP68( 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 Vq(Z8JCp1aB`!~|@5xDO6{TGaHgg^iQ diff --git a/src/Native/libcryptonote/cryptonote_basic/cryptonote_basic.h b/src/Native/libcryptonote/cryptonote_basic/cryptonote_basic.h index c4adf1fcb..873bf5374 100644 --- a/src/Native/libcryptonote/cryptonote_basic/cryptonote_basic.h +++ b/src/Native/libcryptonote/cryptonote_basic/cryptonote_basic.h @@ -418,6 +418,22 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; + struct integrated_address + { + account_public_address adr; + crypto::hash8 payment_id; + + BEGIN_SERIALIZE_OBJECT() + FIELD(adr) + FIELD(payment_id) + END_SERIALIZE() + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(adr) + KV_SERIALIZE(payment_id) + END_KV_SERIALIZE_MAP() + }; + struct keypair { crypto::public_key pub; diff --git a/src/Native/libcryptonote/exports.cpp b/src/Native/libcryptonote/exports.cpp index 5555ae8ee..5081474df 100644 --- a/src/Native/libcryptonote/exports.cpp +++ b/src/Native/libcryptonote/exports.cpp @@ -76,7 +76,7 @@ extern "C" MODULE_API bool convert_blob_export(const char* input, unsigned int i return true; } -extern "C" MODULE_API uint32_t decode_address_export(const char* input, unsigned int inputSize) +extern "C" MODULE_API uint64_t decode_address_export(const char* input, unsigned int inputSize) { blobdata input_blob = std::string(input, inputSize); blobdata data = ""; @@ -85,16 +85,34 @@ extern "C" MODULE_API uint32_t decode_address_export(const char* input, unsigned bool decodeResult = tools::base58::decode_addr(input_blob, prefix, data); if (!decodeResult || data.length() == 0) - return 0; // error + return 0L; // error account_public_address adr; if (!::serialization::parse_binary(data, adr)) - return 0; + return 0L; if (!crypto::check_key(adr.m_spend_public_key) || !crypto::check_key(adr.m_view_public_key)) - return 0; + return 0L; - return static_cast(prefix); + return prefix; +} + +extern "C" MODULE_API uint64_t decode_integrated_address_export(const char* input, unsigned int inputSize) +{ + blobdata input_blob = std::string(input, inputSize); + blobdata data = ""; + + uint64_t prefix; + bool decodeResult = tools::base58::decode_addr(input_blob, prefix, data); + + if (!decodeResult || data.length() == 0) + return 0L; // error + + integrated_address iadr; + if (!::serialization::parse_binary(data, iadr) || !crypto::check_key(iadr.adr.m_spend_public_key) || !crypto::check_key(iadr.adr.m_view_public_key)) + return 0L; // error + + return prefix; } extern "C" MODULE_API void cn_slow_hash_export(const char* input, unsigned char *output, uint32_t inputSize) From a0ff75768737ced64dd3d8bc15515d48aae90676 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 12 Jan 2018 17:24:40 +0100 Subject: [PATCH 011/348] Better handling for invalid payment Ids --- .../Blockchain/Monero/MoneroConstants.cs | 1 + .../Blockchain/Monero/MoneroPayoutHandler.cs | 40 ++++++++++++++----- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroConstants.cs b/src/MiningCore/Blockchain/Monero/MoneroConstants.cs index e16ac7ba9..40f5b9263 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroConstants.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroConstants.cs @@ -86,6 +86,7 @@ public class MoneroConstants public const int MoneroRpcMethodNotFound = -32601; public const char MainNetAddressPrefix = '4'; public const char TestNetAddressPrefix = '9'; + public const int PaymentIdHexLength = 64; public static readonly Regex RegexValidNonce = new Regex("^[0-9a-f]{8}$", RegexOptions.Compiled); public static readonly BigInteger Diff1 = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16); diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index e28383e99..aa5428d67 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -211,19 +211,33 @@ private async Task PayoutBatch(Balance[] balances) HandleTransferResponse(transferResponse, balances); } - private async Task PayoutToPaymentId(Balance balance) + private void ExtractAddressAndPaymentId(string input, out string address, out string paymentId) { - // extract paymentId - var address = (string) null; - var paymentId = (string) null; + paymentId = null; + var index = input.IndexOf(PayoutConstants.PayoutInfoSeperator); - var index = balance.Address.IndexOf(PayoutConstants.PayoutInfoSeperator); if (index != -1) { - paymentId = balance.Address.Substring(index + 1); - address = balance.Address.Substring(0, index); + address = input.Substring(0, index); + + if (index + 1 < input.Length) + { + paymentId = input.Substring(index + 1); + + // ignore invalid payment ids + if (paymentId.Length != MoneroConstants.PaymentIdHexLength) + paymentId = null; + } } + else + address = input; + } + + private async Task PayoutToPaymentId(Balance balance) + { + ExtractAddressAndPaymentId(balance.Address, out var address, out var paymentId); + if (string.IsNullOrEmpty(paymentId)) throw new InvalidOperationException("invalid paymentid"); @@ -418,8 +432,10 @@ public async Task PayoutAsync(Balance[] balances) balances = balances .Where(x => { - var addressPrefix = LibCryptonote.DecodeAddress(x.Address); - var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(x.Address); + ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId); + + var addressPrefix = LibCryptonote.DecodeAddress(address); + var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(address); switch (networkType) { @@ -450,9 +466,11 @@ public async Task PayoutAsync(Balance[] balances) var simpleBalances = balances .Where(x => { - var hasPaymentId = x.Address.Contains(PayoutConstants.PayoutInfoSeperator); + ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId); + + var hasPaymentId = !string.IsNullOrEmpty(paymentId); var isIntegratedAddress = false; - var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(x.Address); + var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(address); switch (networkType) { From 80e72795868a67845267a3a17a18a69bbd15ba7a Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 12 Jan 2018 17:32:02 +0100 Subject: [PATCH 012/348] Payout-batch address fix --- .../Blockchain/Monero/MoneroPayoutHandler.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index aa5428d67..2d7d42602 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -140,10 +140,15 @@ private async Task PayoutBatch(Balance[] balances) { Destinations = balances .Where(x => x.Amount > 0) - .Select(x => new TransferDestination + .Select(x => { - Address = x.Address, - Amount = (ulong) Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type]) + ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId); + + return new TransferDestination + { + Address = address, + Amount = (ulong) Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type]) + }; }).ToArray(), GetTxKey = true From 80a154e17de87d06b5fd9726936fffb4d077b247 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 12 Jan 2018 17:42:10 +0100 Subject: [PATCH 013/348] Actually detect network type in payout handler --- .../Blockchain/Monero/MoneroPayoutHandler.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index 2d7d42602..a7fa0d590 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -196,10 +196,15 @@ private async Task PayoutBatch(Balance[] balances) // update request request.Destinations = page .Where(x => x.Amount > 0) - .Select(x => new TransferDestination + .Select(x => { - Address = x.Address, - Amount = (ulong)Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type]) + ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId); + + return new TransferDestination + { + Address = address, + Amount = (ulong) Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type]) + }; }).ToArray(); logger.Info(() => $"[{LogCategory}] Page {i + 1}: Paying out {FormatAmount(page.Sum(x => x.Amount))} to {page.Length} addresses"); @@ -310,6 +315,9 @@ public async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolCon walletDaemon = new DaemonClient(jsonSerializerSettings); walletDaemon.Configure(walletDaemonEndpoints, MoneroConstants.DaemonRpcLocation); + // detect network + await GetNetworkTypeAsync(); + // detect transfer_split support var response = await walletDaemon.ExecuteCmdSingleAsync(MWC.TransferSplit); walletSupportsTransferSplit = response.Error.Code != MoneroConstants.MoneroRpcMethodNotFound; @@ -473,7 +481,7 @@ public async Task PayoutAsync(Balance[] balances) { ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId); - var hasPaymentId = !string.IsNullOrEmpty(paymentId); + var hasPaymentId = paymentId != null; var isIntegratedAddress = false; var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(address); From c3d79c3667eaeb11d628a8f5565b06f43ad8d3ee Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 15 Jan 2018 17:07:44 +0100 Subject: [PATCH 014/348] Support ZMQ block notify for Bitcoin family. Fixes #151. --- .../Blockchain/Bitcoin/BitcoinConstants.cs | 5 + .../Blockchain/Bitcoin/BitcoinJobManager.cs | 99 ++++++++++++++++--- .../Configuration/BitcoinPoolConfigExtra.cs | 6 ++ .../Blockchain/ZCash/ZCashJobManager.cs | 5 +- 4 files changed, 99 insertions(+), 16 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs index 56f2e0382..cf61c495a 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs @@ -68,6 +68,11 @@ public class BitcoinConstants public static double Pow2x32 = Math.Pow(2, 32); public static readonly BigInteger Diff1 = BigInteger.Parse("00ffff0000000000000000000000000000000000000000000000000000", NumberStyles.HexNumber); public const int CoinbaseMinConfimations = 102; + + public const string ZmqPublisherTopicBlockHash = "hashblock"; + public const string ZmqPublisherTopicTxHash = "hashtx"; + public const string ZmqPublisherTopicBlockRaw = "rawblock"; + public const string ZmqPublisherTopicTxRaw = "rawtx"; } public class KnownAddresses diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 80f6918c7..28d8e2ca1 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -20,11 +20,16 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net; +using System.Reactive.Disposables; using System.Reactive.Linq; +using System.Text; +using System.Threading; using System.Threading.Tasks; using Autofac; +using MiningCore.Blockchain.Bitcoin.Configuration; using MiningCore.Blockchain.Bitcoin.DaemonResponses; using MiningCore.Configuration; using MiningCore.Contracts; @@ -33,11 +38,14 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Crypto.Hashing.Special; using MiningCore.DaemonInterface; using MiningCore.Extensions; +using MiningCore.Mining; using MiningCore.Notifications; using MiningCore.Stratum; using MiningCore.Time; using MiningCore.Util; using NBitcoin; +using NetMQ; +using NetMQ.Sockets; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; @@ -68,6 +76,7 @@ public BitcoinJobManager( protected readonly NotificationService notificationService; protected readonly IMasterClock clock; protected DaemonClient daemon; + protected BitcoinPoolConfigExtra extraPoolConfig; protected readonly IExtraNonceProvider extraNonceProvider; protected const int ExtranonceBytes = 4; protected readonly IHashAlgorithm sha256d = new Sha256D(); @@ -99,29 +108,83 @@ protected virtual void SetupJobUpdates() return; jobRebroadcastTimeout = TimeSpan.FromSeconds(poolConfig.JobRebroadcastTimeout); + var sources = new List>(); + + // block updates via ZMQ pub/sub + var zmqPublisherSocket = extraPoolConfig?.ZmqBlockNotifySocket?.Trim(); + + if (!string.IsNullOrEmpty(zmqPublisherSocket)) + { + sources.Add(Observable.Defer(()=> Observable.Create(obs => + { + var tcs = new CancellationTokenSource(); + + Task.Factory.StartNew(() => + { + while (!tcs.IsCancellationRequested) + { + try + { + using (var subSocket = new SubscriberSocket()) + { + //subSocket.Options.ReceiveHighWatermark = 1000; + subSocket.Connect(zmqPublisherSocket); + subSocket.Subscribe(BitcoinConstants.ZmqPublisherTopicBlockHash); + + logger.Info($"Subscribed to {zmqPublisherSocket}/{BitcoinConstants.ZmqPublisherTopicBlockHash} for ZMQ pub/sub block updates"); + + while (true) + { + subSocket.ReceiveMultipartMessage(2); + //var msg = subSocket.ReceiveMultipartMessage(2); + //var topic = msg.First().ConvertToString(Encoding.UTF8); + //var body = msg.Last().ConvertToString(Encoding.UTF8); + + obs.OnNext(true); + } + } + } + + catch (Exception ex) + { + logger.Error(ex); + } + + // do not consume all CPU cycles in case of a long lasting error condition + Thread.Sleep(1000); + } + }, tcs.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); + + return Disposable.Create(() => + { + tcs.Cancel(); + }); + })) + .Select(_ => Observable.FromAsync(() => UpdateJob(false, "ZMQ pub/sub"))) + .Concat() + .Publish() + .RefCount()); + } // periodically update block-template from daemon - var newJobs = Observable.Interval(TimeSpan.FromMilliseconds(poolConfig.BlockRefreshInterval)) - .Select(_ => Observable.FromAsync(() => UpdateJob(false))) + var newJobsPolled = Observable.Interval(TimeSpan.FromMilliseconds(poolConfig.BlockRefreshInterval)) + .Select(_ => Observable.FromAsync(() => UpdateJob(false, "RPC polling"))) .Concat() - .Do(isNew => - { - if (isNew) - logger.Info(() => $"[{LogCat}] New block {currentJob.BlockTemplate.Height} detected"); - }) .Where(isNew => isNew) .Publish() .RefCount(); + sources.Add(newJobsPolled); + // if there haven't been any new jobs for a while, force an update - var forcedNewJobs = Observable.Timer(jobRebroadcastTimeout) - .TakeUntil(newJobs) // cancel timeout if an actual new job has been detected + sources.Add(Observable.Timer(jobRebroadcastTimeout) + .TakeUntil(newJobsPolled) // cancel timeout if an actual new job has been detected .Do(_ => logger.Debug(() => $"[{LogCat}] No new blocks for {jobRebroadcastTimeout.TotalSeconds} seconds - updating transactions & rebroadcasting work")) .Select(x => Observable.FromAsync(() => UpdateJob(true))) .Concat() - .Repeat(); + .Repeat()); - Jobs = newJobs.Merge(forcedNewJobs) + Jobs = Observable.Merge(sources) .Select(GetJobParamsForStratum); } @@ -380,6 +443,13 @@ public virtual async Task SubmitShareAsync(StratumClient worker, o protected override string LogCat => "Bitcoin Job Manager"; + public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig) + { + extraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); + + base.Configure(poolConfig, clusterConfig); + } + protected override void ConfigureDaemons() { var jsonSerializerSettings = ctx.Resolve(); @@ -540,7 +610,7 @@ protected virtual void ConfigureRewards() } } - protected virtual async Task UpdateJob(bool forceUpdate) + protected virtual async Task UpdateJob(bool forceUpdate, string via = null) { logger.LogInvoke(LogCat); @@ -571,9 +641,12 @@ protected virtual async Task UpdateJob(bool forceUpdate) ShareMultiplier, coinbaseHasher, headerHasher, blockHasher); - // update stats if (isNew) { + if(via != null) + logger.Info($"[{LogCat}] Detected new block {blockTemplate.Height} via {via}"); + + // update stats BlockchainStats.LastNetworkBlockTime = clock.Now; BlockchainStats.BlockHeight = blockTemplate.Height; BlockchainStats.NetworkDifficulty = job.Difficulty; diff --git a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs index 952fb7905..7f5acf8b6 100644 --- a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs +++ b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs @@ -23,5 +23,11 @@ namespace MiningCore.Blockchain.Bitcoin.Configuration public class BitcoinPoolConfigExtra { public int? MinimumConfirmations { get; set; } + + /// + /// Address of ZeroMQ block notify socket + /// Should match the value of -zmqpubhashblock daemon start parameter + /// + public string ZmqBlockNotifySocket { get; set; } } } diff --git a/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs b/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs index 11cfebc16..db5d00fae 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs @@ -37,14 +37,13 @@ public ZCashJobManager( }; } - private ZCashPoolConfigExtra poolExtraConfig; + private ZCashPoolConfigExtra zcashExtraPoolConfig; #region Overrides of JobManagerBase - /// public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig) { - poolExtraConfig = poolConfig.Extra.SafeExtensionDataAs(); + zcashExtraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); base.Configure(poolConfig, clusterConfig); } From db8cab8febd42db23680dd8255fda02e4e2ec2ab Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 15 Jan 2018 17:10:09 +0100 Subject: [PATCH 015/348] Adjust Lyra2v2 Hashrate multiplier. Fixes #168 --- src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs index e90a5ffe3..ee1a85d91 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs @@ -355,7 +355,7 @@ public override double HashrateFromShares(double shares, double interval) // OW: tmp hotfix if (poolConfig.Coin.Type == CoinType.MONA || poolConfig.Coin.Type == CoinType.VTC || poolConfig.Coin.Type == CoinType.STAK) - result *= 2; + result *= 4; return result; } From 7690e50d8750e16383ed5acce9c8b92cfa72ec7d Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 15 Jan 2018 17:57:58 +0100 Subject: [PATCH 016/348] Made RPC polling optional Update configuration examples to be closer to real world usage Added no RPC polling config example --- examples/dash_pool.json | 6 +- examples/dash_pool_no_polling.json | 93 +++++++++++++++++++ examples/digibyte_scrypt_pool.json | 6 +- examples/digibyte_sha256_pool.json | 6 +- examples/ethereum_pool.json | 3 +- examples/litecoin_dash_pool.json | 6 +- examples/litecoin_pool.json | 6 +- examples/monero_pool.json | 3 +- examples/straks_pool.json | 4 +- examples/zcash_pool.json | 6 +- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 54 ++++++++--- .../Blockchain/Ethereum/EthereumJobManager.cs | 4 - .../Configuration/ClusterConfigValidation.cs | 2 +- 13 files changed, 157 insertions(+), 42 deletions(-) create mode 100644 examples/dash_pool_no_polling.json diff --git a/examples/dash_pool.json b/examples/dash_pool.json index 5e43045ad..603701bc7 100644 --- a/examples/dash_pool.json +++ b/examples/dash_pool.json @@ -8,7 +8,7 @@ "perPoolLogFile": false }, "banning": { - "manager": "integrated" + "manager": "integrated", "banOnJunkReceive": true, "banOnInvalidShares": false }, @@ -53,8 +53,8 @@ "address": "XgmfWd5DXWGcYhxPcDPUJk44Cnh2e8tZk7", "percentage": 0 }], - "blockRefreshInterval": 1000, - "jobRebroadcastTimeout": 55, + "blockRefreshInterval": 500, + "jobRebroadcastTimeout": 10, "clientConnectionTimeout": 600, "banning": { "enabled": true, diff --git a/examples/dash_pool_no_polling.json b/examples/dash_pool_no_polling.json new file mode 100644 index 000000000..203ac77c2 --- /dev/null +++ b/examples/dash_pool_no_polling.json @@ -0,0 +1,93 @@ +{ + "logging": { + "level": "info", + "enableConsoleLog": true, + "enableConsoleColors": true, + "logFile": "", + "logBaseDirectory": "", + "perPoolLogFile": false + }, + "banning": { + "manager": "integrated", + "banOnJunkReceive": true, + "banOnInvalidShares": false + }, + "notifications": { + "enabled": true, + "email": { + "host": "smtp.example.com", + "port": 587, + "user": "user", + "password": "password", + "fromAddress": "info@yourpool.org", + "fromName": "support" + }, + "admin": { + "enabled": false, + "emailAddress": "user@example.com", + "notifyBlockFound": true + } + }, + "persistence": { + "postgres": { + "host": "127.0.0.1", + "port": 5432, + "user": "miningcore", + "password": "password", + "database": "miningcore" + } + }, + "paymentProcessing": { + "enabled": true, + "interval": 600, + "shareRecoveryFile": "recovered-shares.txt" + }, + "pools": [{ + "id": "dash1", + "enabled": true, + "coin": { + "type": "DASH" + }, + "address": "XgmfWd5DXWGcYhxPcDPUJk44Cnh2e8tZk7", + "rewardRecipients": [{ + "address": "XgmfWd5DXWGcYhxPcDPUJk44Cnh2e8tZk7", + "percentage": 0 + }], + "zmqBlockNotifySocket": "tcp://127.0.0.1:3000", + "jobRebroadcastTimeout": 10, + "clientConnectionTimeout": 600, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 50, + "checkThreshold": 50 + }, + "ports": { + "3062": { + "listenAddress": "0.0.0.0", + "difficulty": 1024, + "name": "ASIC Mining", + "varDiff": { + "minDiff": 512, + "targetTime": 15, + "retargetTime": 90, + "variancePercent": 30 + } + } + }, + "daemons": [{ + "host": "127.0.0.1", + "port": 9998, + "user": "user", + "password": "password" + }], + "paymentProcessing": { + "enabled": true, + "minimumPayment": 0.5, + "payoutScheme": "PPLNS", + "payoutSchemeConfig": { + "factor": 2.0 + } + } + }] +} \ No newline at end of file diff --git a/examples/digibyte_scrypt_pool.json b/examples/digibyte_scrypt_pool.json index ed4ca9265..9f60fe06e 100644 --- a/examples/digibyte_scrypt_pool.json +++ b/examples/digibyte_scrypt_pool.json @@ -8,7 +8,7 @@ "perPoolLogFile": false }, "banning": { - "manager": "integrated" + "manager": "integrated", "banOnJunkReceive": true, "banOnInvalidShares": false }, @@ -56,8 +56,8 @@ "percentage": 1.0 } ], - "blockRefreshInterval": 1000, - "jobRebroadcastTimeout": 55, + "blockRefreshInterval": 500, + "jobRebroadcastTimeout": 10, "clientConnectionTimeout": 600, "banning": { "enabled": true, diff --git a/examples/digibyte_sha256_pool.json b/examples/digibyte_sha256_pool.json index 155bd527b..bb1636b0b 100644 --- a/examples/digibyte_sha256_pool.json +++ b/examples/digibyte_sha256_pool.json @@ -8,7 +8,7 @@ "perPoolLogFile": false }, "banning": { - "manager": "integrated" + "manager": "integrated", "banOnJunkReceive": true, "banOnInvalidShares": false }, @@ -56,8 +56,8 @@ "percentage": 1 } ], - "blockRefreshInterval": 1000, - "jobRebroadcastTimeout": 55, + "blockRefreshInterval": 500, + "jobRebroadcastTimeout": 10, "clientConnectionTimeout": 600, "banning": { "enabled": true, diff --git a/examples/ethereum_pool.json b/examples/ethereum_pool.json index 94948f4f7..e9f3bcc40 100644 --- a/examples/ethereum_pool.json +++ b/examples/ethereum_pool.json @@ -54,8 +54,7 @@ "address": "0x0942e9144606ad43f2e61a7ee332fe9914424712", "percentage": 0 }], - "blockRefreshInterval": 1000, - "jobRebroadcastTimeout": 55, + "blockRefreshInterval": 500, "clientConnectionTimeout": 600, "banning": { "enabled": true, diff --git a/examples/litecoin_dash_pool.json b/examples/litecoin_dash_pool.json index a5f89127f..f8a5c97d8 100644 --- a/examples/litecoin_dash_pool.json +++ b/examples/litecoin_dash_pool.json @@ -8,7 +8,7 @@ "perPoolLogFile": false }, "banning": { - "manager": "integrated" + "manager": "integrated", "banOnJunkReceive": true, "banOnInvalidShares": false }, @@ -53,8 +53,8 @@ "address": "LUWYwkz6DQLVeqJqHRtGjNhWUxBvBmE3SX", "percentage": 1 }], - "blockRefreshInterval": 1000, - "jobRebroadcastTimeout": 55, + "blockRefreshInterval": 500, + "jobRebroadcastTimeout": 10, "clientConnectionTimeout": 600, "banning": { "enabled": true, diff --git a/examples/litecoin_pool.json b/examples/litecoin_pool.json index c1c6ebe5c..542a193a0 100644 --- a/examples/litecoin_pool.json +++ b/examples/litecoin_pool.json @@ -8,7 +8,7 @@ "perPoolLogFile": false }, "banning": { - "manager": "integrated" + "manager": "integrated", "banOnJunkReceive": true, "banOnInvalidShares": false }, @@ -55,8 +55,8 @@ "percentage": 1.0 } ], - "blockRefreshInterval": 1000, - "jobRebroadcastTimeout": 55, + "blockRefreshInterval": 500, + "jobRebroadcastTimeout": 10, "clientConnectionTimeout": 600, "banning": { "enabled": true, diff --git a/examples/monero_pool.json b/examples/monero_pool.json index d8976c229..44c0ea650 100644 --- a/examples/monero_pool.json +++ b/examples/monero_pool.json @@ -55,8 +55,7 @@ "percentage": 1 } ], - "blockRefreshInterval": 1000, - "jobRebroadcastTimeout": 55, + "blockRefreshInterval": 500, "clientConnectionTimeout": 600, "banning": { "enabled": true, diff --git a/examples/straks_pool.json b/examples/straks_pool.json index 39f610cb0..0958e4c95 100644 --- a/examples/straks_pool.json +++ b/examples/straks_pool.json @@ -66,8 +66,8 @@ "percentage": 1.0 } ], - "blockRefreshInterval": 1000, - "jobRebroadcastTimeout": 55, + "blockRefreshInterval": 500, + "jobRebroadcastTimeout": 10, "clientConnectionTimeout": 600, "banning": { "enabled": true, diff --git a/examples/zcash_pool.json b/examples/zcash_pool.json index fa0001ab1..bfec39dd5 100644 --- a/examples/zcash_pool.json +++ b/examples/zcash_pool.json @@ -8,7 +8,7 @@ "perPoolLogFile": false }, "banning": { - "manager": "integrated" + "manager": "integrated", "banOnJunkReceive": true, "banOnInvalidShares": false }, @@ -58,8 +58,8 @@ "percentage": 1.5 } ], - "blockRefreshInterval": 1000, - "jobRebroadcastTimeout": 55, + "blockRefreshInterval": 500, + "jobRebroadcastTimeout": 10, "clientConnectionTimeout": 600, "banning": { "enabled": true, diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 28d8e2ca1..f13e945df 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -107,15 +107,17 @@ protected virtual void SetupJobUpdates() if (poolConfig.ExternalStratum) return; - jobRebroadcastTimeout = TimeSpan.FromSeconds(poolConfig.JobRebroadcastTimeout); + jobRebroadcastTimeout = TimeSpan.FromSeconds(Math.Max(1, poolConfig.JobRebroadcastTimeout)); + var sources = new List>(); + var cancelTimeout = new List>(); // block updates via ZMQ pub/sub var zmqPublisherSocket = extraPoolConfig?.ZmqBlockNotifySocket?.Trim(); if (!string.IsNullOrEmpty(zmqPublisherSocket)) { - sources.Add(Observable.Defer(()=> Observable.Create(obs => + var newJobsPubSub = Observable.Defer(()=> Observable.Create(obs => { var tcs = new CancellationTokenSource(); @@ -152,7 +154,7 @@ protected virtual void SetupJobUpdates() // do not consume all CPU cycles in case of a long lasting error condition Thread.Sleep(1000); - } + } }, tcs.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); return Disposable.Create(() => @@ -163,22 +165,48 @@ protected virtual void SetupJobUpdates() .Select(_ => Observable.FromAsync(() => UpdateJob(false, "ZMQ pub/sub"))) .Concat() .Publish() - .RefCount()); + .RefCount(); + + sources.Add(newJobsPubSub); + cancelTimeout.Add(newJobsPubSub); } - // periodically update block-template from daemon - var newJobsPolled = Observable.Interval(TimeSpan.FromMilliseconds(poolConfig.BlockRefreshInterval)) - .Select(_ => Observable.FromAsync(() => UpdateJob(false, "RPC polling"))) - .Concat() - .Where(isNew => isNew) - .Publish() - .RefCount(); + if (poolConfig.BlockRefreshInterval > 0) + { + // periodically update block-template from daemon + var newJobsPolled = Observable.Interval(TimeSpan.FromMilliseconds(poolConfig.BlockRefreshInterval)) + .Select(_ => Observable.FromAsync(() => UpdateJob(false, "RPC polling"))) + .Concat() + .Where(isNew => isNew) + .Publish() + .RefCount(); + + sources.Add(newJobsPolled); + cancelTimeout.Add(newJobsPolled); + } - sources.Add(newJobsPolled); + else + { + // poll for the first successful update after which polling is suspended forever + var newJobsPolled = Observable.Interval(TimeSpan.FromMilliseconds(poolConfig.BlockRefreshInterval)) + .Select(_ => Observable.FromAsync(() => UpdateJob(false, "RPC polling"))) + .Concat() + .Where(isNew => isNew) + .Take(1) + .Publish() + .RefCount(); + + sources.Add(newJobsPolled); + cancelTimeout.Add(newJobsPolled); + } // if there haven't been any new jobs for a while, force an update + var cancelRebroadcast = cancelTimeout.Count > 0 ? + cancelTimeout.Count > 1 ? Observable.Merge(cancelTimeout) : cancelTimeout.First() : + Observable.Never(); + sources.Add(Observable.Timer(jobRebroadcastTimeout) - .TakeUntil(newJobsPolled) // cancel timeout if an actual new job has been detected + .TakeUntil(cancelRebroadcast) // cancel timeout if an actual new job has been detected .Do(_ => logger.Debug(() => $"[{LogCat}] No new blocks for {jobRebroadcastTimeout.TotalSeconds} seconds - updating transactions & rebroadcasting work")) .Select(x => Observable.FromAsync(() => UpdateJob(true))) .Concat() diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs index c01dd3049..93f5a5d4d 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs @@ -20,12 +20,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Net; -using System.Numerics; -using System.Reactive; using System.Reactive.Linq; using System.Text; using System.Threading.Tasks; @@ -43,7 +40,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Stratum; using MiningCore.Time; using MiningCore.Util; -using NBitcoin; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; diff --git a/src/MiningCore/Configuration/ClusterConfigValidation.cs b/src/MiningCore/Configuration/ClusterConfigValidation.cs index b9a6ff374..aefc7c572 100644 --- a/src/MiningCore/Configuration/ClusterConfigValidation.cs +++ b/src/MiningCore/Configuration/ClusterConfigValidation.cs @@ -70,7 +70,7 @@ public SlackNotificationsConfigValidator() .WithMessage("You must provide the webhook url"); } } - + public class NetworkEndpointConfigValidator : AbstractValidator where T : NetworkEndpointConfig { From 475620399017a7072134ed55054d278bffbfcf35 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 15 Jan 2018 18:10:29 +0100 Subject: [PATCH 017/348] Honor cancellation token in inner loop --- src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index f13e945df..3078b9878 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -135,7 +135,7 @@ protected virtual void SetupJobUpdates() logger.Info($"Subscribed to {zmqPublisherSocket}/{BitcoinConstants.ZmqPublisherTopicBlockHash} for ZMQ pub/sub block updates"); - while (true) + while (!tcs.IsCancellationRequested) { subSocket.ReceiveMultipartMessage(2); //var msg = subSocket.ReceiveMultipartMessage(2); From 9efb4b7fcc266ddfe10dd7a4a845b55c66c7b746 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 16 Jan 2018 12:03:32 +0100 Subject: [PATCH 018/348] Add addresses --- src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs index cf61c495a..15220d1c2 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs @@ -80,6 +80,7 @@ public class KnownAddresses public static readonly Dictionary DevFeeAddresses = new Dictionary { {CoinType.BTC, "17QnVor1B6oK1rWnVVBrdX9gFzVkZZbhDm"}, + {CoinType.BCH, "1LJGTzNDTuTvkHpTxNSdmAEBAXAnEHDVqQ"}, {CoinType.LTC, "LTK6CWastkmBzGxgQhTTtCUjkjDA14kxzC"}, {CoinType.DOGE, "DGDuKRhBewGP1kbUz4hszNd2p6dDzWYy9Q"}, {CoinType.NMC, "NDSLDpFEcTbuRVcWHdJyiRZThVAcb5Z79o"}, @@ -93,7 +94,9 @@ public class KnownAddresses {CoinType.VTC, "VfCAvPVrksYvwcpU7E44e51HxfvVhcxMXf"}, {CoinType.ZEC, "t1YHZHz2DGVMJiggD2P4fBQ2TAPgtLSUwZ7"}, {CoinType.BTG, "GQb77ZuMCyJGZFyxpzqNfm7GB1rQreP4n6"}, - {CoinType.XMR, "475YVJbPHPedudkhrcNp1wDcLMTGYusGPF5fqE7XjnragVLPdqbCHBdZg3dF4dN9hXMjjvGbykS6a77dTAQvGrpiQqHp2eH"} + {CoinType.MOON, "2QvpGimMYLyqKsczQXZjv56h6me3M8orwj" }, + {CoinType.XMR, "475YVJbPHPedudkhrcNp1wDcLMTGYusGPF5fqE7XjnragVLPdqbCHBdZg3dF4dN9hXMjjvGbykS6a77dTAQvGrpiQqHp2eH"}, + {CoinType.ETN, "etnkQJwBCjmR1MfkU8D355ZWxxLMhs8miPrtKHWN4U3uUowq9ugeKccVBoEG3n13n74us5AkT8tEoTog46w4HBgn8sMuBRhm9h"} }; } From 7ce66554a2592c812e0087d356cff679baa59628 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 16 Jan 2018 12:46:11 +0100 Subject: [PATCH 019/348] Don't expose payment processing config extension data via API. Fixes #103 --- src/MiningCore/Api/Responses/GetPoolsResponse.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/MiningCore/Api/Responses/GetPoolsResponse.cs b/src/MiningCore/Api/Responses/GetPoolsResponse.cs index 992e20813..4c77cc44b 100644 --- a/src/MiningCore/Api/Responses/GetPoolsResponse.cs +++ b/src/MiningCore/Api/Responses/GetPoolsResponse.cs @@ -38,9 +38,6 @@ public class ApiPoolPaymentProcessingConfig public decimal MinimumPayment { get; set; } // in pool-base-currency (ie. Bitcoin, not Satoshis) public string PayoutScheme { get; set; } public JToken PayoutSchemeConfig { get; set; } - - [JsonExtensionData] - public IDictionary Extra { get; set; } } public partial class PoolInfo From e0dc273b799ba8621b6b93e53a8a37dac6fd75dc Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 17 Jan 2018 15:37:22 +0100 Subject: [PATCH 020/348] Keep extra data in api result but sanitize it first --- .../Api/Extensions/MiningPoolExtensions.cs | 10 +++++ .../Api/Responses/GetPoolsResponse.cs | 3 ++ .../Extensions/DictionaryExtensions.cs | 45 +++++++++++++++++++ src/MiningCore/Extensions/StringExtensions.cs | 1 - 4 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 src/MiningCore/Extensions/DictionaryExtensions.cs diff --git a/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs b/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs index 9bb5ca2cd..5598fd23d 100644 --- a/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs +++ b/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs @@ -2,7 +2,9 @@ using AutoMapper; using MiningCore.Api.Responses; using MiningCore.Blockchain; +using MiningCore.Blockchain.Ethereum.Configuration; using MiningCore.Configuration; +using MiningCore.Extensions; using MiningCore.Mining; namespace MiningCore.Api.Extensions @@ -24,6 +26,14 @@ public static PoolInfo ToPoolInfo(this PoolConfig pool, IMapper mapper, Persiste // pool fees poolInfo.PoolFeePercent = (float)pool.RewardRecipients.Sum(x => x.Percentage); + // strip security critical stuff + if (poolInfo.PaymentProcessing.Extra != null) + { + var extra = poolInfo.PaymentProcessing.Extra; + + extra.StripValue(nameof(EthereumPoolPaymentProcessingConfigExtra.CoinbasePassword)); + } + return poolInfo; } } diff --git a/src/MiningCore/Api/Responses/GetPoolsResponse.cs b/src/MiningCore/Api/Responses/GetPoolsResponse.cs index 4c77cc44b..992e20813 100644 --- a/src/MiningCore/Api/Responses/GetPoolsResponse.cs +++ b/src/MiningCore/Api/Responses/GetPoolsResponse.cs @@ -38,6 +38,9 @@ public class ApiPoolPaymentProcessingConfig public decimal MinimumPayment { get; set; } // in pool-base-currency (ie. Bitcoin, not Satoshis) public string PayoutScheme { get; set; } public JToken PayoutSchemeConfig { get; set; } + + [JsonExtensionData] + public IDictionary Extra { get; set; } } public partial class PoolInfo diff --git a/src/MiningCore/Extensions/DictionaryExtensions.cs b/src/MiningCore/Extensions/DictionaryExtensions.cs new file mode 100644 index 000000000..e938e19e5 --- /dev/null +++ b/src/MiningCore/Extensions/DictionaryExtensions.cs @@ -0,0 +1,45 @@ +/* +Copyright 2017 Coin Foundry (coinfoundry.org) +Authors: Oliver Weichhold (oliver@weichhold.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; + +namespace MiningCore.Extensions +{ + public static class DictionaryExtensions + { + public static void StripValue(this IDictionary dict, string key) + { + key = key.ToLower(CultureInfo.InvariantCulture); + + var keyActual = dict.Keys.FirstOrDefault(x => x.ToLower(CultureInfo.InvariantCulture) == key); + + if (keyActual != null) + { + var result = dict.Remove(keyActual); + Debug.Assert(result); + } + } + } +} diff --git a/src/MiningCore/Extensions/StringExtensions.cs b/src/MiningCore/Extensions/StringExtensions.cs index 89d996c2a..b80f0c8e7 100644 --- a/src/MiningCore/Extensions/StringExtensions.cs +++ b/src/MiningCore/Extensions/StringExtensions.cs @@ -25,7 +25,6 @@ namespace MiningCore.Extensions { public static class StringExtensions { - /// /// Converts a str string to byte array. /// From a4737a98d2fe2fd9701b4c2ce8506e27fa7d362b Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 19 Jan 2018 12:28:03 +0100 Subject: [PATCH 021/348] WIP --- src/MiningCore/Notifications/NotificationService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Notifications/NotificationService.cs b/src/MiningCore/Notifications/NotificationService.cs index 6bf241f8e..32f605453 100644 --- a/src/MiningCore/Notifications/NotificationService.cs +++ b/src/MiningCore/Notifications/NotificationService.cs @@ -99,7 +99,7 @@ public void NotifyPaymentSuccess(string poolId, decimal amount, int recpientsCou Category = NotificationCategory.PaymentSuccess, PoolId = poolId, Subject = "Payout Success Notification", - Msg = $"Paid out {FormatAmount(amount, poolId)} from pool {poolId} to {recpientsCount} recipients in Transaction(s) {txInfo}." + Msg = $"Paid {FormatAmount(amount, poolId)} from pool {poolId} to {recpientsCount} recipients in Transaction(s) {txInfo}." }); } From 4493effad635dc0292f1122420d734c2ec240c74 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 19 Jan 2018 20:52:04 +0100 Subject: [PATCH 022/348] PG command timeout --- src/MiningCore/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index f18e61fdb..eb438a599 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -505,7 +505,7 @@ private static void ConfigurePostgres(DatabaseConfig pgConfig, ContainerBuilder logger.ThrowLogPoolStartupException("Postgres configuration: invalid or missing 'user'"); // build connection string - var connectionString = $"Server={pgConfig.Host};Port={pgConfig.Port};Database={pgConfig.Database};User Id={pgConfig.User};Password={pgConfig.Password};CommandTimeout=90;"; + var connectionString = $"Server={pgConfig.Host};Port={pgConfig.Port};Database={pgConfig.Database};User Id={pgConfig.User};Password={pgConfig.Password};CommandTimeout=300;"; // register connection factory builder.RegisterInstance(new ConnectionFactory(connectionString)) From f19c7c9e4455c62cd01d09c0cda1b6db1d05c185 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 23 Jan 2018 11:17:21 +0100 Subject: [PATCH 023/348] Add support for ZEN and XVG --- .../Blockchain/Bitcoin/BitcoinConstants.cs | 4 +- .../Blockchain/Bitcoin/BitcoinJob.cs | 1 - .../Bitcoin/BitcoinPayoutHandler.cs | 2 +- .../Blockchain/Bitcoin/BitcoinPool.cs | 2 +- .../Blockchain/Bitcoin/BitcoinProperties.cs | 22 ++++ src/MiningCore/Blockchain/CoinMetaData.cs | 8 +- .../Blockchain/ZCash/ZCashConstants.cs | 113 +++++++++++++++++- .../Blockchain/ZCash/ZCashPayoutHandler.cs | 2 +- src/MiningCore/Blockchain/ZCash/ZCashPool.cs | 2 +- src/MiningCore/Configuration/ClusterConfig.cs | 6 +- 10 files changed, 149 insertions(+), 13 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs index 2501f3c2c..ed57574bd 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs @@ -93,9 +93,11 @@ public class KnownAddresses {CoinType.MONA, "MBbkeAM3VQKg474bgxJEXrtcnMg8cjHY3S"}, {CoinType.VTC, "VfCAvPVrksYvwcpU7E44e51HxfvVhcxMXf"}, {CoinType.ZEC, "t1YHZHz2DGVMJiggD2P4fBQ2TAPgtLSUwZ7"}, - {CoinType.ZCL, "t1Ysa2CHdpMu8T2zjYXQNQxPgGh8ehe9QYo"}, + {CoinType.ZCL, "t1MFU1vD3YKgsK6Uh8hW7UTY8mKAV2xVqBr"}, + {CoinType.ZEN, "znigQacfTvRiwD2TRhwkBHLNchQ2AZisD94"}, {CoinType.BTG, "GQb77ZuMCyJGZFyxpzqNfm7GB1rQreP4n6"}, {CoinType.MOON, "2QvpGimMYLyqKsczQXZjv56h6me3M8orwj" }, + {CoinType.XVG, "D5xPoHLM6HPkwWSqAweECTSQirJBmRjS8i" }, {CoinType.XMR, "475YVJbPHPedudkhrcNp1wDcLMTGYusGPF5fqE7XjnragVLPdqbCHBdZg3dF4dN9hXMjjvGbykS6a77dTAQvGrpiQqHp2eH"}, {CoinType.ETN, "etnkQJwBCjmR1MfkU8D355ZWxxLMhs8miPrtKHWN4U3uUowq9ugeKccVBoEG3n13n74us5AkT8tEoTog46w4HBgn8sMuBRhm9h"} }; diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs index 72da03f57..bd677a4da 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs @@ -23,7 +23,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Globalization; using System.IO; using System.Linq; -using System.Numerics; using System.Text; using MiningCore.Blockchain.Bitcoin.DaemonResponses; using MiningCore.Configuration; diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs index af0fa4a7e..690dcae42 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs @@ -48,7 +48,7 @@ namespace MiningCore.Blockchain.Bitcoin CoinType.LTC, CoinType.DOGE, CoinType.DGB, CoinType.VIA, CoinType.GRS, CoinType.MONA, CoinType.VTC, CoinType.BTG, CoinType.GLT, CoinType.STAK, - CoinType.MOON)] + CoinType.MOON, CoinType.XVG)] public class BitcoinPayoutHandler : PayoutHandlerBase, IPayoutHandler { diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs index 29438ebb1..c9c7940b6 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs @@ -34,7 +34,7 @@ namespace MiningCore.Blockchain.Bitcoin CoinType.BTC, CoinType.BCH, CoinType.NMC, CoinType.PPC, CoinType.LTC, CoinType.DOGE, CoinType.DGB, CoinType.VIA, CoinType.GRS, CoinType.MONA, CoinType.VTC, CoinType.GLT, - CoinType.MOON)] + CoinType.MOON, CoinType.XVG)] public class BitcoinPool : BitcoinPoolBase, BlockTemplate> { public BitcoinPool(IComponentContext ctx, diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index 701b1b3d3..0ef93d899 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -102,12 +102,15 @@ public class BitcoinProperties { CoinType.ZEC, equihashCoin }, { CoinType.BTG, equihashCoin }, { CoinType.ZCL, equihashCoin }, + { CoinType.ZEN, equihashCoin }, }; public static BitcoinCoinProperties GetCoinProperties(CoinType coin, string algorithm = null) { if (coin == CoinType.DGB) return GetDigiByteProperties(algorithm); + else if (coin == CoinType.XVG) + return GetVergeProperties(algorithm); coinProperties.TryGetValue(coin, out var props); return props; @@ -118,6 +121,25 @@ private static BitcoinCoinProperties GetDigiByteProperties(string algorithm) Contract.Requires(!string.IsNullOrEmpty(algorithm), $"{nameof(algorithm)} must not be empty"); switch(algorithm) + { + case "lyra2rev2": + return lyra2Rev2CoinVariantA; + + case "x17": + case "groestl-myriad": + case "blake2s": + throw new NotSupportedException($"algorithm {algorithm} not yet supported"); + + default: // scrypt + return scryptCoin; + } + } + + private static BitcoinCoinProperties GetVergeProperties(string algorithm) + { + Contract.Requires(!string.IsNullOrEmpty(algorithm), $"{nameof(algorithm)} must not be empty"); + + switch (algorithm) { case "sha256d": return sha256Coin; diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index 264d83eaf..3a3a38ffe 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -29,6 +29,7 @@ public static class CoinMetaData { CoinType.DOGE, new Dictionary { { string.Empty, "https://dogechain.info/block/{0}" }}}, { CoinType.ZEC, new Dictionary { { string.Empty, "https://explorer.zcha.in/blocks/{0}" }}}, { CoinType.ZCL, new Dictionary { { string.Empty, "http://explorer.zclmine.pro/blocks/{0}" }}}, + { CoinType.ZEN, new Dictionary { { string.Empty, "http://explorer.zensystem.io/blocks/{0}" } }}, { CoinType.DGB, new Dictionary { { string.Empty, "https://digiexplorer.info/block/{0}" }}}, { CoinType.NMC, new Dictionary { { string.Empty, "https://explorer.namecoin.info/b/{0}" }}}, { CoinType.GRS, new Dictionary { { string.Empty, "https://groestlsight.groestlcoin.org/block/{0}" }}}, @@ -40,7 +41,8 @@ public static class CoinMetaData { CoinType.EXP, new Dictionary { { string.Empty, "http://www.gander.tech/blocks/{0}" }}}, { CoinType.AEON, new Dictionary { { string.Empty, "https://chainradar.com/aeon/block/{0}" }}}, { CoinType.STAK, new Dictionary { { string.Empty, "https://straks.info/block/{0}" }}}, - { CoinType.MOON, new Dictionary { { string.Empty, " https://chainz.cryptoid.info/moon/block.dws?{0}.htm" }}}, + { CoinType.MOON, new Dictionary { { string.Empty, "https://chainz.cryptoid.info/moon/block.dws?{0}.htm" }}}, + { CoinType.XVG, new Dictionary { { string.Empty, "https://verge-blockchain.info/block/{0}" } }}, }; public static readonly Dictionary PaymentInfoLinks = new Dictionary @@ -56,6 +58,7 @@ public static class CoinMetaData { CoinType.DOGE, "https://dogechain.info/tx/{0}" }, { CoinType.ZEC, "https://explorer.zcha.in/transactions/{0}" }, { CoinType.ZCL, "http://explorer.zclmine.pro/transactions/{0}" }, + { CoinType.ZEN, "http://explorer.zensystem.io/transactions/{0}" }, { CoinType.DGB, "https://digiexplorer.info/tx/{0}" }, { CoinType.NMC, "https://explorer.namecoin.info/tx/{0}" }, { CoinType.GRS, "https://groestlsight.groestlcoin.org/tx/{0}" }, @@ -68,6 +71,7 @@ public static class CoinMetaData { CoinType.EXP, "http://www.gander.tech/tx/{0}" }, { CoinType.AEON, "https://chainradar.com/aeon/transaction/{0}" }, { CoinType.MOON, "https://chainz.cryptoid.info/moon/tx.dws?{0}.htm" }, + { CoinType.XVG, "https://verge-blockchain.info/tx/{0}" }, }; public static readonly Dictionary AddressInfoLinks = new Dictionary @@ -81,6 +85,7 @@ public static class CoinMetaData { CoinType.DOGE, "https://dogechain.info/address/{0}" }, { CoinType.ZEC, "https://explorer.zcha.in/accounts/{0}" }, { CoinType.ZCL, "http://explorer.zclmine.pro/accounts/{0}" }, + { CoinType.ZEN, "http://explorer.zensystem.io/accounts/{0}" }, { CoinType.DGB, "https://digiexplorer.info/address/{0}" }, { CoinType.NMC, "https://explorer.namecoin.info/a/{0}" }, { CoinType.GRS, "https://groestlsight.groestlcoin.org/address/{0}" }, @@ -92,6 +97,7 @@ public static class CoinMetaData { CoinType.ELLA, "https://explorer.ellaism.org/addr/{0}" }, { CoinType.EXP, "http://www.gander.tech/address/{0}" }, { CoinType.MOON, "https://chainz.cryptoid.info/moon/address.dws?{0}.htm" }, + { CoinType.XVG, "https://verge-blockchain.info/address/{0}" }, }; } } diff --git a/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs b/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs index fd94ae50f..e8ac8fe22 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs @@ -29,14 +29,14 @@ namespace MiningCore.Blockchain.ZCash public class ZCashCoinbaseTxConfig { public bool PayFoundersReward { get; set; } - public int PercentFoundersReward { get; set; } + public decimal PercentFoundersReward { get; set; } public string[] FoundersRewardAddresses { get; set; } public ulong FoundersRewardSubsidySlowStartInterval { get; set; } public ulong FoundersRewardSubsidyHalvingInterval { get; set; } public ulong FoundersRewardSubsidySlowStartShift => FoundersRewardSubsidySlowStartInterval / 2; public ulong LastFoundersRewardBlockHeight => FoundersRewardSubsidyHalvingInterval + FoundersRewardSubsidySlowStartShift - 1; - public int PercentTreasuryReward { get; set; } + public decimal PercentTreasuryReward { get; set; } public ulong TreasuryRewardStartBlockHeight { get; set; } public string[] TreasuryRewardAddresses { get; set; } public double TreasuryRewardAddressChangeInterval { get; set; } @@ -119,7 +119,7 @@ public class ZCashConstants } }, }; - + private static readonly Dictionary ZCLCoinbaseTxConfig = new Dictionary { { @@ -166,11 +166,116 @@ public class ZCashConstants }, }; + private static readonly Dictionary ZencashCoinbaseTxConfig = new Dictionary + { + { + BitcoinNetworkType.Main, new ZCashCoinbaseTxConfig + { + PayFoundersReward = true, + PercentFoundersReward = 8.5m, + FoundersRewardSubsidyHalvingInterval = 840000, + FoundersRewardSubsidySlowStartInterval = 2, + + FoundersRewardAddresses = new[] + { + "zssEdGnZCQ9G86LZFtbynMn1hYTVhn6eYCL", "zsrCsXXmUf8k59NLasEKfxA7us3iNvaPATz", "zsnLPsWMXW2s4w9EmFSwtSLRxL2LhPcfdby", + "zshdovbcPfUAfkPeEE2qLbKnoue9RsbVokU", "zsqmq97JAKCCBFRGvxgv6FiJgQLCZBDp62S", "zskyFVFA7VRYX8EGdXmYN75WaBB25FmiL3g", + "zsmncLmwEUdVmAGPUrUnNKmPGXyej7mbmdM", "zsfa9VVJCEdjfPbku4XrFcRR8kTDm2T64rz", "zsjdMnfWuFi46VeN2HSXVQWEGsnGHgVxayY", + "zseb8wRQ8rZ722oLX5B8rx7qwZiBRb9mdig", "zsjxkovhqiMVggoW7jvSRi3NTSD3a6b6qfd", "zsokCCSU3wvZrS2G6mEDpJ5cH49E7sDyNr1", + "zt12EsFgkABHLMRXA7JNnpMqLrxsgCLnVEV", "zt39mvuG9gDTHX8A8Qk45rbk3dSdQoJ8ZAv", "zssTQZs5YxDGijKC86dvcDxzWogWcK7n5AK", + "zsywuMoQK7Bved2nrXs56AEtWBhpb88rMzS", "zsxVS2w7h1fHFX2nQtGm4372pd4DSHzq9ee", "zsupGi7ro3uC8CEVwm9r7vrdVUZaXQnHF6T", + "zshVZvW47dA5AB3Sqk1h7ytwWJeUJUJxxaE", "zsubBCjvDx252MKFsL4Dcf5rJU9Z9Upqr1N", "zsweaST3NcU4hfgkVULfCsfEq41pjgMDgcW", + "zswz6Rxb1S33fUpftETZwtGiVSeYxNKq2xc", "zswnpHtiBbrvYDzbhPQshkgvLSfYhDMRJ4S", "zsjSYAWaEYj35Ht7aXrRJUGY6Dc8qCmgYqu", + "zsvMv8fGroWR8epbSiGDCJHmfe6ec2uFQrt", "zsujxCT56BExQDAwKwktBjtnopYnw8BiKbg", "zsxeXc2FTAzmUmeZmqVsKVdwTMSvzyns4rT", + "zsuLqgABNudD8bVPbVGeUjGqapuoXp68i7F", "zsoc39J1dCFK1U8kckZznvQkv8As7sajYLz", "zt21NFdu1KRPJ7VRKtrWugM2Jqe5ePNmU4T", + "zsp15qbVcbx9ifcjKe6XZEJTvzsFUZ2BHLT", "zso2KvqH6yxLQEYggHdmfL3Tcd5V6E9tqhp", "zsnFG2W5ZHRYh3QucNze4mp31tBkemtfxdj", + "zsex2CGJtxHyHbpLXm7kESBmp3vWRqUkJMy", "zsvtFv96nrgrXKUbtNe2BpCt8aQEp5oJ7F8", "zsk5KitThmhK9KBa1KDybPgEmGSFTHzhMVA", + "zsuy4n48c4NsJyaCZEzwdAKULM1FqbB6Y4z", "zsgtQVMpX2zNMLvHHG2NDwfqKoaebvVectJ", "zszQqXRSPGdqsWw4iaMTNN6aJz4JjEzSdCF", + "zst6dBLrTtaMQBX7BLMNjKLTGcP11PBmgTV", "zshD9r6Eb6dZGdzYW2HCb9CzkMokCT1NGJR", "zswUaj1TboEGmvSfF7fdoxWyH3RMx7MBHHo", + "zsv8s4Poi5GxCsbBrRJ97Vsvazp84nrz5AN", "zsmmxrKU6dqWFwUKow1iyovg3gxrgXpEivr", "zskh1221aRC9WEfb5a59WxffeW34McmZZsw", + "zssAhuj57NnVm4yNFT6o8muRctABkUaBu3L", "zsi5Yr4Z8HwBvdBqQE8gk7ahExDu95J4oqZ", "zsy6ryEaxfk8emJ8bGVB7tmwRwBL8cfSqBW", + }, + + PercentTreasuryReward = 12, + TreasuryRewardAddressChangeInterval = 50000, + TreasuryRewardStartBlockHeight = 139200, + + TreasuryRewardAddresses = new[] + { + "zsyF68hcYYNLPj5i4PfQJ1kUY6nsFnZkc82", "zsfULrmbX7xbhqhAFRffVqCw9RyGv2hqNNG", + "zsoemTfqjicem2QVU8cgBHquKb1o9JR5p4Z", "zt339oiGL6tTgc9Q71f5g1sFTZf6QiXrRUr" + } + } + }, + { + BitcoinNetworkType.Test, new ZCashCoinbaseTxConfig + { + PayFoundersReward = true, + PercentFoundersReward = 8.5m, + FoundersRewardSubsidyHalvingInterval = 840000, + FoundersRewardSubsidySlowStartInterval = 2, + + FoundersRewardAddresses = new[] + { + "zrH8KT8KUcpKKNBu3fjH4hA84jZBCawErqn", "zrGsMC4ou1r5Vxy7Dnxg4PfKpansx83BM8g", "zr6sB2Az36D8CqzeZNavB11WbovsGtJSAZG", + "zrBAG3pXCTDq14nivNK9mW8SfwMNcdmMQpb", "zrRLwpYRYky4wsvwLVrDp8fs89EBTRhNMB1", "zrLozMfptTmE3zLP5SrTLyB8TXqH84Agjrr", + "zrMckkaLtVTEUvxj4ouU7BPDGa8xmdTZSVE", "zrFc897wJXmF7BcEdbvi2mS1bLrcuWYK6hm", "zrHEnni486u9SNcLWacroSgcdyMA33tfM92", + "zrJ3ymPV3R8Xk4N3BdNb898xvZvARm5K7mq", "zrDj3P6trx73291krxU51U9QnfkbGxkyJ6E", "zrJs3vMGFJi9pQCireeSLckJonamBnwTSrY", + "zrKFdXQoAkkycy52EFoWARyzZWx6Kat2Som", "zrEXbSe79FXA9KRMnJTZUWZdZhNnjcnAdrq", "zr7iAwfNgJsMpSCmijK3TuVDuNvSmLr1rUz", + "zrDEK7K6cftqSjeyVUH1WqJtBUkXN7GidxH", "zrRennuq75hyBVU4JymtZk8UcQ1vRPKpmpj", "zr9HRTL79pKmn5R8fvkC9kucZ4u1bQruLTD", + "zrML8KXpJsa1NVnbJuawX86ZvAn543tWdTT", "zrLBAkQoxpEtnztSUEcdxnEvuwtgxyAMGX7", "zr6kPnVzFBYmcBDsWoTrPHRuBxLq21o4zvT", + "zrMY3vdvqs9KSvx9TawvcyuVurt1Jj6GPVo", "zr9WB1qBpM4nwi1mudUFfjtMNmqzaBQDsXn", "zrAHbtHDPAqmzWJMQqSYzGyFnDWN3oELZRs", + "zrH1f5K3z7EQ6RWWZ7StCDWHTZwFChBVA2W", "zrNTacAid9LS4kAqzM4sw1YcF7gLFrzVM7U", "zrFyZpMVKMeDqbn6A2uUiL9mZmgxuR1pUBg", + "zrD1cqGFGzBcPogFHJvnN4XegvvmbTjA43t", "zr5A1D7czWkB4pAWfGC5Pux5Ek7anYybdPK", "zr8yTAxCy6jAdsc6qPvmVEQHbYo25AJKhy9", + "zrFW2YjQw4cABim5kEDwapbSqTz3wW7cWkk", "zr9nJvNbsrvUTZD41fhqAQeUcgMfqZmAweN", "zrCx4dXZd5b2tD483Ds4diHpo1QxBMJ76Jr", + "zr6eVeRwU6Puob3K1RfWtva1R458oj8pzkL", "zr7B92iHtQcobZjGCXo3DAqMQjsn7ka31wE", "zr8bcemLWAjYuphXSVqtqZWEnJipCB9F5oC", + "zrFzsuPXb7HsFd3srBqtVqnC9GQ94DQubV2", "zr4yiBobiHjHnCYi75NmYtyoqCV4A3kpHDL", "zrGVdR4K4F8MfmWxhUiTypK7PTsvHi8uTAh", + "zr7WiCDqCMvUdH1xmMu8YrBMFb2x2E6BX3z", "zrEFrGWLX4hPHuHRUD3TPbMAJyeSpMSctUc", "zr5c3f8PTnW8qBFX1GvK2LhyLBBCb1WDdGG", + "zrGkAZkZLqC9QKJR3XomgxNizCpNuAupTeg", "zrM7muDowiun9tCHhu5K9vcDGfUptuYorfZ", "zrCsWfwKotWnQmFviqAHAPAJ2jXqZYW966P", + "zrLLB3JB3jozUoMGFEGhjqyVXTpngVQ8c4T", "zrAEa8YjJ2f3m2VsM1Xa9EwibZxEnRoSLUx", "zrAdJgp7Cx35xTvB7ABWP8YLTNDArMjP1s3" + }, + + PercentTreasuryReward = 12, + TreasuryRewardAddressChangeInterval = 10000, + TreasuryRewardStartBlockHeight = 85500, + + TreasuryRewardAddresses = new[] + { + "zrRBQ5heytPMN5nY3ssPf3cG4jocXeD8fm1" + } + } + }, + { + BitcoinNetworkType.RegTest, new ZCashCoinbaseTxConfig + { + PayFoundersReward = true, + PercentFoundersReward = 8.5m, + FoundersRewardSubsidyHalvingInterval = 2000, + FoundersRewardSubsidySlowStartInterval = 0, + + FoundersRewardAddresses = new[] + { + "zrKmSdqZKZjnARd5e8FfRg4v1m74X7twxGa", + }, + + PercentTreasuryReward = 12, + TreasuryRewardAddressChangeInterval = 100, + TreasuryRewardStartBlockHeight = 139200, + + TreasuryRewardAddresses = new[] + { + "zrKmSdqZKZjnARd5e8FfRg4v1m74X7twxGa" + } + } + }, + }; + public static Dictionary> CoinbaseTxConfig = new Dictionary> { { CoinType.ZEC, ZCashCoinbaseTxConfig }, - { CoinType.ZCL, ZCLCoinbaseTxConfig } + { CoinType.ZCL, ZCLCoinbaseTxConfig }, + { CoinType.ZEN, ZencashCoinbaseTxConfig } }; } diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs index b707341f7..40543b7a8 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs @@ -39,7 +39,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace MiningCore.Blockchain.ZCash { - [CoinMetadata(CoinType.ZEC, CoinType.ZCL)] + [CoinMetadata(CoinType.ZEC, CoinType.ZCL, CoinType.ZEN)] public class ZCashPayoutHandler : BitcoinPayoutHandler { public ZCashPayoutHandler( diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPool.cs b/src/MiningCore/Blockchain/ZCash/ZCashPool.cs index 1cf2ca629..571078eb9 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPool.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPool.cs @@ -32,7 +32,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace MiningCore.Blockchain.ZCash { - [CoinMetadata(CoinType.ZEC, CoinType.ZCL)] + [CoinMetadata(CoinType.ZEC, CoinType.ZCL, CoinType.ZEN)] public class ZCashPool : ZCashPoolBase { public ZCashPool(IComponentContext ctx, diff --git a/src/MiningCore/Configuration/ClusterConfig.cs b/src/MiningCore/Configuration/ClusterConfig.cs index 359245535..5883e8528 100644 --- a/src/MiningCore/Configuration/ClusterConfig.cs +++ b/src/MiningCore/Configuration/ClusterConfig.cs @@ -38,7 +38,8 @@ public enum CoinType VIA, // Viacoin PPC, // Peercoin ZEC, // ZCash - ZCL, // Zcassic + ZCL, // ZClassic + ZEN, // Zencash ETH, // Ethereum ETC, // Ethereum Classic EXP, // Expanse @@ -51,7 +52,8 @@ public enum CoinType AEON, // AEON STAK, // Straks ETN, // Electroneum - MOON //MoonCoin + MOON, // MoonCoin + XVG, // Verge } public class CoinConfig From 728c2ea519cc42a6ae6a5bbd7895cd76688bda98 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 23 Jan 2018 15:21:20 +0100 Subject: [PATCH 024/348] Return Coin POW Algorithm in API results --- .../Api/Extensions/MiningPoolExtensions.cs | 20 ++++++ .../Api/Responses/GetPoolsResponse.cs | 1 + .../Blockchain/Bitcoin/BitcoinProperties.cs | 62 +++++++++++-------- src/MiningCore/Blockchain/CoinMetaData.cs | 35 +++++++++++ 4 files changed, 93 insertions(+), 25 deletions(-) diff --git a/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs b/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs index 5598fd23d..96dacf9ad 100644 --- a/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs +++ b/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs @@ -15,6 +15,9 @@ public static PoolInfo ToPoolInfo(this PoolConfig pool, IMapper mapper, Persiste { var poolInfo = mapper.Map(pool); + // enrich with basic information + poolInfo.Coin.Algorithm = GetPoolAlgorithm(pool); + poolInfo.PoolStats = mapper.Map(stats); poolInfo.NetworkStats = mapper.Map(stats); @@ -36,5 +39,22 @@ public static PoolInfo ToPoolInfo(this PoolConfig pool, IMapper mapper, Persiste return poolInfo; } + + private static string GetPoolAlgorithm(PoolConfig pool) + { + var result = pool.Coin.Algorithm; + + if (string.IsNullOrEmpty(result)) + { + if (CoinMetaData.CoinAlgorithm.TryGetValue(pool.Coin.Type, out var getter)) + result = getter(pool.Coin.Type); + } + + // Capitalize + if (!string.IsNullOrEmpty(result) && result.Length > 1) + result = result.Substring(0, 1).ToUpper() + result.Substring(1); + + return result; + } } } \ No newline at end of file diff --git a/src/MiningCore/Api/Responses/GetPoolsResponse.cs b/src/MiningCore/Api/Responses/GetPoolsResponse.cs index 992e20813..ff3b4503f 100644 --- a/src/MiningCore/Api/Responses/GetPoolsResponse.cs +++ b/src/MiningCore/Api/Responses/GetPoolsResponse.cs @@ -30,6 +30,7 @@ namespace MiningCore.Api.Responses public class ApiCoinConfig { public string Type { get; set; } + public string Algorithm { get; set; } } public class ApiPoolPaymentProcessingConfig diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index 0ef93d899..fa826bca5 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -11,13 +11,14 @@ namespace MiningCore.Blockchain.Bitcoin public class BitcoinCoinProperties { public BitcoinCoinProperties(double shareMultiplier, IHashAlgorithm coinbaseHasher, IHashAlgorithm headerHasher, - IHashAlgorithm blockHasher, IHashAlgorithm posblockHasher = null) + IHashAlgorithm blockHasher, string algorithm, IHashAlgorithm posblockHasher = null) { ShareMultiplier = shareMultiplier; CoinbaseHasher = coinbaseHasher; HeaderHasher = headerHasher; BlockHasher = blockHasher; PoSBlockHasher = posblockHasher; + Algorithm = algorithm; } public double ShareMultiplier { get; } @@ -25,6 +26,7 @@ public BitcoinCoinProperties(double shareMultiplier, IHashAlgorithm coinbaseHash public IHashAlgorithm HeaderHasher { get; } public IHashAlgorithm BlockHasher { get; } public IHashAlgorithm PoSBlockHasher { get; } + public string Algorithm { get; } } public class BitcoinProperties @@ -41,34 +43,34 @@ public class BitcoinProperties private static readonly IHashAlgorithm groestlMyriad = new GroestlMyriad(); private static readonly BitcoinCoinProperties sha256Coin = - new BitcoinCoinProperties(1, sha256D, sha256D, sha256DReverse); + new BitcoinCoinProperties(1, sha256D, sha256D, sha256DReverse, "Sha256"); private static readonly BitcoinCoinProperties scryptCoin = - new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, scrypt_1024_1, sha256DReverse, new DigestReverser(scrypt_1024_1)); + new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, scrypt_1024_1, sha256DReverse, "Scrypt", new DigestReverser(scrypt_1024_1)); private static readonly BitcoinCoinProperties groestlCoin = - new BitcoinCoinProperties(Math.Pow(2, 8), sha256S, groestl, new DigestReverser(groestl)); + new BitcoinCoinProperties(Math.Pow(2, 8), sha256S, groestl, new DigestReverser(groestl), "Groestl"); private static readonly BitcoinCoinProperties lyra2Rev2CoinVariantA = - new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, sha256DReverse); + new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, sha256DReverse, "Lyra2re2"); private static readonly BitcoinCoinProperties lyra2Rev2CoinVariantB = - new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, new DigestReverser(lyra2Rev2)); + new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, new DigestReverser(lyra2Rev2), "Lyra2re2"); private static readonly BitcoinCoinProperties x11Coin = - new BitcoinCoinProperties(1, sha256D, x11, new DigestReverser(x11)); + new BitcoinCoinProperties(1, sha256D, x11, new DigestReverser(x11), "X11"); private static readonly BitcoinCoinProperties skeinCoin = - new BitcoinCoinProperties(1, sha256D, skein, sha256DReverse); + new BitcoinCoinProperties(1, sha256D, skein, sha256DReverse, "Skein"); private static readonly BitcoinCoinProperties qubitCoin = - new BitcoinCoinProperties(1, sha256D, qubit, sha256DReverse); + new BitcoinCoinProperties(1, sha256D, qubit, sha256DReverse, "Qubit"); private static readonly BitcoinCoinProperties groestlMyriadCoin = - new BitcoinCoinProperties(Math.Pow(2, 8), sha256S, groestlMyriad, sha256DReverse); + new BitcoinCoinProperties(Math.Pow(2, 8), sha256S, groestlMyriad, sha256DReverse, "Groestl-Myriad"); private static readonly BitcoinCoinProperties equihashCoin = - new BitcoinCoinProperties(1, new DummyHasher(), sha256D, sha256DReverse); + new BitcoinCoinProperties(1, new DummyHasher(), sha256D, sha256DReverse, "Equihash"); private static readonly Dictionary coinProperties = new Dictionary { @@ -122,13 +124,18 @@ private static BitcoinCoinProperties GetDigiByteProperties(string algorithm) switch(algorithm) { - case "lyra2rev2": - return lyra2Rev2CoinVariantA; + case "sha256d": + return sha256Coin; - case "x17": + case "skein": + return skeinCoin; + + case "qubit": + return qubitCoin; + + case "groestl": case "groestl-myriad": - case "blake2s": - throw new NotSupportedException($"algorithm {algorithm} not yet supported"); + return groestlMyriadCoin; default: // scrypt return scryptCoin; @@ -141,22 +148,27 @@ private static BitcoinCoinProperties GetVergeProperties(string algorithm) switch (algorithm) { - case "sha256d": - return sha256Coin; - - case "skein": - return skeinCoin; - - case "qubit": - return qubitCoin; + case "lyra2rev2": + return lyra2Rev2CoinVariantA; - case "groestl": case "groestl-myriad": return groestlMyriadCoin; + case "x17": + case "blake2s": + throw new NotSupportedException($"algorithm {algorithm} not yet supported"); + default: // scrypt return scryptCoin; } } + + public static string GetAlgorithm(CoinType coin) + { + if (coinProperties.TryGetValue(coin, out var props)) + return props.Algorithm; + + return string.Empty; + } } } diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index 3a3a38ffe..e5b68c286 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using MiningCore.Blockchain.Bitcoin; using MiningCore.Blockchain.Ethereum; using MiningCore.Configuration; @@ -99,5 +101,38 @@ public static class CoinMetaData { CoinType.MOON, "https://chainz.cryptoid.info/moon/address.dws?{0}.htm" }, { CoinType.XVG, "https://verge-blockchain.info/address/{0}" }, }; + + private const string Ethash = "Dagger-Hashimoto"; + private const string Cryptonight = "Cryptonight"; + private const string CryptonightLight = "Cryptonight-Light"; + + public static readonly Dictionary> CoinAlgorithm = new Dictionary> + { + { CoinType.ETH, (coin)=> Ethash }, + { CoinType.ETC, (coin)=> Ethash }, + { CoinType.LTC, BitcoinProperties.GetAlgorithm }, + { CoinType.BCH, BitcoinProperties.GetAlgorithm }, + { CoinType.DASH, BitcoinProperties.GetAlgorithm }, + { CoinType.BTC, BitcoinProperties.GetAlgorithm }, + { CoinType.DOGE, BitcoinProperties.GetAlgorithm }, + { CoinType.ZEC, BitcoinProperties.GetAlgorithm }, + { CoinType.ZCL, BitcoinProperties.GetAlgorithm }, + { CoinType.ZEN, BitcoinProperties.GetAlgorithm }, + { CoinType.DGB, BitcoinProperties.GetAlgorithm }, + { CoinType.NMC, BitcoinProperties.GetAlgorithm }, + { CoinType.GRS, BitcoinProperties.GetAlgorithm }, + { CoinType.MONA, BitcoinProperties.GetAlgorithm }, + { CoinType.STAK, BitcoinProperties.GetAlgorithm }, + { CoinType.GLT, BitcoinProperties.GetAlgorithm }, + { CoinType.VTC, BitcoinProperties.GetAlgorithm }, + { CoinType.BTG, BitcoinProperties.GetAlgorithm }, + { CoinType.ELLA, (coin)=> Ethash }, + { CoinType.EXP, (coin)=> Ethash }, + { CoinType.MOON, BitcoinProperties.GetAlgorithm }, + { CoinType.XVG, BitcoinProperties.GetAlgorithm }, + { CoinType.XMR, (coin)=> Cryptonight }, + { CoinType.ETN, (coin)=> Cryptonight }, + { CoinType.AEON, (coin)=> CryptonightLight }, + }; } } From 4f26ac39c8828bade457fee10cc5b99fcb35881a Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 23 Jan 2018 18:39:37 +0100 Subject: [PATCH 025/348] Expose pool total miner earnings via API --- src/MiningCore/Api/ApiServer.cs | 6 ++++-- src/MiningCore/Api/Responses/GetPoolsResponse.cs | 1 + .../Postgres/Repositories/StatsRepository.cs | 10 ++++++++++ .../Persistence/Repositories/IStatsRepository.cs | 1 + 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Api/ApiServer.cs b/src/MiningCore/Api/ApiServer.cs index a0efde744..0595d6577 100644 --- a/src/MiningCore/Api/ApiServer.cs +++ b/src/MiningCore/Api/ApiServer.cs @@ -227,6 +227,7 @@ private async Task GetPoolInfosAsync(HttpContext context, Match m) var result = config.ToPoolInfo(mapper, stats); // enrich + result.TotalPaid = cf.Run(con => statsRepo.GetTotalPoolPayments(con, config.Id)); #if DEBUG var from = new DateTime(2018, 1, 6, 16, 0, 0); #else @@ -256,9 +257,10 @@ private async Task GetPoolInfoAsync(HttpContext context, Match m) var response = new GetPoolResponse { Pool = pool.ToPoolInfo(mapper, stats) - }; - + }; + // enrich + response.Pool.TotalPaid = cf.Run(con => statsRepo.GetTotalPoolPayments(con, pool.Id)); #if DEBUG var from = new DateTime(2018, 1, 7, 16, 0, 0); #else diff --git a/src/MiningCore/Api/Responses/GetPoolsResponse.cs b/src/MiningCore/Api/Responses/GetPoolsResponse.cs index ff3b4503f..6642e30ba 100644 --- a/src/MiningCore/Api/Responses/GetPoolsResponse.cs +++ b/src/MiningCore/Api/Responses/GetPoolsResponse.cs @@ -64,6 +64,7 @@ public partial class PoolInfo public PoolStats PoolStats { get; set; } public BlockchainStats NetworkStats { get; set; } public MinerPerformanceStats[] TopMiners { get; set; } + public decimal TotalPaid { get; set; } } public class GetPoolsResponse diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index 0b0cb87d2..c7de52c9d 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -84,6 +84,16 @@ public PoolStats GetLastPoolStats(IDbConnection con, string poolId) return mapper.Map(entity); } + public decimal GetTotalPoolPayments(IDbConnection con, string poolId) + { + logger.LogInvoke(); + + var query = "SELECT sum(amount) FROM payments WHERE poolid = @poolId"; + + var result = con.ExecuteScalar(query, new { poolId }); + return result; + } + public PoolStats[] GetPoolPerformanceBetweenHourly(IDbConnection con, string poolId, DateTime start, DateTime end) { logger.LogInvoke(new []{ poolId }); diff --git a/src/MiningCore/Persistence/Repositories/IStatsRepository.cs b/src/MiningCore/Persistence/Repositories/IStatsRepository.cs index 2bc58a450..e6e4c8829 100644 --- a/src/MiningCore/Persistence/Repositories/IStatsRepository.cs +++ b/src/MiningCore/Persistence/Repositories/IStatsRepository.cs @@ -31,6 +31,7 @@ public interface IStatsRepository void InsertPoolStats(IDbConnection con, IDbTransaction tx, PoolStats stats); void InsertMinerWorkerPerformanceStats(IDbConnection con, IDbTransaction tx, MinerWorkerPerformanceStats stats); PoolStats GetLastPoolStats(IDbConnection con, string poolId); + decimal GetTotalPoolPayments(IDbConnection con, string poolId); PoolStats[] GetPoolPerformanceBetweenHourly(IDbConnection con, string poolId, DateTime start, DateTime end); MinerStats GetMinerStats(IDbConnection con, IDbTransaction tx, string poolId, string miner); MinerWorkerPerformanceStats[] PagePoolMinersByHashrate(IDbConnection con, string poolId, DateTime from, int page, int pageSize); From b6634be6e01ac4aff6f66985ba0fda284b3e7dc1 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 23 Jan 2018 22:41:14 +0100 Subject: [PATCH 026/348] Improved LTC block explorer links --- src/MiningCore/Blockchain/CoinMetaData.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index e5b68c286..7f9c22412 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -24,7 +24,7 @@ public static class CoinMetaData { CoinType.XMR, new Dictionary { { string.Empty, "https://chainradar.com/xmr/block/{0}" }}}, { CoinType.ETN, new Dictionary { { string.Empty, "https://blockexplorer.electroneum.com/block/{0}" } }}, - { CoinType.LTC, new Dictionary { { string.Empty, "http://explorer.litecoin.net/block/{0}" }}}, + { CoinType.LTC, new Dictionary { { string.Empty, "https://chainz.cryptoid.info/ltc/block.dws?{0}.htm" } }}, { CoinType.BCH, new Dictionary { { string.Empty, "https://www.blocktrail.com/BCC/block/{0}" }}}, { CoinType.DASH, new Dictionary { { string.Empty, "https://chainz.cryptoid.info/dash/block.dws?{0}.htm" }}}, { CoinType.BTC, new Dictionary { { string.Empty, "https://blockchain.info/block/{0}" }}}, @@ -53,7 +53,7 @@ public static class CoinMetaData { CoinType.ETN, "https://blockexplorer.electroneum.com/tx/{0}" }, { CoinType.ETH, "https://etherscan.io/tx/{0}" }, { CoinType.ETC, "https://gastracker.io/tx/{0}" }, - { CoinType.LTC, "http://explorer.litecoin.net/tx/{0}" }, + { CoinType.LTC, "https://chainz.cryptoid.info/ltc/tx.dws?{0}.htm" }, { CoinType.BCH, "https://www.blocktrail.com/BCC/tx/{0}" }, { CoinType.DASH, "https://chainz.cryptoid.info/dash/tx.dws?{0}.htm" }, { CoinType.BTC, "https://blockchain.info/tx/{0}" }, @@ -80,7 +80,7 @@ public static class CoinMetaData { { CoinType.ETH, "https://etherscan.io/address/{0}" }, { CoinType.ETC, "https://gastracker.io/addr/{0}" }, - { CoinType.LTC, "http://explorer.litecoin.net/address/{0}" }, + { CoinType.LTC, "https://chainz.cryptoid.info/ltc/address.dws?{0}.htm" }, { CoinType.BCH, "https://www.blocktrail.com/BCC/address/{0}" }, { CoinType.DASH, "https://chainz.cryptoid.info/dash/address.dws?{0}.htm" }, { CoinType.BTC, "https://blockchain.info/address/{0}" }, From a93be434522623c28a62d3733d11049ad85f1dea Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 24 Jan 2018 10:04:44 +0100 Subject: [PATCH 027/348] Neoscrypt update --- src/Native/libmultihash/exports.cpp | 2 +- src/Native/libmultihash/neoscrypt.c | 2884 +++- src/Native/libmultihash/neoscrypt.h | 70 +- src/Native/libmultihash/neoscrypt_asm.S | 17064 ++++++++++++++++++++++ 4 files changed, 19504 insertions(+), 516 deletions(-) create mode 100644 src/Native/libmultihash/neoscrypt_asm.S diff --git a/src/Native/libmultihash/exports.cpp b/src/Native/libmultihash/exports.cpp index ade36801b..ac276fda8 100644 --- a/src/Native/libmultihash/exports.cpp +++ b/src/Native/libmultihash/exports.cpp @@ -73,7 +73,7 @@ extern "C" MODULE_API void x15_export(const char* input, char* output, uint32_t x15_hash(input, output, input_len); } -extern "C" MODULE_API void neoscrypt_export(const char* input, char* output, uint32_t profile) +extern "C" MODULE_API void neoscrypt_export(const unsigned char* input, unsigned char* output, uint32_t profile) { neoscrypt(input, output, profile); } diff --git a/src/Native/libmultihash/neoscrypt.c b/src/Native/libmultihash/neoscrypt.c index 0811cee81..228c57bb5 100644 --- a/src/Native/libmultihash/neoscrypt.c +++ b/src/Native/libmultihash/neoscrypt.c @@ -2,7 +2,7 @@ * Copyright (c) 2009 Colin Percival, 2011 ArtForz * Copyright (c) 2012 Andrew Moon (floodyberry) * Copyright (c) 2012 Samuel Neves - * Copyright (c) 2014 John Doering + * Copyright (c) 2014-2016 John Doering * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,38 +34,24 @@ #include "neoscrypt.h" -#if (_MSC_VER) -#include -#endif - -#if (WINDOWS) -/* sizeof(unsigned long) = 4 for MinGW64 */ -typedef unsigned long long ulong; -#else -typedef unsigned long ulong; -#endif -typedef unsigned int uint; -typedef unsigned char uchar; -typedef unsigned int bool; +#ifdef _WIN32 +#include +#define alloca(x) _alloca(x) +#endif - -#define MIN(a, b) ((a) < (b) ? a : b) -#define MAX(a, b) ((a) > (b) ? a : b) - - -#if (NEOSCRYPT_SHA256) +#ifdef SHA256 /* SHA-256 */ -static const uint32_t sha256_constants[64] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +static const uint sha256_constants[64] = { + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 }; #define Ch(x,y,z) (z ^ (x & (y ^ z))) @@ -88,18 +74,15 @@ static const uint32_t sha256_constants[64] = { r[1] = r[0]; \ r[0] = t0 + t1; - typedef struct sha256_hash_state_t { - uint32_t H[8]; - uint64_t T; - uint32_t leftover; - uint8_t buffer[SCRYPT_HASH_BLOCK_SIZE]; + uint H[8]; + ullong T; + uint leftover; + uchar buffer[BLOCK_SIZE]; } sha256_hash_state; - -static void sha256_blocks(sha256_hash_state *S, const uint8_t *in, size_t blocks) { - uint32_t r[8], w[64], t0, t1; - size_t i; +static void sha256_blocks(sha256_hash_state *S, const uchar *in, uint blocks) { + uint r[8], w[64], t0, t1, i; for(i = 0; i < 8; i++) r[i] = S->H[i]; @@ -118,34 +101,34 @@ static void sha256_blocks(sha256_hash_state *S, const uint8_t *in, size_t blocks r[i] += S->H[i]; S->H[i] = r[i]; } - S->T += SCRYPT_HASH_BLOCK_SIZE * 8; - in += SCRYPT_HASH_BLOCK_SIZE; + S->T += BLOCK_SIZE * 8; + in += BLOCK_SIZE; } } static void neoscrypt_hash_init_sha256(sha256_hash_state *S) { - S->H[0] = 0x6a09e667; - S->H[1] = 0xbb67ae85; - S->H[2] = 0x3c6ef372; - S->H[3] = 0xa54ff53a; - S->H[4] = 0x510e527f; - S->H[5] = 0x9b05688c; - S->H[6] = 0x1f83d9ab; - S->H[7] = 0x5be0cd19; + S->H[0] = 0x6A09E667; + S->H[1] = 0xBB67AE85; + S->H[2] = 0x3C6EF372; + S->H[3] = 0xA54FF53A; + S->H[4] = 0x510E527F; + S->H[5] = 0x9B05688C; + S->H[6] = 0x1F83D9AB; + S->H[7] = 0x5BE0CD19; S->T = 0; S->leftover = 0; } -static void neoscrypt_hash_update_sha256(sha256_hash_state *S, const uint8_t *in, size_t inlen) { - size_t blocks, want; +static void neoscrypt_hash_update_sha256(sha256_hash_state *S, const uchar *in, uint inlen) { + uint blocks, want; /* handle the previous data */ if(S->leftover) { - want = (SCRYPT_HASH_BLOCK_SIZE - S->leftover); + want = (BLOCK_SIZE - S->leftover); want = (want < inlen) ? want : inlen; - memcpy(S->buffer + S->leftover, in, want); - S->leftover += (uint32_t)want; - if(S->leftover < SCRYPT_HASH_BLOCK_SIZE) + neoscrypt_copy(S->buffer + S->leftover, in, want); + S->leftover += (uint)want; + if(S->leftover < BLOCK_SIZE) return; in += want; inlen -= want; @@ -153,28 +136,28 @@ static void neoscrypt_hash_update_sha256(sha256_hash_state *S, const uint8_t *in } /* handle the current data */ - blocks = (inlen & ~(SCRYPT_HASH_BLOCK_SIZE - 1)); - S->leftover = (uint32_t)(inlen - blocks); + blocks = (inlen & ~(BLOCK_SIZE - 1)); + S->leftover = (uint)(inlen - blocks); if(blocks) { - sha256_blocks(S, in, blocks / SCRYPT_HASH_BLOCK_SIZE); + sha256_blocks(S, in, blocks / BLOCK_SIZE); in += blocks; } /* handle leftover data */ if(S->leftover) - memcpy(S->buffer, in, S->leftover); + neoscrypt_copy(S->buffer, in, S->leftover); } -static void neoscrypt_hash_finish_sha256(sha256_hash_state *S, uint8_t *hash) { - uint64_t t = S->T + (S->leftover * 8); +static void neoscrypt_hash_finish_sha256(sha256_hash_state *S, uchar *hash) { + ullong t = S->T + (S->leftover * 8); S->buffer[S->leftover] = 0x80; if(S->leftover <= 55) { - memset(S->buffer + S->leftover + 1, 0, 55 - S->leftover); + neoscrypt_erase(S->buffer + S->leftover + 1, 55 - S->leftover); } else { - memset(S->buffer + S->leftover + 1, 0, 63 - S->leftover); + neoscrypt_erase(S->buffer + S->leftover + 1, 63 - S->leftover); sha256_blocks(S, S->buffer, 1); - memset(S->buffer, 0, 56); + neoscrypt_erase(S->buffer, 56); } U64TO8_BE(S->buffer + 56, t); @@ -190,13 +173,6 @@ static void neoscrypt_hash_finish_sha256(sha256_hash_state *S, uint8_t *hash) { U32TO8_BE(&hash[28], S->H[7]); } -static void neoscrypt_hash_sha256(hash_digest hash, const uint8_t *m, size_t mlen) { - sha256_hash_state st; - neoscrypt_hash_init_sha256(&st); - neoscrypt_hash_update_sha256(&st, m, mlen); - neoscrypt_hash_finish_sha256(&st, hash); -} - /* HMAC for SHA-256 */ @@ -204,40 +180,49 @@ typedef struct sha256_hmac_state_t { sha256_hash_state inner, outer; } sha256_hmac_state; -static void neoscrypt_hmac_init_sha256(sha256_hmac_state *st, const uint8_t *key, size_t keylen) { - uint8_t pad[SCRYPT_HASH_BLOCK_SIZE] = {0}; - size_t i; +static inline void neoscrypt_hmac_init_sha256(sha256_hmac_state *st, + const uchar *key, uint keylen) { + uchar pad[BLOCK_SIZE + DIGEST_SIZE]; + uint *P = (uint *) pad; + uint i; - neoscrypt_hash_init_sha256(&st->inner); - neoscrypt_hash_init_sha256(&st->outer); + /* The pad initialisation for the inner loop */ + for(i = 0; i < (BLOCK_SIZE >> 2); i++) + P[i] = 0x36363636; - if(keylen <= SCRYPT_HASH_BLOCK_SIZE) { - /* use the key directly if it's <= blocksize bytes */ - memcpy(pad, key, keylen); + if(keylen <= BLOCK_SIZE) { + /* XOR the key into the pad */ + neoscrypt_xor(pad, key, keylen); } else { - /* if it's > blocksize bytes, hash it */ - neoscrypt_hash_sha256(pad, key, keylen); + /* Hash the key and XOR into the pad */ + sha256_hash_state st0; + neoscrypt_hash_init_sha256(&st0); + neoscrypt_hash_update_sha256(&st0, key, keylen); + neoscrypt_hash_finish_sha256(&st0, &pad[BLOCK_SIZE]); + neoscrypt_xor(&pad[0], &pad[BLOCK_SIZE], DIGEST_SIZE); } - /* inner = (key ^ 0x36) */ - /* h(inner || ...) */ - for(i = 0; i < SCRYPT_HASH_BLOCK_SIZE; i++) - pad[i] ^= 0x36; - neoscrypt_hash_update_sha256(&st->inner, pad, SCRYPT_HASH_BLOCK_SIZE); - - /* outer = (key ^ 0x5c) */ - /* h(outer || ...) */ - for(i = 0; i < SCRYPT_HASH_BLOCK_SIZE; i++) - pad[i] ^= (0x5c ^ 0x36); - neoscrypt_hash_update_sha256(&st->outer, pad, SCRYPT_HASH_BLOCK_SIZE); + neoscrypt_hash_init_sha256(&st->inner); + /* h(inner || pad) */ + neoscrypt_hash_update_sha256(&st->inner, pad, BLOCK_SIZE); + + /* The pad re-initialisation for the outer loop */ + for(i = 0; i < (BLOCK_SIZE >> 2); i++) + P[i] ^= (0x36363636 ^ 0x5C5C5C5C); + + neoscrypt_hash_init_sha256(&st->outer); + /* h(outer || pad) */ + neoscrypt_hash_update_sha256(&st->outer, pad, BLOCK_SIZE); } -static void neoscrypt_hmac_update_sha256(sha256_hmac_state *st, const uint8_t *m, size_t mlen) { +static inline void neoscrypt_hmac_update_sha256(sha256_hmac_state *st, + const uchar *m, uint mlen) { /* h(inner || m...) */ neoscrypt_hash_update_sha256(&st->inner, m, mlen); } -static void neoscrypt_hmac_finish_sha256(sha256_hmac_state *st, hash_digest mac) { +static inline void neoscrypt_hmac_finish_sha256(sha256_hmac_state *st, + hash_digest mac) { /* h(inner || m) */ hash_digest innerhash; neoscrypt_hash_finish_sha256(&st->inner, innerhash); @@ -250,14 +235,14 @@ static void neoscrypt_hmac_finish_sha256(sha256_hmac_state *st, hash_digest mac) /* PBKDF2 for SHA-256 */ -static void neoscrypt_pbkdf2_sha256(const uint8_t *password, size_t password_len, - const uint8_t *salt, size_t salt_len, uint64_t N, uint8_t *output, size_t output_len) { +void neoscrypt_pbkdf2_sha256(const uchar *password, uint password_len, + const uchar *salt, uint salt_len, uint N, uchar *output, uint output_len) { sha256_hmac_state hmac_pw, hmac_pw_salt, work; hash_digest ti, u; - uint8_t be[4]; - uint32_t i, j, k, blocks; + uchar be[4]; + uint i, j, k, blocks; - /* bytes must be <= (0xffffffff - (SCRYPT_HASH_DIGEST_SIZE - 1)), which they will always be under scrypt */ + /* bytes must be <= (0xffffffff - (DIGEST_SIZE - 1)), which they will always be under scrypt */ /* hmac(password, ...) */ neoscrypt_hmac_init_sha256(&hmac_pw, password, password_len); @@ -266,20 +251,20 @@ static void neoscrypt_pbkdf2_sha256(const uint8_t *password, size_t password_len hmac_pw_salt = hmac_pw; neoscrypt_hmac_update_sha256(&hmac_pw_salt, salt, salt_len); - blocks = ((uint32_t)output_len + (SCRYPT_HASH_DIGEST_SIZE - 1)) / SCRYPT_HASH_DIGEST_SIZE; + blocks = ((uint)output_len + (DIGEST_SIZE - 1)) / DIGEST_SIZE; for(i = 1; i <= blocks; i++) { /* U1 = hmac(password, salt || be(i)) */ U32TO8_BE(be, i); work = hmac_pw_salt; neoscrypt_hmac_update_sha256(&work, be, 4); neoscrypt_hmac_finish_sha256(&work, ti); - memcpy(u, ti, sizeof(u)); + neoscrypt_copy(u, ti, sizeof(u)); /* T[i] = U1 ^ U2 ^ U3... */ for(j = 0; j < N - 1; j++) { /* UX = hmac(password, U{X-1}) */ work = hmac_pw; - neoscrypt_hmac_update_sha256(&work, u, SCRYPT_HASH_DIGEST_SIZE); + neoscrypt_hmac_update_sha256(&work, u, DIGEST_SIZE); neoscrypt_hmac_finish_sha256(&work, u); /* T[i] ^= UX */ @@ -287,47 +272,50 @@ static void neoscrypt_pbkdf2_sha256(const uint8_t *password, size_t password_len ti[k] ^= u[k]; } - memcpy(output, ti, (output_len > SCRYPT_HASH_DIGEST_SIZE) ? SCRYPT_HASH_DIGEST_SIZE : output_len); - output += SCRYPT_HASH_DIGEST_SIZE; - output_len -= SCRYPT_HASH_DIGEST_SIZE; + neoscrypt_copy(output, ti, (output_len > DIGEST_SIZE) ? DIGEST_SIZE : output_len); + output += DIGEST_SIZE; + output_len -= DIGEST_SIZE; } } -#endif +#endif /* SHA256 */ -#if (NEOSCRYPT_BLAKE256) +#ifdef BLAKE256 /* BLAKE-256 */ -const uint8_t blake256_sigma[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 14,10, 4, 8, 9,15,13, 6, 1,12, 0, 2,11, 7, 5, 3, - 11, 8,12, 0, 5, 2,15,13,10,14, 3, 6, 7, 1, 9, 4, - 7, 9, 3, 1,13,12,11,14, 2, 6, 5,10, 4, 0,15, 8, - 9, 0, 5, 7, 2, 4,10,15,14, 1,11,12, 6, 8, 3,13, - 2,12, 6,10, 0,11, 8, 3, 4,13, 7, 5,15,14, 1, 9, - 12, 5, 1,15,14,13, 4,10, 0, 7, 6, 3, 9, 2, 8,11, - 13,11, 7,14,12, 1, 3, 9, 5, 0,15, 4, 8, 6, 2,10, - 6,15,14, 9,11, 3, 0, 8,12, 2,13, 7, 1, 4,10, 5, - 10, 2, 8, 4, 7, 6, 1, 5,15,11, 9,14, 3,12,13 ,0, +const uchar blake256_sigma[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, + 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4, + 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8, + 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13, + 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9, + 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11, + 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10, + 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5, + 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }; -const uint32_t blake256_constants[16] = { - 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, - 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917 +const uint blake256_constants[16] = { + 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, + 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89, + 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, + 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917 }; typedef struct blake256_hash_state_t { - uint32_t H[8], T[2]; - uint32_t leftover; - uint8_t buffer[SCRYPT_HASH_BLOCK_SIZE]; + uint H[8]; + uint T[2]; + uint leftover; + uchar buffer[BLOCK_SIZE]; } blake256_hash_state; -static void blake256_blocks(blake256_hash_state *S, const uint8_t *in, size_t blocks) { - const uint8_t *sigma, *sigma_end = blake256_sigma + (10 * 16); - uint32_t m[16], v[16], h[8], t[2]; - uint32_t i; +static void blake256_blocks(blake256_hash_state *S, const uchar *in, uint blocks) { + const uchar *sigma, *sigma_end = blake256_sigma + (10 * 16); + uint m[16], v[16], h[8], t[2]; + uint i; for(i = 0; i < 8; i++) h[i] = S->H[i]; @@ -343,35 +331,35 @@ static void blake256_blocks(blake256_hash_state *S, const uint8_t *in, size_t bl for(i = 0; i < 4; i++) v[i + 8] = blake256_constants[i]; for(i = 0; i < 2; i++) - v[i + 12] = blake256_constants[i+4] ^ t[0]; + v[i + 12] = blake256_constants[i + 4] ^ t[0]; for(i = 0; i < 2; i++) - v[i + 14] = blake256_constants[i+6] ^ t[1]; + v[i + 14] = blake256_constants[i + 6] ^ t[1]; for(i = 0; i < 16; i++) m[i] = U8TO32_BE(&in[i * 4]); in += 64; -#define G(a,b,c,d,e) \ - v[a] += (m[sigma[e+0]] ^ blake256_constants[sigma[e+1]]) + v[b]; \ - v[d] = ROTR32(v[d] ^ v[a],16); \ +#define G(a, b, c, d, e) \ + v[a] += (m[sigma[e + 0]] ^ blake256_constants[sigma[e + 1]]) + v[b]; \ + v[d] = ROTR32(v[d] ^ v[a], 16); \ v[c] += v[d]; \ - v[b] = ROTR32(v[b] ^ v[c],12); \ - v[a] += (m[sigma[e+1]] ^ blake256_constants[sigma[e+0]]) + v[b]; \ + v[b] = ROTR32(v[b] ^ v[c], 12); \ + v[a] += (m[sigma[e + 1]] ^ blake256_constants[sigma[e + 0]]) + v[b]; \ v[d] = ROTR32(v[d] ^ v[a], 8); \ v[c] += v[d]; \ v[b] = ROTR32(v[b] ^ v[c], 7); for(i = 0, sigma = blake256_sigma; i < 14; i++) { - G(0, 4, 8,12, 0); - G(1, 5, 9,13, 2); - G(2, 6,10,14, 4); - G(3, 7,11,15, 6); + G( 0, 4, 8, 12, 0); + G( 1, 5, 9, 13, 2); + G( 2, 6, 10, 14, 4); + G( 3, 7, 11, 15, 6); - G(0, 5,10,15, 8); - G(1, 6,11,12,10); - G(2, 7, 8,13,12); - G(3, 4, 9,14,14); + G( 0, 5, 10, 15, 8); + G( 1, 6, 11, 12, 10); + G( 2, 7, 8, 13, 12); + G( 3, 4, 9, 14, 14); sigma += 16; if(sigma == sigma_end) @@ -404,16 +392,17 @@ static void neoscrypt_hash_init_blake256(blake256_hash_state *S) { S->leftover = 0; } -static void neoscrypt_hash_update_blake256(blake256_hash_state *S, const uint8_t *in, size_t inlen) { - size_t blocks, want; +static void neoscrypt_hash_update_blake256(blake256_hash_state *S, + const uchar *in, uint inlen) { + uint blocks, want; /* handle the previous data */ if(S->leftover) { - want = (SCRYPT_HASH_BLOCK_SIZE - S->leftover); + want = (BLOCK_SIZE - S->leftover); want = (want < inlen) ? want : inlen; - memcpy(S->buffer + S->leftover, in, want); - S->leftover += (uint32_t)want; - if(S->leftover < SCRYPT_HASH_BLOCK_SIZE) + neoscrypt_copy(S->buffer + S->leftover, in, want); + S->leftover += (uint)want; + if(S->leftover < BLOCK_SIZE) return; in += want; inlen -= want; @@ -421,29 +410,29 @@ static void neoscrypt_hash_update_blake256(blake256_hash_state *S, const uint8_t } /* handle the current data */ - blocks = (inlen & ~(SCRYPT_HASH_BLOCK_SIZE - 1)); - S->leftover = (uint32_t)(inlen - blocks); + blocks = (inlen & ~(BLOCK_SIZE - 1)); + S->leftover = (uint)(inlen - blocks); if(blocks) { - blake256_blocks(S, in, blocks / SCRYPT_HASH_BLOCK_SIZE); + blake256_blocks(S, in, blocks / BLOCK_SIZE); in += blocks; } /* handle leftover data */ if(S->leftover) - memcpy(S->buffer, in, S->leftover); + neoscrypt_copy(S->buffer, in, S->leftover); } -static void neoscrypt_hash_finish_blake256(blake256_hash_state *S, uint8_t *hash) { - uint32_t th, tl, bits; +static void neoscrypt_hash_finish_blake256(blake256_hash_state *S, uchar *hash) { + uint th, tl, bits; bits = (S->leftover << 3); tl = S->T[0] + bits; th = S->T[1]; if(S->leftover == 0) { - S->T[0] = (uint32_t)0 - (uint32_t)512; - S->T[1] = (uint32_t)0 - (uint32_t)1; + S->T[0] = (uint)0 - (uint)512; + S->T[1] = (uint)0 - (uint)1; } else if(S->T[0] == 0) { - S->T[0] = ((uint32_t)0 - (uint32_t)512) + bits; + S->T[0] = ((uint)0 - (uint)512) + bits; S->T[1] = S->T[1] - 1; } else { S->T[0] -= (512 - bits); @@ -451,13 +440,13 @@ static void neoscrypt_hash_finish_blake256(blake256_hash_state *S, uint8_t *hash S->buffer[S->leftover] = 0x80; if(S->leftover <= 55) { - memset(S->buffer + S->leftover + 1, 0, 55 - S->leftover); + neoscrypt_erase(S->buffer + S->leftover + 1, 55 - S->leftover); } else { - memset(S->buffer + S->leftover + 1, 0, 63 - S->leftover); + neoscrypt_erase(S->buffer + S->leftover + 1, 63 - S->leftover); blake256_blocks(S, S->buffer, 1); - S->T[0] = (uint32_t)0 - (uint32_t)512; - S->T[1] = (uint32_t)0 - (uint32_t)1; - memset(S->buffer, 0, 56); + S->T[0] = (uint)0 - (uint)512; + S->T[1] = (uint)0 - (uint)1; + neoscrypt_erase(S->buffer, 56); } S->buffer[55] |= 1; U32TO8_BE(S->buffer + 56, th); @@ -474,54 +463,57 @@ static void neoscrypt_hash_finish_blake256(blake256_hash_state *S, uint8_t *hash U32TO8_BE(&hash[28], S->H[7]); } -static void neoscrypt_hash_blake256(hash_digest hash, const uint8_t *m, size_t mlen) { - blake256_hash_state st; - neoscrypt_hash_init_blake256(&st); - neoscrypt_hash_update_blake256(&st, m, mlen); - neoscrypt_hash_finish_blake256(&st, hash); -} - /* HMAC for BLAKE-256 */ typedef struct blake256_hmac_state_t { - blake256_hash_state inner, outer; + blake256_hash_state inner; + blake256_hash_state outer; } blake256_hmac_state; -static void neoscrypt_hmac_init_blake256(blake256_hmac_state *st, const uint8_t *key, size_t keylen) { - uint8_t pad[SCRYPT_HASH_BLOCK_SIZE] = {0}; - size_t i; +static inline void neoscrypt_hmac_init_blake256(blake256_hmac_state *st, + const uchar *key, uint keylen) { + uchar pad[BLOCK_SIZE + DIGEST_SIZE]; + uint *P = (uint *) pad; + uint i; - neoscrypt_hash_init_blake256(&st->inner); - neoscrypt_hash_init_blake256(&st->outer); + /* The pad initialisation for the inner loop */ + for(i = 0; i < (BLOCK_SIZE >> 2); i++) + P[i] = 0x36363636; - if(keylen <= SCRYPT_HASH_BLOCK_SIZE) { - /* use the key directly if it's <= blocksize bytes */ - memcpy(pad, key, keylen); + if(keylen <= BLOCK_SIZE) { + /* XOR the key into the pad */ + neoscrypt_xor(pad, key, keylen); } else { - /* if it's > blocksize bytes, hash it */ - neoscrypt_hash_blake256(pad, key, keylen); + /* Hash the key and XOR into the pad */ + blake256_hash_state st0; + neoscrypt_hash_init_blake256(&st0); + neoscrypt_hash_update_blake256(&st0, key, keylen); + neoscrypt_hash_finish_blake256(&st0, &pad[BLOCK_SIZE]); + neoscrypt_xor(&pad[0], &pad[BLOCK_SIZE], DIGEST_SIZE); } - /* inner = (key ^ 0x36) */ - /* h(inner || ...) */ - for(i = 0; i < SCRYPT_HASH_BLOCK_SIZE; i++) - pad[i] ^= 0x36; - neoscrypt_hash_update_blake256(&st->inner, pad, SCRYPT_HASH_BLOCK_SIZE); - - /* outer = (key ^ 0x5c) */ - /* h(outer || ...) */ - for(i = 0; i < SCRYPT_HASH_BLOCK_SIZE; i++) - pad[i] ^= (0x5c ^ 0x36); - neoscrypt_hash_update_blake256(&st->outer, pad, SCRYPT_HASH_BLOCK_SIZE); + neoscrypt_hash_init_blake256(&st->inner); + /* h(inner || pad) */ + neoscrypt_hash_update_blake256(&st->inner, pad, BLOCK_SIZE); + + /* The pad re-initialisation for the outer loop */ + for(i = 0; i < (BLOCK_SIZE >> 2); i++) + P[i] ^= (0x36363636 ^ 0x5C5C5C5C); + + neoscrypt_hash_init_blake256(&st->outer); + /* h(outer || pad) */ + neoscrypt_hash_update_blake256(&st->outer, pad, BLOCK_SIZE); } -static void neoscrypt_hmac_update_blake256(blake256_hmac_state *st, const uint8_t *m, size_t mlen) { +static inline void neoscrypt_hmac_update_blake256(blake256_hmac_state *st, + const uchar *m, uint mlen) { /* h(inner || m...) */ neoscrypt_hash_update_blake256(&st->inner, m, mlen); } -static void neoscrypt_hmac_finish_blake256(blake256_hmac_state *st, hash_digest mac) { +static inline void neoscrypt_hmac_finish_blake256(blake256_hmac_state *st, + hash_digest mac) { /* h(inner || m) */ hash_digest innerhash; neoscrypt_hash_finish_blake256(&st->inner, innerhash); @@ -534,14 +526,15 @@ static void neoscrypt_hmac_finish_blake256(blake256_hmac_state *st, hash_digest /* PBKDF2 for BLAKE-256 */ -static void neoscrypt_pbkdf2_blake256(const uint8_t *password, size_t password_len, - const uint8_t *salt, size_t salt_len, uint64_t N, uint8_t *output, size_t output_len) { +static void neoscrypt_pbkdf2_blake256(const uchar *password, + uint password_len, const uchar *salt, uint salt_len, uint N, + uchar *output, uint output_len) { blake256_hmac_state hmac_pw, hmac_pw_salt, work; hash_digest ti, u; - uint8_t be[4]; - uint32_t i, j, k, blocks; + uchar be[4]; + uint i, j, k, blocks; - /* bytes must be <= (0xffffffff - (SCRYPT_HASH_DIGEST_SIZE - 1)), which they will always be under scrypt */ + /* bytes must be <= (0xffffffff - (DIGEST_SIZE - 1)), which they will always be under scrypt */ /* hmac(password, ...) */ neoscrypt_hmac_init_blake256(&hmac_pw, password, password_len); @@ -550,20 +543,20 @@ static void neoscrypt_pbkdf2_blake256(const uint8_t *password, size_t password_l hmac_pw_salt = hmac_pw; neoscrypt_hmac_update_blake256(&hmac_pw_salt, salt, salt_len); - blocks = ((uint32_t)output_len + (SCRYPT_HASH_DIGEST_SIZE - 1)) / SCRYPT_HASH_DIGEST_SIZE; + blocks = ((uint)output_len + (DIGEST_SIZE - 1)) / DIGEST_SIZE; for(i = 1; i <= blocks; i++) { /* U1 = hmac(password, salt || be(i)) */ U32TO8_BE(be, i); work = hmac_pw_salt; neoscrypt_hmac_update_blake256(&work, be, 4); neoscrypt_hmac_finish_blake256(&work, ti); - memcpy(u, ti, sizeof(u)); + neoscrypt_copy(u, ti, sizeof(u)); /* T[i] = U1 ^ U2 ^ U3... */ for(j = 0; j < N - 1; j++) { /* UX = hmac(password, U{X-1}) */ work = hmac_pw; - neoscrypt_hmac_update_blake256(&work, u, SCRYPT_HASH_DIGEST_SIZE); + neoscrypt_hmac_update_blake256(&work, u, DIGEST_SIZE); neoscrypt_hmac_finish_blake256(&work, u); /* T[i] ^= UX */ @@ -571,26 +564,22 @@ static void neoscrypt_pbkdf2_blake256(const uint8_t *password, size_t password_l ti[k] ^= u[k]; } - memcpy(output, ti, (output_len > SCRYPT_HASH_DIGEST_SIZE) ? SCRYPT_HASH_DIGEST_SIZE : output_len); - output += SCRYPT_HASH_DIGEST_SIZE; - output_len -= SCRYPT_HASH_DIGEST_SIZE; + neoscrypt_copy(output, ti, (output_len > DIGEST_SIZE) ? DIGEST_SIZE : output_len); + output += DIGEST_SIZE; + output_len -= DIGEST_SIZE; } } -#endif +#endif /* BLAKE256 */ /* NeoScrypt */ -#if defined(ASM) +#ifdef ASM -extern void neoscrypt_salsa(uint *X, uint rounds); -extern void neoscrypt_salsa_tangle(uint *X, uint count); -extern void neoscrypt_chacha(uint *X, uint rounds); - -extern void neoscrypt_blkcpy(void *dstp, const void *srcp, uint len); -extern void neoscrypt_blkswp(void *blkAp, void *blkBp, uint len); -extern void neoscrypt_blkxor(void *dstp, const void *srcp, uint len); +extern void neoscrypt_copy(void *dstp, const void *srcp, uint len); +extern void neoscrypt_erase(void *dstp, uint len); +extern void neoscrypt_xor(void *dstp, const void *srcp, uint len); #else @@ -662,15 +651,14 @@ static void neoscrypt_chacha(uint *X, uint rounds) { #undef quarter } - /* Fast 32-bit / 64-bit memcpy(); * len must be a multiple of 32 bytes */ static void neoscrypt_blkcpy(void *dstp, const void *srcp, uint len) { - ulong *dst = (ulong *) dstp; - ulong *src = (ulong *) srcp; + size_t *dst = (size_t *) dstp; + size_t *src = (size_t *) srcp; uint i; - for(i = 0; i < (len / sizeof(ulong)); i += 4) { + for(i = 0; i < (len / sizeof(size_t)); i += 4) { dst[i] = src[i]; dst[i + 1] = src[i + 1]; dst[i + 2] = src[i + 2]; @@ -681,12 +669,12 @@ static void neoscrypt_blkcpy(void *dstp, const void *srcp, uint len) { /* Fast 32-bit / 64-bit block swapper; * len must be a multiple of 32 bytes */ static void neoscrypt_blkswp(void *blkAp, void *blkBp, uint len) { - ulong *blkA = (ulong *) blkAp; - ulong *blkB = (ulong *) blkBp; - register ulong t0, t1, t2, t3; + size_t *blkA = (size_t *) blkAp; + size_t *blkB = (size_t *) blkBp; + register size_t t0, t1, t2, t3; uint i; - for(i = 0; i < (len / sizeof(ulong)); i += 4) { + for(i = 0; i < (len / sizeof(size_t)); i += 4) { t0 = blkA[i]; t1 = blkA[i + 1]; t2 = blkA[i + 2]; @@ -705,11 +693,11 @@ static void neoscrypt_blkswp(void *blkAp, void *blkBp, uint len) { /* Fast 32-bit / 64-bit block XOR engine; * len must be a multiple of 32 bytes */ static void neoscrypt_blkxor(void *dstp, const void *srcp, uint len) { - ulong *dst = (ulong *) dstp; - ulong *src = (ulong *) srcp; + size_t *dst = (size_t *) dstp; + size_t *src = (size_t *) srcp; uint i; - for(i = 0; i < (len / sizeof(ulong)); i += 4) { + for(i = 0; i < (len / sizeof(size_t)); i += 4) { dst[i] ^= src[i]; dst[i + 1] ^= src[i + 1]; dst[i + 2] ^= src[i + 2]; @@ -717,18 +705,16 @@ static void neoscrypt_blkxor(void *dstp, const void *srcp, uint len) { } } -#endif - /* 32-bit / 64-bit optimised memcpy() */ -static void neoscrypt_copy(void *dstp, const void *srcp, uint len) { - ulong *dst = (ulong *) dstp; - ulong *src = (ulong *) srcp; +void neoscrypt_copy(void *dstp, const void *srcp, uint len) { + size_t *dst = (size_t *) dstp; + size_t *src = (size_t *) srcp; uint i, tail; - for(i = 0; i < (len / sizeof(ulong)); i++) + for(i = 0; i < (len / sizeof(size_t)); i++) dst[i] = src[i]; - tail = len & (sizeof(ulong) - 1); + tail = len & (sizeof(size_t) - 1); if(tail) { uchar *dstb = (uchar *) dstp; uchar *srcb = (uchar *) srcp; @@ -739,15 +725,15 @@ static void neoscrypt_copy(void *dstp, const void *srcp, uint len) { } /* 32-bit / 64-bit optimised memory erase aka memset() to zero */ -static void neoscrypt_erase(void *dstp, uint len) { - const ulong null = 0; - ulong *dst = (ulong *) dstp; +void neoscrypt_erase(void *dstp, uint len) { + const size_t null = 0; + size_t *dst = (size_t *) dstp; uint i, tail; - for(i = 0; i < (len / sizeof(ulong)); i++) + for(i = 0; i < (len / sizeof(size_t)); i++) dst[i] = null; - tail = len & (sizeof(ulong) - 1); + tail = len & (sizeof(size_t) - 1); if(tail) { uchar *dstb = (uchar *) dstp; @@ -757,15 +743,15 @@ static void neoscrypt_erase(void *dstp, uint len) { } /* 32-bit / 64-bit optimised XOR engine */ -static void neoscrypt_xor(void *dstp, const void *srcp, uint len) { - ulong *dst = (ulong *) dstp; - ulong *src = (ulong *) srcp; +void neoscrypt_xor(void *dstp, const void *srcp, uint len) { + size_t *dst = (size_t *) dstp; + size_t *src = (size_t *) srcp; uint i, tail; - for(i = 0; i < (len / sizeof(ulong)); i++) + for(i = 0; i < (len / sizeof(size_t)); i++) dst[i] ^= src[i]; - tail = len & (sizeof(ulong) - 1); + tail = len & (sizeof(size_t) - 1); if(tail) { uchar *dstb = (uchar *) dstp; uchar *srcb = (uchar *) srcp; @@ -775,12 +761,10 @@ static void neoscrypt_xor(void *dstp, const void *srcp, uint len) { } } +#endif /* ASM */ -/* BLAKE2s */ -#define BLAKE2S_BLOCK_SIZE 64U -#define BLAKE2S_OUT_SIZE 32U -#define BLAKE2S_KEY_SIZE 32U +/* BLAKE2s */ /* Parameter block of 32 bytes */ typedef struct blake2s_param_t { @@ -796,13 +780,15 @@ typedef struct blake2s_param_t { uchar personal[8]; } blake2s_param; -/* State block of 180 bytes */ +/* State block of 256 bytes */ typedef struct blake2s_state_t { uint h[8]; uint t[2]; uint f[2]; - uchar buf[2 * BLAKE2S_BLOCK_SIZE]; + uchar buf[2 * BLOCK_SIZE]; uint buflen; + uint padding[3]; + uchar tempbuf[BLOCK_SIZE]; } blake2s_state; static const uint blake2s_IV[8] = { @@ -810,97 +796,1440 @@ static const uint blake2s_IV[8] = { 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 }; -static const uint8_t blake2s_sigma[10][16] = { - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , - { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , - { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , - { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , -}; +#ifdef ASM -static void blake2s_compress(blake2s_state *S, const uint *buf) { - uint i; - uint m[16]; - uint v[16]; +extern void blake2s_compress(blake2s_state *S); - neoscrypt_copy(m, buf, 64); - neoscrypt_copy(v, S, 32); +#else - v[ 8] = blake2s_IV[0]; - v[ 9] = blake2s_IV[1]; +/* Buffer mixer (compressor) */ +static void blake2s_compress(blake2s_state *S) { + uint *v = (uint *) S->tempbuf; + uint *m = (uint *) S->buf; + register uint t0, t1, t2, t3; + + v[0] = S->h[0]; + v[1] = S->h[1]; + v[2] = S->h[2]; + v[3] = S->h[3]; + v[4] = S->h[4]; + v[5] = S->h[5]; + v[6] = S->h[6]; + v[7] = S->h[7]; + v[8] = blake2s_IV[0]; + v[9] = blake2s_IV[1]; v[10] = blake2s_IV[2]; v[11] = blake2s_IV[3]; v[12] = S->t[0] ^ blake2s_IV[4]; v[13] = S->t[1] ^ blake2s_IV[5]; v[14] = S->f[0] ^ blake2s_IV[6]; v[15] = S->f[1] ^ blake2s_IV[7]; -#define G(r,i,a,b,c,d) \ - do { \ - a = a + b + m[blake2s_sigma[r][2*i+0]]; \ - d = ROTR32(d ^ a, 16); \ - c = c + d; \ - b = ROTR32(b ^ c, 12); \ - a = a + b + m[blake2s_sigma[r][2*i+1]]; \ - d = ROTR32(d ^ a, 8); \ - c = c + d; \ - b = ROTR32(b ^ c, 7); \ - } while(0) -#define ROUND(r) \ - do { \ - G(r, 0, v[ 0], v[ 4], v[ 8], v[12]); \ - G(r, 1, v[ 1], v[ 5], v[ 9], v[13]); \ - G(r, 2, v[ 2], v[ 6], v[10], v[14]); \ - G(r, 3, v[ 3], v[ 7], v[11], v[15]); \ - G(r, 4, v[ 0], v[ 5], v[10], v[15]); \ - G(r, 5, v[ 1], v[ 6], v[11], v[12]); \ - G(r, 6, v[ 2], v[ 7], v[ 8], v[13]); \ - G(r, 7, v[ 3], v[ 4], v[ 9], v[14]); \ - } while(0) - ROUND(0); - ROUND(1); - ROUND(2); - ROUND(3); - ROUND(4); - ROUND(5); - ROUND(6); - ROUND(7); - ROUND(8); - ROUND(9); - - for(i = 0; i < 8; i++) - S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; -#undef G -#undef ROUND +/* Round 0 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[0]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[1]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[2]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[3]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[4]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[5]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[6]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[7]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[8]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[9]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[10]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[11]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[12]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[13]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[14]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[15]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 1 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[14]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[10]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[4]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[8]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[9]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[15]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[13]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[6]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[1]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[12]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[0]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[2]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[11]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[7]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[5]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[3]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 2 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[11]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[8]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[12]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[0]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[5]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[2]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[15]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[13]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[10]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[14]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[3]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[6]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[7]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[1]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[9]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[4]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 3 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[7]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[9]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[3]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[1]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[13]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[12]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[11]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[14]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[2]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[6]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[5]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[10]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[4]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[0]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[15]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[8]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 4 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[9]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[0]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[5]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[7]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[2]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[4]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[10]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[15]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[14]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[1]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[11]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[12]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[6]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[8]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[3]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[13]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 5 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[2]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[12]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[6]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[10]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[0]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[11]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[8]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[3]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[4]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[13]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[7]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[5]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[15]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[14]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[1]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[9]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 6 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[12]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[5]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[1]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[15]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[14]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[13]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[4]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[10]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[0]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[7]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[6]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[3]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[9]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[2]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[8]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[11]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 7 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[13]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[11]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[7]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[14]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[12]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[1]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[3]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[9]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[5]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[0]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[15]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[4]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[8]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[6]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[2]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[10]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 8 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[6]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[15]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[14]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[9]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[11]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[3]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[0]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[8]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[12]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[2]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[13]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[7]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[1]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[4]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[10]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[5]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 9 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[10]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[2]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[8]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[4]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[7]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[6]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[1]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[5]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[15]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[11]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[9]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[14]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[3]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[12]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[13]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[0]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + S->h[0] ^= v[0] ^ v[8]; + S->h[1] ^= v[1] ^ v[9]; + S->h[2] ^= v[2] ^ v[10]; + S->h[3] ^= v[3] ^ v[11]; + S->h[4] ^= v[4] ^ v[12]; + S->h[5] ^= v[5] ^ v[13]; + S->h[6] ^= v[6] ^ v[14]; + S->h[7] ^= v[7] ^ v[15]; } -static void blake2s_update(blake2s_state *S, const uchar *input, uint input_size) { +#endif /* ASM */ + +static void blake2s_update(blake2s_state *S, const uchar *input, + uint input_size) { uint left, fill; while(input_size > 0) { left = S->buflen; - fill = 2 * BLAKE2S_BLOCK_SIZE - left; + fill = 2 * BLOCK_SIZE - left; if(input_size > fill) { /* Buffer fill */ neoscrypt_copy(S->buf + left, input, fill); S->buflen += fill; /* Counter increment */ - S->t[0] += BLAKE2S_BLOCK_SIZE; + S->t[0] += BLOCK_SIZE; /* Compress */ - blake2s_compress(S, (uint *) S->buf); + blake2s_compress(S); /* Shift buffer left */ - neoscrypt_copy(S->buf, S->buf + BLAKE2S_BLOCK_SIZE, BLAKE2S_BLOCK_SIZE); - S->buflen -= BLAKE2S_BLOCK_SIZE; + neoscrypt_copy(S->buf, S->buf + BLOCK_SIZE, BLOCK_SIZE); + S->buflen -= BLOCK_SIZE; input += fill; input_size -= fill; } else { neoscrypt_copy(S->buf + left, input, input_size); - S->buflen += input_size; + S->buflen += input_size; /* Do not compress */ input += input_size; input_size = 0; @@ -908,9 +2237,9 @@ static void blake2s_update(blake2s_state *S, const uchar *input, uint input_size } } -static void neoscrypt_blake2s(const void *input, const uint input_size, const void *key, const uchar key_size, - void *output, const uchar output_size) { - uchar block[BLAKE2S_BLOCK_SIZE]; +void neoscrypt_blake2s(const void *input, const uint input_size, + const void *key, const uchar key_size, void *output, const uchar output_size) { + uchar block[BLOCK_SIZE]; blake2s_param P[1]; blake2s_state S[1]; @@ -921,52 +2250,60 @@ static void neoscrypt_blake2s(const void *input, const uint input_size, const vo P->fanout = 1; P->depth = 1; - neoscrypt_erase(S, 180); + neoscrypt_erase(S, 256); neoscrypt_copy(S, blake2s_IV, 32); neoscrypt_xor(S, P, 32); - neoscrypt_erase(block, BLAKE2S_BLOCK_SIZE); + neoscrypt_erase(block, BLOCK_SIZE); neoscrypt_copy(block, key, key_size); - blake2s_update(S, (uchar *) block, BLAKE2S_BLOCK_SIZE); + blake2s_update(S, (uchar *) block, BLOCK_SIZE); /* Update */ blake2s_update(S, (uchar *) input, input_size); /* Finish */ - if(S->buflen > BLAKE2S_BLOCK_SIZE) { - S->t[0] += BLAKE2S_BLOCK_SIZE; - blake2s_compress(S, (uint *) S->buf); - S->buflen -= BLAKE2S_BLOCK_SIZE; - neoscrypt_copy(S->buf, S->buf + BLAKE2S_BLOCK_SIZE, S->buflen); + if(S->buflen > BLOCK_SIZE) { + S->t[0] += BLOCK_SIZE; + blake2s_compress(S); + S->buflen -= BLOCK_SIZE; + neoscrypt_copy(S->buf, S->buf + BLOCK_SIZE, S->buflen); } S->t[0] += S->buflen; S->f[0] = ~0U; - neoscrypt_erase(S->buf + S->buflen, 2 * BLAKE2S_BLOCK_SIZE - S->buflen); - blake2s_compress(S, (uint *) S->buf); + neoscrypt_erase(S->buf + S->buflen, 2 * BLOCK_SIZE - S->buflen); + blake2s_compress(S); /* Write back */ neoscrypt_copy(output, S, output_size); } +#ifndef OPT #define FASTKDF_BUFFER_SIZE 256U -#define NEOSCRYPT_FASTKDF_STACK_SIZE 2 * FASTKDF_BUFFER_SIZE + BLAKE2S_BLOCK_SIZE + BLAKE2S_KEY_SIZE + BLAKE2S_OUT_SIZE + 0x40 - /* FastKDF, a fast buffered key derivation function: * FASTKDF_BUFFER_SIZE must be a power of 2; * password_len, salt_len and output_len should not exceed FASTKDF_BUFFER_SIZE; * prf_output_size must be <= prf_key_size; */ -static void neoscrypt_fastkdf(const uchar *password, uint password_len, const uchar *salt, uint salt_len, - uint N, uchar *output, uint output_len) { - const uint stack_align = 0x40, kdf_buf_size = FASTKDF_BUFFER_SIZE, - prf_input_size = BLAKE2S_BLOCK_SIZE, prf_key_size = BLAKE2S_KEY_SIZE, prf_output_size = BLAKE2S_OUT_SIZE; +void neoscrypt_fastkdf(const uchar *password, uint password_len, + const uchar *salt, uint salt_len, uint N, uchar *output, uint output_len) { + const size_t stack_align = 0x40; + const uint kdf_buf_size = FASTKDF_BUFFER_SIZE, + prf_input_size = 64, prf_key_size = 32, prf_output_size = 32; uint bufptr, a, b, i, j; uchar *A, *B, *prf_input, *prf_key, *prf_output; /* Align and set up the buffers in stack */ - uchar stack[NEOSCRYPT_FASTKDF_STACK_SIZE]; - A = &stack[stack_align & ~(stack_align - 1)]; + // No VLAs with VC ;-( +#ifndef _MSC_VER + uchar stack[2 * kdf_buf_size + prf_input_size + prf_key_size + + prf_output_size + stack_align]; +#else + uchar* stack = alloca(2 * kdf_buf_size + prf_input_size + prf_key_size + + prf_output_size + stack_align); +#endif + + A = (uchar *) (((size_t)stack & ~(stack_align - 1)) + stack_align); B = &A[kdf_buf_size + prf_input_size]; prf_output = &A[2 * kdf_buf_size + prf_input_size + prf_key_size]; @@ -1004,7 +2341,8 @@ static void neoscrypt_fastkdf(const uchar *password, uint password_len, const uc prf_key = &B[bufptr]; /* PRF */ - neoscrypt_blake2s(prf_input, prf_input_size, prf_key, prf_key_size, prf_output, prf_output_size); + neoscrypt_blake2s(prf_input, prf_input_size, prf_key, prf_key_size, + prf_output, prf_output_size); /* Calculate the next buffer pointer */ for(j = 0, bufptr = 0; j < prf_output_size; j++) @@ -1016,17 +2354,18 @@ static void neoscrypt_fastkdf(const uchar *password, uint password_len, const uc /* Head modified, tail updated */ if(bufptr < prf_key_size) - neoscrypt_copy(&B[kdf_buf_size + bufptr], &B[bufptr], MIN(prf_output_size, prf_key_size - bufptr)); - + neoscrypt_copy(&B[kdf_buf_size + bufptr], &B[bufptr], + MIN(prf_output_size, prf_key_size - bufptr)); /* Tail modified, head updated */ - if((kdf_buf_size - bufptr) < prf_output_size) - neoscrypt_copy(&B[0], &B[kdf_buf_size], prf_output_size - (kdf_buf_size - bufptr)); + else if((kdf_buf_size - bufptr) < prf_output_size) + neoscrypt_copy(&B[0], &B[kdf_buf_size], + prf_output_size - (kdf_buf_size - bufptr)); } /* Modify and copy into the output buffer */ if(output_len > kdf_buf_size) - output_len = kdf_buf_size; + output_len = kdf_buf_size; a = kdf_buf_size - bufptr; if(a >= output_len) { @@ -1041,6 +2380,111 @@ static void neoscrypt_fastkdf(const uchar *password, uint password_len, const uc } +#else + +#ifdef ASM + +extern void neoscrypt_fastkdf_opt(const uchar *password, const uchar *salt, + uchar *output, uint mode); + +#else + +/* Initialisation vector with a parameter block XOR'ed in */ +static const uint blake2s_IV_P_XOR[8] = { + 0x6B08C647, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 +}; + +/* Performance optimised FastKDF with BLAKE2s integrated */ +void neoscrypt_fastkdf_opt(const uchar *password, const uchar *salt, + uchar *output, uint mode) { + const size_t stack_align = 0x40; + uint bufptr, output_len, i, j; + uchar *A, *B; + uint *S; + + /* Align and set up the buffers in stack */ + uchar stack[864 + stack_align]; + A = (uchar *) (((size_t)stack & ~(stack_align - 1)) + stack_align); + B = &A[320]; + S = (uint *) &A[608]; + + neoscrypt_copy(&A[0], &password[0], 80); + neoscrypt_copy(&A[80], &password[0], 80); + neoscrypt_copy(&A[160], &password[0], 80); + neoscrypt_copy(&A[240], &password[0], 16); + neoscrypt_copy(&A[256], &password[0], 64); + + if(!mode) { + output_len = 256; + neoscrypt_copy(&B[0], &salt[0], 80); + neoscrypt_copy(&B[80], &salt[0], 80); + neoscrypt_copy(&B[160], &salt[0], 80); + neoscrypt_copy(&B[240], &salt[0], 16); + neoscrypt_copy(&B[256], &salt[0], 32); + } else { + output_len = 32; + neoscrypt_copy(&B[0], &salt[0], 256); + neoscrypt_copy(&B[256], &salt[0], 32); + } + + for(i = 0, bufptr = 0; i < 32; i++) { + + /* BLAKE2s: initialise */ + neoscrypt_copy(&S[0], blake2s_IV_P_XOR, 32); + neoscrypt_erase(&S[8], 16); + + /* BLAKE2s: update key */ + neoscrypt_copy(&S[12], &B[bufptr], 32); + neoscrypt_erase(&S[20], 32); + + /* BLAKE2s: compress IV using key */ + S[8] = 64; + blake2s_compress((blake2s_state *) S); + + /* BLAKE2s: update input */ + neoscrypt_copy(&S[12], &A[bufptr], 64); + + /* BLAKE2s: compress again using input */ + S[8] = 128; + S[10] = ~0U; + blake2s_compress((blake2s_state *) S); + + for(j = 0, bufptr = 0; j < 8; j++) { + bufptr += S[j]; + bufptr += (S[j] >> 8); + bufptr += (S[j] >> 16); + bufptr += (S[j] >> 24); + } + bufptr &= 0xFF; + + neoscrypt_xor(&B[bufptr], &S[0], 32); + + if(bufptr < 32) + neoscrypt_copy(&B[256 + bufptr], &B[bufptr], 32 - bufptr); + else if(bufptr > 224) + neoscrypt_copy(&B[0], &B[256], bufptr - 224); + + } + + i = 256 - bufptr; + if(i >= output_len) { + neoscrypt_xor(&B[bufptr], &A[0], output_len); + neoscrypt_copy(&output[0], &B[bufptr], output_len); + } else { + neoscrypt_xor(&B[bufptr], &A[0], i); + neoscrypt_xor(&B[0], &A[i], output_len - i); + neoscrypt_copy(&output[0], &B[bufptr], i); + neoscrypt_copy(&output[i], &B[0], output_len - i); + } +} + +#endif /* ASM */ + +#endif /* !(OPT) */ + + +#ifndef ASM /* Configurable optimised block mixer */ static void neoscrypt_blkmix(uint *X, uint *Y, uint r, uint mixmode) { @@ -1058,60 +2502,62 @@ static void neoscrypt_blkmix(uint *X, uint *Y, uint r, uint mixmode) { Xc" = Yb; Xd" = Yd; */ if(r == 1) { - neoscrypt_blkxor(&X[0], &X[16], SCRYPT_BLOCK_SIZE); - if(mixer) - neoscrypt_chacha(&X[0], rounds); - else - neoscrypt_salsa(&X[0], rounds); - neoscrypt_blkxor(&X[16], &X[0], SCRYPT_BLOCK_SIZE); - if(mixer) - neoscrypt_chacha(&X[16], rounds); - else - neoscrypt_salsa(&X[16], rounds); + if(mixer) { + neoscrypt_blkxor(&X[0], &X[16], BLOCK_SIZE); + neoscrypt_chacha(&X[0], rounds); + neoscrypt_blkxor(&X[16], &X[0], BLOCK_SIZE); + neoscrypt_chacha(&X[16], rounds); + } else { + neoscrypt_blkxor(&X[0], &X[16], BLOCK_SIZE); + neoscrypt_salsa(&X[0], rounds); + neoscrypt_blkxor(&X[16], &X[0], BLOCK_SIZE); + neoscrypt_salsa(&X[16], rounds); + } return; } if(r == 2) { - neoscrypt_blkxor(&X[0], &X[48], SCRYPT_BLOCK_SIZE); - if(mixer) - neoscrypt_chacha(&X[0], rounds); - else - neoscrypt_salsa(&X[0], rounds); - neoscrypt_blkxor(&X[16], &X[0], SCRYPT_BLOCK_SIZE); - if(mixer) - neoscrypt_chacha(&X[16], rounds); - else - neoscrypt_salsa(&X[16], rounds); - neoscrypt_blkxor(&X[32], &X[16], SCRYPT_BLOCK_SIZE); - if(mixer) - neoscrypt_chacha(&X[32], rounds); - else - neoscrypt_salsa(&X[32], rounds); - neoscrypt_blkxor(&X[48], &X[32], SCRYPT_BLOCK_SIZE); - if(mixer) - neoscrypt_chacha(&X[48], rounds); - else - neoscrypt_salsa(&X[48], rounds); - neoscrypt_blkswp(&X[16], &X[32], SCRYPT_BLOCK_SIZE); + if(mixer) { + neoscrypt_blkxor(&X[0], &X[48], BLOCK_SIZE); + neoscrypt_chacha(&X[0], rounds); + neoscrypt_blkxor(&X[16], &X[0], BLOCK_SIZE); + neoscrypt_chacha(&X[16], rounds); + neoscrypt_blkxor(&X[32], &X[16], BLOCK_SIZE); + neoscrypt_chacha(&X[32], rounds); + neoscrypt_blkxor(&X[48], &X[32], BLOCK_SIZE); + neoscrypt_chacha(&X[48], rounds); + neoscrypt_blkswp(&X[16], &X[32], BLOCK_SIZE); + } else { + neoscrypt_blkxor(&X[0], &X[48], BLOCK_SIZE); + neoscrypt_salsa(&X[0], rounds); + neoscrypt_blkxor(&X[16], &X[0], BLOCK_SIZE); + neoscrypt_salsa(&X[16], rounds); + neoscrypt_blkxor(&X[32], &X[16], BLOCK_SIZE); + neoscrypt_salsa(&X[32], rounds); + neoscrypt_blkxor(&X[48], &X[32], BLOCK_SIZE); + neoscrypt_salsa(&X[48], rounds); + neoscrypt_blkswp(&X[16], &X[32], BLOCK_SIZE); + } return; } /* Reference code for any reasonable r */ for(i = 0; i < 2 * r; i++) { - if(i) neoscrypt_blkxor(&X[16 * i], &X[16 * (i - 1)], SCRYPT_BLOCK_SIZE); - else neoscrypt_blkxor(&X[0], &X[16 * (2 * r - 1)], SCRYPT_BLOCK_SIZE); + if(i) neoscrypt_blkxor(&X[16 * i], &X[16 * (i - 1)], BLOCK_SIZE); + else neoscrypt_blkxor(&X[0], &X[16 * (2 * r - 1)], BLOCK_SIZE); if(mixer) neoscrypt_chacha(&X[16 * i], rounds); else neoscrypt_salsa(&X[16 * i], rounds); - neoscrypt_blkcpy(&Y[16 * i], &X[16 * i], SCRYPT_BLOCK_SIZE); + neoscrypt_blkcpy(&Y[16 * i], &X[16 * i], BLOCK_SIZE); } for(i = 0; i < r; i++) - neoscrypt_blkcpy(&X[16 * i], &Y[16 * 2 * i], SCRYPT_BLOCK_SIZE); + neoscrypt_blkcpy(&X[16 * i], &Y[16 * 2 * i], BLOCK_SIZE); for(i = 0; i < r; i++) - neoscrypt_blkcpy(&X[16 * (i + r)], &Y[16 * (2 * i + 1)], SCRYPT_BLOCK_SIZE); + neoscrypt_blkcpy(&X[16 * (i + r)], &Y[16 * (2 * i + 1)], BLOCK_SIZE); } + /* NeoScrypt core engine: * p = 1, salt = password; * Basic customisation (required): @@ -1143,11 +2589,9 @@ static void neoscrypt_blkmix(uint *X, uint *Y, uint r, uint mixmode) { * ..... * 11110 = N of 2147483648; * profile bits 30 to 13 are reserved */ -void neoscrypt(const char *input, char *uoutput, int profile) { - const unsigned char *password = input; - const unsigned char *output = uoutput; - - uint N = 128, r = 2, dblmix = 1, mixmode = 0x14, stack_align = 0x40; +void neoscrypt(const uchar *password, uchar *output, uint profile) { + const size_t stack_align = 0x40; + uint N = 128, r = 2, dblmix = 1, mixmode = 0x14; uint kdf, i, j; uint *X, *Y, *Z, *V; @@ -1163,20 +2607,19 @@ void neoscrypt(const char *input, char *uoutput, int profile) { r = (1 << ((profile >> 5) & 0x7)); } - const int stack_size = (N + 3) * r * 2 * SCRYPT_BLOCK_SIZE + stack_align; - -#if !defined(_MSC_VER) - uchar stack[stack_size]; + // No VLAs with VC ;-( +#ifndef _MSC_VER + uchar stack[(N + 3) * r * 2 * BLOCK_SIZE + stack_align]; #else - uchar *stack = _alloca(stack_size); + uchar* stack = alloca((N + 3) * r * 2 * BLOCK_SIZE + stack_align); #endif - /* X = r * 2 * SCRYPT_BLOCK_SIZE */ - X = (uint *) &stack[stack_align & ~(stack_align - 1)]; + /* X = r * 2 * BLOCK_SIZE */ + X = (uint *) (((size_t)stack & ~(stack_align - 1)) + stack_align); /* Z is a copy of X for ChaCha */ Z = &X[32 * r]; /* Y is an X sized temporal space */ Y = &X[64 * r]; - /* V = N * r * 2 * SCRYPT_BLOCK_SIZE */ + /* V = N * r * 2 * BLOCK_SIZE */ V = &X[96 * r]; /* X = KDF(password, salt) */ @@ -1186,55 +2629,58 @@ void neoscrypt(const char *input, char *uoutput, int profile) { default: case(0x0): - neoscrypt_fastkdf(password, 80, password, 80, 32, (uchar *) X, r * 2 * SCRYPT_BLOCK_SIZE); +#ifdef OPT + neoscrypt_fastkdf_opt(password, password, (uchar *) X, 0); +#else + neoscrypt_fastkdf(password, 80, password, 80, 32, + (uchar *) X, r * 2 * BLOCK_SIZE); +#endif break; -#if (NEOSCRYPT_SHA256) +#ifdef SHA256 case(0x1): - neoscrypt_pbkdf2_sha256(password, 80, password, 80, 1, (uchar *) X, r * 2 * SCRYPT_BLOCK_SIZE); + neoscrypt_pbkdf2_sha256(password, 80, password, 80, 1, + (uchar *) X, r * 2 * BLOCK_SIZE); break; #endif -#if (NEOSCRYPT_BLAKE256) +#ifdef BLAKE256 case(0x2): - neoscrypt_pbkdf2_blake256(password, 80, password, 80, 1, (uchar *) X, r * 2 * SCRYPT_BLOCK_SIZE); + neoscrypt_pbkdf2_blake256(password, 80, password, 80, 1, + (uchar *) X, r * 2 * BLOCK_SIZE); break; #endif } - /* Process ChaCha 1st, Salsa 2nd and XOR them into PBKDF2; otherwise Salsa only */ + /* Process ChaCha 1st, Salsa 2nd and XOR them into FastKDF; otherwise Salsa only */ if(dblmix) { /* blkcpy(Z, X) */ - neoscrypt_blkcpy(&Z[0], &X[0], r * 2 * SCRYPT_BLOCK_SIZE); + neoscrypt_blkcpy(&Z[0], &X[0], r * 2 * BLOCK_SIZE); /* Z = SMix(Z) */ for(i = 0; i < N; i++) { /* blkcpy(V, Z) */ - neoscrypt_blkcpy(&V[i * (32 * r)], &Z[0], r * 2 * SCRYPT_BLOCK_SIZE); + neoscrypt_blkcpy(&V[i * (32 * r)], &Z[0], r * 2 * BLOCK_SIZE); /* blkmix(Z, Y) */ neoscrypt_blkmix(&Z[0], &Y[0], r, (mixmode | 0x0100)); } + for(i = 0; i < N; i++) { /* integerify(Z) mod N */ j = (32 * r) * (Z[16 * (2 * r - 1)] & (N - 1)); /* blkxor(Z, V) */ - neoscrypt_blkxor(&Z[0], &V[j], r * 2 * SCRYPT_BLOCK_SIZE); + neoscrypt_blkxor(&Z[0], &V[j], r * 2 * BLOCK_SIZE); /* blkmix(Z, Y) */ neoscrypt_blkmix(&Z[0], &Y[0], r, (mixmode | 0x0100)); } } -#if (ASM) - /* Must be called before and after SSE2 Salsa */ - neoscrypt_salsa_tangle(&X[0], r * 2); -#endif - /* X = SMix(X) */ for(i = 0; i < N; i++) { /* blkcpy(V, X) */ - neoscrypt_blkcpy(&V[i * (32 * r)], &X[0], r * 2 * SCRYPT_BLOCK_SIZE); + neoscrypt_blkcpy(&V[i * (32 * r)], &X[0], r * 2 * BLOCK_SIZE); /* blkmix(X, Y) */ neoscrypt_blkmix(&X[0], &Y[0], r, mixmode); } @@ -1242,36 +2688,39 @@ void neoscrypt(const char *input, char *uoutput, int profile) { /* integerify(X) mod N */ j = (32 * r) * (X[16 * (2 * r - 1)] & (N - 1)); /* blkxor(X, V) */ - neoscrypt_blkxor(&X[0], &V[j], r * 2 * SCRYPT_BLOCK_SIZE); + neoscrypt_blkxor(&X[0], &V[j], r * 2 * BLOCK_SIZE); /* blkmix(X, Y) */ neoscrypt_blkmix(&X[0], &Y[0], r, mixmode); } -#if (ASM) - neoscrypt_salsa_tangle(&X[0], r * 2); -#endif - if(dblmix) /* blkxor(X, Z) */ - neoscrypt_blkxor(&X[0], &Z[0], r * 2 * SCRYPT_BLOCK_SIZE); + neoscrypt_blkxor(&X[0], &Z[0], r * 2 * BLOCK_SIZE); /* output = KDF(password, X) */ switch(kdf) { default: case(0x0): - neoscrypt_fastkdf(password, 80, (uchar *) X, r * 2 * SCRYPT_BLOCK_SIZE, 32, output, 32); +#ifdef OPT + neoscrypt_fastkdf_opt(password, (uchar *) X, output, 1); +#else + neoscrypt_fastkdf(password, 80, (uchar *) X, + r * 2 * BLOCK_SIZE, 32, output, 32); +#endif break; -#if (NEOSCRYPT_SHA256) +#ifdef SHA256 case(0x1): - neoscrypt_pbkdf2_sha256(password, 80, (uchar *) X, r * 2 * SCRYPT_BLOCK_SIZE, 1, output, 32); + neoscrypt_pbkdf2_sha256(password, 80, (uchar *) X, + r * 2 * BLOCK_SIZE, 1, output, 32); break; #endif -#if (NEOSCRYPT_BLAKE256) +#ifdef BLAKE256 case(0x2): - neoscrypt_pbkdf2_blake256(password, 80, (uchar *) X, r * 2 * SCRYPT_BLOCK_SIZE, 1, output, 32); + neoscrypt_pbkdf2_blake256(password, 80, (uchar *) X, + r * 2 * BLOCK_SIZE, 1, output, 32); break; #endif @@ -1279,119 +2728,552 @@ void neoscrypt(const char *input, char *uoutput, int profile) { } +#endif /* !(ASM) */ + + +#if defined(ASM) && defined(MINER_4WAY) + +extern void neoscrypt_xor_salsa_4way(uint *X, uint *X0, uint *Y, uint double_rounds); +extern void neoscrypt_xor_chacha_4way(uint *Z, uint *Z0, uint *Y, uint double_rounds); + +#if (1) + +extern void neoscrypt_blkcpy(void *dstp, const void *srcp, uint len); +extern void neoscrypt_blkswp(void *blkAp, void *blkBp, uint len); +extern void neoscrypt_blkxor(void *dstp, const void *srcp, uint len); + +extern void neoscrypt_pack_4way(void *dstp, const void *srcp, uint len); +extern void neoscrypt_unpack_4way(void *dstp, const void *srcp, uint len); +extern void neoscrypt_xor_4way(void *dstp, const void *srcAp, + const void *srcBp, const void *srcCp, const void *srcDp, uint len); + +#else + +/* The following code is for reference only */ + +static void neoscrypt_blkcpy(void *dstp, const void *srcp, uint len) { + size_t *dst = (size_t *) dstp; + size_t *src = (size_t *) srcp; + uint i; + + for(i = 0; i < (len / sizeof(size_t)); i += 4) { + dst[i] = src[i]; + dst[i + 1] = src[i + 1]; + dst[i + 2] = src[i + 2]; + dst[i + 3] = src[i + 3]; + } +} + +static void neoscrypt_blkswp(void *blkAp, void *blkBp, uint len) { + size_t *blkA = (size_t *) blkAp; + size_t *blkB = (size_t *) blkBp; + register size_t t0, t1, t2, t3; + uint i; + + for(i = 0; i < (len / sizeof(size_t)); i += 4) { + t0 = blkA[i]; + t1 = blkA[i + 1]; + t2 = blkA[i + 2]; + t3 = blkA[i + 3]; + blkA[i] = blkB[i]; + blkA[i + 1] = blkB[i + 1]; + blkA[i + 2] = blkB[i + 2]; + blkA[i + 3] = blkB[i + 3]; + blkB[i] = t0; + blkB[i + 1] = t1; + blkB[i + 2] = t2; + blkB[i + 3] = t3; + } +} + +static void neoscrypt_blkxor(void *dstp, const void *srcp, uint len) { + size_t *dst = (size_t *) dstp; + size_t *src = (size_t *) srcp; + uint i; + + for(i = 0; i < (len / sizeof(size_t)); i += 4) { + dst[i] ^= src[i]; + dst[i + 1] ^= src[i + 1]; + dst[i + 2] ^= src[i + 2]; + dst[i + 3] ^= src[i + 3]; + } +} + + +static void neoscrypt_pack_4way(void *dstp, const void *srcp, uint len) { + uint *dst = (uint *) dstp; + uint *src = (uint *) srcp; + uint i, j; + + len >>= 4; + + for(i = 0, j = 0; j < len; i += 4, j++) { + dst[i] = src[j]; + dst[i + 1] = src[j + len]; + dst[i + 2] = src[j + 2 * len]; + dst[i + 3] = src[j + 3 * len]; + } +} -#if (NEOSCRYPT_TEST) +static void neoscrypt_unpack_4way(void *dstp, const void *srcp, uint len) { + uint *dst = (uint *) dstp; + uint *src = (uint *) srcp; + uint i, j; -#include + len >>= 4; -int main() { - uint prf_input_len = 64, prf_key_len = 32, prf_output_len = 32; - uint kdf_input_len = 80, kdf_output_len = 256, N = 32; - uint neoscrypt_output_len = 32; - uchar input[kdf_input_len], output[kdf_output_len]; + for(i = 0, j = 0; j < len; i += 4, j++) { + dst[j] = src[i]; + dst[j + len] = src[i + 1]; + dst[j + 2 * len] = src[i + 2]; + dst[j + 3 * len] = src[i + 3]; + } +} + +static void neoscrypt_xor_4way(void *dstp, const void *srcAp, + const void *srcBp, const void *srcCp, const void *srcDp, uint len) { + uint *dst = (uint *) dstp; + uint *srcA = (uint *) srcAp; + uint *srcB = (uint *) srcBp; + uint *srcC = (uint *) srcCp; + uint *srcD = (uint *) srcDp; uint i; - bool fail; - for(i = 0; i < kdf_input_len; i++) { - input[i] = i; + for(i = 0; i < (len >> 2); i += 4) { + dst[i] ^= srcA[i]; + dst[i + 1] ^= srcB[i + 1]; + dst[i + 2] ^= srcC[i + 2]; + dst[i + 3] ^= srcD[i + 3]; } +} - neoscrypt_blake2s(input, prf_input_len, input, prf_key_len, output, prf_output_len); +#endif - uchar blake2s_ref[32] = { - 0x89, 0x75, 0xB0, 0x57, 0x7F, 0xD3, 0x55, 0x66, - 0xD7, 0x50, 0xB3, 0x62, 0xB0, 0x89, 0x7A, 0x26, - 0xC3, 0x99, 0x13, 0x6D, 0xF0, 0x7B, 0xAB, 0xAB, - 0xBD, 0xE6, 0x20, 0x3F, 0xF2, 0x95, 0x4E, 0xD4 }; - for(i = 0, fail = 0; i < prf_output_len; i++) { - if(output[i] != blake2s_ref[i]) { - fail = 1; - break; - } +/* 4-way NeoScrypt(128, 2, 1) with Salsa20/20 and ChaCha20/20 */ +void neoscrypt_4way(const uchar *password, uchar *output, uchar *scratchpad) { + const uint N = 128, r = 2, double_rounds = 10; + uint *X, *Z, *V, *Y, *P; + uint i, j0, j1, j2, j3, k; + + /* 2 * BLOCK_SIZE compacted to 128 below */; + + /* Scratchpad size is 4 * ((N + 3) * r * 128 + 80) bytes */ + + X = (uint *) &scratchpad[0]; + Z = &X[4 * 32 * r]; + V = &X[4 * 64 * r]; + /* Y is a temporary work space */ + Y = &X[4 * (N + 2) * 32 * r]; + /* P is a set of passwords 80 bytes each */ + P = &X[4 * (N + 3) * 32 * r]; + + /* Load the password and increment nonces */ + for(k = 0; k < 4; k++) { + neoscrypt_copy(&P[k * 20], password, 80); + P[(k + 1) * 20 - 1] += k; } - if(fail) { - printf("BLAKE2s integrity test failed!\n"); - return(1); - } else { - printf("BLAKE2s integrity test passed.\n"); + neoscrypt_fastkdf_4way((uchar *) &P[0], (uchar *) &P[0], (uchar *) &Y[0], + (uchar *) &scratchpad[0], 0); + + neoscrypt_pack_4way(&X[0], &Y[0], 4 * r * 128); + + neoscrypt_blkcpy(&Z[0], &X[0], 4 * r * 128); + + for(i = 0; i < N; i++) { + neoscrypt_blkcpy(&V[i * r * 128], &Z[0], 4 * r * 128); + neoscrypt_xor_chacha_4way(&Z[0], &Z[192], &Y[0], double_rounds); + neoscrypt_xor_chacha_4way(&Z[64], &Z[0], &Y[0], double_rounds); + neoscrypt_xor_chacha_4way(&Z[128], &Z[64], &Y[0], double_rounds); + neoscrypt_xor_chacha_4way(&Z[192], &Z[128], &Y[0], double_rounds); + neoscrypt_blkswp(&Z[64], &Z[128], r * 128); } - neoscrypt_fastkdf(input, kdf_input_len, input, kdf_input_len, N, output, kdf_output_len); - - uchar fastkdf_ref[256] = { - 0xCC, 0xBC, 0x19, 0x71, 0xEC, 0x44, 0xE3, 0x17, - 0xB3, 0xC9, 0xDE, 0x16, 0x76, 0x02, 0x60, 0xB8, - 0xE2, 0xD4, 0x79, 0xB6, 0x88, 0xCA, 0xB5, 0x4A, - 0xCF, 0x6E, 0x0E, 0x9A, 0xAE, 0x48, 0x78, 0x12, - 0xA1, 0x95, 0x1E, 0xE1, 0xD1, 0x0A, 0xC2, 0x94, - 0x1F, 0x0A, 0x39, 0x73, 0xFE, 0xA4, 0xCD, 0x87, - 0x4B, 0x38, 0x54, 0x72, 0xB5, 0x53, 0xC3, 0xEA, - 0xC1, 0x26, 0x8D, 0xA7, 0xFF, 0x3F, 0xC1, 0x79, - 0xA6, 0xFF, 0x96, 0x54, 0x29, 0x05, 0xC0, 0x22, - 0x90, 0xDB, 0x53, 0x87, 0x2D, 0x29, 0x00, 0xA6, - 0x14, 0x16, 0x38, 0x63, 0xDA, 0xBC, 0x0E, 0x99, - 0x68, 0xB3, 0x98, 0x92, 0x42, 0xE3, 0xF6, 0xB4, - 0x19, 0xE3, 0xE3, 0xF6, 0x8E, 0x67, 0x47, 0x7B, - 0xB6, 0xFB, 0xEA, 0xCE, 0x6D, 0x0F, 0xAF, 0xF6, - 0x19, 0x43, 0x8D, 0xF7, 0x3E, 0xB5, 0xFB, 0xA3, - 0x64, 0x5E, 0xD2, 0x72, 0x80, 0x6B, 0x39, 0x93, - 0xB7, 0x80, 0x04, 0xCB, 0xF5, 0xC2, 0x61, 0xB1, - 0x90, 0x4E, 0x2B, 0x02, 0x57, 0x53, 0x77, 0x16, - 0x6A, 0x52, 0xBD, 0xD1, 0x62, 0xEC, 0xA1, 0xCB, - 0x89, 0x03, 0x29, 0xA2, 0x02, 0x5C, 0x9A, 0x62, - 0x99, 0x44, 0x54, 0xEA, 0x44, 0x91, 0x27, 0x3A, - 0x50, 0x82, 0x62, 0x03, 0x99, 0xB3, 0xFA, 0xF7, - 0xD4, 0x13, 0x47, 0x61, 0xFB, 0x0A, 0xE7, 0x81, - 0x61, 0x57, 0x58, 0x4C, 0x69, 0x4E, 0x67, 0x0A, - 0xC1, 0x21, 0xA7, 0xD2, 0xF6, 0x6D, 0x2F, 0x10, - 0x01, 0xFB, 0xA5, 0x47, 0x2C, 0xE5, 0x15, 0xD7, - 0x6A, 0xEF, 0xC9, 0xE2, 0xC2, 0x88, 0xA2, 0x3B, - 0x6C, 0x8D, 0xBB, 0x26, 0xE7, 0xC4, 0x15, 0xEC, - 0x5E, 0x5D, 0x74, 0x79, 0xBD, 0x81, 0x35, 0xA1, - 0x42, 0x27, 0xEB, 0x57, 0xCF, 0xF6, 0x2E, 0x51, - 0x90, 0xFD, 0xD9, 0xE4, 0x53, 0x6E, 0x12, 0xA1, - 0x99, 0x79, 0x4D, 0x29, 0x6F, 0x5B, 0x4D, 0x9A }; - - for(i = 0, fail = 0; i < kdf_output_len; i++) { - if(output[i] != fastkdf_ref[i]) { - fail = 1; - break; - } + for(i = 0; i < N; i++) { + j0 = (4 * r * 32) * (Z[64 * (2 * r - 1)] & (N - 1)); + j1 = (4 * r * 32) * (Z[1 + (64 * (2 * r - 1))] & (N - 1)); + j2 = (4 * r * 32) * (Z[2 + (64 * (2 * r - 1))] & (N - 1)); + j3 = (4 * r * 32) * (Z[3 + (64 * (2 * r - 1))] & (N - 1)); + neoscrypt_xor_4way(&Z[0], + &V[j0], &V[j1], &V[j2], &V[j3], 4 * r * 128); + neoscrypt_xor_chacha_4way(&Z[0], &Z[192], &Y[0], double_rounds); + neoscrypt_xor_chacha_4way(&Z[64], &Z[0], &Y[0], double_rounds); + neoscrypt_xor_chacha_4way(&Z[128], &Z[64], &Y[0], double_rounds); + neoscrypt_xor_chacha_4way(&Z[192], &Z[128], &Y[0], double_rounds); + neoscrypt_blkswp(&Z[64], &Z[128], 256); } - if(fail) { - printf("FastKDF integrity test failed!\n"); - return(1); - } else { - printf("FastKDF integrity test passed.\n"); + for(i = 0; i < N; i++) { + neoscrypt_blkcpy(&V[i * r * 128], &X[0], 4 * r * 128); + neoscrypt_xor_salsa_4way(&X[0], &X[192], &Y[0], double_rounds); + neoscrypt_xor_salsa_4way(&X[64], &X[0], &Y[0], double_rounds); + neoscrypt_xor_salsa_4way(&X[128], &X[64], &Y[0], double_rounds); + neoscrypt_xor_salsa_4way(&X[192], &X[128], &Y[0], double_rounds); + neoscrypt_blkswp(&X[64], &X[128], r * 128); } - neoscrypt(input, output, 0x80000620); + for(i = 0; i < N; i++) { + j0 = (4 * r * 32) * (X[64 * (2 * r - 1)] & (N - 1)); + j1 = (4 * r * 32) * (X[1 + (64 * (2 * r - 1))] & (N - 1)); + j2 = (4 * r * 32) * (X[2 + (64 * (2 * r - 1))] & (N - 1)); + j3 = (4 * r * 32) * (X[3 + (64 * (2 * r - 1))] & (N - 1)); + neoscrypt_xor_4way(&X[0], + &V[j0], &V[j1], &V[j2], &V[j3], 4 * r * 128); + neoscrypt_xor_salsa_4way(&X[0], &X[192], &Y[0], double_rounds); + neoscrypt_xor_salsa_4way(&X[64], &X[0], &Y[0], double_rounds); + neoscrypt_xor_salsa_4way(&X[128], &X[64], &Y[0], double_rounds); + neoscrypt_xor_salsa_4way(&X[192], &X[128], &Y[0], double_rounds); + neoscrypt_blkswp(&X[64], &X[128], r * 128); + } - uchar neoscrypt_ref[32] = { - 0x72, 0x58, 0x96, 0x1A, 0xFB, 0x33, 0xFD, 0x12, - 0xD0, 0x0C, 0xAC, 0xB8, 0xD6, 0x3F, 0x4F, 0x4F, - 0x52, 0xBB, 0x69, 0x17, 0x04, 0x38, 0x65, 0xDD, - 0x24, 0xA0, 0x8F, 0x57, 0x88, 0x53, 0x12, 0x2D }; + neoscrypt_blkxor(&X[0], &Z[0], 4 * r * 128); - for(i = 0, fail = 0; i < neoscrypt_output_len; i++) { - if(output[i] != neoscrypt_ref[i]) { - fail = 1; - break; + neoscrypt_unpack_4way(&Y[0], &X[0], 4 * r * 128); + + neoscrypt_fastkdf_4way((uchar *) &P[0], (uchar *) &Y[0], (uchar *) &output[0], + (uchar *) &scratchpad[0], 1); +} + +#ifdef SHA256 +/* 4-way Scrypt(1024, 1, 1) with Salsa20/8 */ +void scrypt_4way(const uchar *password, uchar *output, uchar *scratchpad) { + const uint N = 1024, r = 1, double_rounds = 4; + uint *X, *V, *Y, *P; + uint i, j0, j1, j2, j3, k; + + /* Scratchpad size is 4 * ((N + 2) * r * 128 + 80) bytes */ + + X = (uint *) &scratchpad[0]; + V = &X[4 * 32 * r]; + Y = &X[4 * (N + 1) * 32 * r]; + P = &X[4 * (N + 2) * 32 * r]; + + for(k = 0; k < 4; k++) { + neoscrypt_copy(&P[k * 20], password, 80); + P[(k + 1) * 20 - 1] += k; + } + + for(k = 0; k < 4; k++) + neoscrypt_pbkdf2_sha256((uchar *) &P[k * 20], 80, + (uchar *) &P[k * 20], 80, 1, + (uchar *) &Y[k * r * 32], r * 128); + + neoscrypt_pack_4way(&X[0], &Y[0], 4 * r * 128); + + for(i = 0; i < N; i++) { + neoscrypt_blkcpy(&V[i * r * 128], &X[0], 4 * r * 128); + neoscrypt_xor_salsa_4way(&X[0], &X[64], &Y[0], double_rounds); + neoscrypt_xor_salsa_4way(&X[64], &X[0], &Y[0], double_rounds); + } + + for(i = 0; i < N; i++) { + j0 = (4 * r * 32) * (X[64 * (2 * r - 1)] & (N - 1)); + j1 = (4 * r * 32) * (X[1 + (64 * (2 * r - 1))] & (N - 1)); + j2 = (4 * r * 32) * (X[2 + (64 * (2 * r - 1))] & (N - 1)); + j3 = (4 * r * 32) * (X[3 + (64 * (2 * r - 1))] & (N - 1)); + neoscrypt_xor_4way(&X[0], + &V[j0], &V[j1], &V[j2], &V[j3], 4 * r * 128); + neoscrypt_xor_salsa_4way(&X[0], &X[64], &Y[0], double_rounds); + neoscrypt_xor_salsa_4way(&X[64], &X[0], &Y[0], double_rounds); + } + + neoscrypt_unpack_4way(&Y[0], &X[0], 4 * r * 128); + + for(k = 0; k < 4; k++) + neoscrypt_pbkdf2_sha256((uchar *) &P[k * 20], 80, + (uchar *) &Y[k * r * 32], r * 128, 1, + (uchar *) &output[k * 32], 32); +} +#endif /* SHA256 */ + + +extern void blake2s_compress_4way(void *T); + +/* 4-way initialisation vector with a parameter block XOR'ed in */ +static const uint blake2s_IV_P_XOR_4way[32] = { + 0x6B08C647, 0x6B08C647, 0x6B08C647, 0x6B08C647, + 0xBB67AE85, 0xBB67AE85, 0xBB67AE85, 0xBB67AE85, + 0x3C6EF372, 0x3C6EF372, 0x3C6EF372, 0x3C6EF372, + 0xA54FF53A, 0xA54FF53A, 0xA54FF53A, 0xA54FF53A, + 0x510E527F, 0x510E527F, 0x510E527F, 0x510E527F, + 0x9B05688C, 0x9B05688C, 0x9B05688C, 0x9B05688C, + 0x1F83D9AB, 0x1F83D9AB, 0x1F83D9AB, 0x1F83D9AB, + 0x5BE0CD19, 0x5BE0CD19, 0x5BE0CD19, 0x5BE0CD19 +}; + +/* 4-way BLAKE2s implementation */ +void neoscrypt_blake2s_4way(const uchar *input, const uchar *key, uchar *output) { + const size_t stack_align = 0x40; + uint *T; + + /* Align and set up the buffer in stack */ + uchar stack[704 + stack_align]; + T = (uint *) (((size_t)stack & ~(stack_align - 1)) + stack_align); + + /* Initialise */ + neoscrypt_copy(&T[0], blake2s_IV_P_XOR_4way, 128); + neoscrypt_erase(&T[32], 64); + + /* Update keys */ + neoscrypt_pack_4way(&T[48], &key[0], 128); + neoscrypt_erase(&T[80], 128); + + /* Compress IVs using keys */ + T[32] = 64; + T[33] = 64; + T[34] = 64; + T[35] = 64; + blake2s_compress_4way(&T[0]); + + /* Update inputs */ + neoscrypt_pack_4way(&T[48], &input[0], 256); + + /* Compress using inputs */ + T[32] = 128; + T[33] = 128; + T[34] = 128; + T[35] = 128; + T[40] = ~0U; + T[41] = ~0U; + T[42] = ~0U; + T[43] = ~0U; + blake2s_compress_4way(&T[0]); + + neoscrypt_unpack_4way(&output[0], &T[0], 128); +} + + +/* 4-way FastKDF with BLAKE2s integrated */ +void neoscrypt_fastkdf_4way(const uchar *password, const uchar *salt, + uchar *output, uchar *scratchpad, uint mode) { + uint bufptr_a = 0, bufptr_b = 0, bufptr_c = 0, bufptr_d = 0; + uint output_len, i, j; + uint *T; + uchar *Aa, *Ab, *Ac, *Ad; + uchar *Ba, *Bb, *Bc, *Bd; + + T = (uint *) &scratchpad[0]; + Aa = (uchar *) &T[176]; + Ab = (uchar *) &T[256]; + Ac = (uchar *) &T[336]; + Ad = (uchar *) &T[416]; + Ba = (uchar *) &T[496]; + Bb = (uchar *) &T[568]; + Bc = (uchar *) &T[640]; + Bd = (uchar *) &T[712]; + + neoscrypt_copy(&Aa[0], &password[0], 80); + neoscrypt_copy(&Aa[80], &password[0], 80); + neoscrypt_copy(&Aa[160], &password[0], 80); + neoscrypt_copy(&Aa[240], &password[0], 16); + neoscrypt_copy(&Aa[256], &password[0], 64); + neoscrypt_copy(&Ab[0], &password[80], 80); + neoscrypt_copy(&Ab[80], &password[80], 80); + neoscrypt_copy(&Ab[160], &password[80], 80); + neoscrypt_copy(&Ab[240], &password[80], 16); + neoscrypt_copy(&Ab[256], &password[80], 64); + neoscrypt_copy(&Ac[0], &password[160], 80); + neoscrypt_copy(&Ac[80], &password[160], 80); + neoscrypt_copy(&Ac[160], &password[160], 80); + neoscrypt_copy(&Ac[240], &password[160], 16); + neoscrypt_copy(&Ac[256], &password[160], 64); + neoscrypt_copy(&Ad[0], &password[240], 80); + neoscrypt_copy(&Ad[80], &password[240], 80); + neoscrypt_copy(&Ad[160], &password[240], 80); + neoscrypt_copy(&Ad[240], &password[240], 16); + neoscrypt_copy(&Ad[256], &password[240], 64); + + if(!mode) { + output_len = 256; + neoscrypt_copy(&Ba[0], &salt[0], 80); + neoscrypt_copy(&Ba[80], &salt[0], 80); + neoscrypt_copy(&Ba[160], &salt[0], 80); + neoscrypt_copy(&Ba[240], &salt[0], 16); + neoscrypt_copy(&Ba[256], &salt[0], 32); + neoscrypt_copy(&Bb[0], &salt[80], 80); + neoscrypt_copy(&Bb[80], &salt[80], 80); + neoscrypt_copy(&Bb[160], &salt[80], 80); + neoscrypt_copy(&Bb[240], &salt[80], 16); + neoscrypt_copy(&Bb[256], &salt[80], 32); + neoscrypt_copy(&Bc[0], &salt[160], 80); + neoscrypt_copy(&Bc[80], &salt[160], 80); + neoscrypt_copy(&Bc[160], &salt[160], 80); + neoscrypt_copy(&Bc[240], &salt[160], 16); + neoscrypt_copy(&Bc[256], &salt[160], 32); + neoscrypt_copy(&Bd[0], &salt[240], 80); + neoscrypt_copy(&Bd[80], &salt[240], 80); + neoscrypt_copy(&Bd[160], &salt[240], 80); + neoscrypt_copy(&Bd[240], &salt[240], 16); + neoscrypt_copy(&Bd[256], &salt[240], 32); + } else { + output_len = 32; + neoscrypt_copy(&Ba[0], &salt[0], 256); + neoscrypt_copy(&Ba[256], &salt[0], 32); + neoscrypt_copy(&Bb[0], &salt[256], 256); + neoscrypt_copy(&Bb[256], &salt[256], 32); + neoscrypt_copy(&Bc[0], &salt[512], 256); + neoscrypt_copy(&Bc[256], &salt[512], 32); + neoscrypt_copy(&Bd[0], &salt[768], 256); + neoscrypt_copy(&Bd[256], &salt[768], 32); + } + + for(i = 0; i < 32; i++) { + + /* BLAKE2s: initialise */ + neoscrypt_copy(&T[0], blake2s_IV_P_XOR_4way, 128); + neoscrypt_erase(&T[32], 64); + + /* BLAKE2s: update keys */ + for(j = 0; j < 32; j += 8) { + T[j + 48] = *((uint *) &Ba[bufptr_a + j]); + T[j + 49] = *((uint *) &Bb[bufptr_b + j]); + T[j + 50] = *((uint *) &Bc[bufptr_c + j]); + T[j + 51] = *((uint *) &Bd[bufptr_d + j]); + T[j + 52] = *((uint *) &Ba[bufptr_a + j + 4]); + T[j + 53] = *((uint *) &Bb[bufptr_b + j + 4]); + T[j + 54] = *((uint *) &Bc[bufptr_c + j + 4]); + T[j + 55] = *((uint *) &Bd[bufptr_d + j + 4]); + } + neoscrypt_erase(&T[80], 128); + + /* BLAKE2s: compress IVs using keys */ + T[32] = 64; + T[33] = 64; + T[34] = 64; + T[35] = 64; + blake2s_compress_4way(&T[0]); + + /* BLAKE2s: update inputs */ + for(j = 0; j < 64; j += 8) { + T[j + 48] = *((uint *) &Aa[bufptr_a + j]); + T[j + 49] = *((uint *) &Ab[bufptr_b + j]); + T[j + 50] = *((uint *) &Ac[bufptr_c + j]); + T[j + 51] = *((uint *) &Ad[bufptr_d + j]); + T[j + 52] = *((uint *) &Aa[bufptr_a + j + 4]); + T[j + 53] = *((uint *) &Ab[bufptr_b + j + 4]); + T[j + 54] = *((uint *) &Ac[bufptr_c + j + 4]); + T[j + 55] = *((uint *) &Ad[bufptr_d + j + 4]); + } + + /* BLAKE2s: compress using inputs */ + T[32] = 128; + T[33] = 128; + T[34] = 128; + T[35] = 128; + T[40] = ~0U; + T[41] = ~0U; + T[42] = ~0U; + T[43] = ~0U; + blake2s_compress_4way(&T[0]); + + bufptr_a = 0; + bufptr_b = 0; + bufptr_c = 0; + bufptr_d = 0; + for(j = 0; j < 32; j += 4) { + bufptr_a += T[j]; + bufptr_a += (T[j] >> 8); + bufptr_a += (T[j] >> 16); + bufptr_a += (T[j] >> 24); + bufptr_b += T[j + 1]; + bufptr_b += (T[j + 1] >> 8); + bufptr_b += (T[j + 1] >> 16); + bufptr_b += (T[j + 1] >> 24); + bufptr_c += T[j + 2]; + bufptr_c += (T[j + 2] >> 8); + bufptr_c += (T[j + 2] >> 16); + bufptr_c += (T[j + 2] >> 24); + bufptr_d += T[j + 3]; + bufptr_d += (T[j + 3] >> 8); + bufptr_d += (T[j + 3] >> 16); + bufptr_d += (T[j + 3] >> 24); + } + bufptr_a &= 0xFF; + bufptr_b &= 0xFF; + bufptr_c &= 0xFF; + bufptr_d &= 0xFF; + + for(j = 0; j < 32; j += 8) { + *((uint *) &Ba[bufptr_a + j]) ^= T[j]; + *((uint *) &Bb[bufptr_b + j]) ^= T[j + 1]; + *((uint *) &Bc[bufptr_c + j]) ^= T[j + 2]; + *((uint *) &Bd[bufptr_d + j]) ^= T[j + 3]; + *((uint *) &Ba[bufptr_a + j + 4]) ^= T[j + 4]; + *((uint *) &Bb[bufptr_b + j + 4]) ^= T[j + 5]; + *((uint *) &Bc[bufptr_c + j + 4]) ^= T[j + 6]; + *((uint *) &Bd[bufptr_d + j + 4]) ^= T[j + 7]; } + + if(bufptr_a < 32) + neoscrypt_copy(&Ba[256 + bufptr_a], &Ba[bufptr_a], 32 - bufptr_a); + else if(bufptr_a > 224) + neoscrypt_copy(&Ba[0], &Ba[256], bufptr_a - 224); + if(bufptr_b < 32) + neoscrypt_copy(&Bb[256 + bufptr_b], &Bb[bufptr_b], 32 - bufptr_b); + else if(bufptr_b > 224) + neoscrypt_copy(&Bb[0], &Bb[256], bufptr_b - 224); + if(bufptr_c < 32) + neoscrypt_copy(&Bc[256 + bufptr_c], &Bc[bufptr_c], 32 - bufptr_c); + else if(bufptr_c > 224) + neoscrypt_copy(&Bc[0], &Bc[256], bufptr_c - 224); + if(bufptr_d < 32) + neoscrypt_copy(&Bd[256 + bufptr_d], &Bd[bufptr_d], 32 - bufptr_d); + else if(bufptr_d > 224) + neoscrypt_copy(&Bd[0], &Bd[256], bufptr_d - 224); + } - if(fail) { - printf("NeoScrypt integrity test failed!\n"); - return(1); + i = 256 - bufptr_a; + if(i >= output_len) { + neoscrypt_xor(&Ba[bufptr_a], &Aa[0], output_len); + neoscrypt_copy(&output[0], &Ba[bufptr_a], output_len); + } else { + neoscrypt_xor(&Ba[bufptr_a], &Aa[0], i); + neoscrypt_xor(&Ba[0], &Aa[i], output_len - i); + neoscrypt_copy(&output[0], &Ba[bufptr_a], i); + neoscrypt_copy(&output[i], &Ba[0], output_len - i); + } + i = 256 - bufptr_b; + if(i >= output_len) { + neoscrypt_xor(&Bb[bufptr_b], &Ab[0], output_len); + neoscrypt_copy(&output[output_len], &Bb[bufptr_b], output_len); + } else { + neoscrypt_xor(&Bb[bufptr_b], &Ab[0], i); + neoscrypt_xor(&Bb[0], &Ab[i], output_len - i); + neoscrypt_copy(&output[output_len], &Bb[bufptr_b], i); + neoscrypt_copy(&output[output_len + i], &Bb[0], output_len - i); + } + i = 256 - bufptr_c; + if(i >= output_len) { + neoscrypt_xor(&Bc[bufptr_c], &Ac[0], output_len); + neoscrypt_copy(&output[2 * output_len], &Bc[bufptr_c], output_len); + } else { + neoscrypt_xor(&Bc[bufptr_c], &Ac[0], i); + neoscrypt_xor(&Bc[0], &Ac[i], output_len - i); + neoscrypt_copy(&output[2 * output_len], &Bc[bufptr_c], i); + neoscrypt_copy(&output[2 * output_len + i], &Bc[0], output_len - i); + } + i = 256 - bufptr_d; + if(i >= output_len) { + neoscrypt_xor(&Bd[bufptr_d], &Ad[0], output_len); + neoscrypt_copy(&output[3 * output_len], &Bd[bufptr_d], output_len); } else { - printf("NeoScrypt integrity test passed.\n"); + neoscrypt_xor(&Bd[bufptr_d], &Ad[0], i); + neoscrypt_xor(&Bd[0], &Ad[i], output_len - i); + neoscrypt_copy(&output[3 * output_len], &Bd[bufptr_d], i); + neoscrypt_copy(&output[3 * output_len + i], &Bd[0], output_len - i); } - return(0); } -#endif +#endif /* (ASM) && (MINER_4WAY) */ + +#ifndef ASM +uint cpu_vec_exts() { + + /* No assembly, no extensions */ + return(0); +} +#endif diff --git a/src/Native/libmultihash/neoscrypt.h b/src/Native/libmultihash/neoscrypt.h index cfbcfc7e2..f385b4368 100644 --- a/src/Native/libmultihash/neoscrypt.h +++ b/src/Native/libmultihash/neoscrypt.h @@ -1,30 +1,72 @@ -#ifdef __cplusplus +#if (__cplusplus) extern "C" { #endif -void neoscrypt(const char *input, char *output, int profile); -#ifdef __cplusplus + +void neoscrypt(const unsigned char *password, unsigned char *output, + unsigned int profile); + +void neoscrypt_blake2s(const void *input, const unsigned int input_size, + const void *key, const unsigned char key_size, + void *output, const unsigned char output_size); + +void neoscrypt_copy(void *dstp, const void *srcp, unsigned int len); +void neoscrypt_erase(void *dstp, unsigned int len); +void neoscrypt_xor(void *dstp, const void *srcp, unsigned int len); + +#if defined(ASM) && defined(MINER_4WAY) +void neoscrypt_4way(const unsigned char *password, unsigned char *output, + unsigned char *scratchpad); + +#ifdef SHA256 +void scrypt_4way(const unsigned char *password, unsigned char *output, + unsigned char *scratchpad); +#endif + +void neoscrypt_blake2s_4way(const unsigned char *input, + const unsigned char *key, unsigned char *output); + +void neoscrypt_fastkdf_4way(const unsigned char *password, + const unsigned char *salt, unsigned char *output, unsigned char *scratchpad, + const unsigned int mode); +#endif + +unsigned int cpu_vec_exts(void); + +#if (__cplusplus) } #else -#define SCRYPT_BLOCK_SIZE 64 -#define SCRYPT_HASH_BLOCK_SIZE 64 -#define SCRYPT_HASH_DIGEST_SIZE 32 -typedef uint8_t hash_digest[SCRYPT_HASH_DIGEST_SIZE]; +typedef unsigned long long ullong; +typedef signed long long llong; +typedef unsigned int uint; +typedef unsigned char uchar; + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? a : b) +#endif + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? a : b) +#endif + +#define BLOCK_SIZE 64 +#define DIGEST_SIZE 32 + +typedef uchar hash_digest[DIGEST_SIZE]; #define ROTL32(a,b) (((a) << (b)) | ((a) >> (32 - b))) #define ROTR32(a,b) (((a) >> (b)) | ((a) << (32 - b))) #define U8TO32_BE(p) \ - (((uint32_t)((p)[0]) << 24) | ((uint32_t)((p)[1]) << 16) | \ - ((uint32_t)((p)[2]) << 8) | ((uint32_t)((p)[3]))) + (((uint)((p)[0]) << 24) | ((uint)((p)[1]) << 16) | \ + ((uint)((p)[2]) << 8) | ((uint)((p)[3]))) #define U32TO8_BE(p, v) \ - (p)[0] = (uint8_t)((v) >> 24); (p)[1] = (uint8_t)((v) >> 16); \ - (p)[2] = (uint8_t)((v) >> 8); (p)[3] = (uint8_t)((v) ); + (p)[0] = (uchar)((v) >> 24); (p)[1] = (uchar)((v) >> 16); \ + (p)[2] = (uchar)((v) >> 8); (p)[3] = (uchar)((v) ); #define U64TO8_BE(p, v) \ - U32TO8_BE((p), (uint32_t)((v) >> 32)); \ - U32TO8_BE((p) + 4, (uint32_t)((v) )); + U32TO8_BE((p), (uint)((v) >> 32)); \ + U32TO8_BE((p) + 4, (uint)((v) )); #endif - diff --git a/src/Native/libmultihash/neoscrypt_asm.S b/src/Native/libmultihash/neoscrypt_asm.S new file mode 100644 index 000000000..3889cb8a6 --- /dev/null +++ b/src/Native/libmultihash/neoscrypt_asm.S @@ -0,0 +1,17064 @@ +/* + * Copyright (c) 2014-2016 John Doering + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(ASM) && defined(__x86_64__) + +/* MOVQ_FIX addresses incorrect behaviour of old GNU assembler when transferring + * data between a 64-bit general purpose register and an MMX/SSE register: + * suffix or operands invalid for `movq' */ + +/* blake2s_compress(mem) + * AMD64 BLAKE2s block compression; + * the MMX registers are used as a temporal general purpose storage */ +.globl blake2s_compress +.globl _blake2s_compress +blake2s_compress: +_blake2s_compress: + pushq %rbx + pushq %rbp + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 +#ifdef WIN64 + pushq %rdi + pushq %rsi + movq %rcx, %rdi +#endif + +#ifndef MOVQ_FIX + movq %rsp, %mm0 +#else + movd %esp, %mm0 + shrq $32, %rsp + movd %esp, %mm7 +#endif + +/* initialise */ + movl 0(%rdi), %eax + movl 4(%rdi), %ebx + movl 8(%rdi), %ecx + movl 12(%rdi), %edx + movl 16(%rdi), %ebp + movl 20(%rdi), %esp + movl 24(%rdi), %esi + movd 28(%rdi), %mm2 + movl 32(%rdi), %r12d + movl 36(%rdi), %r13d + movl 40(%rdi), %r14d + movl 44(%rdi), %r15d + addl 48(%rdi), %eax /* A */ + movl $0x6A09E667, %r8d + addl 64(%rdi), %ecx /* C */ + movl $0x3C6EF372, %r10d + addl %ebp, %eax /* A */ + movl $0xBB67AE85, %r9d + addl %esi, %ecx /* C */ + movl $0xA54FF53A, %r11d + xorl $0x510E527F, %r12d + xorl $0x1F83D9AB, %r14d + xorl %eax, %r12d /* A */ + xorl $0x9B05688C, %r13d + xorl %ecx, %r14d /* C */ + xorl $0x5BE0CD19, %r15d +/* round 0 (A and C) */ + rorl $16, %r12d /* A */ + addl 52(%rdi), %eax /* A */ + rorl $16, %r14d /* C */ + addl 68(%rdi), %ecx /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $12, %ebp /* A */ + addl 56(%rdi), %ebx /* B (initial) */ + rorl $12, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $8, %r12d /* A */ + addl 72(%rdi), %edx /* D (initial) */ + rorl $8, %r14d /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $7, %ebp /* A */ + rorl $7, %esi /* C */ + movd %esi, %mm1 /* C */ +/* round 0 (B and D) */ + movd %mm2, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $16, %r13d /* B */ + addl 60(%rdi), %ebx /* B */ + rorl $16, %r15d /* D */ + addl 76(%rdi), %edx /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $12, %esp /* B */ + addl 80(%rdi), %eax /* E (initial) */ + rorl $12, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $8, %r13d /* B */ + addl 96(%rdi), %ecx /* G (initial) */ + rorl $8, %r15d /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $7, %esp /* B */ + rorl $7, %esi /* D */ +/* round 0 (E and G) */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $16, %r15d /* E */ + addl 84(%rdi), %eax /* E */ + rorl $16, %r13d /* G */ + addl 100(%rdi), %ecx /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $12, %esp /* E */ + addl 88(%rdi), %ebx /* F (initial) */ + rorl $12, %esi /* G */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $8, %r15d /* E */ + addl 104(%rdi), %edx /* H (initial) */ + rorl $8, %r13d /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $7, %esp /* E */ + rorl $7, %esi /* G */ + movd %esi, %mm2 /* G */ +/* round 0 (F and H) */ + movd %mm1, %esi /* F */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $16, %r12d /* F */ + addl 92(%rdi), %ebx /* F */ + rorl $16, %r14d /* H */ + addl 108(%rdi), %edx /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $12, %esi /* F */ + addl 104(%rdi), %eax /* A (initial) */ + rorl $12, %ebp /* H */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $8, %r12d /* F */ + addl 84(%rdi), %ecx /* C (initial) */ + rorl $8, %r14d /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $7, %esi /* F */ + rorl $7, %ebp /* H */ + movd %esi, %mm1 /* F */ +/* round 1 (A and C) */ + movd %mm1, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $16, %r12d /* A */ + addl 88(%rdi), %eax /* A */ + rorl $16, %r14d /* C */ + addl 108(%rdi), %ecx /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $12, %ebp /* A */ + addl 64(%rdi), %ebx /* B (initial) */ + rorl $12, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $8, %r12d /* A */ + addl 100(%rdi), %edx /* D (initial) */ + rorl $8, %r14d /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $7, %ebp /* A */ + rorl $7, %esi /* C */ + movd %esi, %mm1 /* C */ +/* round 1 (B and D) */ + movd %mm2, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $16, %r13d /* B */ + addl 80(%rdi), %ebx /* B */ + rorl $16, %r15d /* D */ + addl 72(%rdi), %edx /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $12, %esp /* B */ + addl 52(%rdi), %eax /* E (initial) */ + rorl $12, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $8, %r13d /* B */ + addl 92(%rdi), %ecx /* G (initial) */ + rorl $8, %r15d /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $7, %esp /* B */ + rorl $7, %esi /* D */ +/* round 1 (E and G) */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $16, %r15d /* E */ + addl 96(%rdi), %eax /* E */ + rorl $16, %r13d /* G */ + addl 76(%rdi), %ecx /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $12, %esp /* E */ + addl 48(%rdi), %ebx /* F (initial) */ + rorl $12, %esi /* G */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $8, %r15d /* E */ + addl 68(%rdi), %edx /* H (initial) */ + rorl $8, %r13d /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $7, %esp /* E */ + rorl $7, %esi /* G */ + movd %esi, %mm2 /* G */ +/* round 1 (F and H) */ + movd %mm1, %esi /* F */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $16, %r12d /* F */ + addl 56(%rdi), %ebx /* F */ + rorl $16, %r14d /* H */ + addl 60(%rdi), %edx /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $12, %esi /* F */ + addl 92(%rdi), %eax /* A (initial) */ + rorl $12, %ebp /* H */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $8, %r12d /* F */ + addl 68(%rdi), %ecx /* C (initial) */ + rorl $8, %r14d /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $7, %esi /* F */ + rorl $7, %ebp /* H */ + movd %esi, %mm1 /* F */ +/* round 2 (A and C) */ + movd %mm1, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $16, %r12d /* A */ + addl 80(%rdi), %eax /* A */ + rorl $16, %r14d /* C */ + addl 56(%rdi), %ecx /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $12, %ebp /* A */ + addl 96(%rdi), %ebx /* B (initial) */ + rorl $12, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $8, %r12d /* A */ + addl 108(%rdi), %edx /* D (initial) */ + rorl $8, %r14d /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $7, %ebp /* A */ + rorl $7, %esi /* C */ + movd %esi, %mm1 /* C */ +/* round 2 (B and D) */ + movd %mm2, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $16, %r13d /* B */ + addl 48(%rdi), %ebx /* B */ + rorl $16, %r15d /* D */ + addl 100(%rdi), %edx /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $12, %esp /* B */ + addl 88(%rdi), %eax /* E (initial) */ + rorl $12, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $8, %r13d /* B */ + addl 76(%rdi), %ecx /* G (initial) */ + rorl $8, %r15d /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $7, %esp /* B */ + rorl $7, %esi /* D */ +/* round 2 (E and G) */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $16, %r15d /* E */ + addl 104(%rdi), %eax /* E */ + rorl $16, %r13d /* G */ + addl 52(%rdi), %ecx /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $12, %esp /* E */ + addl 60(%rdi), %ebx /* F (initial) */ + rorl $12, %esi /* G */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $8, %r15d /* E */ + addl 84(%rdi), %edx /* H (initial) */ + rorl $8, %r13d /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $7, %esp /* E */ + rorl $7, %esi /* G */ + movd %esi, %mm2 /* G */ +/* round 2 (F and H) */ + movd %mm1, %esi /* F */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $16, %r12d /* F */ + addl 72(%rdi), %ebx /* F */ + rorl $16, %r14d /* H */ + addl 64(%rdi), %edx /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $12, %esi /* F */ + addl 76(%rdi), %eax /* A (initial) */ + rorl $12, %ebp /* H */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $8, %r12d /* F */ + addl 100(%rdi), %ecx /* C (initial) */ + rorl $8, %r14d /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $7, %esi /* F */ + rorl $7, %ebp /* H */ + movd %esi, %mm1 /* F */ +/* round 3 (A and C) */ + movd %mm1, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $16, %r12d /* A */ + addl 84(%rdi), %eax /* A */ + rorl $16, %r14d /* C */ + addl 96(%rdi), %ecx /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $12, %ebp /* A */ + addl 60(%rdi), %ebx /* B (initial) */ + rorl $12, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $8, %r12d /* A */ + addl 92(%rdi), %edx /* D (initial) */ + rorl $8, %r14d /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $7, %ebp /* A */ + rorl $7, %esi /* C */ + movd %esi, %mm1 /* C */ +/* round 3 (B and D) */ + movd %mm2, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $16, %r13d /* B */ + addl 52(%rdi), %ebx /* B */ + rorl $16, %r15d /* D */ + addl 104(%rdi), %edx /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $12, %esp /* B */ + addl 56(%rdi), %eax /* E (initial) */ + rorl $12, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $8, %r13d /* B */ + addl 64(%rdi), %ecx /* G (initial) */ + rorl $8, %r15d /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $7, %esp /* B */ + rorl $7, %esi /* D */ +/* round 3 (E and G) */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $16, %r15d /* E */ + addl 72(%rdi), %eax /* E */ + rorl $16, %r13d /* G */ + addl 48(%rdi), %ecx /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $12, %esp /* E */ + addl 68(%rdi), %ebx /* F (initial) */ + rorl $12, %esi /* G */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $8, %r15d /* E */ + addl 108(%rdi), %edx /* H (initial) */ + rorl $8, %r13d /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $7, %esp /* E */ + rorl $7, %esi /* G */ + movd %esi, %mm2 /* G */ +/* round 3 (F and H) */ + movd %mm1, %esi /* F */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $16, %r12d /* F */ + addl 88(%rdi), %ebx /* F */ + rorl $16, %r14d /* H */ + addl 80(%rdi), %edx /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $12, %esi /* F */ + addl 84(%rdi), %eax /* A (initial) */ + rorl $12, %ebp /* H */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $8, %r12d /* F */ + addl 56(%rdi), %ecx /* C (initial) */ + rorl $8, %r14d /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $7, %esi /* F */ + rorl $7, %ebp /* H */ + movd %esi, %mm1 /* F */ +/* round 4 (A and C) */ + movd %mm1, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $16, %r12d /* A */ + addl 48(%rdi), %eax /* A */ + rorl $16, %r14d /* C */ + addl 64(%rdi), %ecx /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $12, %ebp /* A */ + addl 68(%rdi), %ebx /* B (initial) */ + rorl $12, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $8, %r12d /* A */ + addl 88(%rdi), %edx /* D (initial) */ + rorl $8, %r14d /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $7, %ebp /* A */ + rorl $7, %esi /* C */ + movd %esi, %mm1 /* C */ +/* round 4 (B and D) */ + movd %mm2, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $16, %r13d /* B */ + addl 76(%rdi), %ebx /* B */ + rorl $16, %r15d /* D */ + addl 108(%rdi), %edx /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $12, %esp /* B */ + addl 104(%rdi), %eax /* E (initial) */ + rorl $12, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $8, %r13d /* B */ + addl 72(%rdi), %ecx /* G (initial) */ + rorl $8, %r15d /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $7, %esp /* B */ + rorl $7, %esi /* D */ +/* round 4 (E and G) */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $16, %r15d /* E */ + addl 52(%rdi), %eax /* E */ + rorl $16, %r13d /* G */ + addl 80(%rdi), %ecx /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $12, %esp /* E */ + addl 92(%rdi), %ebx /* F (initial) */ + rorl $12, %esi /* G */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $8, %r15d /* E */ + addl 60(%rdi), %edx /* H (initial) */ + rorl $8, %r13d /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $7, %esp /* E */ + rorl $7, %esi /* G */ + movd %esi, %mm2 /* G */ +/* round 4 (F and H) */ + movd %mm1, %esi /* F */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $16, %r12d /* F */ + addl 96(%rdi), %ebx /* F */ + rorl $16, %r14d /* H */ + addl 100(%rdi), %edx /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $12, %esi /* F */ + addl 56(%rdi), %eax /* A (initial) */ + rorl $12, %ebp /* H */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $8, %r12d /* F */ + addl 48(%rdi), %ecx /* C (initial) */ + rorl $8, %r14d /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $7, %esi /* F */ + rorl $7, %ebp /* H */ + movd %esi, %mm1 /* F */ +/* round 5 (A and C) */ + movd %mm1, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $16, %r12d /* A */ + addl 96(%rdi), %eax /* A */ + rorl $16, %r14d /* C */ + addl 92(%rdi), %ecx /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $12, %ebp /* A */ + addl 72(%rdi), %ebx /* B (initial) */ + rorl $12, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $8, %r12d /* A */ + addl 80(%rdi), %edx /* D (initial) */ + rorl $8, %r14d /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $7, %ebp /* A */ + rorl $7, %esi /* C */ + movd %esi, %mm1 /* C */ +/* round 5 (B and D) */ + movd %mm2, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $16, %r13d /* B */ + addl 88(%rdi), %ebx /* B */ + rorl $16, %r15d /* D */ + addl 60(%rdi), %edx /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $12, %esp /* B */ + addl 64(%rdi), %eax /* E (initial) */ + rorl $12, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $8, %r13d /* B */ + addl 108(%rdi), %ecx /* G (initial) */ + rorl $8, %r15d /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $7, %esp /* B */ + rorl $7, %esi /* D */ +/* round 5 (E and G) */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $16, %r15d /* E */ + addl 100(%rdi), %eax /* E */ + rorl $16, %r13d /* G */ + addl 104(%rdi), %ecx /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $12, %esp /* E */ + addl 76(%rdi), %ebx /* F (initial) */ + rorl $12, %esi /* G */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $8, %r15d /* E */ + addl 52(%rdi), %edx /* H (initial) */ + rorl $8, %r13d /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $7, %esp /* E */ + rorl $7, %esi /* G */ + movd %esi, %mm2 /* G */ +/* round 5 (F and H) */ + movd %mm1, %esi /* F */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $16, %r12d /* F */ + addl 68(%rdi), %ebx /* F */ + rorl $16, %r14d /* H */ + addl 84(%rdi), %edx /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $12, %esi /* F */ + addl 96(%rdi), %eax /* A (initial) */ + rorl $12, %ebp /* H */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $8, %r12d /* F */ + addl 104(%rdi), %ecx /* C (initial) */ + rorl $8, %r14d /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $7, %esi /* F */ + rorl $7, %ebp /* H */ + movd %esi, %mm1 /* F */ +/* round 6 (A and C) */ + movd %mm1, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $16, %r12d /* A */ + addl 68(%rdi), %eax /* A */ + rorl $16, %r14d /* C */ + addl 100(%rdi), %ecx /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $12, %ebp /* A */ + addl 52(%rdi), %ebx /* B (initial) */ + rorl $12, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $8, %r12d /* A */ + addl 64(%rdi), %edx /* D (initial) */ + rorl $8, %r14d /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $7, %ebp /* A */ + rorl $7, %esi /* C */ + movd %esi, %mm1 /* C */ +/* round 6 (B and D) */ + movd %mm2, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $16, %r13d /* B */ + addl 108(%rdi), %ebx /* B */ + rorl $16, %r15d /* D */ + addl 88(%rdi), %edx /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $12, %esp /* B */ + addl 48(%rdi), %eax /* E (initial) */ + rorl $12, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $8, %r13d /* B */ + addl 84(%rdi), %ecx /* G (initial) */ + rorl $8, %r15d /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $7, %esp /* B */ + rorl $7, %esi /* D */ +/* round 6 (E and G) */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $16, %r15d /* E */ + addl 76(%rdi), %eax /* E */ + rorl $16, %r13d /* G */ + addl 56(%rdi), %ecx /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $12, %esp /* E */ + addl 72(%rdi), %ebx /* F (initial) */ + rorl $12, %esi /* G */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $8, %r15d /* E */ + addl 80(%rdi), %edx /* H (initial) */ + rorl $8, %r13d /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $7, %esp /* E */ + rorl $7, %esi /* G */ + movd %esi, %mm2 /* G */ +/* round 6 (F and H) */ + movd %mm1, %esi /* F */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $16, %r12d /* F */ + addl 60(%rdi), %ebx /* F */ + rorl $16, %r14d /* H */ + addl 92(%rdi), %edx /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $12, %esi /* F */ + addl 100(%rdi), %eax /* A (initial) */ + rorl $12, %ebp /* H */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $8, %r12d /* F */ + addl 96(%rdi), %ecx /* C (initial) */ + rorl $8, %r14d /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $7, %esi /* F */ + rorl $7, %ebp /* H */ + movd %esi, %mm1 /* F */ +/* round 7 (A and C) */ + movd %mm1, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $16, %r12d /* A */ + addl 92(%rdi), %eax /* A */ + rorl $16, %r14d /* C */ + addl 52(%rdi), %ecx /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $12, %ebp /* A */ + addl 76(%rdi), %ebx /* B (initial) */ + rorl $12, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $8, %r12d /* A */ + addl 60(%rdi), %edx /* D (initial) */ + rorl $8, %r14d /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $7, %ebp /* A */ + rorl $7, %esi /* C */ + movd %esi, %mm1 /* C */ +/* round 7 (B and D) */ + movd %mm2, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $16, %r13d /* B */ + addl 104(%rdi), %ebx /* B */ + rorl $16, %r15d /* D */ + addl 84(%rdi), %edx /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $12, %esp /* B */ + addl 68(%rdi), %eax /* E (initial) */ + rorl $12, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $8, %r13d /* B */ + addl 80(%rdi), %ecx /* G (initial) */ + rorl $8, %r15d /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $7, %esp /* B */ + rorl $7, %esi /* D */ +/* round 7 (E and G) */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $16, %r15d /* E */ + addl 48(%rdi), %eax /* E */ + rorl $16, %r13d /* G */ + addl 72(%rdi), %ecx /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $12, %esp /* E */ + addl 108(%rdi), %ebx /* F (initial) */ + rorl $12, %esi /* G */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $8, %r15d /* E */ + addl 56(%rdi), %edx /* H (initial) */ + rorl $8, %r13d /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $7, %esp /* E */ + rorl $7, %esi /* G */ + movd %esi, %mm2 /* G */ +/* round 7 (F and H) */ + movd %mm1, %esi /* F */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $16, %r12d /* F */ + addl 64(%rdi), %ebx /* F */ + rorl $16, %r14d /* H */ + addl 88(%rdi), %edx /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $12, %esi /* F */ + addl 72(%rdi), %eax /* A (initial) */ + rorl $12, %ebp /* H */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $8, %r12d /* F */ + addl 92(%rdi), %ecx /* C (initial) */ + rorl $8, %r14d /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $7, %esi /* F */ + rorl $7, %ebp /* H */ + movd %esi, %mm1 /* F */ +/* round 8 (A and C) */ + movd %mm1, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $16, %r12d /* A */ + addl 108(%rdi), %eax /* A */ + rorl $16, %r14d /* C */ + addl 60(%rdi), %ecx /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $12, %ebp /* A */ + addl 104(%rdi), %ebx /* B (initial) */ + rorl $12, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $8, %r12d /* A */ + addl 48(%rdi), %edx /* D (initial) */ + rorl $8, %r14d /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $7, %ebp /* A */ + rorl $7, %esi /* C */ + movd %esi, %mm1 /* C */ +/* round 8 (B and D) */ + movd %mm2, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $16, %r13d /* B */ + addl 84(%rdi), %ebx /* B */ + rorl $16, %r15d /* D */ + addl 80(%rdi), %edx /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $12, %esp /* B */ + addl 96(%rdi), %eax /* E (initial) */ + rorl $12, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $8, %r13d /* B */ + addl 52(%rdi), %ecx /* G (initial) */ + rorl $8, %r15d /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $7, %esp /* B */ + rorl $7, %esi /* D */ +/* round 8 (E and G) */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $16, %r15d /* E */ + addl 56(%rdi), %eax /* E */ + rorl $16, %r13d /* G */ + addl 64(%rdi), %ecx /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $12, %esp /* E */ + addl 100(%rdi), %ebx /* F (initial) */ + rorl $12, %esi /* G */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $8, %r15d /* E */ + addl 88(%rdi), %edx /* H (initial) */ + rorl $8, %r13d /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $7, %esp /* E */ + rorl $7, %esi /* G */ + movd %esi, %mm2 /* G */ +/* round 8 (F and H) */ + movd %mm1, %esi /* F */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $16, %r12d /* F */ + addl 76(%rdi), %ebx /* F */ + rorl $16, %r14d /* H */ + addl 68(%rdi), %edx /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $12, %esi /* F */ + addl 88(%rdi), %eax /* A (initial) */ + rorl $12, %ebp /* H */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $8, %r12d /* F */ + addl 76(%rdi), %ecx /* C (initial) */ + rorl $8, %r14d /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $7, %esi /* F */ + rorl $7, %ebp /* H */ + movd %esi, %mm1 /* F */ +/* round 9 (A and C) */ + movd %mm1, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $16, %r12d /* A */ + addl 56(%rdi), %eax /* A */ + rorl $16, %r14d /* C */ + addl 72(%rdi), %ecx /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $12, %ebp /* A */ + addl 80(%rdi), %ebx /* B (initial) */ + rorl $12, %esi /* C */ + addl %ebp, %eax /* A */ + addl %esi, %ecx /* C */ + xorl %eax, %r12d /* A */ + xorl %ecx, %r14d /* C */ + rorl $8, %r12d /* A */ + addl 52(%rdi), %edx /* D (initial) */ + rorl $8, %r14d /* C */ + addl %r12d, %r8d /* A */ + addl %r14d, %r10d /* C */ + xorl %r8d, %ebp /* A */ + xorl %r10d, %esi /* C */ + rorl $7, %ebp /* A */ + rorl $7, %esi /* C */ + movd %esi, %mm1 /* C */ +/* round 9 (B and D) */ + movd %mm2, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $16, %r13d /* B */ + addl 64(%rdi), %ebx /* B */ + rorl $16, %r15d /* D */ + addl 68(%rdi), %edx /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $12, %esp /* B */ + addl 108(%rdi), %eax /* E (initial) */ + rorl $12, %esi /* D */ + addl %esp, %ebx /* B */ + addl %esi, %edx /* D */ + xorl %ebx, %r13d /* B */ + xorl %edx, %r15d /* D */ + rorl $8, %r13d /* B */ + addl 60(%rdi), %ecx /* G (initial) */ + rorl $8, %r15d /* D */ + addl %r13d, %r9d /* B */ + addl %r15d, %r11d /* D */ + xorl %r9d, %esp /* B */ + xorl %r11d, %esi /* D */ + rorl $7, %esp /* B */ + rorl $7, %esi /* D */ +/* round 9 (E and G) */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $16, %r15d /* E */ + addl 92(%rdi), %eax /* E */ + rorl $16, %r13d /* G */ + addl 96(%rdi), %ecx /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + rorl $12, %esp /* E */ + addl 84(%rdi), %ebx /* F (initial) */ + rorl $12, %esi /* G */ + addl %esp, %eax /* E */ + addl %esi, %ecx /* G */ + xorl %eax, %r15d /* E */ + xorl %ecx, %r13d /* G */ + rorl $8, %r15d /* E */ + addl 100(%rdi), %edx /* H (initial) */ + rorl $8, %r13d /* G */ + addl %r15d, %r10d /* E */ + addl %r13d, %r8d /* G */ + xorl %r10d, %esp /* E */ + xorl %r8d, %esi /* G */ + xorl %ecx, %r10d /* finalise */ + rorl $7, %esp /* E */ + xorl %eax, %r8d /* finalise */ + rorl $7, %esi /* G */ + xorl %r10d, 8(%rdi) /* finalise */ + xorl %r8d, 0(%rdi) /* finalise */ + xorl %esi, %r15d /* finalise */ + xorl %esp, %r13d /* finalise */ +/* round 9 (F and H) */ + movd %mm1, %esi /* F */ + xorl %r15d, 28(%rdi) /* finalise */ + xorl %r13d, 20(%rdi) /* finalise */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $16, %r12d /* F */ + addl 104(%rdi), %ebx /* F */ + rorl $16, %r14d /* H */ + addl 48(%rdi), %edx /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + rorl $12, %esi /* F */ + rorl $12, %ebp /* H */ + addl %esi, %ebx /* F */ + addl %ebp, %edx /* H */ + xorl %ebx, %r12d /* F */ + xorl %edx, %r14d /* H */ + rorl $8, %r12d /* F */ + rorl $8, %r14d /* H */ + addl %r12d, %r11d /* F */ + addl %r14d, %r9d /* H */ + xorl %r11d, %esi /* F */ + xorl %r9d, %ebp /* H */ + xorl %edx, %r11d /* finalise */ + rorl $7, %esi /* F */ + xorl %ebx, %r9d /* finalise */ + rorl $7, %ebp /* H */ + xorl %esi, %r14d /* finalise */ + xorl %ebp, %r12d /* finalise */ + xorl %r9d, 4(%rdi) /* finalise */ + xorl %r11d, 12(%rdi) /* finalise */ + xorl %r12d, 16(%rdi) /* finalise */ + xorl %r14d, 24(%rdi) /* finalise */ + +#ifndef MOVQ_FIX + movq %mm0, %rsp +#else + movd %mm0, %esp + movd %mm7, %eax + shlq $32, %rax + orq %rax, %rsp +#endif + +#ifdef WIN64 + popq %rsi + popq %rdi +#endif + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbp + popq %rbx + emms + ret + + +/* neoscrypt_copy(dst, src, len) + * AMD64 memcpy() */ +.globl neoscrypt_copy +.globl _neoscrypt_copy +neoscrypt_copy: +_neoscrypt_copy: +#ifdef WIN64 + movq %rdi, %r10 + movq %rsi, %r11 + movq %rcx, %rdi + movq %rdx, %rsi + movq %r8, %rdx +#endif + xorq %rcx, %rcx + movl %edx, %ecx + shrq $4, %rcx + xorq %r9, %r9 + cmpq %r9, %rcx + jz .4byte_copy_test +.16byte_copy: + movq 0(%rsi), %rax + movq 8(%rsi), %r8 + movq %rax, 0(%rdi) + movq %r8, 8(%rdi) + addq $16, %rsi + addq $16, %rdi + decq %rcx + jnz .16byte_copy + +.4byte_copy_test: + movl %edx, %ecx + shrq $2, %rcx + andq $0x3, %rcx + cmpq %r9, %rcx + jz .byte_copy_test +.4byte_copy: + movl 0(%rsi), %eax + movl %eax, 0(%rdi) + addq $4, %rsi + addq $4, %rdi + decq %rcx + jnz .4byte_copy + +.byte_copy_test: + movl %edx, %ecx + andq $0x3, %rcx + cmpq %r9, %rcx + jz .copy_finish +.byte_copy: + movb 0(%rsi), %al + movb %al, 0(%rdi) + incq %rsi + incq %rdi + decq %rcx + jnz .byte_copy + +.copy_finish: +#ifdef WIN64 + movq %r10, %rdi + movq %r11, %rsi +#endif + ret + + +/* neoscrypt_erase(dst, len) + * AMD64 memory eraser */ +.globl neoscrypt_erase +.globl _neoscrypt_erase +neoscrypt_erase: +_neoscrypt_erase: +#ifdef WIN64 + movq %rdi, %r10 + movq %rsi, %r11 + movq %rcx, %rdi + movq %rdx, %rsi +#endif + xorq %rcx, %rcx + movl %esi, %ecx + shrq $4, %rcx + xorq %rax, %rax + cmpq %rax, %rcx + jz .4byte_erase_test +.16byte_erase: + movq %rax, 0(%rdi) + movq %rax, 8(%rdi) + addq $16, %rdi + decq %rcx + jnz .16byte_erase + +.4byte_erase_test: + movl %esi, %ecx + shrq $2, %rcx + andq $0x3, %rcx + cmpq %rax, %rcx + jz .byte_erase_test +.4byte_erase: + movl %eax, 0(%rdi) + addq $4, %rdi + decq %rcx + jnz .4byte_erase + +.byte_erase_test: + movl %esi, %ecx + andq $0x3, %rcx + cmpq %rax, %rcx + jz .erase_finish +.byte_erase: + movb %al, 0(%rdi) + incq %rdi + decq %rcx + jnz .byte_erase + +.erase_finish: +#ifdef WIN64 + movq %r10, %rdi + movq %r11, %rsi +#endif + ret + + +/* neoscrypt_xor(dst, src, len) + * AMD64 XOR engine */ +.globl neoscrypt_xor +.globl _neoscrypt_xor +neoscrypt_xor: +_neoscrypt_xor: +#ifdef WIN64 + movq %rdi, %r10 + movq %rsi, %r11 + movq %rcx, %rdi + movq %rdx, %rsi + movq %r8, %rdx +#endif + xorq %rcx, %rcx + movl %edx, %ecx + shrq $4, %rcx + xorq %r9, %r9 + cmpq %r9, %rcx + jz .4byte_xor_test +.16byte_xor: + movq 0(%rsi), %rax + movq 8(%rsi), %r8 + xorq 0(%rdi), %rax + xorq 8(%rdi), %r8 + movq %rax, 0(%rdi) + movq %r8, 8(%rdi) + addq $16, %rsi + addq $16, %rdi + decq %rcx + jnz .16byte_xor + +.4byte_xor_test: + movl %edx, %ecx + shrq $2, %rcx + andq $0x3, %rcx + cmpq %r9, %rcx + jz .byte_xor_test +.4byte_xor: + movl 0(%rsi), %eax + xorl 0(%rdi), %eax + movl %eax, 0(%rdi) + addq $4, %rsi + addq $4, %rdi + decq %rcx + jnz .4byte_xor + +.byte_xor_test: + movl %edx, %ecx + andq $0x3, %rcx + cmpq %r9, %rcx + jz .xor_finish +.byte_xor: + movb 0(%rsi), %al + xorb 0(%rdi), %al + movb %al, 0(%rdi) + incq %rsi + incq %rdi + decq %rcx + jnz .byte_xor + +.xor_finish: +#ifdef WIN64 + movq %r10, %rdi + movq %r11, %rsi +#endif + ret + + +/* neoscrypt_fastkdf_opt(password, salt, output, output_len) + * AMD64 SSE2 FastKDF optimised */ +.globl neoscrypt_fastkdf_opt +.globl _neoscrypt_fastkdf_opt +neoscrypt_fastkdf_opt: +_neoscrypt_fastkdf_opt: + pushq %rbx + pushq %rbp + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 +#ifdef WIN64 + pushq %rdi + pushq %rsi + subq $160, %rsp + movdqu %xmm6, 144(%rsp) + movdqu %xmm7, 128(%rsp) + movdqu %xmm8, 112(%rsp) + movdqu %xmm9, 96(%rsp) + movdqu %xmm10, 80(%rsp) + movdqu %xmm11, 64(%rsp) + movdqu %xmm12, 48(%rsp) + movdqu %xmm13, 32(%rsp) + movdqu %xmm14, 16(%rsp) + movdqu %xmm15, 0(%rsp) + movq %rcx, %rdi + movq %rdx, %rsi + movq %r8, %rdx + movq %r9, %rcx +#endif + +/* 64 bytes (local variables) + 64 bytes (alignment space) + 320 bytes (password + * buffer) + 288 bytes (salt buffer) + 112 bytes (BLAKE2s space) = 848 bytes */ + subq $848, %rsp + leaq 128(%rsp), %rbp + andq $0xFFFFFFFFFFFFFFC0, %rbp + movq %rdx, 48(%rsp) + movq %rcx, 56(%rsp) + + movdqu 0(%rdi), %xmm0 + movdqu 16(%rdi), %xmm1 + movdqu 32(%rdi), %xmm2 + movdqu 48(%rdi), %xmm3 + movdqu 64(%rdi), %xmm4 + movdqa %xmm0, 0(%rbp) + movdqa %xmm1, 16(%rbp) + movdqa %xmm2, 32(%rbp) + movdqa %xmm3, 48(%rbp) + movdqa %xmm4, 64(%rbp) + movdqa %xmm0, 80(%rbp) + movdqa %xmm1, 96(%rbp) + movdqa %xmm2, 112(%rbp) + movdqa %xmm3, 128(%rbp) + movdqa %xmm4, 144(%rbp) + movdqa %xmm0, 160(%rbp) + movdqa %xmm1, 176(%rbp) + movdqa %xmm2, 192(%rbp) + movdqa %xmm3, 208(%rbp) + movdqa %xmm4, 224(%rbp) + movdqa %xmm0, 240(%rbp) + movdqa %xmm0, 256(%rbp) + movdqa %xmm1, 272(%rbp) + movdqa %xmm2, 288(%rbp) + movdqa %xmm3, 304(%rbp) + + leaq 320(%rbp), %rbx + leaq 608(%rbp), %r14 + movq %rbp, %r12 + xorq %r13, %r13 + movq $32, %r15 + testl $0x01, 56(%rsp) + jnz .fastkdf_mode_one + + movl $256, 56(%rsp) + movdqu 0(%rsi), %xmm0 + movdqu 16(%rsi), %xmm1 + movdqu 32(%rsi), %xmm2 + movdqu 48(%rsi), %xmm3 + movdqu 64(%rsi), %xmm4 + movdqa %xmm0, 0(%rbx) + movdqa %xmm1, 16(%rbx) + movdqa %xmm2, 32(%rbx) + movdqa %xmm3, 48(%rbx) + movdqa %xmm4, 64(%rbx) + movdqa %xmm0, 80(%rbx) + movdqa %xmm1, 96(%rbx) + movdqa %xmm2, 112(%rbx) + movdqa %xmm3, 128(%rbx) + movdqa %xmm4, 144(%rbx) + movdqa %xmm0, 160(%rbx) + movdqa %xmm1, 176(%rbx) + movdqa %xmm2, 192(%rbx) + movdqa %xmm3, 208(%rbx) + movdqa %xmm4, 224(%rbx) + movdqa %xmm0, 240(%rbx) + movdqa %xmm0, 256(%rbx) + movdqa %xmm1, 272(%rbx) + jmp .fastkdf_loop + +.fastkdf_mode_one: + movl $32, 56(%rsp) + movdqa 0(%rsi), %xmm0 + movdqa 16(%rsi), %xmm1 + movdqa 32(%rsi), %xmm2 + movdqa 48(%rsi), %xmm3 + movdqa 64(%rsi), %xmm4 + movdqa 80(%rsi), %xmm5 + movdqa 96(%rsi), %xmm6 + movdqa 112(%rsi), %xmm7 + movdqa 128(%rsi), %xmm8 + movdqa 144(%rsi), %xmm9 + movdqa 160(%rsi), %xmm10 + movdqa 176(%rsi), %xmm11 + movdqa 192(%rsi), %xmm12 + movdqa 208(%rsi), %xmm13 + movdqa 224(%rsi), %xmm14 + movdqa 240(%rsi), %xmm15 + movdqa %xmm0, 0(%rbx) + movdqa %xmm1, 16(%rbx) + movdqa %xmm2, 32(%rbx) + movdqa %xmm3, 48(%rbx) + movdqa %xmm4, 64(%rbx) + movdqa %xmm5, 80(%rbx) + movdqa %xmm6, 96(%rbx) + movdqa %xmm7, 112(%rbx) + movdqa %xmm8, 128(%rbx) + movdqa %xmm9, 144(%rbx) + movdqa %xmm10, 160(%rbx) + movdqa %xmm11, 176(%rbx) + movdqa %xmm12, 192(%rbx) + movdqa %xmm13, 208(%rbx) + movdqa %xmm14, 224(%rbx) + movdqa %xmm15, 240(%rbx) + movdqa %xmm0, 256(%rbx) + movdqa %xmm1, 272(%rbx) + +.fastkdf_loop: + leaq 0(%r12, %r13), %rbp + leaq 320(%r12, %r13), %rbx + pxor %xmm5, %xmm5 + + movq $0xBB67AE856B08C647, %r8 + movq %r8, 0(%r14) + movq $0xA54FF53A3C6EF372, %r9 + movq %r9, 8(%r14) + movq $0x9B05688C510E527F, %r10 + movq %r10, 16(%r14) + movq $0x5BE0CD191F83D9AB, %r11 + movq %r11, 24(%r14) + movdqa %xmm5, 32(%r14) + movl $64, 32(%r14) + + movdqu 0(%rbx), %xmm0 + movdqu 16(%rbx), %xmm1 + movdqa %xmm0, 48(%r14) + movdqa %xmm1, 64(%r14) + movdqa %xmm5, 80(%r14) + movdqa %xmm5, 96(%r14) + +#ifdef WIN64 + movq %r14, %rcx +#else + movq %r14, %rdi +#endif + call blake2s_compress + + movdqu 0(%rbp), %xmm0 + movdqu 16(%rbp), %xmm1 + movdqu 32(%rbp), %xmm2 + movdqu 48(%rbp), %xmm3 + movdqa %xmm0, 48(%r14) + movdqa %xmm1, 64(%r14) + movdqa %xmm2, 80(%r14) + movdqa %xmm3, 96(%r14) + + movl $128, 32(%r14) + movl $0xFFFFFFFF, 40(%r14) + +#ifdef WIN64 + movq %r14, %rcx +#else + movq %r14, %rdi +#endif + call blake2s_compress + + pxor %xmm5, %xmm5 + movdqa 0(%r14), %xmm0 + movdqa 16(%r14), %xmm1 + movdqa %xmm0, %xmm2 + movdqa %xmm1, %xmm3 + paddb %xmm1, %xmm0 + psadbw %xmm5, %xmm0 + movhlps %xmm0, %xmm1 + paddq %xmm1, %xmm0 +#ifndef MOVQ_FIX + movq %xmm0, %r13 +#else + movq %xmm0, 0(%r14) + movq 0(%r14), %r13 +#endif + andq $0xFF, %r13 + leaq 320(%r12, %r13), %rbx + movdqu 0(%rbx), %xmm0 + movdqu 16(%rbx), %xmm1 + pxor %xmm2, %xmm0 + pxor %xmm3, %xmm1 + movdqu %xmm0, 0(%rbx) + movdqu %xmm1, 16(%rbx) + +/* tail update */ + movq $32, %rdx + cmpq %r13, %rdx + jc .fastkdf_headupd +#ifdef WIN64 + movq %rdx, %r8 + leaq 256(%rbx), %rcx + movq %rbx, %rdx + subq %r13, %r8 +#else + leaq 256(%rbx), %rdi + movq %rbx, %rsi + subq %r13, %rdx +#endif + call neoscrypt_copy + jmp .fastkdf_loop_end + +/* head update */ +.fastkdf_headupd: + movq $224, %rdx + cmpq %r13, %rdx + jnc .fastkdf_loop_end + movq %r13, %rax + subq %rdx, %rax +#ifdef WIN64 + leaq 320(%r12), %rcx + leaq 576(%r12), %rdx + movq %rax, %r8 +#else + leaq 320(%r12), %rdi + leaq 576(%r12), %rsi + movq %rax, %rdx +#endif + call neoscrypt_copy + +.fastkdf_loop_end: + decq %r15 + jnz .fastkdf_loop + + movq 48(%rsp), %r14 + movq 56(%rsp), %r15 + movq $256, %rbp + subq %r13, %rbp + cmpq %r15, %rbp + jc .fastkdf_crosscopy + + leaq 320(%r12, %r13), %rbp +#ifdef WIN64 + movq %rbp, %rcx + movq %r12, %rdx + movq %r15, %r8 +#else + movq %rbp, %rdi + movq %r12, %rsi + movq %r15, %rdx +#endif + call neoscrypt_xor +#ifdef WIN64 + movq %r14, %rcx + movq %rbp, %rdx + movq %r15, %r8 +#else + movq %r14, %rdi + movq %rbp, %rsi + movq %r15, %rdx +#endif + call neoscrypt_copy + jmp .fastkdf_finish + +.fastkdf_crosscopy: + leaq 320(%r12, %r13), %rbx +#ifdef WIN64 + movq %rbx, %rcx + movq %r12, %rdx + movq %rbp, %r8 +#else + movq %rbx, %rdi + movq %r12, %rsi + movq %rbp, %rdx +#endif + call neoscrypt_xor + leaq 320(%r12), %rdi + leaq 0(%r12, %rbp), %rsi +#ifdef WIN64 + movq %rdi, %rcx + movq %rsi, %rdx + movq %r15, %r8 + subq %rbp, %r8 +#else + movq %r15, %rdx + subq %rbp, %rdx +#endif + call neoscrypt_xor +#ifdef WIN64 + movq %r14, %rcx + movq %rbx, %rdx + movq %rbp, %r8 +#else + movq %r14, %rdi + movq %rbx, %rsi + movq %rbp, %rdx +#endif + call neoscrypt_copy +#ifdef WIN64 + leaq 0(%r14, %rbp), %rcx + leaq 320(%r12), %rdx + movq %r15, %r8 + subq %rbp, %r8 +#else + leaq 0(%r14, %rbp), %rdi + leaq 320(%r12), %rsi + movq %r15, %rdx + subq %rbp, %rdx +#endif + call neoscrypt_copy + +.fastkdf_finish: + addq $848, %rsp + +#ifdef WIN64 + movdqu 0(%rsp), %xmm15 + movdqu 16(%rsp), %xmm14 + movdqu 32(%rsp), %xmm13 + movdqu 48(%rsp), %xmm12 + movdqu 64(%rsp), %xmm11 + movdqu 80(%rsp), %xmm10 + movdqu 96(%rsp), %xmm9 + movdqu 112(%rsp), %xmm8 + movdqu 128(%rsp), %xmm7 + movdqu 144(%rsp), %xmm6 + addq $160, %rsp + popq %rsi + popq %rdi +#endif + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbp + popq %rbx + ret + + +/* neoscrypt_salsa_tangle(mem, count) + * AMD64 (SSE2) Salsa20 map switcher; + * correct map: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + * SSE2 map: 0 5 10 15 12 1 6 11 8 13 2 7 4 9 14 3 + * NOTE: arguments passed in %r8 and %r9; %rbx not preserved */ +neoscrypt_salsa_tangle_sse2: +.salsa_tangle_sse2: + movl 4(%r8), %eax + movl 20(%r8), %ebx + movl 8(%r8), %ecx + movl 40(%r8), %edx + movl %eax, 20(%r8) + movl %ebx, 4(%r8) + movl %ecx, 40(%r8) + movl %edx, 8(%r8) + movl 12(%r8), %eax + movl 60(%r8), %ebx + movl 16(%r8), %ecx + movl 48(%r8), %edx + movl %eax, 60(%r8) + movl %ebx, 12(%r8) + movl %ecx, 48(%r8) + movl %edx, 16(%r8) + movl 28(%r8), %eax + movl 44(%r8), %ebx + movl 36(%r8), %ecx + movl 52(%r8), %edx + movl %eax, 44(%r8) + movl %ebx, 28(%r8) + movl %ecx, 52(%r8) + movl %edx, 36(%r8) + addq $64, %r8 + decq %r9 + jnz .salsa_tangle_sse2 + + ret + + +/* neoscrypt_xor_salsa_sse2(mem, xormem, double_rounds) + * AMD64 (SSE2) Salsa20 with XOR; + * mem and xormem must be aligned properly; + * NOTE: arguments passed in %r8, %r9, %r10 */ +neoscrypt_xor_salsa_sse2: + movdqa 0(%r8), %xmm0 + movdqa 16(%r8), %xmm1 + movdqa 32(%r8), %xmm2 + movdqa 48(%r8), %xmm3 + pxor 0(%r9), %xmm0 + pxor 16(%r9), %xmm1 + pxor 32(%r9), %xmm2 + pxor 48(%r9), %xmm3 + movdqa %xmm0, %xmm12 + movdqa %xmm1, %xmm13 + movdqa %xmm2, %xmm14 + movdqa %xmm3, %xmm15 +.xor_salsa_sse2: + movdqa %xmm1, %xmm4 + paddd %xmm0, %xmm4 + movdqa %xmm4, %xmm5 + pslld $7, %xmm4 + psrld $25, %xmm5 + pxor %xmm4, %xmm3 + movdqa %xmm0, %xmm4 + pxor %xmm5, %xmm3 + paddd %xmm3, %xmm4 + movdqa %xmm4, %xmm5 + pslld $9, %xmm4 + psrld $23, %xmm5 + pxor %xmm4, %xmm2 + movdqa %xmm3, %xmm4 + pxor %xmm5, %xmm2 + pshufd $0x93, %xmm3, %xmm3 + paddd %xmm2, %xmm4 + movdqa %xmm4, %xmm5 + pslld $13, %xmm4 + psrld $19, %xmm5 + pxor %xmm4, %xmm1 + movdqa %xmm2, %xmm4 + pxor %xmm5, %xmm1 + pshufd $0x4E, %xmm2, %xmm2 + paddd %xmm1, %xmm4 + movdqa %xmm4, %xmm5 + pslld $18, %xmm4 + psrld $14, %xmm5 + pxor %xmm4, %xmm0 + movdqa %xmm3, %xmm4 + pxor %xmm5, %xmm0 + pshufd $0x39, %xmm1, %xmm1 + paddd %xmm0, %xmm4 + movdqa %xmm4, %xmm5 + pslld $7, %xmm4 + psrld $25, %xmm5 + pxor %xmm4, %xmm1 + movdqa %xmm0, %xmm4 + pxor %xmm5, %xmm1 + paddd %xmm1, %xmm4 + movdqa %xmm4, %xmm5 + pslld $9, %xmm4 + psrld $23, %xmm5 + pxor %xmm4, %xmm2 + movdqa %xmm1, %xmm4 + pxor %xmm5, %xmm2 + pshufd $0x93, %xmm1, %xmm1 + paddd %xmm2, %xmm4 + movdqa %xmm4, %xmm5 + pslld $13, %xmm4 + psrld $19, %xmm5 + pxor %xmm4, %xmm3 + movdqa %xmm2, %xmm4 + pxor %xmm5, %xmm3 + pshufd $0x4E, %xmm2, %xmm2 + paddd %xmm3, %xmm4 + movdqa %xmm4, %xmm5 + pslld $18, %xmm4 + psrld $14, %xmm5 + pxor %xmm4, %xmm0 + pshufd $0x39, %xmm3, %xmm3 + pxor %xmm5, %xmm0 + decq %r10 + jnz .xor_salsa_sse2 + + paddd %xmm12, %xmm0 + paddd %xmm13, %xmm1 + paddd %xmm14, %xmm2 + paddd %xmm15, %xmm3 + movdqa %xmm0, 0(%r8) + movdqa %xmm1, 16(%r8) + movdqa %xmm2, 32(%r8) + movdqa %xmm3, 48(%r8) + + ret + + +/* neoscrypt_xor_chacha_sse2(mem, xormem, double_rounds) + * AMD64 (SSE2) ChaCha20 with XOR; + * mem and xormem must be aligned properly; + * NOTE: arguments passed in %r8, %r9, %r10 */ +neoscrypt_xor_chacha_sse2: + movdqa 0(%r8), %xmm0 + movdqa 16(%r8), %xmm1 + movdqa 32(%r8), %xmm2 + movdqa 48(%r8), %xmm3 + pxor 0(%r9), %xmm0 + pxor 16(%r9), %xmm1 + pxor 32(%r9), %xmm2 + pxor 48(%r9), %xmm3 + movdqa %xmm0, %xmm12 + movdqa %xmm1, %xmm13 + movdqa %xmm2, %xmm14 + movdqa %xmm3, %xmm15 +.xor_chacha_sse2: + paddd %xmm1, %xmm0 + pxor %xmm0, %xmm3 + pshuflw $0xB1, %xmm3, %xmm3 + pshufhw $0xB1, %xmm3, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm4 + pslld $12, %xmm1 + psrld $20, %xmm4 + pxor %xmm4, %xmm1 + paddd %xmm1, %xmm0 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm4 + pslld $8, %xmm3 + psrld $24, %xmm4 + pxor %xmm4, %xmm3 + pshufd $0x93, %xmm0, %xmm0 + paddd %xmm3, %xmm2 + pshufd $0x4E, %xmm3, %xmm3 + pxor %xmm2, %xmm1 + pshufd $0x39, %xmm2, %xmm2 + movdqa %xmm1, %xmm4 + pslld $7, %xmm1 + psrld $25, %xmm4 + pxor %xmm4, %xmm1 + paddd %xmm1, %xmm0 + pxor %xmm0, %xmm3 + pshuflw $0xB1, %xmm3, %xmm3 + pshufhw $0xB1, %xmm3, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm4 + pslld $12, %xmm1 + psrld $20, %xmm4 + pxor %xmm4, %xmm1 + paddd %xmm1, %xmm0 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm4 + pslld $8, %xmm3 + psrld $24, %xmm4 + pxor %xmm4, %xmm3 + pshufd $0x39, %xmm0, %xmm0 + paddd %xmm3, %xmm2 + pshufd $0x4E, %xmm3, %xmm3 + pxor %xmm2, %xmm1 + pshufd $0x93, %xmm2, %xmm2 + movdqa %xmm1, %xmm4 + pslld $7, %xmm1 + psrld $25, %xmm4 + pxor %xmm4, %xmm1 + decq %r10 + jnz .xor_chacha_sse2 + + paddd %xmm12, %xmm0 + paddd %xmm13, %xmm1 + paddd %xmm14, %xmm2 + paddd %xmm15, %xmm3 + movdqa %xmm0, 0(%r8) + movdqa %xmm1, 16(%r8) + movdqa %xmm2, 32(%r8) + movdqa %xmm3, 48(%r8) + + ret + + +/* neoscrypt_xor_salsa(mem, xormem, tempmem, double_rounds) + * AMD64 (INT) Salsa20 with XOR (SSE2 support required); + * NOTE: arguments passed in %r8, %r9, %r10, %r11 */ +neoscrypt_xor_salsa: +/* XOR and copy to temporary memory */ + movdqa 0(%r8), %xmm0 + movdqa 16(%r8), %xmm1 + movdqa 32(%r8), %xmm2 + movdqa 48(%r8), %xmm3 + pxor 0(%r9), %xmm0 + pxor 16(%r9), %xmm1 + pxor 32(%r9), %xmm2 + pxor 48(%r9), %xmm3 + movdqa %xmm0, 0(%r10) + movdqa %xmm1, 16(%r10) + movdqa %xmm2, 32(%r10) + movdqa %xmm3, 48(%r10) + movdqa %xmm0, %xmm12 + movdqa %xmm1, %xmm13 + movdqa %xmm2, %xmm14 + movdqa %xmm3, %xmm15 +.xor_salsa: +/* quarters A and B, initial C and D */ + movl 0(%r10), %eax /* A: load a */ + movl 20(%r10), %ebx /* B: load a */ + addl 48(%r10), %eax /* A: t = a + d */ + addl 4(%r10), %ebx /* B: t = a + d */ + roll $7, %eax /* A: rotate t */ + roll $7, %ebx /* B: rotate t */ + xorl 16(%r10), %eax /* A: b = b ^ t */ + xorl 36(%r10), %ebx /* B: b = b ^ t */ + movl %eax, %esi /* A: copy b */ + movl %ebx, %edi /* B: copy b */ + movl %esi, 16(%r10) /* A: store b */ + movl %edi, 36(%r10) /* B: store b */ + addl 0(%r10), %eax /* A: t = b + a */ + addl 20(%r10), %ebx /* B: t = b + a */ + roll $9, %eax /* A: rotate t */ + roll $9, %ebx /* B: rotate t */ + xorl 32(%r10), %eax /* A: c = c ^ t */ + xorl 52(%r10), %ebx /* B: c = c ^ t */ + movl %eax, %ecx /* A: copy c */ + movl %ebx, %edx /* B: copy c */ + movl %ecx, 32(%r10) /* A: store c */ + movl %edx, 52(%r10) /* B: store c */ + addl %esi, %eax /* A: t = c + b */ + addl %edi, %ebx /* B: t = c + b */ + roll $13, %eax /* A: rotate t */ + roll $13, %ebx /* B: rotate t */ + xorl 48(%r10), %eax /* A: d = d ^ t */ + xorl 4(%r10), %ebx /* B: d = d ^ t */ + movl %eax, 48(%r10) /* A: store d */ + movl %ebx, 4(%r10) /* B: store d */ + addl %eax, %ecx /* A: t = d + c */ + movl 40(%r10), %eax /* C: load a */ + addl %ebx, %edx /* B: t = d + c */ + movl 60(%r10), %ebx /* D: load a */ + roll $18, %ecx /* A: rotate t */ + addl 24(%r10), %eax /* C: t = a + d */ + roll $18, %edx /* B: rotate t */ + addl 44(%r10), %ebx /* D: t = a + d */ + xorl 0(%r10), %ecx /* A: a = a ^ t */ + roll $7, %eax /* C: rotate t */ + xorl 20(%r10), %edx /* B: a = a ^ t */ + roll $7, %ebx /* D: rotate t */ + movl %ecx, 0(%r10) /* A: store a */ + movl %edx, 20(%r10) /* B: store a */ +/* quarters C and D, initial E and F */ + xorl 56(%r10), %eax /* C: b = b ^ t */ + xorl 12(%r10), %ebx /* D: b = b ^ t */ + movl %eax, %esi /* C: copy b */ + movl %ebx, %edi /* D: copy b */ + movl %esi, 56(%r10) /* C: store b */ + movl %edi, 12(%r10) /* D: store b */ + addl 40(%r10), %eax /* C: t = b + a */ + addl 60(%r10), %ebx /* D: t = b + a */ + roll $9, %eax /* C: rotate t */ + roll $9, %ebx /* D: rotate t */ + xorl 8(%r10), %eax /* C: c = c ^ t */ + xorl 28(%r10), %ebx /* D: c = c ^ t */ + movl %eax, %ecx /* C: copy c */ + movl %ebx, %edx /* D: copy c */ + movl %ecx, 8(%r10) /* C: store c */ + movl %edx, 28(%r10) /* D: store c */ + addl %esi, %eax /* C: t = c + b */ + addl %edi, %ebx /* D: t = c + b */ + roll $13, %eax /* C: rotate t */ + roll $13, %ebx /* D: rotate t */ + xorl 24(%r10), %eax /* C: d = d ^ t */ + xorl 44(%r10), %ebx /* D: d = d ^ t */ + movl %eax, 24(%r10) /* C: store d */ + movl %ebx, 44(%r10) /* D: store d */ + addl %eax, %ecx /* C: t = d + c */ + movl 0(%r10), %eax /* E: load a */ + addl %ebx, %edx /* D: t = d + c */ + movl 20(%r10), %ebx /* F: load a */ + roll $18, %ecx /* C: rotate t */ + addl 12(%r10), %eax /* E: t = a + d */ + roll $18, %edx /* D: rotate t */ + addl 16(%r10), %ebx /* F: t = a + d */ + xorl 40(%r10), %ecx /* C: a = a ^ t */ + roll $7, %eax /* E: rotate t */ + xorl 60(%r10), %edx /* D: a = a ^ t */ + roll $7, %ebx /* F: rotate t */ + movl %ecx, 40(%r10) /* C: store a */ + movl %edx, 60(%r10) /* D: store a */ +/* quarters E and F, initial G and H */ + xorl 4(%r10), %eax /* E: b = b ^ t */ + xorl 24(%r10), %ebx /* F: b = b ^ t */ + movl %eax, %esi /* E: copy b */ + movl %ebx, %edi /* F: copy b */ + movl %esi, 4(%r10) /* E: store b */ + movl %edi, 24(%r10) /* F: store b */ + addl 0(%r10), %eax /* E: t = b + a */ + addl 20(%r10), %ebx /* F: t = b + a */ + roll $9, %eax /* E: rotate t */ + roll $9, %ebx /* F: rotate t */ + xorl 8(%r10), %eax /* E: c = c ^ t */ + xorl 28(%r10), %ebx /* F: c = c ^ t */ + movl %eax, %ecx /* E: copy c */ + movl %ebx, %edx /* F: copy c */ + movl %ecx, 8(%r10) /* E: store c */ + movl %edx, 28(%r10) /* F: store c */ + addl %esi, %eax /* E: t = c + b */ + addl %edi, %ebx /* F: t = c + b */ + roll $13, %eax /* E: rotate t */ + roll $13, %ebx /* F: rotate t */ + xorl 12(%r10), %eax /* E: d = d ^ t */ + xorl 16(%r10), %ebx /* F: d = d ^ t */ + movl %eax, 12(%r10) /* E: store d */ + movl %ebx, 16(%r10) /* F: store d */ + addl %eax, %ecx /* E: t = d + c */ + movl 40(%r10), %eax /* G: load a */ + addl %ebx, %edx /* F: t = d + c */ + movl 60(%r10), %ebx /* H: load a */ + roll $18, %ecx /* E: rotate t */ + addl 36(%r10), %eax /* G: t = a + d */ + roll $18, %edx /* F: rotate t */ + addl 56(%r10), %ebx /* H: t = a + d */ + xorl 0(%r10), %ecx /* E: a = a ^ t */ + roll $7, %eax /* G: rotate t */ + xorl 20(%r10), %edx /* F: a = a ^ t */ + roll $7, %ebx /* H: rotate t */ + movl %ecx, 0(%r10) /* E: store a */ + movl %edx, 20(%r10) /* F: store a */ +/* quarters G and H */ + xorl 44(%r10), %eax /* G: b = b ^ t */ + xorl 48(%r10), %ebx /* H: b = b ^ t */ + movl %eax, %esi /* G: copy b */ + movl %ebx, %edi /* H: copy b */ + movl %esi, 44(%r10) /* G: store b */ + movl %edi, 48(%r10) /* H: store b */ + addl 40(%r10), %eax /* G: t = b + a */ + addl 60(%r10), %ebx /* H: t = b + a */ + roll $9, %eax /* G: rotate t */ + roll $9, %ebx /* H: rotate t */ + xorl 32(%r10), %eax /* G: c = c ^ t */ + xorl 52(%r10), %ebx /* H: c = c ^ t */ + movl %eax, %ecx /* G: copy c */ + movl %ebx, %edx /* H: copy c */ + movl %ecx, 32(%r10) /* G: store c */ + movl %edx, 52(%r10) /* H: store c */ + addl %esi, %eax /* G: t = c + b */ + addl %edi, %ebx /* H: t = c + b */ + roll $13, %eax /* G: rotate t */ + roll $13, %ebx /* H: rotate t */ + xorl 36(%r10), %eax /* G: d = d ^ t */ + xorl 56(%r10), %ebx /* H: d = d ^ t */ + movl %eax, 36(%r10) /* G: store d */ + movl %ebx, 56(%r10) /* H: store d */ + addl %eax, %ecx /* G: t = d + c */ + addl %ebx, %edx /* H: t = d + c */ + roll $18, %ecx /* G: rotate t */ + roll $18, %edx /* H: rotate t */ + xorl 40(%r10), %ecx /* G: a = a ^ t */ + xorl 60(%r10), %edx /* H: a = a ^ t */ + movl %ecx, 40(%r10) /* G: store a */ + movl %edx, 60(%r10) /* H: store a */ + decq %r11 + jnz .xor_salsa + + movdqa 0(%r10), %xmm0 + movdqa 16(%r10), %xmm1 + movdqa 32(%r10), %xmm2 + movdqa 48(%r10), %xmm3 + paddd %xmm12, %xmm0 + paddd %xmm13, %xmm1 + paddd %xmm14, %xmm2 + paddd %xmm15, %xmm3 + movdqa %xmm0, 0(%r8) + movdqa %xmm1, 16(%r8) + movdqa %xmm2, 32(%r8) + movdqa %xmm3, 48(%r8) + + ret + + +/* neoscrypt_xor_chacha(mem, xormem, tempmem, double_rounds) + * AMD64 (INT) ChaCha20 with XOR (SSE2 support required); + * NOTE: arguments passed in %r8, %r9, %r10, %r11 */ +neoscrypt_xor_chacha: +/* XOR and copy to temporary memory */ + movdqa 0(%r8), %xmm0 + movdqa 16(%r8), %xmm1 + movdqa 32(%r8), %xmm2 + movdqa 48(%r8), %xmm3 + pxor 0(%r9), %xmm0 + pxor 16(%r9), %xmm1 + pxor 32(%r9), %xmm2 + pxor 48(%r9), %xmm3 + movdqa %xmm0, 0(%r10) + movdqa %xmm1, 16(%r10) + movdqa %xmm2, 32(%r10) + movdqa %xmm3, 48(%r10) + movdqa %xmm0, %xmm12 + movdqa %xmm1, %xmm13 + movdqa %xmm2, %xmm14 + movdqa %xmm3, %xmm15 +.xor_chacha: +/* quarters A and B, initial C */ + movl 0(%r10), %eax /* A: load a */ + movl 16(%r10), %ebx /* A: load b */ + addl %ebx, %eax /* A: a = a + b */ + movl 32(%r10), %ecx /* A: load c */ + movl 48(%r10), %edx /* A: load d */ + xorl %eax, %edx /* A: d = d ^ a */ + movl 4(%r10), %edi /* B: load a */ + roll $16, %edx /* A: rotate d */ + movl 20(%r10), %esi /* B: load b */ + addl %edx, %ecx /* A: c = c + d */ + xorl %ecx, %ebx /* A: b = b ^ c */ + addl %esi, %edi /* B: a = a + b */ + roll $12, %ebx /* A: rotate b */ + addl %ebx, %eax /* A: a = a + b */ + movl %eax, 0(%r10) /* A: store a */ + xorl %eax, %edx /* A: d = d ^ a */ + movl 52(%r10), %eax /* B: load d */ + roll $8, %edx /* A: rotate d */ + xorl %edi, %eax /* B: d = d ^ a */ + movl %edx, 48(%r10) /* A: store d */ + addl %edx, %ecx /* A: c = c + d */ + movl 36(%r10), %edx /* B: load c */ + movl %ecx, 32(%r10) /* A: store c */ + xorl %ecx, %ebx /* A: b = b ^ c */ + roll $16, %eax /* B: rotate d */ + movl 40(%r10), %ecx /* C: load c */ + roll $7, %ebx /* A: rotate b */ + addl %eax, %edx /* B: c = c + d */ + movl %ebx, 16(%r10) /* A: store b */ + xorl %edx, %esi /* B: b = b ^ c */ + movl 24(%r10), %ebx /* C: load b */ + roll $12, %esi /* B: rotate b */ + addl %esi, %edi /* B: a = a + b */ + movl %edi, 4(%r10) /* B: store a */ + xorl %edi, %eax /* B: d = d ^ a */ + roll $8, %eax /* B: rotate d */ + movl %eax, 52(%r10) /* B: store d */ + addl %eax, %edx /* B: c = c + d */ + movl 8(%r10), %eax /* C: load a */ + movl %edx, 36(%r10) /* B: store c */ + xorl %edx, %esi /* B: b = b ^ c */ + movl 56(%r10), %edx /* C: load d */ + roll $7, %esi /* B: rotate b */ + addl %ebx, %eax /* C: a = a + b */ + movl %esi, 20(%r10) /* B: store b */ +/* quarters C and D, initial E */ + xorl %eax, %edx /* C: d = d ^ a */ + movl 12(%r10), %edi /* D: load a */ + roll $16, %edx /* C: rotate d */ + movl 28(%r10), %esi /* D: load b */ + addl %edx, %ecx /* C: c = c + d */ + xorl %ecx, %ebx /* C: b = b ^ c */ + addl %esi, %edi /* D: a = a + b */ + roll $12, %ebx /* C: rotate b */ + addl %ebx, %eax /* C: a = a + b */ + movl %eax, 8(%r10) /* C: store a */ + xorl %eax, %edx /* C: d = d ^ a */ + movl 60(%r10), %eax /* D: load d */ + roll $8, %edx /* C: rotate d */ + xorl %edi, %eax /* D: d = d ^ a */ + movl %edx, 56(%r10) /* C: store d */ + addl %edx, %ecx /* C: c = c + d */ + movl 44(%r10), %edx /* D: load c */ + movl %ecx, 40(%r10) /* C: store c */ + xorl %ecx, %ebx /* C: b = b ^ c */ + roll $16, %eax /* D: rotate d */ + movl 40(%r10), %ecx /* E: load c */ + roll $7, %ebx /* C: rotate b */ + addl %eax, %edx /* D: c = c + d */ + movl %ebx, 24(%r10) /* C: store b */ + xorl %edx, %esi /* D: b = b ^ c */ + movl 20(%r10), %ebx /* E: load b */ + roll $12, %esi /* D: rotate b */ + addl %esi, %edi /* D: a = a + b */ + movl %edi, 12(%r10) /* D: store a */ + xorl %edi, %eax /* D: d = d ^ a */ + roll $8, %eax /* D: rotate d */ + movl %eax, 60(%r10) /* D: store d */ + addl %eax, %edx /* D: c = c + d */ + movl 0(%r10), %eax /* E: load a */ + movl %edx, 44(%r10) /* D: store c */ + xorl %edx, %esi /* D: b = b ^ c */ + movl 60(%r10), %edx /* E: load d */ + roll $7, %esi /* D: rotate b */ + addl %ebx, %eax /* E: a = a + b */ + movl %esi, 28(%r10) /* D: store b */ +/* quarters E and F, initial G */ + xorl %eax, %edx /* E: d = d ^ a */ + movl 4(%r10), %edi /* F: load a */ + roll $16, %edx /* E: rotate d */ + movl 24(%r10), %esi /* F: load b */ + addl %edx, %ecx /* E: c = c + d */ + xorl %ecx, %ebx /* E: b = b ^ c */ + addl %esi, %edi /* F: a = a + b */ + roll $12, %ebx /* E: rotate b */ + addl %ebx, %eax /* E: a = a + b */ + movl %eax, 0(%r10) /* E: store a */ + xorl %eax, %edx /* E: d = d ^ a */ + movl 48(%r10), %eax /* F: load d */ + roll $8, %edx /* E: rotate d */ + xorl %edi, %eax /* F: d = d ^ a */ + movl %edx, 60(%r10) /* E: store d */ + addl %edx, %ecx /* E: c = c + d */ + movl 44(%r10), %edx /* F: load c */ + movl %ecx, 40(%r10) /* E: store c */ + xorl %ecx, %ebx /* E: b = b ^ c */ + roll $16, %eax /* F: rotate d */ + movl 32(%r10), %ecx /* G: load c */ + roll $7, %ebx /* E: rotate b */ + addl %eax, %edx /* F: c = c + d */ + movl %ebx, 20(%r10) /* E: store b */ + xorl %edx, %esi /* F: b = b ^ c */ + movl 28(%r10), %ebx /* G: load b */ + roll $12, %esi /* F: rotate b */ + addl %esi, %edi /* F: a = a + b */ + movl %edi, 4(%r10) /* F: store a */ + xorl %edi, %eax /* F: d = d ^ a */ + roll $8, %eax /* F: rotate d */ + movl %eax, 48(%r10) /* F: store d */ + addl %eax, %edx /* F: c = c + d */ + movl 8(%r10), %eax /* G: load a */ + movl %edx, 44(%r10) /* F: store c */ + xorl %edx, %esi /* F: b = b ^ c */ + movl 52(%r10), %edx /* G: load d */ + roll $7, %esi /* F: rotate b */ + addl %ebx, %eax /* G: a = a + b */ + movl %esi, 24(%r10) /* F: store b */ +/* quarters G and H */ + xorl %eax, %edx /* G: d = d ^ a */ + movl 12(%r10), %edi /* H: load a */ + roll $16, %edx /* G: rotate d */ + movl 16(%r10), %esi /* H: load b */ + addl %edx, %ecx /* G: c = c + d */ + xorl %ecx, %ebx /* G: b = b ^ c */ + addl %esi, %edi /* H: a = a + b */ + roll $12, %ebx /* G: rotate b */ + addl %ebx, %eax /* G: a = a + b */ + movl %eax, 8(%r10) /* G: store a */ + xorl %eax, %edx /* G: d = d ^ a */ + movl 56(%r10), %eax /* H: load d */ + roll $8, %edx /* G: rotate d */ + xorl %edi, %eax /* H: d = d ^ a */ + movl %edx, 52(%r10) /* G: store d */ + addl %edx, %ecx /* G: c = c + d */ + movl 36(%r10), %edx /* H: load c */ + movl %ecx, 32(%r10) /* G: store c */ + xorl %ecx, %ebx /* G: b = b ^ c */ + roll $16, %eax /* H: rotate d */ + roll $7, %ebx /* G: rotate b */ + addl %eax, %edx /* H: c = c + d */ + movl %ebx, 28(%r10) /* G: store b */ + xorl %edx, %esi /* H: b = b ^ c */ + roll $12, %esi /* H: rotate b */ + addl %esi, %edi /* H: a = a + b */ + movl %edi, 12(%r10) /* H: store a */ + xorl %edi, %eax /* H: d = d ^ a */ + roll $8, %eax /* H: rotate d */ + movl %eax, 56(%r10) /* H: store d */ + addl %eax, %edx /* H: c = c + d */ + movl %edx, 36(%r10) /* H: store c */ + xorl %edx, %esi /* H: b = b ^ c */ + roll $7, %esi /* H: rotate b */ + movl %esi, 16(%r10) /* H: store b */ + decq %r11 + jnz .xor_chacha + + movdqa 0(%r10), %xmm0 + movdqa 16(%r10), %xmm1 + movdqa 32(%r10), %xmm2 + movdqa 48(%r10), %xmm3 + paddd %xmm12, %xmm0 + paddd %xmm13, %xmm1 + paddd %xmm14, %xmm2 + paddd %xmm15, %xmm3 + movdqa %xmm0, 0(%r8) + movdqa %xmm1, 16(%r8) + movdqa %xmm2, 32(%r8) + movdqa %xmm3, 48(%r8) + + ret + + +/* neoscrypt(input, output, profile) + * AMD64 (INT, SSE2) NeoScrypt engine (SSE2 required for INT); + * supports NeoScrypt and Scrypt only */ +.globl neoscrypt +.globl _neoscrypt +neoscrypt: +_neoscrypt: +#ifdef WIN64 + pushq %rdi + pushq %rsi + movq %rcx, %rdi + movq %rdx, %rsi + movq %r8, %rdx +#endif + pushq %rbx + pushq %rbp + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 +/* save input, output and profile */ + movq %rdi, %r14 + movq %rsi, %r15 + movq %rdx, %rbx + +#ifdef SHA256 +/* Scrypt mode */ + testl $0x01, %ebx + jnz .scrypt +#endif + +#ifdef WIN64 +/* attempt to allocate 33280 + 128 bytes of stack space fails miserably; + * have to use malloc() and free() instead */ + subq $128, %rsp +/* allocate memory (9 pages of 4Kb each) */ + movq $0x9000, %rcx + call malloc +/* save memory address */ + movq %rax, 64(%rsp) +/* align memory */ + addq $64, %rax + andq $0xFFFFFFFFFFFFFFC0, %rax +/* memory base: X, Z, V */ + leaq 64(%rax), %rbp +#else +/* align stack */ + movq %rsp, %rax + andq $0xFFFFFFFFFFFFFFC0, %rsp + subq $0x8280, %rsp +/* save unaligned stack */ + movq %rax, 32(%rsp) +/* memory base: X, Z, V */ + leaq 128(%rsp), %rbp +#endif /* WIN64 */ + +/* FastKDF */ +#ifdef WIN64 +#ifdef OPT + movq %r14, %rcx + movq %r14, %rdx + movq %rbp, %r8 + xorq %r9, %r9 + call neoscrypt_fastkdf_opt +#else + movq $80, %rax + movq %r14, %rcx + movq %rax, %rdx + movq %r14, %r8 + movq %rax, %r9 + movq $32, 32(%rsp) + movq %rbp, 40(%rsp) + movq $256, 48(%rsp) + call neoscrypt_fastkdf +#endif /* OPT */ +#else +#ifdef OPT + movq %r14, %rdi + movq %r14, %rsi + movq %rbp, %rdx + xorq %rcx, %rcx +#ifdef __APPLE__ + call _neoscrypt_fastkdf_opt +#else + call neoscrypt_fastkdf_opt +#endif /* __APPLE__ */ +#else + movq $80, %rax + movq %r14, %rdi + movq %rax, %rsi + movq %r14, %rdx + movq %rax, %rcx + movq $32, %r8 + movq %rbp, %r9 + movq $256, 0(%rsp) +#ifdef __APPLE__ + call _neoscrypt_fastkdf +#else + call neoscrypt_fastkdf +#endif /* __APPLE__ */ +#endif /* OPT */ +#endif /* WIN64 */ + +/* blkcpy(Z, X) */ + leaq 256(%rbp), %rax + movdqa 0(%rbp), %xmm0 + movdqa 16(%rbp), %xmm1 + movdqa 32(%rbp), %xmm2 + movdqa 48(%rbp), %xmm3 + movdqa 64(%rbp), %xmm4 + movdqa 80(%rbp), %xmm5 + movdqa 96(%rbp), %xmm6 + movdqa 112(%rbp), %xmm7 + movdqa 128(%rbp), %xmm8 + movdqa 144(%rbp), %xmm9 + movdqa 160(%rbp), %xmm10 + movdqa 176(%rbp), %xmm11 + movdqa 192(%rbp), %xmm12 + movdqa 208(%rbp), %xmm13 + movdqa 224(%rbp), %xmm14 + movdqa 240(%rbp), %xmm15 + movdqa %xmm0, 0(%rax) + movdqa %xmm1, 16(%rax) + movdqa %xmm2, 32(%rax) + movdqa %xmm3, 48(%rax) + movdqa %xmm4, 64(%rax) + movdqa %xmm5, 80(%rax) + movdqa %xmm6, 96(%rax) + movdqa %xmm7, 112(%rax) + movdqa %xmm8, 128(%rax) + movdqa %xmm9, 144(%rax) + movdqa %xmm10, 160(%rax) + movdqa %xmm11, 176(%rax) + movdqa %xmm12, 192(%rax) + movdqa %xmm13, 208(%rax) + movdqa %xmm14, 224(%rax) + movdqa %xmm15, 240(%rax) + +/* SSE2 switch */ + testl $0x1000, %ebx + jnz .neoscrypt_sse2 + +/* tempmem and double rounds */ + leaq -64(%rbp), %r10 + movq $10, %r12 + + xorq %r13, %r13 +.chacha_ns1: +/* blkcpy(V, Z) */ + leaq 512(%rbp), %rax + movq %r13, %rdx + movb $8, %cl + shlq %cl, %rdx + leaq 256(%rbp), %rcx + addq %rdx, %rax + movdqa 0(%rcx), %xmm0 + movdqa 16(%rcx), %xmm1 + movdqa 32(%rcx), %xmm2 + movdqa 48(%rcx), %xmm3 + movdqa 64(%rcx), %xmm4 + movdqa 80(%rcx), %xmm5 + movdqa 96(%rcx), %xmm6 + movdqa 112(%rcx), %xmm7 + movdqa 128(%rcx), %xmm8 + movdqa 144(%rcx), %xmm9 + movdqa 160(%rcx), %xmm10 + movdqa 176(%rcx), %xmm11 + movdqa 192(%rcx), %xmm12 + movdqa 208(%rcx), %xmm13 + movdqa 224(%rcx), %xmm14 + movdqa 240(%rcx), %xmm15 + movdqa %xmm0, 0(%rax) + movdqa %xmm1, 16(%rax) + movdqa %xmm2, 32(%rax) + movdqa %xmm3, 48(%rax) + movdqa %xmm4, 64(%rax) + movdqa %xmm5, 80(%rax) + movdqa %xmm6, 96(%rax) + movdqa %xmm7, 112(%rax) + movdqa %xmm8, 128(%rax) + movdqa %xmm9, 144(%rax) + movdqa %xmm10, 160(%rax) + movdqa %xmm11, 176(%rax) + movdqa %xmm12, 192(%rax) + movdqa %xmm13, 208(%rax) + movdqa %xmm14, 224(%rax) + movdqa %xmm15, 240(%rax) +/* blkmix(Z) */ + leaq 256(%rbp), %r8 + leaq 448(%rbp), %r9 + movq %r12, %r11 + call neoscrypt_xor_chacha + leaq 320(%rbp), %r8 + leaq 256(%rbp), %r9 + movq %r12, %r11 + call neoscrypt_xor_chacha + leaq 384(%rbp), %r8 + leaq 320(%rbp), %r9 + movq %r12, %r11 + call neoscrypt_xor_chacha + leaq 448(%rbp), %r8 + leaq 384(%rbp), %r9 + movq %r12, %r11 + call neoscrypt_xor_chacha + leaq 320(%rbp), %rax + leaq 384(%rbp), %rdx + movdqa 0(%rax), %xmm0 + movdqa 16(%rax), %xmm1 + movdqa 32(%rax), %xmm2 + movdqa 48(%rax), %xmm3 + movdqa 0(%rdx), %xmm4 + movdqa 16(%rdx), %xmm5 + movdqa 32(%rdx), %xmm6 + movdqa 48(%rdx), %xmm7 + movdqa %xmm0, 0(%rdx) + movdqa %xmm1, 16(%rdx) + movdqa %xmm2, 32(%rdx) + movdqa %xmm3, 48(%rdx) + movdqa %xmm4, 0(%rax) + movdqa %xmm5, 16(%rax) + movdqa %xmm6, 32(%rax) + movdqa %xmm7, 48(%rax) + incq %r13 + cmpq $128, %r13 + jnz .chacha_ns1 + + xorq %r13, %r13 +.chacha_ns2: +/* integerify(Z) mod 128 */ + leaq 256(%rbp), %rax + leaq 512(%rbp), %rcx + xorq %rdx, %rdx + movl 448(%rbp), %edx + andl $0x7F, %edx + shlq $8, %rdx + addq %rdx, %rcx +/* blkxor(Z, V) */ + movdqa 0(%rax), %xmm0 + movdqa 16(%rax), %xmm1 + movdqa 32(%rax), %xmm2 + movdqa 48(%rax), %xmm3 + movdqa 64(%rax), %xmm4 + movdqa 80(%rax), %xmm5 + movdqa 96(%rax), %xmm6 + movdqa 112(%rax), %xmm7 + movdqa 128(%rax), %xmm8 + movdqa 144(%rax), %xmm9 + movdqa 160(%rax), %xmm10 + movdqa 176(%rax), %xmm11 + movdqa 192(%rax), %xmm12 + movdqa 208(%rax), %xmm13 + movdqa 224(%rax), %xmm14 + movdqa 240(%rax), %xmm15 + pxor 0(%rcx), %xmm0 + pxor 16(%rcx), %xmm1 + pxor 32(%rcx), %xmm2 + pxor 48(%rcx), %xmm3 + pxor 64(%rcx), %xmm4 + pxor 80(%rcx), %xmm5 + pxor 96(%rcx), %xmm6 + pxor 112(%rcx), %xmm7 + pxor 128(%rcx), %xmm8 + pxor 144(%rcx), %xmm9 + pxor 160(%rcx), %xmm10 + pxor 176(%rcx), %xmm11 + pxor 192(%rcx), %xmm12 + pxor 208(%rcx), %xmm13 + pxor 224(%rcx), %xmm14 + pxor 240(%rcx), %xmm15 + movdqa %xmm0, 0(%rax) + movdqa %xmm1, 16(%rax) + movdqa %xmm2, 32(%rax) + movdqa %xmm3, 48(%rax) + movdqa %xmm4, 64(%rax) + movdqa %xmm5, 80(%rax) + movdqa %xmm6, 96(%rax) + movdqa %xmm7, 112(%rax) + movdqa %xmm8, 128(%rax) + movdqa %xmm9, 144(%rax) + movdqa %xmm10, 160(%rax) + movdqa %xmm11, 176(%rax) + movdqa %xmm12, 192(%rax) + movdqa %xmm13, 208(%rax) + movdqa %xmm14, 224(%rax) + movdqa %xmm15, 240(%rax) +/* blkmix(Z) */ + leaq 256(%rbp), %r8 + leaq 448(%rbp), %r9 + movq %r12, %r11 + call neoscrypt_xor_chacha + leaq 320(%rbp), %r8 + leaq 256(%rbp), %r9 + movq %r12, %r11 + call neoscrypt_xor_chacha + leaq 384(%rbp), %r8 + leaq 320(%rbp), %r9 + movq %r12, %r11 + call neoscrypt_xor_chacha + leaq 448(%rbp), %r8 + leaq 384(%rbp), %r9 + movq %r12, %r11 + call neoscrypt_xor_chacha + leaq 320(%rbp), %rax + leaq 384(%rbp), %rdx + movdqa 0(%rax), %xmm0 + movdqa 16(%rax), %xmm1 + movdqa 32(%rax), %xmm2 + movdqa 48(%rax), %xmm3 + movdqa 0(%rdx), %xmm4 + movdqa 16(%rdx), %xmm5 + movdqa 32(%rdx), %xmm6 + movdqa 48(%rdx), %xmm7 + movdqa %xmm0, 0(%rdx) + movdqa %xmm1, 16(%rdx) + movdqa %xmm2, 32(%rdx) + movdqa %xmm3, 48(%rdx) + movdqa %xmm4, 0(%rax) + movdqa %xmm5, 16(%rax) + movdqa %xmm6, 32(%rax) + movdqa %xmm7, 48(%rax) + incq %r13 + cmpq $128, %r13 + jnz .chacha_ns2 + + xorq %r13, %r13 +.salsa_ns1: +/* blkcpy(V, X) */ + leaq 512(%rbp), %rax + movq %r13, %rdx + movb $8, %cl + shlq %cl, %rdx + addq %rdx, %rax + movdqa 0(%rbp), %xmm0 + movdqa 16(%rbp), %xmm1 + movdqa 32(%rbp), %xmm2 + movdqa 48(%rbp), %xmm3 + movdqa 64(%rbp), %xmm4 + movdqa 80(%rbp), %xmm5 + movdqa 96(%rbp), %xmm6 + movdqa 112(%rbp), %xmm7 + movdqa 128(%rbp), %xmm8 + movdqa 144(%rbp), %xmm9 + movdqa 160(%rbp), %xmm10 + movdqa 176(%rbp), %xmm11 + movdqa 192(%rbp), %xmm12 + movdqa 208(%rbp), %xmm13 + movdqa 224(%rbp), %xmm14 + movdqa 240(%rbp), %xmm15 + movdqa %xmm0, 0(%rax) + movdqa %xmm1, 16(%rax) + movdqa %xmm2, 32(%rax) + movdqa %xmm3, 48(%rax) + movdqa %xmm4, 64(%rax) + movdqa %xmm5, 80(%rax) + movdqa %xmm6, 96(%rax) + movdqa %xmm7, 112(%rax) + movdqa %xmm8, 128(%rax) + movdqa %xmm9, 144(%rax) + movdqa %xmm10, 160(%rax) + movdqa %xmm11, 176(%rax) + movdqa %xmm12, 192(%rax) + movdqa %xmm13, 208(%rax) + movdqa %xmm14, 224(%rax) + movdqa %xmm15, 240(%rax) +/* blkmix(X) */ + movq %rbp, %r8 + leaq 192(%rbp), %r9 + movq %r12, %r11 + call neoscrypt_xor_salsa + leaq 64(%rbp), %r8 + movq %rbp, %r9 + movq %r12, %r11 + call neoscrypt_xor_salsa + leaq 128(%rbp), %r8 + leaq 64(%rbp), %r9 + movq %r12, %r11 + call neoscrypt_xor_salsa + leaq 192(%rbp), %r8 + leaq 128(%rbp), %r9 + movq %r12, %r11 + call neoscrypt_xor_salsa + leaq 64(%rbp), %rax + leaq 128(%rbp), %rdx + movdqa 0(%rax), %xmm0 + movdqa 16(%rax), %xmm1 + movdqa 32(%rax), %xmm2 + movdqa 48(%rax), %xmm3 + movdqa 0(%rdx), %xmm4 + movdqa 16(%rdx), %xmm5 + movdqa 32(%rdx), %xmm6 + movdqa 48(%rdx), %xmm7 + movdqa %xmm0, 0(%rdx) + movdqa %xmm1, 16(%rdx) + movdqa %xmm2, 32(%rdx) + movdqa %xmm3, 48(%rdx) + movdqa %xmm4, 0(%rax) + movdqa %xmm5, 16(%rax) + movdqa %xmm6, 32(%rax) + movdqa %xmm7, 48(%rax) + incq %r13 + cmpq $128, %r13 + jnz .salsa_ns1 + + xorq %r13, %r13 +.salsa_ns2: +/* integerify(X) mod 128 */ + leaq 512(%rbp), %rcx + xorq %rdx, %rdx + movl 192(%rbp), %edx + andl $0x7F, %edx + shlq $8, %rdx + addq %rdx, %rcx +/* blkxor(X, V) */ + movdqa 0(%rbp), %xmm0 + movdqa 16(%rbp), %xmm1 + movdqa 32(%rbp), %xmm2 + movdqa 48(%rbp), %xmm3 + movdqa 64(%rbp), %xmm4 + movdqa 80(%rbp), %xmm5 + movdqa 96(%rbp), %xmm6 + movdqa 112(%rbp), %xmm7 + movdqa 128(%rbp), %xmm8 + movdqa 144(%rbp), %xmm9 + movdqa 160(%rbp), %xmm10 + movdqa 176(%rbp), %xmm11 + movdqa 192(%rbp), %xmm12 + movdqa 208(%rbp), %xmm13 + movdqa 224(%rbp), %xmm14 + movdqa 240(%rbp), %xmm15 + pxor 0(%rcx), %xmm0 + pxor 16(%rcx), %xmm1 + pxor 32(%rcx), %xmm2 + pxor 48(%rcx), %xmm3 + pxor 64(%rcx), %xmm4 + pxor 80(%rcx), %xmm5 + pxor 96(%rcx), %xmm6 + pxor 112(%rcx), %xmm7 + pxor 128(%rcx), %xmm8 + pxor 144(%rcx), %xmm9 + pxor 160(%rcx), %xmm10 + pxor 176(%rcx), %xmm11 + pxor 192(%rcx), %xmm12 + pxor 208(%rcx), %xmm13 + pxor 224(%rcx), %xmm14 + pxor 240(%rcx), %xmm15 + movdqa %xmm0, 0(%rbp) + movdqa %xmm1, 16(%rbp) + movdqa %xmm2, 32(%rbp) + movdqa %xmm3, 48(%rbp) + movdqa %xmm4, 64(%rbp) + movdqa %xmm5, 80(%rbp) + movdqa %xmm6, 96(%rbp) + movdqa %xmm7, 112(%rbp) + movdqa %xmm8, 128(%rbp) + movdqa %xmm9, 144(%rbp) + movdqa %xmm10, 160(%rbp) + movdqa %xmm11, 176(%rbp) + movdqa %xmm12, 192(%rbp) + movdqa %xmm13, 208(%rbp) + movdqa %xmm14, 224(%rbp) + movdqa %xmm15, 240(%rbp) +/* blkmix(X) */ + movq %rbp, %r8 + leaq 192(%rbp), %r9 + movq %r12, %r11 + call neoscrypt_xor_salsa + leaq 64(%rbp), %r8 + movq %rbp, %r9 + movq %r12, %r11 + call neoscrypt_xor_salsa + leaq 128(%rbp), %r8 + leaq 64(%rbp), %r9 + movq %r12, %r11 + call neoscrypt_xor_salsa + leaq 192(%rbp), %r8 + leaq 128(%rbp), %r9 + movq %r12, %r11 + call neoscrypt_xor_salsa + leaq 64(%rbp), %rax + leaq 128(%rbp), %rdx + movdqa 0(%rax), %xmm0 + movdqa 16(%rax), %xmm1 + movdqa 32(%rax), %xmm2 + movdqa 48(%rax), %xmm3 + movdqa 0(%rdx), %xmm4 + movdqa 16(%rdx), %xmm5 + movdqa 32(%rdx), %xmm6 + movdqa 48(%rdx), %xmm7 + movdqa %xmm0, 0(%rdx) + movdqa %xmm1, 16(%rdx) + movdqa %xmm2, 32(%rdx) + movdqa %xmm3, 48(%rdx) + movdqa %xmm4, 0(%rax) + movdqa %xmm5, 16(%rax) + movdqa %xmm6, 32(%rax) + movdqa %xmm7, 48(%rax) + incq %r13 + cmpq $128, %r13 + jnz .salsa_ns2 + +/* blkxor(X, Z) */ + leaq 256(%rbp), %rcx + movdqa 0(%rbp), %xmm0 + movdqa 16(%rbp), %xmm1 + movdqa 32(%rbp), %xmm2 + movdqa 48(%rbp), %xmm3 + movdqa 64(%rbp), %xmm4 + movdqa 80(%rbp), %xmm5 + movdqa 96(%rbp), %xmm6 + movdqa 112(%rbp), %xmm7 + movdqa 128(%rbp), %xmm8 + movdqa 144(%rbp), %xmm9 + movdqa 160(%rbp), %xmm10 + movdqa 176(%rbp), %xmm11 + movdqa 192(%rbp), %xmm12 + movdqa 208(%rbp), %xmm13 + movdqa 224(%rbp), %xmm14 + movdqa 240(%rbp), %xmm15 + pxor 0(%rcx), %xmm0 + pxor 16(%rcx), %xmm1 + pxor 32(%rcx), %xmm2 + pxor 48(%rcx), %xmm3 + pxor 64(%rcx), %xmm4 + pxor 80(%rcx), %xmm5 + pxor 96(%rcx), %xmm6 + pxor 112(%rcx), %xmm7 + pxor 128(%rcx), %xmm8 + pxor 144(%rcx), %xmm9 + pxor 160(%rcx), %xmm10 + pxor 176(%rcx), %xmm11 + pxor 192(%rcx), %xmm12 + pxor 208(%rcx), %xmm13 + pxor 224(%rcx), %xmm14 + pxor 240(%rcx), %xmm15 + movdqa %xmm0, 0(%rbp) + movdqa %xmm1, 16(%rbp) + movdqa %xmm2, 32(%rbp) + movdqa %xmm3, 48(%rbp) + movdqa %xmm4, 64(%rbp) + movdqa %xmm5, 80(%rbp) + movdqa %xmm6, 96(%rbp) + movdqa %xmm7, 112(%rbp) + movdqa %xmm8, 128(%rbp) + movdqa %xmm9, 144(%rbp) + movdqa %xmm10, 160(%rbp) + movdqa %xmm11, 176(%rbp) + movdqa %xmm12, 192(%rbp) + movdqa %xmm13, 208(%rbp) + movdqa %xmm14, 224(%rbp) + movdqa %xmm15, 240(%rbp) + +/* FastKDF */ +#ifdef WIN64 +#ifdef OPT + movq %r14, %rcx + movq %rbp, %rdx + movq %r15, %r8 + xorq %r9, %r9 + incq %r9 + call neoscrypt_fastkdf_opt +#else + movq %r14, %rcx + movq $80, %rdx + movq %rbp, %r8 + movq $256, %r9 + movq $32, %rax + movq %rax, 32(%rsp) + movq %r15, 40(%rsp) + movq %rax, 48(%rsp) + call neoscrypt_fastkdf +#endif /* OPT */ +#else +#ifdef OPT + movq %r14, %rdi + movq %rbp, %rsi + movq %r15, %rdx + xorq %rcx, %rcx + incq %rcx +#ifdef __APPLE__ + call _neoscrypt_fastkdf_opt +#else + call neoscrypt_fastkdf_opt +#endif /* __APPLE__ */ +#else + movq %r14, %rdi + movq $80, %rsi + movq %rbp, %rdx + movq $256, %rcx + movq $32, %r8 + movq %r15, %r9 + movq $32, 0(%rsp) +#ifdef __APPLE__ + call _neoscrypt_fastkdf +#else + call neoscrypt_fastkdf +#endif /* __APPLE__ */ +#endif /* OPT */ +#endif /* WIN64 */ + +#ifdef WIN64 +/* free memory */ + movq 64(%rsp), %rcx + call free +/* restore stack */ + addq $128, %rsp +#else +/* restore stack */ + movq 32(%rsp), %rsp +#endif + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbp + popq %rbx +#ifdef WIN64 + popq %rsi + popq %rdi +#endif + ret + +.neoscrypt_sse2: + movq $10, %r12 + + xorq %r13, %r13 +.chacha_ns1_sse2: +/* blkcpy(V, Z) */ + leaq 512(%rbp), %rax + movq %r13, %rdx + movb $8, %cl + shlq %cl, %rdx + leaq 256(%rbp), %rcx + addq %rdx, %rax + movdqa 0(%rcx), %xmm0 + movdqa 16(%rcx), %xmm1 + movdqa 32(%rcx), %xmm2 + movdqa 48(%rcx), %xmm3 + movdqa 64(%rcx), %xmm4 + movdqa 80(%rcx), %xmm5 + movdqa 96(%rcx), %xmm6 + movdqa 112(%rcx), %xmm7 + movdqa 128(%rcx), %xmm8 + movdqa 144(%rcx), %xmm9 + movdqa 160(%rcx), %xmm10 + movdqa 176(%rcx), %xmm11 + movdqa 192(%rcx), %xmm12 + movdqa 208(%rcx), %xmm13 + movdqa 224(%rcx), %xmm14 + movdqa 240(%rcx), %xmm15 + movdqa %xmm0, 0(%rax) + movdqa %xmm1, 16(%rax) + movdqa %xmm2, 32(%rax) + movdqa %xmm3, 48(%rax) + movdqa %xmm4, 64(%rax) + movdqa %xmm5, 80(%rax) + movdqa %xmm6, 96(%rax) + movdqa %xmm7, 112(%rax) + movdqa %xmm8, 128(%rax) + movdqa %xmm9, 144(%rax) + movdqa %xmm10, 160(%rax) + movdqa %xmm11, 176(%rax) + movdqa %xmm12, 192(%rax) + movdqa %xmm13, 208(%rax) + movdqa %xmm14, 224(%rax) + movdqa %xmm15, 240(%rax) +/* blkmix(Z) */ + leaq 256(%rbp), %r8 + leaq 448(%rbp), %r9 + movq %r12, %r10 + call neoscrypt_xor_chacha_sse2 + leaq 320(%rbp), %r8 + leaq 256(%rbp), %r9 + movq %r12, %r10 + call neoscrypt_xor_chacha_sse2 + leaq 384(%rbp), %r8 + leaq 320(%rbp), %r9 + movq %r12, %r10 + call neoscrypt_xor_chacha_sse2 + leaq 448(%rbp), %r8 + leaq 384(%rbp), %r9 + movq %r12, %r10 + call neoscrypt_xor_chacha_sse2 + leaq 320(%rbp), %rax + leaq 384(%rbp), %rdx + movdqa 0(%rax), %xmm0 + movdqa 16(%rax), %xmm1 + movdqa 32(%rax), %xmm2 + movdqa 48(%rax), %xmm3 + movdqa 0(%rdx), %xmm4 + movdqa 16(%rdx), %xmm5 + movdqa 32(%rdx), %xmm6 + movdqa 48(%rdx), %xmm7 + movdqa %xmm0, 0(%rdx) + movdqa %xmm1, 16(%rdx) + movdqa %xmm2, 32(%rdx) + movdqa %xmm3, 48(%rdx) + movdqa %xmm4, 0(%rax) + movdqa %xmm5, 16(%rax) + movdqa %xmm6, 32(%rax) + movdqa %xmm7, 48(%rax) + incq %r13 + cmpq $128, %r13 + jnz .chacha_ns1_sse2 + + xorq %r13, %r13 +.chacha_ns2_sse2: +/* integerify(Z) mod 128 */ + leaq 256(%rbp), %rax + leaq 512(%rbp), %rcx + xorq %rdx, %rdx + movl 448(%rbp), %edx + andl $0x7F, %edx + shlq $8, %rdx + addq %rdx, %rcx +/* blkxor(Z, V) */ + movdqa 0(%rax), %xmm0 + movdqa 16(%rax), %xmm1 + movdqa 32(%rax), %xmm2 + movdqa 48(%rax), %xmm3 + movdqa 64(%rax), %xmm4 + movdqa 80(%rax), %xmm5 + movdqa 96(%rax), %xmm6 + movdqa 112(%rax), %xmm7 + movdqa 128(%rax), %xmm8 + movdqa 144(%rax), %xmm9 + movdqa 160(%rax), %xmm10 + movdqa 176(%rax), %xmm11 + movdqa 192(%rax), %xmm12 + movdqa 208(%rax), %xmm13 + movdqa 224(%rax), %xmm14 + movdqa 240(%rax), %xmm15 + pxor 0(%rcx), %xmm0 + pxor 16(%rcx), %xmm1 + pxor 32(%rcx), %xmm2 + pxor 48(%rcx), %xmm3 + pxor 64(%rcx), %xmm4 + pxor 80(%rcx), %xmm5 + pxor 96(%rcx), %xmm6 + pxor 112(%rcx), %xmm7 + pxor 128(%rcx), %xmm8 + pxor 144(%rcx), %xmm9 + pxor 160(%rcx), %xmm10 + pxor 176(%rcx), %xmm11 + pxor 192(%rcx), %xmm12 + pxor 208(%rcx), %xmm13 + pxor 224(%rcx), %xmm14 + pxor 240(%rcx), %xmm15 + movdqa %xmm0, 0(%rax) + movdqa %xmm1, 16(%rax) + movdqa %xmm2, 32(%rax) + movdqa %xmm3, 48(%rax) + movdqa %xmm4, 64(%rax) + movdqa %xmm5, 80(%rax) + movdqa %xmm6, 96(%rax) + movdqa %xmm7, 112(%rax) + movdqa %xmm8, 128(%rax) + movdqa %xmm9, 144(%rax) + movdqa %xmm10, 160(%rax) + movdqa %xmm11, 176(%rax) + movdqa %xmm12, 192(%rax) + movdqa %xmm13, 208(%rax) + movdqa %xmm14, 224(%rax) + movdqa %xmm15, 240(%rax) +/* blkmix(Z) */ + leaq 256(%rbp), %r8 + leaq 448(%rbp), %r9 + movq %r12, %r10 + call neoscrypt_xor_chacha_sse2 + leaq 320(%rbp), %r8 + leaq 256(%rbp), %r9 + movq %r12, %r10 + call neoscrypt_xor_chacha_sse2 + leaq 384(%rbp), %r8 + leaq 320(%rbp), %r9 + movq %r12, %r10 + call neoscrypt_xor_chacha_sse2 + leaq 448(%rbp), %r8 + leaq 384(%rbp), %r9 + movq %r12, %r10 + call neoscrypt_xor_chacha_sse2 + leaq 320(%rbp), %rax + leaq 384(%rbp), %rdx + movdqa 0(%rax), %xmm0 + movdqa 16(%rax), %xmm1 + movdqa 32(%rax), %xmm2 + movdqa 48(%rax), %xmm3 + movdqa 0(%rdx), %xmm4 + movdqa 16(%rdx), %xmm5 + movdqa 32(%rdx), %xmm6 + movdqa 48(%rdx), %xmm7 + movdqa %xmm0, 0(%rdx) + movdqa %xmm1, 16(%rdx) + movdqa %xmm2, 32(%rdx) + movdqa %xmm3, 48(%rdx) + movdqa %xmm4, 0(%rax) + movdqa %xmm5, 16(%rax) + movdqa %xmm6, 32(%rax) + movdqa %xmm7, 48(%rax) + incq %r13 + cmpq $128, %r13 + jnz .chacha_ns2_sse2 + + movq %rbp, %r8 + movq $4, %r9 + call neoscrypt_salsa_tangle_sse2 + + xorq %r13, %r13 +.salsa_ns1_sse2: +/* blkcpy(V, X) */ + leaq 512(%rbp), %rax + movq %r13, %rdx + movb $8, %cl + shlq %cl, %rdx + addq %rdx, %rax + movdqa 0(%rbp), %xmm0 + movdqa 16(%rbp), %xmm1 + movdqa 32(%rbp), %xmm2 + movdqa 48(%rbp), %xmm3 + movdqa 64(%rbp), %xmm4 + movdqa 80(%rbp), %xmm5 + movdqa 96(%rbp), %xmm6 + movdqa 112(%rbp), %xmm7 + movdqa 128(%rbp), %xmm8 + movdqa 144(%rbp), %xmm9 + movdqa 160(%rbp), %xmm10 + movdqa 176(%rbp), %xmm11 + movdqa 192(%rbp), %xmm12 + movdqa 208(%rbp), %xmm13 + movdqa 224(%rbp), %xmm14 + movdqa 240(%rbp), %xmm15 + movdqa %xmm0, 0(%rax) + movdqa %xmm1, 16(%rax) + movdqa %xmm2, 32(%rax) + movdqa %xmm3, 48(%rax) + movdqa %xmm4, 64(%rax) + movdqa %xmm5, 80(%rax) + movdqa %xmm6, 96(%rax) + movdqa %xmm7, 112(%rax) + movdqa %xmm8, 128(%rax) + movdqa %xmm9, 144(%rax) + movdqa %xmm10, 160(%rax) + movdqa %xmm11, 176(%rax) + movdqa %xmm12, 192(%rax) + movdqa %xmm13, 208(%rax) + movdqa %xmm14, 224(%rax) + movdqa %xmm15, 240(%rax) +/* blkmix(X) */ + movq %rbp, %r8 + leaq 192(%rbp), %r9 + movq %r12, %r10 + call neoscrypt_xor_salsa_sse2 + leaq 64(%rbp), %r8 + movq %rbp, %r9 + movq %r12, %r10 + call neoscrypt_xor_salsa_sse2 + leaq 128(%rbp), %r8 + leaq 64(%rbp), %r9 + movq %r12, %r10 + call neoscrypt_xor_salsa_sse2 + leaq 192(%rbp), %r8 + leaq 128(%rbp), %r9 + movq %r12, %r10 + call neoscrypt_xor_salsa_sse2 + leaq 64(%rbp), %rax + leaq 128(%rbp), %rdx + movdqa 0(%rax), %xmm0 + movdqa 16(%rax), %xmm1 + movdqa 32(%rax), %xmm2 + movdqa 48(%rax), %xmm3 + movdqa 0(%rdx), %xmm4 + movdqa 16(%rdx), %xmm5 + movdqa 32(%rdx), %xmm6 + movdqa 48(%rdx), %xmm7 + movdqa %xmm0, 0(%rdx) + movdqa %xmm1, 16(%rdx) + movdqa %xmm2, 32(%rdx) + movdqa %xmm3, 48(%rdx) + movdqa %xmm4, 0(%rax) + movdqa %xmm5, 16(%rax) + movdqa %xmm6, 32(%rax) + movdqa %xmm7, 48(%rax) + incq %r13 + cmpq $128, %r13 + jnz .salsa_ns1_sse2 + + xorq %r13, %r13 +.salsa_ns2_sse2: +/* integerify(X) mod 128 */ + leaq 512(%rbp), %rcx + xorq %rdx, %rdx + movl 192(%rbp), %edx + andl $0x7F, %edx + shlq $8, %rdx + addq %rdx, %rcx +/* blkxor(X, V) */ + movdqa 0(%rbp), %xmm0 + movdqa 16(%rbp), %xmm1 + movdqa 32(%rbp), %xmm2 + movdqa 48(%rbp), %xmm3 + movdqa 64(%rbp), %xmm4 + movdqa 80(%rbp), %xmm5 + movdqa 96(%rbp), %xmm6 + movdqa 112(%rbp), %xmm7 + movdqa 128(%rbp), %xmm8 + movdqa 144(%rbp), %xmm9 + movdqa 160(%rbp), %xmm10 + movdqa 176(%rbp), %xmm11 + movdqa 192(%rbp), %xmm12 + movdqa 208(%rbp), %xmm13 + movdqa 224(%rbp), %xmm14 + movdqa 240(%rbp), %xmm15 + pxor 0(%rcx), %xmm0 + pxor 16(%rcx), %xmm1 + pxor 32(%rcx), %xmm2 + pxor 48(%rcx), %xmm3 + pxor 64(%rcx), %xmm4 + pxor 80(%rcx), %xmm5 + pxor 96(%rcx), %xmm6 + pxor 112(%rcx), %xmm7 + pxor 128(%rcx), %xmm8 + pxor 144(%rcx), %xmm9 + pxor 160(%rcx), %xmm10 + pxor 176(%rcx), %xmm11 + pxor 192(%rcx), %xmm12 + pxor 208(%rcx), %xmm13 + pxor 224(%rcx), %xmm14 + pxor 240(%rcx), %xmm15 + movdqa %xmm0, 0(%rbp) + movdqa %xmm1, 16(%rbp) + movdqa %xmm2, 32(%rbp) + movdqa %xmm3, 48(%rbp) + movdqa %xmm4, 64(%rbp) + movdqa %xmm5, 80(%rbp) + movdqa %xmm6, 96(%rbp) + movdqa %xmm7, 112(%rbp) + movdqa %xmm8, 128(%rbp) + movdqa %xmm9, 144(%rbp) + movdqa %xmm10, 160(%rbp) + movdqa %xmm11, 176(%rbp) + movdqa %xmm12, 192(%rbp) + movdqa %xmm13, 208(%rbp) + movdqa %xmm14, 224(%rbp) + movdqa %xmm15, 240(%rbp) +/* blkmix(X) */ + movq %rbp, %r8 + leaq 192(%rbp), %r9 + movq %r12, %r10 + call neoscrypt_xor_salsa_sse2 + leaq 64(%rbp), %r8 + movq %rbp, %r9 + movq %r12, %r10 + call neoscrypt_xor_salsa_sse2 + leaq 128(%rbp), %r8 + leaq 64(%rbp), %r9 + movq %r12, %r10 + call neoscrypt_xor_salsa_sse2 + leaq 192(%rbp), %r8 + leaq 128(%rbp), %r9 + movq %r12, %r10 + call neoscrypt_xor_salsa_sse2 + leaq 64(%rbp), %rax + leaq 128(%rbp), %rdx + movdqa 0(%rax), %xmm0 + movdqa 16(%rax), %xmm1 + movdqa 32(%rax), %xmm2 + movdqa 48(%rax), %xmm3 + movdqa 0(%rdx), %xmm4 + movdqa 16(%rdx), %xmm5 + movdqa 32(%rdx), %xmm6 + movdqa 48(%rdx), %xmm7 + movdqa %xmm0, 0(%rdx) + movdqa %xmm1, 16(%rdx) + movdqa %xmm2, 32(%rdx) + movdqa %xmm3, 48(%rdx) + movdqa %xmm4, 0(%rax) + movdqa %xmm5, 16(%rax) + movdqa %xmm6, 32(%rax) + movdqa %xmm7, 48(%rax) + incq %r13 + cmpq $128, %r13 + jnz .salsa_ns2_sse2 + + movq %rbp, %r8 + movq $4, %r9 + call neoscrypt_salsa_tangle_sse2 + +/* blkxor(X, Z) */ + leaq 256(%rbp), %rcx + movdqa 0(%rbp), %xmm0 + movdqa 16(%rbp), %xmm1 + movdqa 32(%rbp), %xmm2 + movdqa 48(%rbp), %xmm3 + movdqa 64(%rbp), %xmm4 + movdqa 80(%rbp), %xmm5 + movdqa 96(%rbp), %xmm6 + movdqa 112(%rbp), %xmm7 + movdqa 128(%rbp), %xmm8 + movdqa 144(%rbp), %xmm9 + movdqa 160(%rbp), %xmm10 + movdqa 176(%rbp), %xmm11 + movdqa 192(%rbp), %xmm12 + movdqa 208(%rbp), %xmm13 + movdqa 224(%rbp), %xmm14 + movdqa 240(%rbp), %xmm15 + pxor 0(%rcx), %xmm0 + pxor 16(%rcx), %xmm1 + pxor 32(%rcx), %xmm2 + pxor 48(%rcx), %xmm3 + pxor 64(%rcx), %xmm4 + pxor 80(%rcx), %xmm5 + pxor 96(%rcx), %xmm6 + pxor 112(%rcx), %xmm7 + pxor 128(%rcx), %xmm8 + pxor 144(%rcx), %xmm9 + pxor 160(%rcx), %xmm10 + pxor 176(%rcx), %xmm11 + pxor 192(%rcx), %xmm12 + pxor 208(%rcx), %xmm13 + pxor 224(%rcx), %xmm14 + pxor 240(%rcx), %xmm15 + movdqa %xmm0, 0(%rbp) + movdqa %xmm1, 16(%rbp) + movdqa %xmm2, 32(%rbp) + movdqa %xmm3, 48(%rbp) + movdqa %xmm4, 64(%rbp) + movdqa %xmm5, 80(%rbp) + movdqa %xmm6, 96(%rbp) + movdqa %xmm7, 112(%rbp) + movdqa %xmm8, 128(%rbp) + movdqa %xmm9, 144(%rbp) + movdqa %xmm10, 160(%rbp) + movdqa %xmm11, 176(%rbp) + movdqa %xmm12, 192(%rbp) + movdqa %xmm13, 208(%rbp) + movdqa %xmm14, 224(%rbp) + movdqa %xmm15, 240(%rbp) + +/* FastKDF */ +#ifdef WIN64 +#ifdef OPT + movq %r14, %rcx + movq %rbp, %rdx + movq %r15, %r8 + xorq %r9, %r9 + incq %r9 + call neoscrypt_fastkdf_opt +#else + movq %r14, %rcx + movq $80, %rdx + movq %rbp, %r8 + movq $256, %r9 + movq $32, %rax + movq %rax, 32(%rsp) + movq %r15, 40(%rsp) + movq %rax, 48(%rsp) + call neoscrypt_fastkdf +#endif /* OPT */ +#else +#ifdef OPT + movq %r14, %rdi + movq %rbp, %rsi + movq %r15, %rdx + xorq %rcx, %rcx + incq %rcx +#ifdef __APPLE__ + call _neoscrypt_fastkdf_opt +#else + call neoscrypt_fastkdf_opt +#endif /* __APPLE__ */ +#else + movq %r14, %rdi + movq $80, %rsi + movq %rbp, %rdx + movq $256, %rcx + movq $32, %rax + movq %rax, %r8 + movq %r15, %r9 + movq %rax, 0(%rsp) +#ifdef __APPLE__ + call _neoscrypt_fastkdf +#else + call neoscrypt_fastkdf +#endif /* __APPLE__ */ +#endif /* OPT */ +#endif /* WIN64 */ + +#ifdef WIN64 +/* free memory */ + movq 64(%rsp), %rcx + call free +/* restore stack */ + addq $128, %rsp +#else +/* restore stack */ + movq 32(%rsp), %rsp +#endif + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbp + popq %rbx +#ifdef WIN64 + popq %rsi + popq %rdi +#endif + ret + +#ifdef SHA256 + +.scrypt: +#ifdef WIN64 +/* attempt to allocate 131200 + 128 bytes of stack space fails miserably; + * have to use malloc() and free() instead */ + subq $128, %rsp +/* allocate memory (33 pages of 4Kb each) */ + movq $0x21000, %rcx + call malloc +/* save memory address */ + movq %rax, 64(%rsp) +/* align memory */ + addq $64, %rax + andq $0xFFFFFFFFFFFFFFC0, %rax +/* memory base: X, Z, V */ + leaq 64(%rax), %rbp +#else +/* align stack */ + movq %rsp, %rax + andq $0xFFFFFFFFFFFFFFC0, %rsp + subq $0x20100, %rsp +/* save unaligned stack */ + movq %rax, 32(%rsp) +/* memory base: X, Z, V */ + leaq 128(%rsp), %rbp +#endif /* WIN64 */ + +/* PBKDF2-HMAC-SHA256 */ +#ifdef WIN64 + movq $80, %rax + movq %r14, %rcx + movq %rax, %rdx + movq %r14, %r8 + movq %rax, %r9 + movq $1, 32(%rsp) + movq %rbp, 40(%rsp) + movq $128, 48(%rsp) + call neoscrypt_pbkdf2_sha256 +#else + movq $80, %rax + movq %r14, %rdi + movq %rax, %rsi + movq %r14, %rdx + movq %rax, %rcx + movq $1, %r8 + movq %rbp, %r9 + movq $128, 0(%rsp) +#ifdef __APPLE__ + call _neoscrypt_pbkdf2_sha256 +#else + call neoscrypt_pbkdf2_sha256 +#endif /* __APPLE__ */ +#endif /* WIN64 */ + +/* SSE2 switch */ + testl $0x1000, %ebx + jnz .scrypt_sse2 + +/* tempmem and double rounds */ + leaq -64(%rbp), %r10 + movq $4, %r12 + + xorq %r13, %r13 +.salsa_s1: +/* blkcpy(V, X) */ + leaq 128(%rbp), %rax + movq %r13, %rdx + movb $7, %cl + shlq %cl, %rdx + addq %rdx, %rax + movdqa 0(%rbp), %xmm0 + movdqa 16(%rbp), %xmm1 + movdqa 32(%rbp), %xmm2 + movdqa 48(%rbp), %xmm3 + movdqa 64(%rbp), %xmm4 + movdqa 80(%rbp), %xmm5 + movdqa 96(%rbp), %xmm6 + movdqa 112(%rbp), %xmm7 + movdqa %xmm0, 0(%rax) + movdqa %xmm1, 16(%rax) + movdqa %xmm2, 32(%rax) + movdqa %xmm3, 48(%rax) + movdqa %xmm4, 64(%rax) + movdqa %xmm5, 80(%rax) + movdqa %xmm6, 96(%rax) + movdqa %xmm7, 112(%rax) +/* blkmix(X) */ + movq %rbp, %r8 + leaq 64(%rbp), %r9 + movq %r12, %r11 + call neoscrypt_xor_salsa + leaq 64(%rbp), %r8 + movq %rbp, %r9 + movq %r12, %r11 + call neoscrypt_xor_salsa + incq %r13 + cmpq $1024, %r13 + jnz .salsa_s1 + + xorq %r13, %r13 +.salsa_s2: +/* integerify(X) mod 1024 */ + leaq 128(%rbp), %rcx + xorq %rdx, %rdx + movl 64(%rbp), %edx + andl $0x03FF, %edx + shlq $7, %rdx + addq %rdx, %rcx +/* blkxor(X, V) */ + movdqa 0(%rbp), %xmm0 + movdqa 16(%rbp), %xmm1 + movdqa 32(%rbp), %xmm2 + movdqa 48(%rbp), %xmm3 + movdqa 64(%rbp), %xmm4 + movdqa 80(%rbp), %xmm5 + movdqa 96(%rbp), %xmm6 + movdqa 112(%rbp), %xmm7 + pxor 0(%rcx), %xmm0 + pxor 16(%rcx), %xmm1 + pxor 32(%rcx), %xmm2 + pxor 48(%rcx), %xmm3 + pxor 64(%rcx), %xmm4 + pxor 80(%rcx), %xmm5 + pxor 96(%rcx), %xmm6 + pxor 112(%rcx), %xmm7 + movdqa %xmm0, 0(%rbp) + movdqa %xmm1, 16(%rbp) + movdqa %xmm2, 32(%rbp) + movdqa %xmm3, 48(%rbp) + movdqa %xmm4, 64(%rbp) + movdqa %xmm5, 80(%rbp) + movdqa %xmm6, 96(%rbp) + movdqa %xmm7, 112(%rbp) +/* blkmix(X) */ + movq %rbp, %r8 + leaq 64(%rbp), %r9 + movq %r12, %r11 + call neoscrypt_xor_salsa + leaq 64(%rbp), %r8 + movq %rbp, %r9 + movq %r12, %r11 + call neoscrypt_xor_salsa + incq %r13 + cmpq $1024, %r13 + jnz .salsa_s2 + +/* PBKDF2-HMAC-SHA256 */ +#ifdef WIN64 + movq %r14, %rcx + movq $80, %rdx + movq %rbp, %r8 + movq $128, %r9 + movq $1, 32(%rsp) + movq %r15, 40(%rsp) + movq $32, 48(%rsp) + call neoscrypt_pbkdf2_sha256 +#else + movq %r14, %rdi + movq $80, %rsi + movq %rbp, %rdx + movq $128, %rcx + movq $1, %r8 + movq %r15, %r9 + movq $32, 0(%rsp) +#ifdef __APPLE__ + call _neoscrypt_pbkdf2_sha256 +#else + call neoscrypt_pbkdf2_sha256 +#endif /* __APPLE__ */ + +#endif /* WIN64 */ + +#ifdef WIN64 +/* free memory */ + movq 64(%rsp), %rcx + call free +/* restore stack */ + addq $128, %rsp +#else +/* restore stack */ + movq 32(%rsp), %rsp +#endif + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbp + popq %rbx +#ifdef WIN64 + popq %rsi + popq %rdi +#endif + ret + +.scrypt_sse2: + movq %rbp, %r8 + movq $2, %r9 + call neoscrypt_salsa_tangle_sse2 + + movq $4, %r12 + + xorq %r13, %r13 +.salsa_s1_sse2: +/* blkcpy(V, X) */ + leaq 128(%rbp), %rax + movq %r13, %rdx + movb $7, %cl + shlq %cl, %rdx + addq %rdx, %rax + movdqa 0(%rbp), %xmm0 + movdqa 16(%rbp), %xmm1 + movdqa 32(%rbp), %xmm2 + movdqa 48(%rbp), %xmm3 + movdqa 64(%rbp), %xmm4 + movdqa 80(%rbp), %xmm5 + movdqa 96(%rbp), %xmm6 + movdqa 112(%rbp), %xmm7 + movdqa %xmm0, 0(%rax) + movdqa %xmm1, 16(%rax) + movdqa %xmm2, 32(%rax) + movdqa %xmm3, 48(%rax) + movdqa %xmm4, 64(%rax) + movdqa %xmm5, 80(%rax) + movdqa %xmm6, 96(%rax) + movdqa %xmm7, 112(%rax) +/* blkmix(X) */ + movq %rbp, %r8 + leaq 64(%rbp), %r9 + movq %r12, %r10 + call neoscrypt_xor_salsa_sse2 + leaq 64(%rbp), %r8 + movq %rbp, %r9 + movq %r12, %r10 + call neoscrypt_xor_salsa_sse2 + incq %r13 + cmpq $1024, %r13 + jnz .salsa_s1_sse2 + + xorq %r13, %r13 +.salsa_s2_sse2: +/* integerify(X) mod 1024 */ + leaq 128(%rbp), %rcx + xorq %rdx, %rdx + movl 64(%rbp), %edx + andl $0x03FF, %edx + shlq $7, %rdx + addq %rdx, %rcx +/* blkxor(X, V) */ + movdqa 0(%rbp), %xmm0 + movdqa 16(%rbp), %xmm1 + movdqa 32(%rbp), %xmm2 + movdqa 48(%rbp), %xmm3 + movdqa 64(%rbp), %xmm4 + movdqa 80(%rbp), %xmm5 + movdqa 96(%rbp), %xmm6 + movdqa 112(%rbp), %xmm7 + pxor 0(%rcx), %xmm0 + pxor 16(%rcx), %xmm1 + pxor 32(%rcx), %xmm2 + pxor 48(%rcx), %xmm3 + pxor 64(%rcx), %xmm4 + pxor 80(%rcx), %xmm5 + pxor 96(%rcx), %xmm6 + pxor 112(%rcx), %xmm7 + movdqa %xmm0, 0(%rbp) + movdqa %xmm1, 16(%rbp) + movdqa %xmm2, 32(%rbp) + movdqa %xmm3, 48(%rbp) + movdqa %xmm4, 64(%rbp) + movdqa %xmm5, 80(%rbp) + movdqa %xmm6, 96(%rbp) + movdqa %xmm7, 112(%rbp) +/* blkmix(X) */ + movq %rbp, %r8 + leaq 64(%rbp), %r9 + movq %r12, %r10 + call neoscrypt_xor_salsa_sse2 + leaq 64(%rbp), %r8 + movq %rbp, %r9 + movq %r12, %r10 + call neoscrypt_xor_salsa_sse2 + incq %r13 + cmpq $1024, %r13 + jnz .salsa_s2_sse2 + + movq %rbp, %r8 + movq $2, %r9 + call neoscrypt_salsa_tangle_sse2 + +/* PBKDF2-HMAC-SHA256 */ +#ifdef WIN64 + movq %r14, %rcx + movq $80, %rdx + movq %rbp, %r8 + movq $128, %r9 + movq $1, 32(%rsp) + movq %r15, 40(%rsp) + movq $32, 48(%rsp) + call neoscrypt_pbkdf2_sha256 +#else + movq %r14, %rdi + movq $80, %rsi + movq %rbp, %rdx + movq $128, %rcx + movq $1, %r8 + movq %r15, %r9 + movq $32, 0(%rsp) +#ifdef __APPLE__ + call _neoscrypt_pbkdf2_sha256 +#else + call neoscrypt_pbkdf2_sha256 +#endif /* __APPLE__ */ +#endif /* WIN64 */ + +#ifdef WIN64 +/* free memory */ + movq 64(%rsp), %rcx + call free +/* restore stack */ + addq $128, %rsp +#else +/* restore stack */ + movq 32(%rsp), %rsp +#endif + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbp + popq %rbx +#ifdef WIN64 + popq %rsi + popq %rdi +#endif + ret + +#endif /* SHA256 */ + +#ifdef MINER_4WAY + +/* blake2s_compress_4way(mem) + * AMD64 (SSE2) BLAKE2s 4-way block compression */ +.globl blake2s_compress_4way +.globl _blake2s_compress_4way +blake2s_compress_4way: +_blake2s_compress_4way: + pushq %rbx + pushq %rbp + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 +#ifdef WIN64 + pushq %rdi + pushq %rsi + subq $160, %rsp + movdqu %xmm6, 144(%rsp) + movdqu %xmm7, 128(%rsp) + movdqu %xmm8, 112(%rsp) + movdqu %xmm9, 96(%rsp) + movdqu %xmm10, 80(%rsp) + movdqu %xmm11, 64(%rsp) + movdqu %xmm12, 48(%rsp) + movdqu %xmm13, 32(%rsp) + movdqu %xmm14, 16(%rsp) + movdqu %xmm15, 0(%rsp) + movq %rcx, %rdi +#endif + +/* initialise */ + leaq 448(%rdi), %rsi + movdqa 0(%rdi), %xmm0 + movdqa 16(%rdi), %xmm8 + movdqa 32(%rdi), %xmm4 + movdqa 48(%rdi), %xmm10 + movdqa 64(%rdi), %xmm12 + movdqa 80(%rdi), %xmm1 + movdqa 96(%rdi), %xmm13 + movdqa 112(%rdi), %xmm5 +/* movdqa %xmm0, 0(%rsi) */ +/* movdqa %xmm8, 16(%rsi) */ +/* movdqa %xmm4, 32(%rsi) */ +/* movdqa %xmm10, 48(%rsi) */ +/* movdqa %xmm12, 64(%rsi) */ +/* movdqa %xmm1, 80(%rsi) */ +/* movdqa %xmm13, 96(%rsi) */ +/* movdqa %xmm5, 112(%rsi) */ + movq $0x6A09E6676A09E667, %r8 + movq $0xBB67AE85BB67AE85, %r9 + movq $0x3C6EF3723C6EF372, %r10 + movq $0xA54FF53AA54FF53A, %r11 + movq %r8, 128(%rsi) + movq %r8, 136(%rsi) +/* movq %r9, 144(%rsi) */ +/* movq %r9, 152(%rsi) */ +#ifndef MOVQ_FIX + movq %r9, %xmm9 + movlhps %xmm9, %xmm9 +#else + movq %r9, 0(%rsi) + movq %r9, 8(%rsi) + movdqa 0(%rsi), %xmm9 +#endif + movq %r10, 160(%rsi) + movq %r10, 168(%rsi) +/* movq %r11, 176(%rsi) */ +/* movq %r11, 184(%rsi) */ +#ifndef MOVQ_FIX + movq %r11, %xmm11 + movlhps %xmm11, %xmm11 +#else + movq %r11, 0(%rsi) + movq %r11, 8(%rsi) + movdqa 0(%rsi), %xmm11 +#endif + movq $0x510E527F510E527F, %r12 + movq $0x9B05688C9B05688C, %r13 + movq $0x1F83D9AB1F83D9AB, %r14 + movq $0x5BE0CD195BE0CD19, %r15 + movq 128(%rdi), %rax + movq 136(%rdi), %rbx + movq 144(%rdi), %rcx + movq 152(%rdi), %rdx + movq 160(%rdi), %r8 + movq 168(%rdi), %r9 + movq 176(%rdi), %r10 + movq 184(%rdi), %r11 +/* movdqa 0(%rsi), %xmm0 */ /* A */ + paddd 192(%rdi), %xmm0 /* A */ + xorq %r12, %rax +/* movdqa 32(%rsi), %xmm4 */ /* C */ + paddd 256(%rdi), %xmm4 /* C */ + xorq %r12, %rbx +/* movdqa 64(%rsi), %xmm12 */ /* A */ + paddd %xmm12, %xmm0 /* A */ + xorq %r13, %rcx +/* movdqa 96(%rsi), %xmm13 */ /* C */ + paddd %xmm13, %xmm4 /* C */ + xorq %r13, %rdx + movdqa 128(%rsi), %xmm2 /* A */ + xorq %r14, %r8 + xorq %r14, %r9 + movdqa 160(%rsi), %xmm6 /* C */ + xorq %r15, %r10 + xorq %r15, %r11 +/* movq %rax, 192(%rsi) */ +/* movq %rbx, 200(%rsi) */ +/* movdqa 192(%rsi), %xmm3 */ /* A */ +#ifndef MOVQ_FIX + movq %rax, %xmm3 + movq %rbx, %xmm15 + movlhps %xmm15, %xmm3 +#else + movq %rax, 0(%rsi) + movq %rbx, 8(%rsi) + movdqa 0(%rsi), %xmm3 +#endif + movq %rcx, 208(%rsi) + pxor %xmm0, %xmm3 /* A */ + movq %rdx, 216(%rsi) +/* movq %r8, 224(%rsi) */ +/* movq %r9, 232(%rsi) */ +/* movdqa 224(%rsi), %xmm7 */ /* C */ +#ifndef MOVQ_FIX + movq %r8, %xmm7 + movq %r9, %xmm15 + movlhps %xmm15, %xmm7 +#else + movq %r8, 0(%rsi) + movq %r9, 8(%rsi) + movdqa 0(%rsi), %xmm7 +#endif + movq %r10, 240(%rsi) + pxor %xmm4, %xmm7 /* C */ + movq %r11, 248(%rsi) +/* round 0 (A and C) */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + paddd 208(%rdi), %xmm0 /* A */ + paddd 272(%rdi), %xmm4 /* C */ + pslld $16, %xmm3 /* A */ + psrld $16, %xmm14 /* A */ + pslld $16, %xmm7 /* C */ + psrld $16, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ + pslld $20, %xmm12 /* A */ + psrld $12, %xmm14 /* A */ + pslld $20, %xmm13 /* C */ + psrld $12, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa %xmm0, 0(%rsi) */ /* A */ +/* movdqa %xmm4, 32(%rsi) */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + pslld $24, %xmm3 /* A */ + psrld $8, %xmm14 /* A */ + pslld $24, %xmm7 /* C */ + psrld $8, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + movdqa %xmm3, 192(%rsi) /* A */ + movdqa %xmm7, 224(%rsi) /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ +/* movdqa %xmm2, 128(%rsi) */ /* A */ +/* movdqa %xmm6, 160(%rsi) */ /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ +/* movdqa 16(%rsi), %xmm8 */ /* B */ +/* movdqa 48(%rsi), %xmm10 */ /* D */ + paddd 224(%rdi), %xmm8 /* B */ + paddd 288(%rdi), %xmm10 /* D */ + pslld $25, %xmm12 /* A */ + psrld $7, %xmm14 /* A */ + pslld $25, %xmm13 /* C */ + psrld $7, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ +/* movdqa %xmm12, 64(%rsi) */ /* A */ +/* movdqa %xmm13, 96(%rsi) */ /* C */ +/* round 0 (B and D) */ +/* movdqa 80(%rsi), %xmm1 */ /* B */ +/* movdqa 112(%rsi), %xmm5 */ /* D */ + movdqa 208(%rsi), %xmm3 /* B */ + movdqa 240(%rsi), %xmm7 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa 144(%rsi), %xmm9 */ /* B */ +/* movdqa 176(%rsi), %xmm11 */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + paddd 240(%rdi), %xmm8 /* B */ + paddd 304(%rdi), %xmm10 /* D */ + pslld $16, %xmm3 /* B */ + psrld $16, %xmm14 /* B */ + pslld $16, %xmm7 /* D */ + psrld $16, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ + pslld $20, %xmm1 /* B */ + psrld $12, %xmm14 /* B */ + pslld $20, %xmm5 /* D */ + psrld $12, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa %xmm8, 16(%rsi) */ /* B */ +/* movdqa %xmm10, 48(%rsi) */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + pslld $24, %xmm3 /* B */ + psrld $8, %xmm14 /* B */ + pslld $24, %xmm7 /* D */ + psrld $8, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ +/* movdqa %xmm3, 208(%rsi) */ /* B */ +/* movdqa %xmm7, 240(%rsi) */ /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ +/* movdqa %xmm9, 144(%rsi) */ /* B */ +/* movdqa %xmm11, 176(%rsi) */ /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ +/* movdqa 0(%rsi), %xmm0 */ /* E */ +/* movdqa 32(%rsi), %xmm4 */ /* G */ + paddd 320(%rdi), %xmm0 /* E */ + paddd 384(%rdi), %xmm4 /* G */ + pslld $25, %xmm1 /* B */ + psrld $7, %xmm14 /* B */ + pslld $25, %xmm5 /* D */ + psrld $7, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ +/* movdqa %xmm1, 80(%rsi) */ /* B */ +/* movdqa %xmm5, 112(%rsi) */ /* D */ +/* round 0 (E and G) */ +/* movdqa 80(%rsi), %xmm1 */ /* E */ +/* movdqa 112(%rsi), %xmm5 */ /* G */ +/* movdqa 240(%rsi), %xmm7 */ /* E */ +/* movdqa 208(%rsi), %xmm3 */ /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa 160(%rsi), %xmm6 */ /* E */ +/* movdqa 128(%rsi), %xmm2 */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + paddd 336(%rdi), %xmm0 /* E */ + paddd 400(%rdi), %xmm4 /* G */ + pslld $16, %xmm7 /* E */ + psrld $16, %xmm14 /* E */ + pslld $16, %xmm3 /* G */ + psrld $16, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ + pslld $20, %xmm1 /* E */ + psrld $12, %xmm14 /* E */ + pslld $20, %xmm5 /* G */ + psrld $12, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa %xmm0, 0(%rsi) */ /* E */ +/* movdqa %xmm4, 32(%rsi) */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + pslld $24, %xmm7 /* E */ + psrld $8, %xmm14 /* E */ + pslld $24, %xmm3 /* G */ + psrld $8, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + movdqa %xmm7, 240(%rsi) /* E */ + movdqa %xmm3, 208(%rsi) /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ +/* movdqa %xmm6, 160(%rsi) */ /* E */ +/* movdqa %xmm2, 128(%rsi) */ /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ +/* movdqa 16(%rsi), %xmm8 */ /* F */ +/* movdqa 48(%rsi), %xmm10 */ /* H */ + paddd 352(%rdi), %xmm8 /* F */ + paddd 416(%rdi), %xmm10 /* H */ + pslld $25, %xmm1 /* E */ + psrld $7, %xmm14 /* E */ + pslld $25, %xmm5 /* G */ + psrld $7, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ +/* movdqa %xmm1, 80(%rsi) */ /* E */ +/* movdqa %xmm5, 112(%rsi) */ /* G */ +/* round 0 (F and H) */ +/* movdqa 96(%rsi), %xmm13 */ /* F */ +/* movdqa 64(%rsi), %xmm12 */ /* H */ + movdqa 192(%rsi), %xmm3 /* F */ + movdqa 224(%rsi), %xmm7 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa 176(%rsi), %xmm11 */ /* F */ +/* movdqa 144(%rsi), %xmm9 */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + paddd 368(%rdi), %xmm8 /* F */ + paddd 432(%rdi), %xmm10 /* H */ + pslld $16, %xmm3 /* F */ + psrld $16, %xmm14 /* F */ + pslld $16, %xmm7 /* H */ + psrld $16, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + pslld $20, %xmm13 /* F */ + psrld $12, %xmm14 /* F */ + pslld $20, %xmm12 /* H */ + psrld $12, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa %xmm8, 16(%rsi) */ /* F */ +/* movdqa %xmm10, 48(%rsi) */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + pslld $24, %xmm3 /* F */ + psrld $8, %xmm14 /* F */ + pslld $24, %xmm7 /* H */ + psrld $8, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ +/* movdqa %xmm3, 192(%rsi) */ /* F */ +/* movdqa %xmm7, 224(%rsi) */ /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ +/* movdqa %xmm11, 176(%rsi) */ /* F */ +/* movdqa %xmm9, 144(%rsi) */ /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ +/* movdqa 0(%rsi), %xmm0 */ /* A */ +/* movdqa 32(%rsi), %xmm4 */ /* C */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + paddd 416(%rdi), %xmm0 /* A */ + paddd 336(%rdi), %xmm4 /* C */ + pslld $25, %xmm13 /* F */ + psrld $7, %xmm14 /* F */ + pslld $25, %xmm12 /* H */ + psrld $7, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ +/* movdqa %xmm13, 96(%rsi) */ /* F */ +/* movdqa %xmm12, 64(%rsi) */ /* H */ +/* round 1 (A and C) */ +/* movdqa 64(%rsi), %xmm12 */ /* A */ +/* movdqa 96(%rsi), %xmm13 */ /* C */ +/* movdqa 192(%rsi), %xmm3 */ /* A */ +/* movdqa 224(%rsi), %xmm7 */ /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa 128(%rsi), %xmm2 */ /* A */ +/* movdqa 160(%rsi), %xmm6 */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + paddd 352(%rdi), %xmm0 /* A */ + paddd 432(%rdi), %xmm4 /* C */ + pslld $16, %xmm3 /* A */ + psrld $16, %xmm14 /* A */ + pslld $16, %xmm7 /* C */ + psrld $16, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ + pslld $20, %xmm12 /* A */ + psrld $12, %xmm14 /* A */ + pslld $20, %xmm13 /* C */ + psrld $12, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa %xmm0, 0(%rsi) */ /* A */ +/* movdqa %xmm4, 32(%rsi) */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + pslld $24, %xmm3 /* A */ + psrld $8, %xmm14 /* A */ + pslld $24, %xmm7 /* C */ + psrld $8, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + movdqa %xmm3, 192(%rsi) /* A */ + movdqa %xmm7, 224(%rsi) /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ +/* movdqa %xmm2, 128(%rsi) */ /* A */ +/* movdqa %xmm6, 160(%rsi) */ /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ +/* movdqa 16(%rsi), %xmm8 */ /* B */ +/* movdqa 48(%rsi), %xmm10 */ /* D */ + paddd 256(%rdi), %xmm8 /* B */ + paddd 400(%rdi), %xmm10 /* D */ + pslld $25, %xmm12 /* A */ + psrld $7, %xmm14 /* A */ + pslld $25, %xmm13 /* C */ + psrld $7, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ +/* movdqa %xmm12, 64(%rsi) */ /* A */ +/* movdqa %xmm13, 96(%rsi) */ /* C */ +/* round 1 (B and D) */ +/* movdqa 80(%rsi), %xmm1 */ /* B */ +/* movdqa 112(%rsi), %xmm5 */ /* D */ + movdqa 208(%rsi), %xmm3 /* B */ + movdqa 240(%rsi), %xmm7 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa 144(%rsi), %xmm9 */ /* B */ +/* movdqa 176(%rsi), %xmm11 */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + paddd 320(%rdi), %xmm8 /* B */ + paddd 288(%rdi), %xmm10 /* D */ + pslld $16, %xmm3 /* B */ + psrld $16, %xmm14 /* B */ + pslld $16, %xmm7 /* D */ + psrld $16, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ + pslld $20, %xmm1 /* B */ + psrld $12, %xmm14 /* B */ + pslld $20, %xmm5 /* D */ + psrld $12, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa %xmm8, 16(%rsi) */ /* B */ +/* movdqa %xmm10, 48(%rsi) */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + pslld $24, %xmm3 /* B */ + psrld $8, %xmm14 /* B */ + pslld $24, %xmm7 /* D */ + psrld $8, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ +/* movdqa %xmm3, 208(%rsi) */ /* B */ +/* movdqa %xmm7, 240(%rsi) */ /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ +/* movdqa %xmm9, 144(%rsi) */ /* B */ +/* movdqa %xmm11, 176(%rsi) */ /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ +/* movdqa 0(%rsi), %xmm0 */ /* E */ +/* movdqa 32(%rsi), %xmm4 */ /* G */ + paddd 208(%rdi), %xmm0 /* E */ + paddd 368(%rdi), %xmm4 /* G */ + pslld $25, %xmm1 /* B */ + psrld $7, %xmm14 /* B */ + pslld $25, %xmm5 /* D */ + psrld $7, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ +/* movdqa %xmm1, 80(%rsi) */ /* B */ +/* movdqa %xmm5, 112(%rsi) */ /* D */ +/* round 1 (E and G) */ +/* movdqa 80(%rsi), %xmm1 */ /* E */ +/* movdqa 112(%rsi), %xmm5 */ /* G */ +/* movdqa 240(%rsi), %xmm7 */ /* E */ +/* movdqa 208(%rsi), %xmm3 */ /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa 160(%rsi), %xmm6 */ /* E */ +/* movdqa 128(%rsi), %xmm2 */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + paddd 384(%rdi), %xmm0 /* E */ + paddd 304(%rdi), %xmm4 /* G */ + pslld $16, %xmm7 /* E */ + psrld $16, %xmm14 /* E */ + pslld $16, %xmm3 /* G */ + psrld $16, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ + pslld $20, %xmm1 /* E */ + psrld $12, %xmm14 /* E */ + pslld $20, %xmm5 /* G */ + psrld $12, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa %xmm0, 0(%rsi) */ /* E */ +/* movdqa %xmm4, 32(%rsi) */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + pslld $24, %xmm7 /* E */ + psrld $8, %xmm14 /* E */ + pslld $24, %xmm3 /* G */ + psrld $8, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + movdqa %xmm7, 240(%rsi) /* E */ + movdqa %xmm3, 208(%rsi) /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ +/* movdqa %xmm6, 160(%rsi) */ /* E */ +/* movdqa %xmm2, 128(%rsi) */ /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ +/* movdqa 16(%rsi), %xmm8 */ /* F */ +/* movdqa 48(%rsi), %xmm10 */ /* H */ + paddd 192(%rdi), %xmm8 /* F */ + paddd 272(%rdi), %xmm10 /* H */ + pslld $25, %xmm1 /* E */ + psrld $7, %xmm14 /* E */ + pslld $25, %xmm5 /* G */ + psrld $7, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ +/* movdqa %xmm1, 80(%rsi) */ /* E */ +/* movdqa %xmm5, 112(%rsi) */ /* G */ +/* round 1 (F and H) */ +/* movdqa 96(%rsi), %xmm13 */ /* F */ +/* movdqa 64(%rsi), %xmm12 */ /* H */ + movdqa 192(%rsi), %xmm3 /* F */ + movdqa 224(%rsi), %xmm7 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa 176(%rsi), %xmm11 */ /* F */ +/* movdqa 144(%rsi), %xmm9 */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + paddd 224(%rdi), %xmm8 /* F */ + paddd 240(%rdi), %xmm10 /* H */ + pslld $16, %xmm3 /* F */ + psrld $16, %xmm14 /* F */ + pslld $16, %xmm7 /* H */ + psrld $16, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + pslld $20, %xmm13 /* F */ + psrld $12, %xmm14 /* F */ + pslld $20, %xmm12 /* H */ + psrld $12, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa %xmm8, 16(%rsi) */ /* F */ +/* movdqa %xmm10, 48(%rsi) */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + pslld $24, %xmm3 /* F */ + psrld $8, %xmm14 /* F */ + pslld $24, %xmm7 /* H */ + psrld $8, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ +/* movdqa %xmm3, 192(%rsi) */ /* F */ +/* movdqa %xmm7, 224(%rsi) */ /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ +/* movdqa %xmm11, 176(%rsi) */ /* F */ +/* movdqa %xmm9, 144(%rsi) */ /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ +/* movdqa 0(%rsi), %xmm0 */ /* A */ +/* movdqa 32(%rsi), %xmm4 */ /* C */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + paddd 368(%rdi), %xmm0 /* A */ + paddd 272(%rdi), %xmm4 /* C */ + pslld $25, %xmm13 /* F */ + psrld $7, %xmm14 /* F */ + pslld $25, %xmm12 /* H */ + psrld $7, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ +/* movdqa %xmm13, 96(%rsi) */ /* F */ +/* movdqa %xmm12, 64(%rsi) */ /* H */ +/* round 2 (A and C) */ +/* movdqa 64(%rsi), %xmm12 */ /* A */ +/* movdqa 96(%rsi), %xmm13 */ /* C */ +/* movdqa 192(%rsi), %xmm3 */ /* A */ +/* movdqa 224(%rsi), %xmm7 */ /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa 128(%rsi), %xmm2 */ /* A */ +/* movdqa 160(%rsi), %xmm6 */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + paddd 320(%rdi), %xmm0 /* A */ + paddd 224(%rdi), %xmm4 /* C */ + pslld $16, %xmm3 /* A */ + psrld $16, %xmm14 /* A */ + pslld $16, %xmm7 /* C */ + psrld $16, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ + pslld $20, %xmm12 /* A */ + psrld $12, %xmm14 /* A */ + pslld $20, %xmm13 /* C */ + psrld $12, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa %xmm0, 0(%rsi) */ /* A */ +/* movdqa %xmm4, 32(%rsi) */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + pslld $24, %xmm3 /* A */ + psrld $8, %xmm14 /* A */ + pslld $24, %xmm7 /* C */ + psrld $8, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + movdqa %xmm3, 192(%rsi) /* A */ + movdqa %xmm7, 224(%rsi) /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ +/* movdqa %xmm2, 128(%rsi) */ /* A */ +/* movdqa %xmm6, 160(%rsi) */ /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ +/* movdqa 16(%rsi), %xmm8 */ /* B */ +/* movdqa 48(%rsi), %xmm10 */ /* D */ + paddd 384(%rdi), %xmm8 /* B */ + paddd 432(%rdi), %xmm10 /* D */ + pslld $25, %xmm12 /* A */ + psrld $7, %xmm14 /* A */ + pslld $25, %xmm13 /* C */ + psrld $7, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ +/* movdqa %xmm12, 64(%rsi) */ /* A */ +/* movdqa %xmm13, 96(%rsi) */ /* C */ +/* round 2 (B and D) */ +/* movdqa 80(%rsi), %xmm1 */ /* B */ +/* movdqa 112(%rsi), %xmm5 */ /* D */ + movdqa 208(%rsi), %xmm3 /* B */ + movdqa 240(%rsi), %xmm7 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa 144(%rsi), %xmm9 */ /* B */ +/* movdqa 176(%rsi), %xmm11 */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + paddd 192(%rdi), %xmm8 /* B */ + paddd 400(%rdi), %xmm10 /* D */ + pslld $16, %xmm3 /* B */ + psrld $16, %xmm14 /* B */ + pslld $16, %xmm7 /* D */ + psrld $16, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ + pslld $20, %xmm1 /* B */ + psrld $12, %xmm14 /* B */ + pslld $20, %xmm5 /* D */ + psrld $12, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa %xmm8, 16(%rsi) */ /* B */ +/* movdqa %xmm10, 48(%rsi) */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + pslld $24, %xmm3 /* B */ + psrld $8, %xmm14 /* B */ + pslld $24, %xmm7 /* D */ + psrld $8, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ +/* movdqa %xmm3, 208(%rsi) */ /* B */ +/* movdqa %xmm7, 240(%rsi) */ /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ +/* movdqa %xmm9, 144(%rsi) */ /* B */ +/* movdqa %xmm11, 176(%rsi) */ /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ +/* movdqa 0(%rsi), %xmm0 */ /* E */ +/* movdqa 32(%rsi), %xmm4 */ /* G */ + paddd 352(%rdi), %xmm0 /* E */ + paddd 304(%rdi), %xmm4 /* G */ + pslld $25, %xmm1 /* B */ + psrld $7, %xmm14 /* B */ + pslld $25, %xmm5 /* D */ + psrld $7, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ +/* movdqa %xmm1, 80(%rsi) */ /* B */ +/* movdqa %xmm5, 112(%rsi) */ /* D */ +/* round 2 (E and G) */ +/* movdqa 80(%rsi), %xmm1 */ /* E */ +/* movdqa 112(%rsi), %xmm5 */ /* G */ +/* movdqa 240(%rsi), %xmm7 */ /* E */ +/* movdqa 208(%rsi), %xmm3 */ /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa 160(%rsi), %xmm6 */ /* E */ +/* movdqa 128(%rsi), %xmm2 */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + paddd 416(%rdi), %xmm0 /* E */ + paddd 208(%rdi), %xmm4 /* G */ + pslld $16, %xmm7 /* E */ + psrld $16, %xmm14 /* E */ + pslld $16, %xmm3 /* G */ + psrld $16, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ + pslld $20, %xmm1 /* E */ + psrld $12, %xmm14 /* E */ + pslld $20, %xmm5 /* G */ + psrld $12, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa %xmm0, 0(%rsi) */ /* E */ +/* movdqa %xmm4, 32(%rsi) */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + pslld $24, %xmm7 /* E */ + psrld $8, %xmm14 /* E */ + pslld $24, %xmm3 /* G */ + psrld $8, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + movdqa %xmm7, 240(%rsi) /* E */ + movdqa %xmm3, 208(%rsi) /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ +/* movdqa %xmm6, 160(%rsi) */ /* E */ +/* movdqa %xmm2, 128(%rsi) */ /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ +/* movdqa 16(%rsi), %xmm8 */ /* F */ +/* movdqa 48(%rsi), %xmm10 */ /* H */ + paddd 240(%rdi), %xmm8 /* F */ + paddd 336(%rdi), %xmm10 /* H */ + pslld $25, %xmm1 /* E */ + psrld $7, %xmm14 /* E */ + pslld $25, %xmm5 /* G */ + psrld $7, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ +/* movdqa %xmm1, 80(%rsi) */ /* E */ +/* movdqa %xmm5, 112(%rsi) */ /* G */ +/* round 2 (F and H) */ +/* movdqa 96(%rsi), %xmm13 */ /* F */ +/* movdqa 64(%rsi), %xmm12 */ /* H */ + movdqa 192(%rsi), %xmm3 /* F */ + movdqa 224(%rsi), %xmm7 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa 176(%rsi), %xmm11 */ /* F */ +/* movdqa 144(%rsi), %xmm9 */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + paddd 288(%rdi), %xmm8 /* F */ + paddd 256(%rdi), %xmm10 /* H */ + pslld $16, %xmm3 /* F */ + psrld $16, %xmm14 /* F */ + pslld $16, %xmm7 /* H */ + psrld $16, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + pslld $20, %xmm13 /* F */ + psrld $12, %xmm14 /* F */ + pslld $20, %xmm12 /* H */ + psrld $12, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa %xmm8, 16(%rsi) */ /* F */ +/* movdqa %xmm10, 48(%rsi) */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + pslld $24, %xmm3 /* F */ + psrld $8, %xmm14 /* F */ + pslld $24, %xmm7 /* H */ + psrld $8, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ +/* movdqa %xmm3, 192(%rsi) */ /* F */ +/* movdqa %xmm7, 224(%rsi) */ /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ +/* movdqa %xmm11, 176(%rsi) */ /* F */ +/* movdqa %xmm9, 144(%rsi) */ /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ +/* movdqa 0(%rsi), %xmm0 */ /* A */ +/* movdqa 32(%rsi), %xmm4 */ /* C */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + paddd 304(%rdi), %xmm0 /* A */ + paddd 400(%rdi), %xmm4 /* C */ + pslld $25, %xmm13 /* F */ + psrld $7, %xmm14 /* F */ + pslld $25, %xmm12 /* H */ + psrld $7, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ +/* movdqa %xmm13, 96(%rsi) */ /* F */ +/* movdqa %xmm12, 64(%rsi) */ /* H */ +/* round 3 (A and C) */ +/* movdqa 64(%rsi), %xmm12 */ /* A */ +/* movdqa 96(%rsi), %xmm13 */ /* C */ +/* movdqa 192(%rsi), %xmm3 */ /* A */ +/* movdqa 224(%rsi), %xmm7 */ /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa 128(%rsi), %xmm2 */ /* A */ +/* movdqa 160(%rsi), %xmm6 */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + paddd 336(%rdi), %xmm0 /* A */ + paddd 384(%rdi), %xmm4 /* C */ + pslld $16, %xmm3 /* A */ + psrld $16, %xmm14 /* A */ + pslld $16, %xmm7 /* C */ + psrld $16, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ + pslld $20, %xmm12 /* A */ + psrld $12, %xmm14 /* A */ + pslld $20, %xmm13 /* C */ + psrld $12, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa %xmm0, 0(%rsi) */ /* A */ +/* movdqa %xmm4, 32(%rsi) */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + pslld $24, %xmm3 /* A */ + psrld $8, %xmm14 /* A */ + pslld $24, %xmm7 /* C */ + psrld $8, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + movdqa %xmm3, 192(%rsi) /* A */ + movdqa %xmm7, 224(%rsi) /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ +/* movdqa %xmm2, 128(%rsi) */ /* A */ +/* movdqa %xmm6, 160(%rsi) */ /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ +/* movdqa 16(%rsi), %xmm8 */ /* B */ +/* movdqa 48(%rsi), %xmm10 */ /* D */ + paddd 240(%rdi), %xmm8 /* B */ + paddd 368(%rdi), %xmm10 /* D */ + pslld $25, %xmm12 /* A */ + psrld $7, %xmm14 /* A */ + pslld $25, %xmm13 /* C */ + psrld $7, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ +/* movdqa %xmm12, 64(%rsi) */ /* A */ +/* movdqa %xmm13, 96(%rsi) */ /* C */ +/* round 3 (B and D) */ +/* movdqa 80(%rsi), %xmm1 */ /* B */ +/* movdqa 112(%rsi), %xmm5 */ /* D */ + movdqa 208(%rsi), %xmm3 /* B */ + movdqa 240(%rsi), %xmm7 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa 144(%rsi), %xmm9 */ /* B */ +/* movdqa 176(%rsi), %xmm11 */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + paddd 208(%rdi), %xmm8 /* B */ + paddd 416(%rdi), %xmm10 /* D */ + pslld $16, %xmm3 /* B */ + psrld $16, %xmm14 /* B */ + pslld $16, %xmm7 /* D */ + psrld $16, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ + pslld $20, %xmm1 /* B */ + psrld $12, %xmm14 /* B */ + pslld $20, %xmm5 /* D */ + psrld $12, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa %xmm8, 16(%rsi) */ /* B */ +/* movdqa %xmm10, 48(%rsi) */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + pslld $24, %xmm3 /* B */ + psrld $8, %xmm14 /* B */ + pslld $24, %xmm7 /* D */ + psrld $8, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ +/* movdqa %xmm3, 208(%rsi) */ /* B */ +/* movdqa %xmm7, 240(%rsi) */ /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ +/* movdqa %xmm9, 144(%rsi) */ /* B */ +/* movdqa %xmm11, 176(%rsi) */ /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ +/* movdqa 0(%rsi), %xmm0 */ /* E */ +/* movdqa 32(%rsi), %xmm4 */ /* G */ + paddd 224(%rdi), %xmm0 /* E */ + paddd 256(%rdi), %xmm4 /* G */ + pslld $25, %xmm1 /* B */ + psrld $7, %xmm14 /* B */ + pslld $25, %xmm5 /* D */ + psrld $7, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ +/* movdqa %xmm1, 80(%rsi) */ /* B */ +/* movdqa %xmm5, 112(%rsi) */ /* D */ +/* round 3 (E and G) */ +/* movdqa 80(%rsi), %xmm1 */ /* E */ +/* movdqa 112(%rsi), %xmm5 */ /* G */ +/* movdqa 240(%rsi), %xmm7 */ /* E */ +/* movdqa 208(%rsi), %xmm3 */ /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa 160(%rsi), %xmm6 */ /* E */ +/* movdqa 128(%rsi), %xmm2 */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + paddd 288(%rdi), %xmm0 /* E */ + paddd 192(%rdi), %xmm4 /* G */ + pslld $16, %xmm7 /* E */ + psrld $16, %xmm14 /* E */ + pslld $16, %xmm3 /* G */ + psrld $16, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ + pslld $20, %xmm1 /* E */ + psrld $12, %xmm14 /* E */ + pslld $20, %xmm5 /* G */ + psrld $12, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa %xmm0, 0(%rsi) */ /* E */ +/* movdqa %xmm4, 32(%rsi) */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + pslld $24, %xmm7 /* E */ + psrld $8, %xmm14 /* E */ + pslld $24, %xmm3 /* G */ + psrld $8, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + movdqa %xmm7, 240(%rsi) /* E */ + movdqa %xmm3, 208(%rsi) /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ +/* movdqa %xmm6, 160(%rsi) */ /* E */ +/* movdqa %xmm2, 128(%rsi) */ /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ +/* movdqa 16(%rsi), %xmm8 */ /* F */ +/* movdqa 48(%rsi), %xmm10 */ /* H */ + paddd 272(%rdi), %xmm8 /* F */ + paddd 432(%rdi), %xmm10 /* H */ + pslld $25, %xmm1 /* E */ + psrld $7, %xmm14 /* E */ + pslld $25, %xmm5 /* G */ + psrld $7, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ +/* movdqa %xmm1, 80(%rsi) */ /* E */ +/* movdqa %xmm5, 112(%rsi) */ /* G */ +/* round 3 (F and H) */ +/* movdqa 96(%rsi), %xmm13 */ /* F */ +/* movdqa 64(%rsi), %xmm12 */ /* H */ + movdqa 192(%rsi), %xmm3 /* F */ + movdqa 224(%rsi), %xmm7 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa 176(%rsi), %xmm11 */ /* F */ +/* movdqa 144(%rsi), %xmm9 */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + paddd 352(%rdi), %xmm8 /* F */ + paddd 320(%rdi), %xmm10 /* H */ + pslld $16, %xmm3 /* F */ + psrld $16, %xmm14 /* F */ + pslld $16, %xmm7 /* H */ + psrld $16, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + pslld $20, %xmm13 /* F */ + psrld $12, %xmm14 /* F */ + pslld $20, %xmm12 /* H */ + psrld $12, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa %xmm8, 16(%rsi) */ /* F */ +/* movdqa %xmm10, 48(%rsi) */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + pslld $24, %xmm3 /* F */ + psrld $8, %xmm14 /* F */ + pslld $24, %xmm7 /* H */ + psrld $8, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ +/* movdqa %xmm3, 192(%rsi) */ /* F */ +/* movdqa %xmm7, 224(%rsi) */ /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ +/* movdqa %xmm11, 176(%rsi) */ /* F */ +/* movdqa %xmm9, 144(%rsi) */ /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ +/* movdqa 0(%rsi), %xmm0 */ /* A */ +/* movdqa 32(%rsi), %xmm4 */ /* C */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + paddd 336(%rdi), %xmm0 /* A */ + paddd 224(%rdi), %xmm4 /* C */ + pslld $25, %xmm13 /* F */ + psrld $7, %xmm14 /* F */ + pslld $25, %xmm12 /* H */ + psrld $7, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ +/* movdqa %xmm13, 96(%rsi) */ /* F */ +/* movdqa %xmm12, 64(%rsi) */ /* H */ +/* round 4 (A and C) */ +/* movdqa 64(%rsi), %xmm12 */ /* A */ +/* movdqa 96(%rsi), %xmm13 */ /* C */ +/* movdqa 192(%rsi), %xmm3 */ /* A */ +/* movdqa 224(%rsi), %xmm7 */ /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa 128(%rsi), %xmm2 */ /* A */ +/* movdqa 160(%rsi), %xmm6 */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + paddd 192(%rdi), %xmm0 /* A */ + paddd 256(%rdi), %xmm4 /* C */ + pslld $16, %xmm3 /* A */ + psrld $16, %xmm14 /* A */ + pslld $16, %xmm7 /* C */ + psrld $16, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ + pslld $20, %xmm12 /* A */ + psrld $12, %xmm14 /* A */ + pslld $20, %xmm13 /* C */ + psrld $12, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa %xmm0, 0(%rsi) */ /* A */ +/* movdqa %xmm4, 32(%rsi) */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + pslld $24, %xmm3 /* A */ + psrld $8, %xmm14 /* A */ + pslld $24, %xmm7 /* C */ + psrld $8, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + movdqa %xmm3, 192(%rsi) /* A */ + movdqa %xmm7, 224(%rsi) /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ +/* movdqa %xmm2, 128(%rsi) */ /* A */ +/* movdqa %xmm6, 160(%rsi) */ /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ +/* movdqa 16(%rsi), %xmm8 */ /* B */ +/* movdqa 48(%rsi), %xmm10 */ /* D */ + paddd 272(%rdi), %xmm8 /* B */ + paddd 352(%rdi), %xmm10 /* D */ + pslld $25, %xmm12 /* A */ + psrld $7, %xmm14 /* A */ + pslld $25, %xmm13 /* C */ + psrld $7, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ +/* movdqa %xmm12, 64(%rsi) */ /* A */ +/* movdqa %xmm13, 96(%rsi) */ /* C */ +/* round 4 (B and D) */ +/* movdqa 80(%rsi), %xmm1 */ /* B */ +/* movdqa 112(%rsi), %xmm5 */ /* D */ + movdqa 208(%rsi), %xmm3 /* B */ + movdqa 240(%rsi), %xmm7 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa 144(%rsi), %xmm9 */ /* B */ +/* movdqa 176(%rsi), %xmm11 */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + paddd 304(%rdi), %xmm8 /* B */ + paddd 432(%rdi), %xmm10 /* D */ + pslld $16, %xmm3 /* B */ + psrld $16, %xmm14 /* B */ + pslld $16, %xmm7 /* D */ + psrld $16, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ + pslld $20, %xmm1 /* B */ + psrld $12, %xmm14 /* B */ + pslld $20, %xmm5 /* D */ + psrld $12, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa %xmm8, 16(%rsi) */ /* B */ +/* movdqa %xmm10, 48(%rsi) */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + pslld $24, %xmm3 /* B */ + psrld $8, %xmm14 /* B */ + pslld $24, %xmm7 /* D */ + psrld $8, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ +/* movdqa %xmm3, 208(%rsi) */ /* B */ +/* movdqa %xmm7, 240(%rsi) */ /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ +/* movdqa %xmm9, 144(%rsi) */ /* B */ +/* movdqa %xmm11, 176(%rsi) */ /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ +/* movdqa 0(%rsi), %xmm0 */ /* E */ +/* movdqa 32(%rsi), %xmm4 */ /* G */ + paddd 416(%rdi), %xmm0 /* E */ + paddd 288(%rdi), %xmm4 /* G */ + pslld $25, %xmm1 /* B */ + psrld $7, %xmm14 /* B */ + pslld $25, %xmm5 /* D */ + psrld $7, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ +/* movdqa %xmm1, 80(%rsi) */ /* B */ +/* movdqa %xmm5, 112(%rsi) */ /* D */ +/* round 4 (E and G) */ +/* movdqa 80(%rsi), %xmm1 */ /* E */ +/* movdqa 112(%rsi), %xmm5 */ /* G */ +/* movdqa 240(%rsi), %xmm7 */ /* E */ +/* movdqa 208(%rsi), %xmm3 */ /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa 160(%rsi), %xmm6 */ /* E */ +/* movdqa 128(%rsi), %xmm2 */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + paddd 208(%rdi), %xmm0 /* E */ + paddd 320(%rdi), %xmm4 /* G */ + pslld $16, %xmm7 /* E */ + psrld $16, %xmm14 /* E */ + pslld $16, %xmm3 /* G */ + psrld $16, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ + pslld $20, %xmm1 /* E */ + psrld $12, %xmm14 /* E */ + pslld $20, %xmm5 /* G */ + psrld $12, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa %xmm0, 0(%rsi) */ /* E */ +/* movdqa %xmm4, 32(%rsi) */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + pslld $24, %xmm7 /* E */ + psrld $8, %xmm14 /* E */ + pslld $24, %xmm3 /* G */ + psrld $8, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + movdqa %xmm7, 240(%rsi) /* E */ + movdqa %xmm3, 208(%rsi) /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ +/* movdqa %xmm6, 160(%rsi) */ /* E */ +/* movdqa %xmm2, 128(%rsi) */ /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ +/* movdqa 16(%rsi), %xmm8 */ /* F */ +/* movdqa 48(%rsi), %xmm10 */ /* H */ + paddd 368(%rdi), %xmm8 /* F */ + paddd 240(%rdi), %xmm10 /* H */ + pslld $25, %xmm1 /* E */ + psrld $7, %xmm14 /* E */ + pslld $25, %xmm5 /* G */ + psrld $7, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ +/* movdqa %xmm1, 80(%rsi) */ /* E */ +/* movdqa %xmm5, 112(%rsi) */ /* G */ +/* round 4 (F and H) */ +/* movdqa 96(%rsi), %xmm13 */ /* F */ +/* movdqa 64(%rsi), %xmm12 */ /* H */ + movdqa 192(%rsi), %xmm3 /* F */ + movdqa 224(%rsi), %xmm7 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa 176(%rsi), %xmm11 */ /* F */ +/* movdqa 144(%rsi), %xmm9 */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + paddd 384(%rdi), %xmm8 /* F */ + paddd 400(%rdi), %xmm10 /* H */ + pslld $16, %xmm3 /* F */ + psrld $16, %xmm14 /* F */ + pslld $16, %xmm7 /* H */ + psrld $16, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + pslld $20, %xmm13 /* F */ + psrld $12, %xmm14 /* F */ + pslld $20, %xmm12 /* H */ + psrld $12, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa %xmm8, 16(%rsi) */ /* F */ +/* movdqa %xmm10, 48(%rsi) */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + pslld $24, %xmm3 /* F */ + psrld $8, %xmm14 /* F */ + pslld $24, %xmm7 /* H */ + psrld $8, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ +/* movdqa %xmm3, 192(%rsi) */ /* F */ +/* movdqa %xmm7, 224(%rsi) */ /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ +/* movdqa %xmm11, 176(%rsi) */ /* F */ +/* movdqa %xmm9, 144(%rsi) */ /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ +/* movdqa 0(%rsi), %xmm0 */ /* A */ +/* movdqa 32(%rsi), %xmm4 */ /* C */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + paddd 224(%rdi), %xmm0 /* A */ + paddd 192(%rdi), %xmm4 /* C */ + pslld $25, %xmm13 /* F */ + psrld $7, %xmm14 /* F */ + pslld $25, %xmm12 /* H */ + psrld $7, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ +/* movdqa %xmm13, 96(%rsi) */ /* F */ +/* movdqa %xmm12, 64(%rsi) */ /* H */ +/* round 5 (A and C) */ +/* movdqa 64(%rsi), %xmm12 */ /* A */ +/* movdqa 96(%rsi), %xmm13 */ /* C */ +/* movdqa 192(%rsi), %xmm3 */ /* A */ +/* movdqa 224(%rsi), %xmm7 */ /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa 128(%rsi), %xmm2 */ /* A */ +/* movdqa 160(%rsi), %xmm6 */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + paddd 384(%rdi), %xmm0 /* A */ + paddd 368(%rdi), %xmm4 /* C */ + pslld $16, %xmm3 /* A */ + psrld $16, %xmm14 /* A */ + pslld $16, %xmm7 /* C */ + psrld $16, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ + pslld $20, %xmm12 /* A */ + psrld $12, %xmm14 /* A */ + pslld $20, %xmm13 /* C */ + psrld $12, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa %xmm0, 0(%rsi) */ /* A */ +/* movdqa %xmm4, 32(%rsi) */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + pslld $24, %xmm3 /* A */ + psrld $8, %xmm14 /* A */ + pslld $24, %xmm7 /* C */ + psrld $8, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + movdqa %xmm3, 192(%rsi) /* A */ + movdqa %xmm7, 224(%rsi) /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ +/* movdqa %xmm2, 128(%rsi) */ /* A */ +/* movdqa %xmm6, 160(%rsi) */ /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ +/* movdqa 16(%rsi), %xmm8 */ /* B */ +/* movdqa 48(%rsi), %xmm10 */ /* D */ + paddd 288(%rdi), %xmm8 /* B */ + paddd 320(%rdi), %xmm10 /* D */ + pslld $25, %xmm12 /* A */ + psrld $7, %xmm14 /* A */ + pslld $25, %xmm13 /* C */ + psrld $7, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ +/* movdqa %xmm12, 64(%rsi) */ /* A */ +/* movdqa %xmm13, 96(%rsi) */ /* C */ +/* round 5 (B and D) */ +/* movdqa 80(%rsi), %xmm1 */ /* B */ +/* movdqa 112(%rsi), %xmm5 */ /* D */ + movdqa 208(%rsi), %xmm3 /* B */ + movdqa 240(%rsi), %xmm7 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa 144(%rsi), %xmm9 */ /* B */ +/* movdqa 176(%rsi), %xmm11 */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + paddd 352(%rdi), %xmm8 /* B */ + paddd 240(%rdi), %xmm10 /* D */ + pslld $16, %xmm3 /* B */ + psrld $16, %xmm14 /* B */ + pslld $16, %xmm7 /* D */ + psrld $16, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ + pslld $20, %xmm1 /* B */ + psrld $12, %xmm14 /* B */ + pslld $20, %xmm5 /* D */ + psrld $12, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa %xmm8, 16(%rsi) */ /* B */ +/* movdqa %xmm10, 48(%rsi) */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + pslld $24, %xmm3 /* B */ + psrld $8, %xmm14 /* B */ + pslld $24, %xmm7 /* D */ + psrld $8, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ +/* movdqa %xmm3, 208(%rsi) */ /* B */ +/* movdqa %xmm7, 240(%rsi) */ /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ +/* movdqa %xmm9, 144(%rsi) */ /* B */ +/* movdqa %xmm11, 176(%rsi) */ /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ +/* movdqa 0(%rsi), %xmm0 */ /* E */ +/* movdqa 32(%rsi), %xmm4 */ /* G */ + paddd 256(%rdi), %xmm0 /* E */ + paddd 432(%rdi), %xmm4 /* G */ + pslld $25, %xmm1 /* B */ + psrld $7, %xmm14 /* B */ + pslld $25, %xmm5 /* D */ + psrld $7, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ +/* movdqa %xmm1, 80(%rsi) */ /* B */ +/* movdqa %xmm5, 112(%rsi) */ /* D */ +/* round 5 (E and G) */ +/* movdqa 80(%rsi), %xmm1 */ /* E */ +/* movdqa 112(%rsi), %xmm5 */ /* G */ +/* movdqa 240(%rsi), %xmm7 */ /* E */ +/* movdqa 208(%rsi), %xmm3 */ /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa 160(%rsi), %xmm6 */ /* E */ +/* movdqa 128(%rsi), %xmm2 */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + paddd 400(%rdi), %xmm0 /* E */ + paddd 416(%rdi), %xmm4 /* G */ + pslld $16, %xmm7 /* E */ + psrld $16, %xmm14 /* E */ + pslld $16, %xmm3 /* G */ + psrld $16, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ + pslld $20, %xmm1 /* E */ + psrld $12, %xmm14 /* E */ + pslld $20, %xmm5 /* G */ + psrld $12, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa %xmm0, 0(%rsi) */ /* E */ +/* movdqa %xmm4, 32(%rsi) */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + pslld $24, %xmm7 /* E */ + psrld $8, %xmm14 /* E */ + pslld $24, %xmm3 /* G */ + psrld $8, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + movdqa %xmm7, 240(%rsi) /* E */ + movdqa %xmm3, 208(%rsi) /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ +/* movdqa %xmm6, 160(%rsi) */ /* E */ +/* movdqa %xmm2, 128(%rsi) */ /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ +/* movdqa 16(%rsi), %xmm8 */ /* F */ +/* movdqa 48(%rsi), %xmm10 */ /* H */ + paddd 304(%rdi), %xmm8 /* F */ + paddd 208(%rdi), %xmm10 /* H */ + pslld $25, %xmm1 /* E */ + psrld $7, %xmm14 /* E */ + pslld $25, %xmm5 /* G */ + psrld $7, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ +/* movdqa %xmm1, 80(%rsi) */ /* E */ +/* movdqa %xmm5, 112(%rsi) */ /* G */ +/* round 5 (F and H) */ +/* movdqa 96(%rsi), %xmm13 */ /* F */ +/* movdqa 64(%rsi), %xmm12 */ /* H */ + movdqa 192(%rsi), %xmm3 /* F */ + movdqa 224(%rsi), %xmm7 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa 176(%rsi), %xmm11 */ /* F */ +/* movdqa 144(%rsi), %xmm9 */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + paddd 272(%rdi), %xmm8 /* F */ + paddd 336(%rdi), %xmm10 /* H */ + pslld $16, %xmm3 /* F */ + psrld $16, %xmm14 /* F */ + pslld $16, %xmm7 /* H */ + psrld $16, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + pslld $20, %xmm13 /* F */ + psrld $12, %xmm14 /* F */ + pslld $20, %xmm12 /* H */ + psrld $12, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa %xmm8, 16(%rsi) */ /* F */ +/* movdqa %xmm10, 48(%rsi) */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + pslld $24, %xmm3 /* F */ + psrld $8, %xmm14 /* F */ + pslld $24, %xmm7 /* H */ + psrld $8, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ +/* movdqa %xmm3, 192(%rsi) */ /* F */ +/* movdqa %xmm7, 224(%rsi) */ /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ +/* movdqa %xmm11, 176(%rsi) */ /* F */ +/* movdqa %xmm9, 144(%rsi) */ /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ +/* movdqa 0(%rsi), %xmm0 */ /* A */ +/* movdqa 32(%rsi), %xmm4 */ /* C */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + paddd 384(%rdi), %xmm0 /* A */ + paddd 416(%rdi), %xmm4 /* C */ + pslld $25, %xmm13 /* F */ + psrld $7, %xmm14 /* F */ + pslld $25, %xmm12 /* H */ + psrld $7, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ +/* movdqa %xmm13, 96(%rsi) */ /* F */ +/* movdqa %xmm12, 64(%rsi) */ /* H */ +/* round 6 (A and C) */ +/* movdqa 64(%rsi), %xmm12 */ /* A */ +/* movdqa 96(%rsi), %xmm13 */ /* C */ +/* movdqa 192(%rsi), %xmm3 */ /* A */ +/* movdqa 224(%rsi), %xmm7 */ /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa 128(%rsi), %xmm2 */ /* A */ +/* movdqa 160(%rsi), %xmm6 */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + paddd 272(%rdi), %xmm0 /* A */ + paddd 400(%rdi), %xmm4 /* C */ + pslld $16, %xmm3 /* A */ + psrld $16, %xmm14 /* A */ + pslld $16, %xmm7 /* C */ + psrld $16, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ + pslld $20, %xmm12 /* A */ + psrld $12, %xmm14 /* A */ + pslld $20, %xmm13 /* C */ + psrld $12, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa %xmm0, 0(%rsi) */ /* A */ +/* movdqa %xmm4, 32(%rsi) */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + pslld $24, %xmm3 /* A */ + psrld $8, %xmm14 /* A */ + pslld $24, %xmm7 /* C */ + psrld $8, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + movdqa %xmm3, 192(%rsi) /* A */ + movdqa %xmm7, 224(%rsi) /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ +/* movdqa %xmm2, 128(%rsi) */ /* A */ +/* movdqa %xmm6, 160(%rsi) */ /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ +/* movdqa 16(%rsi), %xmm8 */ /* B */ +/* movdqa 48(%rsi), %xmm10 */ /* D */ + paddd 208(%rdi), %xmm8 /* B */ + paddd 256(%rdi), %xmm10 /* D */ + pslld $25, %xmm12 /* A */ + psrld $7, %xmm14 /* A */ + pslld $25, %xmm13 /* C */ + psrld $7, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ +/* movdqa %xmm12, 64(%rsi) */ /* A */ +/* movdqa %xmm13, 96(%rsi) */ /* C */ +/* round 6 (B and D) */ +/* movdqa 80(%rsi), %xmm1 */ /* B */ +/* movdqa 112(%rsi), %xmm5 */ /* D */ + movdqa 208(%rsi), %xmm3 /* B */ + movdqa 240(%rsi), %xmm7 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa 144(%rsi), %xmm9 */ /* B */ +/* movdqa 176(%rsi), %xmm11 */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + paddd 432(%rdi), %xmm8 /* B */ + paddd 352(%rdi), %xmm10 /* D */ + pslld $16, %xmm3 /* B */ + psrld $16, %xmm14 /* B */ + pslld $16, %xmm7 /* D */ + psrld $16, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ + pslld $20, %xmm1 /* B */ + psrld $12, %xmm14 /* B */ + pslld $20, %xmm5 /* D */ + psrld $12, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa %xmm8, 16(%rsi) */ /* B */ +/* movdqa %xmm10, 48(%rsi) */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + pslld $24, %xmm3 /* B */ + psrld $8, %xmm14 /* B */ + pslld $24, %xmm7 /* D */ + psrld $8, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ +/* movdqa %xmm3, 208(%rsi) */ /* B */ +/* movdqa %xmm7, 240(%rsi) */ /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ +/* movdqa %xmm9, 144(%rsi) */ /* B */ +/* movdqa %xmm11, 176(%rsi) */ /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ +/* movdqa 0(%rsi), %xmm0 */ /* E */ +/* movdqa 32(%rsi), %xmm4 */ /* G */ + paddd 192(%rdi), %xmm0 /* E */ + paddd 336(%rdi), %xmm4 /* G */ + pslld $25, %xmm1 /* B */ + psrld $7, %xmm14 /* B */ + pslld $25, %xmm5 /* D */ + psrld $7, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ +/* movdqa %xmm1, 80(%rsi) */ /* B */ +/* movdqa %xmm5, 112(%rsi) */ /* D */ +/* round 6 (E and G) */ +/* movdqa 80(%rsi), %xmm1 */ /* E */ +/* movdqa 112(%rsi), %xmm5 */ /* G */ +/* movdqa 240(%rsi), %xmm7 */ /* E */ +/* movdqa 208(%rsi), %xmm3 */ /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa 160(%rsi), %xmm6 */ /* E */ +/* movdqa 128(%rsi), %xmm2 */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + paddd 304(%rdi), %xmm0 /* E */ + paddd 224(%rdi), %xmm4 /* G */ + pslld $16, %xmm7 /* E */ + psrld $16, %xmm14 /* E */ + pslld $16, %xmm3 /* G */ + psrld $16, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ + pslld $20, %xmm1 /* E */ + psrld $12, %xmm14 /* E */ + pslld $20, %xmm5 /* G */ + psrld $12, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa %xmm0, 0(%rsi) */ /* E */ +/* movdqa %xmm4, 32(%rsi) */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + pslld $24, %xmm7 /* E */ + psrld $8, %xmm14 /* E */ + pslld $24, %xmm3 /* G */ + psrld $8, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + movdqa %xmm7, 240(%rsi) /* E */ + movdqa %xmm3, 208(%rsi) /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ +/* movdqa %xmm6, 160(%rsi) */ /* E */ +/* movdqa %xmm2, 128(%rsi) */ /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ +/* movdqa 16(%rsi), %xmm8 */ /* F */ +/* movdqa 48(%rsi), %xmm10 */ /* H */ + paddd 288(%rdi), %xmm8 /* F */ + paddd 320(%rdi), %xmm10 /* H */ + pslld $25, %xmm1 /* E */ + psrld $7, %xmm14 /* E */ + pslld $25, %xmm5 /* G */ + psrld $7, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ +/* movdqa %xmm1, 80(%rsi) */ /* E */ +/* movdqa %xmm5, 112(%rsi) */ /* G */ +/* round 6 (F and H) */ +/* movdqa 96(%rsi), %xmm13 */ /* F */ +/* movdqa 64(%rsi), %xmm12 */ /* H */ + movdqa 192(%rsi), %xmm3 /* F */ + movdqa 224(%rsi), %xmm7 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa 176(%rsi), %xmm11 */ /* F */ +/* movdqa 144(%rsi), %xmm9 */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + paddd 240(%rdi), %xmm8 /* F */ + paddd 368(%rdi), %xmm10 /* H */ + pslld $16, %xmm3 /* F */ + psrld $16, %xmm14 /* F */ + pslld $16, %xmm7 /* H */ + psrld $16, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + pslld $20, %xmm13 /* F */ + psrld $12, %xmm14 /* F */ + pslld $20, %xmm12 /* H */ + psrld $12, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa %xmm8, 16(%rsi) */ /* F */ +/* movdqa %xmm10, 48(%rsi) */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + pslld $24, %xmm3 /* F */ + psrld $8, %xmm14 /* F */ + pslld $24, %xmm7 /* H */ + psrld $8, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ +/* movdqa %xmm3, 192(%rsi) */ /* F */ +/* movdqa %xmm7, 224(%rsi) */ /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ +/* movdqa %xmm11, 176(%rsi) */ /* F */ +/* movdqa %xmm9, 144(%rsi) */ /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ +/* movdqa 0(%rsi), %xmm0 */ /* A */ +/* movdqa 32(%rsi), %xmm4 */ /* C */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + paddd 400(%rdi), %xmm0 /* A */ + paddd 384(%rdi), %xmm4 /* C */ + pslld $25, %xmm13 /* F */ + psrld $7, %xmm14 /* F */ + pslld $25, %xmm12 /* H */ + psrld $7, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ +/* movdqa %xmm13, 96(%rsi) */ /* F */ +/* movdqa %xmm12, 64(%rsi) */ /* H */ +/* round 7 (A and C) */ +/* movdqa 64(%rsi), %xmm12 */ /* A */ +/* movdqa 96(%rsi), %xmm13 */ /* C */ +/* movdqa 192(%rsi), %xmm3 */ /* A */ +/* movdqa 224(%rsi), %xmm7 */ /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa 128(%rsi), %xmm2 */ /* A */ +/* movdqa 160(%rsi), %xmm6 */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + paddd 368(%rdi), %xmm0 /* A */ + paddd 208(%rdi), %xmm4 /* C */ + pslld $16, %xmm3 /* A */ + psrld $16, %xmm14 /* A */ + pslld $16, %xmm7 /* C */ + psrld $16, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ + pslld $20, %xmm12 /* A */ + psrld $12, %xmm14 /* A */ + pslld $20, %xmm13 /* C */ + psrld $12, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa %xmm0, 0(%rsi) */ /* A */ +/* movdqa %xmm4, 32(%rsi) */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + pslld $24, %xmm3 /* A */ + psrld $8, %xmm14 /* A */ + pslld $24, %xmm7 /* C */ + psrld $8, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + movdqa %xmm3, 192(%rsi) /* A */ + movdqa %xmm7, 224(%rsi) /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ +/* movdqa %xmm2, 128(%rsi) */ /* A */ +/* movdqa %xmm6, 160(%rsi) */ /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ +/* movdqa 16(%rsi), %xmm8 */ /* B */ +/* movdqa 48(%rsi), %xmm10 */ /* D */ + paddd 304(%rdi), %xmm8 /* B */ + paddd 240(%rdi), %xmm10 /* D */ + pslld $25, %xmm12 /* A */ + psrld $7, %xmm14 /* A */ + pslld $25, %xmm13 /* C */ + psrld $7, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ +/* movdqa %xmm12, 64(%rsi) */ /* A */ +/* movdqa %xmm13, 96(%rsi) */ /* C */ +/* round 7 (B and D) */ +/* movdqa 80(%rsi), %xmm1 */ /* B */ +/* movdqa 112(%rsi), %xmm5 */ /* D */ + movdqa 208(%rsi), %xmm3 /* B */ + movdqa 240(%rsi), %xmm7 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa 144(%rsi), %xmm9 */ /* B */ +/* movdqa 176(%rsi), %xmm11 */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + paddd 416(%rdi), %xmm8 /* B */ + paddd 336(%rdi), %xmm10 /* D */ + pslld $16, %xmm3 /* B */ + psrld $16, %xmm14 /* B */ + pslld $16, %xmm7 /* D */ + psrld $16, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ + pslld $20, %xmm1 /* B */ + psrld $12, %xmm14 /* B */ + pslld $20, %xmm5 /* D */ + psrld $12, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa %xmm8, 16(%rsi) */ /* B */ +/* movdqa %xmm10, 48(%rsi) */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + pslld $24, %xmm3 /* B */ + psrld $8, %xmm14 /* B */ + pslld $24, %xmm7 /* D */ + psrld $8, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ +/* movdqa %xmm3, 208(%rsi) */ /* B */ +/* movdqa %xmm7, 240(%rsi) */ /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ +/* movdqa %xmm9, 144(%rsi) */ /* B */ +/* movdqa %xmm11, 176(%rsi) */ /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ +/* movdqa 0(%rsi), %xmm0 */ /* E */ +/* movdqa 32(%rsi), %xmm4 */ /* G */ + paddd 272(%rdi), %xmm0 /* E */ + paddd 320(%rdi), %xmm4 /* G */ + pslld $25, %xmm1 /* B */ + psrld $7, %xmm14 /* B */ + pslld $25, %xmm5 /* D */ + psrld $7, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ +/* movdqa %xmm1, 80(%rsi) */ /* B */ +/* movdqa %xmm5, 112(%rsi) */ /* D */ +/* round 7 (E and G) */ +/* movdqa 80(%rsi), %xmm1 */ /* E */ +/* movdqa 112(%rsi), %xmm5 */ /* G */ +/* movdqa 240(%rsi), %xmm7 */ /* E */ +/* movdqa 208(%rsi), %xmm3 */ /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa 160(%rsi), %xmm6 */ /* E */ +/* movdqa 128(%rsi), %xmm2 */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + paddd 192(%rdi), %xmm0 /* E */ + paddd 288(%rdi), %xmm4 /* G */ + pslld $16, %xmm7 /* E */ + psrld $16, %xmm14 /* E */ + pslld $16, %xmm3 /* G */ + psrld $16, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ + pslld $20, %xmm1 /* E */ + psrld $12, %xmm14 /* E */ + pslld $20, %xmm5 /* G */ + psrld $12, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa %xmm0, 0(%rsi) */ /* E */ +/* movdqa %xmm4, 32(%rsi) */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + pslld $24, %xmm7 /* E */ + psrld $8, %xmm14 /* E */ + pslld $24, %xmm3 /* G */ + psrld $8, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + movdqa %xmm7, 240(%rsi) /* E */ + movdqa %xmm3, 208(%rsi) /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ +/* movdqa %xmm6, 160(%rsi) */ /* E */ +/* movdqa %xmm2, 128(%rsi) */ /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ +/* movdqa 16(%rsi), %xmm8 */ /* F */ +/* movdqa 48(%rsi), %xmm10 */ /* H */ + paddd 432(%rdi), %xmm8 /* F */ + paddd 224(%rdi), %xmm10 /* H */ + pslld $25, %xmm1 /* E */ + psrld $7, %xmm14 /* E */ + pslld $25, %xmm5 /* G */ + psrld $7, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ +/* movdqa %xmm1, 80(%rsi) */ /* E */ +/* movdqa %xmm5, 112(%rsi) */ /* G */ +/* round 7 (F and H) */ +/* movdqa 96(%rsi), %xmm13 */ /* F */ +/* movdqa 64(%rsi), %xmm12 */ /* H */ + movdqa 192(%rsi), %xmm3 /* F */ + movdqa 224(%rsi), %xmm7 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa 176(%rsi), %xmm11 */ /* F */ +/* movdqa 144(%rsi), %xmm9 */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + paddd 256(%rdi), %xmm8 /* F */ + paddd 352(%rdi), %xmm10 /* H */ + pslld $16, %xmm3 /* F */ + psrld $16, %xmm14 /* F */ + pslld $16, %xmm7 /* H */ + psrld $16, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + pslld $20, %xmm13 /* F */ + psrld $12, %xmm14 /* F */ + pslld $20, %xmm12 /* H */ + psrld $12, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa %xmm8, 16(%rsi) */ /* F */ +/* movdqa %xmm10, 48(%rsi) */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + pslld $24, %xmm3 /* F */ + psrld $8, %xmm14 /* F */ + pslld $24, %xmm7 /* H */ + psrld $8, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ +/* movdqa %xmm3, 192(%rsi) */ /* F */ +/* movdqa %xmm7, 224(%rsi) */ /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ +/* movdqa %xmm11, 176(%rsi) */ /* F */ +/* movdqa %xmm9, 144(%rsi) */ /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ +/* movdqa 0(%rsi), %xmm0 */ /* A */ +/* movdqa 32(%rsi), %xmm4 */ /* C */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + paddd 288(%rdi), %xmm0 /* A */ + paddd 368(%rdi), %xmm4 /* C */ + pslld $25, %xmm13 /* F */ + psrld $7, %xmm14 /* F */ + pslld $25, %xmm12 /* H */ + psrld $7, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ +/* movdqa %xmm13, 96(%rsi) */ /* F */ +/* movdqa %xmm12, 64(%rsi) */ /* H */ +/* round 8 (A and C) */ +/* movdqa 64(%rsi), %xmm12 */ /* A */ +/* movdqa 96(%rsi), %xmm13 */ /* C */ +/* movdqa 192(%rsi), %xmm3 */ /* A */ +/* movdqa 224(%rsi), %xmm7 */ /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa 128(%rsi), %xmm2 */ /* A */ +/* movdqa 160(%rsi), %xmm6 */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + paddd 432(%rdi), %xmm0 /* A */ + paddd 240(%rdi), %xmm4 /* C */ + pslld $16, %xmm3 /* A */ + psrld $16, %xmm14 /* A */ + pslld $16, %xmm7 /* C */ + psrld $16, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ + pslld $20, %xmm12 /* A */ + psrld $12, %xmm14 /* A */ + pslld $20, %xmm13 /* C */ + psrld $12, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa %xmm0, 0(%rsi) */ /* A */ +/* movdqa %xmm4, 32(%rsi) */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + pslld $24, %xmm3 /* A */ + psrld $8, %xmm14 /* A */ + pslld $24, %xmm7 /* C */ + psrld $8, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + movdqa %xmm3, 192(%rsi) /* A */ + movdqa %xmm7, 224(%rsi) /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ +/* movdqa %xmm2, 128(%rsi) */ /* A */ +/* movdqa %xmm6, 160(%rsi) */ /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ +/* movdqa 16(%rsi), %xmm8 */ /* B */ +/* movdqa 48(%rsi), %xmm10 */ /* D */ + paddd 416(%rdi), %xmm8 /* B */ + paddd 192(%rdi), %xmm10 /* D */ + pslld $25, %xmm12 /* A */ + psrld $7, %xmm14 /* A */ + pslld $25, %xmm13 /* C */ + psrld $7, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ +/* movdqa %xmm12, 64(%rsi) */ /* A */ +/* movdqa %xmm13, 96(%rsi) */ /* C */ +/* round 8 (B and D) */ +/* movdqa 80(%rsi), %xmm1 */ /* B */ +/* movdqa 112(%rsi), %xmm5 */ /* D */ + movdqa 208(%rsi), %xmm3 /* B */ + movdqa 240(%rsi), %xmm7 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa 144(%rsi), %xmm9 */ /* B */ +/* movdqa 176(%rsi), %xmm11 */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + paddd 336(%rdi), %xmm8 /* B */ + paddd 320(%rdi), %xmm10 /* D */ + pslld $16, %xmm3 /* B */ + psrld $16, %xmm14 /* B */ + pslld $16, %xmm7 /* D */ + psrld $16, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ + pslld $20, %xmm1 /* B */ + psrld $12, %xmm14 /* B */ + pslld $20, %xmm5 /* D */ + psrld $12, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa %xmm8, 16(%rsi) */ /* B */ +/* movdqa %xmm10, 48(%rsi) */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + pslld $24, %xmm3 /* B */ + psrld $8, %xmm14 /* B */ + pslld $24, %xmm7 /* D */ + psrld $8, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ +/* movdqa %xmm3, 208(%rsi) */ /* B */ +/* movdqa %xmm7, 240(%rsi) */ /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ +/* movdqa %xmm9, 144(%rsi) */ /* B */ +/* movdqa %xmm11, 176(%rsi) */ /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ +/* movdqa 0(%rsi), %xmm0 */ /* E */ +/* movdqa 32(%rsi), %xmm4 */ /* G */ + paddd 384(%rdi), %xmm0 /* E */ + paddd 208(%rdi), %xmm4 /* G */ + pslld $25, %xmm1 /* B */ + psrld $7, %xmm14 /* B */ + pslld $25, %xmm5 /* D */ + psrld $7, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ +/* movdqa %xmm1, 80(%rsi) */ /* B */ +/* movdqa %xmm5, 112(%rsi) */ /* D */ +/* round 8 (E and G) */ +/* movdqa 80(%rsi), %xmm1 */ /* E */ +/* movdqa 112(%rsi), %xmm5 */ /* G */ +/* movdqa 240(%rsi), %xmm7 */ /* E */ +/* movdqa 208(%rsi), %xmm3 */ /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa 160(%rsi), %xmm6 */ /* E */ +/* movdqa 128(%rsi), %xmm2 */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + paddd 224(%rdi), %xmm0 /* E */ + paddd 256(%rdi), %xmm4 /* G */ + pslld $16, %xmm7 /* E */ + psrld $16, %xmm14 /* E */ + pslld $16, %xmm3 /* G */ + psrld $16, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ + pslld $20, %xmm1 /* E */ + psrld $12, %xmm14 /* E */ + pslld $20, %xmm5 /* G */ + psrld $12, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa %xmm0, 0(%rsi) */ /* E */ +/* movdqa %xmm4, 32(%rsi) */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + pslld $24, %xmm7 /* E */ + psrld $8, %xmm14 /* E */ + pslld $24, %xmm3 /* G */ + psrld $8, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + movdqa %xmm7, 240(%rsi) /* E */ + movdqa %xmm3, 208(%rsi) /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ +/* movdqa %xmm6, 160(%rsi) */ /* E */ +/* movdqa %xmm2, 128(%rsi) */ /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ +/* movdqa 16(%rsi), %xmm8 */ /* F */ +/* movdqa 48(%rsi), %xmm10 */ /* H */ + paddd 400(%rdi), %xmm8 /* F */ + paddd 352(%rdi), %xmm10 /* H */ + pslld $25, %xmm1 /* E */ + psrld $7, %xmm14 /* E */ + pslld $25, %xmm5 /* G */ + psrld $7, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ +/* movdqa %xmm1, 80(%rsi) */ /* E */ +/* movdqa %xmm5, 112(%rsi) */ /* G */ +/* round 8 (F and H) */ +/* movdqa 96(%rsi), %xmm13 */ /* F */ +/* movdqa 64(%rsi), %xmm12 */ /* H */ + movdqa 192(%rsi), %xmm3 /* F */ + movdqa 224(%rsi), %xmm7 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa 176(%rsi), %xmm11 */ /* F */ +/* movdqa 144(%rsi), %xmm9 */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + paddd 304(%rdi), %xmm8 /* F */ + paddd 272(%rdi), %xmm10 /* H */ + pslld $16, %xmm3 /* F */ + psrld $16, %xmm14 /* F */ + pslld $16, %xmm7 /* H */ + psrld $16, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + pslld $20, %xmm13 /* F */ + psrld $12, %xmm14 /* F */ + pslld $20, %xmm12 /* H */ + psrld $12, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa %xmm8, 16(%rsi) */ /* F */ +/* movdqa %xmm10, 48(%rsi) */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + pslld $24, %xmm3 /* F */ + psrld $8, %xmm14 /* F */ + pslld $24, %xmm7 /* H */ + psrld $8, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ +/* movdqa %xmm3, 192(%rsi) */ /* F */ +/* movdqa %xmm7, 224(%rsi) */ /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ +/* movdqa %xmm11, 176(%rsi) */ /* F */ +/* movdqa %xmm9, 144(%rsi) */ /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ +/* movdqa 0(%rsi), %xmm0 */ /* A */ +/* movdqa 32(%rsi), %xmm4 */ /* C */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + paddd 352(%rdi), %xmm0 /* A */ + paddd 304(%rdi), %xmm4 /* C */ + pslld $25, %xmm13 /* F */ + psrld $7, %xmm14 /* F */ + pslld $25, %xmm12 /* H */ + psrld $7, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ +/* movdqa %xmm13, 96(%rsi) */ /* F */ +/* movdqa %xmm12, 64(%rsi) */ /* H */ +/* round 9 (A and C) */ +/* movdqa 64(%rsi), %xmm12 */ /* A */ +/* movdqa 96(%rsi), %xmm13 */ /* C */ +/* movdqa 192(%rsi), %xmm3 */ /* A */ +/* movdqa 224(%rsi), %xmm7 */ /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa 128(%rsi), %xmm2 */ /* A */ +/* movdqa 160(%rsi), %xmm6 */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + paddd 224(%rdi), %xmm0 /* A */ + paddd 288(%rdi), %xmm4 /* C */ + pslld $16, %xmm3 /* A */ + psrld $16, %xmm14 /* A */ + pslld $16, %xmm7 /* C */ + psrld $16, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ + pslld $20, %xmm12 /* A */ + psrld $12, %xmm14 /* A */ + pslld $20, %xmm13 /* C */ + psrld $12, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ + paddd %xmm12, %xmm0 /* A */ + paddd %xmm13, %xmm4 /* C */ +/* movdqa %xmm0, 0(%rsi) */ /* A */ +/* movdqa %xmm4, 32(%rsi) */ /* C */ + pxor %xmm0, %xmm3 /* A */ + pxor %xmm4, %xmm7 /* C */ + movdqa %xmm3, %xmm14 /* A */ + movdqa %xmm7, %xmm15 /* C */ + pslld $24, %xmm3 /* A */ + psrld $8, %xmm14 /* A */ + pslld $24, %xmm7 /* C */ + psrld $8, %xmm15 /* C */ + por %xmm14, %xmm3 /* A */ + por %xmm15, %xmm7 /* C */ + movdqa %xmm3, 192(%rsi) /* A */ + movdqa %xmm7, 224(%rsi) /* C */ + paddd %xmm3, %xmm2 /* A */ + paddd %xmm7, %xmm6 /* C */ +/* movdqa %xmm2, 128(%rsi) */ /* A */ +/* movdqa %xmm6, 160(%rsi) */ /* C */ + pxor %xmm2, %xmm12 /* A */ + pxor %xmm6, %xmm13 /* C */ + movdqa %xmm12, %xmm14 /* A */ + movdqa %xmm13, %xmm15 /* C */ +/* movdqa 16(%rsi), %xmm8 */ /* B */ +/* movdqa 48(%rsi), %xmm10 */ /* D */ + paddd 320(%rdi), %xmm8 /* B */ + paddd 208(%rdi), %xmm10 /* D */ + pslld $25, %xmm12 /* A */ + psrld $7, %xmm14 /* A */ + pslld $25, %xmm13 /* C */ + psrld $7, %xmm15 /* C */ + por %xmm14, %xmm12 /* A */ + por %xmm15, %xmm13 /* C */ +/* movdqa %xmm12, 64(%rsi) */ /* A */ +/* movdqa %xmm13, 96(%rsi) */ /* C */ +/* round 9 (B and D) */ +/* movdqa 80(%rsi), %xmm1 */ /* B */ +/* movdqa 112(%rsi), %xmm5 */ /* D */ + movdqa 208(%rsi), %xmm3 /* B */ + movdqa 240(%rsi), %xmm7 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa 144(%rsi), %xmm9 */ /* B */ +/* movdqa 176(%rsi), %xmm11 */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + paddd 256(%rdi), %xmm8 /* B */ + paddd 272(%rdi), %xmm10 /* D */ + pslld $16, %xmm3 /* B */ + psrld $16, %xmm14 /* B */ + pslld $16, %xmm7 /* D */ + psrld $16, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ + pslld $20, %xmm1 /* B */ + psrld $12, %xmm14 /* B */ + pslld $20, %xmm5 /* D */ + psrld $12, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ + paddd %xmm1, %xmm8 /* B */ + paddd %xmm5, %xmm10 /* D */ +/* movdqa %xmm8, 16(%rsi) */ /* B */ +/* movdqa %xmm10, 48(%rsi) */ /* D */ + pxor %xmm8, %xmm3 /* B */ + pxor %xmm10, %xmm7 /* D */ + movdqa %xmm3, %xmm14 /* B */ + movdqa %xmm7, %xmm15 /* D */ + pslld $24, %xmm3 /* B */ + psrld $8, %xmm14 /* B */ + pslld $24, %xmm7 /* D */ + psrld $8, %xmm15 /* D */ + por %xmm14, %xmm3 /* B */ + por %xmm15, %xmm7 /* D */ +/* movdqa %xmm3, 208(%rsi) */ /* B */ +/* movdqa %xmm7, 240(%rsi) */ /* D */ + paddd %xmm3, %xmm9 /* B */ + paddd %xmm7, %xmm11 /* D */ +/* movdqa %xmm9, 144(%rsi) */ /* B */ +/* movdqa %xmm11, 176(%rsi) */ /* D */ + pxor %xmm9, %xmm1 /* B */ + pxor %xmm11, %xmm5 /* D */ + movdqa %xmm1, %xmm14 /* B */ + movdqa %xmm5, %xmm15 /* D */ +/* movdqa 0(%rsi), %xmm0 */ /* E */ +/* movdqa 32(%rsi), %xmm4 */ /* G */ + paddd 432(%rdi), %xmm0 /* E */ + paddd 240(%rdi), %xmm4 /* G */ + pslld $25, %xmm1 /* B */ + psrld $7, %xmm14 /* B */ + pslld $25, %xmm5 /* D */ + psrld $7, %xmm15 /* D */ + por %xmm14, %xmm1 /* B */ + por %xmm15, %xmm5 /* D */ +/* movdqa %xmm1, 80(%rsi) */ /* B */ +/* movdqa %xmm5, 112(%rsi) */ /* D */ +/* round 9 (E and G) */ +/* movdqa 80(%rsi), %xmm1 */ /* E */ +/* movdqa 112(%rsi), %xmm5 */ /* G */ +/* movdqa 240(%rsi), %xmm7 */ /* E */ +/* movdqa 208(%rsi), %xmm3 */ /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa 160(%rsi), %xmm6 */ /* E */ +/* movdqa 128(%rsi), %xmm2 */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + paddd 368(%rdi), %xmm0 /* E */ + paddd 384(%rdi), %xmm4 /* G */ + pslld $16, %xmm7 /* E */ + psrld $16, %xmm14 /* E */ + pslld $16, %xmm3 /* G */ + psrld $16, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ + pslld $20, %xmm1 /* E */ + psrld $12, %xmm14 /* E */ + pslld $20, %xmm5 /* G */ + psrld $12, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ + paddd %xmm1, %xmm0 /* E */ + paddd %xmm5, %xmm4 /* G */ +/* movdqa %xmm0, 0(%rsi) */ /* E */ +/* movdqa %xmm4, 32(%rsi) */ /* G */ + pxor %xmm0, %xmm7 /* E */ + pxor %xmm4, %xmm3 /* G */ + movdqa %xmm7, %xmm14 /* E */ + movdqa %xmm3, %xmm15 /* G */ + pslld $24, %xmm7 /* E */ + psrld $8, %xmm14 /* E */ + pslld $24, %xmm3 /* G */ + psrld $8, %xmm15 /* G */ + por %xmm14, %xmm7 /* E */ + por %xmm15, %xmm3 /* G */ + movdqa %xmm7, 240(%rsi) /* E */ + movdqa %xmm3, 208(%rsi) /* G */ + paddd %xmm7, %xmm6 /* E */ + paddd %xmm3, %xmm2 /* G */ +/* movdqa %xmm6, 160(%rsi) */ /* E */ +/* movdqa %xmm2, 128(%rsi) */ /* G */ + pxor %xmm6, %xmm1 /* E */ + pxor %xmm2, %xmm5 /* G */ + movdqa %xmm1, %xmm14 /* E */ + movdqa %xmm5, %xmm15 /* G */ +/* movdqa 16(%rsi), %xmm8 */ /* F */ +/* movdqa 48(%rsi), %xmm10 */ /* H */ + paddd 336(%rdi), %xmm8 /* F */ + paddd 400(%rdi), %xmm10 /* H */ + pslld $25, %xmm1 /* E */ + psrld $7, %xmm14 /* E */ + pslld $25, %xmm5 /* G */ + psrld $7, %xmm15 /* G */ + por %xmm14, %xmm1 /* E */ + por %xmm15, %xmm5 /* G */ +/* movdqa %xmm1, 80(%rsi) */ /* E */ +/* movdqa %xmm5, 112(%rsi) */ /* G */ +/* round 9 (F and H) */ +/* movdqa 96(%rsi), %xmm13 */ /* F */ +/* movdqa 64(%rsi), %xmm12 */ /* H */ + movdqa 192(%rsi), %xmm3 /* F */ + movdqa 224(%rsi), %xmm7 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa 176(%rsi), %xmm11 */ /* F */ +/* movdqa 144(%rsi), %xmm9 */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + paddd 416(%rdi), %xmm8 /* F */ + paddd 192(%rdi), %xmm10 /* H */ + pslld $16, %xmm3 /* F */ + psrld $16, %xmm14 /* F */ + pslld $16, %xmm7 /* H */ + psrld $16, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + pslld $20, %xmm13 /* F */ + psrld $12, %xmm14 /* F */ + pslld $20, %xmm12 /* H */ + psrld $12, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ + paddd %xmm13, %xmm8 /* F */ + paddd %xmm12, %xmm10 /* H */ +/* movdqa %xmm8, 16(%rsi) */ /* F */ +/* movdqa %xmm10, 48(%rsi) */ /* H */ + pxor %xmm8, %xmm3 /* F */ + pxor %xmm10, %xmm7 /* H */ + movdqa %xmm3, %xmm14 /* F */ + movdqa %xmm7, %xmm15 /* H */ + pslld $24, %xmm3 /* F */ + psrld $8, %xmm14 /* F */ + pslld $24, %xmm7 /* H */ + psrld $8, %xmm15 /* H */ + por %xmm14, %xmm3 /* F */ + por %xmm15, %xmm7 /* H */ +/* movdqa %xmm3, 192(%rsi) */ /* F */ +/* movdqa %xmm7, 224(%rsi) */ /* H */ + paddd %xmm3, %xmm11 /* F */ + paddd %xmm7, %xmm9 /* H */ +/* movdqa %xmm11, 176(%rsi) */ /* F */ +/* movdqa %xmm9, 144(%rsi) */ /* H */ + pxor %xmm11, %xmm13 /* F */ + pxor %xmm9, %xmm12 /* H */ + movdqa %xmm13, %xmm14 /* F */ + movdqa %xmm12, %xmm15 /* H */ + pslld $25, %xmm13 /* F */ + psrld $7, %xmm14 /* F */ + pslld $25, %xmm12 /* H */ + psrld $7, %xmm15 /* H */ + por %xmm14, %xmm13 /* F */ + por %xmm15, %xmm12 /* H */ +/* movdqa %xmm13, 96(%rsi) */ /* F */ +/* movdqa %xmm12, 64(%rsi) */ /* H */ +/* finalise */ + movdqa 208(%rsi), %xmm14 + pxor %xmm2, %xmm0 /* 0() ^ 128() */ + pxor %xmm9, %xmm8 /* 16() ^ 144() */ + movdqa 240(%rsi), %xmm15 + pxor %xmm6, %xmm4 /* 32() ^ 160() */ + pxor %xmm11, %xmm10 /* 48() ^ 176() */ + pxor %xmm3, %xmm12 /* 64() ^ 192() */ + pxor %xmm14, %xmm1 /* 80() ^ 208() */ + pxor %xmm7, %xmm13 /* 96() ^ 224() */ + pxor %xmm15, %xmm5 /* 112() ^ 240() */ + pxor 0(%rdi), %xmm0 + pxor 16(%rdi), %xmm8 + pxor 32(%rdi), %xmm4 + pxor 48(%rdi), %xmm10 + pxor 64(%rdi), %xmm12 + pxor 80(%rdi), %xmm1 + pxor 96(%rdi), %xmm13 + pxor 112(%rdi), %xmm5 + movdqa %xmm0, 0(%rdi) + movdqa %xmm8, 16(%rdi) + movdqa %xmm4, 32(%rdi) + movdqa %xmm10, 48(%rdi) + movdqa %xmm12, 64(%rdi) + movdqa %xmm1, 80(%rdi) + movdqa %xmm13, 96(%rdi) + movdqa %xmm5, 112(%rdi) + +#ifdef WIN64 + movdqu 0(%rsp), %xmm15 + movdqu 16(%rsp), %xmm14 + movdqu 32(%rsp), %xmm13 + movdqu 48(%rsp), %xmm12 + movdqu 64(%rsp), %xmm11 + movdqu 80(%rsp), %xmm10 + movdqu 96(%rsp), %xmm9 + movdqu 112(%rsp), %xmm8 + movdqu 128(%rsp), %xmm7 + movdqu 144(%rsp), %xmm6 + addq $160, %rsp + popq %rsi + popq %rdi +#endif + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbp + popq %rbx + ret + + +/* neoscrypt_blkcpy(dst, src, len) + * AMD64 (SSE2) block memcpy(); + * len must be a multiple of 64 bytes aligned properly */ +.globl neoscrypt_blkcpy +.globl _neoscrypt_blkcpy +neoscrypt_blkcpy: +_neoscrypt_blkcpy: +#ifdef WIN64 + movq %rdi, %r10 + movq %rsi, %r11 + movq %rcx, %rdi + movq %rdx, %rsi + movq %r8, %rdx +#endif + xorq %rcx, %rcx + movl %edx, %ecx + shrl $6, %ecx + movq $64, %rax +.blkcpy: + movdqa 0(%rsi), %xmm0 + movdqa 16(%rsi), %xmm1 + movdqa 32(%rsi), %xmm2 + movdqa 48(%rsi), %xmm3 + movdqa %xmm0, 0(%rdi) + movdqa %xmm1, 16(%rdi) + movdqa %xmm2, 32(%rdi) + movdqa %xmm3, 48(%rdi) + addq %rax, %rdi + addq %rax, %rsi + decl %ecx + jnz .blkcpy +#ifdef WIN64 + movq %r10, %rdi + movq %r11, %rsi +#endif + ret + + +/* neoscrypt_blkswp(blkA, blkB, len) + * AMD64 (SSE2) block swapper; + * len must be a multiple of 64 bytes aligned properly */ +.globl neoscrypt_blkswp +.globl _neoscrypt_blkswp +neoscrypt_blkswp: +_neoscrypt_blkswp: +#ifdef WIN64 + movq %rdi, %r10 + movq %rsi, %r11 + movq %rcx, %rdi + movq %rdx, %rsi + movq %r8, %rdx +#endif + xorq %rcx, %rcx + movl %edx, %ecx + shrl $6, %ecx + movq $64, %rax +.blkswp: + movdqa 0(%rdi), %xmm0 + movdqa 16(%rdi), %xmm1 + movdqa 32(%rdi), %xmm2 + movdqa 48(%rdi), %xmm3 + movdqa 0(%rsi), %xmm4 + movdqa 16(%rsi), %xmm5 + movdqa 32(%rsi), %xmm8 + movdqa 48(%rsi), %xmm9 + movdqa %xmm0, 0(%rsi) + movdqa %xmm1, 16(%rsi) + movdqa %xmm2, 32(%rsi) + movdqa %xmm3, 48(%rsi) + movdqa %xmm4, 0(%rdi) + movdqa %xmm5, 16(%rdi) + movdqa %xmm8, 32(%rdi) + movdqa %xmm9, 48(%rdi) + addq %rax, %rdi + addq %rax, %rsi + decl %ecx + jnz .blkswp +#ifdef WIN64 + movq %r10, %rdi + movq %r11, %rsi +#endif + ret + + +/* neoscrypt_blkxor(dst, src, len) + * AMD64 (SSE2) block XOR engine; + * len must be a multiple of 64 bytes aligned properly */ +.globl neoscrypt_blkxor +.globl _neoscrypt_blkxor +neoscrypt_blkxor: +_neoscrypt_blkxor: +#ifdef WIN64 + movq %rdi, %r10 + movq %rsi, %r11 + movq %rcx, %rdi + movq %rdx, %rsi + movq %r8, %rdx +#endif + xorq %rcx, %rcx + movl %edx, %ecx + shrl $6, %ecx + movq $64, %rax +.blkxor: + movdqa 0(%rdi), %xmm0 + movdqa 16(%rdi), %xmm1 + movdqa 32(%rdi), %xmm2 + movdqa 48(%rdi), %xmm3 + pxor 0(%rsi), %xmm0 + pxor 16(%rsi), %xmm1 + pxor 32(%rsi), %xmm2 + pxor 48(%rsi), %xmm3 + movdqa %xmm0, 0(%rdi) + movdqa %xmm1, 16(%rdi) + movdqa %xmm2, 32(%rdi) + movdqa %xmm3, 48(%rdi) + addq %rax, %rdi + addq %rax, %rsi + decl %ecx + jnz .blkxor +#ifdef WIN64 + movq %r10, %rdi + movq %r11, %rsi +#endif + ret + + +/* neoscrypt_pack_4way(dst, src, len) + * AMD64 4-way data packer */ +.globl neoscrypt_pack_4way +.globl _neoscrypt_pack_4way +neoscrypt_pack_4way: +_neoscrypt_pack_4way: +#ifdef WIN64 + pushq %rdi + pushq %rsi + movq %rcx, %rdi + movq %rdx, %rsi + movq %r8, %rdx +#endif + + movq %rdx, %rcx + shrq $2, %rdx + leaq 0(%rsi, %rdx, 2), %rax + shrq $4, %rcx +.pack_4way: + movl 0(%rsi), %r8d + movl 0(%rsi, %rdx), %r9d + addq $4, %rsi + movl 0(%rax), %r10d + movl 0(%rax, %rdx), %r11d + addq $4, %rax + movl %r8d, 0(%rdi) + movl %r9d, 4(%rdi) + movl %r10d, 8(%rdi) + movl %r11d, 12(%rdi) + addq $16, %rdi + decq %rcx + jnz .pack_4way + +#ifdef WIN64 + popq %rsi + popq %rdi +#endif + ret + + +/* neoscrypt_unpack_4way(dst, src, len) + * AMD64 4-way data unpacker */ +.globl neoscrypt_unpack_4way +.globl _neoscrypt_unpack_4way +neoscrypt_unpack_4way: +_neoscrypt_unpack_4way: +#ifdef WIN64 + pushq %rdi + pushq %rsi + movq %rcx, %rdi + movq %rdx, %rsi + movq %r8, %rdx +#endif + + movq %rdx, %rcx + shrq $2, %rdx + leaq 0(%rdi, %rdx, 2), %rax + shrq $4, %rcx +.unpack_4way: + movq 0(%rsi), %r8 + movq 8(%rsi), %r9 + addq $16, %rsi + movl %r8d, 0(%rdi) + shrq $32, %r8 + movl %r9d, 0(%rax) + shrq $32, %r9 + movl %r8d, 0(%rdi, %rdx) + addq $4, %rdi + movl %r9d, 0(%rax, %rdx) + addq $4, %rax + decq %rcx + jnz .unpack_4way + +#ifdef WIN64 + popq %rsi + popq %rdi +#endif + ret + + +/* neoscrypt_xor_4way(dst, srcA, srcB, srcC, srcD, len) + * AMD64 4-way XOR engine */ +.globl neoscrypt_xor_4way +.globl _neoscrypt_xor_4way +neoscrypt_xor_4way: +_neoscrypt_xor_4way: + pushq %rbx + pushq %rbp +#ifdef WIN64 + pushq %rdi + pushq %rsi + movq %rcx, %rdi + movq %rdx, %rsi + movq %r8, %rdx + movq %r9, %rbx + movq 72(%rsp), %rax + movq 80(%rsp), %rcx +#else + movq %rcx, %rbx + movq %r8, %rax + movq %r9, %rcx +#endif + + xorq %rbp, %rbp + shrq $4, %rcx +.xor_4way: + movl 0(%rsi, %rbp), %r8d + movl 4(%rdx, %rbp), %r9d + movl 8(%rbx, %rbp), %r10d + movl 12(%rax, %rbp), %r11d + xorl %r8d, 0(%rdi) + xorl %r9d, 4(%rdi) + xorl %r10d, 8(%rdi) + xorl %r11d, 12(%rdi) + addl $16, %ebp + addq $16, %rdi + decq %rcx + jnz .xor_4way + +#ifdef WIN64 + popq %rsi + popq %rdi +#endif + popq %rbp + popq %rbx + ret + + +/* neoscrypt_xor_salsa_4way(mem, xormem, workmem, double_rounds) + * AMD64 (SSE2) Salsa20 4-way with XOR */ +.globl neoscrypt_xor_salsa_4way +.globl _neoscrypt_xor_salsa_4way +neoscrypt_xor_salsa_4way: +_neoscrypt_xor_salsa_4way: +#ifdef WIN64 + movq %rdi, %r10 + movq %rsi, %r11 + movq %rcx, %rdi + movq %rdx, %rsi + movq %r8, %rdx + movq %r9, %rcx +#endif +/* XOR and copy to temporary memory */ + movdqa 0(%rdi), %xmm0 + movdqa 16(%rdi), %xmm1 + movdqa 32(%rdi), %xmm2 + movdqa 48(%rdi), %xmm3 + movdqa 64(%rdi), %xmm4 + movdqa 80(%rdi), %xmm5 + movdqa 96(%rdi), %xmm6 + movdqa 112(%rdi), %xmm7 + movdqa 128(%rdi), %xmm8 + movdqa 144(%rdi), %xmm9 + movdqa 160(%rdi), %xmm10 + movdqa 176(%rdi), %xmm11 + movdqa 192(%rdi), %xmm12 + movdqa 208(%rdi), %xmm13 + movdqa 224(%rdi), %xmm14 + movdqa 240(%rdi), %xmm15 + pxor 0(%rsi), %xmm0 + pxor 16(%rsi), %xmm1 + pxor 32(%rsi), %xmm2 + pxor 48(%rsi), %xmm3 + pxor 64(%rsi), %xmm4 + pxor 80(%rsi), %xmm5 + pxor 96(%rsi), %xmm6 + pxor 112(%rsi), %xmm7 + pxor 128(%rsi), %xmm8 + pxor 144(%rsi), %xmm9 + pxor 160(%rsi), %xmm10 + pxor 176(%rsi), %xmm11 + pxor 192(%rsi), %xmm12 + pxor 208(%rsi), %xmm13 + pxor 224(%rsi), %xmm14 + pxor 240(%rsi), %xmm15 + movdqa %xmm0, 0(%rdi) + movdqa %xmm1, 16(%rdi) + movdqa %xmm2, 32(%rdi) + movdqa %xmm3, 48(%rdi) + movdqa %xmm4, 64(%rdi) + movdqa %xmm5, 80(%rdi) + movdqa %xmm6, 96(%rdi) + movdqa %xmm7, 112(%rdi) + movdqa %xmm8, 128(%rdi) + movdqa %xmm9, 144(%rdi) + movdqa %xmm10, 160(%rdi) + movdqa %xmm11, 176(%rdi) + movdqa %xmm12, 192(%rdi) + movdqa %xmm13, 208(%rdi) + movdqa %xmm14, 224(%rdi) + movdqa %xmm15, 240(%rdi) + movdqa %xmm0, 0(%rdx) + movdqa %xmm1, 16(%rdx) + movdqa %xmm2, 32(%rdx) + movdqa %xmm3, 48(%rdx) + movdqa %xmm4, 64(%rdx) + movdqa %xmm5, 80(%rdx) + movdqa %xmm6, 96(%rdx) + movdqa %xmm7, 112(%rdx) + movdqa %xmm8, 128(%rdx) + movdqa %xmm9, 144(%rdx) + movdqa %xmm10, 160(%rdx) + movdqa %xmm11, 176(%rdx) + movdqa %xmm12, 192(%rdx) + movdqa %xmm13, 208(%rdx) + movdqa %xmm14, 224(%rdx) + movdqa %xmm15, 240(%rdx) +.xor_salsa_4way: +/* quarters A and B */ + movdqa 0(%rdx), %xmm0 /* A: load a */ + movdqa 80(%rdx), %xmm4 /* B: load a */ + paddd 192(%rdx), %xmm0 /* A: t = a + d */ + paddd 16(%rdx), %xmm4 /* B: t = a + d */ + movdqa %xmm0, %xmm3 /* A: rotate t (0) */ + movdqa %xmm4, %xmm7 /* B: rotate t (0) */ + pslld $7, %xmm0 /* A: rotate t (1) */ + psrld $25, %xmm3 /* A: rotate t (2) */ + pslld $7, %xmm4 /* B: rotate t (1) */ + psrld $25, %xmm7 /* B: rotate t (2) */ + por %xmm3, %xmm0 /* A: rotate t (3) */ + por %xmm7, %xmm4 /* B: rotate t (3) */ + pxor 64(%rdx), %xmm0 /* A: b = b ^ t */ + pxor 144(%rdx), %xmm4 /* B: b = b ^ t */ + movdqa %xmm0, %xmm1 /* A: copy b */ + movdqa %xmm4, %xmm5 /* B: copy b */ + movdqa %xmm1, 64(%rdx) /* A: store b */ + movdqa %xmm5, 144(%rdx) /* B: store b */ + paddd 0(%rdx), %xmm0 /* A: t = b + a */ + paddd 80(%rdx), %xmm4 /* B: t = b + a */ + movdqa %xmm0, %xmm3 /* A: rotate t (0) */ + movdqa %xmm4, %xmm7 /* B: rotate t (0) */ + pslld $9, %xmm0 /* A: rotate t (1) */ + psrld $23, %xmm3 /* A: rotate t (2) */ + pslld $9, %xmm4 /* B: rotate t (1) */ + psrld $23, %xmm7 /* B: rotate t (2) */ + por %xmm3, %xmm0 /* A: rotate t (3) */ + por %xmm7, %xmm4 /* B: rotate t (3) */ + pxor 128(%rdx), %xmm0 /* A: c = c ^ t */ + pxor 208(%rdx), %xmm4 /* B: c = c ^ t */ + movdqa %xmm0, %xmm2 /* A: copy c */ + movdqa %xmm4, %xmm6 /* B: copy c */ + movdqa %xmm2, 128(%rdx) /* A: store c */ + movdqa %xmm6, 208(%rdx) /* B: store c */ + paddd %xmm1, %xmm0 /* A: t = c + b */ + paddd %xmm5, %xmm4 /* B: t = c + b */ + movdqa %xmm0, %xmm3 /* A: rotate t (0) */ + movdqa %xmm4, %xmm7 /* B: rotate t (0) */ + pslld $13, %xmm0 /* A: rotate t (1) */ + psrld $19, %xmm3 /* A: rotate t (2) */ + pslld $13, %xmm4 /* B: rotate t (1) */ + psrld $19, %xmm7 /* B: rotate t (2) */ + por %xmm3, %xmm0 /* A: rotate t (3) */ + por %xmm7, %xmm4 /* B: rotate t (3) */ + pxor 192(%rdx), %xmm0 /* A: d = d ^ t */ + pxor 16(%rdx), %xmm4 /* B: d = d ^ t */ + movdqa %xmm0, 192(%rdx) /* A: store d */ + movdqa %xmm4, 16(%rdx) /* B: store d */ + paddd %xmm2, %xmm0 /* A: t = d + c */ + paddd %xmm6, %xmm4 /* B: t = d + c */ + movdqa %xmm0, %xmm3 /* A: rotate t (0) */ + movdqa %xmm4, %xmm7 /* B: rotate t (0) */ + pslld $18, %xmm0 /* A: rotate t (1) */ + psrld $14, %xmm3 /* A: rotate t (2) */ + pslld $18, %xmm4 /* B: rotate t (1) */ + psrld $14, %xmm7 /* B: rotate t (2) */ + por %xmm3, %xmm0 /* A: rotate t (3) */ + por %xmm7, %xmm4 /* B: rotate t (3) */ + pxor 0(%rdx), %xmm0 /* A: a = a ^ t */ + pxor 80(%rdx), %xmm4 /* B: a = a ^ t */ + movdqa %xmm0, 0(%rdx) /* A: store a */ + movdqa %xmm4, 80(%rdx) /* B: store a */ +/* quarters C and D*/ + movdqa 160(%rdx), %xmm0 /* C: load a */ + movdqa 240(%rdx), %xmm4 /* D: load a */ + paddd 96(%rdx), %xmm0 /* C: t = a + d */ + paddd 176(%rdx), %xmm4 /* D: t = a + d */ + movdqa %xmm0, %xmm3 /* C: rotate t (0) */ + movdqa %xmm4, %xmm7 /* D: rotate t (0) */ + pslld $7, %xmm0 /* C: rotate t (1) */ + psrld $25, %xmm3 /* C: rotate t (2) */ + pslld $7, %xmm4 /* D: rotate t (1) */ + psrld $25, %xmm7 /* D: rotate t (2) */ + por %xmm3, %xmm0 /* C: rotate t (3) */ + por %xmm7, %xmm4 /* D: rotate t (3) */ + pxor 224(%rdx), %xmm0 /* C: b = b ^ t */ + pxor 48(%rdx), %xmm4 /* D: b = b ^ t */ + movdqa %xmm0, %xmm1 /* C: copy b */ + movdqa %xmm4, %xmm5 /* D: copy b */ + movdqa %xmm1, 224(%rdx) /* C: store b */ + movdqa %xmm5, 48(%rdx) /* D: store b */ + paddd 160(%rdx), %xmm0 /* C: t = b + a */ + paddd 240(%rdx), %xmm4 /* D: t = b + a */ + movdqa %xmm0, %xmm3 /* C: rotate t (0) */ + movdqa %xmm4, %xmm7 /* D: rotate t (0) */ + pslld $9, %xmm0 /* C: rotate t (1) */ + psrld $23, %xmm3 /* C: rotate t (2) */ + pslld $9, %xmm4 /* D: rotate t (1) */ + psrld $23, %xmm7 /* D: rotate t (2) */ + por %xmm3, %xmm0 /* C: rotate t (3) */ + por %xmm7, %xmm4 /* D: rotate t (3) */ + pxor 32(%rdx), %xmm0 /* C: c = c ^ t */ + pxor 112(%rdx), %xmm4 /* D: c = c ^ t */ + movdqa %xmm0, %xmm2 /* C: copy c */ + movdqa %xmm4, %xmm6 /* D: copy c */ + movdqa %xmm2, 32(%rdx) /* C: store c */ + movdqa %xmm6, 112(%rdx) /* D: store c */ + paddd %xmm1, %xmm0 /* C: t = c + b */ + paddd %xmm5, %xmm4 /* D: t = c + b */ + movdqa %xmm0, %xmm3 /* C: rotate t (0) */ + movdqa %xmm4, %xmm7 /* D: rotate t (0) */ + pslld $13, %xmm0 /* C: rotate t (1) */ + psrld $19, %xmm3 /* C: rotate t (2) */ + pslld $13, %xmm4 /* D: rotate t (1) */ + psrld $19, %xmm7 /* D: rotate t (2) */ + por %xmm3, %xmm0 /* C: rotate t (3) */ + por %xmm7, %xmm4 /* D: rotate t (3) */ + pxor 96(%rdx), %xmm0 /* C: d = d ^ t */ + pxor 176(%rdx), %xmm4 /* D: d = d ^ t */ + movdqa %xmm0, 96(%rdx) /* C: store d */ + movdqa %xmm4, 176(%rdx) /* D: store d */ + paddd %xmm2, %xmm0 /* C: t = d + c */ + paddd %xmm6, %xmm4 /* D: t = d + c */ + movdqa %xmm0, %xmm3 /* C: rotate t (0) */ + movdqa %xmm4, %xmm7 /* D: rotate t (0) */ + pslld $18, %xmm0 /* C: rotate t (1) */ + psrld $14, %xmm3 /* C: rotate t (2) */ + pslld $18, %xmm4 /* D: rotate t (1) */ + psrld $14, %xmm7 /* D: rotate t (2) */ + por %xmm3, %xmm0 /* C: rotate t (3) */ + por %xmm7, %xmm4 /* D: rotate t (3) */ + pxor 160(%rdx), %xmm0 /* C: a = a ^ t */ + pxor 240(%rdx), %xmm4 /* D: a = a ^ t */ + movdqa %xmm0, 160(%rdx) /* C: store a */ + movdqa %xmm4, 240(%rdx) /* D: store a */ +/* quarters E and F */ + movdqa 0(%rdx), %xmm0 /* E: load a */ + movdqa 80(%rdx), %xmm4 /* F: load a */ + paddd 48(%rdx), %xmm0 /* E: t = a + d */ + paddd 64(%rdx), %xmm4 /* F: t = a + d */ + movdqa %xmm0, %xmm3 /* E: rotate t (0) */ + movdqa %xmm4, %xmm7 /* F: rotate t (0) */ + pslld $7, %xmm0 /* E: rotate t (1) */ + psrld $25, %xmm3 /* E: rotate t (2) */ + pslld $7, %xmm4 /* F: rotate t (1) */ + psrld $25, %xmm7 /* F: rotate t (2) */ + por %xmm3, %xmm0 /* E: rotate t (3) */ + por %xmm7, %xmm4 /* F: rotate t (3) */ + pxor 16(%rdx), %xmm0 /* E: b = b ^ t */ + pxor 96(%rdx), %xmm4 /* F: b = b ^ t */ + movdqa %xmm0, %xmm1 /* E: copy b */ + movdqa %xmm4, %xmm5 /* F: copy b */ + movdqa %xmm1, 16(%rdx) /* E: store b */ + movdqa %xmm5, 96(%rdx) /* F: store b */ + paddd 0(%rdx), %xmm0 /* E: t = b + a */ + paddd 80(%rdx), %xmm4 /* F: t = b + a */ + movdqa %xmm0, %xmm3 /* E: rotate t (0) */ + movdqa %xmm4, %xmm7 /* F: rotate t (0) */ + pslld $9, %xmm0 /* E: rotate t (1) */ + psrld $23, %xmm3 /* E: rotate t (2) */ + pslld $9, %xmm4 /* F: rotate t (1) */ + psrld $23, %xmm7 /* F: rotate t (2) */ + por %xmm3, %xmm0 /* E: rotate t (3) */ + por %xmm7, %xmm4 /* F: rotate t (3) */ + pxor 32(%rdx), %xmm0 /* E: c = c ^ t */ + pxor 112(%rdx), %xmm4 /* F: c = c ^ t */ + movdqa %xmm0, %xmm2 /* E: copy c */ + movdqa %xmm4, %xmm6 /* F: copy c */ + movdqa %xmm2, 32(%rdx) /* E: store c */ + movdqa %xmm6, 112(%rdx) /* F: store c */ + paddd %xmm1, %xmm0 /* E: t = c + b */ + paddd %xmm5, %xmm4 /* F: t = c + b */ + movdqa %xmm0, %xmm3 /* E: rotate t (0) */ + movdqa %xmm4, %xmm7 /* F: rotate t (0) */ + pslld $13, %xmm0 /* E: rotate t (1) */ + psrld $19, %xmm3 /* E: rotate t (2) */ + pslld $13, %xmm4 /* F: rotate t (1) */ + psrld $19, %xmm7 /* F: rotate t (2) */ + por %xmm3, %xmm0 /* E: rotate t (3) */ + por %xmm7, %xmm4 /* F: rotate t (3) */ + pxor 48(%rdx), %xmm0 /* E: d = d ^ t */ + pxor 64(%rdx), %xmm4 /* F: d = d ^ t */ + movdqa %xmm0, 48(%rdx) /* E: store d */ + movdqa %xmm4, 64(%rdx) /* F: store d */ + paddd %xmm2, %xmm0 /* E: t = d + c */ + paddd %xmm6, %xmm4 /* F: t = d + c */ + movdqa %xmm0, %xmm3 /* E: rotate t (0) */ + movdqa %xmm4, %xmm7 /* F: rotate t (0) */ + pslld $18, %xmm0 /* E: rotate t (1) */ + psrld $14, %xmm3 /* E: rotate t (2) */ + pslld $18, %xmm4 /* F: rotate t (1) */ + psrld $14, %xmm7 /* F: rotate t (2) */ + por %xmm3, %xmm0 /* E: rotate t (3) */ + por %xmm7, %xmm4 /* F: rotate t (3) */ + pxor 0(%rdx), %xmm0 /* E: a = a ^ t */ + pxor 80(%rdx), %xmm4 /* F: a = a ^ t */ + movdqa %xmm0, 0(%rdx) /* E: store a */ + movdqa %xmm4, 80(%rdx) /* F: store a */ +/* quarters G and H */ + movdqa 160(%rdx), %xmm0 /* G: load a */ + movdqa 240(%rdx), %xmm4 /* H: load a */ + paddd 144(%rdx), %xmm0 /* G: t = a + d */ + paddd 224(%rdx), %xmm4 /* H: t = a + d */ + movdqa %xmm0, %xmm3 /* G: rotate t (0) */ + movdqa %xmm4, %xmm7 /* H: rotate t (0) */ + pslld $7, %xmm0 /* G: rotate t (1) */ + psrld $25, %xmm3 /* G: rotate t (2) */ + pslld $7, %xmm4 /* H: rotate t (1) */ + psrld $25, %xmm7 /* H: rotate t (2) */ + por %xmm3, %xmm0 /* G: rotate t (3) */ + por %xmm7, %xmm4 /* H: rotate t (3) */ + pxor 176(%rdx), %xmm0 /* G: b = b ^ t */ + pxor 192(%rdx), %xmm4 /* H: b = b ^ t */ + movdqa %xmm0, %xmm1 /* G: copy b */ + movdqa %xmm4, %xmm5 /* H: copy b */ + movdqa %xmm1, 176(%rdx) /* G: store b */ + movdqa %xmm5, 192(%rdx) /* H: store b */ + paddd 160(%rdx), %xmm0 /* G: t = b + a */ + paddd 240(%rdx), %xmm4 /* H: t = b + a */ + movdqa %xmm0, %xmm3 /* G: rotate t (0) */ + movdqa %xmm4, %xmm7 /* H: rotate t (0) */ + pslld $9, %xmm0 /* G: rotate t (1) */ + psrld $23, %xmm3 /* G: rotate t (2) */ + pslld $9, %xmm4 /* H: rotate t (1) */ + psrld $23, %xmm7 /* H: rotate t (2) */ + por %xmm3, %xmm0 /* G: rotate t (3) */ + por %xmm7, %xmm4 /* H: rotate t (3) */ + pxor 128(%rdx), %xmm0 /* G: c = c ^ t */ + pxor 208(%rdx), %xmm4 /* H: c = c ^ t */ + movdqa %xmm0, %xmm2 /* G: copy c */ + movdqa %xmm4, %xmm6 /* H: copy c */ + movdqa %xmm2, 128(%rdx) /* G: store c */ + movdqa %xmm6, 208(%rdx) /* H: store c */ + paddd %xmm1, %xmm0 /* G: t = c + b */ + paddd %xmm5, %xmm4 /* H: t = c + b */ + movdqa %xmm0, %xmm3 /* G: rotate t (0) */ + movdqa %xmm4, %xmm7 /* H: rotate t (0) */ + pslld $13, %xmm0 /* G: rotate t (1) */ + psrld $19, %xmm3 /* G: rotate t (2) */ + pslld $13, %xmm4 /* H: rotate t (1) */ + psrld $19, %xmm7 /* H: rotate t (2) */ + por %xmm3, %xmm0 /* G: rotate t (3) */ + por %xmm7, %xmm4 /* H: rotate t (3) */ + pxor 144(%rdx), %xmm0 /* G: d = d ^ t */ + pxor 224(%rdx), %xmm4 /* H: d = d ^ t */ + movdqa %xmm0, 144(%rdx) /* G: store d */ + movdqa %xmm4, 224(%rdx) /* H: store d */ + paddd %xmm2, %xmm0 /* G: t = d + c */ + paddd %xmm6, %xmm4 /* H: t = d + c */ + movdqa %xmm0, %xmm3 /* G: rotate t (0) */ + movdqa %xmm4, %xmm7 /* H: rotate t (0) */ + pslld $18, %xmm0 /* G: rotate t (1) */ + psrld $14, %xmm3 /* G: rotate t (2) */ + pslld $18, %xmm4 /* H: rotate t (1) */ + psrld $14, %xmm7 /* H: rotate t (2) */ + por %xmm3, %xmm0 /* G: rotate t (3) */ + por %xmm7, %xmm4 /* H: rotate t (3) */ + pxor 160(%rdx), %xmm0 /* G: a = a ^ t */ + pxor 240(%rdx), %xmm4 /* H: a = a ^ t */ + movdqa %xmm0, 160(%rdx) /* G: store a */ + movdqa %xmm4, 240(%rdx) /* H: store a */ + decl %ecx + jnz .xor_salsa_4way + +/* write back data */ + movdqa 0(%rdi), %xmm0 + movdqa 16(%rdi), %xmm1 + movdqa 32(%rdi), %xmm2 + movdqa 48(%rdi), %xmm3 + movdqa 64(%rdi), %xmm4 + movdqa 80(%rdi), %xmm5 + movdqa 96(%rdi), %xmm6 + movdqa 112(%rdi), %xmm7 + movdqa 128(%rdi), %xmm8 + movdqa 144(%rdi), %xmm9 + movdqa 160(%rdi), %xmm10 + movdqa 176(%rdi), %xmm11 + movdqa 192(%rdi), %xmm12 + movdqa 208(%rdi), %xmm13 + movdqa 224(%rdi), %xmm14 + movdqa 240(%rdi), %xmm15 + paddd 0(%rdx), %xmm0 + paddd 16(%rdx), %xmm1 + paddd 32(%rdx), %xmm2 + paddd 48(%rdx), %xmm3 + paddd 64(%rdx), %xmm4 + paddd 80(%rdx), %xmm5 + paddd 96(%rdx), %xmm6 + paddd 112(%rdx), %xmm7 + paddd 128(%rdx), %xmm8 + paddd 144(%rdx), %xmm9 + paddd 160(%rdx), %xmm10 + paddd 176(%rdx), %xmm11 + paddd 192(%rdx), %xmm12 + paddd 208(%rdx), %xmm13 + paddd 224(%rdx), %xmm14 + paddd 240(%rdx), %xmm15 + movdqa %xmm0, 0(%rdi) + movdqa %xmm1, 16(%rdi) + movdqa %xmm2, 32(%rdi) + movdqa %xmm3, 48(%rdi) + movdqa %xmm4, 64(%rdi) + movdqa %xmm5, 80(%rdi) + movdqa %xmm6, 96(%rdi) + movdqa %xmm7, 112(%rdi) + movdqa %xmm8, 128(%rdi) + movdqa %xmm9, 144(%rdi) + movdqa %xmm10, 160(%rdi) + movdqa %xmm11, 176(%rdi) + movdqa %xmm12, 192(%rdi) + movdqa %xmm13, 208(%rdi) + movdqa %xmm14, 224(%rdi) + movdqa %xmm15, 240(%rdi) +#ifdef WIN64 + movq %r10, %rdi + movq %r11, %rsi +#endif + ret + + +/* neoscrypt_xor_chacha_4way(mem, xormem, workmem, double_rounds) + * AMD64 (SSE2) ChaCha20 4-way with XOR */ +.globl neoscrypt_xor_chacha_4way +.globl _neoscrypt_xor_chacha_4way +neoscrypt_xor_chacha_4way: +_neoscrypt_xor_chacha_4way: +#ifdef WIN64 + movq %rdi, %r10 + movq %rsi, %r11 + movq %rcx, %rdi + movq %rdx, %rsi + movq %r8, %rdx + movq %r9, %rcx +#endif +/* XOR and copy to temporary memory */ + movdqa 0(%rdi), %xmm0 + movdqa 16(%rdi), %xmm1 + movdqa 32(%rdi), %xmm2 + movdqa 48(%rdi), %xmm3 + movdqa 64(%rdi), %xmm4 + movdqa 80(%rdi), %xmm5 + movdqa 96(%rdi), %xmm6 + movdqa 112(%rdi), %xmm7 + movdqa 128(%rdi), %xmm8 + movdqa 144(%rdi), %xmm9 + movdqa 160(%rdi), %xmm10 + movdqa 176(%rdi), %xmm11 + movdqa 192(%rdi), %xmm12 + movdqa 208(%rdi), %xmm13 + movdqa 224(%rdi), %xmm14 + movdqa 240(%rdi), %xmm15 + pxor 0(%rsi), %xmm0 + pxor 16(%rsi), %xmm1 + pxor 32(%rsi), %xmm2 + pxor 48(%rsi), %xmm3 + pxor 64(%rsi), %xmm4 + pxor 80(%rsi), %xmm5 + pxor 96(%rsi), %xmm6 + pxor 112(%rsi), %xmm7 + pxor 128(%rsi), %xmm8 + pxor 144(%rsi), %xmm9 + pxor 160(%rsi), %xmm10 + pxor 176(%rsi), %xmm11 + pxor 192(%rsi), %xmm12 + pxor 208(%rsi), %xmm13 + pxor 224(%rsi), %xmm14 + pxor 240(%rsi), %xmm15 + movdqa %xmm0, 0(%rdi) + movdqa %xmm1, 16(%rdi) + movdqa %xmm2, 32(%rdi) + movdqa %xmm3, 48(%rdi) + movdqa %xmm4, 64(%rdi) + movdqa %xmm5, 80(%rdi) + movdqa %xmm6, 96(%rdi) + movdqa %xmm7, 112(%rdi) + movdqa %xmm8, 128(%rdi) + movdqa %xmm9, 144(%rdi) + movdqa %xmm10, 160(%rdi) + movdqa %xmm11, 176(%rdi) + movdqa %xmm12, 192(%rdi) + movdqa %xmm13, 208(%rdi) + movdqa %xmm14, 224(%rdi) + movdqa %xmm15, 240(%rdi) + movdqa %xmm0, 0(%rdx) + movdqa %xmm1, 16(%rdx) + movdqa %xmm2, 32(%rdx) + movdqa %xmm3, 48(%rdx) + movdqa %xmm4, 64(%rdx) + movdqa %xmm5, 80(%rdx) + movdqa %xmm6, 96(%rdx) + movdqa %xmm7, 112(%rdx) + movdqa %xmm8, 128(%rdx) + movdqa %xmm9, 144(%rdx) + movdqa %xmm10, 160(%rdx) + movdqa %xmm11, 176(%rdx) + movdqa %xmm12, 192(%rdx) + movdqa %xmm13, 208(%rdx) + movdqa %xmm14, 224(%rdx) + movdqa %xmm15, 240(%rdx) +.xor_chacha_4way: +/* quarters A and B */ + movdqa 0(%rdx), %xmm0 /* A: load a */ + movdqa 16(%rdx), %xmm4 /* B: load a */ + movdqa 64(%rdx), %xmm1 /* A: load b */ + movdqa 80(%rdx), %xmm5 /* B: load b */ + paddd %xmm1, %xmm0 /* A: a = a + b */ + paddd %xmm5, %xmm4 /* B: a = a + b */ + movdqa 192(%rdx), %xmm3 /* A: load d */ + movdqa 208(%rdx), %xmm7 /* B: load d */ + pxor %xmm0, %xmm3 /* A: d = d ^ a */ + pxor %xmm4, %xmm7 /* B: d = d ^ a */ + movdqa 128(%rdx), %xmm2 /* A: load c */ + movdqa 144(%rdx), %xmm6 /* B: load c */ + movdqa %xmm3, %xmm14 /* A: rotate d (0) */ + movdqa %xmm7, %xmm15 /* B: rotate d (0) */ + pslld $16, %xmm3 /* A: rotate d (1) */ + psrld $16, %xmm14 /* A: rotate d (2) */ + pslld $16, %xmm7 /* B: rotate d (1) */ + psrld $16, %xmm15 /* B: rotate d (2) */ + por %xmm14, %xmm3 /* A: rotate d (3) */ + por %xmm15, %xmm7 /* B: rotate d (3) */ + paddd %xmm3, %xmm2 /* A: c = c + d */ + paddd %xmm7, %xmm6 /* B: c = c + d */ + pxor %xmm2, %xmm1 /* A: b = b ^ c */ + pxor %xmm6, %xmm5 /* B: b = b ^ c */ + movdqa %xmm1, %xmm14 /* A: rotate b (0) */ + movdqa %xmm5, %xmm15 /* B: rotate b (0) */ + pslld $12, %xmm1 /* A: rotate b (1) */ + psrld $20, %xmm14 /* A: rotate b (2) */ + pslld $12, %xmm5 /* B: rotate b (1) */ + psrld $20, %xmm15 /* B: rotate b (2) */ + por %xmm14, %xmm1 /* A: rotate b (3) */ + por %xmm15, %xmm5 /* B: rotate b (3) */ + paddd %xmm1, %xmm0 /* A: a = a + b */ + paddd %xmm5, %xmm4 /* B: a = a + b */ + movdqa %xmm0, 0(%rdx) /* A: store a */ + movdqa %xmm4, 16(%rdx) /* B: store a */ + pxor %xmm0, %xmm3 /* A: d = d ^ a */ + pxor %xmm4, %xmm7 /* B: d = d ^ a */ + movdqa %xmm3, %xmm14 /* A: rotate d (0) */ + movdqa %xmm7, %xmm15 /* B: rotate d (0) */ + pslld $8, %xmm3 /* A: rotate d (1) */ + psrld $24, %xmm14 /* A: rotate d (2) */ + pslld $8, %xmm7 /* B: rotate d (1) */ + psrld $24, %xmm15 /* B: rotate d (2) */ + por %xmm14, %xmm3 /* A: rotate d (3) */ + por %xmm15, %xmm7 /* B: rotate d (3) */ + movdqa %xmm3, 192(%rdx) /* A: store d */ + movdqa %xmm7, 208(%rdx) /* B: store d */ + paddd %xmm3, %xmm2 /* A: c = c + d */ + paddd %xmm7, %xmm6 /* B: c = c + d */ + movdqa %xmm2, 128(%rdx) /* A: store c */ + movdqa %xmm6, 144(%rdx) /* B: store c */ + pxor %xmm2, %xmm1 /* A: b = b ^ c */ + pxor %xmm6, %xmm5 /* B: b = b ^ c */ + movdqa %xmm1, %xmm14 /* A: rotate b (0) */ + movdqa %xmm5, %xmm15 /* B: rotate b (0) */ + pslld $7, %xmm1 /* A: rotate b (1) */ + psrld $25, %xmm14 /* A: rotate b (2) */ + pslld $7, %xmm5 /* B: rotate b (1) */ + psrld $25, %xmm15 /* B: rotate b (2) */ + por %xmm14, %xmm1 /* A: rotate b (3) */ + por %xmm15, %xmm5 /* B: rotate b (3) */ + movdqa %xmm1, 64(%rdx) /* A: store b */ + movdqa %xmm5, 80(%rdx) /* B: store b */ +/* quarters C and D */ + movdqa 32(%rdx), %xmm0 /* C: load a */ + movdqa 48(%rdx), %xmm4 /* D: load a */ + movdqa 96(%rdx), %xmm1 /* C: load b */ + movdqa 112(%rdx), %xmm5 /* D: load b */ + paddd %xmm1, %xmm0 /* C: a = a + b */ + paddd %xmm5, %xmm4 /* D: a = a + b */ + movdqa 224(%rdx), %xmm3 /* C: load d */ + movdqa 240(%rdx), %xmm7 /* D: load d */ + pxor %xmm0, %xmm3 /* C: d = d ^ a */ + pxor %xmm4, %xmm7 /* D: d = d ^ a */ + movdqa 160(%rdx), %xmm2 /* C: load c */ + movdqa 176(%rdx), %xmm6 /* D: load c */ + movdqa %xmm3, %xmm14 /* C: rotate d (0) */ + movdqa %xmm7, %xmm15 /* D: rotate d (0) */ + pslld $16, %xmm3 /* C: rotate d (1) */ + psrld $16, %xmm14 /* C: rotate d (2) */ + pslld $16, %xmm7 /* D: rotate d (1) */ + psrld $16, %xmm15 /* D: rotate d (2) */ + por %xmm14, %xmm3 /* C: rotate d (3) */ + por %xmm15, %xmm7 /* D: rotate d (3) */ + paddd %xmm3, %xmm2 /* C: c = c + d */ + paddd %xmm7, %xmm6 /* D: c = c + d */ + pxor %xmm2, %xmm1 /* C: b = b ^ c */ + pxor %xmm6, %xmm5 /* D: b = b ^ c */ + movdqa %xmm1, %xmm14 /* C: rotate b (0) */ + movdqa %xmm5, %xmm15 /* D: rotate b (0) */ + pslld $12, %xmm1 /* C: rotate b (1) */ + psrld $20, %xmm14 /* C: rotate b (2) */ + pslld $12, %xmm5 /* D: rotate b (1) */ + psrld $20, %xmm15 /* D: rotate b (2) */ + por %xmm14, %xmm1 /* C: rotate b (3) */ + por %xmm15, %xmm5 /* D: rotate b (3) */ + paddd %xmm1, %xmm0 /* C: a = a + b */ + paddd %xmm5, %xmm4 /* D: a = a + b */ + movdqa %xmm0, 32(%rdx) /* C: store a */ + movdqa %xmm4, 48(%rdx) /* D: store a */ + pxor %xmm0, %xmm3 /* C: d = d ^ a */ + pxor %xmm4, %xmm7 /* D: d = d ^ a */ + movdqa %xmm3, %xmm14 /* C: rotate d (0) */ + movdqa %xmm7, %xmm15 /* D: rotate d (0) */ + pslld $8, %xmm3 /* C: rotate d (1) */ + psrld $24, %xmm14 /* C: rotate d (2) */ + pslld $8, %xmm7 /* D: rotate d (1) */ + psrld $24, %xmm15 /* D: rotate d (2) */ + por %xmm14, %xmm3 /* C: rotate d (3) */ + por %xmm15, %xmm7 /* D: rotate d (3) */ + movdqa %xmm3, 224(%rdx) /* C: store d */ + movdqa %xmm7, 240(%rdx) /* D: store d */ + paddd %xmm3, %xmm2 /* C: c = c + d */ + paddd %xmm7, %xmm6 /* D: c = c + d */ + movdqa %xmm2, 160(%rdx) /* C: store c */ + movdqa %xmm6, 176(%rdx) /* D: store c */ + pxor %xmm2, %xmm1 /* C: b = b ^ c */ + pxor %xmm6, %xmm5 /* D: b = b ^ c */ + movdqa %xmm1, %xmm14 /* C: rotate b (0) */ + movdqa %xmm5, %xmm15 /* D: rotate b (0) */ + pslld $7, %xmm1 /* C: rotate b (1) */ + psrld $25, %xmm14 /* C: rotate b (2) */ + pslld $7, %xmm5 /* D: rotate b (1) */ + psrld $25, %xmm15 /* D: rotate b (2) */ + por %xmm14, %xmm1 /* C: rotate b (3) */ + por %xmm15, %xmm5 /* D: rotate b (3) */ + movdqa %xmm1, 96(%rdx) /* C: store b */ + movdqa %xmm5, 112(%rdx) /* D: store b */ +/* quarters E and F */ + movdqa 0(%rdx), %xmm0 /* E: load a */ + movdqa 16(%rdx), %xmm4 /* F: load a */ + movdqa 80(%rdx), %xmm1 /* E: load b */ + movdqa 96(%rdx), %xmm5 /* F: load b */ + paddd %xmm1, %xmm0 /* E: a = a + b */ + paddd %xmm5, %xmm4 /* F: a = a + b */ + movdqa 240(%rdx), %xmm3 /* E: load d */ + movdqa 192(%rdx), %xmm7 /* F: load d */ + pxor %xmm0, %xmm3 /* E: d = d ^ a */ + pxor %xmm4, %xmm7 /* F: d = d ^ a */ + movdqa 160(%rdx), %xmm2 /* E: load c */ + movdqa 176(%rdx), %xmm6 /* F: load c */ + movdqa %xmm3, %xmm14 /* E: rotate d (0) */ + movdqa %xmm7, %xmm15 /* F: rotate d (0) */ + pslld $16, %xmm3 /* E: rotate d (1) */ + psrld $16, %xmm14 /* E: rotate d (2) */ + pslld $16, %xmm7 /* F: rotate d (1) */ + psrld $16, %xmm15 /* F: rotate d (2) */ + por %xmm14, %xmm3 /* E: rotate d (3) */ + por %xmm15, %xmm7 /* F: rotate d (3) */ + paddd %xmm3, %xmm2 /* E: c = c + d */ + paddd %xmm7, %xmm6 /* F: c = c + d */ + pxor %xmm2, %xmm1 /* E: b = b ^ c */ + pxor %xmm6, %xmm5 /* F: b = b ^ c */ + movdqa %xmm1, %xmm14 /* E: rotate b (0) */ + movdqa %xmm5, %xmm15 /* F: rotate b (0) */ + pslld $12, %xmm1 /* E: rotate b (1) */ + psrld $20, %xmm14 /* E: rotate b (2) */ + pslld $12, %xmm5 /* F: rotate b (1) */ + psrld $20, %xmm15 /* F: rotate b (2) */ + por %xmm14, %xmm1 /* E: rotate b (3) */ + por %xmm15, %xmm5 /* F: rotate b (3) */ + paddd %xmm1, %xmm0 /* E: a = a + b */ + paddd %xmm5, %xmm4 /* F: a = a + b */ + movdqa %xmm0, 0(%rdx) /* E: store a */ + movdqa %xmm4, 16(%rdx) /* F: store a */ + pxor %xmm0, %xmm3 /* E: d = d ^ a */ + pxor %xmm4, %xmm7 /* F: d = d ^ a */ + movdqa %xmm3, %xmm14 /* E: rotate d (0) */ + movdqa %xmm7, %xmm15 /* F: rotate d (0) */ + pslld $8, %xmm3 /* E: rotate d (1) */ + psrld $24, %xmm14 /* E: rotate d (2) */ + pslld $8, %xmm7 /* F: rotate d (1) */ + psrld $24, %xmm15 /* F: rotate d (2) */ + por %xmm14, %xmm3 /* E: rotate d (3) */ + por %xmm15, %xmm7 /* F: rotate d (3) */ + movdqa %xmm3, 240(%rdx) /* E: store d */ + movdqa %xmm7, 192(%rdx) /* F: store d */ + paddd %xmm3, %xmm2 /* E: c = c + d */ + paddd %xmm7, %xmm6 /* F: c = c + d */ + movdqa %xmm2, 160(%rdx) /* E: store c */ + movdqa %xmm6, 176(%rdx) /* F: store c */ + pxor %xmm2, %xmm1 /* E: b = b ^ c */ + pxor %xmm6, %xmm5 /* F: b = b ^ c */ + movdqa %xmm1, %xmm14 /* E: rotate b (0) */ + movdqa %xmm5, %xmm15 /* F: rotate b (0) */ + pslld $7, %xmm1 /* E: rotate b (1) */ + psrld $25, %xmm14 /* E: rotate b (2) */ + pslld $7, %xmm5 /* F: rotate b (1) */ + psrld $25, %xmm15 /* F: rotate b (2) */ + por %xmm14, %xmm1 /* E: rotate b (3) */ + por %xmm15, %xmm5 /* F: rotate b (3) */ + movdqa %xmm1, 80(%rdx) /* E: store b */ + movdqa %xmm5, 96(%rdx) /* F: store b */ +/* quarter G and H */ + movdqa 32(%rdx), %xmm0 /* G: load a */ + movdqa 48(%rdx), %xmm4 /* H: load a */ + movdqa 64(%rdx), %xmm5 /* H: load b */ + movdqa 112(%rdx), %xmm1 /* G: load b */ + paddd %xmm1, %xmm0 /* G: a = a + b */ + paddd %xmm5, %xmm4 /* H: a = a + b */ + movdqa 208(%rdx), %xmm3 /* G: load d */ + movdqa 224(%rdx), %xmm7 /* H: load d */ + pxor %xmm0, %xmm3 /* G: d = d ^ a */ + pxor %xmm4, %xmm7 /* H: d = d ^ a */ + movdqa 128(%rdx), %xmm2 /* G: load c */ + movdqa 144(%rdx), %xmm6 /* H: load c */ + movdqa %xmm3, %xmm14 /* G: rotate d (0) */ + movdqa %xmm7, %xmm15 /* H: rotate d (0) */ + pslld $16, %xmm3 /* G: rotate d (1) */ + psrld $16, %xmm14 /* G: rotate d (2) */ + pslld $16, %xmm7 /* H: rotate d (1) */ + psrld $16, %xmm15 /* H: rotate d (2) */ + por %xmm14, %xmm3 /* G: rotate d (3) */ + por %xmm15, %xmm7 /* H: rotate d (3) */ + paddd %xmm3, %xmm2 /* G: c = c + d */ + paddd %xmm7, %xmm6 /* H: c = c + d */ + pxor %xmm2, %xmm1 /* G: b = b ^ c */ + pxor %xmm6, %xmm5 /* H: b = b ^ c */ + movdqa %xmm1, %xmm14 /* G: rotate b (0) */ + movdqa %xmm5, %xmm15 /* H: rotate b (0) */ + pslld $12, %xmm1 /* G: rotate b (1) */ + psrld $20, %xmm14 /* G: rotate b (2) */ + pslld $12, %xmm5 /* H: rotate b (1) */ + psrld $20, %xmm15 /* H: rotate b (2) */ + por %xmm14, %xmm1 /* G: rotate b (3) */ + por %xmm15, %xmm5 /* H: rotate b (3) */ + paddd %xmm1, %xmm0 /* G: a = a + b */ + paddd %xmm5, %xmm4 /* H: a = a + b */ + movdqa %xmm0, 32(%rdx) /* G: store a */ + movdqa %xmm4, 48(%rdx) /* H: store a */ + pxor %xmm0, %xmm3 /* G: d = d ^ a */ + pxor %xmm4, %xmm7 /* H: d = d ^ a */ + movdqa %xmm3, %xmm14 /* G: rotate d (0) */ + movdqa %xmm7, %xmm15 /* H: rotate d (0) */ + pslld $8, %xmm3 /* G: rotate d (1) */ + psrld $24, %xmm14 /* G: rotate d (2) */ + pslld $8, %xmm7 /* H: rotate d (1) */ + psrld $24, %xmm15 /* H: rotate d (2) */ + por %xmm14, %xmm3 /* G: rotate d (3) */ + por %xmm15, %xmm7 /* H: rotate d (3) */ + movdqa %xmm3, 208(%rdx) /* G: store d */ + movdqa %xmm7, 224(%rdx) /* H: store d */ + paddd %xmm3, %xmm2 /* G: c = c + d */ + paddd %xmm7, %xmm6 /* H: c = c + d */ + movdqa %xmm2, 128(%rdx) /* G: store c */ + movdqa %xmm6, 144(%rdx) /* H: store c */ + pxor %xmm2, %xmm1 /* G: b = b ^ c */ + pxor %xmm6, %xmm5 /* H: b = b ^ c */ + movdqa %xmm1, %xmm14 /* G: rotate b (0) */ + movdqa %xmm5, %xmm15 /* H: rotate b (0) */ + pslld $7, %xmm1 /* G: rotate b (1) */ + psrld $25, %xmm14 /* G: rotate b (2) */ + pslld $7, %xmm5 /* H: rotate b (1) */ + psrld $25, %xmm15 /* H: rotate b (2) */ + por %xmm14, %xmm1 /* G: rotate b (3) */ + por %xmm15, %xmm5 /* H: rotate b (3) */ + movdqa %xmm5, 64(%rdx) /* H: store b */ + movdqa %xmm1, 112(%rdx) /* G: store b */ + decl %ecx + jnz .xor_chacha_4way + +/* write back data */ + movdqa 0(%rdi), %xmm0 + movdqa 16(%rdi), %xmm1 + movdqa 32(%rdi), %xmm2 + movdqa 48(%rdi), %xmm3 + movdqa 64(%rdi), %xmm4 + movdqa 80(%rdi), %xmm5 + movdqa 96(%rdi), %xmm6 + movdqa 112(%rdi), %xmm7 + movdqa 128(%rdi), %xmm8 + movdqa 144(%rdi), %xmm9 + movdqa 160(%rdi), %xmm10 + movdqa 176(%rdi), %xmm11 + movdqa 192(%rdi), %xmm12 + movdqa 208(%rdi), %xmm13 + movdqa 224(%rdi), %xmm14 + movdqa 240(%rdi), %xmm15 + paddd 0(%rdx), %xmm0 + paddd 16(%rdx), %xmm1 + paddd 32(%rdx), %xmm2 + paddd 48(%rdx), %xmm3 + paddd 64(%rdx), %xmm4 + paddd 80(%rdx), %xmm5 + paddd 96(%rdx), %xmm6 + paddd 112(%rdx), %xmm7 + paddd 128(%rdx), %xmm8 + paddd 144(%rdx), %xmm9 + paddd 160(%rdx), %xmm10 + paddd 176(%rdx), %xmm11 + paddd 192(%rdx), %xmm12 + paddd 208(%rdx), %xmm13 + paddd 224(%rdx), %xmm14 + paddd 240(%rdx), %xmm15 + movdqa %xmm0, 0(%rdi) + movdqa %xmm1, 16(%rdi) + movdqa %xmm2, 32(%rdi) + movdqa %xmm3, 48(%rdi) + movdqa %xmm4, 64(%rdi) + movdqa %xmm5, 80(%rdi) + movdqa %xmm6, 96(%rdi) + movdqa %xmm7, 112(%rdi) + movdqa %xmm8, 128(%rdi) + movdqa %xmm9, 144(%rdi) + movdqa %xmm10, 160(%rdi) + movdqa %xmm11, 176(%rdi) + movdqa %xmm12, 192(%rdi) + movdqa %xmm13, 208(%rdi) + movdqa %xmm14, 224(%rdi) + movdqa %xmm15, 240(%rdi) +#ifdef WIN64 + movq %r10, %rdi + movq %r11, %rsi +#endif + ret + +#endif /* MINER_4WAY */ + +/* cpu_vec_exts() + * AMD64 detector of any processor vector extensions present + * output bits set in %rax: + * 0 : MMX [always true] + * 1 : Extended MMX (MMX+) [always true] + * 2 : 3DNow! + * 3 : Extended 3DNow! (3DNow!+) + * 4 : SSE [always true] + * 5 : SSE2 [always true] + * 6 : SSE3 + * 7 : SSSE3 + * 8 : SSE41 + * 9 : SSE42 + * 10 : SSE4A + * 11 : XOP + * 12 : FMA4 + * 13 : AVX + * 14 : F16C + * 15 : FMA3 + * the other bits are reserved for the future use */ +.globl cpu_vec_exts +.globl _cpu_vec_exts +cpu_vec_exts: +_cpu_vec_exts: + pushq %rbx + pushq %rbp + xorq %rbp, %rbp +/* all AMD64 compatible processors support MMX, MMX+, SSE, SSE2 */ + orl $0x00000033, %ebp +/* the CPUID extended function 0 should report the max. + * supported extended function number in %eax */ + movl $0x80000000, %eax + cpuid + cmpl $0x80000001, %eax + jb .cpu_vec_st1 + movl $0x80000001, %eax + cpuid +/* 3DNow!+ (bit 30 of %edx); implies 3DNow! */ + testl $0x80000000, %edx + jz .cpu_vec_sse4a + orl $0x0000000C, %ebp + jmp .cpu_vec_sse4a +.cpu_vec_sse4a: +/* SSE4A (bit 6 of %ecx) */ + testl $0x00000040, %ecx + jz .cpu_vec_st1 + orl $0x00000400, %ebp +/* XOP (bit 11 of %ecx) */ + testl $0x00000800, %ecx + jz .cpu_vec_st1 + orl $0x00000800, %ebp +/* FMA4 (bit 16 of %ecx) */ + testl $0x00010000, %ecx + jz .cpu_vec_st1 + orl $0x00001000, %ebp +.cpu_vec_st1: +/* the CPUID standard function 1 */ + movl $1, %eax + cpuid +/* SSE3 (bit 0 of %ecx) */ + testl $0x00000001, %ecx + jz .cpu_vec_exit + orl $0x00000040, %ebp +/* SSSE3 (bit 9 of %ecx) */ + testl $0x00000100, %ecx + jz .cpu_vec_exit + orl $0x00000080, %ebp +/* SSE4.1 (bit 19 of %ecx) */ + testl $0x00080000, %ecx + jz .cpu_vec_exit + orl $0x00000100, %ebp +/* SSE4.2 (bit 20 of %ecx) */ + testl $0x00100000, %ecx + jz .cpu_vec_exit + orl $0x00000200, %ebp +/* AVX (bit 28 of %ecx) */ + testl $0x10000000, %ecx + jz .cpu_vec_exit + orl $0x00002000, %ebp + jmp .cpu_vec_exit +/* F16C (bit 29 of %ecx) */ + testl $0x20000000, %ecx + jz .cpu_vec_exit + orl $0x00004000, %ebp + jmp .cpu_vec_exit +/* FMA3 (bit 12 of %ecx) */ + testl $0x00001000, %ecx + jz .cpu_vec_exit + orl $0x00008000, %ebp + +.cpu_vec_exit: + movq %rbp, %rax + popq %rbp + popq %rbx + ret + +#endif /* (ASM) && (__x86_64__) */ + + +#if defined(ASM) && defined(__i386__) + +/* blake2s_compress(mem) + * i386 BLAKE2s block compression */ +.globl blake2s_compress +.globl _blake2s_compress +blake2s_compress: +_blake2s_compress: + pushl %ebx + pushl %ebp + pushl %esi + pushl %edi + movl 20(%esp), %edi + leal -64(%esp), %esi + +/* initialise */ + movl 0(%edi), %eax + movl 4(%edi), %ebx + movl 8(%edi), %ecx + movl 12(%edi), %edx + movl %eax, 0(%esi) + movl %ebx, 4(%esi) + movl %ecx, 8(%esi) + movl %edx, 12(%esi) + movl 16(%edi), %eax + movl 20(%edi), %ebx + movl 24(%edi), %ecx + movl 28(%edi), %edx + movl %eax, 16(%esi) + movl %ebx, 20(%esi) + movl %ecx, 24(%esi) + movl %edx, 28(%esi) + movl $0x6A09E667, %eax + movl $0xBB67AE85, %ebx + movl $0x3C6EF372, %ecx + movl $0xA54FF53A, %edx + movl %eax, 32(%esi) + movl %ebx, 36(%esi) + movl %ecx, 40(%esi) + movl %edx, 44(%esi) + movl 32(%edi), %eax + movl 36(%edi), %ebx + movl 40(%edi), %ecx + movl 44(%edi), %edx + xorl $0x510E527F, %eax + xorl $0x9B05688C, %ebx + xorl $0x1F83D9AB, %ecx + xorl $0x5BE0CD19, %edx + movl %eax, 48(%esi) + movl 0(%esi), %eax /* A */ + movl %ebx, 52(%esi) + movl 16(%esi), %ebx /* A */ + movl %ecx, 56(%esi) + addl 48(%edi), %eax /* A */ + movl %edx, 60(%esi) +/* round 0 (A) */ + movl 48(%esi), %edx + addl %ebx, %eax + movl 32(%esi), %ecx + xorl %eax, %edx + movl 52(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 20(%esi), %ebp /* B */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* B */ + rorl $8, %edx + addl 56(%edi), %eax /* B */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* B */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* B */ + rorl $7, %ebx + movl %ebx, 16(%esi) +/* round 0 (B) */ + addl %ebp, %eax + xorl %eax, %edx + movl 60(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 24(%esi), %ebx /* C */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* C */ + rorl $8, %edx + addl 64(%edi), %eax /* C */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* C */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* C */ + rorl $7, %ebp + movl %ebp, 20(%esi) +/* round 0 (C) */ + addl %ebx, %eax + xorl %eax, %edx + movl 68(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 28(%esi), %ebp /* D */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* D */ + rorl $8, %edx + addl 72(%edi), %eax /* D */ + movl %edx, 56(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* D */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* D */ + rorl $7, %ebx + movl %ebx, 24(%esi) +/* round 0 (D) */ + addl %ebp, %eax + xorl %eax, %edx + movl 76(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 20(%esi), %ebx /* E */ + movl %eax, 12(%esi) + xorl %eax, %edx + movl 0(%esi), %eax /* E */ + rorl $8, %edx + addl 80(%edi), %eax /* E */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* E */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* E */ + rorl $7, %ebp + movl %ebp, 28(%esi) +/* round 0 (E) */ + addl %ebx, %eax + xorl %eax, %edx + movl 84(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 24(%esi), %ebp /* F */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* F */ + rorl $8, %edx + addl 88(%edi), %eax /* F */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 48(%esi), %edx /* F */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* F */ + rorl $7, %ebx + movl %ebx, 20(%esi) +/* round 0 (F) */ + addl %ebp, %eax + xorl %eax, %edx + movl 92(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 28(%esi), %ebx /* G */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* G */ + rorl $8, %edx + addl 96(%edi), %eax /* G */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* G */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 32(%esi), %ecx /* G */ + rorl $7, %ebp + movl %ebp, 24(%esi) +/* round 0 (G) */ + addl %ebx, %eax + xorl %eax, %edx + movl 100(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 16(%esi), %ebp /* H */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* H */ + rorl $8, %edx + addl 104(%edi), %eax /* H */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* H */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* H */ + rorl $7, %ebx + movl %ebx, 28(%esi) +/* round 0 (H) */ + addl %ebp, %eax + xorl %eax, %edx + movl 108(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl %eax, 12(%esi) + xorl %eax, %edx + rorl $8, %edx + movl 0(%esi), %eax /* A */ + movl %edx, 56(%esi) + addl %edx, %ecx + addl 104(%edi), %eax /* A */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 48(%esi), %edx /* A */ + rorl $7, %ebp + movl %ebp, %ebx +/* round 1 (A) */ + addl %ebx, %eax + movl 32(%esi), %ecx + xorl %eax, %edx + movl 88(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 20(%esi), %ebp /* B */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* B */ + rorl $8, %edx + addl 64(%edi), %eax /* B */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* B */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* B */ + rorl $7, %ebx + movl %ebx, 16(%esi) +/* round 1 (B) */ + addl %ebp, %eax + xorl %eax, %edx + movl 80(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 24(%esi), %ebx /* C */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* C */ + rorl $8, %edx + addl 84(%edi), %eax /* C */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* C */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* C */ + rorl $7, %ebp + movl %ebp, 20(%esi) +/* round 1 (C) */ + addl %ebx, %eax + xorl %eax, %edx + movl 108(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 28(%esi), %ebp /* D */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* D */ + rorl $8, %edx + addl 100(%edi), %eax /* D */ + movl %edx, 56(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* D */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* D */ + rorl $7, %ebx + movl %ebx, 24(%esi) +/* round 1 (D) */ + addl %ebp, %eax + xorl %eax, %edx + movl 72(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 20(%esi), %ebx /* E */ + movl %eax, 12(%esi) + xorl %eax, %edx + movl 0(%esi), %eax /* E */ + rorl $8, %edx + addl 52(%edi), %eax /* E */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* E */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* E */ + rorl $7, %ebp + movl %ebp, 28(%esi) +/* round 1 (E) */ + addl %ebx, %eax + xorl %eax, %edx + movl 96(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 24(%esi), %ebp /* F */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* F */ + rorl $8, %edx + addl 48(%edi), %eax /* F */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 48(%esi), %edx /* F */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* F */ + rorl $7, %ebx + movl %ebx, 20(%esi) +/* round 1 (F) */ + addl %ebp, %eax + xorl %eax, %edx + movl 56(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 28(%esi), %ebx /* G */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* G */ + rorl $8, %edx + addl 92(%edi), %eax /* G */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* G */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 32(%esi), %ecx /* G */ + rorl $7, %ebp + movl %ebp, 24(%esi) +/* round 1 (G) */ + addl %ebx, %eax + xorl %eax, %edx + movl 76(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 16(%esi), %ebp /* H */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* H */ + rorl $8, %edx + addl 68(%edi), %eax /* H */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* H */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* H */ + rorl $7, %ebx + movl %ebx, 28(%esi) +/* round 1 (H) */ + addl %ebp, %eax + xorl %eax, %edx + movl 60(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl %eax, 12(%esi) + xorl %eax, %edx + rorl $8, %edx + movl 0(%esi), %eax /* A */ + movl %edx, 56(%esi) + addl %edx, %ecx + addl 92(%edi), %eax /* A */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 48(%esi), %edx /* A */ + rorl $7, %ebp + movl %ebp, %ebx +/* round 2 (A) */ + addl %ebx, %eax + movl 32(%esi), %ecx + xorl %eax, %edx + movl 80(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 20(%esi), %ebp /* B */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* B */ + rorl $8, %edx + addl 96(%edi), %eax /* B */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* B */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* B */ + rorl $7, %ebx + movl %ebx, 16(%esi) +/* round 2 (B) */ + addl %ebp, %eax + xorl %eax, %edx + movl 48(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 24(%esi), %ebx /* C */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* C */ + rorl $8, %edx + addl 68(%edi), %eax /* C */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* C */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* C */ + rorl $7, %ebp + movl %ebp, 20(%esi) +/* round 2 (C) */ + addl %ebx, %eax + xorl %eax, %edx + movl 56(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 28(%esi), %ebp /* D */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* D */ + rorl $8, %edx + addl 108(%edi), %eax /* D */ + movl %edx, 56(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* D */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* D */ + rorl $7, %ebx + movl %ebx, 24(%esi) +/* round 2 (D) */ + addl %ebp, %eax + xorl %eax, %edx + movl 100(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 20(%esi), %ebx /* E */ + movl %eax, 12(%esi) + xorl %eax, %edx + movl 0(%esi), %eax /* E */ + rorl $8, %edx + addl 88(%edi), %eax /* E */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* E */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* E */ + rorl $7, %ebp + movl %ebp, 28(%esi) +/* round 2 (E) */ + addl %ebx, %eax + xorl %eax, %edx + movl 104(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 24(%esi), %ebp /* F */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* F */ + rorl $8, %edx + addl 60(%edi), %eax /* F */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 48(%esi), %edx /* F */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* F */ + rorl $7, %ebx + movl %ebx, 20(%esi) +/* round 2 (F) */ + addl %ebp, %eax + xorl %eax, %edx + movl 72(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 28(%esi), %ebx /* G */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* G */ + rorl $8, %edx + addl 76(%edi), %eax /* G */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* G */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 32(%esi), %ecx /* G */ + rorl $7, %ebp + movl %ebp, 24(%esi) +/* round 2 (G) */ + addl %ebx, %eax + xorl %eax, %edx + movl 52(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 16(%esi), %ebp /* H */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* H */ + rorl $8, %edx + addl 84(%edi), %eax /* H */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* H */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* H */ + rorl $7, %ebx + movl %ebx, 28(%esi) +/* round 2 (H) */ + addl %ebp, %eax + xorl %eax, %edx + movl 64(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl %eax, 12(%esi) + xorl %eax, %edx + rorl $8, %edx + movl 0(%esi), %eax /* A */ + movl %edx, 56(%esi) + addl %edx, %ecx + addl 76(%edi), %eax /* A */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 48(%esi), %edx /* A */ + rorl $7, %ebp + movl %ebp, %ebx +/* round 3 (A) */ + addl %ebx, %eax + movl 32(%esi), %ecx + xorl %eax, %edx + movl 84(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 20(%esi), %ebp /* B */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* B */ + rorl $8, %edx + addl 60(%edi), %eax /* B */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* B */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* B */ + rorl $7, %ebx + movl %ebx, 16(%esi) +/* round 3 (B) */ + addl %ebp, %eax + xorl %eax, %edx + movl 52(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 24(%esi), %ebx /* C */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* C */ + rorl $8, %edx + addl 100(%edi), %eax /* C */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* C */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* C */ + rorl $7, %ebp + movl %ebp, 20(%esi) +/* round 3 (C) */ + addl %ebx, %eax + xorl %eax, %edx + movl 96(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 28(%esi), %ebp /* D */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* D */ + rorl $8, %edx + addl 92(%edi), %eax /* D */ + movl %edx, 56(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* D */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* D */ + rorl $7, %ebx + movl %ebx, 24(%esi) +/* round 3 (D) */ + addl %ebp, %eax + xorl %eax, %edx + movl 104(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 20(%esi), %ebx /* E */ + movl %eax, 12(%esi) + xorl %eax, %edx + movl 0(%esi), %eax /* E */ + rorl $8, %edx + addl 56(%edi), %eax /* E */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* E */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* E */ + rorl $7, %ebp + movl %ebp, 28(%esi) +/* round 3 (E) */ + addl %ebx, %eax + xorl %eax, %edx + movl 72(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 24(%esi), %ebp /* F */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* F */ + rorl $8, %edx + addl 68(%edi), %eax /* F */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 48(%esi), %edx /* F */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* F */ + rorl $7, %ebx + movl %ebx, 20(%esi) +/* round 3 (F) */ + addl %ebp, %eax + xorl %eax, %edx + movl 88(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 28(%esi), %ebx /* G */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* G */ + rorl $8, %edx + addl 64(%edi), %eax /* G */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* G */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 32(%esi), %ecx /* G */ + rorl $7, %ebp + movl %ebp, 24(%esi) +/* round 3 (G) */ + addl %ebx, %eax + xorl %eax, %edx + movl 48(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 16(%esi), %ebp /* H */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* H */ + rorl $8, %edx + addl 108(%edi), %eax /* H */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* H */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* H */ + rorl $7, %ebx + movl %ebx, 28(%esi) +/* round 3 (H) */ + addl %ebp, %eax + xorl %eax, %edx + movl 80(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl %eax, 12(%esi) + xorl %eax, %edx + rorl $8, %edx + movl 0(%esi), %eax /* A */ + movl %edx, 56(%esi) + addl %edx, %ecx + addl 84(%edi), %eax /* A */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 48(%esi), %edx /* A */ + rorl $7, %ebp + movl %ebp, %ebx +/* round 4 (A) */ + addl %ebx, %eax + movl 32(%esi), %ecx + xorl %eax, %edx + movl 48(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 20(%esi), %ebp /* B */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* B */ + rorl $8, %edx + addl 68(%edi), %eax /* B */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* B */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* B */ + rorl $7, %ebx + movl %ebx, 16(%esi) +/* round 4 (B) */ + addl %ebp, %eax + xorl %eax, %edx + movl 76(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 24(%esi), %ebx /* C */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* C */ + rorl $8, %edx + addl 56(%edi), %eax /* C */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* C */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* C */ + rorl $7, %ebp + movl %ebp, 20(%esi) +/* round 4 (C) */ + addl %ebx, %eax + xorl %eax, %edx + movl 64(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 28(%esi), %ebp /* D */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* D */ + rorl $8, %edx + addl 88(%edi), %eax /* D */ + movl %edx, 56(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* D */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* D */ + rorl $7, %ebx + movl %ebx, 24(%esi) +/* round 4 (D) */ + addl %ebp, %eax + xorl %eax, %edx + movl 108(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 20(%esi), %ebx /* E */ + movl %eax, 12(%esi) + xorl %eax, %edx + movl 0(%esi), %eax /* E */ + rorl $8, %edx + addl 104(%edi), %eax /* E */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* E */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* E */ + rorl $7, %ebp + movl %ebp, 28(%esi) +/* round 4 (E) */ + addl %ebx, %eax + xorl %eax, %edx + movl 52(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 24(%esi), %ebp /* F */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* F */ + rorl $8, %edx + addl 92(%edi), %eax /* F */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 48(%esi), %edx /* F */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* F */ + rorl $7, %ebx + movl %ebx, 20(%esi) +/* round 4 (F) */ + addl %ebp, %eax + xorl %eax, %edx + movl 96(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 28(%esi), %ebx /* G */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* G */ + rorl $8, %edx + addl 72(%edi), %eax /* G */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* G */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 32(%esi), %ecx /* G */ + rorl $7, %ebp + movl %ebp, 24(%esi) +/* round 4 (G) */ + addl %ebx, %eax + xorl %eax, %edx + movl 80(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 16(%esi), %ebp /* H */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* H */ + rorl $8, %edx + addl 60(%edi), %eax /* H */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* H */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* H */ + rorl $7, %ebx + movl %ebx, 28(%esi) +/* round 4 (H) */ + addl %ebp, %eax + xorl %eax, %edx + movl 100(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl %eax, 12(%esi) + xorl %eax, %edx + rorl $8, %edx + movl 0(%esi), %eax /* A */ + movl %edx, 56(%esi) + addl %edx, %ecx + addl 56(%edi), %eax /* A */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 48(%esi), %edx /* A */ + rorl $7, %ebp + movl %ebp, %ebx +/* round 5 (A) */ + addl %ebx, %eax + movl 32(%esi), %ecx + xorl %eax, %edx + movl 96(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 20(%esi), %ebp /* B */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* B */ + rorl $8, %edx + addl 72(%edi), %eax /* B */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* B */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* B */ + rorl $7, %ebx + movl %ebx, 16(%esi) +/* round 5 (B) */ + addl %ebp, %eax + xorl %eax, %edx + movl 88(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 24(%esi), %ebx /* C */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* C */ + rorl $8, %edx + addl 48(%edi), %eax /* C */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* C */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* C */ + rorl $7, %ebp + movl %ebp, 20(%esi) +/* round 5 (C) */ + addl %ebx, %eax + xorl %eax, %edx + movl 92(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 28(%esi), %ebp /* D */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* D */ + rorl $8, %edx + addl 80(%edi), %eax /* D */ + movl %edx, 56(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* D */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* D */ + rorl $7, %ebx + movl %ebx, 24(%esi) +/* round 5 (D) */ + addl %ebp, %eax + xorl %eax, %edx + movl 60(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 20(%esi), %ebx /* E */ + movl %eax, 12(%esi) + xorl %eax, %edx + movl 0(%esi), %eax /* E */ + rorl $8, %edx + addl 64(%edi), %eax /* E */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* E */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* E */ + rorl $7, %ebp + movl %ebp, 28(%esi) +/* round 5 (E) */ + addl %ebx, %eax + xorl %eax, %edx + movl 100(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 24(%esi), %ebp /* F */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* F */ + rorl $8, %edx + addl 76(%edi), %eax /* F */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 48(%esi), %edx /* F */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* F */ + rorl $7, %ebx + movl %ebx, 20(%esi) +/* round 5 (F) */ + addl %ebp, %eax + xorl %eax, %edx + movl 68(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 28(%esi), %ebx /* G */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* G */ + rorl $8, %edx + addl 108(%edi), %eax /* G */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* G */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 32(%esi), %ecx /* G */ + rorl $7, %ebp + movl %ebp, 24(%esi) +/* round 5 (G) */ + addl %ebx, %eax + xorl %eax, %edx + movl 104(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 16(%esi), %ebp /* H */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* H */ + rorl $8, %edx + addl 52(%edi), %eax /* H */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* H */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* H */ + rorl $7, %ebx + movl %ebx, 28(%esi) +/* round 5 (H) */ + addl %ebp, %eax + xorl %eax, %edx + movl 84(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl %eax, 12(%esi) + xorl %eax, %edx + rorl $8, %edx + movl 0(%esi), %eax /* A */ + movl %edx, 56(%esi) + addl %edx, %ecx + addl 96(%edi), %eax /* A */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 48(%esi), %edx /* A */ + rorl $7, %ebp + movl %ebp, %ebx +/* round 6 (A) */ + addl %ebx, %eax + movl 32(%esi), %ecx + xorl %eax, %edx + movl 68(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 20(%esi), %ebp /* B */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* B */ + rorl $8, %edx + addl 52(%edi), %eax /* B */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* B */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* B */ + rorl $7, %ebx + movl %ebx, 16(%esi) +/* round 6 (B) */ + addl %ebp, %eax + xorl %eax, %edx + movl 108(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 24(%esi), %ebx /* C */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* C */ + rorl $8, %edx + addl 104(%edi), %eax /* C */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* C */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* C */ + rorl $7, %ebp + movl %ebp, 20(%esi) +/* round 6 (C) */ + addl %ebx, %eax + xorl %eax, %edx + movl 100(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 28(%esi), %ebp /* D */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* D */ + rorl $8, %edx + addl 64(%edi), %eax /* D */ + movl %edx, 56(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* D */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* D */ + rorl $7, %ebx + movl %ebx, 24(%esi) +/* round 6 (D) */ + addl %ebp, %eax + xorl %eax, %edx + movl 88(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 20(%esi), %ebx /* E */ + movl %eax, 12(%esi) + xorl %eax, %edx + movl 0(%esi), %eax /* E */ + rorl $8, %edx + addl 48(%edi), %eax /* E */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* E */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* E */ + rorl $7, %ebp + movl %ebp, 28(%esi) +/* round 6 (E) */ + addl %ebx, %eax + xorl %eax, %edx + movl 76(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 24(%esi), %ebp /* F */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* F */ + rorl $8, %edx + addl 72(%edi), %eax /* F */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 48(%esi), %edx /* F */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* F */ + rorl $7, %ebx + movl %ebx, 20(%esi) +/* round 6 (F) */ + addl %ebp, %eax + xorl %eax, %edx + movl 60(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 28(%esi), %ebx /* G */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* G */ + rorl $8, %edx + addl 84(%edi), %eax /* G */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* G */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 32(%esi), %ecx /* G */ + rorl $7, %ebp + movl %ebp, 24(%esi) +/* round 6 (G) */ + addl %ebx, %eax + xorl %eax, %edx + movl 56(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 16(%esi), %ebp /* H */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* H */ + rorl $8, %edx + addl 80(%edi), %eax /* H */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* H */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* H */ + rorl $7, %ebx + movl %ebx, 28(%esi) +/* round 6 (H) */ + addl %ebp, %eax + xorl %eax, %edx + movl 92(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl %eax, 12(%esi) + xorl %eax, %edx + rorl $8, %edx + movl 0(%esi), %eax /* A */ + movl %edx, 56(%esi) + addl %edx, %ecx + addl 100(%edi), %eax /* A */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 48(%esi), %edx /* A */ + rorl $7, %ebp + movl %ebp, %ebx +/* round 7 (A) */ + addl %ebx, %eax + movl 32(%esi), %ecx + xorl %eax, %edx + movl 92(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 20(%esi), %ebp /* B */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* B */ + rorl $8, %edx + addl 76(%edi), %eax /* B */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* B */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* B */ + rorl $7, %ebx + movl %ebx, 16(%esi) +/* round 7 (B) */ + addl %ebp, %eax + xorl %eax, %edx + movl 104(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 24(%esi), %ebx /* C */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* C */ + rorl $8, %edx + addl 96(%edi), %eax /* C */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* C */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* C */ + rorl $7, %ebp + movl %ebp, 20(%esi) +/* round 7 (C) */ + addl %ebx, %eax + xorl %eax, %edx + movl 52(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 28(%esi), %ebp /* D */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* D */ + rorl $8, %edx + addl 60(%edi), %eax /* D */ + movl %edx, 56(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* D */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* D */ + rorl $7, %ebx + movl %ebx, 24(%esi) +/* round 7 (D) */ + addl %ebp, %eax + xorl %eax, %edx + movl 84(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 20(%esi), %ebx /* E */ + movl %eax, 12(%esi) + xorl %eax, %edx + movl 0(%esi), %eax /* E */ + rorl $8, %edx + addl 68(%edi), %eax /* E */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* E */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* E */ + rorl $7, %ebp + movl %ebp, 28(%esi) +/* round 7 (E) */ + addl %ebx, %eax + xorl %eax, %edx + movl 48(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 24(%esi), %ebp /* F */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* F */ + rorl $8, %edx + addl 108(%edi), %eax /* F */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 48(%esi), %edx /* F */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* F */ + rorl $7, %ebx + movl %ebx, 20(%esi) +/* round 7 (F) */ + addl %ebp, %eax + xorl %eax, %edx + movl 64(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 28(%esi), %ebx /* G */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* G */ + rorl $8, %edx + addl 80(%edi), %eax /* G */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* G */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 32(%esi), %ecx /* G */ + rorl $7, %ebp + movl %ebp, 24(%esi) +/* round 7 (G) */ + addl %ebx, %eax + xorl %eax, %edx + movl 72(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 16(%esi), %ebp /* H */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* H */ + rorl $8, %edx + addl 56(%edi), %eax /* H */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* H */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* H */ + rorl $7, %ebx + movl %ebx, 28(%esi) +/* round 7 (H) */ + addl %ebp, %eax + xorl %eax, %edx + movl 88(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl %eax, 12(%esi) + xorl %eax, %edx + rorl $8, %edx + movl 0(%esi), %eax /* A */ + movl %edx, 56(%esi) + addl %edx, %ecx + addl 72(%edi), %eax /* A */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 48(%esi), %edx /* A */ + rorl $7, %ebp + movl %ebp, %ebx +/* round 8 (A) */ + addl %ebx, %eax + movl 32(%esi), %ecx + xorl %eax, %edx + movl 108(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 20(%esi), %ebp /* B */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* B */ + rorl $8, %edx + addl 104(%edi), %eax /* B */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* B */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* B */ + rorl $7, %ebx + movl %ebx, 16(%esi) +/* round 8 (B) */ + addl %ebp, %eax + xorl %eax, %edx + movl 84(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 24(%esi), %ebx /* C */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* C */ + rorl $8, %edx + addl 92(%edi), %eax /* C */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* C */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* C */ + rorl $7, %ebp + movl %ebp, 20(%esi) +/* round 8 (C) */ + addl %ebx, %eax + xorl %eax, %edx + movl 60(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 28(%esi), %ebp /* D */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* D */ + rorl $8, %edx + addl 48(%edi), %eax /* D */ + movl %edx, 56(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* D */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* D */ + rorl $7, %ebx + movl %ebx, 24(%esi) +/* round 8 (D) */ + addl %ebp, %eax + xorl %eax, %edx + movl 80(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 20(%esi), %ebx /* E */ + movl %eax, 12(%esi) + xorl %eax, %edx + movl 0(%esi), %eax /* E */ + rorl $8, %edx + addl 96(%edi), %eax /* E */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* E */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* E */ + rorl $7, %ebp + movl %ebp, 28(%esi) +/* round 8 (E) */ + addl %ebx, %eax + xorl %eax, %edx + movl 56(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 24(%esi), %ebp /* F */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* F */ + rorl $8, %edx + addl 100(%edi), %eax /* F */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 48(%esi), %edx /* F */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* F */ + rorl $7, %ebx + movl %ebx, 20(%esi) +/* round 8 (F) */ + addl %ebp, %eax + xorl %eax, %edx + movl 76(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 28(%esi), %ebx /* G */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* G */ + rorl $8, %edx + addl 52(%edi), %eax /* G */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* G */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 32(%esi), %ecx /* G */ + rorl $7, %ebp + movl %ebp, 24(%esi) +/* round 8 (G) */ + addl %ebx, %eax + xorl %eax, %edx + movl 64(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 16(%esi), %ebp /* H */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* H */ + rorl $8, %edx + addl 88(%edi), %eax /* H */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* H */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* H */ + rorl $7, %ebx + movl %ebx, 28(%esi) +/* round 8 (H) */ + addl %ebp, %eax + xorl %eax, %edx + movl 68(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl %eax, 12(%esi) + xorl %eax, %edx + rorl $8, %edx + movl 0(%esi), %eax /* A */ + movl %edx, 56(%esi) + addl %edx, %ecx + addl 88(%edi), %eax /* A */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 48(%esi), %edx /* A */ + rorl $7, %ebp + movl %ebp, %ebx +/* round 9 (A) */ + addl %ebx, %eax + movl 32(%esi), %ecx + xorl %eax, %edx + movl 56(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 20(%esi), %ebp /* B */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* B */ + rorl $8, %edx + addl 80(%edi), %eax /* B */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* B */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* B */ + rorl $7, %ebx + movl %ebx, 16(%esi) +/* round 9 (B) */ + addl %ebp, %eax + xorl %eax, %edx + movl 64(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 24(%esi), %ebx /* C */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* C */ + rorl $8, %edx + addl 76(%edi), %eax /* C */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* C */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* C */ + rorl $7, %ebp + movl %ebp, 20(%esi) +/* round 9 (C) */ + addl %ebx, %eax + xorl %eax, %edx + movl 72(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 28(%esi), %ebp /* D */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* D */ + rorl $8, %edx + addl 52(%edi), %eax /* D */ + movl %edx, 56(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* D */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* D */ + rorl $7, %ebx + movl %ebx, 24(%esi) +/* round 9 (D) */ + addl %ebp, %eax + xorl %eax, %edx + movl 68(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 20(%esi), %ebx /* E */ + movl %eax, 12(%esi) + xorl %eax, %edx + movl 0(%esi), %eax /* E */ + rorl $8, %edx + addl 108(%edi), %eax /* E */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 60(%esi), %edx /* E */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 40(%esi), %ecx /* E */ + rorl $7, %ebp + movl %ebp, 28(%esi) +/* round 9 (E) */ + addl %ebx, %eax + xorl %eax, %edx + movl 92(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 24(%esi), %ebp /* F */ + movl %eax, 0(%esi) + xorl %eax, %edx + movl 4(%esi), %eax /* F */ + rorl $8, %edx + addl 84(%edi), %eax /* F */ + movl %edx, 60(%esi) + addl %edx, %ecx + movl 48(%esi), %edx /* F */ + movl %ecx, 40(%esi) + xorl %ecx, %ebx + movl 44(%esi), %ecx /* F */ + rorl $7, %ebx + movl %ebx, 20(%esi) +/* round 9 (F) */ + addl %ebp, %eax + xorl %eax, %edx + movl 104(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + movl 28(%esi), %ebx /* G */ + movl %eax, 4(%esi) + xorl %eax, %edx + movl 8(%esi), %eax /* G */ + rorl $8, %edx + addl 60(%edi), %eax /* G */ + movl %edx, 48(%esi) + addl %edx, %ecx + movl 52(%esi), %edx /* G */ + movl %ecx, 44(%esi) + xorl %ecx, %ebp + movl 32(%esi), %ecx /* G */ + rorl $7, %ebp + movl %ebp, 24(%esi) +/* round 9 (G) */ + addl %ebx, %eax + xorl %eax, %edx + movl 96(%edi), %ebp + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebx + rorl $12, %ebx + addl %ebp, %eax + addl %ebx, %eax + movl 16(%esi), %ebp /* H */ + movl %eax, 8(%esi) + xorl %eax, %edx + movl 12(%esi), %eax /* H */ + rorl $8, %edx + addl 100(%edi), %eax /* H */ + movl %edx, 52(%esi) + addl %edx, %ecx + movl 56(%esi), %edx /* H */ + movl %ecx, 32(%esi) + xorl %ecx, %ebx + movl 36(%esi), %ecx /* H */ + rorl $7, %ebx + movl %ebx, 28(%esi) +/* round 9 (H) */ + addl %ebp, %eax + xorl %eax, %edx + movl 48(%edi), %ebx + rorl $16, %edx + addl %edx, %ecx + xorl %ecx, %ebp + rorl $12, %ebp + addl %ebx, %eax + addl %ebp, %eax + xorl %eax, %edx + rorl $8, %edx + movl 8(%esi), %ebx /* finalise */ + movl %edx, 56(%esi) + addl %edx, %ecx + movl 0(%esi), %edx /* finalise */ + movl %ecx, 36(%esi) + xorl %ecx, %ebp + movl 4(%esi), %ecx /* finalise */ + rorl $7, %ebp + movl %ebp, 16(%esi) +/* finalise */ + xorl 32(%esi), %edx + xorl 36(%esi), %ecx + xorl 40(%esi), %ebx + xorl 44(%esi), %eax + xorl 0(%edi), %edx + xorl 4(%edi), %ecx + xorl 8(%edi), %ebx + xorl 12(%edi), %eax + movl %edx, 0(%edi) + movl %ecx, 4(%edi) + movl %ebx, 8(%edi) + movl %eax, 12(%edi) + movl 16(%esi), %eax + movl 20(%esi), %ebx + movl 24(%esi), %ecx + movl 28(%esi), %edx + xorl 48(%esi), %eax + xorl 52(%esi), %ebx + xorl 56(%esi), %ecx + xorl 60(%esi), %edx + xorl 16(%edi), %eax + xorl 20(%edi), %ebx + xorl 24(%edi), %ecx + xorl 28(%edi), %edx + movl %eax, 16(%edi) + movl %ebx, 20(%edi) + movl %ecx, 24(%edi) + movl %edx, 28(%edi) + + popl %edi + popl %esi + popl %ebp + popl %ebx + ret + + +/* neoscrypt_copy(dst, src, len) + * i386 memcpy() */ +.globl neoscrypt_copy +.globl _neoscrypt_copy +neoscrypt_copy: +_neoscrypt_copy: + pushl %ebx + pushl %ebp + pushl %edi + pushl %esi + movl 20(%esp), %edi + movl 24(%esp), %esi + movl 28(%esp), %ecx + shrl $4, %ecx + xorl %eax, %eax + cmpl %eax, %ecx + jz .copy_tail +.copy_16b: + movl 0(%esi), %eax + movl 4(%esi), %edx + movl 8(%esi), %ebx + movl 12(%esi), %ebp + movl %eax, 0(%edi) + movl %edx, 4(%edi) + movl %ebx, 8(%edi) + movl %ebp, 12(%edi) + addl $16, %esi + addl $16, %edi + decl %ecx + jnz .copy_16b + +.copy_tail: + xorl %eax, %eax + movl 28(%esp), %ecx + andl $0xF, %ecx + cmpl %eax, %ecx + jz .copy_finish + movb %cl, %ch + andb $0x3, %cl + shrb $2, %ch + cmpb %ah, %ch + jz .copy_1b +.copy_4b: + movl 0(%esi), %edx + movl %edx, 0(%edi) + addl $4, %esi + addl $4, %edi + decb %ch + jnz .copy_4b + + cmpb %al, %cl + jz .copy_finish +.copy_1b: + movb 0(%esi), %dl + movb %dl, 0(%edi) + incl %esi + incl %edi + decb %cl + jnz .copy_1b + +.copy_finish: + popl %esi + popl %edi + popl %ebp + popl %ebx + ret + + +/* neoscrypt_erase(dst, len) + * i386 memory eraser */ +.globl neoscrypt_erase +.globl _neoscrypt_erase +neoscrypt_erase: +_neoscrypt_erase: + movl 4(%esp), %edx + movl 8(%esp), %ecx + shrl $4, %ecx + xorl %eax, %eax + cmpl %eax, %ecx + jz .erase_tail +.erase_16b: + movl %eax, 0(%edx) + movl %eax, 4(%edx) + movl %eax, 8(%edx) + movl %eax, 12(%edx) + addl $16, %edx + decl %ecx + jnz .erase_16b + +.erase_tail: + movl 8(%esp), %ecx + andl $0xF, %ecx + cmpl %eax, %ecx + jz .erase_finish + movb %cl, %ch + andb $0x3, %cl + shrb $2, %ch + cmpb %ah, %ch + jz .erase_1b +.erase_4b: + movl %eax, 0(%edx) + addl $4, %edx + decb %ch + jnz .erase_4b + + cmpb %al, %cl + jz .erase_finish +.erase_1b: + movb %al, 0(%edx) + incl %edx + decb %cl + jnz .erase_1b + +.erase_finish: + ret + + +/* neoscrypt_xor(dst, src, len) + * i386 XOR engine */ +.globl neoscrypt_xor +.globl _neoscrypt_xor +neoscrypt_xor: +_neoscrypt_xor: + pushl %ebx + pushl %ebp + pushl %edi + pushl %esi + movl 20(%esp), %edi + movl 24(%esp), %esi + movl 28(%esp), %ecx + shrl $4, %ecx + xorl %eax, %eax + cmpl %eax, %ecx + jz .xor_tail +.xor_16b: + movl 0(%edi), %eax + movl 4(%edi), %edx + movl 8(%edi), %ebx + movl 12(%edi), %ebp + xorl 0(%esi), %eax + xorl 4(%esi), %edx + xorl 8(%esi), %ebx + xorl 12(%esi), %ebp + movl %eax, 0(%edi) + movl %edx, 4(%edi) + movl %ebx, 8(%edi) + movl %ebp, 12(%edi) + addl $16, %esi + addl $16, %edi + decl %ecx + jnz .xor_16b + +.xor_tail: + xorl %eax, %eax + movl 28(%esp), %ecx + andl $0xF, %ecx + cmpl %eax, %ecx + jz .xor_finish + movb %cl, %ch + andb $0x3, %cl + shrb $2, %ch + cmpb %ah, %ch + jz .xor_1b +.xor_4b: + movl 0(%edi), %edx + xorl 0(%esi), %edx + movl %edx, 0(%edi) + addl $4, %esi + addl $4, %edi + decb %ch + jnz .xor_4b + + cmpb %al, %cl + jz .xor_finish +.xor_1b: + movb 0(%edi), %dl + xorb 0(%esi), %dl + movb %dl, 0(%edi) + incl %esi + incl %edi + decb %cl + jnz .xor_1b + +.xor_finish: + popl %esi + popl %edi + popl %ebp + popl %ebx + ret + + +/* neoscrypt_fastkdf_opt(password, salt, output, output_len) + * i386 (MMX) FastKDF optimised */ +.globl neoscrypt_fastkdf_opt +.globl _neoscrypt_fastkdf_opt +neoscrypt_fastkdf_opt: +_neoscrypt_fastkdf_opt: + pushl %ebx + pushl %ebp + pushl %esi + pushl %edi + +/* 32 bytes (call stack and local variables + 64 bytes (alignment space) + + * 320 bytes (password buffer) + 288 bytes (salt buffer) + 112 bytes (BLAKE2s + * space) = 816 bytes */ + subl $816, %esp + leal 96(%esp), %ebp + andl $0xFFFFFFC0, %ebp + movl %ebp, 28(%esp) + + movl 836(%esp), %edx + movq 0(%edx), %mm0 + movq 8(%edx), %mm1 + movq 16(%edx), %mm2 + movq 24(%edx), %mm3 + movq 32(%edx), %mm4 + movq 40(%edx), %mm5 + movq 48(%edx), %mm6 + movq 56(%edx), %mm7 + movq %mm0, 0(%ebp) + movq %mm1, 8(%ebp) + movq %mm2, 16(%ebp) + movq %mm3, 24(%ebp) + movq %mm4, 32(%ebp) + movq %mm5, 40(%ebp) + movq %mm6, 48(%ebp) + movq %mm7, 56(%ebp) + movq %mm0, 80(%ebp) + movq %mm1, 88(%ebp) + movq %mm2, 96(%ebp) + movq %mm3, 104(%ebp) + movq %mm4, 112(%ebp) + movq %mm5, 120(%ebp) + movq %mm6, 128(%ebp) + movq %mm7, 136(%ebp) + movq %mm0, 160(%ebp) + movq %mm1, 168(%ebp) + movq %mm2, 176(%ebp) + movq %mm3, 184(%ebp) + movq %mm4, 192(%ebp) + movq %mm5, 200(%ebp) + movq %mm6, 208(%ebp) + movq %mm7, 216(%ebp) + movq %mm0, 240(%ebp) + movq %mm1, 248(%ebp) + movq %mm0, 256(%ebp) + movq %mm1, 264(%ebp) + movq %mm2, 272(%ebp) + movq %mm3, 280(%ebp) + movq %mm4, 288(%ebp) + movq %mm5, 296(%ebp) + movq %mm6, 304(%ebp) + movq %mm7, 312(%ebp) + movq 64(%edx), %mm0 + movq 72(%edx), %mm1 + movq %mm0, 64(%ebp) + movq %mm1, 72(%ebp) + movq %mm0, 144(%ebp) + movq %mm1, 152(%ebp) + movq %mm0, 224(%ebp) + movq %mm1, 232(%ebp) + + movl 840(%esp), %edx + leal 320(%ebp), %ebx + movl $32, 20(%esp) + xorl %edi, %edi + testl $0x01, 848(%esp) + jnz .fastkdf_mode_one + + movl $256, 24(%esp) + movq 0(%edx), %mm0 + movq 8(%edx), %mm1 + movq 16(%edx), %mm2 + movq 24(%edx), %mm3 + movq 32(%edx), %mm4 + movq 40(%edx), %mm5 + movq 48(%edx), %mm6 + movq 56(%edx), %mm7 + movq %mm0, 0(%ebx) + movq %mm1, 8(%ebx) + movq %mm2, 16(%ebx) + movq %mm3, 24(%ebx) + movq %mm4, 32(%ebx) + movq %mm5, 40(%ebx) + movq %mm6, 48(%ebx) + movq %mm7, 56(%ebx) + movq %mm0, 80(%ebx) + movq %mm1, 88(%ebx) + movq %mm2, 96(%ebx) + movq %mm3, 104(%ebx) + movq %mm4, 112(%ebx) + movq %mm5, 120(%ebx) + movq %mm6, 128(%ebx) + movq %mm7, 136(%ebx) + movq %mm0, 160(%ebx) + movq %mm1, 168(%ebx) + movq %mm2, 176(%ebx) + movq %mm3, 184(%ebx) + movq %mm4, 192(%ebx) + movq %mm5, 200(%ebx) + movq %mm6, 208(%ebx) + movq %mm7, 216(%ebx) + movq %mm0, 240(%ebx) + movq %mm1, 248(%ebx) + movq %mm0, 256(%ebx) + movq %mm1, 264(%ebx) + movq %mm2, 272(%ebx) + movq %mm3, 280(%ebx) + movq 64(%edx), %mm0 + movq 72(%edx), %mm1 + movq %mm0, 64(%ebx) + movq %mm1, 72(%ebx) + movq %mm0, 144(%ebx) + movq %mm1, 152(%ebx) + movq %mm0, 224(%ebx) + movq %mm1, 232(%ebx) + jmp .fastkdf_loop + +.fastkdf_mode_one: + movl $32, 24(%esp) + movq 0(%edx), %mm0 + movq 8(%edx), %mm1 + movq 16(%edx), %mm2 + movq 24(%edx), %mm3 + movq 32(%edx), %mm4 + movq 40(%edx), %mm5 + movq 48(%edx), %mm6 + movq 56(%edx), %mm7 + movq %mm0, 0(%ebx) + movq %mm1, 8(%ebx) + movq %mm2, 16(%ebx) + movq %mm3, 24(%ebx) + movq %mm4, 32(%ebx) + movq %mm5, 40(%ebx) + movq %mm6, 48(%ebx) + movq %mm7, 56(%ebx) + movq %mm0, 256(%ebx) + movq %mm1, 264(%ebx) + movq %mm2, 272(%ebx) + movq %mm3, 280(%ebx) + movq 64(%edx), %mm0 + movq 72(%edx), %mm1 + movq 80(%edx), %mm2 + movq 88(%edx), %mm3 + movq 96(%edx), %mm4 + movq 104(%edx), %mm5 + movq 112(%edx), %mm6 + movq 120(%edx), %mm7 + movq %mm0, 64(%ebx) + movq %mm1, 72(%ebx) + movq %mm2, 80(%ebx) + movq %mm3, 88(%ebx) + movq %mm4, 96(%ebx) + movq %mm5, 104(%ebx) + movq %mm6, 112(%ebx) + movq %mm7, 120(%ebx) + movq 128(%edx), %mm0 + movq 136(%edx), %mm1 + movq 144(%edx), %mm2 + movq 152(%edx), %mm3 + movq 160(%edx), %mm4 + movq 168(%edx), %mm5 + movq 176(%edx), %mm6 + movq 184(%edx), %mm7 + movq %mm0, 128(%ebx) + movq %mm1, 136(%ebx) + movq %mm2, 144(%ebx) + movq %mm3, 152(%ebx) + movq %mm4, 160(%ebx) + movq %mm5, 168(%ebx) + movq %mm6, 176(%ebx) + movq %mm7, 184(%ebx) + movq 192(%edx), %mm0 + movq 200(%edx), %mm1 + movq 208(%edx), %mm2 + movq 216(%edx), %mm3 + movq 224(%edx), %mm4 + movq 232(%edx), %mm5 + movq 240(%edx), %mm6 + movq 248(%edx), %mm7 + movq %mm0, 192(%ebx) + movq %mm1, 200(%ebx) + movq %mm2, 208(%ebx) + movq %mm3, 216(%ebx) + movq %mm4, 224(%ebx) + movq %mm5, 232(%ebx) + movq %mm6, 240(%ebx) + movq %mm7, 248(%ebx) + +.fastkdf_loop: + movl 28(%esp), %edx + leal 0(%edx, %edi), %ebp + leal 320(%edx, %edi), %ebx + leal 608(%edx), %esi + xorl %ecx, %ecx + pxor %mm0, %mm0 + + movl $0x6B08C647, 0(%esi) + movl $0xBB67AE85, 4(%esi) + movl $0x3C6EF372, 8(%esi) + movl $0xA54FF53A, 12(%esi) + movl $0x510E527F, 16(%esi) + movl $0x9B05688C, 20(%esi) + movl $0x1F83D9AB, 24(%esi) + movl $0x5BE0CD19, 28(%esi) + movl $64, 32(%esi) + movl %ecx, 36(%esi) + movq %mm0, 40(%esi) + + movq 0(%ebx), %mm4 + movq 8(%ebx), %mm5 + movq 16(%ebx), %mm6 + movq 24(%ebx), %mm7 + movq %mm4, 48(%esi) + movq %mm5, 56(%esi) + movq %mm6, 64(%esi) + movq %mm7, 72(%esi) + movq %mm0, 80(%esi) + movq %mm0, 88(%esi) + movq %mm0, 96(%esi) + movq %mm0, 104(%esi) + + movl %esi, 0(%esp) + call blake2s_compress + + movq 0(%ebp), %mm0 + movq 8(%ebp), %mm1 + movq 16(%ebp), %mm2 + movq 24(%ebp), %mm3 + movq 32(%ebp), %mm4 + movq 40(%ebp), %mm5 + movq 48(%ebp), %mm6 + movq 56(%ebp), %mm7 + movq %mm0, 48(%esi) + movq %mm1, 56(%esi) + movq %mm2, 64(%esi) + movq %mm3, 72(%esi) + movq %mm4, 80(%esi) + movq %mm5, 88(%esi) + movq %mm6, 96(%esi) + movq %mm7, 104(%esi) + + movl $128, 32(%esi) + movl $0xFFFFFFFF, 40(%esi) + call blake2s_compress + + movq 0(%esi), %mm3 + movq 8(%esi), %mm5 + movq 16(%esi), %mm6 + movq 24(%esi), %mm7 + pxor %mm0, %mm0 + movq %mm3, %mm4 + paddb %mm5, %mm3 + paddb %mm6, %mm3 + paddb %mm7, %mm3 + psadbw %mm0, %mm3 + movd %mm3, %edi + andl $0xFF, %edi + movl 28(%esp), %edx + leal 320(%edx, %edi), %ebx + movq 0(%ebx), %mm0 + movq 8(%ebx), %mm1 + movq 16(%ebx), %mm2 + movq 24(%ebx), %mm3 + pxor %mm4, %mm0 + pxor %mm5, %mm1 + pxor %mm6, %mm2 + pxor %mm7, %mm3 + movq %mm0, 0(%ebx) + movq %mm1, 8(%ebx) + movq %mm2, 16(%ebx) + movq %mm3, 24(%ebx) + +/* tail update */ + movl $32, %eax + cmpl %edi, %eax + jc .fastkdf_headupd + leal 256(%ebx), %edx + movl %edx, 0(%esp) + movl %ebx, 4(%esp) + subl %edi, %eax + movl %eax, 8(%esp) + call neoscrypt_copy + jmp .fastkdf_loop_end + +/* head update */ +.fastkdf_headupd: + movl $224, %eax + cmpl %edi, %eax + jnc .fastkdf_loop_end + movl %ebx, %edx + subl %edi, %edx + movl %edx, 0(%esp) + leal 256(%edx), %edx + movl %edx, 4(%esp) + movl %edi, %edx + subl %eax, %edx + movl %edx, 8(%esp) + call neoscrypt_copy + +.fastkdf_loop_end: + decl 20(%esp) + jnz .fastkdf_loop + + movl 24(%esp), %esi + movl 28(%esp), %ebp + movl $256, %ebx + subl %edi, %ebx + cmpl %esi, %ebx + jc .fastkdf_crosscopy + + leal 320(%ebp, %edi), %ebx + movl %ebx, 0(%esp) + movl %ebp, 4(%esp) + movl %esi, 8(%esp) + call neoscrypt_xor + movl 844(%esp), %eax + movl %eax, 0(%esp) + movl %ebx, 4(%esp) + call neoscrypt_copy + jmp .fastkdf_finish + +.fastkdf_crosscopy: + leal 320(%ebp, %edi), %edi + movl %edi, 0(%esp) + movl %ebp, 4(%esp) + movl %ebx, 8(%esp) + call neoscrypt_xor + leal 320(%ebp), %eax + movl %eax, 0(%esp) + leal 0(%ebp, %ebx), %edx + movl %edx, 4(%esp) + subl %ebx, %esi + movl %esi, 8(%esp) + call neoscrypt_xor + movl 844(%esp), %eax + movl %eax, 0(%esp) + movl %edi, 4(%esp) + movl %ebx, 8(%esp) + call neoscrypt_copy + movl 844(%esp), %eax + leal 0(%eax, %ebx), %eax + movl %eax, 0(%esp) + leal 320(%ebp), %edx + movl %edx, 4(%esp) + movl %esi, 8(%esp) + call neoscrypt_copy + +.fastkdf_finish: + addl $816, %esp + popl %edi + popl %esi + popl %ebp + popl %ebx + emms + ret + + +/* neoscrypt_xor_salsa(mem, xormem, workmem, double_rounds) + * i386 (INT) Salsa20 with XOR (MMX support required) */ +neoscrypt_xor_salsa: + pushl %ebx + pushl %ebp + pushl %esi + pushl %edi +/* XOR and copy to temporary memory */ + movl 20(%esp), %ebx + movl 24(%esp), %ecx + movl 28(%esp), %ebp + movq 0(%ebx), %mm0 + movq 8(%ebx), %mm1 + movq 16(%ebx), %mm2 + movq 24(%ebx), %mm3 + movq 32(%ebx), %mm4 + movq 40(%ebx), %mm5 + movq 48(%ebx), %mm6 + movq 56(%ebx), %mm7 + pxor 0(%ecx), %mm0 + pxor 8(%ecx), %mm1 + pxor 16(%ecx), %mm2 + pxor 24(%ecx), %mm3 + pxor 32(%ecx), %mm4 + pxor 40(%ecx), %mm5 + pxor 48(%ecx), %mm6 + pxor 56(%ecx), %mm7 + movq %mm0, 0(%ebx) + movq %mm1, 8(%ebx) + movq %mm2, 16(%ebx) + movq %mm3, 24(%ebx) + movq %mm4, 32(%ebx) + movq %mm5, 40(%ebx) + movq %mm6, 48(%ebx) + movq %mm7, 56(%ebx) + movq %mm0, 0(%ebp) + movq %mm1, 8(%ebp) + movq %mm2, 16(%ebp) + movq %mm3, 24(%ebp) + movq %mm4, 32(%ebp) + movq %mm5, 40(%ebp) + movq %mm6, 48(%ebp) + movq %mm7, 56(%ebp) +/* number of double rounds */ + movl 32(%esp), %eax + movl %eax, -4(%esp) +.xor_salsa: +/* quarters A and B, initial C and D */ + movl 0(%ebp), %eax /* A: load a */ + movl 20(%ebp), %ebx /* B: load a */ + addl 48(%ebp), %eax /* A: t = a + d */ + addl 4(%ebp), %ebx /* B: t = a + d */ + roll $7, %eax /* A: rotate t */ + roll $7, %ebx /* B: rotate t */ + xorl 16(%ebp), %eax /* A: b = b ^ t */ + xorl 36(%ebp), %ebx /* B: b = b ^ t */ + movl %eax, %esi /* A: copy b */ + movl %ebx, %edi /* B: copy b */ + movl %esi, 16(%ebp) /* A: store b */ + movl %edi, 36(%ebp) /* B: store b */ + addl 0(%ebp), %eax /* A: t = b + a */ + addl 20(%ebp), %ebx /* B: t = b + a */ + roll $9, %eax /* A: rotate t */ + roll $9, %ebx /* B: rotate t */ + xorl 32(%ebp), %eax /* A: c = c ^ t */ + xorl 52(%ebp), %ebx /* B: c = c ^ t */ + movl %eax, %ecx /* A: copy c */ + movl %ebx, %edx /* B: copy c */ + movl %ecx, 32(%ebp) /* A: store c */ + movl %edx, 52(%ebp) /* B: store c */ + addl %esi, %eax /* A: t = c + b */ + addl %edi, %ebx /* B: t = c + b */ + roll $13, %eax /* A: rotate t */ + roll $13, %ebx /* B: rotate t */ + xorl 48(%ebp), %eax /* A: d = d ^ t */ + xorl 4(%ebp), %ebx /* B: d = d ^ t */ + movl %eax, 48(%ebp) /* A: store d */ + movl %ebx, 4(%ebp) /* B: store d */ + addl %eax, %ecx /* A: t = d + c */ + movl 40(%ebp), %eax /* C: load a */ + addl %ebx, %edx /* B: t = d + c */ + movl 60(%ebp), %ebx /* D: load a */ + roll $18, %ecx /* A: rotate t */ + addl 24(%ebp), %eax /* C: t = a + d */ + roll $18, %edx /* B: rotate t */ + addl 44(%ebp), %ebx /* D: t = a + d */ + xorl 0(%ebp), %ecx /* A: a = a ^ t */ + roll $7, %eax /* C: rotate t */ + xorl 20(%ebp), %edx /* B: a = a ^ t */ + roll $7, %ebx /* D: rotate t */ + movl %ecx, 0(%ebp) /* A: store a */ + movl %edx, 20(%ebp) /* B: store a */ +/* quarters C and D, initial E and F */ + xorl 56(%ebp), %eax /* C: b = b ^ t */ + xorl 12(%ebp), %ebx /* D: b = b ^ t */ + movl %eax, %esi /* C: copy b */ + movl %ebx, %edi /* D: copy b */ + movl %esi, 56(%ebp) /* C: store b */ + movl %edi, 12(%ebp) /* D: store b */ + addl 40(%ebp), %eax /* C: t = b + a */ + addl 60(%ebp), %ebx /* D: t = b + a */ + roll $9, %eax /* C: rotate t */ + roll $9, %ebx /* D: rotate t */ + xorl 8(%ebp), %eax /* C: c = c ^ t */ + xorl 28(%ebp), %ebx /* D: c = c ^ t */ + movl %eax, %ecx /* C: copy c */ + movl %ebx, %edx /* D: copy c */ + movl %ecx, 8(%ebp) /* C: store c */ + movl %edx, 28(%ebp) /* D: store c */ + addl %esi, %eax /* C: t = c + b */ + addl %edi, %ebx /* D: t = c + b */ + roll $13, %eax /* C: rotate t */ + roll $13, %ebx /* D: rotate t */ + xorl 24(%ebp), %eax /* C: d = d ^ t */ + xorl 44(%ebp), %ebx /* D: d = d ^ t */ + movl %eax, 24(%ebp) /* C: store d */ + movl %ebx, 44(%ebp) /* D: store d */ + addl %eax, %ecx /* C: t = d + c */ + movl 0(%ebp), %eax /* E: load a */ + addl %ebx, %edx /* D: t = d + c */ + movl 20(%ebp), %ebx /* F: load a */ + roll $18, %ecx /* C: rotate t */ + addl 12(%ebp), %eax /* E: t = a + d */ + roll $18, %edx /* D: rotate t */ + addl 16(%ebp), %ebx /* F: t = a + d */ + xorl 40(%ebp), %ecx /* C: a = a ^ t */ + roll $7, %eax /* E: rotate t */ + xorl 60(%ebp), %edx /* D: a = a ^ t */ + roll $7, %ebx /* F: rotate t */ + movl %ecx, 40(%ebp) /* C: store a */ + movl %edx, 60(%ebp) /* D: store a */ +/* quarters E and F, initial G and H */ + xorl 4(%ebp), %eax /* E: b = b ^ t */ + xorl 24(%ebp), %ebx /* F: b = b ^ t */ + movl %eax, %esi /* E: copy b */ + movl %ebx, %edi /* F: copy b */ + movl %esi, 4(%ebp) /* E: store b */ + movl %edi, 24(%ebp) /* F: store b */ + addl 0(%ebp), %eax /* E: t = b + a */ + addl 20(%ebp), %ebx /* F: t = b + a */ + roll $9, %eax /* E: rotate t */ + roll $9, %ebx /* F: rotate t */ + xorl 8(%ebp), %eax /* E: c = c ^ t */ + xorl 28(%ebp), %ebx /* F: c = c ^ t */ + movl %eax, %ecx /* E: copy c */ + movl %ebx, %edx /* F: copy c */ + movl %ecx, 8(%ebp) /* E: store c */ + movl %edx, 28(%ebp) /* F: store c */ + addl %esi, %eax /* E: t = c + b */ + addl %edi, %ebx /* F: t = c + b */ + roll $13, %eax /* E: rotate t */ + roll $13, %ebx /* F: rotate t */ + xorl 12(%ebp), %eax /* E: d = d ^ t */ + xorl 16(%ebp), %ebx /* F: d = d ^ t */ + movl %eax, 12(%ebp) /* E: store d */ + movl %ebx, 16(%ebp) /* F: store d */ + addl %eax, %ecx /* E: t = d + c */ + movl 40(%ebp), %eax /* G: load a */ + addl %ebx, %edx /* F: t = d + c */ + movl 60(%ebp), %ebx /* H: load a */ + roll $18, %ecx /* E: rotate t */ + addl 36(%ebp), %eax /* G: t = a + d */ + roll $18, %edx /* F: rotate t */ + addl 56(%ebp), %ebx /* H: t = a + d */ + xorl 0(%ebp), %ecx /* E: a = a ^ t */ + roll $7, %eax /* G: rotate t */ + xorl 20(%ebp), %edx /* F: a = a ^ t */ + roll $7, %ebx /* H: rotate t */ + movl %ecx, 0(%ebp) /* E: store a */ + movl %edx, 20(%ebp) /* F: store a */ +/* quarters G and H */ + xorl 44(%ebp), %eax /* G: b = b ^ t */ + xorl 48(%ebp), %ebx /* H: b = b ^ t */ + movl %eax, %esi /* G: copy b */ + movl %ebx, %edi /* H: copy b */ + movl %esi, 44(%ebp) /* G: store b */ + movl %edi, 48(%ebp) /* H: store b */ + addl 40(%ebp), %eax /* G: t = b + a */ + addl 60(%ebp), %ebx /* H: t = b + a */ + roll $9, %eax /* G: rotate t */ + roll $9, %ebx /* H: rotate t */ + xorl 32(%ebp), %eax /* G: c = c ^ t */ + xorl 52(%ebp), %ebx /* H: c = c ^ t */ + movl %eax, %ecx /* G: copy c */ + movl %ebx, %edx /* H: copy c */ + movl %ecx, 32(%ebp) /* G: store c */ + movl %edx, 52(%ebp) /* H: store c */ + addl %esi, %eax /* G: t = c + b */ + addl %edi, %ebx /* H: t = c + b */ + roll $13, %eax /* G: rotate t */ + roll $13, %ebx /* H: rotate t */ + xorl 36(%ebp), %eax /* G: d = d ^ t */ + xorl 56(%ebp), %ebx /* H: d = d ^ t */ + movl %eax, 36(%ebp) /* G: store d */ + movl %ebx, 56(%ebp) /* H: store d */ + addl %eax, %ecx /* G: t = d + c */ + addl %ebx, %edx /* H: t = d + c */ + roll $18, %ecx /* G: rotate t */ + roll $18, %edx /* H: rotate t */ + xorl 40(%ebp), %ecx /* G: a = a ^ t */ + xorl 60(%ebp), %edx /* H: a = a ^ t */ + movl %ecx, 40(%ebp) /* G: store a */ + movl %edx, 60(%ebp) /* H: store a */ + decl -4(%esp) + jnz .xor_salsa + +/* write back data */ + movl 20(%esp), %ebx + movq 0(%ebx), %mm0 + movq 8(%ebx), %mm1 + movq 16(%ebx), %mm2 + movq 24(%ebx), %mm3 + movq 32(%ebx), %mm4 + movq 40(%ebx), %mm5 + movq 48(%ebx), %mm6 + movq 56(%ebx), %mm7 + paddd 0(%ebp), %mm0 + paddd 8(%ebp), %mm1 + paddd 16(%ebp), %mm2 + paddd 24(%ebp), %mm3 + paddd 32(%ebp), %mm4 + paddd 40(%ebp), %mm5 + paddd 48(%ebp), %mm6 + paddd 56(%ebp), %mm7 + movq %mm0, 0(%ebx) + movq %mm1, 8(%ebx) + movq %mm2, 16(%ebx) + movq %mm3, 24(%ebx) + movq %mm4, 32(%ebx) + movq %mm5, 40(%ebx) + movq %mm6, 48(%ebx) + movq %mm7, 56(%ebx) + + popl %edi + popl %esi + popl %ebp + popl %ebx + ret + + +/* neoscrypt_xor_chacha(mem, xormem, tempmem, double_rounds) + * i386 (INT) ChaCha20 with XOR (MMX support required) */ +neoscrypt_xor_chacha: + pushl %ebx + pushl %ebp + pushl %esi + pushl %edi +/* XOR and copy to temporary memory */ + movl 20(%esp), %ebx + movl 24(%esp), %ecx + movl 28(%esp), %ebp + movq 0(%ebx), %mm0 + movq 8(%ebx), %mm1 + movq 16(%ebx), %mm2 + movq 24(%ebx), %mm3 + movq 32(%ebx), %mm4 + movq 40(%ebx), %mm5 + movq 48(%ebx), %mm6 + movq 56(%ebx), %mm7 + pxor 0(%ecx), %mm0 + pxor 8(%ecx), %mm1 + pxor 16(%ecx), %mm2 + pxor 24(%ecx), %mm3 + pxor 32(%ecx), %mm4 + pxor 40(%ecx), %mm5 + pxor 48(%ecx), %mm6 + pxor 56(%ecx), %mm7 + movq %mm0, 0(%ebx) + movq %mm1, 8(%ebx) + movq %mm2, 16(%ebx) + movq %mm3, 24(%ebx) + movq %mm4, 32(%ebx) + movq %mm5, 40(%ebx) + movq %mm6, 48(%ebx) + movq %mm7, 56(%ebx) + movq %mm0, 0(%ebp) + movq %mm1, 8(%ebp) + movq %mm2, 16(%ebp) + movq %mm3, 24(%ebp) + movq %mm4, 32(%ebp) + movq %mm5, 40(%ebp) + movq %mm6, 48(%ebp) + movq %mm7, 56(%ebp) +/* number of double rounds */ + movl 32(%esp), %eax + movl %eax, -4(%esp) +.xor_chacha: +/* quarters A and B, initial C */ + movl 0(%ebp), %eax /* A: load a */ + movl 16(%ebp), %ebx /* A: load b */ + addl %ebx, %eax /* A: a = a + b */ + movl 32(%ebp), %ecx /* A: load c */ + movl 48(%ebp), %edx /* A: load d */ + xorl %eax, %edx /* A: d = d ^ a */ + movl 4(%ebp), %edi /* B: load a */ + roll $16, %edx /* A: rotate d */ + movl 20(%ebp), %esi /* B: load b */ + addl %edx, %ecx /* A: c = c + d */ + xorl %ecx, %ebx /* A: b = b ^ c */ + addl %esi, %edi /* B: a = a + b */ + roll $12, %ebx /* A: rotate b */ + addl %ebx, %eax /* A: a = a + b */ + movl %eax, 0(%ebp) /* A: store a */ + xorl %eax, %edx /* A: d = d ^ a */ + movl 52(%ebp), %eax /* B: load d */ + roll $8, %edx /* A: rotate d */ + xorl %edi, %eax /* B: d = d ^ a */ + movl %edx, 48(%ebp) /* A: store d */ + addl %edx, %ecx /* A: c = c + d */ + movl 36(%ebp), %edx /* B: load c */ + movl %ecx, 32(%ebp) /* A: store c */ + xorl %ecx, %ebx /* A: b = b ^ c */ + roll $16, %eax /* B: rotate d */ + movl 40(%ebp), %ecx /* C: load c */ + roll $7, %ebx /* A: rotate b */ + addl %eax, %edx /* B: c = c + d */ + movl %ebx, 16(%ebp) /* A: store b */ + xorl %edx, %esi /* B: b = b ^ c */ + movl 24(%ebp), %ebx /* C: load b */ + roll $12, %esi /* B: rotate b */ + addl %esi, %edi /* B: a = a + b */ + movl %edi, 4(%ebp) /* B: store a */ + xorl %edi, %eax /* B: d = d ^ a */ + roll $8, %eax /* B: rotate d */ + movl %eax, 52(%ebp) /* B: store d */ + addl %eax, %edx /* B: c = c + d */ + movl 8(%ebp), %eax /* C: load a */ + movl %edx, 36(%ebp) /* B: store c */ + xorl %edx, %esi /* B: b = b ^ c */ + movl 56(%ebp), %edx /* C: load d */ + roll $7, %esi /* B: rotate b */ + addl %ebx, %eax /* C: a = a + b */ + movl %esi, 20(%ebp) /* B: store b */ +/* quarters C and D, initial E */ + xorl %eax, %edx /* C: d = d ^ a */ + movl 12(%ebp), %edi /* D: load a */ + roll $16, %edx /* C: rotate d */ + movl 28(%ebp), %esi /* D: load b */ + addl %edx, %ecx /* C: c = c + d */ + xorl %ecx, %ebx /* C: b = b ^ c */ + addl %esi, %edi /* D: a = a + b */ + roll $12, %ebx /* C: rotate b */ + addl %ebx, %eax /* C: a = a + b */ + movl %eax, 8(%ebp) /* C: store a */ + xorl %eax, %edx /* C: d = d ^ a */ + movl 60(%ebp), %eax /* D: load d */ + roll $8, %edx /* C: rotate d */ + xorl %edi, %eax /* D: d = d ^ a */ + movl %edx, 56(%ebp) /* C: store d */ + addl %edx, %ecx /* C: c = c + d */ + movl 44(%ebp), %edx /* D: load c */ + movl %ecx, 40(%ebp) /* C: store c */ + xorl %ecx, %ebx /* C: b = b ^ c */ + roll $16, %eax /* D: rotate d */ + movl 40(%ebp), %ecx /* E: load c */ + roll $7, %ebx /* C: rotate b */ + addl %eax, %edx /* D: c = c + d */ + movl %ebx, 24(%ebp) /* C: store b */ + xorl %edx, %esi /* D: b = b ^ c */ + movl 20(%ebp), %ebx /* E: load b */ + roll $12, %esi /* D: rotate b */ + addl %esi, %edi /* D: a = a + b */ + movl %edi, 12(%ebp) /* D: store a */ + xorl %edi, %eax /* D: d = d ^ a */ + roll $8, %eax /* D: rotate d */ + movl %eax, 60(%ebp) /* D: store d */ + addl %eax, %edx /* D: c = c + d */ + movl 0(%ebp), %eax /* E: load a */ + movl %edx, 44(%ebp) /* D: store c */ + xorl %edx, %esi /* D: b = b ^ c */ + movl 60(%ebp), %edx /* E: load d */ + roll $7, %esi /* D: rotate b */ + addl %ebx, %eax /* E: a = a + b */ + movl %esi, 28(%ebp) /* D: store b */ +/* quarters E and F, initial G */ + xorl %eax, %edx /* E: d = d ^ a */ + movl 4(%ebp), %edi /* F: load a */ + roll $16, %edx /* E: rotate d */ + movl 24(%ebp), %esi /* F: load b */ + addl %edx, %ecx /* E: c = c + d */ + xorl %ecx, %ebx /* E: b = b ^ c */ + addl %esi, %edi /* F: a = a + b */ + roll $12, %ebx /* E: rotate b */ + addl %ebx, %eax /* E: a = a + b */ + movl %eax, 0(%ebp) /* E: store a */ + xorl %eax, %edx /* E: d = d ^ a */ + movl 48(%ebp), %eax /* F: load d */ + roll $8, %edx /* E: rotate d */ + xorl %edi, %eax /* F: d = d ^ a */ + movl %edx, 60(%ebp) /* E: store d */ + addl %edx, %ecx /* E: c = c + d */ + movl 44(%ebp), %edx /* F: load c */ + movl %ecx, 40(%ebp) /* E: store c */ + xorl %ecx, %ebx /* E: b = b ^ c */ + roll $16, %eax /* F: rotate d */ + movl 32(%ebp), %ecx /* G: load c */ + roll $7, %ebx /* E: rotate b */ + addl %eax, %edx /* F: c = c + d */ + movl %ebx, 20(%ebp) /* E: store b */ + xorl %edx, %esi /* F: b = b ^ c */ + movl 28(%ebp), %ebx /* G: load b */ + roll $12, %esi /* F: rotate b */ + addl %esi, %edi /* F: a = a + b */ + movl %edi, 4(%ebp) /* F: store a */ + xorl %edi, %eax /* F: d = d ^ a */ + roll $8, %eax /* F: rotate d */ + movl %eax, 48(%ebp) /* F: store d */ + addl %eax, %edx /* F: c = c + d */ + movl 8(%ebp), %eax /* G: load a */ + movl %edx, 44(%ebp) /* F: store c */ + xorl %edx, %esi /* F: b = b ^ c */ + movl 52(%ebp), %edx /* G: load d */ + roll $7, %esi /* F: rotate b */ + addl %ebx, %eax /* G: a = a + b */ + movl %esi, 24(%ebp) /* F: store b */ +/* quarters G and H */ + xorl %eax, %edx /* G: d = d ^ a */ + movl 12(%ebp), %edi /* H: load a */ + roll $16, %edx /* G: rotate d */ + movl 16(%ebp), %esi /* H: load b */ + addl %edx, %ecx /* G: c = c + d */ + xorl %ecx, %ebx /* G: b = b ^ c */ + addl %esi, %edi /* H: a = a + b */ + roll $12, %ebx /* G: rotate b */ + addl %ebx, %eax /* G: a = a + b */ + movl %eax, 8(%ebp) /* G: store a */ + xorl %eax, %edx /* G: d = d ^ a */ + movl 56(%ebp), %eax /* H: load d */ + roll $8, %edx /* G: rotate d */ + xorl %edi, %eax /* H: d = d ^ a */ + movl %edx, 52(%ebp) /* G: store d */ + addl %edx, %ecx /* G: c = c + d */ + movl 36(%ebp), %edx /* H: load c */ + movl %ecx, 32(%ebp) /* G: store c */ + xorl %ecx, %ebx /* G: b = b ^ c */ + roll $16, %eax /* H: rotate d */ + roll $7, %ebx /* G: rotate b */ + addl %eax, %edx /* H: c = c + d */ + movl %ebx, 28(%ebp) /* G: store b */ + xorl %edx, %esi /* H: b = b ^ c */ + roll $12, %esi /* H: rotate b */ + addl %esi, %edi /* H: a = a + b */ + movl %edi, 12(%ebp) /* H: store a */ + xorl %edi, %eax /* H: d = d ^ a */ + roll $8, %eax /* H: rotate d */ + movl %eax, 56(%ebp) /* H: store d */ + addl %eax, %edx /* H: c = c + d */ + movl %edx, 36(%ebp) /* H: store c */ + xorl %edx, %esi /* H: b = b ^ c */ + roll $7, %esi /* H: rotate b */ + movl %esi, 16(%ebp) /* H: store b */ + decl -4(%esp) + jnz .xor_chacha + +/* write back data */ + movl 20(%esp), %ebx + movq 0(%ebx), %mm0 + movq 8(%ebx), %mm1 + movq 16(%ebx), %mm2 + movq 24(%ebx), %mm3 + movq 32(%ebx), %mm4 + movq 40(%ebx), %mm5 + movq 48(%ebx), %mm6 + movq 56(%ebx), %mm7 + paddd 0(%ebp), %mm0 + paddd 8(%ebp), %mm1 + paddd 16(%ebp), %mm2 + paddd 24(%ebp), %mm3 + paddd 32(%ebp), %mm4 + paddd 40(%ebp), %mm5 + paddd 48(%ebp), %mm6 + paddd 56(%ebp), %mm7 + movq %mm0, 0(%ebx) + movq %mm1, 8(%ebx) + movq %mm2, 16(%ebx) + movq %mm3, 24(%ebx) + movq %mm4, 32(%ebx) + movq %mm5, 40(%ebx) + movq %mm6, 48(%ebx) + movq %mm7, 56(%ebx) + + popl %edi + popl %esi + popl %ebp + popl %ebx + ret + + +/* neoscrypt_salsa_tangle_sse2(mem, count) + * i386 (SSE2) Salsa20 map switcher; + * correct map: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + * SSE2 map: 0 5 10 15 12 1 6 11 8 13 2 7 4 9 14 3 */ +neoscrypt_salsa_tangle_sse2: + pushl %ebx + movl 8(%esp), %ebx + movl 12(%esp), %ecx +.salsa_tangle_sse2: + movl 4(%ebx), %eax + movl 20(%ebx), %edx + movl %eax, 20(%ebx) + movl %edx, 4(%ebx) + movl 8(%ebx), %eax + movl 40(%ebx), %edx + movl %eax, 40(%ebx) + movl %edx, 8(%ebx) + movl 12(%ebx), %eax + movl 60(%ebx), %edx + movl %eax, 60(%ebx) + movl %edx, 12(%ebx) + movl 16(%ebx), %eax + movl 48(%ebx), %edx + movl %eax, 48(%ebx) + movl %edx, 16(%ebx) + movl 28(%ebx), %eax + movl 44(%ebx), %edx + movl %eax, 44(%ebx) + movl %edx, 28(%ebx) + movl 36(%ebx), %eax + movl 52(%ebx), %edx + movl %eax, 52(%ebx) + movl %edx, 36(%ebx) + addl $64, %ebx + decl %ecx + jnz .salsa_tangle_sse2 + + popl %ebx + ret + + +/* neoscrypt_xor_salsa_sse2(mem, xormem, double_rounds) + * i386 (SSE2) Salsa20 with XOR; + * mem and xormem must be aligned properly */ +neoscrypt_xor_salsa_sse2: + movl 4(%esp), %edx + movl 8(%esp), %eax + movl 12(%esp), %ecx + movdqa 0(%edx), %xmm0 + movdqa 16(%edx), %xmm1 + movdqa 32(%edx), %xmm2 + movdqa 48(%edx), %xmm3 + pxor 0(%eax), %xmm0 + pxor 16(%eax), %xmm1 + pxor 32(%eax), %xmm2 + pxor 48(%eax), %xmm3 + movdqa %xmm0, %xmm6 + movdqa %xmm1, %xmm7 + movdqa %xmm2, 32(%edx) + movdqa %xmm3, 48(%edx) +.xor_salsa_sse2: + movdqa %xmm1, %xmm4 + paddd %xmm0, %xmm4 + movdqa %xmm4, %xmm5 + pslld $7, %xmm4 + psrld $25, %xmm5 + pxor %xmm4, %xmm3 + movdqa %xmm0, %xmm4 + pxor %xmm5, %xmm3 + paddd %xmm3, %xmm4 + movdqa %xmm4, %xmm5 + pslld $9, %xmm4 + psrld $23, %xmm5 + pxor %xmm4, %xmm2 + movdqa %xmm3, %xmm4 + pxor %xmm5, %xmm2 + pshufd $0x93, %xmm3, %xmm3 + paddd %xmm2, %xmm4 + movdqa %xmm4, %xmm5 + pslld $13, %xmm4 + psrld $19, %xmm5 + pxor %xmm4, %xmm1 + movdqa %xmm2, %xmm4 + pxor %xmm5, %xmm1 + pshufd $0x4E, %xmm2, %xmm2 + paddd %xmm1, %xmm4 + movdqa %xmm4, %xmm5 + pslld $18, %xmm4 + psrld $14, %xmm5 + pxor %xmm4, %xmm0 + movdqa %xmm3, %xmm4 + pxor %xmm5, %xmm0 + pshufd $0x39, %xmm1, %xmm1 + paddd %xmm0, %xmm4 + movdqa %xmm4, %xmm5 + pslld $7, %xmm4 + psrld $25, %xmm5 + pxor %xmm4, %xmm1 + movdqa %xmm0, %xmm4 + pxor %xmm5, %xmm1 + paddd %xmm1, %xmm4 + movdqa %xmm4, %xmm5 + pslld $9, %xmm4 + psrld $23, %xmm5 + pxor %xmm4, %xmm2 + movdqa %xmm1, %xmm4 + pxor %xmm5, %xmm2 + pshufd $0x93, %xmm1, %xmm1 + paddd %xmm2, %xmm4 + movdqa %xmm4, %xmm5 + pslld $13, %xmm4 + psrld $19, %xmm5 + pxor %xmm4, %xmm3 + movdqa %xmm2, %xmm4 + pxor %xmm5, %xmm3 + pshufd $0x4E, %xmm2, %xmm2 + paddd %xmm3, %xmm4 + movdqa %xmm4, %xmm5 + pslld $18, %xmm4 + psrld $14, %xmm5 + pxor %xmm4, %xmm0 + pshufd $0x39, %xmm3, %xmm3 + pxor %xmm5, %xmm0 + decl %ecx + jnz .xor_salsa_sse2 + + paddd %xmm6, %xmm0 + paddd %xmm7, %xmm1 + paddd 32(%edx), %xmm2 + paddd 48(%edx), %xmm3 + movdqa %xmm0, 0(%edx) + movdqa %xmm1, 16(%edx) + movdqa %xmm2, 32(%edx) + movdqa %xmm3, 48(%edx) + + ret + + +/* neoscrypt_xor_chacha_sse2(mem, xormem, double_rounds) + * i386 (SSE2) ChaCha20 with XOR; + * mem and xormem must be aligned properly */ +neoscrypt_xor_chacha_sse2: + movl 4(%esp), %edx + movl 8(%esp), %eax + movl 12(%esp), %ecx + movdqa 0(%edx), %xmm0 + movdqa 16(%edx), %xmm1 + movdqa 32(%edx), %xmm2 + movdqa 48(%edx), %xmm3 + pxor 0(%eax), %xmm0 + pxor 16(%eax), %xmm1 + pxor 32(%eax), %xmm2 + pxor 48(%eax), %xmm3 + movdqa %xmm0, %xmm5 + movdqa %xmm1, %xmm6 + movdqa %xmm2, %xmm7 + movdqa %xmm3, 48(%edx) +.xor_chacha_sse2: + paddd %xmm1, %xmm0 + pxor %xmm0, %xmm3 + pshuflw $0xB1, %xmm3, %xmm3 + pshufhw $0xB1, %xmm3, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm4 + pslld $12, %xmm1 + psrld $20, %xmm4 + pxor %xmm4, %xmm1 + paddd %xmm1, %xmm0 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm4 + pslld $8, %xmm3 + psrld $24, %xmm4 + pxor %xmm4, %xmm3 + pshufd $0x93, %xmm0, %xmm0 + paddd %xmm3, %xmm2 + pshufd $0x4E, %xmm3, %xmm3 + pxor %xmm2, %xmm1 + pshufd $0x39, %xmm2, %xmm2 + movdqa %xmm1, %xmm4 + pslld $7, %xmm1 + psrld $25, %xmm4 + pxor %xmm4, %xmm1 + paddd %xmm1, %xmm0 + pxor %xmm0, %xmm3 + pshuflw $0xB1, %xmm3, %xmm3 + pshufhw $0xB1, %xmm3, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm4 + pslld $12, %xmm1 + psrld $20, %xmm4 + pxor %xmm4, %xmm1 + paddd %xmm1, %xmm0 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm4 + pslld $8, %xmm3 + psrld $24, %xmm4 + pxor %xmm4, %xmm3 + pshufd $0x39, %xmm0, %xmm0 + paddd %xmm3, %xmm2 + pshufd $0x4E, %xmm3, %xmm3 + pxor %xmm2, %xmm1 + pshufd $0x93, %xmm2, %xmm2 + movdqa %xmm1, %xmm4 + pslld $7, %xmm1 + psrld $25, %xmm4 + pxor %xmm4, %xmm1 + decl %ecx + jnz .xor_chacha_sse2 + + paddd %xmm5, %xmm0 + paddd %xmm6, %xmm1 + paddd %xmm7, %xmm2 + paddd 48(%edx), %xmm3 + movdqa %xmm0, 0(%edx) + movdqa %xmm1, 16(%edx) + movdqa %xmm2, 32(%edx) + movdqa %xmm3, 48(%edx) + + ret + + +/* neoscrypt(input, output, profile) + * i386 (INT, SSE2) NeoScrypt engine (MMX required for INT); + * supports NeoScrypt and Scrypt only */ +.globl neoscrypt +.globl _neoscrypt +neoscrypt: +_neoscrypt: + pushl %ebx + pushl %ebp + pushl %esi + pushl %edi + movl 20(%esp), %esi + movl 24(%esp), %edi + movl 28(%esp), %ebx + +#ifdef SHA256 +/* Scrypt mode */ + testl $0x01, %ebx + jnz .scrypt +#endif + +#ifdef WIN32 +/* attempt to allocate 33280 + 128 bytes of stack space fails miserably; + * have to use malloc() and free() instead */ + subl $64, %esp +/* allocate memory (9 pages of 4Kb each) */ + movl $0x9000, 0(%esp) + call _malloc +/* save memory address */ + movl %eax, 32(%esp) +/* align memory */ + addl $64, %eax + andl $0xFFFFFFC0, %eax +/* memory base: X, Z, V */ + leal 64(%eax), %ebp +#else +/* align stack */ + movl %esp, %eax + andl $0xFFFFFFC0, %esp + subl $0x8280, %esp +/* save unaligned stack */ + movl %eax, 32(%esp) +/* memory base: X, Z, V */ + leal 128(%esp), %ebp +#endif /* WIN32 */ + +/* FastKDF */ +#ifdef OPT + movl %esi, 0(%esp) + movl %esi, 4(%esp) + movl %ebp, 8(%esp) + xorl %eax, %eax + movl %eax, 12(%esp) +#if defined(WIN32) || defined(__APPLE__) + call _neoscrypt_fastkdf_opt +#else + call neoscrypt_fastkdf_opt +#endif /* (WIN32) || (__APPLE__) */ +#else + movl $80, %eax + movl %esi, 0(%esp) + movl %eax, 4(%esp) + movl %esi, 8(%esp) + movl %eax, 12(%esp) + movl $32, 16(%esp) + movl %ebp, 20(%esp) + movl $256, 24(%esp) +#if defined(WIN32) || defined(__APPLE__) + call _neoscrypt_fastkdf +#else + call neoscrypt_fastkdf +#endif /* (WIN32) || (__APPLE__) */ +#endif /* OPT */ + +/* SSE2 switch */ + testl $0x1000, %ebx + jnz .neoscrypt_sse2 + +/* blkcpy(Z, X) */ + leal 256(%ebp), %eax + movq 0(%ebp), %mm0 + movq 8(%ebp), %mm1 + movq 16(%ebp), %mm2 + movq 24(%ebp), %mm3 + movq 32(%ebp), %mm4 + movq 40(%ebp), %mm5 + movq 48(%ebp), %mm6 + movq 56(%ebp), %mm7 + movq %mm0, 0(%eax) + movq %mm1, 8(%eax) + movq %mm2, 16(%eax) + movq %mm3, 24(%eax) + movq %mm4, 32(%eax) + movq %mm5, 40(%eax) + movq %mm6, 48(%eax) + movq %mm7, 56(%eax) + movq 64(%ebp), %mm0 + movq 72(%ebp), %mm1 + movq 80(%ebp), %mm2 + movq 88(%ebp), %mm3 + movq 96(%ebp), %mm4 + movq 104(%ebp), %mm5 + movq 112(%ebp), %mm6 + movq 120(%ebp), %mm7 + movq %mm0, 64(%eax) + movq %mm1, 72(%eax) + movq %mm2, 80(%eax) + movq %mm3, 88(%eax) + movq %mm4, 96(%eax) + movq %mm5, 104(%eax) + movq %mm6, 112(%eax) + movq %mm7, 120(%eax) + movq 128(%ebp), %mm0 + movq 136(%ebp), %mm1 + movq 144(%ebp), %mm2 + movq 152(%ebp), %mm3 + movq 160(%ebp), %mm4 + movq 168(%ebp), %mm5 + movq 176(%ebp), %mm6 + movq 184(%ebp), %mm7 + movq %mm0, 128(%eax) + movq %mm1, 136(%eax) + movq %mm2, 144(%eax) + movq %mm3, 152(%eax) + movq %mm4, 160(%eax) + movq %mm5, 168(%eax) + movq %mm6, 176(%eax) + movq %mm7, 184(%eax) + movq 192(%ebp), %mm0 + movq 200(%ebp), %mm1 + movq 208(%ebp), %mm2 + movq 216(%ebp), %mm3 + movq 224(%ebp), %mm4 + movq 232(%ebp), %mm5 + movq 240(%ebp), %mm6 + movq 248(%ebp), %mm7 + movq %mm0, 192(%eax) + movq %mm1, 200(%eax) + movq %mm2, 208(%eax) + movq %mm3, 216(%eax) + movq %mm4, 224(%eax) + movq %mm5, 232(%eax) + movq %mm6, 240(%eax) + movq %mm7, 248(%eax) + + leal -64(%ebp), %edx + movl %edx, 8(%esp) + movl $10, 12(%esp) + + xorl %ebx, %ebx +.chacha_ns1: +/* blkcpy(V, Z) */ + leal 512(%ebp), %eax + movl %ebx, %edx + movb $8, %cl + shll %cl, %edx + leal 256(%ebp), %ecx + addl %edx, %eax + movq 0(%ecx), %mm0 + movq 8(%ecx), %mm1 + movq 16(%ecx), %mm2 + movq 24(%ecx), %mm3 + movq 32(%ecx), %mm4 + movq 40(%ecx), %mm5 + movq 48(%ecx), %mm6 + movq 56(%ecx), %mm7 + movq %mm0, 0(%eax) + movq %mm1, 8(%eax) + movq %mm2, 16(%eax) + movq %mm3, 24(%eax) + movq %mm4, 32(%eax) + movq %mm5, 40(%eax) + movq %mm6, 48(%eax) + movq %mm7, 56(%eax) + movq 64(%ecx), %mm0 + movq 72(%ecx), %mm1 + movq 80(%ecx), %mm2 + movq 88(%ecx), %mm3 + movq 96(%ecx), %mm4 + movq 104(%ecx), %mm5 + movq 112(%ecx), %mm6 + movq 120(%ecx), %mm7 + movq %mm0, 64(%eax) + movq %mm1, 72(%eax) + movq %mm2, 80(%eax) + movq %mm3, 88(%eax) + movq %mm4, 96(%eax) + movq %mm5, 104(%eax) + movq %mm6, 112(%eax) + movq %mm7, 120(%eax) + movq 128(%ecx), %mm0 + movq 136(%ecx), %mm1 + movq 144(%ecx), %mm2 + movq 152(%ecx), %mm3 + movq 160(%ecx), %mm4 + movq 168(%ecx), %mm5 + movq 176(%ecx), %mm6 + movq 184(%ecx), %mm7 + movq %mm0, 128(%eax) + movq %mm1, 136(%eax) + movq %mm2, 144(%eax) + movq %mm3, 152(%eax) + movq %mm4, 160(%eax) + movq %mm5, 168(%eax) + movq %mm6, 176(%eax) + movq %mm7, 184(%eax) + movq 192(%ecx), %mm0 + movq 200(%ecx), %mm1 + movq 208(%ecx), %mm2 + movq 216(%ecx), %mm3 + movq 224(%ecx), %mm4 + movq 232(%ecx), %mm5 + movq 240(%ecx), %mm6 + movq 248(%ecx), %mm7 + movq %mm0, 192(%eax) + movq %mm1, 200(%eax) + movq %mm2, 208(%eax) + movq %mm3, 216(%eax) + movq %mm4, 224(%eax) + movq %mm5, 232(%eax) + movq %mm6, 240(%eax) + movq %mm7, 248(%eax) +/* blkmix(Z) */ + leal 256(%ebp), %eax + movl %eax, 0(%esp) + leal 448(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_chacha + leal 320(%ebp), %eax + movl %eax, 0(%esp) + leal 256(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_chacha + leal 384(%ebp), %eax + movl %eax, 0(%esp) + leal 320(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_chacha + leal 448(%ebp), %eax + movl %eax, 0(%esp) + leal 384(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_chacha + leal 320(%ebp), %eax + leal 384(%ebp), %edx + movq 0(%eax), %mm0 + movq 8(%eax), %mm1 + movq 16(%eax), %mm2 + movq 24(%eax), %mm3 + movq 0(%edx), %mm4 + movq 8(%edx), %mm5 + movq 16(%edx), %mm6 + movq 24(%edx), %mm7 + movq %mm0, 0(%edx) + movq %mm1, 8(%edx) + movq %mm2, 16(%edx) + movq %mm3, 24(%edx) + movq %mm4, 0(%eax) + movq %mm5, 8(%eax) + movq %mm6, 16(%eax) + movq %mm7, 24(%eax) + movq 32(%eax), %mm0 + movq 40(%eax), %mm1 + movq 48(%eax), %mm2 + movq 56(%eax), %mm3 + movq 32(%edx), %mm4 + movq 40(%edx), %mm5 + movq 48(%edx), %mm6 + movq 56(%edx), %mm7 + movq %mm0, 32(%edx) + movq %mm1, 40(%edx) + movq %mm2, 48(%edx) + movq %mm3, 56(%edx) + movq %mm4, 32(%eax) + movq %mm5, 40(%eax) + movq %mm6, 48(%eax) + movq %mm7, 56(%eax) + incl %ebx + cmpl $128, %ebx + jnz .chacha_ns1 + + xorl %ebx, %ebx +.chacha_ns2: +/* integerify(Z) mod 128 */ + leal 256(%ebp), %eax + leal 512(%ebp), %ecx + movl 448(%ebp), %edx + andl $0x7F, %edx + shll $8, %edx + addl %edx, %ecx +/* blkxor(Z, V) */ + movq 0(%eax), %mm0 + movq 8(%eax), %mm1 + movq 16(%eax), %mm2 + movq 24(%eax), %mm3 + movq 32(%eax), %mm4 + movq 40(%eax), %mm5 + movq 48(%eax), %mm6 + movq 56(%eax), %mm7 + pxor 0(%ecx), %mm0 + pxor 8(%ecx), %mm1 + pxor 16(%ecx), %mm2 + pxor 24(%ecx), %mm3 + pxor 32(%ecx), %mm4 + pxor 40(%ecx), %mm5 + pxor 48(%ecx), %mm6 + pxor 56(%ecx), %mm7 + movq %mm0, 0(%eax) + movq %mm1, 8(%eax) + movq %mm2, 16(%eax) + movq %mm3, 24(%eax) + movq %mm4, 32(%eax) + movq %mm5, 40(%eax) + movq %mm6, 48(%eax) + movq %mm7, 56(%eax) + movq 64(%eax), %mm0 + movq 72(%eax), %mm1 + movq 80(%eax), %mm2 + movq 88(%eax), %mm3 + movq 96(%eax), %mm4 + movq 104(%eax), %mm5 + movq 112(%eax), %mm6 + movq 120(%eax), %mm7 + pxor 64(%ecx), %mm0 + pxor 72(%ecx), %mm1 + pxor 80(%ecx), %mm2 + pxor 88(%ecx), %mm3 + pxor 96(%ecx), %mm4 + pxor 104(%ecx), %mm5 + pxor 112(%ecx), %mm6 + pxor 120(%ecx), %mm7 + movq %mm0, 64(%eax) + movq %mm1, 72(%eax) + movq %mm2, 80(%eax) + movq %mm3, 88(%eax) + movq %mm4, 96(%eax) + movq %mm5, 104(%eax) + movq %mm6, 112(%eax) + movq %mm7, 120(%eax) + movq 128(%eax), %mm0 + movq 136(%eax), %mm1 + movq 144(%eax), %mm2 + movq 152(%eax), %mm3 + movq 160(%eax), %mm4 + movq 168(%eax), %mm5 + movq 176(%eax), %mm6 + movq 184(%eax), %mm7 + pxor 128(%ecx), %mm0 + pxor 136(%ecx), %mm1 + pxor 144(%ecx), %mm2 + pxor 152(%ecx), %mm3 + pxor 160(%ecx), %mm4 + pxor 168(%ecx), %mm5 + pxor 176(%ecx), %mm6 + pxor 184(%ecx), %mm7 + movq %mm0, 128(%eax) + movq %mm1, 136(%eax) + movq %mm2, 144(%eax) + movq %mm3, 152(%eax) + movq %mm4, 160(%eax) + movq %mm5, 168(%eax) + movq %mm6, 176(%eax) + movq %mm7, 184(%eax) + movq 192(%eax), %mm0 + movq 200(%eax), %mm1 + movq 208(%eax), %mm2 + movq 216(%eax), %mm3 + movq 224(%eax), %mm4 + movq 232(%eax), %mm5 + movq 240(%eax), %mm6 + movq 248(%eax), %mm7 + pxor 192(%ecx), %mm0 + pxor 200(%ecx), %mm1 + pxor 208(%ecx), %mm2 + pxor 216(%ecx), %mm3 + pxor 224(%ecx), %mm4 + pxor 232(%ecx), %mm5 + pxor 240(%ecx), %mm6 + pxor 248(%ecx), %mm7 + movq %mm0, 192(%eax) + movq %mm1, 200(%eax) + movq %mm2, 208(%eax) + movq %mm3, 216(%eax) + movq %mm4, 224(%eax) + movq %mm5, 232(%eax) + movq %mm6, 240(%eax) + movq %mm7, 248(%eax) +/* blkmix(Z) */ + leal 256(%ebp), %eax + movl %eax, 0(%esp) + leal 448(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_chacha + leal 320(%ebp), %eax + movl %eax, 0(%esp) + leal 256(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_chacha + leal 384(%ebp), %eax + movl %eax, 0(%esp) + leal 320(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_chacha + leal 448(%ebp), %eax + movl %eax, 0(%esp) + leal 384(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_chacha + leal 320(%ebp), %eax + leal 384(%ebp), %edx + movq 0(%eax), %mm0 + movq 8(%eax), %mm1 + movq 16(%eax), %mm2 + movq 24(%eax), %mm3 + movq 0(%edx), %mm4 + movq 8(%edx), %mm5 + movq 16(%edx), %mm6 + movq 24(%edx), %mm7 + movq %mm0, 0(%edx) + movq %mm1, 8(%edx) + movq %mm2, 16(%edx) + movq %mm3, 24(%edx) + movq %mm4, 0(%eax) + movq %mm5, 8(%eax) + movq %mm6, 16(%eax) + movq %mm7, 24(%eax) + movq 32(%eax), %mm0 + movq 40(%eax), %mm1 + movq 48(%eax), %mm2 + movq 56(%eax), %mm3 + movq 32(%edx), %mm4 + movq 40(%edx), %mm5 + movq 48(%edx), %mm6 + movq 56(%edx), %mm7 + movq %mm0, 32(%edx) + movq %mm1, 40(%edx) + movq %mm2, 48(%edx) + movq %mm3, 56(%edx) + movq %mm4, 32(%eax) + movq %mm5, 40(%eax) + movq %mm6, 48(%eax) + movq %mm7, 56(%eax) + incl %ebx + cmpl $128, %ebx + jnz .chacha_ns2 + + xorl %ebx, %ebx +.salsa_ns1: +/* blkcpy(V, X) */ + leal 512(%ebp), %eax + movl %ebx, %edx + movl $8, %ecx + shll %cl, %edx + addl %edx, %eax + movq 0(%ebp), %mm0 + movq 8(%ebp), %mm1 + movq 16(%ebp), %mm2 + movq 24(%ebp), %mm3 + movq 32(%ebp), %mm4 + movq 40(%ebp), %mm5 + movq 48(%ebp), %mm6 + movq 56(%ebp), %mm7 + movq %mm0, 0(%eax) + movq %mm1, 8(%eax) + movq %mm2, 16(%eax) + movq %mm3, 24(%eax) + movq %mm4, 32(%eax) + movq %mm5, 40(%eax) + movq %mm6, 48(%eax) + movq %mm7, 56(%eax) + movq 64(%ebp), %mm0 + movq 72(%ebp), %mm1 + movq 80(%ebp), %mm2 + movq 88(%ebp), %mm3 + movq 96(%ebp), %mm4 + movq 104(%ebp), %mm5 + movq 112(%ebp), %mm6 + movq 120(%ebp), %mm7 + movq %mm0, 64(%eax) + movq %mm1, 72(%eax) + movq %mm2, 80(%eax) + movq %mm3, 88(%eax) + movq %mm4, 96(%eax) + movq %mm5, 104(%eax) + movq %mm6, 112(%eax) + movq %mm7, 120(%eax) + movq 128(%ebp), %mm0 + movq 136(%ebp), %mm1 + movq 144(%ebp), %mm2 + movq 152(%ebp), %mm3 + movq 160(%ebp), %mm4 + movq 168(%ebp), %mm5 + movq 176(%ebp), %mm6 + movq 184(%ebp), %mm7 + movq %mm0, 128(%eax) + movq %mm1, 136(%eax) + movq %mm2, 144(%eax) + movq %mm3, 152(%eax) + movq %mm4, 160(%eax) + movq %mm5, 168(%eax) + movq %mm6, 176(%eax) + movq %mm7, 184(%eax) + movq 192(%ebp), %mm0 + movq 200(%ebp), %mm1 + movq 208(%ebp), %mm2 + movq 216(%ebp), %mm3 + movq 224(%ebp), %mm4 + movq 232(%ebp), %mm5 + movq 240(%ebp), %mm6 + movq 248(%ebp), %mm7 + movq %mm0, 192(%eax) + movq %mm1, 200(%eax) + movq %mm2, 208(%eax) + movq %mm3, 216(%eax) + movq %mm4, 224(%eax) + movq %mm5, 232(%eax) + movq %mm6, 240(%eax) + movq %mm7, 248(%eax) +/* blkmix(X) */ + movl %ebp, 0(%esp) + leal 192(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_salsa + leal 64(%ebp), %eax + movl %eax, 0(%esp) + movl %ebp, 4(%esp) + call neoscrypt_xor_salsa + leal 128(%ebp), %eax + movl %eax, 0(%esp) + leal 64(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_salsa + leal 192(%ebp), %eax + movl %eax, 0(%esp) + leal 128(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_salsa + leal 64(%ebp), %eax + leal 128(%ebp), %edx + movq 0(%eax), %mm0 + movq 8(%eax), %mm1 + movq 16(%eax), %mm2 + movq 24(%eax), %mm3 + movq 0(%edx), %mm4 + movq 8(%edx), %mm5 + movq 16(%edx), %mm6 + movq 24(%edx), %mm7 + movq %mm0, 0(%edx) + movq %mm1, 8(%edx) + movq %mm2, 16(%edx) + movq %mm3, 24(%edx) + movq %mm4, 0(%eax) + movq %mm5, 8(%eax) + movq %mm6, 16(%eax) + movq %mm7, 24(%eax) + movq 32(%eax), %mm0 + movq 40(%eax), %mm1 + movq 48(%eax), %mm2 + movq 56(%eax), %mm3 + movq 32(%edx), %mm4 + movq 40(%edx), %mm5 + movq 48(%edx), %mm6 + movq 56(%edx), %mm7 + movq %mm0, 32(%edx) + movq %mm1, 40(%edx) + movq %mm2, 48(%edx) + movq %mm3, 56(%edx) + movq %mm4, 32(%eax) + movq %mm5, 40(%eax) + movq %mm6, 48(%eax) + movq %mm7, 56(%eax) + incl %ebx + cmpl $128, %ebx + jnz .salsa_ns1 + + xorl %ebx, %ebx +.salsa_ns2: +/* integerify(X) mod 128 */ + leal 512(%ebp), %ecx + movl 192(%ebp), %edx + andl $0x7F, %edx + shll $8, %edx + addl %edx, %ecx +/* blkxor(X, V) */ + movq 0(%ebp), %mm0 + movq 8(%ebp), %mm1 + movq 16(%ebp), %mm2 + movq 24(%ebp), %mm3 + movq 32(%ebp), %mm4 + movq 40(%ebp), %mm5 + movq 48(%ebp), %mm6 + movq 56(%ebp), %mm7 + pxor 0(%ecx), %mm0 + pxor 8(%ecx), %mm1 + pxor 16(%ecx), %mm2 + pxor 24(%ecx), %mm3 + pxor 32(%ecx), %mm4 + pxor 40(%ecx), %mm5 + pxor 48(%ecx), %mm6 + pxor 56(%ecx), %mm7 + movq %mm0, 0(%ebp) + movq %mm1, 8(%ebp) + movq %mm2, 16(%ebp) + movq %mm3, 24(%ebp) + movq %mm4, 32(%ebp) + movq %mm5, 40(%ebp) + movq %mm6, 48(%ebp) + movq %mm7, 56(%ebp) + movq 64(%ebp), %mm0 + movq 72(%ebp), %mm1 + movq 80(%ebp), %mm2 + movq 88(%ebp), %mm3 + movq 96(%ebp), %mm4 + movq 104(%ebp), %mm5 + movq 112(%ebp), %mm6 + movq 120(%ebp), %mm7 + pxor 64(%ecx), %mm0 + pxor 72(%ecx), %mm1 + pxor 80(%ecx), %mm2 + pxor 88(%ecx), %mm3 + pxor 96(%ecx), %mm4 + pxor 104(%ecx), %mm5 + pxor 112(%ecx), %mm6 + pxor 120(%ecx), %mm7 + movq %mm0, 64(%ebp) + movq %mm1, 72(%ebp) + movq %mm2, 80(%ebp) + movq %mm3, 88(%ebp) + movq %mm4, 96(%ebp) + movq %mm5, 104(%ebp) + movq %mm6, 112(%ebp) + movq %mm7, 120(%ebp) + movq 128(%ebp), %mm0 + movq 136(%ebp), %mm1 + movq 144(%ebp), %mm2 + movq 152(%ebp), %mm3 + movq 160(%ebp), %mm4 + movq 168(%ebp), %mm5 + movq 176(%ebp), %mm6 + movq 184(%ebp), %mm7 + pxor 128(%ecx), %mm0 + pxor 136(%ecx), %mm1 + pxor 144(%ecx), %mm2 + pxor 152(%ecx), %mm3 + pxor 160(%ecx), %mm4 + pxor 168(%ecx), %mm5 + pxor 176(%ecx), %mm6 + pxor 184(%ecx), %mm7 + movq %mm0, 128(%ebp) + movq %mm1, 136(%ebp) + movq %mm2, 144(%ebp) + movq %mm3, 152(%ebp) + movq %mm4, 160(%ebp) + movq %mm5, 168(%ebp) + movq %mm6, 176(%ebp) + movq %mm7, 184(%ebp) + movq 192(%ebp), %mm0 + movq 200(%ebp), %mm1 + movq 208(%ebp), %mm2 + movq 216(%ebp), %mm3 + movq 224(%ebp), %mm4 + movq 232(%ebp), %mm5 + movq 240(%ebp), %mm6 + movq 248(%ebp), %mm7 + pxor 192(%ecx), %mm0 + pxor 200(%ecx), %mm1 + pxor 208(%ecx), %mm2 + pxor 216(%ecx), %mm3 + pxor 224(%ecx), %mm4 + pxor 232(%ecx), %mm5 + pxor 240(%ecx), %mm6 + pxor 248(%ecx), %mm7 + movq %mm0, 192(%ebp) + movq %mm1, 200(%ebp) + movq %mm2, 208(%ebp) + movq %mm3, 216(%ebp) + movq %mm4, 224(%ebp) + movq %mm5, 232(%ebp) + movq %mm6, 240(%ebp) + movq %mm7, 248(%ebp) +/* blkmix(X) */ + movl %ebp, 0(%esp) + leal 192(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_salsa + leal 64(%ebp), %eax + movl %eax, 0(%esp) + movl %ebp, 4(%esp) + call neoscrypt_xor_salsa + leal 128(%ebp), %eax + movl %eax, 0(%esp) + leal 64(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_salsa + leal 192(%ebp), %eax + movl %eax, 0(%esp) + leal 128(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_salsa + leal 64(%ebp), %eax + leal 128(%ebp), %edx + movq 0(%eax), %mm0 + movq 8(%eax), %mm1 + movq 16(%eax), %mm2 + movq 24(%eax), %mm3 + movq 0(%edx), %mm4 + movq 8(%edx), %mm5 + movq 16(%edx), %mm6 + movq 24(%edx), %mm7 + movq %mm0, 0(%edx) + movq %mm1, 8(%edx) + movq %mm2, 16(%edx) + movq %mm3, 24(%edx) + movq %mm4, 0(%eax) + movq %mm5, 8(%eax) + movq %mm6, 16(%eax) + movq %mm7, 24(%eax) + movq 32(%eax), %mm0 + movq 40(%eax), %mm1 + movq 48(%eax), %mm2 + movq 56(%eax), %mm3 + movq 32(%edx), %mm4 + movq 40(%edx), %mm5 + movq 48(%edx), %mm6 + movq 56(%edx), %mm7 + movq %mm0, 32(%edx) + movq %mm1, 40(%edx) + movq %mm2, 48(%edx) + movq %mm3, 56(%edx) + movq %mm4, 32(%eax) + movq %mm5, 40(%eax) + movq %mm6, 48(%eax) + movq %mm7, 56(%eax) + incl %ebx + cmpl $128, %ebx + jnz .salsa_ns2 + +/* blkxor(X, Z) */ + leal 256(%ebp), %ecx + movq 0(%ebp), %mm0 + movq 8(%ebp), %mm1 + movq 16(%ebp), %mm2 + movq 24(%ebp), %mm3 + movq 32(%ebp), %mm4 + movq 40(%ebp), %mm5 + movq 48(%ebp), %mm6 + movq 56(%ebp), %mm7 + pxor 0(%ecx), %mm0 + pxor 8(%ecx), %mm1 + pxor 16(%ecx), %mm2 + pxor 24(%ecx), %mm3 + pxor 32(%ecx), %mm4 + pxor 40(%ecx), %mm5 + pxor 48(%ecx), %mm6 + pxor 56(%ecx), %mm7 + movq %mm0, 0(%ebp) + movq %mm1, 8(%ebp) + movq %mm2, 16(%ebp) + movq %mm3, 24(%ebp) + movq %mm4, 32(%ebp) + movq %mm5, 40(%ebp) + movq %mm6, 48(%ebp) + movq %mm7, 56(%ebp) + movq 64(%ebp), %mm0 + movq 72(%ebp), %mm1 + movq 80(%ebp), %mm2 + movq 88(%ebp), %mm3 + movq 96(%ebp), %mm4 + movq 104(%ebp), %mm5 + movq 112(%ebp), %mm6 + movq 120(%ebp), %mm7 + pxor 64(%ecx), %mm0 + pxor 72(%ecx), %mm1 + pxor 80(%ecx), %mm2 + pxor 88(%ecx), %mm3 + pxor 96(%ecx), %mm4 + pxor 104(%ecx), %mm5 + pxor 112(%ecx), %mm6 + pxor 120(%ecx), %mm7 + movq %mm0, 64(%ebp) + movq %mm1, 72(%ebp) + movq %mm2, 80(%ebp) + movq %mm3, 88(%ebp) + movq %mm4, 96(%ebp) + movq %mm5, 104(%ebp) + movq %mm6, 112(%ebp) + movq %mm7, 120(%ebp) + movq 128(%ebp), %mm0 + movq 136(%ebp), %mm1 + movq 144(%ebp), %mm2 + movq 152(%ebp), %mm3 + movq 160(%ebp), %mm4 + movq 168(%ebp), %mm5 + movq 176(%ebp), %mm6 + movq 184(%ebp), %mm7 + pxor 128(%ecx), %mm0 + pxor 136(%ecx), %mm1 + pxor 144(%ecx), %mm2 + pxor 152(%ecx), %mm3 + pxor 160(%ecx), %mm4 + pxor 168(%ecx), %mm5 + pxor 176(%ecx), %mm6 + pxor 184(%ecx), %mm7 + movq %mm0, 128(%ebp) + movq %mm1, 136(%ebp) + movq %mm2, 144(%ebp) + movq %mm3, 152(%ebp) + movq %mm4, 160(%ebp) + movq %mm5, 168(%ebp) + movq %mm6, 176(%ebp) + movq %mm7, 184(%ebp) + movq 192(%ebp), %mm0 + movq 200(%ebp), %mm1 + movq 208(%ebp), %mm2 + movq 216(%ebp), %mm3 + movq 224(%ebp), %mm4 + movq 232(%ebp), %mm5 + movq 240(%ebp), %mm6 + movq 248(%ebp), %mm7 + pxor 192(%ecx), %mm0 + pxor 200(%ecx), %mm1 + pxor 208(%ecx), %mm2 + pxor 216(%ecx), %mm3 + pxor 224(%ecx), %mm4 + pxor 232(%ecx), %mm5 + pxor 240(%ecx), %mm6 + pxor 248(%ecx), %mm7 + movq %mm0, 192(%ebp) + movq %mm1, 200(%ebp) + movq %mm2, 208(%ebp) + movq %mm3, 216(%ebp) + movq %mm4, 224(%ebp) + movq %mm5, 232(%ebp) + movq %mm6, 240(%ebp) + movq %mm7, 248(%ebp) + +/* FastKDF */ +#ifdef OPT + movl %esi, 0(%esp) + movl %ebp, 4(%esp) + movl %edi, 8(%esp) + movl $1, 12(%esp) +#if defined(WIN32) || defined(__APPLE__) + call _neoscrypt_fastkdf_opt +#else + call neoscrypt_fastkdf_opt +#endif /* (WIN32) || (__APPLE__) */ +#else + movl %esi, 0(%esp) + movl $80, 4(%esp) + movl %ebp, 8(%esp) + movl $256, 12(%esp) + movl $32, 16(%esp) + movl %edi, 20(%esp) + movl $32, 24(%esp) +#if defined(WIN32) || defined(__APPLE__) + call _neoscrypt_fastkdf +#else + call neoscrypt_fastkdf +#endif /* (WIN32) || (__APPLE__) */ +#endif /* OPT */ + +#ifdef WIN32 +/* free memory */ + movl 32(%esp), %eax + movl %eax, 0(%esp) + call _free +/* restore stack */ + addl $64, %esp +#else +/* restore stack */ + movl 32(%esp), %esp +#endif + popl %edi + popl %esi + popl %ebp + popl %ebx + emms + ret + +.neoscrypt_sse2: +/* blkcpy(Z, X) */ + leal 256(%ebp), %eax + movdqa 0(%ebp), %xmm0 + movdqa 16(%ebp), %xmm1 + movdqa 32(%ebp), %xmm2 + movdqa 48(%ebp), %xmm3 + movdqa 64(%ebp), %xmm4 + movdqa 80(%ebp), %xmm5 + movdqa 96(%ebp), %xmm6 + movdqa 112(%ebp), %xmm7 + movdqa %xmm0, 0(%eax) + movdqa %xmm1, 16(%eax) + movdqa %xmm2, 32(%eax) + movdqa %xmm3, 48(%eax) + movdqa %xmm4, 64(%eax) + movdqa %xmm5, 80(%eax) + movdqa %xmm6, 96(%eax) + movdqa %xmm7, 112(%eax) + movdqa 128(%ebp), %xmm0 + movdqa 144(%ebp), %xmm1 + movdqa 160(%ebp), %xmm2 + movdqa 176(%ebp), %xmm3 + movdqa 192(%ebp), %xmm4 + movdqa 208(%ebp), %xmm5 + movdqa 224(%ebp), %xmm6 + movdqa 240(%ebp), %xmm7 + movdqa %xmm0, 128(%eax) + movdqa %xmm1, 144(%eax) + movdqa %xmm2, 160(%eax) + movdqa %xmm3, 176(%eax) + movdqa %xmm4, 192(%eax) + movdqa %xmm5, 208(%eax) + movdqa %xmm6, 224(%eax) + movdqa %xmm7, 240(%eax) + + movl $10, 8(%esp) + + xorl %ebx, %ebx +.chacha_ns1_sse2: +/* blkcpy(V, Z) */ + leal 512(%ebp), %eax + movl %ebx, %edx + movb $8, %cl + shll %cl, %edx + leal 256(%ebp), %ecx + addl %edx, %eax + movdqa 0(%ecx), %xmm0 + movdqa 16(%ecx), %xmm1 + movdqa 32(%ecx), %xmm2 + movdqa 48(%ecx), %xmm3 + movdqa 64(%ecx), %xmm4 + movdqa 80(%ecx), %xmm5 + movdqa 96(%ecx), %xmm6 + movdqa 112(%ecx), %xmm7 + movdqa %xmm0, 0(%eax) + movdqa %xmm1, 16(%eax) + movdqa %xmm2, 32(%eax) + movdqa %xmm3, 48(%eax) + movdqa %xmm4, 64(%eax) + movdqa %xmm5, 80(%eax) + movdqa %xmm6, 96(%eax) + movdqa %xmm7, 112(%eax) + movdqa 128(%ecx), %xmm0 + movdqa 144(%ecx), %xmm1 + movdqa 160(%ecx), %xmm2 + movdqa 176(%ecx), %xmm3 + movdqa 192(%ecx), %xmm4 + movdqa 208(%ecx), %xmm5 + movdqa 224(%ecx), %xmm6 + movdqa 240(%ecx), %xmm7 + movdqa %xmm0, 128(%eax) + movdqa %xmm1, 144(%eax) + movdqa %xmm2, 160(%eax) + movdqa %xmm3, 176(%eax) + movdqa %xmm4, 192(%eax) + movdqa %xmm5, 208(%eax) + movdqa %xmm6, 224(%eax) + movdqa %xmm7, 240(%eax) +/* blkmix(Z) */ + leal 256(%ebp), %eax + movl %eax, 0(%esp) + leal 448(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_chacha_sse2 + leal 320(%ebp), %eax + movl %eax, 0(%esp) + leal 256(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_chacha_sse2 + leal 384(%ebp), %eax + movl %eax, 0(%esp) + leal 320(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_chacha_sse2 + leal 448(%ebp), %eax + movl %eax, 0(%esp) + leal 384(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_chacha_sse2 + leal 320(%ebp), %eax + leal 384(%ebp), %edx + movdqa 0(%eax), %xmm0 + movdqa 16(%eax), %xmm1 + movdqa 32(%eax), %xmm2 + movdqa 48(%eax), %xmm3 + movdqa 0(%edx), %xmm4 + movdqa 16(%edx), %xmm5 + movdqa 32(%edx), %xmm6 + movdqa 48(%edx), %xmm7 + movdqa %xmm0, 0(%edx) + movdqa %xmm1, 16(%edx) + movdqa %xmm2, 32(%edx) + movdqa %xmm3, 48(%edx) + movdqa %xmm4, 0(%eax) + movdqa %xmm5, 16(%eax) + movdqa %xmm6, 32(%eax) + movdqa %xmm7, 48(%eax) + incl %ebx + cmpl $128, %ebx + jnz .chacha_ns1_sse2 + + xorl %ebx, %ebx +.chacha_ns2_sse2: +/* integerify(Z) mod 128 */ + leal 256(%ebp), %eax + leal 512(%ebp), %ecx + movl 448(%ebp), %edx + andl $0x7F, %edx + shll $8, %edx + addl %edx, %ecx +/* blkxor(Z, V) */ + movdqa 0(%eax), %xmm0 + movdqa 16(%eax), %xmm1 + movdqa 32(%eax), %xmm2 + movdqa 48(%eax), %xmm3 + movdqa 64(%eax), %xmm4 + movdqa 80(%eax), %xmm5 + movdqa 96(%eax), %xmm6 + movdqa 112(%eax), %xmm7 + pxor 0(%ecx), %xmm0 + pxor 16(%ecx), %xmm1 + pxor 32(%ecx), %xmm2 + pxor 48(%ecx), %xmm3 + pxor 64(%ecx), %xmm4 + pxor 80(%ecx), %xmm5 + pxor 96(%ecx), %xmm6 + pxor 112(%ecx), %xmm7 + movdqa %xmm0, 0(%eax) + movdqa %xmm1, 16(%eax) + movdqa %xmm2, 32(%eax) + movdqa %xmm3, 48(%eax) + movdqa %xmm4, 64(%eax) + movdqa %xmm5, 80(%eax) + movdqa %xmm6, 96(%eax) + movdqa %xmm7, 112(%eax) + movdqa 128(%eax), %xmm0 + movdqa 144(%eax), %xmm1 + movdqa 160(%eax), %xmm2 + movdqa 176(%eax), %xmm3 + movdqa 192(%eax), %xmm4 + movdqa 208(%eax), %xmm5 + movdqa 224(%eax), %xmm6 + movdqa 240(%eax), %xmm7 + pxor 128(%ecx), %xmm0 + pxor 144(%ecx), %xmm1 + pxor 160(%ecx), %xmm2 + pxor 176(%ecx), %xmm3 + pxor 192(%ecx), %xmm4 + pxor 208(%ecx), %xmm5 + pxor 224(%ecx), %xmm6 + pxor 240(%ecx), %xmm7 + movdqa %xmm0, 128(%eax) + movdqa %xmm1, 144(%eax) + movdqa %xmm2, 160(%eax) + movdqa %xmm3, 176(%eax) + movdqa %xmm4, 192(%eax) + movdqa %xmm5, 208(%eax) + movdqa %xmm6, 224(%eax) + movdqa %xmm7, 240(%eax) +/* blkmix(Z) */ + leal 256(%ebp), %eax + movl %eax, 0(%esp) + leal 448(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_chacha_sse2 + leal 320(%ebp), %eax + movl %eax, 0(%esp) + leal 256(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_chacha_sse2 + leal 384(%ebp), %eax + movl %eax, 0(%esp) + leal 320(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_chacha_sse2 + leal 448(%ebp), %eax + movl %eax, 0(%esp) + leal 384(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_chacha_sse2 + leal 320(%ebp), %eax + leal 384(%ebp), %edx + movdqa 0(%eax), %xmm0 + movdqa 16(%eax), %xmm1 + movdqa 32(%eax), %xmm2 + movdqa 48(%eax), %xmm3 + movdqa 0(%edx), %xmm4 + movdqa 16(%edx), %xmm5 + movdqa 32(%edx), %xmm6 + movdqa 48(%edx), %xmm7 + movdqa %xmm0, 0(%edx) + movdqa %xmm1, 16(%edx) + movdqa %xmm2, 32(%edx) + movdqa %xmm3, 48(%edx) + movdqa %xmm4, 0(%eax) + movdqa %xmm5, 16(%eax) + movdqa %xmm6, 32(%eax) + movdqa %xmm7, 48(%eax) + incl %ebx + cmpl $128, %ebx + jnz .chacha_ns2_sse2 + + movl %ebp, 0(%esp) + movl $4, 4(%esp) + call neoscrypt_salsa_tangle_sse2 + + xorl %ebx, %ebx +.salsa_ns1_sse2: +/* blkcpy(V, X) */ + leal 512(%ebp), %eax + movl %ebx, %edx + movl $8, %ecx + shll %cl, %edx + addl %edx, %eax + movdqa 0(%ebp), %xmm0 + movdqa 16(%ebp), %xmm1 + movdqa 32(%ebp), %xmm2 + movdqa 48(%ebp), %xmm3 + movdqa 64(%ebp), %xmm4 + movdqa 80(%ebp), %xmm5 + movdqa 96(%ebp), %xmm6 + movdqa 112(%ebp), %xmm7 + movdqa %xmm0, 0(%eax) + movdqa %xmm1, 16(%eax) + movdqa %xmm2, 32(%eax) + movdqa %xmm3, 48(%eax) + movdqa %xmm4, 64(%eax) + movdqa %xmm5, 80(%eax) + movdqa %xmm6, 96(%eax) + movdqa %xmm7, 112(%eax) + movdqa 128(%ebp), %xmm0 + movdqa 144(%ebp), %xmm1 + movdqa 160(%ebp), %xmm2 + movdqa 176(%ebp), %xmm3 + movdqa 192(%ebp), %xmm4 + movdqa 208(%ebp), %xmm5 + movdqa 224(%ebp), %xmm6 + movdqa 240(%ebp), %xmm7 + movdqa %xmm0, 128(%eax) + movdqa %xmm1, 144(%eax) + movdqa %xmm2, 160(%eax) + movdqa %xmm3, 176(%eax) + movdqa %xmm4, 192(%eax) + movdqa %xmm5, 208(%eax) + movdqa %xmm6, 224(%eax) + movdqa %xmm7, 240(%eax) +/* blkmix(X) */ + movl %ebp, 0(%esp) + leal 192(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_salsa_sse2 + leal 64(%ebp), %eax + movl %eax, 0(%esp) + movl %ebp, 4(%esp) + call neoscrypt_xor_salsa_sse2 + leal 128(%ebp), %eax + movl %eax, 0(%esp) + leal 64(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_salsa_sse2 + leal 192(%ebp), %eax + movl %eax, 0(%esp) + leal 128(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_salsa_sse2 + leal 64(%ebp), %eax + leal 128(%ebp), %edx + movdqa 0(%eax), %xmm0 + movdqa 16(%eax), %xmm1 + movdqa 32(%eax), %xmm2 + movdqa 48(%eax), %xmm3 + movdqa 0(%edx), %xmm4 + movdqa 16(%edx), %xmm5 + movdqa 32(%edx), %xmm6 + movdqa 48(%edx), %xmm7 + movdqa %xmm0, 0(%edx) + movdqa %xmm1, 16(%edx) + movdqa %xmm2, 32(%edx) + movdqa %xmm3, 48(%edx) + movdqa %xmm4, 0(%eax) + movdqa %xmm5, 16(%eax) + movdqa %xmm6, 32(%eax) + movdqa %xmm7, 48(%eax) + incl %ebx + cmpl $128, %ebx + jnz .salsa_ns1_sse2 + + xorl %ebx, %ebx +.salsa_ns2_sse2: +/* integerify(X) mod 128 */ + leal 512(%ebp), %ecx + movl 192(%ebp), %edx + andl $0x7F, %edx + shll $8, %edx + addl %edx, %ecx +/* blkxor(X, V) */ + movdqa 0(%ebp), %xmm0 + movdqa 16(%ebp), %xmm1 + movdqa 32(%ebp), %xmm2 + movdqa 48(%ebp), %xmm3 + movdqa 64(%ebp), %xmm4 + movdqa 80(%ebp), %xmm5 + movdqa 96(%ebp), %xmm6 + movdqa 112(%ebp), %xmm7 + pxor 0(%ecx), %xmm0 + pxor 16(%ecx), %xmm1 + pxor 32(%ecx), %xmm2 + pxor 48(%ecx), %xmm3 + pxor 64(%ecx), %xmm4 + pxor 80(%ecx), %xmm5 + pxor 96(%ecx), %xmm6 + pxor 112(%ecx), %xmm7 + movdqa %xmm0, 0(%ebp) + movdqa %xmm1, 16(%ebp) + movdqa %xmm2, 32(%ebp) + movdqa %xmm3, 48(%ebp) + movdqa %xmm4, 64(%ebp) + movdqa %xmm5, 80(%ebp) + movdqa %xmm6, 96(%ebp) + movdqa %xmm7, 112(%ebp) + movdqa 128(%ebp), %xmm0 + movdqa 144(%ebp), %xmm1 + movdqa 160(%ebp), %xmm2 + movdqa 176(%ebp), %xmm3 + movdqa 192(%ebp), %xmm4 + movdqa 208(%ebp), %xmm5 + movdqa 224(%ebp), %xmm6 + movdqa 240(%ebp), %xmm7 + pxor 128(%ecx), %xmm0 + pxor 144(%ecx), %xmm1 + pxor 160(%ecx), %xmm2 + pxor 176(%ecx), %xmm3 + pxor 192(%ecx), %xmm4 + pxor 208(%ecx), %xmm5 + pxor 224(%ecx), %xmm6 + pxor 240(%ecx), %xmm7 + movdqa %xmm0, 128(%ebp) + movdqa %xmm1, 144(%ebp) + movdqa %xmm2, 160(%ebp) + movdqa %xmm3, 176(%ebp) + movdqa %xmm4, 192(%ebp) + movdqa %xmm5, 208(%ebp) + movdqa %xmm6, 224(%ebp) + movdqa %xmm7, 240(%ebp) +/* blkmix(X) */ + movl %ebp, 0(%esp) + leal 192(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_salsa_sse2 + leal 64(%ebp), %eax + movl %eax, 0(%esp) + movl %ebp, 4(%esp) + call neoscrypt_xor_salsa_sse2 + leal 128(%ebp), %eax + movl %eax, 0(%esp) + leal 64(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_salsa_sse2 + leal 192(%ebp), %eax + movl %eax, 0(%esp) + leal 128(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_salsa_sse2 + leal 64(%ebp), %eax + leal 128(%ebp), %edx + movdqa 0(%eax), %xmm0 + movdqa 16(%eax), %xmm1 + movdqa 32(%eax), %xmm2 + movdqa 48(%eax), %xmm3 + movdqa 0(%edx), %xmm4 + movdqa 16(%edx), %xmm5 + movdqa 32(%edx), %xmm6 + movdqa 48(%edx), %xmm7 + movdqa %xmm0, 0(%edx) + movdqa %xmm1, 16(%edx) + movdqa %xmm2, 32(%edx) + movdqa %xmm3, 48(%edx) + movdqa %xmm4, 0(%eax) + movdqa %xmm5, 16(%eax) + movdqa %xmm6, 32(%eax) + movdqa %xmm7, 48(%eax) + incl %ebx + cmpl $128, %ebx + jnz .salsa_ns2_sse2 + + movl %ebp, 0(%esp) + movl $4, 4(%esp) + call neoscrypt_salsa_tangle_sse2 + +/* blkxor(X, Z) */ + leal 256(%ebp), %ecx + movdqa 0(%ebp), %xmm0 + movdqa 16(%ebp), %xmm1 + movdqa 32(%ebp), %xmm2 + movdqa 48(%ebp), %xmm3 + movdqa 64(%ebp), %xmm4 + movdqa 80(%ebp), %xmm5 + movdqa 96(%ebp), %xmm6 + movdqa 112(%ebp), %xmm7 + pxor 0(%ecx), %xmm0 + pxor 16(%ecx), %xmm1 + pxor 32(%ecx), %xmm2 + pxor 48(%ecx), %xmm3 + pxor 64(%ecx), %xmm4 + pxor 80(%ecx), %xmm5 + pxor 96(%ecx), %xmm6 + pxor 112(%ecx), %xmm7 + movdqa %xmm0, 0(%ebp) + movdqa %xmm1, 16(%ebp) + movdqa %xmm2, 32(%ebp) + movdqa %xmm3, 48(%ebp) + movdqa %xmm4, 64(%ebp) + movdqa %xmm5, 80(%ebp) + movdqa %xmm6, 96(%ebp) + movdqa %xmm7, 112(%ebp) + movdqa 128(%ebp), %xmm0 + movdqa 144(%ebp), %xmm1 + movdqa 160(%ebp), %xmm2 + movdqa 176(%ebp), %xmm3 + movdqa 192(%ebp), %xmm4 + movdqa 208(%ebp), %xmm5 + movdqa 224(%ebp), %xmm6 + movdqa 240(%ebp), %xmm7 + pxor 128(%ecx), %xmm0 + pxor 144(%ecx), %xmm1 + pxor 160(%ecx), %xmm2 + pxor 176(%ecx), %xmm3 + pxor 192(%ecx), %xmm4 + pxor 208(%ecx), %xmm5 + pxor 224(%ecx), %xmm6 + pxor 240(%ecx), %xmm7 + movdqa %xmm0, 128(%ebp) + movdqa %xmm1, 144(%ebp) + movdqa %xmm2, 160(%ebp) + movdqa %xmm3, 176(%ebp) + movdqa %xmm4, 192(%ebp) + movdqa %xmm5, 208(%ebp) + movdqa %xmm6, 224(%ebp) + movdqa %xmm7, 240(%ebp) + +/* FastKDF */ +#ifdef OPT + movl %esi, 0(%esp) + movl %ebp, 4(%esp) + movl %edi, 8(%esp) + movl $1, 12(%esp) +#if defined(WIN32) || defined(__APPLE__) + call _neoscrypt_fastkdf_opt +#else + call neoscrypt_fastkdf_opt +#endif /* (WIN32) || (__APPLE__) */ +#else + movl %esi, 0(%esp) + movl $80, 4(%esp) + movl %ebp, 8(%esp) + movl $256, 12(%esp) + movl $32, 16(%esp) + movl %edi, 20(%esp) + movl $32, 24(%esp) +#if defined(WIN32) || defined(__APPLE__) + call _neoscrypt_fastkdf +#else + call neoscrypt_fastkdf +#endif /* (WIN32) || (__APPLE__) */ +#endif /* OPT */ + +#ifdef WIN32 +/* free memory */ + movl 32(%esp), %eax + movl %eax, 0(%esp) + call _free +/* restore stack */ + addl $64, %esp +#else +/* restore stack */ + movl 32(%esp), %esp +#endif + popl %edi + popl %esi + popl %ebp + popl %ebx + ret + +#ifdef SHA256 + +.scrypt: +#ifdef WIN32 +/* attempt to allocate 131200 + 128 bytes of stack space fails miserably; + * have to use malloc() and free() instead */ + subl $64, %esp +/* allocate memory (33 pages of 4Kb each) */ + movl $0x21000, 0(%esp) + call _malloc +/* save memory address */ + movl %eax, 32(%esp) +/* align memory */ + addl $64, %eax + andl $0xFFFFFFC0, %eax +/* memory base: X, Z, V */ + leal 64(%eax), %ebp +#else +/* align stack */ + movl %esp, %eax + andl $0xFFFFFFC0, %esp + subl $0x20100, %esp +/* save unaligned stack */ + movl %eax, 32(%esp) +/* memory base: X, Z, V */ + leal 128(%esp), %ebp +#endif /* WIN32 */ + +/* PBKDF2-HMAC-SHA256 */ + movl $80, %eax + movl %esi, 0(%esp) + movl %eax, 4(%esp) + movl %esi, 8(%esp) + movl %eax, 12(%esp) + movl $1, 16(%esp) + movl %ebp, 20(%esp) + movl $128, 24(%esp) +#if defined(WIN32) || defined(__APPLE__) + call _neoscrypt_pbkdf2_sha256 +#else + call neoscrypt_pbkdf2_sha256 +#endif + +/* SSE2 switch */ + testl $0x1000, %ebx + jnz .scrypt_sse2 + + leal -64(%ebp), %edx + movl %edx, 8(%esp) + movl $4, 12(%esp) + + xorl %ebx, %ebx +.salsa_s1: +/* blkcpy(V, X) */ + leal 128(%ebp), %eax + movl %ebx, %edx + movl $7, %ecx + shll %cl, %edx + addl %edx, %eax + movq 0(%ebp), %mm0 + movq 8(%ebp), %mm1 + movq 16(%ebp), %mm2 + movq 24(%ebp), %mm3 + movq 32(%ebp), %mm4 + movq 40(%ebp), %mm5 + movq 48(%ebp), %mm6 + movq 56(%ebp), %mm7 + movq %mm0, 0(%eax) + movq %mm1, 8(%eax) + movq %mm2, 16(%eax) + movq %mm3, 24(%eax) + movq %mm4, 32(%eax) + movq %mm5, 40(%eax) + movq %mm6, 48(%eax) + movq %mm7, 56(%eax) + movq 64(%ebp), %mm0 + movq 72(%ebp), %mm1 + movq 80(%ebp), %mm2 + movq 88(%ebp), %mm3 + movq 96(%ebp), %mm4 + movq 104(%ebp), %mm5 + movq 112(%ebp), %mm6 + movq 120(%ebp), %mm7 + movq %mm0, 64(%eax) + movq %mm1, 72(%eax) + movq %mm2, 80(%eax) + movq %mm3, 88(%eax) + movq %mm4, 96(%eax) + movq %mm5, 104(%eax) + movq %mm6, 112(%eax) + movq %mm7, 120(%eax) +/* blkmix(X) */ + movl %ebp, 0(%esp) + leal 64(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_salsa + leal 64(%ebp), %eax + movl %eax, 0(%esp) + movl %ebp, 4(%esp) + call neoscrypt_xor_salsa + incl %ebx + cmpl $1024, %ebx + jnz .salsa_s1 + + xorl %ebx, %ebx +.salsa_s2: +/* integerify(X) mod 1024 */ + leal 128(%ebp), %eax + movl 64(%ebp), %edx + andl $0x03FF, %edx + shll $7, %edx + addl %edx, %eax +/* blkxor(X, V) */ + movq 0(%ebp), %mm0 + movq 8(%ebp), %mm1 + movq 16(%ebp), %mm2 + movq 24(%ebp), %mm3 + movq 32(%ebp), %mm4 + movq 40(%ebp), %mm5 + movq 48(%ebp), %mm6 + movq 56(%ebp), %mm7 + pxor 0(%eax), %mm0 + pxor 8(%eax), %mm1 + pxor 16(%eax), %mm2 + pxor 24(%eax), %mm3 + pxor 32(%eax), %mm4 + pxor 40(%eax), %mm5 + pxor 48(%eax), %mm6 + pxor 56(%eax), %mm7 + movq %mm0, 0(%ebp) + movq %mm1, 8(%ebp) + movq %mm2, 16(%ebp) + movq %mm3, 24(%ebp) + movq %mm4, 32(%ebp) + movq %mm5, 40(%ebp) + movq %mm6, 48(%ebp) + movq %mm7, 56(%ebp) + movq 64(%ebp), %mm0 + movq 72(%ebp), %mm1 + movq 80(%ebp), %mm2 + movq 88(%ebp), %mm3 + movq 96(%ebp), %mm4 + movq 104(%ebp), %mm5 + movq 112(%ebp), %mm6 + movq 120(%ebp), %mm7 + pxor 64(%eax), %mm0 + pxor 72(%eax), %mm1 + pxor 80(%eax), %mm2 + pxor 88(%eax), %mm3 + pxor 96(%eax), %mm4 + pxor 104(%eax), %mm5 + pxor 112(%eax), %mm6 + pxor 120(%eax), %mm7 + movq %mm0, 64(%ebp) + movq %mm1, 72(%ebp) + movq %mm2, 80(%ebp) + movq %mm3, 88(%ebp) + movq %mm4, 96(%ebp) + movq %mm5, 104(%ebp) + movq %mm6, 112(%ebp) + movq %mm7, 120(%ebp) +/* blkmix(X) */ + movl %ebp, 0(%esp) + leal 64(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_salsa + leal 64(%ebp), %eax + movl %eax, 0(%esp) + movl %ebp, 4(%esp) + call neoscrypt_xor_salsa + incl %ebx + cmpl $1024, %ebx + jnz .salsa_s2 + +/* PBKDF2-HMAC-SHA256 */ + movl %esi, 0(%esp) + movl $80, 4(%esp) + movl %ebp, 8(%esp) + movl $128, 12(%esp) + movl $1, 16(%esp) + movl %edi, 20(%esp) + movl $32, 24(%esp) +#if defined(WIN32) || defined(__APPLE__) + call _neoscrypt_pbkdf2_sha256 +#else + call neoscrypt_pbkdf2_sha256 +#endif + +#ifdef WIN32 +/* free memory */ + movl 32(%esp), %eax + movl %eax, 0(%esp) + call _free +/* restore stack */ + addl $64, %esp +#else +/* restore stack */ + movl 32(%esp), %esp +#endif + popl %edi + popl %esi + popl %ebp + popl %ebx + emms + ret + +.scrypt_sse2: + movl %ebp, 0(%esp) + movl $2, 4(%esp) + call neoscrypt_salsa_tangle_sse2 + + movl $4, 8(%esp) + + xorl %ebx, %ebx +.salsa_s1_sse2: +/* blkcpy(V, X) */ + leal 128(%ebp), %eax + movl %ebx, %edx + movl $7, %ecx + shll %cl, %edx + addl %edx, %eax + movdqa 0(%ebp), %xmm0 + movdqa 16(%ebp), %xmm1 + movdqa 32(%ebp), %xmm2 + movdqa 48(%ebp), %xmm3 + movdqa 64(%ebp), %xmm4 + movdqa 80(%ebp), %xmm5 + movdqa 96(%ebp), %xmm6 + movdqa 112(%ebp), %xmm7 + movdqa %xmm0, 0(%eax) + movdqa %xmm1, 16(%eax) + movdqa %xmm2, 32(%eax) + movdqa %xmm3, 48(%eax) + movdqa %xmm4, 64(%eax) + movdqa %xmm5, 80(%eax) + movdqa %xmm6, 96(%eax) + movdqa %xmm7, 112(%eax) +/* blkmix(X) */ + movl %ebp, 0(%esp) + leal 64(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_salsa_sse2 + leal 64(%ebp), %eax + movl %eax, 0(%esp) + movl %ebp, 4(%esp) + call neoscrypt_xor_salsa_sse2 + incl %ebx + cmpl $1024, %ebx + jnz .salsa_s1_sse2 + + xorl %ebx, %ebx +.salsa_s2_sse2: +/* integerify(X) mod 1024 */ + leal 128(%ebp), %eax + movl 64(%ebp), %edx + andl $0x03FF, %edx + shll $7, %edx + addl %edx, %eax +/* blkxor(X, V) */ + movdqa 0(%ebp), %xmm0 + movdqa 16(%ebp), %xmm1 + movdqa 32(%ebp), %xmm2 + movdqa 48(%ebp), %xmm3 + movdqa 64(%ebp), %xmm4 + movdqa 80(%ebp), %xmm5 + movdqa 96(%ebp), %xmm6 + movdqa 112(%ebp), %xmm7 + pxor 0(%eax), %xmm0 + pxor 16(%eax), %xmm1 + pxor 32(%eax), %xmm2 + pxor 48(%eax), %xmm3 + pxor 64(%eax), %xmm4 + pxor 80(%eax), %xmm5 + pxor 96(%eax), %xmm6 + pxor 112(%eax), %xmm7 + movdqa %xmm0, 0(%ebp) + movdqa %xmm1, 16(%ebp) + movdqa %xmm2, 32(%ebp) + movdqa %xmm3, 48(%ebp) + movdqa %xmm4, 64(%ebp) + movdqa %xmm5, 80(%ebp) + movdqa %xmm6, 96(%ebp) + movdqa %xmm7, 112(%ebp) +/* blkmix(X) */ + movl %ebp, 0(%esp) + leal 64(%ebp), %edx + movl %edx, 4(%esp) + call neoscrypt_xor_salsa_sse2 + leal 64(%ebp), %eax + movl %eax, 0(%esp) + movl %ebp, 4(%esp) + call neoscrypt_xor_salsa_sse2 + incl %ebx + cmpl $1024, %ebx + jnz .salsa_s2_sse2 + + movl %ebp, 0(%esp) + movl $2, 4(%esp) + call neoscrypt_salsa_tangle_sse2 + +/* PBKDF2-HMAC-SHA256 */ + movl %esi, 0(%esp) + movl $80, 4(%esp) + movl %ebp, 8(%esp) + movl $128, 12(%esp) + movl $1, 16(%esp) + movl %edi, 20(%esp) + movl $32, 24(%esp) +#if defined(WIN32) || defined(__APPLE__) + call _neoscrypt_pbkdf2_sha256 +#else + call neoscrypt_pbkdf2_sha256 +#endif + +#ifdef WIN32 +/* free memory */ + movl 32(%esp), %eax + movl %eax, 0(%esp) + call _free +/* restore stack */ + addl $64, %esp +#else +/* restore stack */ + movl 32(%esp), %esp +#endif + popl %edi + popl %esi + popl %ebp + popl %ebx + ret + +#endif /* SHA256 */ + +#ifdef MINER_4WAY + +/* blake2s_compress_4way(mem) + * i386 (SSE2) BLAKE2s 4-way block compression */ +.globl blake2s_compress_4way +.globl _blake2s_compress_4way +blake2s_compress_4way: +_blake2s_compress_4way: + pushl %ebx + pushl %ebp + pushl %esi + pushl %edi + movl 20(%esp), %edi + +/* initialise */ + leal 448(%edi), %esi + movdqa 0(%edi), %xmm0 + movdqa 16(%edi), %xmm2 + movdqa 32(%edi), %xmm3 + movdqa 48(%edi), %xmm7 + movdqa 64(%edi), %xmm6 + movdqa 80(%edi), %xmm1 + movdqa 96(%edi), %xmm4 + movdqa 112(%edi), %xmm5 +/* movdqa %xmm0, 0(%esi) */ + movdqa %xmm2, 16(%esi) + movdqa %xmm3, 32(%esi) + movdqa %xmm7, 48(%esi) +/* movdqa %xmm6, 64(%esi) */ +/* movdqa %xmm1, 80(%esi) */ +/* movdqa %xmm4, 96(%esi) */ +/* movdqa %xmm5, 112(%esi) */ + movl $0x6A09E667, %eax + movl $0xBB67AE85, %ebx + movl $0x3C6EF372, %ecx + movl $0xA54FF53A, %edx + movl %eax, 128(%esi) + movl %eax, 132(%esi) + movl %eax, 136(%esi) + movl %eax, 140(%esi) + movl %ebx, 144(%esi) + movl %ebx, 148(%esi) + movl %ebx, 152(%esi) + movl %ebx, 156(%esi) + movl %ecx, 160(%esi) + movl %ecx, 164(%esi) + movl %ecx, 168(%esi) + movl %ecx, 172(%esi) + movl %edx, 176(%esi) + movl %edx, 180(%esi) + movl %edx, 184(%esi) + movl %edx, 188(%esi) + movl $0x510E527F, %ebp + movl 128(%edi), %eax + movl 132(%edi), %ebx + movl 136(%edi), %ecx + movl 140(%edi), %edx + xorl %ebp, %eax + xorl %ebp, %ebx + xorl %ebp, %ecx + xorl %ebp, %edx + movl %eax, 192(%esi) + movl %ebx, 196(%esi) + movl %ecx, 200(%esi) + movl %edx, 204(%esi) + movl $0x9B05688C, %ebp + movl 144(%edi), %eax + movl 148(%edi), %ebx + movl 152(%edi), %ecx + movl 156(%edi), %edx + xorl %ebp, %eax + xorl %ebp, %ebx + xorl %ebp, %ecx + xorl %ebp, %edx + movl %eax, 208(%esi) + movl %ebx, 212(%esi) + movl %ecx, 216(%esi) + movl %edx, 220(%esi) + movl $0x1F83D9AB, %ebp + movl 160(%edi), %eax + movl 164(%edi), %ebx + movl 168(%edi), %ecx + movl 172(%edi), %edx + xorl %ebp, %eax + xorl %ebp, %ebx + xorl %ebp, %ecx + xorl %ebp, %edx + movl %eax, 224(%esi) + movl %ebx, 228(%esi) + movl %ecx, 232(%esi) + movl %edx, 236(%esi) + movl $0x5BE0CD19, %ebp + movl 176(%edi), %eax + movl 180(%edi), %ebx + movl 184(%edi), %ecx + movl 188(%edi), %edx + xorl %ebp, %eax + xorl %ebp, %ebx +/* movdqa 0(%esi), %xmm0 */ /* A */ + xorl %ebp, %ecx + xorl %ebp, %edx + paddd 192(%edi), %xmm0 /* A */ + movl %eax, 240(%esi) + movl %ebx, 244(%esi) + movl %ecx, 248(%esi) + movl %edx, 252(%esi) +/* round 0 (A) */ +/* movdqa 64(%esi), %xmm6 */ + movdqa 192(%esi), %xmm3 + paddd %xmm6, %xmm0 + movdqa 128(%esi), %xmm2 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 208(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm6 + movdqa %xmm6, %xmm7 + pslld $20, %xmm6 + psrld $12, %xmm7 + por %xmm7, %xmm6 + paddd %xmm6, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* B */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm2 + movdqa %xmm2, 128(%esi) + pxor %xmm2, %xmm6 + movdqa %xmm6, %xmm7 + paddd 224(%edi), %xmm0 /* B */ + pslld $25, %xmm6 + psrld $7, %xmm7 + por %xmm7, %xmm6 + movdqa %xmm6, 64(%esi) +/* round 0 (B) */ +/* movdqa 80(%esi), %xmm1 */ + movdqa 208(%esi), %xmm3 + paddd %xmm1, %xmm0 + movdqa 144(%esi), %xmm2 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 240(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 32(%esi), %xmm0 /* C */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 208(%esi) + paddd %xmm3, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 256(%edi), %xmm0 /* C */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 +/* movdqa %xmm1, 80(%esi) */ +/* round 0 (C) */ +/* movdqa 96(%esi), %xmm4 */ + movdqa 224(%esi), %xmm3 + paddd %xmm4, %xmm0 + movdqa 160(%esi), %xmm2 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 272(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 48(%esi), %xmm0 /* D */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 224(%esi) + paddd %xmm3, %xmm2 +/* movdqa %xmm2, 160(%esi) */ + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + paddd 288(%edi), %xmm0 /* D */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 +/* movdqa %xmm4, 96(%esi) */ +/* round 0 (D) */ +/* movdqa 112(%esi), %xmm5 */ + movdqa 240(%esi), %xmm3 + paddd %xmm5, %xmm0 + movdqa 176(%esi), %xmm6 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 304(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 0(%esi), %xmm0 /* E */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 +/* movdqa %xmm3, 240(%esi) */ + paddd %xmm3, %xmm6 +/* movdqa %xmm6, 176(%esi) */ + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 320(%edi), %xmm0 /* E */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 +/* movdqa %xmm5, 112(%esi) */ +/* round 0 (E) */ +/* movdqa 80(%esi), %xmm1 */ +/* movdqa 240(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 160(%esi), %xmm2 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 336(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* F */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 240(%esi) + paddd %xmm3, %xmm2 + movdqa %xmm2, 160(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 352(%edi), %xmm0 /* F */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 80(%esi) +/* round 0 (F) */ +/* movdqa 96(%esi), %xmm4 */ + movdqa 192(%esi), %xmm3 + paddd %xmm4, %xmm0 +/* movdqa 176(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + por %xmm7, %xmm3 + paddd 368(%edi), %xmm0 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 32(%esi), %xmm0 /* G */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 +/* movdqa %xmm3, 192(%esi) */ + paddd %xmm3, %xmm6 + movdqa %xmm6, 176(%esi) + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + paddd 384(%edi), %xmm0 /* G */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 96(%esi) +/* round 0 (G) */ +/* movdqa 112(%esi), %xmm5 */ + movdqa 208(%esi), %xmm4 + paddd %xmm5, %xmm0 + movdqa 128(%esi), %xmm6 + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 400(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 48(%esi), %xmm0 /* H */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 +/* movdqa %xmm4, 208(%esi) */ + paddd %xmm4, %xmm6 +/* movdqa %xmm6, 128(%esi) */ + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 416(%edi), %xmm0 /* H */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 112(%esi) +/* round 0 (H) */ + movdqa 64(%esi), %xmm1 + movdqa 224(%esi), %xmm5 + paddd %xmm1, %xmm0 + movdqa 144(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 432(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 0(%esi), %xmm0 /* A */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 +/* movdqa %xmm5, 224(%esi) */ + paddd %xmm5, %xmm2 +/* movdqa %xmm2, 144(%esi) */ + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 416(%edi), %xmm0 /* A */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 +/* movdqa %xmm1, 64(%esi) */ +/* round 1 (A) */ +/* movdqa 64(%esi), %xmm1 */ +/* movdqa 192(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 128(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 352(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* B */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + paddd 256(%edi), %xmm0 /* B */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 1 (B) */ + movdqa 80(%esi), %xmm1 +/* movdqa 208(%esi), %xmm4 */ + paddd %xmm1, %xmm0 +/* movdqa 144(%esi), %xmm2 */ + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 320(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 32(%esi), %xmm0 /* C */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 336(%edi), %xmm0 /* C */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 +/* movdqa %xmm1, 80(%esi) */ +/* round 1 (C) */ + movdqa 96(%esi), %xmm4 +/* movdqa 224(%esi), %xmm5 */ + paddd %xmm4, %xmm0 + movdqa 160(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 432(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 48(%esi), %xmm0 /* D */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 +/* movdqa %xmm2, 160(%esi) */ + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + paddd 400(%edi), %xmm0 /* D */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 +/* movdqa %xmm4, 96(%esi) */ +/* round 1 (D) */ + movdqa 112(%esi), %xmm5 + movdqa 240(%esi), %xmm3 + paddd %xmm5, %xmm0 + movdqa 176(%esi), %xmm6 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 288(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 0(%esi), %xmm0 /* E */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 +/* movdqa %xmm3, 240(%esi) */ + paddd %xmm3, %xmm6 +/* movdqa %xmm6, 176(%esi) */ + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 208(%edi), %xmm0 /* E */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 +/* movdqa %xmm5, 112(%esi) */ +/* round 1 (E) */ +/* movdqa 80(%esi), %xmm1 */ +/* movdqa 240(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 160(%esi), %xmm2 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 384(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* F */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 240(%esi) + paddd %xmm3, %xmm2 + movdqa %xmm2, 160(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 192(%edi), %xmm0 /* F */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 80(%esi) +/* round 1 (F) */ +/* movdqa 96(%esi), %xmm4 */ + movdqa 192(%esi), %xmm3 + paddd %xmm4, %xmm0 +/* movdqa 176(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + por %xmm7, %xmm3 + paddd 224(%edi), %xmm0 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 32(%esi), %xmm0 /* G */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 176(%esi) + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + paddd 368(%edi), %xmm0 /* G */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 96(%esi) +/* round 1 (G) */ +/* movdqa 112(%esi), %xmm5 */ + movdqa 208(%esi), %xmm4 + paddd %xmm5, %xmm0 + movdqa 128(%esi), %xmm6 + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 304(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 48(%esi), %xmm0 /* H */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 272(%edi), %xmm0 /* H */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 112(%esi) +/* round 1 (H) */ + movdqa 64(%esi), %xmm1 + movdqa 224(%esi), %xmm5 + paddd %xmm1, %xmm0 + movdqa 144(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 240(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 0(%esi), %xmm0 /* A */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 368(%edi), %xmm0 /* A */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 2 (A) */ +/* movdqa 64(%esi), %xmm1 */ +/* movdqa 192(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 128(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 320(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* B */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + paddd 384(%edi), %xmm0 /* B */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 2 (B) */ + movdqa 80(%esi), %xmm1 +/* movdqa 208(%esi), %xmm4 */ + paddd %xmm1, %xmm0 +/* movdqa 144(%esi), %xmm2 */ + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 192(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 32(%esi), %xmm0 /* C */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 272(%edi), %xmm0 /* C */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 +/* movdqa %xmm1, 80(%esi) */ +/* round 2 (C) */ + movdqa 96(%esi), %xmm4 +/* movdqa 224(%esi), %xmm5 */ + paddd %xmm4, %xmm0 + movdqa 160(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 224(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 48(%esi), %xmm0 /* D */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 +/* movdqa %xmm2, 160(%esi) */ + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + paddd 432(%edi), %xmm0 /* D */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 +/* movdqa %xmm4, 96(%esi) */ +/* round 2 (D) */ + movdqa 112(%esi), %xmm5 + movdqa 240(%esi), %xmm3 + paddd %xmm5, %xmm0 + movdqa 176(%esi), %xmm6 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 400(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 0(%esi), %xmm0 /* E */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 +/* movdqa %xmm3, 240(%esi) */ + paddd %xmm3, %xmm6 +/* movdqa %xmm6, 176(%esi) */ + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 352(%edi), %xmm0 /* E */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 +/* movdqa %xmm5, 112(%esi) */ +/* round 2 (E) */ +/* movdqa 80(%esi), %xmm1 */ +/* movdqa 240(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 160(%esi), %xmm2 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 416(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* F */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 240(%esi) + paddd %xmm3, %xmm2 + movdqa %xmm2, 160(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 240(%edi), %xmm0 /* F */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 80(%esi) +/* round 2 (F) */ +/* movdqa 96(%esi), %xmm4 */ + movdqa 192(%esi), %xmm3 + paddd %xmm4, %xmm0 +/* movdqa 176(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + por %xmm7, %xmm3 + paddd 288(%edi), %xmm0 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 32(%esi), %xmm0 /* G */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 176(%esi) + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + paddd 304(%edi), %xmm0 /* G */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 96(%esi) +/* round 2 (G) */ +/* movdqa 112(%esi), %xmm5 */ + movdqa 208(%esi), %xmm4 + paddd %xmm5, %xmm0 + movdqa 128(%esi), %xmm6 + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 208(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 48(%esi), %xmm0 /* H */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 336(%edi), %xmm0 /* H */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 112(%esi) +/* round 2 (H) */ + movdqa 64(%esi), %xmm1 + movdqa 224(%esi), %xmm5 + paddd %xmm1, %xmm0 + movdqa 144(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 256(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 0(%esi), %xmm0 /* A */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 304(%edi), %xmm0 /* A */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 3 (A) */ +/* movdqa 64(%esi), %xmm1 */ +/* movdqa 192(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 128(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 336(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* B */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + paddd 240(%edi), %xmm0 /* B */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 3 (B) */ + movdqa 80(%esi), %xmm1 +/* movdqa 208(%esi), %xmm4 */ + paddd %xmm1, %xmm0 +/* movdqa 144(%esi), %xmm2 */ + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 208(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 32(%esi), %xmm0 /* C */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 400(%edi), %xmm0 /* C */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 +/* movdqa %xmm1, 80(%esi) */ +/* round 3 (C) */ + movdqa 96(%esi), %xmm4 +/* movdqa 224(%esi), %xmm5 */ + paddd %xmm4, %xmm0 + movdqa 160(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 384(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 48(%esi), %xmm0 /* D */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 +/* movdqa %xmm2, 160(%esi) */ + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + paddd 368(%edi), %xmm0 /* D */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 +/* movdqa %xmm4, 96(%esi) */ +/* round 3 (D) */ + movdqa 112(%esi), %xmm5 + movdqa 240(%esi), %xmm3 + paddd %xmm5, %xmm0 + movdqa 176(%esi), %xmm6 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 416(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 0(%esi), %xmm0 /* E */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 +/* movdqa %xmm3, 240(%esi) */ + paddd %xmm3, %xmm6 +/* movdqa %xmm6, 176(%esi) */ + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 224(%edi), %xmm0 /* E */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 +/* movdqa %xmm5, 112(%esi) */ +/* round 3 (E) */ +/* movdqa 80(%esi), %xmm1 */ +/* movdqa 240(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 160(%esi), %xmm2 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 288(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* F */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 240(%esi) + paddd %xmm3, %xmm2 + movdqa %xmm2, 160(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 272(%edi), %xmm0 /* F */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 80(%esi) +/* round 3 (F) */ +/* movdqa 96(%esi), %xmm4 */ + movdqa 192(%esi), %xmm3 + paddd %xmm4, %xmm0 +/* movdqa 176(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + por %xmm7, %xmm3 + paddd 352(%edi), %xmm0 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 32(%esi), %xmm0 /* G */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 176(%esi) + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + paddd 256(%edi), %xmm0 /* G */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 96(%esi) +/* round 3 (G) */ +/* movdqa 112(%esi), %xmm5 */ + movdqa 208(%esi), %xmm4 + paddd %xmm5, %xmm0 + movdqa 128(%esi), %xmm6 + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 192(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 48(%esi), %xmm0 /* H */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 432(%edi), %xmm0 /* H */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 112(%esi) +/* round 3 (H) */ + movdqa 64(%esi), %xmm1 + movdqa 224(%esi), %xmm5 + paddd %xmm1, %xmm0 + movdqa 144(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 320(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 0(%esi), %xmm0 /* A */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 336(%edi), %xmm0 /* A */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 4 (A) */ +/* movdqa 64(%esi), %xmm1 */ +/* movdqa 192(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 128(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 192(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* B */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + paddd 272(%edi), %xmm0 /* B */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 4 (B) */ + movdqa 80(%esi), %xmm1 +/* movdqa 208(%esi), %xmm4 */ + paddd %xmm1, %xmm0 +/* movdqa 144(%esi), %xmm2 */ + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 304(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 32(%esi), %xmm0 /* C */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 224(%edi), %xmm0 /* C */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 +/* movdqa %xmm1, 80(%esi) */ +/* round 4 (C) */ + movdqa 96(%esi), %xmm4 +/* movdqa 224(%esi), %xmm5 */ + paddd %xmm4, %xmm0 + movdqa 160(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 256(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 48(%esi), %xmm0 /* D */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 +/* movdqa %xmm2, 160(%esi) */ + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + paddd 352(%edi), %xmm0 /* D */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 +/* movdqa %xmm4, 96(%esi) */ +/* round 4 (D) */ + movdqa 112(%esi), %xmm5 + movdqa 240(%esi), %xmm3 + paddd %xmm5, %xmm0 + movdqa 176(%esi), %xmm6 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 432(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 0(%esi), %xmm0 /* E */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 +/* movdqa %xmm3, 240(%esi) */ + paddd %xmm3, %xmm6 +/* movdqa %xmm6, 176(%esi) */ + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 416(%edi), %xmm0 /* E */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 +/* movdqa %xmm5, 112(%esi) */ +/* round 4 (E) */ +/* movdqa 80(%esi), %xmm1 */ +/* movdqa 240(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 160(%esi), %xmm2 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 208(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* F */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 240(%esi) + paddd %xmm3, %xmm2 + movdqa %xmm2, 160(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 368(%edi), %xmm0 /* F */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 80(%esi) +/* round 4 (F) */ +/* movdqa 96(%esi), %xmm4 */ + movdqa 192(%esi), %xmm3 + paddd %xmm4, %xmm0 +/* movdqa 176(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + por %xmm7, %xmm3 + paddd 384(%edi), %xmm0 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 32(%esi), %xmm0 /* G */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 176(%esi) + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + paddd 288(%edi), %xmm0 /* G */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 96(%esi) +/* round 4 (G) */ +/* movdqa 112(%esi), %xmm5 */ + movdqa 208(%esi), %xmm4 + paddd %xmm5, %xmm0 + movdqa 128(%esi), %xmm6 + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 320(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 48(%esi), %xmm0 /* H */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 240(%edi), %xmm0 /* H */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 112(%esi) +/* round 4 (H) */ + movdqa 64(%esi), %xmm1 + movdqa 224(%esi), %xmm5 + paddd %xmm1, %xmm0 + movdqa 144(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 400(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 0(%esi), %xmm0 /* A */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 224(%edi), %xmm0 /* A */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 5 (A) */ +/* movdqa 64(%esi), %xmm1 */ +/* movdqa 192(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 128(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 384(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* B */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + paddd 288(%edi), %xmm0 /* B */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 5 (B) */ + movdqa 80(%esi), %xmm1 +/* movdqa 208(%esi), %xmm4 */ + paddd %xmm1, %xmm0 +/* movdqa 144(%esi), %xmm2 */ + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 352(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 32(%esi), %xmm0 /* C */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 192(%edi), %xmm0 /* C */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 +/* movdqa %xmm1, 80(%esi) */ +/* round 5 (C) */ + movdqa 96(%esi), %xmm4 +/* movdqa 224(%esi), %xmm5 */ + paddd %xmm4, %xmm0 + movdqa 160(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 368(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 48(%esi), %xmm0 /* D */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 +/* movdqa %xmm2, 160(%esi) */ + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + paddd 320(%edi), %xmm0 /* D */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 +/* movdqa %xmm4, 96(%esi) */ +/* round 5 (D) */ + movdqa 112(%esi), %xmm5 + movdqa 240(%esi), %xmm3 + paddd %xmm5, %xmm0 + movdqa 176(%esi), %xmm6 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 240(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 0(%esi), %xmm0 /* E */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 +/* movdqa %xmm3, 240(%esi) */ + paddd %xmm3, %xmm6 +/* movdqa %xmm6, 176(%esi) */ + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 256(%edi), %xmm0 /* E */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 +/* movdqa %xmm5, 112(%esi) */ +/* round 5 (E) */ +/* movdqa 80(%esi), %xmm1 */ +/* movdqa 240(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 160(%esi), %xmm2 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 400(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* F */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 240(%esi) + paddd %xmm3, %xmm2 + movdqa %xmm2, 160(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 304(%edi), %xmm0 /* F */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 80(%esi) +/* round 5 (F) */ +/* movdqa 96(%esi), %xmm4 */ + movdqa 192(%esi), %xmm3 + paddd %xmm4, %xmm0 +/* movdqa 176(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + por %xmm7, %xmm3 + paddd 272(%edi), %xmm0 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 32(%esi), %xmm0 /* G */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 176(%esi) + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + paddd 432(%edi), %xmm0 /* G */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 96(%esi) +/* round 5 (G) */ +/* movdqa 112(%esi), %xmm5 */ + movdqa 208(%esi), %xmm4 + paddd %xmm5, %xmm0 + movdqa 128(%esi), %xmm6 + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 416(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 48(%esi), %xmm0 /* H */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 208(%edi), %xmm0 /* H */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 112(%esi) +/* round 5 (H) */ + movdqa 64(%esi), %xmm1 + movdqa 224(%esi), %xmm5 + paddd %xmm1, %xmm0 + movdqa 144(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 336(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 0(%esi), %xmm0 /* A */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 384(%edi), %xmm0 /* A */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 6 (A) */ +/* movdqa 64(%esi), %xmm1 */ +/* movdqa 192(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 128(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 272(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* B */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + paddd 208(%edi), %xmm0 /* B */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 6 (B) */ + movdqa 80(%esi), %xmm1 +/* movdqa 208(%esi), %xmm4 */ + paddd %xmm1, %xmm0 +/* movdqa 144(%esi), %xmm2 */ + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 432(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 32(%esi), %xmm0 /* C */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 416(%edi), %xmm0 /* C */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 +/* movdqa %xmm1, 80(%esi) */ +/* round 6 (C) */ + movdqa 96(%esi), %xmm4 +/* movdqa 224(%esi), %xmm5 */ + paddd %xmm4, %xmm0 + movdqa 160(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 400(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 48(%esi), %xmm0 /* D */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 +/* movdqa %xmm2, 160(%esi) */ + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + paddd 256(%edi), %xmm0 /* D */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 +/* movdqa %xmm4, 96(%esi) */ +/* round 6 (D) */ + movdqa 112(%esi), %xmm5 + movdqa 240(%esi), %xmm3 + paddd %xmm5, %xmm0 + movdqa 176(%esi), %xmm6 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 352(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 0(%esi), %xmm0 /* E */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 +/* movdqa %xmm3, 240(%esi) */ + paddd %xmm3, %xmm6 +/* movdqa %xmm6, 176(%esi) */ + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 192(%edi), %xmm0 /* E */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 +/* movdqa %xmm5, 112(%esi) */ +/* round 6 (E) */ +/* movdqa 80(%esi), %xmm1 */ +/* movdqa 240(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 160(%esi), %xmm2 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 304(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* F */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 240(%esi) + paddd %xmm3, %xmm2 + movdqa %xmm2, 160(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 288(%edi), %xmm0 /* F */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 80(%esi) +/* round 6 (F) */ +/* movdqa 96(%esi), %xmm4 */ + movdqa 192(%esi), %xmm3 + paddd %xmm4, %xmm0 +/* movdqa 176(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + por %xmm7, %xmm3 + paddd 240(%edi), %xmm0 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 32(%esi), %xmm0 /* G */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 176(%esi) + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + paddd 336(%edi), %xmm0 /* G */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 96(%esi) +/* round 6 (G) */ +/* movdqa 112(%esi), %xmm5 */ + movdqa 208(%esi), %xmm4 + paddd %xmm5, %xmm0 + movdqa 128(%esi), %xmm6 + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 224(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 48(%esi), %xmm0 /* H */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 320(%edi), %xmm0 /* H */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 112(%esi) +/* round 6 (H) */ + movdqa 64(%esi), %xmm1 + movdqa 224(%esi), %xmm5 + paddd %xmm1, %xmm0 + movdqa 144(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 368(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 0(%esi), %xmm0 /* A */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 400(%edi), %xmm0 /* A */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 7 (A) */ +/* movdqa 64(%esi), %xmm1 */ +/* movdqa 192(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 128(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 368(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* B */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + paddd 304(%edi), %xmm0 /* B */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 7 (B) */ + movdqa 80(%esi), %xmm1 +/* movdqa 208(%esi), %xmm4 */ + paddd %xmm1, %xmm0 +/* movdqa 144(%esi), %xmm2 */ + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 416(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 32(%esi), %xmm0 /* C */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 384(%edi), %xmm0 /* C */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 +/* movdqa %xmm1, 80(%esi) */ +/* round 7 (C) */ + movdqa 96(%esi), %xmm4 +/* movdqa 224(%esi), %xmm5 */ + paddd %xmm4, %xmm0 + movdqa 160(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 208(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 48(%esi), %xmm0 /* D */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 +/* movdqa %xmm2, 160(%esi) */ + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + paddd 240(%edi), %xmm0 /* D */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 +/* movdqa %xmm4, 96(%esi) */ +/* round 7 (D) */ + movdqa 112(%esi), %xmm5 + movdqa 240(%esi), %xmm3 + paddd %xmm5, %xmm0 + movdqa 176(%esi), %xmm6 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 336(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 0(%esi), %xmm0 /* E */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 +/* movdqa %xmm3, 240(%esi) */ + paddd %xmm3, %xmm6 +/* movdqa %xmm6, 176(%esi) */ + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 272(%edi), %xmm0 /* E */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 +/* movdqa %xmm5, 112(%esi) */ +/* round 7 (E) */ +/* movdqa 80(%esi), %xmm1 */ +/* movdqa 240(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 160(%esi), %xmm2 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 192(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* F */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 240(%esi) + paddd %xmm3, %xmm2 + movdqa %xmm2, 160(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 432(%edi), %xmm0 /* F */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 80(%esi) +/* round 7 (F) */ +/* movdqa 96(%esi), %xmm4 */ + movdqa 192(%esi), %xmm3 + paddd %xmm4, %xmm0 +/* movdqa 176(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + por %xmm7, %xmm3 + paddd 256(%edi), %xmm0 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 32(%esi), %xmm0 /* G */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 176(%esi) + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + paddd 320(%edi), %xmm0 /* G */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 96(%esi) +/* round 7 (G) */ +/* movdqa 112(%esi), %xmm5 */ + movdqa 208(%esi), %xmm4 + paddd %xmm5, %xmm0 + movdqa 128(%esi), %xmm6 + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 288(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 48(%esi), %xmm0 /* H */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 224(%edi), %xmm0 /* H */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 112(%esi) +/* round 7 (H) */ + movdqa 64(%esi), %xmm1 + movdqa 224(%esi), %xmm5 + paddd %xmm1, %xmm0 + movdqa 144(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 352(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 0(%esi), %xmm0 /* A */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 288(%edi), %xmm0 /* A */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 8 (A) */ +/* movdqa 64(%esi), %xmm1 */ +/* movdqa 192(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 128(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 432(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* B */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + paddd 416(%edi), %xmm0 /* B */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 8 (B) */ + movdqa 80(%esi), %xmm1 +/* movdqa 208(%esi), %xmm4 */ + paddd %xmm1, %xmm0 +/* movdqa 144(%esi), %xmm2 */ + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 336(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 32(%esi), %xmm0 /* C */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 368(%edi), %xmm0 /* C */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 +/* movdqa %xmm1, 80(%esi) */ +/* round 8 (C) */ + movdqa 96(%esi), %xmm4 +/* movdqa 224(%esi), %xmm5 */ + paddd %xmm4, %xmm0 + movdqa 160(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 240(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 48(%esi), %xmm0 /* D */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 +/* movdqa %xmm2, 160(%esi) */ + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + paddd 192(%edi), %xmm0 /* D */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 +/* movdqa %xmm4, 96(%esi) */ +/* round 8 (D) */ + movdqa 112(%esi), %xmm5 + movdqa 240(%esi), %xmm3 + paddd %xmm5, %xmm0 + movdqa 176(%esi), %xmm6 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 320(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 0(%esi), %xmm0 /* E */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 +/* movdqa %xmm3, 240(%esi) */ + paddd %xmm3, %xmm6 +/* movdqa %xmm6, 176(%esi) */ + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 384(%edi), %xmm0 /* E */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 +/* movdqa %xmm5, 112(%esi) */ +/* round 8 (E) */ +/* movdqa 80(%esi), %xmm1 */ +/* movdqa 240(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 160(%esi), %xmm2 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 224(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* F */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 240(%esi) + paddd %xmm3, %xmm2 + movdqa %xmm2, 160(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 400(%edi), %xmm0 /* F */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 80(%esi) +/* round 8 (F) */ +/* movdqa 96(%esi), %xmm4 */ + movdqa 192(%esi), %xmm3 + paddd %xmm4, %xmm0 +/* movdqa 176(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + por %xmm7, %xmm3 + paddd 304(%edi), %xmm0 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 32(%esi), %xmm0 /* G */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 176(%esi) + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + paddd 208(%edi), %xmm0 /* G */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 96(%esi) +/* round 8 (G) */ +/* movdqa 112(%esi), %xmm5 */ + movdqa 208(%esi), %xmm4 + paddd %xmm5, %xmm0 + movdqa 128(%esi), %xmm6 + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 256(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 48(%esi), %xmm0 /* H */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 352(%edi), %xmm0 /* H */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 112(%esi) +/* round 8 (H) */ + movdqa 64(%esi), %xmm1 + movdqa 224(%esi), %xmm5 + paddd %xmm1, %xmm0 + movdqa 144(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 272(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 0(%esi), %xmm0 /* A */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 352(%edi), %xmm0 /* A */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 9 (A) */ +/* movdqa 64(%esi), %xmm1 */ +/* movdqa 192(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 128(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 224(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* B */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 192(%esi) + paddd %xmm3, %xmm6 + movdqa %xmm6, 128(%esi) + pxor %xmm6, %xmm1 + movdqa %xmm1, %xmm7 + paddd 320(%edi), %xmm0 /* B */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 64(%esi) +/* round 9 (B) */ + movdqa 80(%esi), %xmm1 +/* movdqa 208(%esi), %xmm4 */ + paddd %xmm1, %xmm0 +/* movdqa 144(%esi), %xmm2 */ + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 256(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 32(%esi), %xmm0 /* C */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 208(%esi) + paddd %xmm4, %xmm2 + movdqa %xmm2, 144(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 304(%edi), %xmm0 /* C */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 +/* movdqa %xmm1, 80(%esi) */ +/* round 9 (C) */ + movdqa 96(%esi), %xmm4 +/* movdqa 224(%esi), %xmm5 */ + paddd %xmm4, %xmm0 + movdqa 160(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 288(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + movdqa 48(%esi), %xmm0 /* D */ + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 224(%esi) + paddd %xmm5, %xmm2 +/* movdqa %xmm2, 160(%esi) */ + pxor %xmm2, %xmm4 + movdqa %xmm4, %xmm7 + paddd 208(%edi), %xmm0 /* D */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 +/* movdqa %xmm4, 96(%esi) */ +/* round 9 (D) */ + movdqa 112(%esi), %xmm5 + movdqa 240(%esi), %xmm3 + paddd %xmm5, %xmm0 + movdqa 176(%esi), %xmm6 + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 272(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 48(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 0(%esi), %xmm0 /* E */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 +/* movdqa %xmm3, 240(%esi) */ + paddd %xmm3, %xmm6 +/* movdqa %xmm6, 176(%esi) */ + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 432(%edi), %xmm0 /* E */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 +/* movdqa %xmm5, 112(%esi) */ +/* round 9 (E) */ +/* movdqa 80(%esi), %xmm1 */ +/* movdqa 240(%esi), %xmm3 */ + paddd %xmm1, %xmm0 +/* movdqa 160(%esi), %xmm2 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + paddd 368(%edi), %xmm0 + por %xmm7, %xmm3 + paddd %xmm3, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 + movdqa %xmm0, 0(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 16(%esi), %xmm0 /* F */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 + movdqa %xmm3, 240(%esi) + paddd %xmm3, %xmm2 + movdqa %xmm2, 160(%esi) + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + paddd 336(%edi), %xmm0 /* F */ + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 + movdqa %xmm1, 80(%esi) +/* round 9 (F) */ +/* movdqa 96(%esi), %xmm4 */ + movdqa 192(%esi), %xmm3 + paddd %xmm4, %xmm0 +/* movdqa 176(%esi), %xmm6 */ + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + pslld $16, %xmm3 + psrld $16, %xmm7 + por %xmm7, %xmm3 + paddd 416(%edi), %xmm0 + paddd %xmm3, %xmm6 + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + pslld $20, %xmm4 + psrld $12, %xmm7 + por %xmm7, %xmm4 + paddd %xmm4, %xmm0 + movdqa %xmm0, 16(%esi) + pxor %xmm0, %xmm3 + movdqa %xmm3, %xmm7 + movdqa 32(%esi), %xmm0 /* G */ + pslld $24, %xmm3 + psrld $8, %xmm7 + por %xmm7, %xmm3 +/* movdqa %xmm3, 192(%esi) */ + paddd %xmm3, %xmm6 + movdqa %xmm6, 176(%esi) + pxor %xmm6, %xmm4 + movdqa %xmm4, %xmm7 + paddd 240(%edi), %xmm0 /* G */ + pslld $25, %xmm4 + psrld $7, %xmm7 + por %xmm7, %xmm4 + movdqa %xmm4, 96(%esi) +/* round 9 (G) */ +/* movdqa 112(%esi), %xmm5 */ + movdqa 208(%esi), %xmm4 + paddd %xmm5, %xmm0 + movdqa 128(%esi), %xmm6 + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + pslld $16, %xmm4 + psrld $16, %xmm7 + paddd 384(%edi), %xmm0 + por %xmm7, %xmm4 + paddd %xmm4, %xmm6 + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + pslld $20, %xmm5 + psrld $12, %xmm7 + por %xmm7, %xmm5 + paddd %xmm5, %xmm0 + movdqa %xmm0, 32(%esi) + pxor %xmm0, %xmm4 + movdqa %xmm4, %xmm7 + movdqa 48(%esi), %xmm0 /* H */ + pslld $24, %xmm4 + psrld $8, %xmm7 + por %xmm7, %xmm4 +/* movdqa %xmm4, 208(%esi) */ + paddd %xmm4, %xmm6 +/* movdqa %xmm6, 128(%esi) */ + pxor %xmm6, %xmm5 + movdqa %xmm5, %xmm7 + paddd 400(%edi), %xmm0 /* H */ + pslld $25, %xmm5 + psrld $7, %xmm7 + por %xmm7, %xmm5 + movdqa %xmm5, 112(%esi) +/* round 9 (H) */ + movdqa 64(%esi), %xmm1 + movdqa 224(%esi), %xmm5 + paddd %xmm1, %xmm0 + movdqa 144(%esi), %xmm2 + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $16, %xmm5 + psrld $16, %xmm7 + paddd 192(%edi), %xmm0 + por %xmm7, %xmm5 + paddd %xmm5, %xmm2 + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $20, %xmm1 + psrld $12, %xmm7 + por %xmm7, %xmm1 + paddd %xmm1, %xmm0 +/* movdqa %xmm0, 48(%esi) */ + pxor %xmm0, %xmm5 + movdqa %xmm5, %xmm7 + pslld $24, %xmm5 + psrld $8, %xmm7 + por %xmm7, %xmm5 +/* movdqa %xmm5, 224(%esi) */ + paddd %xmm5, %xmm2 +/* movdqa %xmm2, 144(%esi) */ + pxor %xmm2, %xmm1 + movdqa %xmm1, %xmm7 + pslld $25, %xmm1 + psrld $7, %xmm7 + por %xmm7, %xmm1 +/* movdqa %xmm1, 64(%esi) */ +/* finalise */ + movdqa 112(%esi), %xmm7 + pxor %xmm3, %xmm1 /* 64() ^ 192() */ + pxor 176(%esi), %xmm0 /* 48() ^ 176() */ + pxor 16(%esi), %xmm2 /* 16() ^ 144() */ + movdqa 32(%esi), %xmm3 + pxor 80(%esi), %xmm4 /* 80() ^ 208() */ + pxor 96(%esi), %xmm5 /* 96() ^ 224() */ + pxor 0(%esi), %xmm6 /* 0() ^ 128() */ + pxor 240(%esi), %xmm7 /* 112() ^ 240() */ + pxor 160(%esi), %xmm3 /* 32() ^ 160() */ + pxor 0(%edi), %xmm6 + pxor 16(%edi), %xmm2 + pxor 32(%edi), %xmm3 + pxor 48(%edi), %xmm0 + pxor 64(%edi), %xmm1 + pxor 80(%edi), %xmm4 + pxor 96(%edi), %xmm5 + pxor 112(%edi), %xmm7 + movdqa %xmm6, 0(%edi) + movdqa %xmm2, 16(%edi) + movdqa %xmm3, 32(%edi) + movdqa %xmm0, 48(%edi) + movdqa %xmm1, 64(%edi) + movdqa %xmm4, 80(%edi) + movdqa %xmm5, 96(%edi) + movdqa %xmm7, 112(%edi) + + popl %edi + popl %esi + popl %ebp + popl %ebx + ret + + +/* neoscrypt_blkcpy(dst, src, len) + * i386 (SSE2) block memcpy(); + * len must be a multiple of 64 bytes aligned properly */ +.globl neoscrypt_blkcpy +.globl _neoscrypt_blkcpy +neoscrypt_blkcpy: +_neoscrypt_blkcpy: + movl 4(%esp), %eax + movl 8(%esp), %edx + movl 12(%esp), %ecx + shrl $6, %ecx +.blkcpy: + movdqa 0(%edx), %xmm0 + movdqa 16(%edx), %xmm1 + movdqa 32(%edx), %xmm2 + movdqa 48(%edx), %xmm3 + movdqa %xmm0, 0(%eax) + movdqa %xmm1, 16(%eax) + movdqa %xmm2, 32(%eax) + movdqa %xmm3, 48(%eax) + addl $64, %edx + addl $64, %eax + decl %ecx + jnz .blkcpy + + ret + + +/* neoscrypt_blkswp(blkA, blkB, len) + * i386 (SSE2) block swapper; + * len must be a multiple of 64 bytes aligned properly */ +.globl neoscrypt_blkswp +.globl _neoscrypt_blkswp +neoscrypt_blkswp: +_neoscrypt_blkswp: + movl 4(%esp), %eax + movl 8(%esp), %edx + movl 12(%esp), %ecx + shrl $6, %ecx +.blkswp: + movdqa 0(%eax), %xmm0 + movdqa 16(%eax), %xmm1 + movdqa 32(%eax), %xmm2 + movdqa 48(%eax), %xmm3 + movdqa 0(%edx), %xmm4 + movdqa 16(%edx), %xmm5 + movdqa 32(%edx), %xmm6 + movdqa 48(%edx), %xmm7 + movdqa %xmm0, 0(%edx) + movdqa %xmm1, 16(%edx) + movdqa %xmm2, 32(%edx) + movdqa %xmm3, 48(%edx) + movdqa %xmm4, 0(%eax) + movdqa %xmm5, 16(%eax) + movdqa %xmm6, 32(%eax) + movdqa %xmm7, 48(%eax) + addl $64, %eax + addl $64, %edx + decl %ecx + jnz .blkswp + + ret + + +/* neoscrypt_blkxor(dst, src, len) + * i386 (SSE2) block XOR engine; + * len must be a multiple of 64 bytes aligned properly */ +.globl neoscrypt_blkxor +.globl _neoscrypt_blkxor +neoscrypt_blkxor: +_neoscrypt_blkxor: + movl 4(%esp), %eax + movl 8(%esp), %edx + movl 12(%esp), %ecx + shrl $6, %ecx +.blkxor: + movdqa 0(%eax), %xmm0 + movdqa 16(%eax), %xmm1 + movdqa 32(%eax), %xmm2 + movdqa 48(%eax), %xmm3 + pxor 0(%edx), %xmm0 + pxor 16(%edx), %xmm1 + pxor 32(%edx), %xmm2 + pxor 48(%edx), %xmm3 + movdqa %xmm0, 0(%eax) + movdqa %xmm1, 16(%eax) + movdqa %xmm2, 32(%eax) + movdqa %xmm3, 48(%eax) + addl $64, %eax + addl $64, %edx + decl %ecx + jnz .blkxor + + ret + + +/* neoscrypt_pack_4way(dst, src, len) + * i386 4-way data packer */ +.globl neoscrypt_pack_4way +.globl _neoscrypt_pack_4way +neoscrypt_pack_4way: +_neoscrypt_pack_4way: + pushl %ebx + pushl %ebp + pushl %esi + pushl %edi + movl 20(%esp), %edi + movl 24(%esp), %esi + movl 28(%esp), %edx + movl %edx, %ecx + shrl $2, %edx + leal 0(%esi, %edx, 2), %eax + shrl $4, %ecx +.pack_4way: + movl 0(%esi), %ebx + movl 0(%esi, %edx), %ebp + addl $4, %esi + movl %ebx, 0(%edi) + movl %ebp, 4(%edi) + movl 0(%eax), %ebx + movl 0(%eax, %edx), %ebp + addl $4, %eax + movl %ebx, 8(%edi) + movl %ebp, 12(%edi) + addl $16, %edi + decl %ecx + jnz .pack_4way + + popl %edi + popl %esi + popl %ebp + popl %ebx + ret + + +/* neoscrypt_unpack_4way(dst, src, len) + * i386 4-way data unpacker */ +.globl neoscrypt_unpack_4way +.globl _neoscrypt_unpack_4way +neoscrypt_unpack_4way: +_neoscrypt_unpack_4way: + pushl %ebx + pushl %ebp + pushl %esi + pushl %edi + movl 20(%esp), %edi + movl 24(%esp), %esi + movl 28(%esp), %edx + movl %edx, %ecx + shrl $2, %edx + leal 0(%edi, %edx, 2), %eax + shrl $4, %ecx +.unpack_4way: + movl 0(%esi), %ebx + movl 4(%esi), %ebp + movl %ebx, 0(%edi) + movl %ebp, 0(%edi, %edx) + addl $4, %edi + movl 8(%esi), %ebx + movl 12(%esi), %ebp + addl $16, %esi + movl %ebx, 0(%eax) + movl %ebp, 0(%eax, %edx) + addl $4, %eax + decl %ecx + jnz .unpack_4way + + popl %edi + popl %esi + popl %ebp + popl %ebx + ret + + +/* neoscrypt_xor_4way(dst, srcA, srcB, srcC, srcD, len) + * i386 4-way XOR engine */ +.globl neoscrypt_xor_4way +.globl _neoscrypt_xor_4way +neoscrypt_xor_4way: +_neoscrypt_xor_4way: + pushl %ebx + pushl %ebp + pushl %esi + pushl %edi + movl 20(%esp), %edi + movl 24(%esp), %esi + movl 28(%esp), %edx + movl 32(%esp), %ebx + movl 36(%esp), %eax + movl 40(%esp), %ecx + shrl $4, %ecx +.xor_4way: + movl 0(%esi), %ebp + addl $16, %esi + xorl %ebp, 0(%edi) + movl 4(%edx), %ebp + addl $16, %edx + xorl %ebp, 4(%edi) + movl 8(%ebx), %ebp + addl $16, %ebx + xorl %ebp, 8(%edi) + movl 12(%eax), %ebp + addl $16, %eax + xorl %ebp, 12(%edi) + addl $16, %edi + decl %ecx + jnz .xor_4way + + popl %edi + popl %esi + popl %ebp + popl %ebx + ret + + +/* neoscrypt_xor_salsa_4way(mem, xormem, workmem, rounds) + * i386 (SSE2) Salsa20 4-way with XOR */ +.globl neoscrypt_xor_salsa_4way +.globl _neoscrypt_xor_salsa_4way +neoscrypt_xor_salsa_4way: +_neoscrypt_xor_salsa_4way: +/* XOR and copy to temporary memory */ + movl 4(%esp), %eax + movl 8(%esp), %ecx + movl 12(%esp), %edx + movdqa 0(%eax), %xmm0 + movdqa 16(%eax), %xmm1 + movdqa 32(%eax), %xmm2 + movdqa 48(%eax), %xmm3 + movdqa 64(%eax), %xmm4 + movdqa 80(%eax), %xmm5 + movdqa 96(%eax), %xmm6 + movdqa 112(%eax), %xmm7 + pxor 0(%ecx), %xmm0 + pxor 16(%ecx), %xmm1 + pxor 32(%ecx), %xmm2 + pxor 48(%ecx), %xmm3 + pxor 64(%ecx), %xmm4 + pxor 80(%ecx), %xmm5 + pxor 96(%ecx), %xmm6 + pxor 112(%ecx), %xmm7 + movdqa %xmm0, 0(%eax) + movdqa %xmm1, 16(%eax) + movdqa %xmm2, 32(%eax) + movdqa %xmm3, 48(%eax) + movdqa %xmm4, 64(%eax) + movdqa %xmm5, 80(%eax) + movdqa %xmm6, 96(%eax) + movdqa %xmm7, 112(%eax) + movdqa %xmm0, 0(%edx) + movdqa %xmm1, 16(%edx) + movdqa %xmm2, 32(%edx) + movdqa %xmm3, 48(%edx) + movdqa %xmm4, 64(%edx) + movdqa %xmm5, 80(%edx) + movdqa %xmm6, 96(%edx) + movdqa %xmm7, 112(%edx) + movdqa 128(%eax), %xmm0 + movdqa 144(%eax), %xmm1 + movdqa 160(%eax), %xmm2 + movdqa 176(%eax), %xmm3 + movdqa 192(%eax), %xmm4 + movdqa 208(%eax), %xmm5 + movdqa 224(%eax), %xmm6 + movdqa 240(%eax), %xmm7 + pxor 128(%ecx), %xmm0 + pxor 144(%ecx), %xmm1 + pxor 160(%ecx), %xmm2 + pxor 176(%ecx), %xmm3 + pxor 192(%ecx), %xmm4 + pxor 208(%ecx), %xmm5 + pxor 224(%ecx), %xmm6 + pxor 240(%ecx), %xmm7 + movdqa %xmm0, 128(%eax) + movdqa %xmm1, 144(%eax) + movdqa %xmm2, 160(%eax) + movdqa %xmm3, 176(%eax) + movdqa %xmm4, 192(%eax) + movdqa %xmm5, 208(%eax) + movdqa %xmm6, 224(%eax) + movdqa %xmm7, 240(%eax) + movdqa %xmm0, 128(%edx) + movdqa %xmm1, 144(%edx) + movdqa %xmm2, 160(%edx) + movdqa %xmm3, 176(%edx) + movdqa %xmm4, 192(%edx) + movdqa %xmm5, 208(%edx) + movdqa %xmm6, 224(%edx) + movdqa %xmm7, 240(%edx) +/* number of double rounds */ + movl 16(%esp), %ecx +.xor_salsa_4way: +/* quarters A and B */ + movdqa 0(%edx), %xmm0 /* A: load a */ + movdqa 80(%edx), %xmm4 /* B: load a */ + paddd 192(%edx), %xmm0 /* A: t = a + d */ + paddd 16(%edx), %xmm4 /* B: t = a + d */ + movdqa %xmm0, %xmm3 /* A: rotate t (0) */ + movdqa %xmm4, %xmm7 /* B: rotate t (0) */ + pslld $7, %xmm0 /* A: rotate t (1) */ + psrld $25, %xmm3 /* A: rotate t (2) */ + pslld $7, %xmm4 /* B: rotate t (1) */ + psrld $25, %xmm7 /* B: rotate t (2) */ + por %xmm3, %xmm0 /* A: rotate t (3) */ + por %xmm7, %xmm4 /* B: rotate t (3) */ + pxor 64(%edx), %xmm0 /* A: b = b ^ t */ + pxor 144(%edx), %xmm4 /* B: b = b ^ t */ + movdqa %xmm0, %xmm1 /* A: copy b */ + movdqa %xmm4, %xmm5 /* B: copy b */ + movdqa %xmm1, 64(%edx) /* A: store b */ + movdqa %xmm5, 144(%edx) /* B: store b */ + paddd 0(%edx), %xmm0 /* A: t = b + a */ + paddd 80(%edx), %xmm4 /* B: t = b + a */ + movdqa %xmm0, %xmm3 /* A: rotate t (0) */ + movdqa %xmm4, %xmm7 /* B: rotate t (0) */ + pslld $9, %xmm0 /* A: rotate t (1) */ + psrld $23, %xmm3 /* A: rotate t (2) */ + pslld $9, %xmm4 /* B: rotate t (1) */ + psrld $23, %xmm7 /* B: rotate t (2) */ + por %xmm3, %xmm0 /* A: rotate t (3) */ + por %xmm7, %xmm4 /* B: rotate t (3) */ + pxor 128(%edx), %xmm0 /* A: c = c ^ t */ + pxor 208(%edx), %xmm4 /* B: c = c ^ t */ + movdqa %xmm0, %xmm2 /* A: copy c */ + movdqa %xmm4, %xmm6 /* B: copy c */ + movdqa %xmm2, 128(%edx) /* A: store c */ + movdqa %xmm6, 208(%edx) /* B: store c */ + paddd %xmm1, %xmm0 /* A: t = c + b */ + paddd %xmm5, %xmm4 /* B: t = c + b */ + movdqa %xmm0, %xmm3 /* A: rotate t (0) */ + movdqa %xmm4, %xmm7 /* B: rotate t (0) */ + pslld $13, %xmm0 /* A: rotate t (1) */ + psrld $19, %xmm3 /* A: rotate t (2) */ + pslld $13, %xmm4 /* B: rotate t (1) */ + psrld $19, %xmm7 /* B: rotate t (2) */ + por %xmm3, %xmm0 /* A: rotate t (3) */ + por %xmm7, %xmm4 /* B: rotate t (3) */ + pxor 192(%edx), %xmm0 /* A: d = d ^ t */ + pxor 16(%edx), %xmm4 /* B: d = d ^ t */ + movdqa %xmm0, 192(%edx) /* A: store d */ + movdqa %xmm4, 16(%edx) /* B: store d */ + paddd %xmm2, %xmm0 /* A: t = d + c */ + paddd %xmm6, %xmm4 /* B: t = d + c */ + movdqa %xmm0, %xmm3 /* A: rotate t (0) */ + movdqa %xmm4, %xmm7 /* B: rotate t (0) */ + pslld $18, %xmm0 /* A: rotate t (1) */ + psrld $14, %xmm3 /* A: rotate t (2) */ + pslld $18, %xmm4 /* B: rotate t (1) */ + psrld $14, %xmm7 /* B: rotate t (2) */ + por %xmm3, %xmm0 /* A: rotate t (3) */ + por %xmm7, %xmm4 /* B: rotate t (3) */ + pxor 0(%edx), %xmm0 /* A: a = a ^ t */ + pxor 80(%edx), %xmm4 /* B: a = a ^ t */ + movdqa %xmm0, 0(%edx) /* A: store a */ + movdqa %xmm4, 80(%edx) /* B: store a */ +/* quarters C and D*/ + movdqa 160(%edx), %xmm0 /* C: load a */ + movdqa 240(%edx), %xmm4 /* D: load a */ + paddd 96(%edx), %xmm0 /* C: t = a + d */ + paddd 176(%edx), %xmm4 /* D: t = a + d */ + movdqa %xmm0, %xmm3 /* C: rotate t (0) */ + movdqa %xmm4, %xmm7 /* D: rotate t (0) */ + pslld $7, %xmm0 /* C: rotate t (1) */ + psrld $25, %xmm3 /* C: rotate t (2) */ + pslld $7, %xmm4 /* D: rotate t (1) */ + psrld $25, %xmm7 /* D: rotate t (2) */ + por %xmm3, %xmm0 /* C: rotate t (3) */ + por %xmm7, %xmm4 /* D: rotate t (3) */ + pxor 224(%edx), %xmm0 /* C: b = b ^ t */ + pxor 48(%edx), %xmm4 /* D: b = b ^ t */ + movdqa %xmm0, %xmm1 /* C: copy b */ + movdqa %xmm4, %xmm5 /* D: copy b */ + movdqa %xmm1, 224(%edx) /* C: store b */ + movdqa %xmm5, 48(%edx) /* D: store b */ + paddd 160(%edx), %xmm0 /* C: t = b + a */ + paddd 240(%edx), %xmm4 /* D: t = b + a */ + movdqa %xmm0, %xmm3 /* C: rotate t (0) */ + movdqa %xmm4, %xmm7 /* D: rotate t (0) */ + pslld $9, %xmm0 /* C: rotate t (1) */ + psrld $23, %xmm3 /* C: rotate t (2) */ + pslld $9, %xmm4 /* D: rotate t (1) */ + psrld $23, %xmm7 /* D: rotate t (2) */ + por %xmm3, %xmm0 /* C: rotate t (3) */ + por %xmm7, %xmm4 /* D: rotate t (3) */ + pxor 32(%edx), %xmm0 /* C: c = c ^ t */ + pxor 112(%edx), %xmm4 /* D: c = c ^ t */ + movdqa %xmm0, %xmm2 /* C: copy c */ + movdqa %xmm4, %xmm6 /* D: copy c */ + movdqa %xmm2, 32(%edx) /* C: store c */ + movdqa %xmm6, 112(%edx) /* D: store c */ + paddd %xmm1, %xmm0 /* C: t = c + b */ + paddd %xmm5, %xmm4 /* D: t = c + b */ + movdqa %xmm0, %xmm3 /* C: rotate t (0) */ + movdqa %xmm4, %xmm7 /* D: rotate t (0) */ + pslld $13, %xmm0 /* C: rotate t (1) */ + psrld $19, %xmm3 /* C: rotate t (2) */ + pslld $13, %xmm4 /* D: rotate t (1) */ + psrld $19, %xmm7 /* D: rotate t (2) */ + por %xmm3, %xmm0 /* C: rotate t (3) */ + por %xmm7, %xmm4 /* D: rotate t (3) */ + pxor 96(%edx), %xmm0 /* C: d = d ^ t */ + pxor 176(%edx), %xmm4 /* D: d = d ^ t */ + movdqa %xmm0, 96(%edx) /* C: store d */ + movdqa %xmm4, 176(%edx) /* D: store d */ + paddd %xmm2, %xmm0 /* C: t = d + c */ + paddd %xmm6, %xmm4 /* D: t = d + c */ + movdqa %xmm0, %xmm3 /* C: rotate t (0) */ + movdqa %xmm4, %xmm7 /* D: rotate t (0) */ + pslld $18, %xmm0 /* C: rotate t (1) */ + psrld $14, %xmm3 /* C: rotate t (2) */ + pslld $18, %xmm4 /* D: rotate t (1) */ + psrld $14, %xmm7 /* D: rotate t (2) */ + por %xmm3, %xmm0 /* C: rotate t (3) */ + por %xmm7, %xmm4 /* D: rotate t (3) */ + pxor 160(%edx), %xmm0 /* C: a = a ^ t */ + pxor 240(%edx), %xmm4 /* D: a = a ^ t */ + movdqa %xmm0, 160(%edx) /* C: store a */ + movdqa %xmm4, 240(%edx) /* D: store a */ +/* quarters E and F */ + movdqa 0(%edx), %xmm0 /* E: load a */ + movdqa 80(%edx), %xmm4 /* F: load a */ + paddd 48(%edx), %xmm0 /* E: t = a + d */ + paddd 64(%edx), %xmm4 /* F: t = a + d */ + movdqa %xmm0, %xmm3 /* E: rotate t (0) */ + movdqa %xmm4, %xmm7 /* F: rotate t (0) */ + pslld $7, %xmm0 /* E: rotate t (1) */ + psrld $25, %xmm3 /* E: rotate t (2) */ + pslld $7, %xmm4 /* F: rotate t (1) */ + psrld $25, %xmm7 /* F: rotate t (2) */ + por %xmm3, %xmm0 /* E: rotate t (3) */ + por %xmm7, %xmm4 /* F: rotate t (3) */ + pxor 16(%edx), %xmm0 /* E: b = b ^ t */ + pxor 96(%edx), %xmm4 /* F: b = b ^ t */ + movdqa %xmm0, %xmm1 /* E: copy b */ + movdqa %xmm4, %xmm5 /* F: copy b */ + movdqa %xmm1, 16(%edx) /* E: store b */ + movdqa %xmm5, 96(%edx) /* F: store b */ + paddd 0(%edx), %xmm0 /* E: t = b + a */ + paddd 80(%edx), %xmm4 /* F: t = b + a */ + movdqa %xmm0, %xmm3 /* E: rotate t (0) */ + movdqa %xmm4, %xmm7 /* F: rotate t (0) */ + pslld $9, %xmm0 /* E: rotate t (1) */ + psrld $23, %xmm3 /* E: rotate t (2) */ + pslld $9, %xmm4 /* F: rotate t (1) */ + psrld $23, %xmm7 /* F: rotate t (2) */ + por %xmm3, %xmm0 /* E: rotate t (3) */ + por %xmm7, %xmm4 /* F: rotate t (3) */ + pxor 32(%edx), %xmm0 /* E: c = c ^ t */ + pxor 112(%edx), %xmm4 /* F: c = c ^ t */ + movdqa %xmm0, %xmm2 /* E: copy c */ + movdqa %xmm4, %xmm6 /* F: copy c */ + movdqa %xmm2, 32(%edx) /* E: store c */ + movdqa %xmm6, 112(%edx) /* F: store c */ + paddd %xmm1, %xmm0 /* E: t = c + b */ + paddd %xmm5, %xmm4 /* F: t = c + b */ + movdqa %xmm0, %xmm3 /* E: rotate t (0) */ + movdqa %xmm4, %xmm7 /* F: rotate t (0) */ + pslld $13, %xmm0 /* E: rotate t (1) */ + psrld $19, %xmm3 /* E: rotate t (2) */ + pslld $13, %xmm4 /* F: rotate t (1) */ + psrld $19, %xmm7 /* F: rotate t (2) */ + por %xmm3, %xmm0 /* E: rotate t (3) */ + por %xmm7, %xmm4 /* F: rotate t (3) */ + pxor 48(%edx), %xmm0 /* E: d = d ^ t */ + pxor 64(%edx), %xmm4 /* F: d = d ^ t */ + movdqa %xmm0, 48(%edx) /* E: store d */ + movdqa %xmm4, 64(%edx) /* F: store d */ + paddd %xmm2, %xmm0 /* E: t = d + c */ + paddd %xmm6, %xmm4 /* F: t = d + c */ + movdqa %xmm0, %xmm3 /* E: rotate t (0) */ + movdqa %xmm4, %xmm7 /* F: rotate t (0) */ + pslld $18, %xmm0 /* E: rotate t (1) */ + psrld $14, %xmm3 /* E: rotate t (2) */ + pslld $18, %xmm4 /* F: rotate t (1) */ + psrld $14, %xmm7 /* F: rotate t (2) */ + por %xmm3, %xmm0 /* E: rotate t (3) */ + por %xmm7, %xmm4 /* F: rotate t (3) */ + pxor 0(%edx), %xmm0 /* E: a = a ^ t */ + pxor 80(%edx), %xmm4 /* F: a = a ^ t */ + movdqa %xmm0, 0(%edx) /* E: store a */ + movdqa %xmm4, 80(%edx) /* F: store a */ +/* quarters G and H */ + movdqa 160(%edx), %xmm0 /* G: load a */ + movdqa 240(%edx), %xmm4 /* H: load a */ + paddd 144(%edx), %xmm0 /* G: t = a + d */ + paddd 224(%edx), %xmm4 /* H: t = a + d */ + movdqa %xmm0, %xmm3 /* G: rotate t (0) */ + movdqa %xmm4, %xmm7 /* H: rotate t (0) */ + pslld $7, %xmm0 /* G: rotate t (1) */ + psrld $25, %xmm3 /* G: rotate t (2) */ + pslld $7, %xmm4 /* H: rotate t (1) */ + psrld $25, %xmm7 /* H: rotate t (2) */ + por %xmm3, %xmm0 /* G: rotate t (3) */ + por %xmm7, %xmm4 /* H: rotate t (3) */ + pxor 176(%edx), %xmm0 /* G: b = b ^ t */ + pxor 192(%edx), %xmm4 /* H: b = b ^ t */ + movdqa %xmm0, %xmm1 /* G: copy b */ + movdqa %xmm4, %xmm5 /* H: copy b */ + movdqa %xmm1, 176(%edx) /* G: store b */ + movdqa %xmm5, 192(%edx) /* H: store b */ + paddd 160(%edx), %xmm0 /* G: t = b + a */ + paddd 240(%edx), %xmm4 /* H: t = b + a */ + movdqa %xmm0, %xmm3 /* G: rotate t (0) */ + movdqa %xmm4, %xmm7 /* H: rotate t (0) */ + pslld $9, %xmm0 /* G: rotate t (1) */ + psrld $23, %xmm3 /* G: rotate t (2) */ + pslld $9, %xmm4 /* H: rotate t (1) */ + psrld $23, %xmm7 /* H: rotate t (2) */ + por %xmm3, %xmm0 /* G: rotate t (3) */ + por %xmm7, %xmm4 /* H: rotate t (3) */ + pxor 128(%edx), %xmm0 /* G: c = c ^ t */ + pxor 208(%edx), %xmm4 /* H: c = c ^ t */ + movdqa %xmm0, %xmm2 /* G: copy c */ + movdqa %xmm4, %xmm6 /* H: copy c */ + movdqa %xmm2, 128(%edx) /* G: store c */ + movdqa %xmm6, 208(%edx) /* H: store c */ + paddd %xmm1, %xmm0 /* G: t = c + b */ + paddd %xmm5, %xmm4 /* H: t = c + b */ + movdqa %xmm0, %xmm3 /* G: rotate t (0) */ + movdqa %xmm4, %xmm7 /* H: rotate t (0) */ + pslld $13, %xmm0 /* G: rotate t (1) */ + psrld $19, %xmm3 /* G: rotate t (2) */ + pslld $13, %xmm4 /* H: rotate t (1) */ + psrld $19, %xmm7 /* H: rotate t (2) */ + por %xmm3, %xmm0 /* G: rotate t (3) */ + por %xmm7, %xmm4 /* H: rotate t (3) */ + pxor 144(%edx), %xmm0 /* G: d = d ^ t */ + pxor 224(%edx), %xmm4 /* H: d = d ^ t */ + movdqa %xmm0, 144(%edx) /* G: store d */ + movdqa %xmm4, 224(%edx) /* H: store d */ + paddd %xmm2, %xmm0 /* G: t = d + c */ + paddd %xmm6, %xmm4 /* H: t = d + c */ + movdqa %xmm0, %xmm3 /* G: rotate t (0) */ + movdqa %xmm4, %xmm7 /* H: rotate t (0) */ + pslld $18, %xmm0 /* G: rotate t (1) */ + psrld $14, %xmm3 /* G: rotate t (2) */ + pslld $18, %xmm4 /* H: rotate t (1) */ + psrld $14, %xmm7 /* H: rotate t (2) */ + por %xmm3, %xmm0 /* G: rotate t (3) */ + por %xmm7, %xmm4 /* H: rotate t (3) */ + pxor 160(%edx), %xmm0 /* G: a = a ^ t */ + pxor 240(%edx), %xmm4 /* H: a = a ^ t */ + movdqa %xmm0, 160(%edx) /* G: store a */ + movdqa %xmm4, 240(%edx) /* H: store a */ + decl %ecx + jnz .xor_salsa_4way + +/* write back data */ + movdqa 0(%eax), %xmm0 + movdqa 16(%eax), %xmm1 + movdqa 32(%eax), %xmm2 + movdqa 48(%eax), %xmm3 + movdqa 64(%eax), %xmm4 + movdqa 80(%eax), %xmm5 + movdqa 96(%eax), %xmm6 + movdqa 112(%eax), %xmm7 + paddd 0(%edx), %xmm0 + paddd 16(%edx), %xmm1 + paddd 32(%edx), %xmm2 + paddd 48(%edx), %xmm3 + paddd 64(%edx), %xmm4 + paddd 80(%edx), %xmm5 + paddd 96(%edx), %xmm6 + paddd 112(%edx), %xmm7 + movdqa %xmm0, 0(%eax) + movdqa %xmm1, 16(%eax) + movdqa %xmm2, 32(%eax) + movdqa %xmm3, 48(%eax) + movdqa %xmm4, 64(%eax) + movdqa %xmm5, 80(%eax) + movdqa %xmm6, 96(%eax) + movdqa %xmm7, 112(%eax) + movdqa 128(%eax), %xmm0 + movdqa 144(%eax), %xmm1 + movdqa 160(%eax), %xmm2 + movdqa 176(%eax), %xmm3 + movdqa 192(%eax), %xmm4 + movdqa 208(%eax), %xmm5 + movdqa 224(%eax), %xmm6 + movdqa 240(%eax), %xmm7 + paddd 128(%edx), %xmm0 + paddd 144(%edx), %xmm1 + paddd 160(%edx), %xmm2 + paddd 176(%edx), %xmm3 + paddd 192(%edx), %xmm4 + paddd 208(%edx), %xmm5 + paddd 224(%edx), %xmm6 + paddd 240(%edx), %xmm7 + movdqa %xmm0, 128(%eax) + movdqa %xmm1, 144(%eax) + movdqa %xmm2, 160(%eax) + movdqa %xmm3, 176(%eax) + movdqa %xmm4, 192(%eax) + movdqa %xmm5, 208(%eax) + movdqa %xmm6, 224(%eax) + movdqa %xmm7, 240(%eax) + + ret + + +/* neoscrypt_xor_chacha_4way(mem, xormem, workmem, double_rounds) + * i386 (SSE2) ChaCha20 4-way with XOR */ +.globl neoscrypt_xor_chacha_4way +.globl _neoscrypt_xor_chacha_4way +neoscrypt_xor_chacha_4way: +_neoscrypt_xor_chacha_4way: +/* XOR and copy to temporary memory */ + movl 4(%esp), %eax + movl 8(%esp), %ecx + movl 12(%esp), %edx + movdqa 0(%eax), %xmm0 + movdqa 16(%eax), %xmm1 + movdqa 32(%eax), %xmm2 + movdqa 48(%eax), %xmm3 + movdqa 64(%eax), %xmm4 + movdqa 80(%eax), %xmm5 + movdqa 96(%eax), %xmm6 + movdqa 112(%eax), %xmm7 + pxor 0(%ecx), %xmm0 + pxor 16(%ecx), %xmm1 + pxor 32(%ecx), %xmm2 + pxor 48(%ecx), %xmm3 + pxor 64(%ecx), %xmm4 + pxor 80(%ecx), %xmm5 + pxor 96(%ecx), %xmm6 + pxor 112(%ecx), %xmm7 + movdqa %xmm0, 0(%eax) + movdqa %xmm1, 16(%eax) + movdqa %xmm2, 32(%eax) + movdqa %xmm3, 48(%eax) + movdqa %xmm4, 64(%eax) + movdqa %xmm5, 80(%eax) + movdqa %xmm6, 96(%eax) + movdqa %xmm7, 112(%eax) + movdqa %xmm0, 0(%edx) + movdqa %xmm1, 16(%edx) + movdqa %xmm2, 32(%edx) + movdqa %xmm3, 48(%edx) + movdqa %xmm4, 64(%edx) + movdqa %xmm5, 80(%edx) + movdqa %xmm6, 96(%edx) + movdqa %xmm7, 112(%edx) + movdqa 128(%eax), %xmm0 + movdqa 144(%eax), %xmm1 + movdqa 160(%eax), %xmm2 + movdqa 176(%eax), %xmm3 + movdqa 192(%eax), %xmm4 + movdqa 208(%eax), %xmm5 + movdqa 224(%eax), %xmm6 + movdqa 240(%eax), %xmm7 + pxor 128(%ecx), %xmm0 + pxor 144(%ecx), %xmm1 + pxor 160(%ecx), %xmm2 + pxor 176(%ecx), %xmm3 + pxor 192(%ecx), %xmm4 + pxor 208(%ecx), %xmm5 + pxor 224(%ecx), %xmm6 + pxor 240(%ecx), %xmm7 + movdqa %xmm0, 128(%eax) + movdqa %xmm1, 144(%eax) + movdqa %xmm2, 160(%eax) + movdqa %xmm3, 176(%eax) + movdqa %xmm4, 192(%eax) + movdqa %xmm5, 208(%eax) + movdqa %xmm6, 224(%eax) + movdqa %xmm7, 240(%eax) + movdqa %xmm0, 128(%edx) + movdqa %xmm1, 144(%edx) + movdqa %xmm2, 160(%edx) + movdqa %xmm3, 176(%edx) + movdqa %xmm4, 192(%edx) + movdqa %xmm5, 208(%edx) + movdqa %xmm6, 224(%edx) + movdqa %xmm7, 240(%edx) +/* number of double rounds */ + movl 16(%esp), %ecx +.xor_chacha_4way: +/* quarters A and B, loads for C */ + movdqa 0(%edx), %xmm0 /* A: load a */ + movdqa 64(%edx), %xmm1 /* A: load b */ + paddd %xmm1, %xmm0 /* A: a = a + b */ + movdqa 192(%edx), %xmm3 /* A: load d */ + pxor %xmm0, %xmm3 /* A: d = d ^ a */ + movdqa 128(%edx), %xmm2 /* A: load c */ + movdqa %xmm3, %xmm7 /* A: rotate d (0) */ + movdqa 16(%edx), %xmm4 /* B: load a */ + pslld $16, %xmm3 /* A: rotate d (1) */ + psrld $16, %xmm7 /* A: rotate d (2) */ + movdqa 80(%edx), %xmm5 /* B: load b */ + por %xmm7, %xmm3 /* A: rotate d (3) */ + paddd %xmm3, %xmm2 /* A: c = c + d */ + paddd %xmm5, %xmm4 /* B: a = a + b */ + pxor %xmm2, %xmm1 /* A: b = b ^ c */ + movdqa %xmm1, %xmm7 /* A: rotate b (0) */ + movdqa 208(%edx), %xmm6 /* B: load d */ + pslld $12, %xmm1 /* A: rotate b (1) */ + psrld $20, %xmm7 /* A: rotate b (2) */ + pxor %xmm4, %xmm6 /* B: d = d ^ a */ + por %xmm7, %xmm1 /* A: rotate b (3) */ + movdqa %xmm6, %xmm7 /* B: rotate d (0) */ + paddd %xmm1, %xmm0 /* A: a = a + b */ + pslld $16, %xmm6 /* B: rotate d (1) */ + psrld $16, %xmm7 /* B: rotate d (2) */ + movdqa %xmm0, 0(%edx) /* A: store a */ + pxor %xmm0, %xmm3 /* A: d = d ^ a */ + por %xmm7, %xmm6 /* B: rotate d (3) */ + movdqa 144(%edx), %xmm0 /* B: load c */ + movdqa %xmm3, %xmm7 /* A: rotate d (0) */ + paddd %xmm6, %xmm0 /* B: c = c + d */ + pslld $8, %xmm3 /* A: rotate d (1) */ + psrld $24, %xmm7 /* A: rotate d (2) */ + pxor %xmm0, %xmm5 /* B: b = b ^ c */ + por %xmm7, %xmm3 /* A: rotate d (3) */ + movdqa %xmm5, %xmm7 /* B: rotate b (0) */ + movdqa %xmm3, 192(%edx) /* A: store d */ + paddd %xmm3, %xmm2 /* A: c = c + d */ + pslld $12, %xmm5 /* B: rotate b (1) */ + psrld $20, %xmm7 /* B: rotate b (2) */ + movdqa %xmm2, 128(%edx) /* A: store c */ + pxor %xmm2, %xmm1 /* A: b = b ^ c */ + por %xmm7, %xmm5 /* B: rotate b (3) */ + movdqa 224(%edx), %xmm3 /* C: load d */ + movdqa %xmm1, %xmm7 /* A: rotate b (0) */ + paddd %xmm5, %xmm4 /* B: a = a + b */ + pslld $7, %xmm1 /* A: rotate b (1) */ + psrld $25, %xmm7 /* A: rotate b (2) */ + movdqa %xmm4, 16(%edx) /* B: store a */ + pxor %xmm4, %xmm6 /* B: d = d ^ a */ + por %xmm7, %xmm1 /* A: rotate b (3) */ + movdqa 160(%edx), %xmm2 /* C: load c */ + movdqa %xmm6, %xmm7 /* B: rotate d (0) */ + movdqa %xmm1, 64(%edx) /* A: store b */ + pslld $8, %xmm6 /* B: rotate d (1) */ + psrld $24, %xmm7 /* B: rotate d (2) */ + movdqa 96(%edx), %xmm1 /* C: load b */ + por %xmm7, %xmm6 /* B: rotate d (3) */ + movdqa %xmm6, 208(%edx) /* B: store d */ + paddd %xmm6, %xmm0 /* B: c = c + d */ + movdqa %xmm0, 144(%edx) /* B: store c */ + pxor %xmm0, %xmm5 /* B: b = b ^ c */ + movdqa %xmm5, %xmm7 /* B: rotate b (0) */ + movdqa 32(%edx), %xmm0 /* C: load a */ + pslld $7, %xmm5 /* B: rotate b (1) */ + psrld $25, %xmm7 /* B: rotate b (2) */ + por %xmm7, %xmm5 /* B: rotate b (3) */ + movdqa %xmm5, 80(%edx) /* B: store b */ +/* quarters C and D, loads for E */ + paddd %xmm1, %xmm0 /* C: a = a + b */ + pxor %xmm0, %xmm3 /* C: d = d ^ a */ + movdqa %xmm3, %xmm7 /* C: rotate d (0) */ + movdqa 48(%edx), %xmm4 /* D: load a */ + pslld $16, %xmm3 /* C: rotate d (1) */ + psrld $16, %xmm7 /* C: rotate d (2) */ + movdqa 112(%edx), %xmm5 /* D: load b */ + por %xmm7, %xmm3 /* C: rotate d (3) */ + paddd %xmm3, %xmm2 /* C: c = c + d */ + paddd %xmm5, %xmm4 /* D: a = a + b */ + pxor %xmm2, %xmm1 /* C: b = b ^ c */ + movdqa %xmm1, %xmm7 /* C: rotate b (0) */ + movdqa 240(%edx), %xmm6 /* D: load d */ + pslld $12, %xmm1 /* C: rotate b (1) */ + psrld $20, %xmm7 /* C: rotate b (2) */ + pxor %xmm4, %xmm6 /* D: d = d ^ a */ + por %xmm7, %xmm1 /* C: rotate b (3) */ + movdqa %xmm6, %xmm7 /* D: rotate d (0) */ + paddd %xmm1, %xmm0 /* C: a = a + b */ + pslld $16, %xmm6 /* D: rotate d (1) */ + psrld $16, %xmm7 /* D: rotate d (2) */ + movdqa %xmm0, 32(%edx) /* C: store a */ + pxor %xmm0, %xmm3 /* C: d = d ^ a */ + por %xmm7, %xmm6 /* D: rotate d (3) */ + movdqa 176(%edx), %xmm0 /* D: load c */ + movdqa %xmm3, %xmm7 /* C: rotate d (0) */ + paddd %xmm6, %xmm0 /* D: c = c + d */ + pslld $8, %xmm3 /* C: rotate d (1) */ + psrld $24, %xmm7 /* C: rotate d (2) */ + pxor %xmm0, %xmm5 /* D: b = b ^ c */ + por %xmm7, %xmm3 /* C: rotate d (3) */ + movdqa %xmm5, %xmm7 /* D: rotate b (0) */ + movdqa %xmm3, 224(%edx) /* C: store d */ + paddd %xmm3, %xmm2 /* C: c = c + d */ + pslld $12, %xmm5 /* D: rotate b (1) */ + psrld $20, %xmm7 /* D: rotate b (2) */ + movdqa %xmm2, 160(%edx) /* C: store c */ + pxor %xmm2, %xmm1 /* C: b = b ^ c */ + por %xmm7, %xmm5 /* D: rotate b (3) */ + movdqa %xmm1, %xmm7 /* C: rotate b (0) */ + paddd %xmm5, %xmm4 /* D: a = a + b */ + pslld $7, %xmm1 /* C: rotate b (1) */ + psrld $25, %xmm7 /* C: rotate b (2) */ + movdqa %xmm4, 48(%edx) /* D: store a */ + pxor %xmm4, %xmm6 /* D: d = d ^ a */ + por %xmm7, %xmm1 /* C: rotate b (3) */ + movdqa 160(%edx), %xmm2 /* E: load c */ + movdqa %xmm6, %xmm7 /* D: rotate d (0) */ + movdqa %xmm1, 96(%edx) /* C: store b */ + pslld $8, %xmm6 /* D: rotate d (1) */ + psrld $24, %xmm7 /* D: rotate d (2) */ + movdqa 80(%edx), %xmm1 /* E: load b */ + por %xmm7, %xmm6 /* D: rotate d (3) */ + movdqa %xmm6, 240(%edx) /* D: store d */ + movdqa %xmm6, %xmm3 /* E: load d */ + paddd %xmm6, %xmm0 /* D: c = c + d */ + movdqa %xmm0, 176(%edx) /* D: store c */ + pxor %xmm0, %xmm5 /* D: b = b ^ c */ + movdqa %xmm5, %xmm7 /* D: rotate b (0) */ + movdqa 0(%edx), %xmm0 /* E: load a */ + pslld $7, %xmm5 /* D: rotate b (1) */ + psrld $25, %xmm7 /* D: rotate b (2) */ + por %xmm7, %xmm5 /* D: rotate b (3) */ + movdqa %xmm5, 112(%edx) /* D: store b */ +/* quarters E and F, loads for G */ + paddd %xmm1, %xmm0 /* E: a = a + b */ + pxor %xmm0, %xmm3 /* E: d = d ^ a */ + movdqa %xmm3, %xmm7 /* E: rotate d (0) */ + movdqa 16(%edx), %xmm4 /* F: load a */ + pslld $16, %xmm3 /* E: rotate d (1) */ + psrld $16, %xmm7 /* E: rotate d (2) */ + movdqa 96(%edx), %xmm5 /* F: load b */ + por %xmm7, %xmm3 /* E: rotate d (3) */ + paddd %xmm3, %xmm2 /* E: c = c + d */ + paddd %xmm5, %xmm4 /* F: a = a + b */ + pxor %xmm2, %xmm1 /* E: b = b ^ c */ + movdqa %xmm1, %xmm7 /* E: rotate b (0) */ + movdqa 192(%edx), %xmm6 /* F: load d */ + pslld $12, %xmm1 /* E: rotate b (1) */ + psrld $20, %xmm7 /* E: rotate b (2) */ + pxor %xmm4, %xmm6 /* F: d = d ^ a */ + por %xmm7, %xmm1 /* E: rotate b (3) */ + movdqa %xmm6, %xmm7 /* F: rotate d (0) */ + paddd %xmm1, %xmm0 /* E: a = a + b */ + pslld $16, %xmm6 /* F: rotate d (1) */ + psrld $16, %xmm7 /* F: rotate d (2) */ + movdqa %xmm0, 0(%edx) /* E: store a */ + pxor %xmm0, %xmm3 /* E: d = d ^ a */ + por %xmm7, %xmm6 /* F: rotate d (3) */ + movdqa 176(%edx), %xmm0 /* F: load c */ + movdqa %xmm3, %xmm7 /* E: rotate d (0) */ + paddd %xmm6, %xmm0 /* F: c = c + d */ + pslld $8, %xmm3 /* E: rotate d (1) */ + psrld $24, %xmm7 /* E: rotate d (2) */ + pxor %xmm0, %xmm5 /* F: b = b ^ c */ + por %xmm7, %xmm3 /* E: rotate d (3) */ + movdqa %xmm5, %xmm7 /* F: rotate b (0) */ + movdqa %xmm3, 240(%edx) /* E: store d */ + paddd %xmm3, %xmm2 /* E: c = c + d */ + pslld $12, %xmm5 /* F: rotate b (1) */ + psrld $20, %xmm7 /* F: rotate b (2) */ + movdqa %xmm2, 160(%edx) /* E: store c */ + pxor %xmm2, %xmm1 /* E: b = b ^ c */ + por %xmm7, %xmm5 /* F: rotate b (3) */ + movdqa 208(%edx), %xmm3 /* G: load d */ + movdqa %xmm1, %xmm7 /* E: rotate b (0) */ + paddd %xmm5, %xmm4 /* F: a = a + b */ + pslld $7, %xmm1 /* E: rotate b (1) */ + psrld $25, %xmm7 /* E: rotate b (2) */ + movdqa %xmm4, 16(%edx) /* F: store a */ + pxor %xmm4, %xmm6 /* F: d = d ^ a */ + por %xmm7, %xmm1 /* E: rotate b (3) */ + movdqa 128(%edx), %xmm2 /* G: load c */ + movdqa %xmm6, %xmm7 /* F: rotate d (0) */ + movdqa %xmm1, 80(%edx) /* E: store b */ + pslld $8, %xmm6 /* F: rotate d (1) */ + psrld $24, %xmm7 /* F: rotate d (2) */ + movdqa 112(%edx), %xmm1 /* G: load b */ + por %xmm7, %xmm6 /* F: rotate d (3) */ + movdqa %xmm6, 192(%edx) /* F: store d */ + paddd %xmm6, %xmm0 /* F: c = c + d */ + movdqa %xmm0, 176(%edx) /* F: store c */ + pxor %xmm0, %xmm5 /* F: b = b ^ c */ + movdqa %xmm5, %xmm7 /* F: rotate b (0) */ + movdqa 32(%edx), %xmm0 /* G: load a */ + pslld $7, %xmm5 /* F: rotate b (1) */ + psrld $25, %xmm7 /* F: rotate b (2) */ + por %xmm7, %xmm5 /* F: rotate b (3) */ + movdqa %xmm5, 96(%edx) /* F: store b */ +/* quarters G and H */ + paddd %xmm1, %xmm0 /* G: a = a + b */ + pxor %xmm0, %xmm3 /* G: d = d ^ a */ + movdqa %xmm3, %xmm7 /* G: rotate d (0) */ + movdqa 48(%edx), %xmm4 /* H: load a */ + pslld $16, %xmm3 /* G: rotate d (1) */ + psrld $16, %xmm7 /* G: rotate d (2) */ + movdqa 64(%edx), %xmm5 /* H: load b */ + por %xmm7, %xmm3 /* G: rotate d (3) */ + paddd %xmm3, %xmm2 /* G: c = c + d */ + paddd %xmm5, %xmm4 /* H: a = a + b */ + pxor %xmm2, %xmm1 /* G: b = b ^ c */ + movdqa %xmm1, %xmm7 /* G: rotate b (0) */ + movdqa 224(%edx), %xmm6 /* H: load d */ + pslld $12, %xmm1 /* G: rotate b (1) */ + psrld $20, %xmm7 /* G: rotate b (2) */ + pxor %xmm4, %xmm6 /* H: d = d ^ a */ + por %xmm7, %xmm1 /* G: rotate b (3) */ + movdqa %xmm6, %xmm7 /* H: rotate d (0) */ + paddd %xmm1, %xmm0 /* G: a = a + b */ + pslld $16, %xmm6 /* H: rotate d (1) */ + psrld $16, %xmm7 /* H: rotate d (2) */ + movdqa %xmm0, 32(%edx) /* G: store a */ + pxor %xmm0, %xmm3 /* G: d = d ^ a */ + por %xmm7, %xmm6 /* H: rotate d (3) */ + movdqa 144(%edx), %xmm0 /* H: load c */ + movdqa %xmm3, %xmm7 /* G: rotate d (0) */ + paddd %xmm6, %xmm0 /* H: c = c + d */ + pslld $8, %xmm3 /* G: rotate d (1) */ + psrld $24, %xmm7 /* G: rotate d (2) */ + pxor %xmm0, %xmm5 /* H: b = b ^ c */ + por %xmm7, %xmm3 /* G: rotate d (3) */ + movdqa %xmm5, %xmm7 /* H: rotate b (0) */ + movdqa %xmm3, 208(%edx) /* G: store d */ + paddd %xmm3, %xmm2 /* G: c = c + d */ + pslld $12, %xmm5 /* H: rotate b (1) */ + psrld $20, %xmm7 /* H: rotate b (2) */ + movdqa %xmm2, 128(%edx) /* G: store c */ + pxor %xmm2, %xmm1 /* G: b = b ^ c */ + por %xmm7, %xmm5 /* H: rotate b (3) */ + movdqa %xmm1, %xmm7 /* G: rotate b (0) */ + paddd %xmm5, %xmm4 /* H: a = a + b */ + pslld $7, %xmm1 /* G: rotate b (1) */ + psrld $25, %xmm7 /* G: rotate b (2) */ + movdqa %xmm4, 48(%edx) /* H: store a */ + pxor %xmm4, %xmm6 /* H: d = d ^ a */ + por %xmm7, %xmm1 /* G: rotate b (3) */ + movdqa %xmm6, %xmm7 /* H: rotate d (0) */ + movdqa %xmm1, 112(%edx) /* G: store b */ + pslld $8, %xmm6 /* H: rotate d (1) */ + psrld $24, %xmm7 /* H: rotate d (2) */ + por %xmm7, %xmm6 /* H: rotate d (3) */ + movdqa %xmm6, 224(%edx) /* H: store d */ + paddd %xmm6, %xmm0 /* H: c = c + d */ + movdqa %xmm0, 144(%edx) /* H: store c */ + pxor %xmm0, %xmm5 /* H: b = b ^ c */ + movdqa %xmm5, %xmm7 /* H: rotate b (0) */ + pslld $7, %xmm5 /* H: rotate b (1) */ + psrld $25, %xmm7 /* H: rotate b (2) */ + por %xmm7, %xmm5 /* H: rotate b (3) */ + movdqa %xmm5, 64(%edx) /* H: store b */ + decl %ecx + jnz .xor_chacha_4way + +/* write back data */ + movdqa 0(%eax), %xmm0 + movdqa 16(%eax), %xmm1 + movdqa 32(%eax), %xmm2 + movdqa 48(%eax), %xmm3 + movdqa 64(%eax), %xmm4 + movdqa 80(%eax), %xmm5 + movdqa 96(%eax), %xmm6 + movdqa 112(%eax), %xmm7 + paddd 0(%edx), %xmm0 + paddd 16(%edx), %xmm1 + paddd 32(%edx), %xmm2 + paddd 48(%edx), %xmm3 + paddd 64(%edx), %xmm4 + paddd 80(%edx), %xmm5 + paddd 96(%edx), %xmm6 + paddd 112(%edx), %xmm7 + movdqa %xmm0, 0(%eax) + movdqa %xmm1, 16(%eax) + movdqa %xmm2, 32(%eax) + movdqa %xmm3, 48(%eax) + movdqa %xmm4, 64(%eax) + movdqa %xmm5, 80(%eax) + movdqa %xmm6, 96(%eax) + movdqa %xmm7, 112(%eax) + movdqa 128(%eax), %xmm0 + movdqa 144(%eax), %xmm1 + movdqa 160(%eax), %xmm2 + movdqa 176(%eax), %xmm3 + movdqa 192(%eax), %xmm4 + movdqa 208(%eax), %xmm5 + movdqa 224(%eax), %xmm6 + movdqa 240(%eax), %xmm7 + paddd 128(%edx), %xmm0 + paddd 144(%edx), %xmm1 + paddd 160(%edx), %xmm2 + paddd 176(%edx), %xmm3 + paddd 192(%edx), %xmm4 + paddd 208(%edx), %xmm5 + paddd 224(%edx), %xmm6 + paddd 240(%edx), %xmm7 + movdqa %xmm0, 128(%eax) + movdqa %xmm1, 144(%eax) + movdqa %xmm2, 160(%eax) + movdqa %xmm3, 176(%eax) + movdqa %xmm4, 192(%eax) + movdqa %xmm5, 208(%eax) + movdqa %xmm6, 224(%eax) + movdqa %xmm7, 240(%eax) + + ret + +#endif /* MINER_4WAY */ + +/* cpu_vec_exts() + * i386 detector of any vector extensions present + * output bits set in %eax: + * 0 : MMX + * 1 : Extended MMX (MMX+) + * 2 : 3DNow! + * 3 : Extended 3DNow! (3DNow!+) + * 4 : SSE + * 5 : SSE2 + * 6 : SSE3 + * 7 : SSSE3 + * 8 : SSE41 + * 9 : SSE42 + * 10 : SSE4A + * 11 : XOP + * 12 : FMA4 + * 13 : AVX + * 14 : F16C + * 15 : FMA3 + * the other bits are reserved for the future use */ +.globl cpu_vec_exts +.globl _cpu_vec_exts +cpu_vec_exts: +_cpu_vec_exts: + pushl %ebx + pushl %ebp + xorl %ebp, %ebp +/* the CPUID extended function 0 should report the max. + * supported extended function number in %eax */ + movl $0x80000000, %eax + cpuid + cmpl $0x80000001, %eax + jb .cpu_vec_st1 + movl $0x80000001, %eax + cpuid +/* MMX+ (bit 22 of %edx); implies MMX */ + testl $0x00400000, %edx + jz .cpu_vec_3dnp + orl $0x00000003, %ebp +.cpu_vec_3dnp: +/* 3DNow!+ (bit 30 of %edx); implies 3DNow! */ + testl $0x80000000, %edx + jz .cpu_vec_3dn + orl $0x0000000C, %ebp + jmp .cpu_vec_sse4a +.cpu_vec_3dn: +/* 3DNow! (bit 31 of %edx); implies MMX */ + testl $0x80000000, %edx + jz .cpu_vec_sse4a + orl $0x00000005, %ebp +.cpu_vec_sse4a: +/* SSE4A (bit 6 of %ecx) */ + testl $0x00000040, %ecx + jz .cpu_vec_st1 + orl $0x00000400, %ebp +/* XOP (bit 11 of %ecx) */ + testl $0x00000800, %ecx + jz .cpu_vec_st1 + orl $0x00000800, %ebp +/* FMA4 (bit 16 of %ecx) */ + testl $0x00010000, %ecx + jz .cpu_vec_st1 + orl $0x00001000, %ebp +.cpu_vec_st1: +/* Some original Cyrix processors report nonsense in %ecx of + * the CPUID standard function 1, however they don't support even SSE */ + movl $1, %eax + cpuid +/* SSE (bit 25 of %edx); implies MMX+ and MMX */ + testl $0x02000000, %edx + jz .cpu_vec_mmx + orl $0x00000013, %ebp +/* SSE2 (bit 26 of %edx) */ + testl $0x04000000, %edx + jz .cpu_vec_exit + orl $0x00000020, %ebp +/* SSE3 (bit 0 of %ecx) */ + testl $0x00000001, %ecx + jz .cpu_vec_exit + orl $0x00000040, %ebp +/* SSSE3 (bit 9 of %ecx) */ + testl $0x00000100, %ecx + jz .cpu_vec_exit + orl $0x00000080, %ebp +/* SSE4.1 (bit 19 of %ecx) */ + testl $0x00080000, %ecx + jz .cpu_vec_exit + orl $0x00000100, %ebp +/* SSE4.2 (bit 20 of %ecx) */ + testl $0x00100000, %ecx + jz .cpu_vec_exit + orl $0x00000200, %ebp +/* AVX (bit 28 of %ecx) */ + testl $0x10000000, %ecx + jz .cpu_vec_exit + orl $0x00002000, %ebp + jmp .cpu_vec_exit +/* F16C (bit 29 of %ecx) */ + testl $0x20000000, %ecx + jz .cpu_vec_exit + orl $0x00004000, %ebp + jmp .cpu_vec_exit +/* FMA3 (bit 12 of %ecx) */ + testl $0x00001000, %ecx + jz .cpu_vec_exit + orl $0x00008000, %ebp + jmp .cpu_vec_exit + +.cpu_vec_mmx: +/* MMX (bit 23 of %edx) */ + testl $0x00800000, %edx + jz .cpu_vec_exit + orl $0x00000001, %ebp + +.cpu_vec_exit: + movl %ebp, %eax + popl %ebp + popl %ebx + ret + +#endif /* (ASM) && (__i386__) */ From 39b67098d34fa4902f15518a6479717779a8f863 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 24 Jan 2018 10:32:06 +0100 Subject: [PATCH 028/348] Neoscrypt tests --- src/MiningCore.Tests/Crypto/HashingTests.cs | 18 ++++++ .../Crypto/Hashing/Algorithms/NeoScrypt.cs | 52 ++++++++++++++++++ .../Crypto/Hashing/Algorithms/Scrypt.cs | 26 ++++----- src/MiningCore/Native/LibMultihash.cs | 22 ++++---- .../runtimes/win-x64/native/libmultihash.dll | Bin 870912 -> 873472 bytes .../runtimes/win-x86/native/libmultihash.dll | Bin 870912 -> 872448 bytes 6 files changed, 94 insertions(+), 24 deletions(-) create mode 100644 src/MiningCore/Crypto/Hashing/Algorithms/NeoScrypt.cs diff --git a/src/MiningCore.Tests/Crypto/HashingTests.cs b/src/MiningCore.Tests/Crypto/HashingTests.cs index 064624311..a0f4b87c5 100644 --- a/src/MiningCore.Tests/Crypto/HashingTests.cs +++ b/src/MiningCore.Tests/Crypto/HashingTests.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Linq; using MiningCore.Crypto.Hashing.Algorithms; using MiningCore.Crypto.Hashing.Equihash; @@ -77,6 +78,23 @@ public void Scrypt_Hash_Should_Throw_On_Null_Input() Assert.Throws(() => hasher.Digest(null)); } +/* + [Fact] + public void NeoScrypt_Hash_Should_Match() + { + var hasher = new NeoScrypt(0); + var result = hasher.Digest(testValue).ToHexString(); + + Assert.Equal("2d48f6104ede1ecee0021b1e92f4c85aa6fbf38fe93c6b94bc4addf370ae8bb7", result); + } +*/ + [Fact] + public void NeoScrypt_Hash_Should_Throw_On_Null_Input() + { + var hasher = new NeoScrypt(0); + Assert.Throws(() => hasher.Digest(null)); + } + [Fact] public void ScryptN_Hash_Should_Match() { diff --git a/src/MiningCore/Crypto/Hashing/Algorithms/NeoScrypt.cs b/src/MiningCore/Crypto/Hashing/Algorithms/NeoScrypt.cs new file mode 100644 index 000000000..ede86941c --- /dev/null +++ b/src/MiningCore/Crypto/Hashing/Algorithms/NeoScrypt.cs @@ -0,0 +1,52 @@ +/* +Copyright 2017 Coin Foundry (coinfoundry.org) +Authors: Oliver Weichhold (oliver@weichhold.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +using MiningCore.Contracts; +using MiningCore.Native; + +namespace MiningCore.Crypto.Hashing.Algorithms +{ + public unsafe class NeoScrypt : IHashAlgorithm + { + public NeoScrypt(uint profile) + { + this.profile = profile; + } + + private readonly uint profile; + + public byte[] Digest(byte[] data, params object[] extra) + { + Contract.RequiresNonNull(data, nameof(data)); + + var result = new byte[32]; + + fixed (byte* input = data) + { + fixed (byte* output = result) + { + LibMultihash.neoscrypt(input, output, (uint)data.Length, profile); + } + } + + return result; + } + } +} diff --git a/src/MiningCore/Crypto/Hashing/Algorithms/Scrypt.cs b/src/MiningCore/Crypto/Hashing/Algorithms/Scrypt.cs index 096fed4be..5af65b48f 100644 --- a/src/MiningCore/Crypto/Hashing/Algorithms/Scrypt.cs +++ b/src/MiningCore/Crypto/Hashing/Algorithms/Scrypt.cs @@ -1,20 +1,20 @@ -/* +/* Copyright 2017 Coin Foundry (coinfoundry.org) Authors: Oliver Weichhold (oliver@weichhold.com) -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ @@ -40,11 +40,11 @@ public byte[] Digest(byte[] data, params object[] extra) var result = new byte[32]; - fixed(byte* input = data) + fixed (byte* input = data) { - fixed(byte* output = result) + fixed (byte* output = result) { - LibMultihash.scrypt(input, output, n, r, (uint) data.Length); + LibMultihash.scrypt(input, output, n, r, (uint)data.Length); } } diff --git a/src/MiningCore/Native/LibMultihash.cs b/src/MiningCore/Native/LibMultihash.cs index c53ca8c7f..5d34c0d34 100644 --- a/src/MiningCore/Native/LibMultihash.cs +++ b/src/MiningCore/Native/LibMultihash.cs @@ -1,20 +1,20 @@ -/* +/* Copyright 2017 Coin Foundry (coinfoundry.org) Authors: Oliver Weichhold (oliver@weichhold.com) -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ @@ -38,7 +38,7 @@ public static unsafe class LibMultihash public static extern int x15(byte* input, byte* output, uint inputLength); [DllImport("libmultihash", EntryPoint = "neoscrypt_export", CallingConvention = CallingConvention.Cdecl)] - public static extern int neoscrypt(byte* input, byte* output, uint inputLength, int profile); + public static extern int neoscrypt(byte* input, byte* output, uint inputLength, uint profile); [DllImport("libmultihash", EntryPoint = "scryptn_export", CallingConvention = CallingConvention.Cdecl)] public static extern int scryptn(byte* input, byte* output, uint nFactor, uint inputLength); diff --git a/src/MiningCore/runtimes/win-x64/native/libmultihash.dll b/src/MiningCore/runtimes/win-x64/native/libmultihash.dll index d5b8a193b05bb02efbd31318d4d50d2550ef0bc5..ae8dcd40d2e9d6fe3291511210ae0cff2aee0422 100644 GIT binary patch delta 127731 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

Yxa!tH~;W!<^%y; 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 RT36~R6|ofu?yz+<{|_;HQp5lN delta 125388 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 NDELgPfLYymUlq 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 ~X%4BLb-O3gNB1Y(_t6!S82SGv&R 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

ra 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 A)&Kwi diff --git a/src/Native/libmultihash/Makefile b/src/Native/libmultihash/Makefile index f74270fe1..8bd23a1bd 100644 --- a/src/Native/libmultihash/Makefile +++ b/src/Native/libmultihash/Makefile @@ -10,7 +10,8 @@ OBJECTS = bcrypt.o blake.o c11.o dcrypt.o fresh.o \ sha3/aes_helper.o sha3/hamsi.o sha3/hamsi_helper.o sha3/sph_blake.o sha3/sph_bmw.o sha3/sph_cubehash.o \ sha3/sph_echo.o sha3/sph_fugue.o sha3/sph_groestl.o sha3/sph_hefty1.o sha3/sph_jh.o sha3/sph_keccak.o \ sha3/sph_luffa.o sha3/sph_shabal.o sha3/sph_shavite.o sha3/sph_simd.o sha3/sph_skein.o sha3/sph_whirlpool.o \ - shavite3.o skein.o x11.o x15.o \ + sha3/sph_haval.o sha3/sph_sha2.o sha3/sph_sha2big.o \ + shavite3.o skein.o x11.o x15.o x17.o \ Lyra2.o Lyra2RE.o Sponge.o \ equi/endian.o equi/equi.o \ libethash/internal.o libethash/io.o libethash/io_posix.o libethash/sha3.o diff --git a/src/Native/libmultihash/exports.cpp b/src/Native/libmultihash/exports.cpp index ac276fda8..4c2be0c56 100644 --- a/src/Native/libmultihash/exports.cpp +++ b/src/Native/libmultihash/exports.cpp @@ -34,6 +34,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "x14.h" #include "nist5.h" #include "x15.h" +#include "x17.h" #include "fresh.h" #include "dcrypt.h" #include "jh.h" @@ -68,6 +69,11 @@ extern "C" MODULE_API void x11_export(const char* input, char* output, uint32_t x11_hash(input, output, input_len); } +extern "C" MODULE_API void x17_export(const char* input, char* output, uint32_t input_len) +{ + x17_hash(input, output, input_len); +} + extern "C" MODULE_API void x15_export(const char* input, char* output, uint32_t input_len) { x15_hash(input, output, input_len); diff --git a/src/Native/libmultihash/libmultihash.vcxproj b/src/Native/libmultihash/libmultihash.vcxproj index 347583c2d..a12686db6 100644 --- a/src/Native/libmultihash/libmultihash.vcxproj +++ b/src/Native/libmultihash/libmultihash.vcxproj @@ -202,10 +202,12 @@ + + @@ -222,6 +224,7 @@ + @@ -254,17 +257,19 @@ - + + + @@ -278,6 +283,7 @@ + diff --git a/src/Native/libmultihash/libmultihash.vcxproj.filters b/src/Native/libmultihash/libmultihash.vcxproj.filters index 5dcfc1bf9..96053bcf3 100644 --- a/src/Native/libmultihash/libmultihash.vcxproj.filters +++ b/src/Native/libmultihash/libmultihash.vcxproj.filters @@ -191,6 +191,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + @@ -271,9 +280,6 @@ Source Files - - Source Files - Source Files @@ -355,6 +361,18 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + diff --git a/src/Native/libmultihash/sha3/hamsi_helper.c b/src/Native/libmultihash/sha3/hamsi_helper.c index cdf2fc9fe..807085295 100644 --- a/src/Native/libmultihash/sha3/hamsi_helper.c +++ b/src/Native/libmultihash/sha3/hamsi_helper.c @@ -10,7 +10,7 @@ * ==========================(LICENSE BEGIN)============================ * * Copyright (c) 2007-2010 Projet RNRT SAPHIR - * + * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -18,10 +18,10 @@ * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: - * + * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. diff --git a/src/Native/libmultihash/sha3/haval_helper.c b/src/Native/libmultihash/sha3/haval_helper.c new file mode 100644 index 000000000..c402fc699 --- /dev/null +++ b/src/Native/libmultihash/sha3/haval_helper.c @@ -0,0 +1,195 @@ +/* $Id: haval_helper.c 218 2010-06-08 17:06:34Z tp $ */ +/* + * Helper code, included (three times !) by HAVAL implementation. + * + * TODO: try to merge this with md_helper.c. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @author Thomas Pornin + */ + +#undef SPH_XCAT +#define SPH_XCAT(a, b) SPH_XCAT_(a, b) +#undef SPH_XCAT_ +#define SPH_XCAT_(a, b) a ## b + +static void +#ifdef SPH_UPTR +SPH_XCAT(SPH_XCAT(haval, PASSES), _short) +#else +SPH_XCAT(haval, PASSES) +#endif +(sph_haval_context *sc, const void *data, size_t len) +{ + unsigned current; + +#if SPH_64 + current = (unsigned)sc->count & 127U; +#else + current = (unsigned)sc->count_low & 127U; +#endif + while (len > 0) { + unsigned clen; +#if !SPH_64 + sph_u32 clow, clow2; +#endif + + clen = 128U - current; + if (clen > len) + clen = len; + memcpy(sc->buf + current, data, clen); + data = (const unsigned char *)data + clen; + current += clen; + len -= clen; + if (current == 128U) { + DSTATE; + IN_PREPARE(sc->buf); + + RSTATE; + SPH_XCAT(CORE, PASSES)(INW); + WSTATE; + current = 0; + } +#if SPH_64 + sc->count += clen; +#else + clow = sc->count_low; + clow2 = SPH_T32(clow + clen); + sc->count_low = clow2; + if (clow2 < clow) + sc->count_high ++; +#endif + } +} + +#ifdef SPH_UPTR +static void +SPH_XCAT(haval, PASSES)(sph_haval_context *sc, const void *data, size_t len) +{ + unsigned current; + size_t orig_len; +#if !SPH_64 + sph_u32 clow, clow2; +#endif + DSTATE; + + if (len < 256U) { + SPH_XCAT(SPH_XCAT(haval, PASSES), _short)(sc, data, len); + return; + } +#if SPH_64 + current = (unsigned)sc->count & 127U; +#else + current = (unsigned)sc->count_low & 127U; +#endif + if (current > 0) { + unsigned clen; + + clen = 128U - current; + SPH_XCAT(SPH_XCAT(haval, PASSES), _short)(sc, data, clen); + data = (const unsigned char *)data + clen; + len -= clen; + } +#if !SPH_UNALIGNED + if (((SPH_UPTR)data & 3U) != 0) { + SPH_XCAT(SPH_XCAT(haval, PASSES), _short)(sc, data, len); + return; + } +#endif + orig_len = len; + RSTATE; + while (len >= 128U) { + IN_PREPARE(data); + + SPH_XCAT(CORE, PASSES)(INW); + data = (const unsigned char *)data + 128U; + len -= 128U; + } + WSTATE; + if (len > 0) + memcpy(sc->buf, data, len); +#if SPH_64 + sc->count += (sph_u64)orig_len; +#else + clow = sc->count_low; + clow2 = SPH_T32(clow + orig_len); + sc->count_low = clow2; + if (clow2 < clow) + sc->count_high ++; + orig_len >>= 12; + orig_len >>= 10; + orig_len >>= 10; + sc->count_high += orig_len; +#endif +} +#endif + +static void +SPH_XCAT(SPH_XCAT(haval, PASSES), _close)(sph_haval_context *sc, + unsigned ub, unsigned n, void *dst) +{ + unsigned current; + DSTATE; + +#if SPH_64 + current = (unsigned)sc->count & 127U; +#else + current = (unsigned)sc->count_low & 127U; +#endif + sc->buf[current ++] = (0x01 << n) | ((ub & 0xFF) >> (8 - n)); + RSTATE; + if (current > 118U) { + memset(sc->buf + current, 0, 128U - current); + + do { + IN_PREPARE(sc->buf); + + SPH_XCAT(CORE, PASSES)(INW); + } while (0); + current = 0; + } + memset(sc->buf + current, 0, 118U - current); + sc->buf[118] = 0x01 | (PASSES << 3); + sc->buf[119] = sc->olen << 3; +#if SPH_64 + sph_enc64le_aligned(sc->buf + 120, SPH_T64(sc->count << 3)); +#else + sph_enc32le_aligned(sc->buf + 120, SPH_T32(sc->count_low << 3)); + sph_enc32le_aligned(sc->buf + 124, + SPH_T32((sc->count_high << 3) | (sc->count_low >> 29))); +#endif + do { + IN_PREPARE(sc->buf); + + SPH_XCAT(CORE, PASSES)(INW); + } while (0); + + WSTATE; + haval_out(sc, dst); + haval_init(sc, sc->olen, sc->passes); +} + diff --git a/src/Native/libmultihash/sha3/sph_haval.c b/src/Native/libmultihash/sha3/sph_haval.c new file mode 100644 index 000000000..90922b638 --- /dev/null +++ b/src/Native/libmultihash/sha3/sph_haval.c @@ -0,0 +1,975 @@ +/* $Id: haval.c 227 2010-06-16 17:28:38Z tp $ */ +/* + * HAVAL implementation. + * + * The HAVAL reference paper is of questionable clarity with regards to + * some details such as endianness of bits within a byte, bytes within + * a 32-bit word, or the actual ordering of words within a stream of + * words. This implementation has been made compatible with the reference + * implementation available on: http://labs.calyptix.com/haval.php + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @author Thomas Pornin + */ + +#include +#include + +#include "sph_haval.h" + +#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_HAVAL +#define SPH_SMALL_FOOTPRINT_HAVAL 1 +#endif + +/* + * Basic definition from the reference paper. + * +#define F1(x6, x5, x4, x3, x2, x1, x0) \ + (((x1) & (x4)) ^ ((x2) & (x5)) ^ ((x3) & (x6)) ^ ((x0) & (x1)) ^ (x0)) + * + */ + +#define F1(x6, x5, x4, x3, x2, x1, x0) \ + (((x1) & ((x0) ^ (x4))) ^ ((x2) & (x5)) ^ ((x3) & (x6)) ^ (x0)) + +/* + * Basic definition from the reference paper. + * +#define F2(x6, x5, x4, x3, x2, x1, x0) \ + (((x1) & (x2) & (x3)) ^ ((x2) & (x4) & (x5)) ^ ((x1) & (x2)) \ + ^ ((x1) & (x4)) ^ ((x2) & (x6)) ^ ((x3) & (x5)) \ + ^ ((x4) & (x5)) ^ ((x0) & (x2)) ^ (x0)) + * + */ + +#define F2(x6, x5, x4, x3, x2, x1, x0) \ + (((x2) & (((x1) & ~(x3)) ^ ((x4) & (x5)) ^ (x6) ^ (x0))) \ + ^ ((x4) & ((x1) ^ (x5))) ^ ((x3 & (x5)) ^ (x0))) + +/* + * Basic definition from the reference paper. + * +#define F3(x6, x5, x4, x3, x2, x1, x0) \ + (((x1) & (x2) & (x3)) ^ ((x1) & (x4)) ^ ((x2) & (x5)) \ + ^ ((x3) & (x6)) ^ ((x0) & (x3)) ^ (x0)) + * + */ + +#define F3(x6, x5, x4, x3, x2, x1, x0) \ + (((x3) & (((x1) & (x2)) ^ (x6) ^ (x0))) \ + ^ ((x1) & (x4)) ^ ((x2) & (x5)) ^ (x0)) + +/* + * Basic definition from the reference paper. + * +#define F4(x6, x5, x4, x3, x2, x1, x0) \ + (((x1) & (x2) & (x3)) ^ ((x2) & (x4) & (x5)) ^ ((x3) & (x4) & (x6)) \ + ^ ((x1) & (x4)) ^ ((x2) & (x6)) ^ ((x3) & (x4)) ^ ((x3) & (x5)) \ + ^ ((x3) & (x6)) ^ ((x4) & (x5)) ^ ((x4) & (x6)) ^ ((x0) & (x4)) ^ (x0)) + * + */ + +#define F4(x6, x5, x4, x3, x2, x1, x0) \ + (((x3) & (((x1) & (x2)) ^ ((x4) | (x6)) ^ (x5))) \ + ^ ((x4) & ((~(x2) & (x5)) ^ (x1) ^ (x6) ^ (x0))) \ + ^ ((x2) & (x6)) ^ (x0)) + +/* + * Basic definition from the reference paper. + * +#define F5(x6, x5, x4, x3, x2, x1, x0) \ + (((x1) & (x4)) ^ ((x2) & (x5)) ^ ((x3) & (x6)) \ + ^ ((x0) & (x1) & (x2) & (x3)) ^ ((x0) & (x5)) ^ (x0)) + * + */ + +#define F5(x6, x5, x4, x3, x2, x1, x0) \ + (((x0) & ~(((x1) & (x2) & (x3)) ^ (x5))) \ + ^ ((x1) & (x4)) ^ ((x2) & (x5)) ^ ((x3) & (x6))) + +/* + * The macros below integrate the phi() permutations, depending on the + * pass and the total number of passes. + */ + +#define FP3_1(x6, x5, x4, x3, x2, x1, x0) \ + F1(x1, x0, x3, x5, x6, x2, x4) +#define FP3_2(x6, x5, x4, x3, x2, x1, x0) \ + F2(x4, x2, x1, x0, x5, x3, x6) +#define FP3_3(x6, x5, x4, x3, x2, x1, x0) \ + F3(x6, x1, x2, x3, x4, x5, x0) + +#define FP4_1(x6, x5, x4, x3, x2, x1, x0) \ + F1(x2, x6, x1, x4, x5, x3, x0) +#define FP4_2(x6, x5, x4, x3, x2, x1, x0) \ + F2(x3, x5, x2, x0, x1, x6, x4) +#define FP4_3(x6, x5, x4, x3, x2, x1, x0) \ + F3(x1, x4, x3, x6, x0, x2, x5) +#define FP4_4(x6, x5, x4, x3, x2, x1, x0) \ + F4(x6, x4, x0, x5, x2, x1, x3) + +#define FP5_1(x6, x5, x4, x3, x2, x1, x0) \ + F1(x3, x4, x1, x0, x5, x2, x6) +#define FP5_2(x6, x5, x4, x3, x2, x1, x0) \ + F2(x6, x2, x1, x0, x3, x4, x5) +#define FP5_3(x6, x5, x4, x3, x2, x1, x0) \ + F3(x2, x6, x0, x4, x3, x1, x5) +#define FP5_4(x6, x5, x4, x3, x2, x1, x0) \ + F4(x1, x5, x3, x2, x0, x4, x6) +#define FP5_5(x6, x5, x4, x3, x2, x1, x0) \ + F5(x2, x5, x0, x6, x4, x3, x1) + +/* + * One step, for "n" passes, pass number "p" (1 <= p <= n), using + * input word number "w" and step constant "c". + */ +#define STEP(n, p, x7, x6, x5, x4, x3, x2, x1, x0, w, c) do { \ + sph_u32 t = FP ## n ## _ ## p(x6, x5, x4, x3, x2, x1, x0); \ + (x7) = SPH_T32(SPH_ROTR32(t, 7) + SPH_ROTR32((x7), 11) \ + + (w) + (c)); \ + } while (0) + +/* + * PASSy(n, in) computes pass number "y", for a total of "n", using the + * one-argument macro "in" to access input words. Current state is assumed + * to be held in variables "s0" to "s7". + */ + +#if SPH_SMALL_FOOTPRINT_HAVAL + +#define PASS1(n, in) do { \ + unsigned pass_count; \ + for (pass_count = 0; pass_count < 32; pass_count += 8) { \ + STEP(n, 1, s7, s6, s5, s4, s3, s2, s1, s0, \ + in(pass_count + 0), SPH_C32(0x00000000)); \ + STEP(n, 1, s6, s5, s4, s3, s2, s1, s0, s7, \ + in(pass_count + 1), SPH_C32(0x00000000)); \ + STEP(n, 1, s5, s4, s3, s2, s1, s0, s7, s6, \ + in(pass_count + 2), SPH_C32(0x00000000)); \ + STEP(n, 1, s4, s3, s2, s1, s0, s7, s6, s5, \ + in(pass_count + 3), SPH_C32(0x00000000)); \ + STEP(n, 1, s3, s2, s1, s0, s7, s6, s5, s4, \ + in(pass_count + 4), SPH_C32(0x00000000)); \ + STEP(n, 1, s2, s1, s0, s7, s6, s5, s4, s3, \ + in(pass_count + 5), SPH_C32(0x00000000)); \ + STEP(n, 1, s1, s0, s7, s6, s5, s4, s3, s2, \ + in(pass_count + 6), SPH_C32(0x00000000)); \ + STEP(n, 1, s0, s7, s6, s5, s4, s3, s2, s1, \ + in(pass_count + 7), SPH_C32(0x00000000)); \ + } \ + } while (0) + +#define PASSG(p, n, in) do { \ + unsigned pass_count; \ + for (pass_count = 0; pass_count < 32; pass_count += 8) { \ + STEP(n, p, s7, s6, s5, s4, s3, s2, s1, s0, \ + in(MP ## p[pass_count + 0]), \ + RK ## p[pass_count + 0]); \ + STEP(n, p, s6, s5, s4, s3, s2, s1, s0, s7, \ + in(MP ## p[pass_count + 1]), \ + RK ## p[pass_count + 1]); \ + STEP(n, p, s5, s4, s3, s2, s1, s0, s7, s6, \ + in(MP ## p[pass_count + 2]), \ + RK ## p[pass_count + 2]); \ + STEP(n, p, s4, s3, s2, s1, s0, s7, s6, s5, \ + in(MP ## p[pass_count + 3]), \ + RK ## p[pass_count + 3]); \ + STEP(n, p, s3, s2, s1, s0, s7, s6, s5, s4, \ + in(MP ## p[pass_count + 4]), \ + RK ## p[pass_count + 4]); \ + STEP(n, p, s2, s1, s0, s7, s6, s5, s4, s3, \ + in(MP ## p[pass_count + 5]), \ + RK ## p[pass_count + 5]); \ + STEP(n, p, s1, s0, s7, s6, s5, s4, s3, s2, \ + in(MP ## p[pass_count + 6]), \ + RK ## p[pass_count + 6]); \ + STEP(n, p, s0, s7, s6, s5, s4, s3, s2, s1, \ + in(MP ## p[pass_count + 7]), \ + RK ## p[pass_count + 7]); \ + } \ + } while (0) + +#define PASS2(n, in) PASSG(2, n, in) +#define PASS3(n, in) PASSG(3, n, in) +#define PASS4(n, in) PASSG(4, n, in) +#define PASS5(n, in) PASSG(5, n, in) + +static const unsigned MP2[32] = { + 5, 14, 26, 18, 11, 28, 7, 16, + 0, 23, 20, 22, 1, 10, 4, 8, + 30, 3, 21, 9, 17, 24, 29, 6, + 19, 12, 15, 13, 2, 25, 31, 27 +}; + +static const unsigned MP3[32] = { + 19, 9, 4, 20, 28, 17, 8, 22, + 29, 14, 25, 12, 24, 30, 16, 26, + 31, 15, 7, 3, 1, 0, 18, 27, + 13, 6, 21, 10, 23, 11, 5, 2 +}; + +static const unsigned MP4[32] = { + 24, 4, 0, 14, 2, 7, 28, 23, + 26, 6, 30, 20, 18, 25, 19, 3, + 22, 11, 31, 21, 8, 27, 12, 9, + 1, 29, 5, 15, 17, 10, 16, 13 +}; + +static const unsigned MP5[32] = { + 27, 3, 21, 26, 17, 11, 20, 29, + 19, 0, 12, 7, 13, 8, 31, 10, + 5, 9, 14, 30, 18, 6, 28, 24, + 2, 23, 16, 22, 4, 1, 25, 15 +}; + +static const sph_u32 RK2[32] = { + SPH_C32(0x452821E6), SPH_C32(0x38D01377), + SPH_C32(0xBE5466CF), SPH_C32(0x34E90C6C), + SPH_C32(0xC0AC29B7), SPH_C32(0xC97C50DD), + SPH_C32(0x3F84D5B5), SPH_C32(0xB5470917), + SPH_C32(0x9216D5D9), SPH_C32(0x8979FB1B), + SPH_C32(0xD1310BA6), SPH_C32(0x98DFB5AC), + SPH_C32(0x2FFD72DB), SPH_C32(0xD01ADFB7), + SPH_C32(0xB8E1AFED), SPH_C32(0x6A267E96), + SPH_C32(0xBA7C9045), SPH_C32(0xF12C7F99), + SPH_C32(0x24A19947), SPH_C32(0xB3916CF7), + SPH_C32(0x0801F2E2), SPH_C32(0x858EFC16), + SPH_C32(0x636920D8), SPH_C32(0x71574E69), + SPH_C32(0xA458FEA3), SPH_C32(0xF4933D7E), + SPH_C32(0x0D95748F), SPH_C32(0x728EB658), + SPH_C32(0x718BCD58), SPH_C32(0x82154AEE), + SPH_C32(0x7B54A41D), SPH_C32(0xC25A59B5) +}; + +static const sph_u32 RK3[32] = { + SPH_C32(0x9C30D539), SPH_C32(0x2AF26013), + SPH_C32(0xC5D1B023), SPH_C32(0x286085F0), + SPH_C32(0xCA417918), SPH_C32(0xB8DB38EF), + SPH_C32(0x8E79DCB0), SPH_C32(0x603A180E), + SPH_C32(0x6C9E0E8B), SPH_C32(0xB01E8A3E), + SPH_C32(0xD71577C1), SPH_C32(0xBD314B27), + SPH_C32(0x78AF2FDA), SPH_C32(0x55605C60), + SPH_C32(0xE65525F3), SPH_C32(0xAA55AB94), + SPH_C32(0x57489862), SPH_C32(0x63E81440), + SPH_C32(0x55CA396A), SPH_C32(0x2AAB10B6), + SPH_C32(0xB4CC5C34), SPH_C32(0x1141E8CE), + SPH_C32(0xA15486AF), SPH_C32(0x7C72E993), + SPH_C32(0xB3EE1411), SPH_C32(0x636FBC2A), + SPH_C32(0x2BA9C55D), SPH_C32(0x741831F6), + SPH_C32(0xCE5C3E16), SPH_C32(0x9B87931E), + SPH_C32(0xAFD6BA33), SPH_C32(0x6C24CF5C) +}; + +static const sph_u32 RK4[32] = { + SPH_C32(0x7A325381), SPH_C32(0x28958677), + SPH_C32(0x3B8F4898), SPH_C32(0x6B4BB9AF), + SPH_C32(0xC4BFE81B), SPH_C32(0x66282193), + SPH_C32(0x61D809CC), SPH_C32(0xFB21A991), + SPH_C32(0x487CAC60), SPH_C32(0x5DEC8032), + SPH_C32(0xEF845D5D), SPH_C32(0xE98575B1), + SPH_C32(0xDC262302), SPH_C32(0xEB651B88), + SPH_C32(0x23893E81), SPH_C32(0xD396ACC5), + SPH_C32(0x0F6D6FF3), SPH_C32(0x83F44239), + SPH_C32(0x2E0B4482), SPH_C32(0xA4842004), + SPH_C32(0x69C8F04A), SPH_C32(0x9E1F9B5E), + SPH_C32(0x21C66842), SPH_C32(0xF6E96C9A), + SPH_C32(0x670C9C61), SPH_C32(0xABD388F0), + SPH_C32(0x6A51A0D2), SPH_C32(0xD8542F68), + SPH_C32(0x960FA728), SPH_C32(0xAB5133A3), + SPH_C32(0x6EEF0B6C), SPH_C32(0x137A3BE4) +}; + +static const sph_u32 RK5[32] = { + SPH_C32(0xBA3BF050), SPH_C32(0x7EFB2A98), + SPH_C32(0xA1F1651D), SPH_C32(0x39AF0176), + SPH_C32(0x66CA593E), SPH_C32(0x82430E88), + SPH_C32(0x8CEE8619), SPH_C32(0x456F9FB4), + SPH_C32(0x7D84A5C3), SPH_C32(0x3B8B5EBE), + SPH_C32(0xE06F75D8), SPH_C32(0x85C12073), + SPH_C32(0x401A449F), SPH_C32(0x56C16AA6), + SPH_C32(0x4ED3AA62), SPH_C32(0x363F7706), + SPH_C32(0x1BFEDF72), SPH_C32(0x429B023D), + SPH_C32(0x37D0D724), SPH_C32(0xD00A1248), + SPH_C32(0xDB0FEAD3), SPH_C32(0x49F1C09B), + SPH_C32(0x075372C9), SPH_C32(0x80991B7B), + SPH_C32(0x25D479D8), SPH_C32(0xF6E8DEF7), + SPH_C32(0xE3FE501A), SPH_C32(0xB6794C3B), + SPH_C32(0x976CE0BD), SPH_C32(0x04C006BA), + SPH_C32(0xC1A94FB6), SPH_C32(0x409F60C4) +}; + +#else + +#define PASS1(n, in) do { \ + STEP(n, 1, s7, s6, s5, s4, s3, s2, s1, s0, in( 0), SPH_C32(0x00000000)); \ + STEP(n, 1, s6, s5, s4, s3, s2, s1, s0, s7, in( 1), SPH_C32(0x00000000)); \ + STEP(n, 1, s5, s4, s3, s2, s1, s0, s7, s6, in( 2), SPH_C32(0x00000000)); \ + STEP(n, 1, s4, s3, s2, s1, s0, s7, s6, s5, in( 3), SPH_C32(0x00000000)); \ + STEP(n, 1, s3, s2, s1, s0, s7, s6, s5, s4, in( 4), SPH_C32(0x00000000)); \ + STEP(n, 1, s2, s1, s0, s7, s6, s5, s4, s3, in( 5), SPH_C32(0x00000000)); \ + STEP(n, 1, s1, s0, s7, s6, s5, s4, s3, s2, in( 6), SPH_C32(0x00000000)); \ + STEP(n, 1, s0, s7, s6, s5, s4, s3, s2, s1, in( 7), SPH_C32(0x00000000)); \ + \ + STEP(n, 1, s7, s6, s5, s4, s3, s2, s1, s0, in( 8), SPH_C32(0x00000000)); \ + STEP(n, 1, s6, s5, s4, s3, s2, s1, s0, s7, in( 9), SPH_C32(0x00000000)); \ + STEP(n, 1, s5, s4, s3, s2, s1, s0, s7, s6, in(10), SPH_C32(0x00000000)); \ + STEP(n, 1, s4, s3, s2, s1, s0, s7, s6, s5, in(11), SPH_C32(0x00000000)); \ + STEP(n, 1, s3, s2, s1, s0, s7, s6, s5, s4, in(12), SPH_C32(0x00000000)); \ + STEP(n, 1, s2, s1, s0, s7, s6, s5, s4, s3, in(13), SPH_C32(0x00000000)); \ + STEP(n, 1, s1, s0, s7, s6, s5, s4, s3, s2, in(14), SPH_C32(0x00000000)); \ + STEP(n, 1, s0, s7, s6, s5, s4, s3, s2, s1, in(15), SPH_C32(0x00000000)); \ + \ + STEP(n, 1, s7, s6, s5, s4, s3, s2, s1, s0, in(16), SPH_C32(0x00000000)); \ + STEP(n, 1, s6, s5, s4, s3, s2, s1, s0, s7, in(17), SPH_C32(0x00000000)); \ + STEP(n, 1, s5, s4, s3, s2, s1, s0, s7, s6, in(18), SPH_C32(0x00000000)); \ + STEP(n, 1, s4, s3, s2, s1, s0, s7, s6, s5, in(19), SPH_C32(0x00000000)); \ + STEP(n, 1, s3, s2, s1, s0, s7, s6, s5, s4, in(20), SPH_C32(0x00000000)); \ + STEP(n, 1, s2, s1, s0, s7, s6, s5, s4, s3, in(21), SPH_C32(0x00000000)); \ + STEP(n, 1, s1, s0, s7, s6, s5, s4, s3, s2, in(22), SPH_C32(0x00000000)); \ + STEP(n, 1, s0, s7, s6, s5, s4, s3, s2, s1, in(23), SPH_C32(0x00000000)); \ + \ + STEP(n, 1, s7, s6, s5, s4, s3, s2, s1, s0, in(24), SPH_C32(0x00000000)); \ + STEP(n, 1, s6, s5, s4, s3, s2, s1, s0, s7, in(25), SPH_C32(0x00000000)); \ + STEP(n, 1, s5, s4, s3, s2, s1, s0, s7, s6, in(26), SPH_C32(0x00000000)); \ + STEP(n, 1, s4, s3, s2, s1, s0, s7, s6, s5, in(27), SPH_C32(0x00000000)); \ + STEP(n, 1, s3, s2, s1, s0, s7, s6, s5, s4, in(28), SPH_C32(0x00000000)); \ + STEP(n, 1, s2, s1, s0, s7, s6, s5, s4, s3, in(29), SPH_C32(0x00000000)); \ + STEP(n, 1, s1, s0, s7, s6, s5, s4, s3, s2, in(30), SPH_C32(0x00000000)); \ + STEP(n, 1, s0, s7, s6, s5, s4, s3, s2, s1, in(31), SPH_C32(0x00000000)); \ + } while (0) + +#define PASS2(n, in) do { \ + STEP(n, 2, s7, s6, s5, s4, s3, s2, s1, s0, in( 5), SPH_C32(0x452821E6)); \ + STEP(n, 2, s6, s5, s4, s3, s2, s1, s0, s7, in(14), SPH_C32(0x38D01377)); \ + STEP(n, 2, s5, s4, s3, s2, s1, s0, s7, s6, in(26), SPH_C32(0xBE5466CF)); \ + STEP(n, 2, s4, s3, s2, s1, s0, s7, s6, s5, in(18), SPH_C32(0x34E90C6C)); \ + STEP(n, 2, s3, s2, s1, s0, s7, s6, s5, s4, in(11), SPH_C32(0xC0AC29B7)); \ + STEP(n, 2, s2, s1, s0, s7, s6, s5, s4, s3, in(28), SPH_C32(0xC97C50DD)); \ + STEP(n, 2, s1, s0, s7, s6, s5, s4, s3, s2, in( 7), SPH_C32(0x3F84D5B5)); \ + STEP(n, 2, s0, s7, s6, s5, s4, s3, s2, s1, in(16), SPH_C32(0xB5470917)); \ + \ + STEP(n, 2, s7, s6, s5, s4, s3, s2, s1, s0, in( 0), SPH_C32(0x9216D5D9)); \ + STEP(n, 2, s6, s5, s4, s3, s2, s1, s0, s7, in(23), SPH_C32(0x8979FB1B)); \ + STEP(n, 2, s5, s4, s3, s2, s1, s0, s7, s6, in(20), SPH_C32(0xD1310BA6)); \ + STEP(n, 2, s4, s3, s2, s1, s0, s7, s6, s5, in(22), SPH_C32(0x98DFB5AC)); \ + STEP(n, 2, s3, s2, s1, s0, s7, s6, s5, s4, in( 1), SPH_C32(0x2FFD72DB)); \ + STEP(n, 2, s2, s1, s0, s7, s6, s5, s4, s3, in(10), SPH_C32(0xD01ADFB7)); \ + STEP(n, 2, s1, s0, s7, s6, s5, s4, s3, s2, in( 4), SPH_C32(0xB8E1AFED)); \ + STEP(n, 2, s0, s7, s6, s5, s4, s3, s2, s1, in( 8), SPH_C32(0x6A267E96)); \ + \ + STEP(n, 2, s7, s6, s5, s4, s3, s2, s1, s0, in(30), SPH_C32(0xBA7C9045)); \ + STEP(n, 2, s6, s5, s4, s3, s2, s1, s0, s7, in( 3), SPH_C32(0xF12C7F99)); \ + STEP(n, 2, s5, s4, s3, s2, s1, s0, s7, s6, in(21), SPH_C32(0x24A19947)); \ + STEP(n, 2, s4, s3, s2, s1, s0, s7, s6, s5, in( 9), SPH_C32(0xB3916CF7)); \ + STEP(n, 2, s3, s2, s1, s0, s7, s6, s5, s4, in(17), SPH_C32(0x0801F2E2)); \ + STEP(n, 2, s2, s1, s0, s7, s6, s5, s4, s3, in(24), SPH_C32(0x858EFC16)); \ + STEP(n, 2, s1, s0, s7, s6, s5, s4, s3, s2, in(29), SPH_C32(0x636920D8)); \ + STEP(n, 2, s0, s7, s6, s5, s4, s3, s2, s1, in( 6), SPH_C32(0x71574E69)); \ + \ + STEP(n, 2, s7, s6, s5, s4, s3, s2, s1, s0, in(19), SPH_C32(0xA458FEA3)); \ + STEP(n, 2, s6, s5, s4, s3, s2, s1, s0, s7, in(12), SPH_C32(0xF4933D7E)); \ + STEP(n, 2, s5, s4, s3, s2, s1, s0, s7, s6, in(15), SPH_C32(0x0D95748F)); \ + STEP(n, 2, s4, s3, s2, s1, s0, s7, s6, s5, in(13), SPH_C32(0x728EB658)); \ + STEP(n, 2, s3, s2, s1, s0, s7, s6, s5, s4, in( 2), SPH_C32(0x718BCD58)); \ + STEP(n, 2, s2, s1, s0, s7, s6, s5, s4, s3, in(25), SPH_C32(0x82154AEE)); \ + STEP(n, 2, s1, s0, s7, s6, s5, s4, s3, s2, in(31), SPH_C32(0x7B54A41D)); \ + STEP(n, 2, s0, s7, s6, s5, s4, s3, s2, s1, in(27), SPH_C32(0xC25A59B5)); \ + } while (0) + +#define PASS3(n, in) do { \ + STEP(n, 3, s7, s6, s5, s4, s3, s2, s1, s0, in(19), SPH_C32(0x9C30D539)); \ + STEP(n, 3, s6, s5, s4, s3, s2, s1, s0, s7, in( 9), SPH_C32(0x2AF26013)); \ + STEP(n, 3, s5, s4, s3, s2, s1, s0, s7, s6, in( 4), SPH_C32(0xC5D1B023)); \ + STEP(n, 3, s4, s3, s2, s1, s0, s7, s6, s5, in(20), SPH_C32(0x286085F0)); \ + STEP(n, 3, s3, s2, s1, s0, s7, s6, s5, s4, in(28), SPH_C32(0xCA417918)); \ + STEP(n, 3, s2, s1, s0, s7, s6, s5, s4, s3, in(17), SPH_C32(0xB8DB38EF)); \ + STEP(n, 3, s1, s0, s7, s6, s5, s4, s3, s2, in( 8), SPH_C32(0x8E79DCB0)); \ + STEP(n, 3, s0, s7, s6, s5, s4, s3, s2, s1, in(22), SPH_C32(0x603A180E)); \ + \ + STEP(n, 3, s7, s6, s5, s4, s3, s2, s1, s0, in(29), SPH_C32(0x6C9E0E8B)); \ + STEP(n, 3, s6, s5, s4, s3, s2, s1, s0, s7, in(14), SPH_C32(0xB01E8A3E)); \ + STEP(n, 3, s5, s4, s3, s2, s1, s0, s7, s6, in(25), SPH_C32(0xD71577C1)); \ + STEP(n, 3, s4, s3, s2, s1, s0, s7, s6, s5, in(12), SPH_C32(0xBD314B27)); \ + STEP(n, 3, s3, s2, s1, s0, s7, s6, s5, s4, in(24), SPH_C32(0x78AF2FDA)); \ + STEP(n, 3, s2, s1, s0, s7, s6, s5, s4, s3, in(30), SPH_C32(0x55605C60)); \ + STEP(n, 3, s1, s0, s7, s6, s5, s4, s3, s2, in(16), SPH_C32(0xE65525F3)); \ + STEP(n, 3, s0, s7, s6, s5, s4, s3, s2, s1, in(26), SPH_C32(0xAA55AB94)); \ + \ + STEP(n, 3, s7, s6, s5, s4, s3, s2, s1, s0, in(31), SPH_C32(0x57489862)); \ + STEP(n, 3, s6, s5, s4, s3, s2, s1, s0, s7, in(15), SPH_C32(0x63E81440)); \ + STEP(n, 3, s5, s4, s3, s2, s1, s0, s7, s6, in( 7), SPH_C32(0x55CA396A)); \ + STEP(n, 3, s4, s3, s2, s1, s0, s7, s6, s5, in( 3), SPH_C32(0x2AAB10B6)); \ + STEP(n, 3, s3, s2, s1, s0, s7, s6, s5, s4, in( 1), SPH_C32(0xB4CC5C34)); \ + STEP(n, 3, s2, s1, s0, s7, s6, s5, s4, s3, in( 0), SPH_C32(0x1141E8CE)); \ + STEP(n, 3, s1, s0, s7, s6, s5, s4, s3, s2, in(18), SPH_C32(0xA15486AF)); \ + STEP(n, 3, s0, s7, s6, s5, s4, s3, s2, s1, in(27), SPH_C32(0x7C72E993)); \ + \ + STEP(n, 3, s7, s6, s5, s4, s3, s2, s1, s0, in(13), SPH_C32(0xB3EE1411)); \ + STEP(n, 3, s6, s5, s4, s3, s2, s1, s0, s7, in( 6), SPH_C32(0x636FBC2A)); \ + STEP(n, 3, s5, s4, s3, s2, s1, s0, s7, s6, in(21), SPH_C32(0x2BA9C55D)); \ + STEP(n, 3, s4, s3, s2, s1, s0, s7, s6, s5, in(10), SPH_C32(0x741831F6)); \ + STEP(n, 3, s3, s2, s1, s0, s7, s6, s5, s4, in(23), SPH_C32(0xCE5C3E16)); \ + STEP(n, 3, s2, s1, s0, s7, s6, s5, s4, s3, in(11), SPH_C32(0x9B87931E)); \ + STEP(n, 3, s1, s0, s7, s6, s5, s4, s3, s2, in( 5), SPH_C32(0xAFD6BA33)); \ + STEP(n, 3, s0, s7, s6, s5, s4, s3, s2, s1, in( 2), SPH_C32(0x6C24CF5C)); \ + } while (0) + +#define PASS4(n, in) do { \ + STEP(n, 4, s7, s6, s5, s4, s3, s2, s1, s0, in(24), SPH_C32(0x7A325381)); \ + STEP(n, 4, s6, s5, s4, s3, s2, s1, s0, s7, in( 4), SPH_C32(0x28958677)); \ + STEP(n, 4, s5, s4, s3, s2, s1, s0, s7, s6, in( 0), SPH_C32(0x3B8F4898)); \ + STEP(n, 4, s4, s3, s2, s1, s0, s7, s6, s5, in(14), SPH_C32(0x6B4BB9AF)); \ + STEP(n, 4, s3, s2, s1, s0, s7, s6, s5, s4, in( 2), SPH_C32(0xC4BFE81B)); \ + STEP(n, 4, s2, s1, s0, s7, s6, s5, s4, s3, in( 7), SPH_C32(0x66282193)); \ + STEP(n, 4, s1, s0, s7, s6, s5, s4, s3, s2, in(28), SPH_C32(0x61D809CC)); \ + STEP(n, 4, s0, s7, s6, s5, s4, s3, s2, s1, in(23), SPH_C32(0xFB21A991)); \ + \ + STEP(n, 4, s7, s6, s5, s4, s3, s2, s1, s0, in(26), SPH_C32(0x487CAC60)); \ + STEP(n, 4, s6, s5, s4, s3, s2, s1, s0, s7, in( 6), SPH_C32(0x5DEC8032)); \ + STEP(n, 4, s5, s4, s3, s2, s1, s0, s7, s6, in(30), SPH_C32(0xEF845D5D)); \ + STEP(n, 4, s4, s3, s2, s1, s0, s7, s6, s5, in(20), SPH_C32(0xE98575B1)); \ + STEP(n, 4, s3, s2, s1, s0, s7, s6, s5, s4, in(18), SPH_C32(0xDC262302)); \ + STEP(n, 4, s2, s1, s0, s7, s6, s5, s4, s3, in(25), SPH_C32(0xEB651B88)); \ + STEP(n, 4, s1, s0, s7, s6, s5, s4, s3, s2, in(19), SPH_C32(0x23893E81)); \ + STEP(n, 4, s0, s7, s6, s5, s4, s3, s2, s1, in( 3), SPH_C32(0xD396ACC5)); \ + \ + STEP(n, 4, s7, s6, s5, s4, s3, s2, s1, s0, in(22), SPH_C32(0x0F6D6FF3)); \ + STEP(n, 4, s6, s5, s4, s3, s2, s1, s0, s7, in(11), SPH_C32(0x83F44239)); \ + STEP(n, 4, s5, s4, s3, s2, s1, s0, s7, s6, in(31), SPH_C32(0x2E0B4482)); \ + STEP(n, 4, s4, s3, s2, s1, s0, s7, s6, s5, in(21), SPH_C32(0xA4842004)); \ + STEP(n, 4, s3, s2, s1, s0, s7, s6, s5, s4, in( 8), SPH_C32(0x69C8F04A)); \ + STEP(n, 4, s2, s1, s0, s7, s6, s5, s4, s3, in(27), SPH_C32(0x9E1F9B5E)); \ + STEP(n, 4, s1, s0, s7, s6, s5, s4, s3, s2, in(12), SPH_C32(0x21C66842)); \ + STEP(n, 4, s0, s7, s6, s5, s4, s3, s2, s1, in( 9), SPH_C32(0xF6E96C9A)); \ + \ + STEP(n, 4, s7, s6, s5, s4, s3, s2, s1, s0, in( 1), SPH_C32(0x670C9C61)); \ + STEP(n, 4, s6, s5, s4, s3, s2, s1, s0, s7, in(29), SPH_C32(0xABD388F0)); \ + STEP(n, 4, s5, s4, s3, s2, s1, s0, s7, s6, in( 5), SPH_C32(0x6A51A0D2)); \ + STEP(n, 4, s4, s3, s2, s1, s0, s7, s6, s5, in(15), SPH_C32(0xD8542F68)); \ + STEP(n, 4, s3, s2, s1, s0, s7, s6, s5, s4, in(17), SPH_C32(0x960FA728)); \ + STEP(n, 4, s2, s1, s0, s7, s6, s5, s4, s3, in(10), SPH_C32(0xAB5133A3)); \ + STEP(n, 4, s1, s0, s7, s6, s5, s4, s3, s2, in(16), SPH_C32(0x6EEF0B6C)); \ + STEP(n, 4, s0, s7, s6, s5, s4, s3, s2, s1, in(13), SPH_C32(0x137A3BE4)); \ + } while (0) + +#define PASS5(n, in) do { \ + STEP(n, 5, s7, s6, s5, s4, s3, s2, s1, s0, in(27), SPH_C32(0xBA3BF050)); \ + STEP(n, 5, s6, s5, s4, s3, s2, s1, s0, s7, in( 3), SPH_C32(0x7EFB2A98)); \ + STEP(n, 5, s5, s4, s3, s2, s1, s0, s7, s6, in(21), SPH_C32(0xA1F1651D)); \ + STEP(n, 5, s4, s3, s2, s1, s0, s7, s6, s5, in(26), SPH_C32(0x39AF0176)); \ + STEP(n, 5, s3, s2, s1, s0, s7, s6, s5, s4, in(17), SPH_C32(0x66CA593E)); \ + STEP(n, 5, s2, s1, s0, s7, s6, s5, s4, s3, in(11), SPH_C32(0x82430E88)); \ + STEP(n, 5, s1, s0, s7, s6, s5, s4, s3, s2, in(20), SPH_C32(0x8CEE8619)); \ + STEP(n, 5, s0, s7, s6, s5, s4, s3, s2, s1, in(29), SPH_C32(0x456F9FB4)); \ + \ + STEP(n, 5, s7, s6, s5, s4, s3, s2, s1, s0, in(19), SPH_C32(0x7D84A5C3)); \ + STEP(n, 5, s6, s5, s4, s3, s2, s1, s0, s7, in( 0), SPH_C32(0x3B8B5EBE)); \ + STEP(n, 5, s5, s4, s3, s2, s1, s0, s7, s6, in(12), SPH_C32(0xE06F75D8)); \ + STEP(n, 5, s4, s3, s2, s1, s0, s7, s6, s5, in( 7), SPH_C32(0x85C12073)); \ + STEP(n, 5, s3, s2, s1, s0, s7, s6, s5, s4, in(13), SPH_C32(0x401A449F)); \ + STEP(n, 5, s2, s1, s0, s7, s6, s5, s4, s3, in( 8), SPH_C32(0x56C16AA6)); \ + STEP(n, 5, s1, s0, s7, s6, s5, s4, s3, s2, in(31), SPH_C32(0x4ED3AA62)); \ + STEP(n, 5, s0, s7, s6, s5, s4, s3, s2, s1, in(10), SPH_C32(0x363F7706)); \ + \ + STEP(n, 5, s7, s6, s5, s4, s3, s2, s1, s0, in( 5), SPH_C32(0x1BFEDF72)); \ + STEP(n, 5, s6, s5, s4, s3, s2, s1, s0, s7, in( 9), SPH_C32(0x429B023D)); \ + STEP(n, 5, s5, s4, s3, s2, s1, s0, s7, s6, in(14), SPH_C32(0x37D0D724)); \ + STEP(n, 5, s4, s3, s2, s1, s0, s7, s6, s5, in(30), SPH_C32(0xD00A1248)); \ + STEP(n, 5, s3, s2, s1, s0, s7, s6, s5, s4, in(18), SPH_C32(0xDB0FEAD3)); \ + STEP(n, 5, s2, s1, s0, s7, s6, s5, s4, s3, in( 6), SPH_C32(0x49F1C09B)); \ + STEP(n, 5, s1, s0, s7, s6, s5, s4, s3, s2, in(28), SPH_C32(0x075372C9)); \ + STEP(n, 5, s0, s7, s6, s5, s4, s3, s2, s1, in(24), SPH_C32(0x80991B7B)); \ + \ + STEP(n, 5, s7, s6, s5, s4, s3, s2, s1, s0, in( 2), SPH_C32(0x25D479D8)); \ + STEP(n, 5, s6, s5, s4, s3, s2, s1, s0, s7, in(23), SPH_C32(0xF6E8DEF7)); \ + STEP(n, 5, s5, s4, s3, s2, s1, s0, s7, s6, in(16), SPH_C32(0xE3FE501A)); \ + STEP(n, 5, s4, s3, s2, s1, s0, s7, s6, s5, in(22), SPH_C32(0xB6794C3B)); \ + STEP(n, 5, s3, s2, s1, s0, s7, s6, s5, s4, in( 4), SPH_C32(0x976CE0BD)); \ + STEP(n, 5, s2, s1, s0, s7, s6, s5, s4, s3, in( 1), SPH_C32(0x04C006BA)); \ + STEP(n, 5, s1, s0, s7, s6, s5, s4, s3, s2, in(25), SPH_C32(0xC1A94FB6)); \ + STEP(n, 5, s0, s7, s6, s5, s4, s3, s2, s1, in(15), SPH_C32(0x409F60C4)); \ + } while (0) + +#endif + +#define SAVE_STATE \ + sph_u32 u0, u1, u2, u3, u4, u5, u6, u7; \ + do { \ + u0 = s0; \ + u1 = s1; \ + u2 = s2; \ + u3 = s3; \ + u4 = s4; \ + u5 = s5; \ + u6 = s6; \ + u7 = s7; \ + } while (0) + +#define UPDATE_STATE do { \ + s0 = SPH_T32(s0 + u0); \ + s1 = SPH_T32(s1 + u1); \ + s2 = SPH_T32(s2 + u2); \ + s3 = SPH_T32(s3 + u3); \ + s4 = SPH_T32(s4 + u4); \ + s5 = SPH_T32(s5 + u5); \ + s6 = SPH_T32(s6 + u6); \ + s7 = SPH_T32(s7 + u7); \ + } while (0) + +/* + * COREn(in) performs the core HAVAL computation for "n" passes, using + * the one-argument macro "in" to access the input words. Running state + * is held in variable "s0" to "s7". + */ + +#define CORE3(in) do { \ + SAVE_STATE; \ + PASS1(3, in); \ + PASS2(3, in); \ + PASS3(3, in); \ + UPDATE_STATE; \ + } while (0) + +#define CORE4(in) do { \ + SAVE_STATE; \ + PASS1(4, in); \ + PASS2(4, in); \ + PASS3(4, in); \ + PASS4(4, in); \ + UPDATE_STATE; \ + } while (0) + +#define CORE5(in) do { \ + SAVE_STATE; \ + PASS1(5, in); \ + PASS2(5, in); \ + PASS3(5, in); \ + PASS4(5, in); \ + PASS5(5, in); \ + UPDATE_STATE; \ + } while (0) + +/* + * DSTATE declares the state variables "s0" to "s7". + */ +#define DSTATE sph_u32 s0, s1, s2, s3, s4, s5, s6, s7 + +/* + * RSTATE fills the state variables from the context "sc". + */ +#define RSTATE do { \ + s0 = sc->s0; \ + s1 = sc->s1; \ + s2 = sc->s2; \ + s3 = sc->s3; \ + s4 = sc->s4; \ + s5 = sc->s5; \ + s6 = sc->s6; \ + s7 = sc->s7; \ + } while (0) + +/* + * WSTATE updates the context "sc" from the state variables. + */ +#define WSTATE do { \ + sc->s0 = s0; \ + sc->s1 = s1; \ + sc->s2 = s2; \ + sc->s3 = s3; \ + sc->s4 = s4; \ + sc->s5 = s5; \ + sc->s6 = s6; \ + sc->s7 = s7; \ + } while (0) + +/* + * Initialize a context. "olen" is the output length, in 32-bit words + * (between 4 and 8, inclusive). "passes" is the number of passes + * (3, 4 or 5). + */ +static void +haval_init(sph_haval_context *sc, unsigned olen, unsigned passes) +{ + sc->s0 = SPH_C32(0x243F6A88); + sc->s1 = SPH_C32(0x85A308D3); + sc->s2 = SPH_C32(0x13198A2E); + sc->s3 = SPH_C32(0x03707344); + sc->s4 = SPH_C32(0xA4093822); + sc->s5 = SPH_C32(0x299F31D0); + sc->s6 = SPH_C32(0x082EFA98); + sc->s7 = SPH_C32(0xEC4E6C89); + sc->olen = olen; + sc->passes = passes; +#if SPH_64 + sc->count = 0; +#else + sc->count_high = 0; + sc->count_low = 0; +#endif +} + +/* + * IN_PREPARE(data) contains declarations and code to prepare for + * reading input words pointed to by "data". + * INW(i) reads the word number "i" (from 0 to 31). + */ +#if SPH_LITTLE_FAST +#define IN_PREPARE(indata) const unsigned char *const load_ptr = \ + (const unsigned char *)(indata) +#define INW(i) sph_dec32le_aligned(load_ptr + 4 * (i)) +#else +#define IN_PREPARE(indata) \ + sph_u32 X_var[32]; \ + int load_index; \ + \ + for (load_index = 0; load_index < 32; load_index ++) \ + X_var[load_index] = sph_dec32le_aligned( \ + (const unsigned char *)(indata) + 4 * load_index) +#define INW(i) X_var[i] +#endif + +/* + * Mixing operation used for 128-bit output tailoring. This function + * takes the byte 0 from a0, byte 1 from a1, byte 2 from a2 and byte 3 + * from a3, and combines them into a 32-bit word, which is then rotated + * to the left by n bits. + */ +static SPH_INLINE sph_u32 +mix128(sph_u32 a0, sph_u32 a1, sph_u32 a2, sph_u32 a3, int n) +{ + sph_u32 tmp; + + tmp = (a0 & SPH_C32(0x000000FF)) + | (a1 & SPH_C32(0x0000FF00)) + | (a2 & SPH_C32(0x00FF0000)) + | (a3 & SPH_C32(0xFF000000)); + if (n > 0) + tmp = SPH_ROTL32(tmp, n); + return tmp; +} + +/* + * Mixing operation used to compute output word 0 for 160-bit output. + */ +static SPH_INLINE sph_u32 +mix160_0(sph_u32 x5, sph_u32 x6, sph_u32 x7) +{ + sph_u32 tmp; + + tmp = (x5 & SPH_C32(0x01F80000)) + | (x6 & SPH_C32(0xFE000000)) + | (x7 & SPH_C32(0x0000003F)); + return SPH_ROTL32(tmp, 13); +} + +/* + * Mixing operation used to compute output word 1 for 160-bit output. + */ +static SPH_INLINE sph_u32 +mix160_1(sph_u32 x5, sph_u32 x6, sph_u32 x7) +{ + sph_u32 tmp; + + tmp = (x5 & SPH_C32(0xFE000000)) + | (x6 & SPH_C32(0x0000003F)) + | (x7 & SPH_C32(0x00000FC0)); + return SPH_ROTL32(tmp, 7); +} + +/* + * Mixing operation used to compute output word 2 for 160-bit output. + */ +static SPH_INLINE sph_u32 +mix160_2(sph_u32 x5, sph_u32 x6, sph_u32 x7) +{ + sph_u32 tmp; + + tmp = (x5 & SPH_C32(0x0000003F)) + | (x6 & SPH_C32(0x00000FC0)) + | (x7 & SPH_C32(0x0007F000)); + return tmp; +} + +/* + * Mixing operation used to compute output word 3 for 160-bit output. + */ +static SPH_INLINE sph_u32 +mix160_3(sph_u32 x5, sph_u32 x6, sph_u32 x7) +{ + sph_u32 tmp; + + tmp = (x5 & SPH_C32(0x00000FC0)) + | (x6 & SPH_C32(0x0007F000)) + | (x7 & SPH_C32(0x01F80000)); + return tmp >> 6; +} + +/* + * Mixing operation used to compute output word 4 for 160-bit output. + */ +static SPH_INLINE sph_u32 +mix160_4(sph_u32 x5, sph_u32 x6, sph_u32 x7) +{ + sph_u32 tmp; + + tmp = (x5 & SPH_C32(0x0007F000)) + | (x6 & SPH_C32(0x01F80000)) + | (x7 & SPH_C32(0xFE000000)); + return tmp >> 12; +} + +/* + * Mixing operation used to compute output word 0 for 192-bit output. + */ +static SPH_INLINE sph_u32 +mix192_0(sph_u32 x6, sph_u32 x7) +{ + sph_u32 tmp; + + tmp = (x6 & SPH_C32(0xFC000000)) | (x7 & SPH_C32(0x0000001F)); + return SPH_ROTL32(tmp, 6); +} + +/* + * Mixing operation used to compute output word 1 for 192-bit output. + */ +static SPH_INLINE sph_u32 +mix192_1(sph_u32 x6, sph_u32 x7) +{ + return (x6 & SPH_C32(0x0000001F)) | (x7 & SPH_C32(0x000003E0)); +} + +/* + * Mixing operation used to compute output word 2 for 192-bit output. + */ +static SPH_INLINE sph_u32 +mix192_2(sph_u32 x6, sph_u32 x7) +{ + return ((x6 & SPH_C32(0x000003E0)) | (x7 & SPH_C32(0x0000FC00))) >> 5; +} + +/* + * Mixing operation used to compute output word 3 for 192-bit output. + */ +static SPH_INLINE sph_u32 +mix192_3(sph_u32 x6, sph_u32 x7) +{ + return ((x6 & SPH_C32(0x0000FC00)) | (x7 & SPH_C32(0x001F0000))) >> 10; +} + +/* + * Mixing operation used to compute output word 4 for 192-bit output. + */ +static SPH_INLINE sph_u32 +mix192_4(sph_u32 x6, sph_u32 x7) +{ + return ((x6 & SPH_C32(0x001F0000)) | (x7 & SPH_C32(0x03E00000))) >> 16; +} + +/* + * Mixing operation used to compute output word 5 for 192-bit output. + */ +static SPH_INLINE sph_u32 +mix192_5(sph_u32 x6, sph_u32 x7) +{ + return ((x6 & SPH_C32(0x03E00000)) | (x7 & SPH_C32(0xFC000000))) >> 21; +} + +/* + * Write out HAVAL output. The output length is tailored to the requested + * length. + */ +static void +haval_out(sph_haval_context *sc, void *dst) +{ + DSTATE; + unsigned char *buf; + + buf = (unsigned char*)dst; + RSTATE; + switch (sc->olen) { + case 4: + sph_enc32le(buf, SPH_T32(s0 + mix128(s7, s4, s5, s6, 24))); + sph_enc32le(buf + 4, SPH_T32(s1 + mix128(s6, s7, s4, s5, 16))); + sph_enc32le(buf + 8, SPH_T32(s2 + mix128(s5, s6, s7, s4, 8))); + sph_enc32le(buf + 12, SPH_T32(s3 + mix128(s4, s5, s6, s7, 0))); + break; + case 5: + sph_enc32le(buf, SPH_T32(s0 + mix160_0(s5, s6, s7))); + sph_enc32le(buf + 4, SPH_T32(s1 + mix160_1(s5, s6, s7))); + sph_enc32le(buf + 8, SPH_T32(s2 + mix160_2(s5, s6, s7))); + sph_enc32le(buf + 12, SPH_T32(s3 + mix160_3(s5, s6, s7))); + sph_enc32le(buf + 16, SPH_T32(s4 + mix160_4(s5, s6, s7))); + break; + case 6: + sph_enc32le(buf, SPH_T32(s0 + mix192_0(s6, s7))); + sph_enc32le(buf + 4, SPH_T32(s1 + mix192_1(s6, s7))); + sph_enc32le(buf + 8, SPH_T32(s2 + mix192_2(s6, s7))); + sph_enc32le(buf + 12, SPH_T32(s3 + mix192_3(s6, s7))); + sph_enc32le(buf + 16, SPH_T32(s4 + mix192_4(s6, s7))); + sph_enc32le(buf + 20, SPH_T32(s5 + mix192_5(s6, s7))); + break; + case 7: + sph_enc32le(buf, SPH_T32(s0 + ((s7 >> 27) & 0x1F))); + sph_enc32le(buf + 4, SPH_T32(s1 + ((s7 >> 22) & 0x1F))); + sph_enc32le(buf + 8, SPH_T32(s2 + ((s7 >> 18) & 0x0F))); + sph_enc32le(buf + 12, SPH_T32(s3 + ((s7 >> 13) & 0x1F))); + sph_enc32le(buf + 16, SPH_T32(s4 + ((s7 >> 9) & 0x0F))); + sph_enc32le(buf + 20, SPH_T32(s5 + ((s7 >> 4) & 0x1F))); + sph_enc32le(buf + 24, SPH_T32(s6 + ((s7 ) & 0x0F))); + break; + case 8: + sph_enc32le(buf, s0); + sph_enc32le(buf + 4, s1); + sph_enc32le(buf + 8, s2); + sph_enc32le(buf + 12, s3); + sph_enc32le(buf + 16, s4); + sph_enc32le(buf + 20, s5); + sph_enc32le(buf + 24, s6); + sph_enc32le(buf + 28, s7); + break; + } +} + +/* + * The main core functions inline the code with the COREx() macros. We + * use a helper file, included three times, which avoids code copying. + */ + +#undef PASSES +#define PASSES 3 +#include "haval_helper.c" + +#undef PASSES +#define PASSES 4 +#include "haval_helper.c" + +#undef PASSES +#define PASSES 5 +#include "haval_helper.c" + +/* ====================================================================== */ + +#define API(xxx, y) \ +void \ +sph_haval ## xxx ## _ ## y ## _init(void *cc) \ +{ \ + haval_init((sph_haval_context*)cc, xxx >> 5, y); \ +} \ + \ +void \ +sph_haval ## xxx ## _ ## y (void *cc, const void *data, size_t len) \ +{ \ + haval ## y((sph_haval_context*)cc, data, len); \ +} \ + \ +void \ +sph_haval ## xxx ## _ ## y ## _close(void *cc, void *dst) \ +{ \ + haval ## y ## _close((sph_haval_context*)cc, 0, 0, dst); \ +} \ + \ +void \ +sph_haval ## xxx ## _ ## y ## addbits_and_close( \ + void *cc, unsigned ub, unsigned n, void *dst) \ +{ \ + haval ## y ## _close((sph_haval_context*)cc, ub, n, dst); \ +} + +API(128, 3) +API(128, 4) +API(128, 5) +API(160, 3) +API(160, 4) +API(160, 5) +API(192, 3) +API(192, 4) +API(192, 5) +API(224, 3) +API(224, 4) +API(224, 5) +API(256, 3) +API(256, 4) +API(256, 5) + +#define RVAL do { \ + s0 = val[0]; \ + s1 = val[1]; \ + s2 = val[2]; \ + s3 = val[3]; \ + s4 = val[4]; \ + s5 = val[5]; \ + s6 = val[6]; \ + s7 = val[7]; \ + } while (0) + +#define WVAL do { \ + val[0] = s0; \ + val[1] = s1; \ + val[2] = s2; \ + val[3] = s3; \ + val[4] = s4; \ + val[5] = s5; \ + val[6] = s6; \ + val[7] = s7; \ + } while (0) + +#define INMSG(i) msg[i] + +/* see sph_haval.h */ +void +sph_haval_3_comp(const sph_u32 msg[32], sph_u32 val[8]) +{ + DSTATE; + + RVAL; + CORE3(INMSG); + WVAL; +} + +/* see sph_haval.h */ +void +sph_haval_4_comp(const sph_u32 msg[32], sph_u32 val[8]) +{ + DSTATE; + + RVAL; + CORE4(INMSG); + WVAL; +} + +/* see sph_haval.h */ +void +sph_haval_5_comp(const sph_u32 msg[32], sph_u32 val[8]) +{ + DSTATE; + + RVAL; + CORE5(INMSG); + WVAL; +} + diff --git a/src/Native/libmultihash/sha3/sph_haval.h b/src/Native/libmultihash/sha3/sph_haval.h new file mode 100644 index 000000000..6334a9226 --- /dev/null +++ b/src/Native/libmultihash/sha3/sph_haval.h @@ -0,0 +1,969 @@ +/* $Id: sph_haval.h 218 2010-06-08 17:06:34Z tp $ */ +/** +* HAVAL interface. +* +* HAVAL is actually a family of 15 hash functions, depending on whether +* the internal computation uses 3, 4 or 5 passes, and on the output +* length, which is 128, 160, 192, 224 or 256 bits. This implementation +* provides interface functions for all 15, which internally map to +* three cores (depending on the number of passes). Note that output +* lengths other than 256 bits are not obtained by a simple truncation +* of a longer result; the requested length is encoded within the +* padding data. +* +* HAVAL was published in: Yuliang Zheng, Josef Pieprzyk and Jennifer +* Seberry: "HAVAL -- a one-way hashing algorithm with variable length +* of output", Advances in Cryptology -- AUSCRYPT'92, Lecture Notes in +* Computer Science, Vol.718, pp.83-104, Springer-Verlag, 1993. +* +* This paper, and a reference implementation, are available on the +* Calyptix web site: http://labs.calyptix.com/haval.php +* +* The HAVAL reference paper is quite unclear on the data encoding +* details, i.e. endianness (both byte order within a 32-bit word, and +* word order within a message block). This implementation has been +* made compatible with the reference implementation referenced above. +* +* @warning A collision for HAVAL-128/3 (HAVAL with three passes and +* 128-bit output) has been published; this function is thus considered +* as cryptographically broken. The status for other variants is unclear; +* use only with care. +* +* ==========================(LICENSE BEGIN)============================ +* +* Copyright (c) 2007-2010 Projet RNRT SAPHIR +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +* ===========================(LICENSE END)============================= +* +* @file sph_haval.h +* @author Thomas Pornin +*/ + +#ifndef SPH_HAVAL_H__ +#define SPH_HAVAL_H__ + +#include +#include "sph_types.h" + +/** +* Output size (in bits) for HAVAL-128/3. +*/ +#define SPH_SIZE_haval128_3 128 + +/** +* Output size (in bits) for HAVAL-128/4. +*/ +#define SPH_SIZE_haval128_4 128 + +/** +* Output size (in bits) for HAVAL-128/5. +*/ +#define SPH_SIZE_haval128_5 128 + +/** +* Output size (in bits) for HAVAL-160/3. +*/ +#define SPH_SIZE_haval160_3 160 + +/** +* Output size (in bits) for HAVAL-160/4. +*/ +#define SPH_SIZE_haval160_4 160 + +/** +* Output size (in bits) for HAVAL-160/5. +*/ +#define SPH_SIZE_haval160_5 160 + +/** +* Output size (in bits) for HAVAL-192/3. +*/ +#define SPH_SIZE_haval192_3 192 + +/** +* Output size (in bits) for HAVAL-192/4. +*/ +#define SPH_SIZE_haval192_4 192 + +/** +* Output size (in bits) for HAVAL-192/5. +*/ +#define SPH_SIZE_haval192_5 192 + +/** +* Output size (in bits) for HAVAL-224/3. +*/ +#define SPH_SIZE_haval224_3 224 + +/** +* Output size (in bits) for HAVAL-224/4. +*/ +#define SPH_SIZE_haval224_4 224 + +/** +* Output size (in bits) for HAVAL-224/5. +*/ +#define SPH_SIZE_haval224_5 224 + +/** +* Output size (in bits) for HAVAL-256/3. +*/ +#define SPH_SIZE_haval256_3 256 + +/** +* Output size (in bits) for HAVAL-256/4. +*/ +#define SPH_SIZE_haval256_4 256 + +/** +* Output size (in bits) for HAVAL-256/5. +*/ +#define SPH_SIZE_haval256_5 256 + +/** +* This structure is a context for HAVAL computations: it contains the +* intermediate values and some data from the last entered block. Once +* a HAVAL computation has been performed, the context can be reused for +* another computation. +* +* The contents of this structure are private. A running HAVAL computation +* can be cloned by copying the context (e.g. with a simple +* memcpy()). +*/ +typedef struct { +#ifndef DOXYGEN_IGNORE +unsigned char buf[128]; /* first field, for alignment */ +sph_u32 s0, s1, s2, s3, s4, s5, s6, s7; +unsigned olen, passes; +#if SPH_64 +sph_u64 count; +#else +sph_u32 count_high, count_low; +#endif +#endif +} sph_haval_context; + +/** +* Type for a HAVAL-128/3 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval128_3_context; + +/** +* Type for a HAVAL-128/4 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval128_4_context; + +/** +* Type for a HAVAL-128/5 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval128_5_context; + +/** +* Type for a HAVAL-160/3 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval160_3_context; + +/** +* Type for a HAVAL-160/4 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval160_4_context; + +/** +* Type for a HAVAL-160/5 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval160_5_context; + +/** +* Type for a HAVAL-192/3 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval192_3_context; + +/** +* Type for a HAVAL-192/4 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval192_4_context; + +/** +* Type for a HAVAL-192/5 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval192_5_context; + +/** +* Type for a HAVAL-224/3 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval224_3_context; + +/** +* Type for a HAVAL-224/4 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval224_4_context; + +/** +* Type for a HAVAL-224/5 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval224_5_context; + +/** +* Type for a HAVAL-256/3 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval256_3_context; + +/** +* Type for a HAVAL-256/4 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval256_4_context; + +/** +* Type for a HAVAL-256/5 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval256_5_context; + +/** +* Initialize the context for HAVAL-128/3. +* +* @param cc context to initialize (pointer to a +* sph_haval128_3_context structure) +*/ +void sph_haval128_3_init(void *cc); + +/** +* Process some data bytes for HAVAL-128/3. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-128/3 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval128_3(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-128/3 computation. The output buffer must be wide +* enough to accomodate the result (16 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-128/3 context +* @param dst the output buffer +*/ +void sph_haval128_3_close(void *cc, void *dst); + +/** +* Close a HAVAL-128/3 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (16 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-128/3 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval128_3_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-128/4. +* +* @param cc context to initialize (pointer to a +* sph_haval128_4_context structure) +*/ +void sph_haval128_4_init(void *cc); + +/** +* Process some data bytes for HAVAL-128/4. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-128/4 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval128_4(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-128/4 computation. The output buffer must be wide +* enough to accomodate the result (16 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-128/4 context +* @param dst the output buffer +*/ +void sph_haval128_4_close(void *cc, void *dst); + +/** +* Close a HAVAL-128/4 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (16 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-128/4 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval128_4_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-128/5. +* +* @param cc context to initialize (pointer to a +* sph_haval128_5_context structure) +*/ +void sph_haval128_5_init(void *cc); + +/** +* Process some data bytes for HAVAL-128/5. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-128/5 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval128_5(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-128/5 computation. The output buffer must be wide +* enough to accomodate the result (16 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-128/5 context +* @param dst the output buffer +*/ +void sph_haval128_5_close(void *cc, void *dst); + +/** +* Close a HAVAL-128/5 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (16 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-128/5 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval128_5_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-160/3. +* +* @param cc context to initialize (pointer to a +* sph_haval160_3_context structure) +*/ +void sph_haval160_3_init(void *cc); + +/** +* Process some data bytes for HAVAL-160/3. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-160/3 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval160_3(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-160/3 computation. The output buffer must be wide +* enough to accomodate the result (20 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-160/3 context +* @param dst the output buffer +*/ +void sph_haval160_3_close(void *cc, void *dst); + +/** +* Close a HAVAL-160/3 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (20 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-160/3 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval160_3_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-160/4. +* +* @param cc context to initialize (pointer to a +* sph_haval160_4_context structure) +*/ +void sph_haval160_4_init(void *cc); + +/** +* Process some data bytes for HAVAL-160/4. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-160/4 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval160_4(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-160/4 computation. The output buffer must be wide +* enough to accomodate the result (20 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-160/4 context +* @param dst the output buffer +*/ +void sph_haval160_4_close(void *cc, void *dst); + +/** +* Close a HAVAL-160/4 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (20 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-160/4 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval160_3_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-160/5. +* +* @param cc context to initialize (pointer to a +* sph_haval160_5_context structure) +*/ +void sph_haval160_5_init(void *cc); + +/** +* Process some data bytes for HAVAL-160/5. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-160/5 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval160_5(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-160/5 computation. The output buffer must be wide +* enough to accomodate the result (20 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-160/5 context +* @param dst the output buffer +*/ +void sph_haval160_5_close(void *cc, void *dst); + +/** +* Close a HAVAL-160/5 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (20 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-160/5 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval160_5_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-192/3. +* +* @param cc context to initialize (pointer to a +* sph_haval192_3_context structure) +*/ +void sph_haval192_3_init(void *cc); + +/** +* Process some data bytes for HAVAL-192/3. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-192/3 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval192_3(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-192/3 computation. The output buffer must be wide +* enough to accomodate the result (24 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-192/3 context +* @param dst the output buffer +*/ +void sph_haval192_3_close(void *cc, void *dst); + +/** +* Close a HAVAL-192/3 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (24 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-192/3 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval192_3_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-192/4. +* +* @param cc context to initialize (pointer to a +* sph_haval192_4_context structure) +*/ +void sph_haval192_4_init(void *cc); + +/** +* Process some data bytes for HAVAL-192/4. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-192/4 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval192_4(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-192/4 computation. The output buffer must be wide +* enough to accomodate the result (24 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-192/4 context +* @param dst the output buffer +*/ +void sph_haval192_4_close(void *cc, void *dst); + +/** +* Close a HAVAL-192/4 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (24 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-192/4 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval192_4_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-192/5. +* +* @param cc context to initialize (pointer to a +* sph_haval192_5_context structure) +*/ +void sph_haval192_5_init(void *cc); + +/** +* Process some data bytes for HAVAL-192/5. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-192/5 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval192_5(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-192/5 computation. The output buffer must be wide +* enough to accomodate the result (24 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-192/5 context +* @param dst the output buffer +*/ +void sph_haval192_5_close(void *cc, void *dst); + +/** +* Close a HAVAL-192/5 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (24 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-192/5 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval192_5_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-224/3. +* +* @param cc context to initialize (pointer to a +* sph_haval224_3_context structure) +*/ +void sph_haval224_3_init(void *cc); + +/** +* Process some data bytes for HAVAL-224/3. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-224/3 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval224_3(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-224/3 computation. The output buffer must be wide +* enough to accomodate the result (28 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-224/3 context +* @param dst the output buffer +*/ +void sph_haval224_3_close(void *cc, void *dst); + +/** +* Close a HAVAL-224/3 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (28 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-224/3 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval224_3_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-224/4. +* +* @param cc context to initialize (pointer to a +* sph_haval224_4_context structure) +*/ +void sph_haval224_4_init(void *cc); + +/** +* Process some data bytes for HAVAL-224/4. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-224/4 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval224_4(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-224/4 computation. The output buffer must be wide +* enough to accomodate the result (28 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-224/4 context +* @param dst the output buffer +*/ +void sph_haval224_4_close(void *cc, void *dst); + +/** +* Close a HAVAL-224/4 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (28 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-224/4 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval224_4_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-224/5. +* +* @param cc context to initialize (pointer to a +* sph_haval224_5_context structure) +*/ +void sph_haval224_5_init(void *cc); + +/** +* Process some data bytes for HAVAL-224/5. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-224/5 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval224_5(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-224/5 computation. The output buffer must be wide +* enough to accomodate the result (28 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-224/5 context +* @param dst the output buffer +*/ +void sph_haval224_5_close(void *cc, void *dst); + +/** +* Close a HAVAL-224/5 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (28 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-224/5 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval224_5_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-256/3. +* +* @param cc context to initialize (pointer to a +* sph_haval256_3_context structure) +*/ +void sph_haval256_3_init(void *cc); + +/** +* Process some data bytes for HAVAL-256/3. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-256/3 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval256_3(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-256/3 computation. The output buffer must be wide +* enough to accomodate the result (32 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-256/3 context +* @param dst the output buffer +*/ +void sph_haval256_3_close(void *cc, void *dst); + +/** +* Close a HAVAL-256/3 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (32 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-256/3 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval256_3_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-256/4. +* +* @param cc context to initialize (pointer to a +* sph_haval256_4_context structure) +*/ +void sph_haval256_4_init(void *cc); + +/** +* Process some data bytes for HAVAL-256/4. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-256/4 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval256_4(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-256/4 computation. The output buffer must be wide +* enough to accomodate the result (32 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-256/4 context +* @param dst the output buffer +*/ +void sph_haval256_4_close(void *cc, void *dst); + +/** +* Close a HAVAL-256/4 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (32 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-256/4 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval256_4_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-256/5. +* +* @param cc context to initialize (pointer to a +* sph_haval256_5_context structure) +*/ +void sph_haval256_5_init(void *cc); + +/** +* Process some data bytes for HAVAL-256/5. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-256/5 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval256_5(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-256/5 computation. The output buffer must be wide +* enough to accomodate the result (32 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-256/5 context +* @param dst the output buffer +*/ +void sph_haval256_5_close(void *cc, void *dst); + +/** +* Close a HAVAL-256/5 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (32 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-256/5 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval256_5_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Apply the HAVAL compression function on the provided data. The +* msg parameter contains the 32 32-bit input blocks, +* as numerical values (hence after the little-endian decoding). The +* val parameter contains the 8 32-bit input blocks for +* the compression function; the output is written in place in this +* array. This function uses three internal passes. +* +* @param msg the message block (32 values) +* @param val the function 256-bit input and output +*/ +void sph_haval_3_comp(const sph_u32 msg[32], sph_u32 val[8]); + +/** +* Apply the HAVAL compression function on the provided data. The +* msg parameter contains the 32 32-bit input blocks, +* as numerical values (hence after the little-endian decoding). The +* val parameter contains the 8 32-bit input blocks for +* the compression function; the output is written in place in this +* array. This function uses four internal passes. +* +* @param msg the message block (32 values) +* @param val the function 256-bit input and output +*/ +void sph_haval_4_comp(const sph_u32 msg[32], sph_u32 val[8]); + +/** +* Apply the HAVAL compression function on the provided data. The +* msg parameter contains the 32 32-bit input blocks, +* as numerical values (hence after the little-endian decoding). The +* val parameter contains the 8 32-bit input blocks for +* the compression function; the output is written in place in this +* array. This function uses five internal passes. +* +* @param msg the message block (32 values) +* @param val the function 256-bit input and output +*/ +void sph_haval_5_comp(const sph_u32 msg[32], sph_u32 val[8]); + +#endif diff --git a/src/Native/libmultihash/sha3/sph_sha2.c b/src/Native/libmultihash/sha3/sph_sha2.c new file mode 100644 index 000000000..2c8de457a --- /dev/null +++ b/src/Native/libmultihash/sha3/sph_sha2.c @@ -0,0 +1,691 @@ +/* $Id: sha2.c 227 2010-06-16 17:28:38Z tp $ */ +/* + * SHA-224 / SHA-256 implementation. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @author Thomas Pornin + */ + +#include +#include + +#include "sph_sha2.h" + +#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_SHA2 +#define SPH_SMALL_FOOTPRINT_SHA2 1 +#endif + +#define CH(X, Y, Z) ((((Y) ^ (Z)) & (X)) ^ (Z)) +#define MAJ(X, Y, Z) (((Y) & (Z)) | (((Y) | (Z)) & (X))) + +#define ROTR SPH_ROTR32 + +#define BSG2_0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define BSG2_1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define SSG2_0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SPH_T32((x) >> 3)) +#define SSG2_1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SPH_T32((x) >> 10)) + +static const sph_u32 H224[8] = { + SPH_C32(0xC1059ED8), SPH_C32(0x367CD507), SPH_C32(0x3070DD17), + SPH_C32(0xF70E5939), SPH_C32(0xFFC00B31), SPH_C32(0x68581511), + SPH_C32(0x64F98FA7), SPH_C32(0xBEFA4FA4) +}; + +static const sph_u32 H256[8] = { + SPH_C32(0x6A09E667), SPH_C32(0xBB67AE85), SPH_C32(0x3C6EF372), + SPH_C32(0xA54FF53A), SPH_C32(0x510E527F), SPH_C32(0x9B05688C), + SPH_C32(0x1F83D9AB), SPH_C32(0x5BE0CD19) +}; + +/* + * The SHA2_ROUND_BODY defines the body for a SHA-224 / SHA-256 + * compression function implementation. The "in" parameter should + * evaluate, when applied to a numerical input parameter from 0 to 15, + * to an expression which yields the corresponding input block. The "r" + * parameter should evaluate to an array or pointer expression + * designating the array of 8 words which contains the input and output + * of the compression function. + */ + +#if SPH_SMALL_FOOTPRINT_SHA2 + +static const sph_u32 K[64] = { + SPH_C32(0x428A2F98), SPH_C32(0x71374491), + SPH_C32(0xB5C0FBCF), SPH_C32(0xE9B5DBA5), + SPH_C32(0x3956C25B), SPH_C32(0x59F111F1), + SPH_C32(0x923F82A4), SPH_C32(0xAB1C5ED5), + SPH_C32(0xD807AA98), SPH_C32(0x12835B01), + SPH_C32(0x243185BE), SPH_C32(0x550C7DC3), + SPH_C32(0x72BE5D74), SPH_C32(0x80DEB1FE), + SPH_C32(0x9BDC06A7), SPH_C32(0xC19BF174), + SPH_C32(0xE49B69C1), SPH_C32(0xEFBE4786), + SPH_C32(0x0FC19DC6), SPH_C32(0x240CA1CC), + SPH_C32(0x2DE92C6F), SPH_C32(0x4A7484AA), + SPH_C32(0x5CB0A9DC), SPH_C32(0x76F988DA), + SPH_C32(0x983E5152), SPH_C32(0xA831C66D), + SPH_C32(0xB00327C8), SPH_C32(0xBF597FC7), + SPH_C32(0xC6E00BF3), SPH_C32(0xD5A79147), + SPH_C32(0x06CA6351), SPH_C32(0x14292967), + SPH_C32(0x27B70A85), SPH_C32(0x2E1B2138), + SPH_C32(0x4D2C6DFC), SPH_C32(0x53380D13), + SPH_C32(0x650A7354), SPH_C32(0x766A0ABB), + SPH_C32(0x81C2C92E), SPH_C32(0x92722C85), + SPH_C32(0xA2BFE8A1), SPH_C32(0xA81A664B), + SPH_C32(0xC24B8B70), SPH_C32(0xC76C51A3), + SPH_C32(0xD192E819), SPH_C32(0xD6990624), + SPH_C32(0xF40E3585), SPH_C32(0x106AA070), + SPH_C32(0x19A4C116), SPH_C32(0x1E376C08), + SPH_C32(0x2748774C), SPH_C32(0x34B0BCB5), + SPH_C32(0x391C0CB3), SPH_C32(0x4ED8AA4A), + SPH_C32(0x5B9CCA4F), SPH_C32(0x682E6FF3), + SPH_C32(0x748F82EE), SPH_C32(0x78A5636F), + SPH_C32(0x84C87814), SPH_C32(0x8CC70208), + SPH_C32(0x90BEFFFA), SPH_C32(0xA4506CEB), + SPH_C32(0xBEF9A3F7), SPH_C32(0xC67178F2) +}; + +#define SHA2_MEXP1(in, pc) do { \ + W[pc] = in(pc); \ + } while (0) + +#define SHA2_MEXP2(in, pc) do { \ + W[(pc) & 0x0F] = SPH_T32(SSG2_1(W[((pc) - 2) & 0x0F]) \ + + W[((pc) - 7) & 0x0F] \ + + SSG2_0(W[((pc) - 15) & 0x0F]) + W[(pc) & 0x0F]); \ + } while (0) + +#define SHA2_STEPn(n, a, b, c, d, e, f, g, h, in, pc) do { \ + sph_u32 t1, t2; \ + SHA2_MEXP ## n(in, pc); \ + t1 = SPH_T32(h + BSG2_1(e) + CH(e, f, g) \ + + K[pcount + (pc)] + W[(pc) & 0x0F]); \ + t2 = SPH_T32(BSG2_0(a) + MAJ(a, b, c)); \ + d = SPH_T32(d + t1); \ + h = SPH_T32(t1 + t2); \ + } while (0) + +#define SHA2_STEP1(a, b, c, d, e, f, g, h, in, pc) \ + SHA2_STEPn(1, a, b, c, d, e, f, g, h, in, pc) +#define SHA2_STEP2(a, b, c, d, e, f, g, h, in, pc) \ + SHA2_STEPn(2, a, b, c, d, e, f, g, h, in, pc) + +#define SHA2_ROUND_BODY(in, r) do { \ + sph_u32 A, B, C, D, E, F, G, H; \ + sph_u32 W[16]; \ + unsigned pcount; \ + \ + A = (r)[0]; \ + B = (r)[1]; \ + C = (r)[2]; \ + D = (r)[3]; \ + E = (r)[4]; \ + F = (r)[5]; \ + G = (r)[6]; \ + H = (r)[7]; \ + pcount = 0; \ + SHA2_STEP1(A, B, C, D, E, F, G, H, in, 0); \ + SHA2_STEP1(H, A, B, C, D, E, F, G, in, 1); \ + SHA2_STEP1(G, H, A, B, C, D, E, F, in, 2); \ + SHA2_STEP1(F, G, H, A, B, C, D, E, in, 3); \ + SHA2_STEP1(E, F, G, H, A, B, C, D, in, 4); \ + SHA2_STEP1(D, E, F, G, H, A, B, C, in, 5); \ + SHA2_STEP1(C, D, E, F, G, H, A, B, in, 6); \ + SHA2_STEP1(B, C, D, E, F, G, H, A, in, 7); \ + SHA2_STEP1(A, B, C, D, E, F, G, H, in, 8); \ + SHA2_STEP1(H, A, B, C, D, E, F, G, in, 9); \ + SHA2_STEP1(G, H, A, B, C, D, E, F, in, 10); \ + SHA2_STEP1(F, G, H, A, B, C, D, E, in, 11); \ + SHA2_STEP1(E, F, G, H, A, B, C, D, in, 12); \ + SHA2_STEP1(D, E, F, G, H, A, B, C, in, 13); \ + SHA2_STEP1(C, D, E, F, G, H, A, B, in, 14); \ + SHA2_STEP1(B, C, D, E, F, G, H, A, in, 15); \ + for (pcount = 16; pcount < 64; pcount += 16) { \ + SHA2_STEP2(A, B, C, D, E, F, G, H, in, 0); \ + SHA2_STEP2(H, A, B, C, D, E, F, G, in, 1); \ + SHA2_STEP2(G, H, A, B, C, D, E, F, in, 2); \ + SHA2_STEP2(F, G, H, A, B, C, D, E, in, 3); \ + SHA2_STEP2(E, F, G, H, A, B, C, D, in, 4); \ + SHA2_STEP2(D, E, F, G, H, A, B, C, in, 5); \ + SHA2_STEP2(C, D, E, F, G, H, A, B, in, 6); \ + SHA2_STEP2(B, C, D, E, F, G, H, A, in, 7); \ + SHA2_STEP2(A, B, C, D, E, F, G, H, in, 8); \ + SHA2_STEP2(H, A, B, C, D, E, F, G, in, 9); \ + SHA2_STEP2(G, H, A, B, C, D, E, F, in, 10); \ + SHA2_STEP2(F, G, H, A, B, C, D, E, in, 11); \ + SHA2_STEP2(E, F, G, H, A, B, C, D, in, 12); \ + SHA2_STEP2(D, E, F, G, H, A, B, C, in, 13); \ + SHA2_STEP2(C, D, E, F, G, H, A, B, in, 14); \ + SHA2_STEP2(B, C, D, E, F, G, H, A, in, 15); \ + } \ + (r)[0] = SPH_T32((r)[0] + A); \ + (r)[1] = SPH_T32((r)[1] + B); \ + (r)[2] = SPH_T32((r)[2] + C); \ + (r)[3] = SPH_T32((r)[3] + D); \ + (r)[4] = SPH_T32((r)[4] + E); \ + (r)[5] = SPH_T32((r)[5] + F); \ + (r)[6] = SPH_T32((r)[6] + G); \ + (r)[7] = SPH_T32((r)[7] + H); \ + } while (0) + +#else + +#define SHA2_ROUND_BODY(in, r) do { \ + sph_u32 A, B, C, D, E, F, G, H, T1, T2; \ + sph_u32 W00, W01, W02, W03, W04, W05, W06, W07; \ + sph_u32 W08, W09, W10, W11, W12, W13, W14, W15; \ + \ + A = (r)[0]; \ + B = (r)[1]; \ + C = (r)[2]; \ + D = (r)[3]; \ + E = (r)[4]; \ + F = (r)[5]; \ + G = (r)[6]; \ + H = (r)[7]; \ + W00 = in(0); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0x428A2F98) + W00); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W01 = in(1); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0x71374491) + W01); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W02 = in(2); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0xB5C0FBCF) + W02); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W03 = in(3); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0xE9B5DBA5) + W03); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W04 = in(4); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x3956C25B) + W04); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W05 = in(5); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0x59F111F1) + W05); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W06 = in(6); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x923F82A4) + W06); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W07 = in(7); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0xAB1C5ED5) + W07); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W08 = in(8); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0xD807AA98) + W08); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W09 = in(9); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0x12835B01) + W09); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W10 = in(10); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0x243185BE) + W10); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W11 = in(11); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0x550C7DC3) + W11); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W12 = in(12); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x72BE5D74) + W12); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W13 = in(13); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0x80DEB1FE) + W13); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W14 = in(14); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x9BDC06A7) + W14); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W15 = in(15); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0xC19BF174) + W15); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W00 = SPH_T32(SSG2_1(W14) + W09 + SSG2_0(W01) + W00); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0xE49B69C1) + W00); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W01 = SPH_T32(SSG2_1(W15) + W10 + SSG2_0(W02) + W01); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0xEFBE4786) + W01); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W02 = SPH_T32(SSG2_1(W00) + W11 + SSG2_0(W03) + W02); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0x0FC19DC6) + W02); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W03 = SPH_T32(SSG2_1(W01) + W12 + SSG2_0(W04) + W03); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0x240CA1CC) + W03); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W04 = SPH_T32(SSG2_1(W02) + W13 + SSG2_0(W05) + W04); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x2DE92C6F) + W04); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W05 = SPH_T32(SSG2_1(W03) + W14 + SSG2_0(W06) + W05); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0x4A7484AA) + W05); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W06 = SPH_T32(SSG2_1(W04) + W15 + SSG2_0(W07) + W06); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x5CB0A9DC) + W06); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W07 = SPH_T32(SSG2_1(W05) + W00 + SSG2_0(W08) + W07); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0x76F988DA) + W07); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W08 = SPH_T32(SSG2_1(W06) + W01 + SSG2_0(W09) + W08); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0x983E5152) + W08); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W09 = SPH_T32(SSG2_1(W07) + W02 + SSG2_0(W10) + W09); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0xA831C66D) + W09); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W10 = SPH_T32(SSG2_1(W08) + W03 + SSG2_0(W11) + W10); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0xB00327C8) + W10); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W11 = SPH_T32(SSG2_1(W09) + W04 + SSG2_0(W12) + W11); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0xBF597FC7) + W11); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W12 = SPH_T32(SSG2_1(W10) + W05 + SSG2_0(W13) + W12); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0xC6E00BF3) + W12); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W13 = SPH_T32(SSG2_1(W11) + W06 + SSG2_0(W14) + W13); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0xD5A79147) + W13); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W14 = SPH_T32(SSG2_1(W12) + W07 + SSG2_0(W15) + W14); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x06CA6351) + W14); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W15 = SPH_T32(SSG2_1(W13) + W08 + SSG2_0(W00) + W15); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0x14292967) + W15); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W00 = SPH_T32(SSG2_1(W14) + W09 + SSG2_0(W01) + W00); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0x27B70A85) + W00); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W01 = SPH_T32(SSG2_1(W15) + W10 + SSG2_0(W02) + W01); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0x2E1B2138) + W01); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W02 = SPH_T32(SSG2_1(W00) + W11 + SSG2_0(W03) + W02); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0x4D2C6DFC) + W02); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W03 = SPH_T32(SSG2_1(W01) + W12 + SSG2_0(W04) + W03); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0x53380D13) + W03); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W04 = SPH_T32(SSG2_1(W02) + W13 + SSG2_0(W05) + W04); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x650A7354) + W04); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W05 = SPH_T32(SSG2_1(W03) + W14 + SSG2_0(W06) + W05); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0x766A0ABB) + W05); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W06 = SPH_T32(SSG2_1(W04) + W15 + SSG2_0(W07) + W06); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x81C2C92E) + W06); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W07 = SPH_T32(SSG2_1(W05) + W00 + SSG2_0(W08) + W07); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0x92722C85) + W07); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W08 = SPH_T32(SSG2_1(W06) + W01 + SSG2_0(W09) + W08); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0xA2BFE8A1) + W08); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W09 = SPH_T32(SSG2_1(W07) + W02 + SSG2_0(W10) + W09); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0xA81A664B) + W09); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W10 = SPH_T32(SSG2_1(W08) + W03 + SSG2_0(W11) + W10); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0xC24B8B70) + W10); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W11 = SPH_T32(SSG2_1(W09) + W04 + SSG2_0(W12) + W11); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0xC76C51A3) + W11); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W12 = SPH_T32(SSG2_1(W10) + W05 + SSG2_0(W13) + W12); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0xD192E819) + W12); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W13 = SPH_T32(SSG2_1(W11) + W06 + SSG2_0(W14) + W13); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0xD6990624) + W13); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W14 = SPH_T32(SSG2_1(W12) + W07 + SSG2_0(W15) + W14); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0xF40E3585) + W14); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W15 = SPH_T32(SSG2_1(W13) + W08 + SSG2_0(W00) + W15); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0x106AA070) + W15); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W00 = SPH_T32(SSG2_1(W14) + W09 + SSG2_0(W01) + W00); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0x19A4C116) + W00); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W01 = SPH_T32(SSG2_1(W15) + W10 + SSG2_0(W02) + W01); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0x1E376C08) + W01); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W02 = SPH_T32(SSG2_1(W00) + W11 + SSG2_0(W03) + W02); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0x2748774C) + W02); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W03 = SPH_T32(SSG2_1(W01) + W12 + SSG2_0(W04) + W03); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0x34B0BCB5) + W03); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W04 = SPH_T32(SSG2_1(W02) + W13 + SSG2_0(W05) + W04); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x391C0CB3) + W04); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W05 = SPH_T32(SSG2_1(W03) + W14 + SSG2_0(W06) + W05); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0x4ED8AA4A) + W05); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W06 = SPH_T32(SSG2_1(W04) + W15 + SSG2_0(W07) + W06); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x5B9CCA4F) + W06); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W07 = SPH_T32(SSG2_1(W05) + W00 + SSG2_0(W08) + W07); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0x682E6FF3) + W07); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W08 = SPH_T32(SSG2_1(W06) + W01 + SSG2_0(W09) + W08); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0x748F82EE) + W08); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W09 = SPH_T32(SSG2_1(W07) + W02 + SSG2_0(W10) + W09); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0x78A5636F) + W09); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W10 = SPH_T32(SSG2_1(W08) + W03 + SSG2_0(W11) + W10); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0x84C87814) + W10); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W11 = SPH_T32(SSG2_1(W09) + W04 + SSG2_0(W12) + W11); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0x8CC70208) + W11); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W12 = SPH_T32(SSG2_1(W10) + W05 + SSG2_0(W13) + W12); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x90BEFFFA) + W12); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W13 = SPH_T32(SSG2_1(W11) + W06 + SSG2_0(W14) + W13); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0xA4506CEB) + W13); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W14 = SPH_T32(SSG2_1(W12) + W07 + SSG2_0(W15) + W14); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0xBEF9A3F7) + W14); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W15 = SPH_T32(SSG2_1(W13) + W08 + SSG2_0(W00) + W15); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0xC67178F2) + W15); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + (r)[0] = SPH_T32((r)[0] + A); \ + (r)[1] = SPH_T32((r)[1] + B); \ + (r)[2] = SPH_T32((r)[2] + C); \ + (r)[3] = SPH_T32((r)[3] + D); \ + (r)[4] = SPH_T32((r)[4] + E); \ + (r)[5] = SPH_T32((r)[5] + F); \ + (r)[6] = SPH_T32((r)[6] + G); \ + (r)[7] = SPH_T32((r)[7] + H); \ + } while (0) + +#endif + +/* + * One round of SHA-224 / SHA-256. The data must be aligned for 32-bit access. + */ +static void +sha2_round(const unsigned char *data, sph_u32 r[8]) +{ +#define SHA2_IN(x) sph_dec32be_aligned(data + (4 * (x))) + SHA2_ROUND_BODY(SHA2_IN, r); +#undef SHA2_IN +} + +/* see sph_sha2.h */ +void +sph_sha224_init(void *cc) +{ + sph_sha224_context *sc; + + sc = (sph_sha224_context*)cc; + memcpy(sc->val, H224, sizeof H224); +#if SPH_64 + sc->count = 0; +#else + sc->count_high = sc->count_low = 0; +#endif +} + +/* see sph_sha2.h */ +void +sph_sha256_init(void *cc) +{ + sph_sha256_context *sc; + + sc = (sph_sha224_context*)cc; + memcpy(sc->val, H256, sizeof H256); +#if SPH_64 + sc->count = 0; +#else + sc->count_high = sc->count_low = 0; +#endif +} + +#define RFUN sha2_round +#define HASH sha224 +#define BE32 1 +#include "md_helper.c" + +/* see sph_sha2.h */ +void +sph_sha224_close(void *cc, void *dst) +{ + sha224_close(cc, dst, 7); + sph_sha224_init(cc); +} + +/* see sph_sha2.h */ +void +sph_sha224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + sha224_addbits_and_close(cc, ub, n, dst, 7); + sph_sha224_init(cc); +} + +/* see sph_sha2.h */ +void +sph_sha256_close(void *cc, void *dst) +{ + sha224_close(cc, dst, 8); + sph_sha256_init(cc); +} + +/* see sph_sha2.h */ +void +sph_sha256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + sha224_addbits_and_close(cc, ub, n, dst, 8); + sph_sha256_init(cc); +} + +/* see sph_sha2.h */ +void +sph_sha224_comp(const sph_u32 msg[16], sph_u32 val[8]) +{ +#define SHA2_IN(x) msg[x] + SHA2_ROUND_BODY(SHA2_IN, val); +#undef SHA2_IN +} + diff --git a/src/Native/libmultihash/sha3/sph_sha2.h b/src/Native/libmultihash/sha3/sph_sha2.h new file mode 100644 index 000000000..4b957c2ae --- /dev/null +++ b/src/Native/libmultihash/sha3/sph_sha2.h @@ -0,0 +1,371 @@ +/* $Id: sph_sha2.h 216 2010-06-08 09:46:57Z tp $ */ +/** + * SHA-224, SHA-256, SHA-384 and SHA-512 interface. + * + * SHA-256 has been published in FIPS 180-2, now amended with a change + * notice to include SHA-224 as well (which is a simple variation on + * SHA-256). SHA-384 and SHA-512 are also defined in FIPS 180-2. FIPS + * standards can be found at: + * http://csrc.nist.gov/publications/fips/ + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @file sph_sha2.h + * @author Thomas Pornin + */ + +#ifndef SPH_SHA2_H__ +#define SPH_SHA2_H__ + +#include +#include "sph_types.h" + +/** + * Output size (in bits) for SHA-224. + */ +#define SPH_SIZE_sha224 224 + +/** + * Output size (in bits) for SHA-256. + */ +#define SPH_SIZE_sha256 256 + +/** + * This structure is a context for SHA-224 computations: it contains the + * intermediate values and some data from the last entered block. Once + * a SHA-224 computation has been performed, the context can be reused for + * another computation. + * + * The contents of this structure are private. A running SHA-224 computation + * can be cloned by copying the context (e.g. with a simple + * memcpy()). + */ +typedef struct { +#ifndef DOXYGEN_IGNORE + unsigned char buf[64]; /* first field, for alignment */ + sph_u32 val[8]; +#if SPH_64 + sph_u64 count; +#else + sph_u32 count_high, count_low; +#endif +#endif +} sph_sha224_context; + +/** + * This structure is a context for SHA-256 computations. It is identical + * to the SHA-224 context. However, a context is initialized for SHA-224 + * or SHA-256, but not both (the internal IV is not the + * same). + */ +typedef sph_sha224_context sph_sha256_context; + +/** + * Initialize a SHA-224 context. This process performs no memory allocation. + * + * @param cc the SHA-224 context (pointer to + * a sph_sha224_context) + */ +void sph_sha224_init(void *cc); + +/** + * Process some data bytes. It is acceptable that len is zero + * (in which case this function does nothing). + * + * @param cc the SHA-224 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void sph_sha224(void *cc, const void *data, size_t len); + +/** + * Terminate the current SHA-224 computation and output the result into the + * provided buffer. The destination buffer must be wide enough to + * accomodate the result (28 bytes). The context is automatically + * reinitialized. + * + * @param cc the SHA-224 context + * @param dst the destination buffer + */ +void sph_sha224_close(void *cc, void *dst); + +/** + * Add a few additional bits (0 to 7) to the current computation, then + * terminate it and output the result in the provided buffer, which must + * be wide enough to accomodate the result (28 bytes). If bit number i + * in ub has value 2^i, then the extra bits are those + * numbered 7 downto 8-n (this is the big-endian convention at the byte + * level). The context is automatically reinitialized. + * + * @param cc the SHA-224 context + * @param ub the extra bits + * @param n the number of extra bits (0 to 7) + * @param dst the destination buffer + */ +void sph_sha224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst); + +/** + * Apply the SHA-224 compression function on the provided data. The + * msg parameter contains the 16 32-bit input blocks, + * as numerical values (hence after the big-endian decoding). The + * val parameter contains the 8 32-bit input blocks for + * the compression function; the output is written in place in this + * array. + * + * @param msg the message block (16 values) + * @param val the function 256-bit input and output + */ +void sph_sha224_comp(const sph_u32 msg[16], sph_u32 val[8]); + +/** + * Initialize a SHA-256 context. This process performs no memory allocation. + * + * @param cc the SHA-256 context (pointer to + * a sph_sha256_context) + */ +void sph_sha256_init(void *cc); + +#ifdef DOXYGEN_IGNORE +/** + * Process some data bytes, for SHA-256. This function is identical to + * sha_224() + * + * @param cc the SHA-224 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void sph_sha256(void *cc, const void *data, size_t len); +#endif + +#ifndef DOXYGEN_IGNORE +#define sph_sha256 sph_sha224 +#endif + +/** + * Terminate the current SHA-256 computation and output the result into the + * provided buffer. The destination buffer must be wide enough to + * accomodate the result (32 bytes). The context is automatically + * reinitialized. + * + * @param cc the SHA-256 context + * @param dst the destination buffer + */ +void sph_sha256_close(void *cc, void *dst); + +/** + * Add a few additional bits (0 to 7) to the current computation, then + * terminate it and output the result in the provided buffer, which must + * be wide enough to accomodate the result (32 bytes). If bit number i + * in ub has value 2^i, then the extra bits are those + * numbered 7 downto 8-n (this is the big-endian convention at the byte + * level). The context is automatically reinitialized. + * + * @param cc the SHA-256 context + * @param ub the extra bits + * @param n the number of extra bits (0 to 7) + * @param dst the destination buffer + */ +void sph_sha256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst); + +#ifdef DOXYGEN_IGNORE +/** + * Apply the SHA-256 compression function on the provided data. This + * function is identical to sha224_comp(). + * + * @param msg the message block (16 values) + * @param val the function 256-bit input and output + */ +void sph_sha256_comp(const sph_u32 msg[16], sph_u32 val[8]); +#endif + +#ifndef DOXYGEN_IGNORE +#define sph_sha256_comp sph_sha224_comp +#endif + +#if SPH_64 + +/** + * Output size (in bits) for SHA-384. + */ +#define SPH_SIZE_sha384 384 + +/** + * Output size (in bits) for SHA-512. + */ +#define SPH_SIZE_sha512 512 + +/** + * This structure is a context for SHA-384 computations: it contains the + * intermediate values and some data from the last entered block. Once + * a SHA-384 computation has been performed, the context can be reused for + * another computation. + * + * The contents of this structure are private. A running SHA-384 computation + * can be cloned by copying the context (e.g. with a simple + * memcpy()). + */ +typedef struct { +#ifndef DOXYGEN_IGNORE + unsigned char buf[128]; /* first field, for alignment */ + sph_u64 val[8]; + sph_u64 count; +#endif +} sph_sha384_context; + +/** + * Initialize a SHA-384 context. This process performs no memory allocation. + * + * @param cc the SHA-384 context (pointer to + * a sph_sha384_context) + */ +void sph_sha384_init(void *cc); + +/** + * Process some data bytes. It is acceptable that len is zero + * (in which case this function does nothing). + * + * @param cc the SHA-384 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void sph_sha384(void *cc, const void *data, size_t len); + +/** + * Terminate the current SHA-384 computation and output the result into the + * provided buffer. The destination buffer must be wide enough to + * accomodate the result (48 bytes). The context is automatically + * reinitialized. + * + * @param cc the SHA-384 context + * @param dst the destination buffer + */ +void sph_sha384_close(void *cc, void *dst); + +/** + * Add a few additional bits (0 to 7) to the current computation, then + * terminate it and output the result in the provided buffer, which must + * be wide enough to accomodate the result (48 bytes). If bit number i + * in ub has value 2^i, then the extra bits are those + * numbered 7 downto 8-n (this is the big-endian convention at the byte + * level). The context is automatically reinitialized. + * + * @param cc the SHA-384 context + * @param ub the extra bits + * @param n the number of extra bits (0 to 7) + * @param dst the destination buffer + */ +void sph_sha384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst); + +/** + * Apply the SHA-384 compression function on the provided data. The + * msg parameter contains the 16 64-bit input blocks, + * as numerical values (hence after the big-endian decoding). The + * val parameter contains the 8 64-bit input blocks for + * the compression function; the output is written in place in this + * array. + * + * @param msg the message block (16 values) + * @param val the function 512-bit input and output + */ +void sph_sha384_comp(const sph_u64 msg[16], sph_u64 val[8]); + +/** + * This structure is a context for SHA-512 computations. It is identical + * to the SHA-384 context. However, a context is initialized for SHA-384 + * or SHA-512, but not both (the internal IV is not the + * same). + */ +typedef sph_sha384_context sph_sha512_context; + +/** + * Initialize a SHA-512 context. This process performs no memory allocation. + * + * @param cc the SHA-512 context (pointer to + * a sph_sha512_context) + */ +void sph_sha512_init(void *cc); + +#ifdef DOXYGEN_IGNORE +/** + * Process some data bytes, for SHA-512. This function is identical to + * sph_sha384(). + * + * @param cc the SHA-384 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void sph_sha512(void *cc, const void *data, size_t len); +#endif + +#ifndef DOXYGEN_IGNORE +#define sph_sha512 sph_sha384 +#endif + +/** + * Terminate the current SHA-512 computation and output the result into the + * provided buffer. The destination buffer must be wide enough to + * accomodate the result (64 bytes). The context is automatically + * reinitialized. + * + * @param cc the SHA-512 context + * @param dst the destination buffer + */ +void sph_sha512_close(void *cc, void *dst); + +/** + * Add a few additional bits (0 to 7) to the current computation, then + * terminate it and output the result in the provided buffer, which must + * be wide enough to accomodate the result (64 bytes). If bit number i + * in ub has value 2^i, then the extra bits are those + * numbered 7 downto 8-n (this is the big-endian convention at the byte + * level). The context is automatically reinitialized. + * + * @param cc the SHA-512 context + * @param ub the extra bits + * @param n the number of extra bits (0 to 7) + * @param dst the destination buffer + */ +void sph_sha512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst); + +#ifdef DOXYGEN_IGNORE +/** + * Apply the SHA-512 compression function. This function is identical to + * sph_sha384_comp(). + * + * @param msg the message block (16 values) + * @param val the function 512-bit input and output + */ +void sph_sha512_comp(const sph_u64 msg[16], sph_u64 val[8]); +#endif + +#ifndef DOXYGEN_IGNORE +#define sph_sha512_comp sph_sha384_comp +#endif + +#endif + +#endif + diff --git a/src/Native/libmultihash/sha3/sph_sha2big.c b/src/Native/libmultihash/sha3/sph_sha2big.c new file mode 100644 index 000000000..be97eb986 --- /dev/null +++ b/src/Native/libmultihash/sha3/sph_sha2big.c @@ -0,0 +1,248 @@ +/* $Id: sha2big.c 216 2010-06-08 09:46:57Z tp $ */ +/* + * SHA-384 / SHA-512 implementation. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @author Thomas Pornin + */ + +#include +#include + +#include "sph_sha2.h" + +#if SPH_64 + +#define CH(X, Y, Z) ((((Y) ^ (Z)) & (X)) ^ (Z)) +#define MAJ(X, Y, Z) (((X) & (Y)) | (((X) | (Y)) & (Z))) + +#define ROTR64 SPH_ROTR64 + +#define BSG5_0(x) (ROTR64(x, 28) ^ ROTR64(x, 34) ^ ROTR64(x, 39)) +#define BSG5_1(x) (ROTR64(x, 14) ^ ROTR64(x, 18) ^ ROTR64(x, 41)) +#define SSG5_0(x) (ROTR64(x, 1) ^ ROTR64(x, 8) ^ SPH_T64((x) >> 7)) +#define SSG5_1(x) (ROTR64(x, 19) ^ ROTR64(x, 61) ^ SPH_T64((x) >> 6)) + +static const sph_u64 K512[80] = { + SPH_C64(0x428A2F98D728AE22), SPH_C64(0x7137449123EF65CD), + SPH_C64(0xB5C0FBCFEC4D3B2F), SPH_C64(0xE9B5DBA58189DBBC), + SPH_C64(0x3956C25BF348B538), SPH_C64(0x59F111F1B605D019), + SPH_C64(0x923F82A4AF194F9B), SPH_C64(0xAB1C5ED5DA6D8118), + SPH_C64(0xD807AA98A3030242), SPH_C64(0x12835B0145706FBE), + SPH_C64(0x243185BE4EE4B28C), SPH_C64(0x550C7DC3D5FFB4E2), + SPH_C64(0x72BE5D74F27B896F), SPH_C64(0x80DEB1FE3B1696B1), + SPH_C64(0x9BDC06A725C71235), SPH_C64(0xC19BF174CF692694), + SPH_C64(0xE49B69C19EF14AD2), SPH_C64(0xEFBE4786384F25E3), + SPH_C64(0x0FC19DC68B8CD5B5), SPH_C64(0x240CA1CC77AC9C65), + SPH_C64(0x2DE92C6F592B0275), SPH_C64(0x4A7484AA6EA6E483), + SPH_C64(0x5CB0A9DCBD41FBD4), SPH_C64(0x76F988DA831153B5), + SPH_C64(0x983E5152EE66DFAB), SPH_C64(0xA831C66D2DB43210), + SPH_C64(0xB00327C898FB213F), SPH_C64(0xBF597FC7BEEF0EE4), + SPH_C64(0xC6E00BF33DA88FC2), SPH_C64(0xD5A79147930AA725), + SPH_C64(0x06CA6351E003826F), SPH_C64(0x142929670A0E6E70), + SPH_C64(0x27B70A8546D22FFC), SPH_C64(0x2E1B21385C26C926), + SPH_C64(0x4D2C6DFC5AC42AED), SPH_C64(0x53380D139D95B3DF), + SPH_C64(0x650A73548BAF63DE), SPH_C64(0x766A0ABB3C77B2A8), + SPH_C64(0x81C2C92E47EDAEE6), SPH_C64(0x92722C851482353B), + SPH_C64(0xA2BFE8A14CF10364), SPH_C64(0xA81A664BBC423001), + SPH_C64(0xC24B8B70D0F89791), SPH_C64(0xC76C51A30654BE30), + SPH_C64(0xD192E819D6EF5218), SPH_C64(0xD69906245565A910), + SPH_C64(0xF40E35855771202A), SPH_C64(0x106AA07032BBD1B8), + SPH_C64(0x19A4C116B8D2D0C8), SPH_C64(0x1E376C085141AB53), + SPH_C64(0x2748774CDF8EEB99), SPH_C64(0x34B0BCB5E19B48A8), + SPH_C64(0x391C0CB3C5C95A63), SPH_C64(0x4ED8AA4AE3418ACB), + SPH_C64(0x5B9CCA4F7763E373), SPH_C64(0x682E6FF3D6B2B8A3), + SPH_C64(0x748F82EE5DEFB2FC), SPH_C64(0x78A5636F43172F60), + SPH_C64(0x84C87814A1F0AB72), SPH_C64(0x8CC702081A6439EC), + SPH_C64(0x90BEFFFA23631E28), SPH_C64(0xA4506CEBDE82BDE9), + SPH_C64(0xBEF9A3F7B2C67915), SPH_C64(0xC67178F2E372532B), + SPH_C64(0xCA273ECEEA26619C), SPH_C64(0xD186B8C721C0C207), + SPH_C64(0xEADA7DD6CDE0EB1E), SPH_C64(0xF57D4F7FEE6ED178), + SPH_C64(0x06F067AA72176FBA), SPH_C64(0x0A637DC5A2C898A6), + SPH_C64(0x113F9804BEF90DAE), SPH_C64(0x1B710B35131C471B), + SPH_C64(0x28DB77F523047D84), SPH_C64(0x32CAAB7B40C72493), + SPH_C64(0x3C9EBE0A15C9BEBC), SPH_C64(0x431D67C49C100D4C), + SPH_C64(0x4CC5D4BECB3E42B6), SPH_C64(0x597F299CFC657E2A), + SPH_C64(0x5FCB6FAB3AD6FAEC), SPH_C64(0x6C44198C4A475817) +}; + +static const sph_u64 H384[8] = { + SPH_C64(0xCBBB9D5DC1059ED8), SPH_C64(0x629A292A367CD507), + SPH_C64(0x9159015A3070DD17), SPH_C64(0x152FECD8F70E5939), + SPH_C64(0x67332667FFC00B31), SPH_C64(0x8EB44A8768581511), + SPH_C64(0xDB0C2E0D64F98FA7), SPH_C64(0x47B5481DBEFA4FA4) +}; + +static const sph_u64 H512[8] = { + SPH_C64(0x6A09E667F3BCC908), SPH_C64(0xBB67AE8584CAA73B), + SPH_C64(0x3C6EF372FE94F82B), SPH_C64(0xA54FF53A5F1D36F1), + SPH_C64(0x510E527FADE682D1), SPH_C64(0x9B05688C2B3E6C1F), + SPH_C64(0x1F83D9ABFB41BD6B), SPH_C64(0x5BE0CD19137E2179) +}; + +/* + * This macro defines the body for a SHA-384 / SHA-512 compression function + * implementation. The "in" parameter should evaluate, when applied to a + * numerical input parameter from 0 to 15, to an expression which yields + * the corresponding input block. The "r" parameter should evaluate to + * an array or pointer expression designating the array of 8 words which + * contains the input and output of the compression function. + * + * SHA-512 is hard for the compiler. If the loop is completely unrolled, + * then the code will be quite huge (possibly more than 100 kB), and the + * performance will be degraded due to cache misses on the code. We + * unroll only eight steps, which avoids all needless copies when + * 64-bit registers are swapped. + */ + +#define SHA3_STEP(A, B, C, D, E, F, G, H, i) do { \ + sph_u64 T1, T2; \ + T1 = SPH_T64(H + BSG5_1(E) + CH(E, F, G) + K512[i] + W[i]); \ + T2 = SPH_T64(BSG5_0(A) + MAJ(A, B, C)); \ + D = SPH_T64(D + T1); \ + H = SPH_T64(T1 + T2); \ + } while (0) + +#define SHA3_ROUND_BODY(in, r) do { \ + int i; \ + sph_u64 A, B, C, D, E, F, G, H; \ + sph_u64 W[80]; \ + \ + for (i = 0; i < 16; i ++) \ + W[i] = in(i); \ + for (i = 16; i < 80; i ++) \ + W[i] = SPH_T64(SSG5_1(W[i - 2]) + W[i - 7] \ + + SSG5_0(W[i - 15]) + W[i - 16]); \ + A = (r)[0]; \ + B = (r)[1]; \ + C = (r)[2]; \ + D = (r)[3]; \ + E = (r)[4]; \ + F = (r)[5]; \ + G = (r)[6]; \ + H = (r)[7]; \ + for (i = 0; i < 80; i += 8) { \ + SHA3_STEP(A, B, C, D, E, F, G, H, i + 0); \ + SHA3_STEP(H, A, B, C, D, E, F, G, i + 1); \ + SHA3_STEP(G, H, A, B, C, D, E, F, i + 2); \ + SHA3_STEP(F, G, H, A, B, C, D, E, i + 3); \ + SHA3_STEP(E, F, G, H, A, B, C, D, i + 4); \ + SHA3_STEP(D, E, F, G, H, A, B, C, i + 5); \ + SHA3_STEP(C, D, E, F, G, H, A, B, i + 6); \ + SHA3_STEP(B, C, D, E, F, G, H, A, i + 7); \ + } \ + (r)[0] = SPH_T64((r)[0] + A); \ + (r)[1] = SPH_T64((r)[1] + B); \ + (r)[2] = SPH_T64((r)[2] + C); \ + (r)[3] = SPH_T64((r)[3] + D); \ + (r)[4] = SPH_T64((r)[4] + E); \ + (r)[5] = SPH_T64((r)[5] + F); \ + (r)[6] = SPH_T64((r)[6] + G); \ + (r)[7] = SPH_T64((r)[7] + H); \ + } while (0) + +/* + * One round of SHA-384 / SHA-512. The data must be aligned for 64-bit access. + */ +static void +sha3_round(const unsigned char *data, sph_u64 r[8]) +{ +#define SHA3_IN(x) sph_dec64be_aligned(data + (8 * (x))) + SHA3_ROUND_BODY(SHA3_IN, r); +#undef SHA3_IN +} + +/* see sph_sha3.h */ +void +sph_sha384_init(void *cc) +{ + sph_sha384_context *sc; + + sc = (sph_sha384_context*)cc; + memcpy(sc->val, H384, sizeof H384); + sc->count = 0; +} + +/* see sph_sha3.h */ +void +sph_sha512_init(void *cc) +{ + sph_sha512_context *sc; + + sc = (sph_sha512_context*)cc; + memcpy(sc->val, H512, sizeof H512); + sc->count = 0; +} + +#define RFUN sha3_round +#define HASH sha384 +#define BE64 1 +#include "md_helper.c" + +/* see sph_sha3.h */ +void +sph_sha384_close(void *cc, void *dst) +{ + sha384_close(cc, dst, 6); + sph_sha384_init(cc); +} + +/* see sph_sha3.h */ +void +sph_sha384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + sha384_addbits_and_close(cc, ub, n, dst, 6); + sph_sha384_init(cc); +} + +/* see sph_sha3.h */ +void +sph_sha512_close(void *cc, void *dst) +{ + sha384_close(cc, dst, 8); + sph_sha512_init(cc); +} + +/* see sph_sha3.h */ +void +sph_sha512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + sha384_addbits_and_close(cc, ub, n, dst, 8); + sph_sha512_init(cc); +} + +/* see sph_sha3.h */ +void +sph_sha384_comp(const sph_u64 msg[16], sph_u64 val[8]) +{ +#define SHA3_IN(x) msg[x] + SHA3_ROUND_BODY(SHA3_IN, val); +#undef SHA3_IN +} + +#endif + diff --git a/src/Native/libmultihash/x17.c b/src/Native/libmultihash/x17.c new file mode 100644 index 000000000..9defffb57 --- /dev/null +++ b/src/Native/libmultihash/x17.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include + +#include "sha3/sph_blake.h" +#include "sha3/sph_bmw.h" +#include "sha3/sph_groestl.h" +#include "sha3/sph_jh.h" +#include "sha3/sph_keccak.h" +#include "sha3/sph_skein.h" +#include "sha3/sph_luffa.h" +#include "sha3/sph_cubehash.h" +#include "sha3/sph_shavite.h" +#include "sha3/sph_simd.h" +#include "sha3/sph_echo.h" +#include "sha3/sph_hamsi.h" +#include "sha3/sph_fugue.h" +#include "sha3/sph_shabal.h" +#include "sha3/sph_whirlpool.h" +#include "sha3/sph_sha2.h" +#include "sha3/sph_haval.h" + +void x17_hash(const char* input, char* output, uint32_t len) +{ + sph_blake512_context ctx_blake; + sph_bmw512_context ctx_bmw; + sph_groestl512_context ctx_groestl; + sph_skein512_context ctx_skein; + sph_jh512_context ctx_jh; + sph_keccak512_context ctx_keccak; + sph_luffa512_context ctx_luffa1; + sph_cubehash512_context ctx_cubehash1; + sph_shavite512_context ctx_shavite1; + sph_simd512_context ctx_simd1; + sph_echo512_context ctx_echo1; + sph_hamsi512_context ctx_hamsi1; + sph_fugue512_context ctx_fugue1; + sph_shabal512_context ctx_shabal1; + sph_whirlpool_context ctx_whirlpool1; + sph_sha512_context ctx_sha512; + sph_haval256_5_context ctx_haval; + + uint32_t hash[16]; + + sph_blake512_init(&ctx_blake); + sph_blake512 (&ctx_blake, input, len); + sph_blake512_close (&ctx_blake, hash); + + sph_bmw512_init(&ctx_bmw); + sph_bmw512 (&ctx_bmw, hash, 64); + sph_bmw512_close(&ctx_bmw, hash); + + sph_groestl512_init(&ctx_groestl); + sph_groestl512 (&ctx_groestl, hash, 64); + sph_groestl512_close(&ctx_groestl, hash); + + sph_skein512_init(&ctx_skein); + sph_skein512 (&ctx_skein, hash, 64); + sph_skein512_close (&ctx_skein, hash); + + sph_jh512_init(&ctx_jh); + sph_jh512 (&ctx_jh, hash, 64); + sph_jh512_close(&ctx_jh, hash); + + sph_keccak512_init(&ctx_keccak); + sph_keccak512 (&ctx_keccak, hash, 64); + sph_keccak512_close(&ctx_keccak, hash); + + sph_luffa512_init (&ctx_luffa1); + sph_luffa512 (&ctx_luffa1, hash, 64); + sph_luffa512_close (&ctx_luffa1, hash); + + sph_cubehash512_init (&ctx_cubehash1); + sph_cubehash512 (&ctx_cubehash1, hash, 64); + sph_cubehash512_close(&ctx_cubehash1, hash); + + sph_shavite512_init (&ctx_shavite1); + sph_shavite512 (&ctx_shavite1, hash, 64); + sph_shavite512_close(&ctx_shavite1, hash); + + sph_simd512_init (&ctx_simd1); + sph_simd512 (&ctx_simd1, hash, 64); + sph_simd512_close(&ctx_simd1, hash); + + sph_echo512_init (&ctx_echo1); + sph_echo512 (&ctx_echo1, hash, 64); + sph_echo512_close(&ctx_echo1, hash); + + sph_hamsi512_init (&ctx_hamsi1); + sph_hamsi512 (&ctx_hamsi1, hash, 64); + sph_hamsi512_close(&ctx_hamsi1, hash); + + sph_fugue512_init (&ctx_fugue1); + sph_fugue512 (&ctx_fugue1, hash, 64); + sph_fugue512_close(&ctx_fugue1, hash); + + sph_shabal512_init (&ctx_shabal1); + sph_shabal512 (&ctx_shabal1, hash, 64); + sph_shabal512_close(&ctx_shabal1, hash); + + sph_whirlpool_init (&ctx_whirlpool1); + sph_whirlpool (&ctx_whirlpool1, hash, 64); + sph_whirlpool_close(&ctx_whirlpool1, hash); + + sph_sha512_init(&ctx_sha512); + sph_sha512(&ctx_sha512,(const void*) hash, 64); + sph_sha512_close(&ctx_sha512,(void*) hash); + + sph_haval256_5_init(&ctx_haval); + sph_haval256_5(&ctx_haval,(const void*) hash, 64); + sph_haval256_5_close(&ctx_haval, hash); + + memcpy(output, hash, 32); +} diff --git a/src/Native/libmultihash/x17.h b/src/Native/libmultihash/x17.h new file mode 100644 index 000000000..11c29b889 --- /dev/null +++ b/src/Native/libmultihash/x17.h @@ -0,0 +1,16 @@ +#ifndef X17_H +#define X17_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void x17_hash(const char* input, char* output, uint32_t len); + +#ifdef __cplusplus +} +#endif + +#endif From 56d8d1e74991b059a26dc6ae0f4e7700bf99628b Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 26 Jan 2018 08:59:00 +0100 Subject: [PATCH 034/348] Neoscrypt argument validation --- src/MiningCore/Crypto/Hashing/Algorithms/NeoScrypt.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/MiningCore/Crypto/Hashing/Algorithms/NeoScrypt.cs b/src/MiningCore/Crypto/Hashing/Algorithms/NeoScrypt.cs index ede86941c..39e207f0e 100644 --- a/src/MiningCore/Crypto/Hashing/Algorithms/NeoScrypt.cs +++ b/src/MiningCore/Crypto/Hashing/Algorithms/NeoScrypt.cs @@ -18,6 +18,7 @@ portions of the Software. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +using System; using MiningCore.Contracts; using MiningCore.Native; @@ -35,6 +36,7 @@ public NeoScrypt(uint profile) public byte[] Digest(byte[] data, params object[] extra) { Contract.RequiresNonNull(data, nameof(data)); + Contract.Requires(data.Length == 80, $"{nameof(data)} length must be exactly 80 bytes"); var result = new byte[32]; From af1d7b938fa29079d505c75bf622eb248cea9259 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 26 Jan 2018 09:52:38 +0100 Subject: [PATCH 035/348] Drop paged retries from Monero Payments as those won't work anyway when transfer_split fails. --- .../Blockchain/Monero/MoneroPayoutHandler.cs | 57 +++---------------- 1 file changed, 8 insertions(+), 49 deletions(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index a7fa0d590..6ad8b2efc 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -163,62 +163,21 @@ private async Task PayoutBatch(Balance[] balances) var transferResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.Transfer, request); // gracefully handle error -4 (transaction would be too large. try /transfer_split) - if (transferResponse.Error?.Code == -4) + if (transferResponse.Error?.Code == -4 && walletSupportsTransferSplit) { - if (walletSupportsTransferSplit) - { - logger.Info(() => $"[{LogCategory}] Retrying transfer using {MWC.TransferSplit}"); - - var transferSplitResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.TransferSplit, request); - - // gracefully handle error -4 (transaction would be too large. try /transfer_split) - if (transferResponse.Error?.Code != -4) - { - HandleTransferResponse(transferSplitResponse, balances); - return; - } - } - - // retry paged - logger.Info(() => $"[{LogCategory}] Retrying paged"); + logger.Info(() => $"[{LogCategory}] Retrying transfer using {MWC.TransferSplit}"); - var validBalances = balances.Where(x => x.Amount > 0).ToArray(); - var pageSize = 10; - var pageCount = (int)Math.Ceiling((double)validBalances.Length / pageSize); + var transferSplitResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.TransferSplit, request); - for (var i = 0; i < pageCount; i++) + // gracefully handle error -4 (transaction would be too large. try /transfer_split) + if (transferResponse.Error?.Code != -4) { - var page = validBalances - .Skip(i * pageSize) - .Take(pageSize) - .ToArray(); - - // update request - request.Destinations = page - .Where(x => x.Amount > 0) - .Select(x => - { - ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId); - - return new TransferDestination - { - Address = address, - Amount = (ulong) Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type]) - }; - }).ToArray(); - - logger.Info(() => $"[{LogCategory}] Page {i + 1}: Paying out {FormatAmount(page.Sum(x => x.Amount))} to {page.Length} addresses"); - - transferResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.Transfer, request); - HandleTransferResponse(transferResponse, page); - - if (transferResponse.Error != null) - break; + HandleTransferResponse(transferSplitResponse, balances); + return; } } - else - HandleTransferResponse(transferResponse, balances); + HandleTransferResponse(transferResponse, balances); } private void ExtractAddressAndPaymentId(string input, out string address, out string paymentId) From 01a1f1ab2acc228b2ac87a08256b45beb15151dd Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 26 Jan 2018 10:26:30 +0100 Subject: [PATCH 036/348] WIP --- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 7 +++--- src/MiningCore/Blockchain/JobManagerBase.cs | 25 ++++++++++--------- src/MiningCore/Extensions/StringExtensions.cs | 5 ++++ 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 457d8fd80..fc21c6ce7 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -118,7 +118,7 @@ protected virtual void SetupJobUpdates() if (zmq.Count > 0) { - logger.Info(() => $"[{LogCat}] Subscribing to ZMQ push-updates from {string.Join(", ", zmq.Keys.Select(x => x.Host).Distinct())}"); + logger.Info(() => $"[{LogCat}] Subscribing to ZMQ push-updates from {string.Join(", ", zmq.Values)}"); var newJobsPubSub = daemon.ZmqSubscribe(zmq, BitcoinConstants.ZmqPublisherTopicBlockHash, 2) .Do(x=> x.Dispose()) // we don't care about the contents @@ -610,8 +610,9 @@ protected virtual async Task UpdateJob(bool forceUpdate, string via = null var job = currentJob; var isNew = job == null || - job.BlockTemplate?.PreviousBlockhash != blockTemplate.PreviousBlockhash || - job.BlockTemplate?.Height < blockTemplate.Height; + (blockTemplate != null && + job.BlockTemplate?.PreviousBlockhash != blockTemplate.PreviousBlockhash && + blockTemplate.Height > job.BlockTemplate?.Height); if (isNew || forceUpdate) { diff --git a/src/MiningCore/Blockchain/JobManagerBase.cs b/src/MiningCore/Blockchain/JobManagerBase.cs index 7535c2ec0..535835578 100644 --- a/src/MiningCore/Blockchain/JobManagerBase.cs +++ b/src/MiningCore/Blockchain/JobManagerBase.cs @@ -1,20 +1,20 @@ -/* +/* Copyright 2017 Coin Foundry (coinfoundry.org) Authors: Oliver Weichhold (oliver@weichhold.com) -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ @@ -24,6 +24,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Threading.Tasks; using Autofac; using MiningCore.Configuration; +using MiningCore.Extensions; using MiningCore.Util; using NLog; using Contract = MiningCore.Contracts.Contract; @@ -43,7 +44,7 @@ protected JobManagerBase(IComponentContext ctx) protected ClusterConfig clusterConfig; protected TJob currentJob; - private long jobId; + private int jobId; protected object jobLock = new object(); protected ILogger logger; protected PoolConfig poolConfig; @@ -78,7 +79,7 @@ protected string NextJobId(string format = null) if (format != null) return value.ToString(format); - return value.ToString(CultureInfo.InvariantCulture); + return value.ToStringHex8(); } protected abstract Task AreDaemonsHealthy(); diff --git a/src/MiningCore/Extensions/StringExtensions.cs b/src/MiningCore/Extensions/StringExtensions.cs index b80f0c8e7..d15735cbe 100644 --- a/src/MiningCore/Extensions/StringExtensions.cs +++ b/src/MiningCore/Extensions/StringExtensions.cs @@ -54,6 +54,11 @@ public static string ToStringHex8(this uint value) return value.ToString("x8", CultureInfo.InvariantCulture); } + public static string ToStringHex8(this int value) + { + return value.ToString("x8", CultureInfo.InvariantCulture); + } + public static string ToStringHexWithPrefix(this ulong value) { if (value == 0) From bd41307a7b4d425982d51757e997dd6ec6061c61 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 26 Jan 2018 12:40:41 +0100 Subject: [PATCH 037/348] Interlocked WIP --- src/MiningCore/Blockchain/JobManagerBase.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MiningCore/Blockchain/JobManagerBase.cs b/src/MiningCore/Blockchain/JobManagerBase.cs index 535835578..fa683e422 100644 --- a/src/MiningCore/Blockchain/JobManagerBase.cs +++ b/src/MiningCore/Blockchain/JobManagerBase.cs @@ -74,7 +74,8 @@ protected virtual async Task StartDaemonAsync() protected string NextJobId(string format = null) { - var value = Interlocked.Increment(ref jobId); + Interlocked.Increment(ref jobId); + var value = Interlocked.CompareExchange(ref jobId, 0, Int32.MinValue); if (format != null) return value.ToString(format); From d2382105e0ec1eaa581f556113781bf6cae71ac2 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 26 Jan 2018 12:49:11 +0100 Subject: [PATCH 038/348] Support Verge X17 variant --- src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index d8cddaaf1..40295e853 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -77,6 +77,9 @@ public class BitcoinProperties private static readonly BitcoinCoinProperties neoScryptCoin = new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, neoScryptProfile1, sha256DReverse, "Neoscrypt"); + private static readonly BitcoinCoinProperties x17Coin = + new BitcoinCoinProperties(1, sha256D, x17, new DigestReverser(x17), "X17"); + private static readonly Dictionary coinProperties = new Dictionary { // SHA256 @@ -164,6 +167,8 @@ private static BitcoinCoinProperties GetVergeProperties(string algorithm) return groestlMyriadCoin; case "x17": + return x17Coin; + case "blake2s": throw new NotSupportedException($"algorithm {algorithm} not yet supported"); From 20c8f537605cf73317e41d79b59101561f519838 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 26 Jan 2018 12:52:03 +0100 Subject: [PATCH 039/348] Make algo comparison case-insensitive --- src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index 40295e853..80036d849 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -134,7 +134,7 @@ private static BitcoinCoinProperties GetDigiByteProperties(string algorithm) { Contract.Requires(!string.IsNullOrEmpty(algorithm), $"{nameof(algorithm)} must not be empty"); - switch(algorithm) + switch(algorithm.ToLower()) { case "sha256d": return sha256Coin; @@ -158,7 +158,7 @@ private static BitcoinCoinProperties GetVergeProperties(string algorithm) { Contract.Requires(!string.IsNullOrEmpty(algorithm), $"{nameof(algorithm)} must not be empty"); - switch (algorithm) + switch (algorithm.ToLower()) { case "lyra2rev2": return lyra2Rev2CoinVariantA; From 20a43f7998881637b0acafa0fb685b0a85d6a2a1 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 26 Jan 2018 15:24:11 +0100 Subject: [PATCH 040/348] Added support for MaxActiveJobs Bitcoin Pool extra config --- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 22 ++++++++------- .../Configuration/BitcoinPoolConfigExtra.cs | 27 +++++++++++++++++++ 2 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index fc21c6ce7..9c3a84d2b 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -20,13 +20,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Net; -using System.Reactive.Disposables; using System.Reactive.Linq; -using System.Text; -using System.Threading; using System.Threading.Tasks; using Autofac; using MiningCore.Blockchain.Bitcoin.Configuration; @@ -38,14 +34,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Crypto.Hashing.Special; using MiningCore.DaemonInterface; using MiningCore.Extensions; -using MiningCore.Mining; using MiningCore.Notifications; using MiningCore.Stratum; using MiningCore.Time; using MiningCore.Util; using NBitcoin; -using NetMQ; -using NetMQ.Sockets; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; @@ -80,7 +73,8 @@ public BitcoinJobManager( protected const int ExtranonceBytes = 4; protected readonly IHashAlgorithm sha256d = new Sha256D(); protected readonly IHashAlgorithm sha256dReverse = new DigestReverser(new Sha256D()); - protected const int MaxActiveJobs = 4; + protected int maxActiveJobs = 4; + protected BitcoinPoolConfigExtra extraPoolConfig; protected readonly IHashAlgorithm sha256s = new Sha256S(); protected readonly List validJobs = new List(); protected IHashAlgorithm blockHasher; @@ -431,6 +425,16 @@ public virtual async Task SubmitShareAsync(StratumClient worker, o protected override string LogCat => "Bitcoin Job Manager"; + public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig) + { + extraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); + + if (extraPoolConfig?.MaxActiveJobs.HasValue == true) + maxActiveJobs = extraPoolConfig.MaxActiveJobs.Value; + + base.Configure(poolConfig, clusterConfig); + } + protected override void ConfigureDaemons() { var jsonSerializerSettings = ctx.Resolve(); @@ -639,7 +643,7 @@ protected virtual async Task UpdateJob(bool forceUpdate, string via = null validJobs.Add(job); // trim active jobs - while (validJobs.Count > MaxActiveJobs) + while (validJobs.Count > maxActiveJobs) validJobs.RemoveAt(0); } diff --git a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs new file mode 100644 index 000000000..56607d93c --- /dev/null +++ b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs @@ -0,0 +1,27 @@ +/* +Copyright 2017 Coin Foundry (coinfoundry.org) +Authors: Oliver Weichhold (oliver@weichhold.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +namespace MiningCore.Blockchain.Bitcoin.Configuration +{ + public class BitcoinPoolConfigExtra + { + public int? MaxActiveJobs { get; set; } + } +} From 1bbe4bef692222f972497e2c2cb40ffb93dff259 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 26 Jan 2018 15:27:21 +0100 Subject: [PATCH 041/348] Added logging --- src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 9c3a84d2b..f9742c17b 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -430,8 +430,12 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi extraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); if (extraPoolConfig?.MaxActiveJobs.HasValue == true) + { maxActiveJobs = extraPoolConfig.MaxActiveJobs.Value; + logger.Info(() => $"[{LogCat}] Setting {nameof(maxActiveJobs)} to {maxActiveJobs}"); + } + base.Configure(poolConfig, clusterConfig); } From 0603d13bf1b256455763455c5564329e3079fa65 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 26 Jan 2018 15:32:31 +0100 Subject: [PATCH 042/348] WIP --- src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index f9742c17b..9c3a84d2b 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -430,12 +430,8 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi extraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); if (extraPoolConfig?.MaxActiveJobs.HasValue == true) - { maxActiveJobs = extraPoolConfig.MaxActiveJobs.Value; - logger.Info(() => $"[{LogCat}] Setting {nameof(maxActiveJobs)} to {maxActiveJobs}"); - } - base.Configure(poolConfig, clusterConfig); } From 1175dc05d64355a53a55a58ff14eeb4034a0df94 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 26 Jan 2018 21:17:10 +0100 Subject: [PATCH 043/348] Ignore redundant ZMQ block hash updates for the same block from multiple daemons --- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 9c3a84d2b..0caf5eae5 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -23,6 +23,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Linq; using System.Net; using System.Reactive.Linq; +using System.Text; using System.Threading.Tasks; using Autofac; using MiningCore.Blockchain.Bitcoin.Configuration; @@ -115,7 +116,27 @@ protected virtual void SetupJobUpdates() logger.Info(() => $"[{LogCat}] Subscribing to ZMQ push-updates from {string.Join(", ", zmq.Values)}"); var newJobsPubSub = daemon.ZmqSubscribe(zmq, BitcoinConstants.ZmqPublisherTopicBlockHash, 2) - .Do(x=> x.Dispose()) // we don't care about the contents + .Select(frames => + { + try + { + // second frame contains the block hash as binary data + if (frames.Length > 1) + { + var hash = frames[1].ToHexString(); + return hash; + } + } + + finally + { + frames.Dispose(); + } + + return null; + }) + .Where(x=> x != null) + .DistinctUntilChanged() .Select(_ => Observable.FromAsync(() => UpdateJob(false, "ZMQ pub/sub"))) .Concat() .Publish() From e37603c2d55f56e22ecc89512f10bfe1d0f1358e Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sun, 28 Jan 2018 07:24:52 +0100 Subject: [PATCH 044/348] WIP --- src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index 6ad8b2efc..a0d3b27e4 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -274,9 +274,6 @@ public async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolCon walletDaemon = new DaemonClient(jsonSerializerSettings); walletDaemon.Configure(walletDaemonEndpoints, MoneroConstants.DaemonRpcLocation); - // detect network - await GetNetworkTypeAsync(); - // detect transfer_split support var response = await walletDaemon.ExecuteCmdSingleAsync(MWC.TransferSplit); walletSupportsTransferSplit = response.Error.Code != MoneroConstants.MoneroRpcMethodNotFound; @@ -400,6 +397,9 @@ public async Task PayoutAsync(Balance[] balances) #endif } + // detect network + await GetNetworkTypeAsync(); + // validate addresses balances = balances .Where(x => From 9921e0877ec63bef9a45e587ef56ae62ed7c1582 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sun, 28 Jan 2018 07:48:12 +0100 Subject: [PATCH 045/348] WIP --- .../Blockchain/Monero/MoneroPayoutHandler.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index a0d3b27e4..e46cb9853 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -274,6 +274,9 @@ public async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolCon walletDaemon = new DaemonClient(jsonSerializerSettings); walletDaemon.Configure(walletDaemonEndpoints, MoneroConstants.DaemonRpcLocation); + // detect network + await GetNetworkTypeAsync(); + // detect transfer_split support var response = await walletDaemon.ExecuteCmdSingleAsync(MWC.TransferSplit); walletSupportsTransferSplit = response.Error.Code != MoneroConstants.MoneroRpcMethodNotFound; @@ -386,19 +389,16 @@ public async Task PayoutAsync(Balance[] balances) { Contract.RequiresNonNull(balances, nameof(balances)); +#if !DEBUG // ensure we have peers var infoResponse = await daemon.ExecuteCmdAnyAsync(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 } - - // detect network - await GetNetworkTypeAsync(); +#endif // validate addresses balances = balances From ac17865b3d75de3cf53fe8e8868a144056570ddb Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sun, 28 Jan 2018 08:25:00 +0100 Subject: [PATCH 046/348] Disable suggest_difficulty for now --- src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs index ee1a85d91..0f87a207f 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs @@ -328,9 +328,9 @@ protected override async Task OnRequestAsync(StratumClient client, await OnSubmitAsync(client, tsRequest); break; - case BitcoinStratumMethods.SuggestDifficulty: - OnSuggestDifficulty(client, tsRequest); - break; + //case BitcoinStratumMethods.SuggestDifficulty: + // OnSuggestDifficulty(client, tsRequest); + // break; case BitcoinStratumMethods.GetTransactions: OnGetTransactions(client, tsRequest); From 18dfca074847bd9f6e49c1674fab87f1601ff641 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sun, 28 Jan 2018 13:06:27 +0100 Subject: [PATCH 047/348] Job management change --- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 0caf5eae5..3fe61e1cc 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -661,11 +661,17 @@ protected virtual async Task UpdateJob(bool forceUpdate, string via = null lock (jobLock) { + if(isNew) + validJobs.Clear(); + validJobs.Add(job); - // trim active jobs - while (validJobs.Count > maxActiveJobs) - validJobs.RemoveAt(0); + if (!isNew) + { + // trim active jobs + while(validJobs.Count > maxActiveJobs) + validJobs.RemoveAt(0); + } } currentJob = job; From 8ee93ab761ed72f61651837ce9518a5dc8fa2661 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sun, 28 Jan 2018 14:59:27 +0100 Subject: [PATCH 048/348] Runtime info logging --- src/MiningCore/Program.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index eb438a599..d8b85b704 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -101,6 +101,7 @@ public static void Main(string[] args) ValidateConfig(); Bootstrap(); + LogRuntimeInfo(); if (!shareRecoveryOption.HasValue()) { @@ -151,6 +152,11 @@ public static void Main(string[] args) } } + private static void LogRuntimeInfo() + { + logger.Info(() => $"Running on {RuntimeInformation.FrameworkDescription} under {RuntimeInformation.OSDescription} [{RuntimeInformation.OSArchitecture} - {RuntimeInformation.ProcessArchitecture}]"); + } + private static void ValidateConfig() { try @@ -360,10 +366,14 @@ private static void Logo() "); Console.WriteLine($" https://github.com/coinfoundry/miningcore\n"); Console.WriteLine($" Please contribute to the development of the project by donating:\n"); - Console.WriteLine($" BTC - 17QnVor1B6oK1rWnVVBrdX9gFzVkZZbhDm"); - Console.WriteLine($" ETH - 0xcb55abBfe361B12323eb952110cE33d5F28BeeE1"); - Console.WriteLine($" LTC - LTK6CWastkmBzGxgQhTTtCUjkjDA14kxzC"); - Console.WriteLine($" XMR - 475YVJbPHPedudkhrcNp1wDcLMTGYusGPF5fqE7XjnragVLPdqbCHBdZg3dF4dN9hXMjjvGbykS6a77dTAQvGrpiQqHp2eH"); + Console.WriteLine($" BTC - 17QnVor1B6oK1rWnVVBrdX9gFzVkZZbhDm"); + Console.WriteLine($" LTC - LTK6CWastkmBzGxgQhTTtCUjkjDA14kxzC"); + Console.WriteLine($" DASH - XqpBAV9QCaoLnz42uF5frSSfrJTrqHoxjp"); + Console.WriteLine($" ZEC - t1YHZHz2DGVMJiggD2P4fBQ2TAPgtLSUwZ7"); + Console.WriteLine($" ZCL - t1MFU1vD3YKgsK6Uh8hW7UTY8mKAV2xVqBr"); + Console.WriteLine($" ETH - 0xcb55abBfe361B12323eb952110cE33d5F28BeeE1"); + Console.WriteLine($" ETC - 0xF8cCE9CE143C68d3d4A7e6bf47006f21Cfcf93c0"); + Console.WriteLine($" XMR - 475YVJbPHPedudkhrcNp1wDcLMTGYusGPF5fqE7XjnragVLPdqbCHBdZg3dF4dN9hXMjjvGbykS6a77dTAQvGrpiQqHp2eH"); Console.WriteLine(); } From 7c5047b48a1ad4729ad10b3f131a97c6a5738e6c Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 2 Feb 2018 13:36:15 +0100 Subject: [PATCH 049/348] WIP --- src/MiningCore/Api/ApiServer.cs | 4 +++- .../Blockchain/Bitcoin/BitcoinPoolBase.cs | 9 +++++---- .../Blockchain/Monero/MoneroPayoutHandler.cs | 2 +- src/MiningCore/DaemonInterface/DaemonClient.cs | 16 ++++++++-------- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/MiningCore/Api/ApiServer.cs b/src/MiningCore/Api/ApiServer.cs index bc347181d..93832abac 100644 --- a/src/MiningCore/Api/ApiServer.cs +++ b/src/MiningCore/Api/ApiServer.cs @@ -412,6 +412,8 @@ private async Task GetMinerInfoAsync(HttpContext context, Match m) return; } + var perfMode = context.GetQueryParameter("perfMode", 1); + var statsResult = cf.RunTx((con, tx) => statsRepo.GetMinerStats(con, tx, pool.Id, address), true, IsolationLevel.Serializable); @@ -432,7 +434,7 @@ private async Task GetMinerInfoAsync(HttpContext context, Match m) stats.LastPaymentLink = string.Format(baseUrl, statsResult.LastPayment.TransactionConfirmationData); } - stats.Performance24H = GetMinerPerformanceInternal("day", pool, address); + stats.Performance24H = GetMinerPerformanceInternal(perfMode == 1 ? "day" : "month", pool, address); } await SendJson(context, stats); diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs index 0f87a207f..2454548a1 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs @@ -328,12 +328,13 @@ protected override async Task OnRequestAsync(StratumClient client, await OnSubmitAsync(client, tsRequest); break; - //case BitcoinStratumMethods.SuggestDifficulty: - // OnSuggestDifficulty(client, tsRequest); - // break; + case BitcoinStratumMethods.SuggestDifficulty: + OnSuggestDifficulty(client, tsRequest); + break; case BitcoinStratumMethods.GetTransactions: - OnGetTransactions(client, tsRequest); + //OnGetTransactions(client, tsRequest); + // ignored break; case BitcoinStratumMethods.ExtraNonceSubscribe: diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index e46cb9853..323a5382b 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -124,7 +124,7 @@ private async Task GetNetworkTypeAsync() { if (!networkType.HasValue) { - var infoResponse = await daemon.ExecuteCmdAnyAsync(MC.GetInfo); + var infoResponse = await daemon.ExecuteCmdAnyAsync(MC.GetInfo, true); var info = infoResponse.Response.ToObject(); networkType = info.IsTestnet ? MoneroNetworkType.Test : MoneroNetworkType.Main; diff --git a/src/MiningCore/DaemonInterface/DaemonClient.cs b/src/MiningCore/DaemonInterface/DaemonClient.cs index 5f3a7b902..f3d20f144 100644 --- a/src/MiningCore/DaemonInterface/DaemonClient.cs +++ b/src/MiningCore/DaemonInterface/DaemonClient.cs @@ -142,22 +142,19 @@ public async Task[]> ExecuteCmdAllAsync(str ///

/// Executes the request against all configured demons and returns the first successful response /// - /// /// - public Task> ExecuteCmdAnyAsync(string method) + public Task> ExecuteCmdAnyAsync(string method, bool throwOnError = false) { - return ExecuteCmdAnyAsync(method); + return ExecuteCmdAnyAsync(method, null, null, throwOnError); } /// /// Executes the request against all configured demons and returns the first successful response /// /// - /// - /// /// public async Task> ExecuteCmdAnyAsync(string method, object payload = null, - JsonSerializerSettings payloadJsonSerializerSettings = null) + JsonSerializerSettings payloadJsonSerializerSettings = null, bool throwOnError = false) where TResponse : class { Contract.Requires(!string.IsNullOrEmpty(method), $"{nameof(method)} must not be empty"); @@ -167,7 +164,7 @@ public async Task> ExecuteCmdAnyAsync(strin var tasks = endPoints.Select(endPoint => BuildRequestTask(endPoint, method, payload, payloadJsonSerializerSettings)).ToArray(); var taskFirstCompleted = await Task.WhenAny(tasks); - var result = MapDaemonResponse(0, taskFirstCompleted); + var result = MapDaemonResponse(0, taskFirstCompleted, throwOnError); return result; } @@ -348,7 +345,7 @@ protected string GetRequestId() return rpcRequestId; } - private DaemonResponse MapDaemonResponse(int i, Task x) + private DaemonResponse MapDaemonResponse(int i, Task x, bool throwOnError = false) where TResponse : class { var resp = new DaemonResponse @@ -365,6 +362,9 @@ private DaemonResponse MapDaemonResponse(int i, Task Date: Fri, 2 Feb 2018 15:21:41 +0100 Subject: [PATCH 050/348] WIP --- src/MiningCore/Api/ApiServer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Api/ApiServer.cs b/src/MiningCore/Api/ApiServer.cs index 93832abac..77c8b9143 100644 --- a/src/MiningCore/Api/ApiServer.cs +++ b/src/MiningCore/Api/ApiServer.cs @@ -412,7 +412,7 @@ private async Task GetMinerInfoAsync(HttpContext context, Match m) return; } - var perfMode = context.GetQueryParameter("perfMode", 1); + var perfMode = context.GetQueryParameter("perfMode", "day"); var statsResult = cf.RunTx((con, tx) => statsRepo.GetMinerStats(con, tx, pool.Id, address), true, IsolationLevel.Serializable); @@ -434,7 +434,7 @@ private async Task GetMinerInfoAsync(HttpContext context, Match m) stats.LastPaymentLink = string.Format(baseUrl, statsResult.LastPayment.TransactionConfirmationData); } - stats.Performance24H = GetMinerPerformanceInternal(perfMode == 1 ? "day" : "month", pool, address); + stats.Performance24H = GetMinerPerformanceInternal(perfMode, pool, address); } await SendJson(context, stats); From 69c487508626383001e3c538d757390ef6f4e067 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 3 Feb 2018 10:22:59 +0100 Subject: [PATCH 051/348] Fixed GBX, CRC Trim miner name during stratum login --- src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs | 5 ++--- src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs | 2 +- src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs | 2 +- src/MiningCore/Blockchain/Dash/DashPayoutHandler.cs | 6 +++--- src/MiningCore/Blockchain/Dash/DashPool.cs | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs index 8c7b6e88b..e5d8c39c1 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs @@ -46,9 +46,8 @@ namespace MiningCore.Blockchain.Bitcoin [CoinMetadata( CoinType.BTC, CoinType.BCH, CoinType.NMC, CoinType.PPC, CoinType.LTC, CoinType.DOGE, CoinType.DGB, CoinType.VIA, - CoinType.GRS, CoinType.MONA, CoinType.VTC, CoinType.BTG, - CoinType.GLT, CoinType.STAK, CoinType.MOON, CoinType.XVG, - CoinType.GBX, CoinType.CRC)] + CoinType.GRS, CoinType.MONA, CoinType.VTC, CoinType.BTG, + CoinType.GLT, CoinType.STAK, CoinType.MOON, CoinType.XVG)] public class BitcoinPayoutHandler : PayoutHandlerBase, IPayoutHandler { diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs index a9525f54c..c9c7940b6 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs @@ -34,7 +34,7 @@ namespace MiningCore.Blockchain.Bitcoin CoinType.BTC, CoinType.BCH, CoinType.NMC, CoinType.PPC, CoinType.LTC, CoinType.DOGE, CoinType.DGB, CoinType.VIA, CoinType.GRS, CoinType.MONA, CoinType.VTC, CoinType.GLT, - CoinType.MOON, CoinType.XVG, CoinType.GBX, CoinType.CRC)] + CoinType.MOON, CoinType.XVG)] public class BitcoinPool : BitcoinPoolBase, BlockTemplate> { public BitcoinPool(IComponentContext ctx, diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs index 2454548a1..9bfb4aec1 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs @@ -110,7 +110,7 @@ protected virtual async Task OnAuthorizeAsync(StratumClient client, Timestamped< // extract worker/miner var split = workerValue?.Split('.'); - var minerName = split?.FirstOrDefault(); + var minerName = split?.FirstOrDefault()?.Trim(); var workerName = split?.Skip(1).FirstOrDefault()?.Trim() ?? string.Empty; // assumes that workerName is an address diff --git a/src/MiningCore/Blockchain/Dash/DashPayoutHandler.cs b/src/MiningCore/Blockchain/Dash/DashPayoutHandler.cs index cea070768..689f8e01b 100644 --- a/src/MiningCore/Blockchain/Dash/DashPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Dash/DashPayoutHandler.cs @@ -35,7 +35,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace MiningCore.Blockchain.Dash { - [CoinMetadata(CoinType.DASH)] + [CoinMetadata(CoinType.DASH, CoinType.GBX, CoinType.CRC)] public class DashPayoutHandler : BitcoinPayoutHandler { public DashPayoutHandler( @@ -79,7 +79,7 @@ public override async Task PayoutAsync(Balance[] balances) args = new object[] { - string.Empty, // default account + string.Empty, // default account amounts, // addresses and associated amounts 1, // only spend funds covered by this many confirmations false, // Whether to add confirmations to transactions locked via InstantSend @@ -94,7 +94,7 @@ public override async Task PayoutAsync(Balance[] balances) { args = new object[] { - string.Empty, // default account + string.Empty, // default account amounts, // addresses and associated amounts }; } diff --git a/src/MiningCore/Blockchain/Dash/DashPool.cs b/src/MiningCore/Blockchain/Dash/DashPool.cs index 705944a6f..2d57d8844 100644 --- a/src/MiningCore/Blockchain/Dash/DashPool.cs +++ b/src/MiningCore/Blockchain/Dash/DashPool.cs @@ -30,7 +30,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace MiningCore.Blockchain.Dash { - [CoinMetadata(CoinType.DASH)] + [CoinMetadata(CoinType.DASH, CoinType.GBX, CoinType.CRC)] public class DashPool : BitcoinPoolBase { public DashPool(IComponentContext ctx, From 6c4fc32efe464a981df6b40a0b20131315b81136 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sun, 4 Feb 2018 10:59:00 +0100 Subject: [PATCH 052/348] Do not return stale miner performance stats via API --- src/MiningCore/Api/ApiServer.cs | 3 ++- src/MiningCore/Api/Responses/GetMinerStatsResponse.cs | 2 +- .../Postgres/Repositories/StatsRepository.cs | 10 +++++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Api/ApiServer.cs b/src/MiningCore/Api/ApiServer.cs index 77c8b9143..1593897a4 100644 --- a/src/MiningCore/Api/ApiServer.cs +++ b/src/MiningCore/Api/ApiServer.cs @@ -434,7 +434,8 @@ private async Task GetMinerInfoAsync(HttpContext context, Match m) stats.LastPaymentLink = string.Format(baseUrl, statsResult.LastPayment.TransactionConfirmationData); } - stats.Performance24H = GetMinerPerformanceInternal(perfMode, pool, address); + if(statsResult.Performance != null) + stats.PerformanceSamples = GetMinerPerformanceInternal(perfMode, pool, address); } await SendJson(context, stats); diff --git a/src/MiningCore/Api/Responses/GetMinerStatsResponse.cs b/src/MiningCore/Api/Responses/GetMinerStatsResponse.cs index 1695b68c4..84241e3b4 100644 --- a/src/MiningCore/Api/Responses/GetMinerStatsResponse.cs +++ b/src/MiningCore/Api/Responses/GetMinerStatsResponse.cs @@ -50,6 +50,6 @@ public class MinerStats public DateTime? LastPayment { get; set; } public string LastPaymentLink { get; set; } public WorkerPerformanceStatsContainer Performance { get; set; } - public WorkerPerformanceStatsContainer[] Performance24H { get; set; } + public WorkerPerformanceStatsContainer[] PerformanceSamples { get; set; } } } diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index c7de52c9d..e88e7a56e 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -27,6 +27,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Persistence.Model; using MiningCore.Persistence.Model.Projections; using MiningCore.Persistence.Repositories; +using MiningCore.Time; using NLog; using MinerStats = MiningCore.Persistence.Model.Projections.MinerStats; @@ -34,13 +35,16 @@ namespace MiningCore.Persistence.Postgres.Repositories { public class StatsRepository : IStatsRepository { - public StatsRepository(IMapper mapper) + public StatsRepository(IMapper mapper, IMasterClock clock) { this.mapper = mapper; + this.clock = clock; } private readonly IMapper mapper; + private readonly IMasterClock clock; private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); + private static readonly TimeSpan MinerStatsMaxAge = TimeSpan.FromMinutes(15); public void InsertPoolStats(IDbConnection con, IDbTransaction tx, PoolStats stats) { @@ -134,6 +138,10 @@ public MinerStats GetMinerStats(IDbConnection con, IDbTransaction tx, string poo var lastUpdate = con.QuerySingleOrDefault(query, new { poolId, miner }, tx); + // ignore stale minerstats + if (lastUpdate.HasValue && (clock.Now - lastUpdate) > MinerStatsMaxAge) + lastUpdate = null; + if (lastUpdate.HasValue) { // load rows rows by timestamp From 80053c379c5b87aff94315f6696fb5cad38a367d Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sun, 4 Feb 2018 11:00:30 +0100 Subject: [PATCH 053/348] WIP --- src/MiningCore/Api/ApiServer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/MiningCore/Api/ApiServer.cs b/src/MiningCore/Api/ApiServer.cs index 1593897a4..7138fa742 100644 --- a/src/MiningCore/Api/ApiServer.cs +++ b/src/MiningCore/Api/ApiServer.cs @@ -434,8 +434,7 @@ private async Task GetMinerInfoAsync(HttpContext context, Match m) stats.LastPaymentLink = string.Format(baseUrl, statsResult.LastPayment.TransactionConfirmationData); } - if(statsResult.Performance != null) - stats.PerformanceSamples = GetMinerPerformanceInternal(perfMode, pool, address); + stats.PerformanceSamples = GetMinerPerformanceInternal(perfMode, pool, address); } await SendJson(context, stats); From 2838c268b81ae0bd137410909a0609c75cc74749 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sun, 4 Feb 2018 16:00:35 +0100 Subject: [PATCH 054/348] VarDiff maxDelta support --- src/MiningCore/VarDiff/VarDiffManager.cs | 40 +++++++++++++++++------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/MiningCore/VarDiff/VarDiffManager.cs b/src/MiningCore/VarDiff/VarDiffManager.cs index 5c27ace89..869d7d24a 100644 --- a/src/MiningCore/VarDiff/VarDiffManager.cs +++ b/src/MiningCore/VarDiff/VarDiffManager.cs @@ -1,20 +1,20 @@ -/* +/* Copyright 2017 Coin Foundry (coinfoundry.org) Authors: Oliver Weichhold (oliver@weichhold.com) -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ @@ -64,7 +64,7 @@ public VarDiffManager(VarDiffConfig varDiffOptions, IMasterClock clock) } var minDiff = options.MinDiff; - var maxDiff = options.MaxDiff ?? Math.Max(minDiff, double.MaxValue); // for regtest + var maxDiff = options.MaxDiff ?? Math.Max(minDiff, double.MaxValue); // for regtest var sinceLast = ts - ctx.LastTs.Value; // Always calculate the time until now even there is no share submitted. @@ -85,6 +85,22 @@ public VarDiffManager(VarDiffConfig varDiffOptions, IMasterClock clock) // Possible New Diff var newDiff = difficulty * options.TargetTime / avg; + + // Max delta + if (options.MaxDelta.HasValue && options.MaxDelta > 0) + { + var delta = Math.Abs(newDiff - difficulty); + + if (delta > options.MaxDelta) + { + if (newDiff > difficulty) + newDiff -= delta - options.MaxDelta.Value; + else if (newDiff < difficulty) + newDiff += delta - options.MaxDelta.Value; + } + } + + // Clamp to valid range if (newDiff < minDiff) newDiff = minDiff; if (newDiff > maxDiff) @@ -98,7 +114,7 @@ public VarDiffManager(VarDiffConfig varDiffOptions, IMasterClock clock) // Due to change of diff, Buffer needs to be cleared ctx.TimeBuffer = new CircularDoubleBuffer(bufferSize); - + return newDiff; } } From 559fba44543dd3d3cc88f446e7a2a2c80615fffb Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 10 Feb 2018 14:30:50 +0100 Subject: [PATCH 055/348] Validate Monero PaymentID on login --- .../Blockchain/Monero/MoneroPool.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroPool.cs b/src/MiningCore/Blockchain/Monero/MoneroPool.cs index 85ba4b8f6..69ab2d6e2 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPool.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPool.cs @@ -81,21 +81,31 @@ private void OnLogin(StratumClient client, Timestamped tsRequest // extract worker/miner/paymentid var split = loginRequest.Login.Split('.'); - context.MinerName = split[0]; - context.WorkerName = split.Length > 1 ? split[1] : null; - context.UserAgent = loginRequest.UserAgent; + context.MinerName = split[0].Trim(); + context.WorkerName = split.Length > 1 ? split[1].Trim() : null; + context.UserAgent = loginRequest.UserAgent.Trim(); // extract paymentid var index = context.MinerName.IndexOf('#'); if (index != -1) { - context.PaymentId = context.MinerName.Substring(index + 1); - context.MinerName = context.MinerName.Substring(0, index); + context.PaymentId = context.MinerName.Substring(index + 1).Trim(); + context.MinerName = context.MinerName.Substring(0, index).Trim(); } // validate login var result = manager.ValidateAddress(context.MinerName); + // validate payment Id + if (!string.IsNullOrEmpty(context.PaymentId)) + { + if (context.PaymentId.Length != MoneroConstants.PaymentIdHexLength) + { + client.RespondError(StratumError.MinusOne, "invalid payment id", request.Id); + return; + } + } + context.IsSubscribed = result; context.IsAuthorized = result; From 1d3229bc83458ab257c92585a2d7f196760e0acb Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 10 Feb 2018 14:42:38 +0100 Subject: [PATCH 056/348] WIP --- src/MiningCore/Blockchain/Monero/MoneroPool.cs | 17 +++++++---------- src/MiningCore/Configuration/ClusterConfig.cs | 1 + 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroPool.cs b/src/MiningCore/Blockchain/Monero/MoneroPool.cs index 69ab2d6e2..635d81f03 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPool.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPool.cs @@ -96,16 +96,6 @@ private void OnLogin(StratumClient client, Timestamped tsRequest // validate login var result = manager.ValidateAddress(context.MinerName); - // validate payment Id - if (!string.IsNullOrEmpty(context.PaymentId)) - { - if (context.PaymentId.Length != MoneroConstants.PaymentIdHexLength) - { - client.RespondError(StratumError.MinusOne, "invalid payment id", request.Id); - return; - } - } - context.IsSubscribed = result; context.IsAuthorized = result; @@ -115,6 +105,13 @@ private void OnLogin(StratumClient client, Timestamped tsRequest return; } + // validate payment Id + if (!string.IsNullOrEmpty(context.PaymentId) && context.PaymentId.Length != MoneroConstants.PaymentIdHexLength) + { + client.RespondError(StratumError.MinusOne, "invalid payment id", request.Id); + return; + } + // respond var loginResponse = new MoneroLoginResponse { diff --git a/src/MiningCore/Configuration/ClusterConfig.cs b/src/MiningCore/Configuration/ClusterConfig.cs index 69c15ed49..12e863051 100644 --- a/src/MiningCore/Configuration/ClusterConfig.cs +++ b/src/MiningCore/Configuration/ClusterConfig.cs @@ -56,6 +56,7 @@ public enum CoinType XVG, // Verge GBX, // GoByte CRC, // CrowdCoin + BTCP } public class CoinConfig From 4eb3382b613db0cced30eb2bb06a489cb55582f1 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 10 Feb 2018 14:48:59 +0100 Subject: [PATCH 057/348] Do not send work until login is complete. Fixes #204 --- src/MiningCore/Blockchain/Ethereum/EthereumPool.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs index cbde01973..7a50087f4 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs @@ -230,7 +230,7 @@ private void OnNewJob(object jobParams) { var context = client.GetContextAs(); - if (context.IsSubscribed && context.IsAuthorized) + if (context.IsSubscribed && context.IsAuthorized && context.IsInitialWorkSent) { // check alive var lastActivityAgo = clock.Now - context.LastActivity; From 09a68cd90b6c28bea602016edd3de51275236dbe Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 12 Feb 2018 15:41:34 +0100 Subject: [PATCH 058/348] BTCP prep --- src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs | 1 + src/MiningCore/Blockchain/CoinMetaData.cs | 1 + src/MiningCore/Blockchain/ZCash/ZCashConstants.cs | 3 ++- src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs | 2 +- src/MiningCore/Blockchain/ZCash/ZCashPool.cs | 2 +- src/MiningCore/Configuration/ClusterConfig.cs | 2 +- 6 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index 80036d849..ac9fe52b1 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -113,6 +113,7 @@ public class BitcoinProperties { CoinType.BTG, equihashCoin }, { CoinType.ZCL, equihashCoin }, { CoinType.ZEN, equihashCoin }, + { CoinType.BTCP, equihashCoin }, // Neoscrypt { CoinType.GBX, neoScryptCoin }, diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index 85892af61..a60bd6cfc 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -121,6 +121,7 @@ public static class CoinMetaData { CoinType.DOGE, BitcoinProperties.GetAlgorithm }, { CoinType.ZEC, BitcoinProperties.GetAlgorithm }, { CoinType.ZCL, BitcoinProperties.GetAlgorithm }, + { CoinType.BTCP, BitcoinProperties.GetAlgorithm }, { CoinType.ZEN, BitcoinProperties.GetAlgorithm }, { CoinType.DGB, BitcoinProperties.GetAlgorithm }, { CoinType.NMC, BitcoinProperties.GetAlgorithm }, diff --git a/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs b/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs index e8ac8fe22..fa89154a6 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs @@ -275,7 +275,8 @@ public class ZCashConstants { { CoinType.ZEC, ZCashCoinbaseTxConfig }, { CoinType.ZCL, ZCLCoinbaseTxConfig }, - { CoinType.ZEN, ZencashCoinbaseTxConfig } + { CoinType.ZEN, ZencashCoinbaseTxConfig }, + { CoinType.BTCP, ZCLCoinbaseTxConfig }, }; } diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs index 40543b7a8..4b764a623 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs @@ -39,7 +39,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace MiningCore.Blockchain.ZCash { - [CoinMetadata(CoinType.ZEC, CoinType.ZCL, CoinType.ZEN)] + [CoinMetadata(CoinType.ZEC, CoinType.ZCL, CoinType.ZEN, CoinType.BTCP)] public class ZCashPayoutHandler : BitcoinPayoutHandler { public ZCashPayoutHandler( diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPool.cs b/src/MiningCore/Blockchain/ZCash/ZCashPool.cs index 571078eb9..651ca2b07 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPool.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPool.cs @@ -32,7 +32,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace MiningCore.Blockchain.ZCash { - [CoinMetadata(CoinType.ZEC, CoinType.ZCL, CoinType.ZEN)] + [CoinMetadata(CoinType.ZEC, CoinType.ZCL, CoinType.ZEN, CoinType.BTCP)] public class ZCashPool : ZCashPoolBase { public ZCashPool(IComponentContext ctx, diff --git a/src/MiningCore/Configuration/ClusterConfig.cs b/src/MiningCore/Configuration/ClusterConfig.cs index 12e863051..428d357d1 100644 --- a/src/MiningCore/Configuration/ClusterConfig.cs +++ b/src/MiningCore/Configuration/ClusterConfig.cs @@ -56,7 +56,7 @@ public enum CoinType XVG, // Verge GBX, // GoByte CRC, // CrowdCoin - BTCP + BTCP, // Bitcoin Private } public class CoinConfig From d203887f576ce662d467074b7b8ecce1892992a3 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 12 Feb 2018 15:46:11 +0100 Subject: [PATCH 059/348] Restore Monero paged payout retries --- .../Blockchain/Monero/MoneroPayoutHandler.cs | 57 ++++++++++++++++--- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index 323a5382b..5781f0870 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -163,21 +163,62 @@ private async Task PayoutBatch(Balance[] balances) var transferResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.Transfer, request); // gracefully handle error -4 (transaction would be too large. try /transfer_split) - if (transferResponse.Error?.Code == -4 && walletSupportsTransferSplit) + if (transferResponse.Error?.Code == -4) { - logger.Info(() => $"[{LogCategory}] Retrying transfer using {MWC.TransferSplit}"); + if (walletSupportsTransferSplit) + { + logger.Info(() => $"[{LogCategory}] Retrying transfer using {MWC.TransferSplit}"); - var transferSplitResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.TransferSplit, request); + var transferSplitResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.TransferSplit, request); - // gracefully handle error -4 (transaction would be too large. try /transfer_split) - if (transferResponse.Error?.Code != -4) + // gracefully handle error -4 (transaction would be too large. try /transfer_split) + if (transferResponse.Error?.Code != -4) + { + HandleTransferResponse(transferSplitResponse, balances); + return; + } + } + + // retry paged + logger.Info(() => $"[{LogCategory}] Retrying paged"); + + var validBalances = balances.Where(x => x.Amount > 0).ToArray(); + var pageSize = 10; + var pageCount = (int)Math.Ceiling((double)validBalances.Length / pageSize); + + for (var i = 0; i < pageCount; i++) { - HandleTransferResponse(transferSplitResponse, balances); - return; + var page = validBalances + .Skip(i * pageSize) + .Take(pageSize) + .ToArray(); + + // update request + request.Destinations = page + .Where(x => x.Amount > 0) + .Select(x => + { + ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId); + + return new TransferDestination + { + Address = address, + Amount = (ulong)Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type]) + }; + }).ToArray(); + + logger.Info(() => $"[{LogCategory}] Page {i + 1}: Paying out {FormatAmount(page.Sum(x => x.Amount))} to {page.Length} addresses"); + + transferResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.Transfer, request); + HandleTransferResponse(transferResponse, page); + + if (transferResponse.Error != null) + break; } } - HandleTransferResponse(transferResponse, balances); + else + HandleTransferResponse(transferResponse, balances); } private void ExtractAddressAndPaymentId(string input, out string address, out string paymentId) From c249bcb76f3ce5b3dd002824b388b8c82a701302 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 13 Feb 2018 13:35:48 +0100 Subject: [PATCH 060/348] Blake2s hash integration --- src/MiningCore.Tests/Crypto/HashingTests.cs | 16 + .../Crypto/Hashing/Algorithms/Blake2s.cs | 46 +++ src/MiningCore/Native/LibMultihash.cs | 3 + .../runtimes/win-x64/native/libmultihash.dll | Bin 908800 -> 915968 bytes .../runtimes/win-x86/native/libmultihash.dll | Bin 907776 -> 914432 bytes src/Native/libmultihash/Makefile | 4 +- src/Native/libmultihash/blake2s.c | 15 + src/Native/libmultihash/blake2s.h | 16 + src/Native/libmultihash/exports.cpp | 6 + src/Native/libmultihash/libmultihash.vcxproj | 4 + .../libmultihash/libmultihash.vcxproj.filters | 12 + src/Native/libmultihash/sha3/sph_blake2s.c | 387 ++++++++++++++++++ src/Native/libmultihash/sha3/sph_blake2s.h | 150 +++++++ 13 files changed, 657 insertions(+), 2 deletions(-) create mode 100644 src/MiningCore/Crypto/Hashing/Algorithms/Blake2s.cs create mode 100644 src/Native/libmultihash/blake2s.c create mode 100644 src/Native/libmultihash/blake2s.h create mode 100644 src/Native/libmultihash/sha3/sph_blake2s.c create mode 100644 src/Native/libmultihash/sha3/sph_blake2s.h diff --git a/src/MiningCore.Tests/Crypto/HashingTests.cs b/src/MiningCore.Tests/Crypto/HashingTests.cs index 0f6d3d634..a0be06bd4 100644 --- a/src/MiningCore.Tests/Crypto/HashingTests.cs +++ b/src/MiningCore.Tests/Crypto/HashingTests.cs @@ -33,6 +33,22 @@ public void Blake_Hash_Should_Throw_On_Null_Input() Assert.Throws(() => hasher.Digest(null)); } + [Fact] + public void Blake2s_Hash_Should_Match() + { + var hasher = new Blake2s(); + var result = hasher.Digest(testValue2).ToHexString(); + + Assert.Equal("c3ee938582d70ccd9a323b6097357449365d1fdfbbe2ecd7ee44e4bdbbb24392", result); + } + + [Fact] + public void Blake2s_Hash_Should_Throw_On_Null_Input() + { + var hasher = new Blake2s(); + Assert.Throws(() => hasher.Digest(null)); + } + [Fact] public void Groestl_Hash_Should_Match() { diff --git a/src/MiningCore/Crypto/Hashing/Algorithms/Blake2s.cs b/src/MiningCore/Crypto/Hashing/Algorithms/Blake2s.cs new file mode 100644 index 000000000..4570a7ad3 --- /dev/null +++ b/src/MiningCore/Crypto/Hashing/Algorithms/Blake2s.cs @@ -0,0 +1,46 @@ +/* +Copyright 2017 Coin Foundry (coinfoundry.org) +Authors: Oliver Weichhold (oliver@weichhold.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +using System; +using MiningCore.Contracts; +using MiningCore.Native; + +namespace MiningCore.Crypto.Hashing.Algorithms +{ + public unsafe class Blake2s : IHashAlgorithm + { + public byte[] Digest(byte[] data, params object[] extra) + { + Contract.RequiresNonNull(data, nameof(data)); + + var result = new byte[32]; + + fixed(byte* input = data) + { + fixed(byte* output = result) + { + LibMultihash.blake2s(input, output, (uint) data.Length); + } + } + + return result; + } + } +} diff --git a/src/MiningCore/Native/LibMultihash.cs b/src/MiningCore/Native/LibMultihash.cs index 2a44cf53c..68500c750 100644 --- a/src/MiningCore/Native/LibMultihash.cs +++ b/src/MiningCore/Native/LibMultihash.cs @@ -64,6 +64,9 @@ public static unsafe class LibMultihash [DllImport("libmultihash", EntryPoint = "blake_export", CallingConvention = CallingConvention.Cdecl)] public static extern int blake(byte* input, byte* output, uint inputLength); + [DllImport("libmultihash", EntryPoint = "blake2s_export", CallingConvention = CallingConvention.Cdecl)] + public static extern int blake2s(byte* input, byte* output, uint inputLength); + [DllImport("libmultihash", EntryPoint = "dcrypt_export", CallingConvention = CallingConvention.Cdecl)] public static extern int dcrypt(byte* input, byte* output, uint inputLength); diff --git a/src/MiningCore/runtimes/win-x64/native/libmultihash.dll b/src/MiningCore/runtimes/win-x64/native/libmultihash.dll index ee0b76888a624edef135a13b22427b37e4f0ba64..230fef5e9bec64793badcf4af0acce9d44f4b67e 100644 GIT binary patch delta 118031 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

vb^{n+1wTyHv-9G4v>X4)n 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

  • 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Ïyt2^i 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 ahteq|lTd1AaOd4nJMn?SScN0$-~K-p{EG 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 +#include +#include +#include + +#include "sha3/sph_blake2s.h" + +void blake2s_hash(const char* input, char* output, int inlen) +{ + blake2s_state ctx_blake2s; + blake2s_init(&ctx_blake2s, BLAKE2S_OUTBYTES); + blake2s_update(&ctx_blake2s, input, inlen); + blake2s_final(&ctx_blake2s, output, BLAKE2S_OUTBYTES); +} diff --git a/src/Native/libmultihash/blake2s.h b/src/Native/libmultihash/blake2s.h new file mode 100644 index 000000000..4e13893c1 --- /dev/null +++ b/src/Native/libmultihash/blake2s.h @@ -0,0 +1,16 @@ +#ifndef BLAKE2S_H +#define BLAKE2S_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void blake2s_hash(const char* input, char* output, int inlen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/Native/libmultihash/exports.cpp b/src/Native/libmultihash/exports.cpp index 4c2be0c56..d963d7501 100644 --- a/src/Native/libmultihash/exports.cpp +++ b/src/Native/libmultihash/exports.cpp @@ -25,6 +25,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "x11.h" #include "groestl.h" #include "blake.h" +#include "blake2s.h" #include "fugue.h" #include "qubit.h" #include "s3.h" @@ -121,6 +122,11 @@ extern "C" MODULE_API void blake_export(const char* input, char* output, uint32_ blake_hash(input, output, input_len); } +extern "C" MODULE_API void blake2s_export(const char* input, char* output, uint32_t input_len) +{ + blake2s_hash(input, output, input_len); +} + extern "C" MODULE_API void dcrypt_export(const char* input, char* output, uint32_t input_len) { dcrypt_hash(input, output, input_len); diff --git a/src/Native/libmultihash/libmultihash.vcxproj b/src/Native/libmultihash/libmultihash.vcxproj index a12686db6..029f7196a 100644 --- a/src/Native/libmultihash/libmultihash.vcxproj +++ b/src/Native/libmultihash/libmultihash.vcxproj @@ -164,6 +164,7 @@ + @@ -196,6 +197,7 @@ + @@ -229,6 +231,7 @@ + @@ -258,6 +261,7 @@ + diff --git a/src/Native/libmultihash/libmultihash.vcxproj.filters b/src/Native/libmultihash/libmultihash.vcxproj.filters index 96053bcf3..190142f2f 100644 --- a/src/Native/libmultihash/libmultihash.vcxproj.filters +++ b/src/Native/libmultihash/libmultihash.vcxproj.filters @@ -200,6 +200,12 @@ Header Files + + Header Files + + + Header Files + @@ -373,6 +379,12 @@ Source Files + + Source Files + + + Source Files + diff --git a/src/Native/libmultihash/sha3/sph_blake2s.c b/src/Native/libmultihash/sha3/sph_blake2s.c new file mode 100644 index 000000000..cbb9d890c --- /dev/null +++ b/src/Native/libmultihash/sha3/sph_blake2s.c @@ -0,0 +1,387 @@ +/** + * BLAKE2 reference source code package - reference C implementations + * + * Written in 2012 by Samuel Neves + * + * To the extent possible under law, the author(s) have dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide. This software is distributed without any warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication along with + * this software. If not, see . + */ + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#include "sph_types.h" +#include "sph_blake2s.h" + +static const uint32_t blake2s_IV[8] = +{ + 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, + 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL +}; + +static const uint8_t blake2s_sigma[10][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , +}; + +static inline int blake2s_set_lastnode( blake2s_state *S ) +{ + S->f[1] = ~0U; + return 0; +} + +static inline int blake2s_clear_lastnode( blake2s_state *S ) +{ + S->f[1] = 0U; + return 0; +} + +/* Some helper functions, not necessarily useful */ +static inline int blake2s_set_lastblock( blake2s_state *S ) +{ + if( S->last_node ) blake2s_set_lastnode( S ); + + S->f[0] = ~0U; + return 0; +} + +static inline int blake2s_clear_lastblock( blake2s_state *S ) +{ + if( S->last_node ) blake2s_clear_lastnode( S ); + + S->f[0] = 0U; + return 0; +} + +static inline int blake2s_increment_counter( blake2s_state *S, const uint32_t inc ) +{ + S->t[0] += inc; + S->t[1] += ( S->t[0] < inc ); + return 0; +} + +// Parameter-related functions +static inline int blake2s_param_set_digest_length( blake2s_param *P, const uint8_t digest_length ) +{ + P->digest_length = digest_length; + return 0; +} + +static inline int blake2s_param_set_fanout( blake2s_param *P, const uint8_t fanout ) +{ + P->fanout = fanout; + return 0; +} + +static inline int blake2s_param_set_max_depth( blake2s_param *P, const uint8_t depth ) +{ + P->depth = depth; + return 0; +} + +static inline int blake2s_param_set_leaf_length( blake2s_param *P, const uint32_t leaf_length ) +{ + store32( &P->leaf_length, leaf_length ); + return 0; +} + +static inline int blake2s_param_set_node_offset( blake2s_param *P, const uint64_t node_offset ) +{ + store48( P->node_offset, node_offset ); + return 0; +} + +static inline int blake2s_param_set_node_depth( blake2s_param *P, const uint8_t node_depth ) +{ + P->node_depth = node_depth; + return 0; +} + +static inline int blake2s_param_set_inner_length( blake2s_param *P, const uint8_t inner_length ) +{ + P->inner_length = inner_length; + return 0; +} + +static inline int blake2s_param_set_salt( blake2s_param *P, const uint8_t salt[BLAKE2S_SALTBYTES] ) +{ + memcpy( P->salt, salt, BLAKE2S_SALTBYTES ); + return 0; +} + +static inline int blake2s_param_set_personal( blake2s_param *P, const uint8_t personal[BLAKE2S_PERSONALBYTES] ) +{ + memcpy( P->personal, personal, BLAKE2S_PERSONALBYTES ); + return 0; +} + +static inline int blake2s_init0( blake2s_state *S ) +{ + memset( S, 0, sizeof( blake2s_state ) ); + + for( int i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i]; + + return 0; +} + +/* init2 xors IV with input parameter block */ +int blake2s_init_param( blake2s_state *S, const blake2s_param *P ) +{ + blake2s_init0( S ); + uint32_t *p = ( uint32_t * )( P ); + + /* IV XOR ParamBlock */ + for( size_t i = 0; i < 8; ++i ) + S->h[i] ^= load32( &p[i] ); + + return 0; +} + + +// Sequential blake2s initialization +int blake2s_init( blake2s_state *S, const uint8_t outlen ) +{ + blake2s_param P[1]; + + /* Move interval verification here? */ + if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; + + P->digest_length = outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store48( &P->node_offset, 0 ); + P->node_depth = 0; + P->inner_length = 0; + // memset(P->reserved, 0, sizeof(P->reserved) ); + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2s_init_param( S, P ); +} + +int blake2s_init_key( blake2s_state *S, const uint8_t outlen, const void *key, const uint8_t keylen ) +{ + blake2s_param P[1]; + + if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; + + if ( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1; + + P->digest_length = outlen; + P->key_length = keylen; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store48( &P->node_offset, 0 ); + P->node_depth = 0; + P->inner_length = 0; + // memset(P->reserved, 0, sizeof(P->reserved) ); + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + + if( blake2s_init_param( S, P ) < 0 ) return -1; + + { + uint8_t block[BLAKE2S_BLOCKBYTES]; + memset( block, 0, BLAKE2S_BLOCKBYTES ); + memcpy( block, key, keylen ); + blake2s_update( S, block, BLAKE2S_BLOCKBYTES ); + secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ + } + return 0; +} + +int blake2s_compress( blake2s_state *S, const uint8_t block[BLAKE2S_BLOCKBYTES] ) +{ + uint32_t m[16]; + uint32_t v[16]; + + for( size_t i = 0; i < 16; ++i ) + m[i] = load32( block + i * sizeof( m[i] ) ); + + for( size_t i = 0; i < 8; ++i ) + v[i] = S->h[i]; + + v[ 8] = blake2s_IV[0]; + v[ 9] = blake2s_IV[1]; + v[10] = blake2s_IV[2]; + v[11] = blake2s_IV[3]; + v[12] = S->t[0] ^ blake2s_IV[4]; + v[13] = S->t[1] ^ blake2s_IV[5]; + v[14] = S->f[0] ^ blake2s_IV[6]; + v[15] = S->f[1] ^ blake2s_IV[7]; +#define G(r,i,a,b,c,d) \ + do { \ + a = a + b + m[blake2s_sigma[r][2*i+0]]; \ + d = SPH_ROTR32(d ^ a, 16); \ + c = c + d; \ + b = SPH_ROTR32(b ^ c, 12); \ + a = a + b + m[blake2s_sigma[r][2*i+1]]; \ + d = SPH_ROTR32(d ^ a, 8); \ + c = c + d; \ + b = SPH_ROTR32(b ^ c, 7); \ + } while(0) +#define ROUND(r) \ + do { \ + G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ + G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ + G(r,2,v[ 2],v[ 6],v[10],v[14]); \ + G(r,3,v[ 3],v[ 7],v[11],v[15]); \ + G(r,4,v[ 0],v[ 5],v[10],v[15]); \ + G(r,5,v[ 1],v[ 6],v[11],v[12]); \ + G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ + G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ + } while(0) + ROUND( 0 ); + ROUND( 1 ); + ROUND( 2 ); + ROUND( 3 ); + ROUND( 4 ); + ROUND( 5 ); + ROUND( 6 ); + ROUND( 7 ); + ROUND( 8 ); + ROUND( 9 ); + + for( size_t i = 0; i < 8; ++i ) + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + +#undef G +#undef ROUND + return 0; +} + + +int blake2s_update( blake2s_state *S, const uint8_t *in, uint64_t inlen ) +{ + while( inlen > 0 ) + { + size_t left = S->buflen; + size_t fill = 2 * BLAKE2S_BLOCKBYTES - left; + + if( inlen > fill ) + { + memcpy( S->buf + left, in, fill ); // Fill buffer + S->buflen += fill; + blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); + blake2s_compress( S, S->buf ); // Compress + memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left + S->buflen -= BLAKE2S_BLOCKBYTES; + in += fill; + inlen -= fill; + } + else // inlen <= fill + { + memcpy(S->buf + left, in, (size_t) inlen); + S->buflen += (size_t) inlen; // Be lazy, do not compress + in += inlen; + inlen -= inlen; + } + } + + return 0; +} + +int blake2s_final( blake2s_state *S, uint8_t *out, uint8_t outlen ) +{ + uint8_t buffer[BLAKE2S_OUTBYTES]; + + if( S->buflen > BLAKE2S_BLOCKBYTES ) + { + blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); + blake2s_compress( S, S->buf ); + S->buflen -= BLAKE2S_BLOCKBYTES; + memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen ); + } + + blake2s_increment_counter( S, ( uint32_t )S->buflen ); + blake2s_set_lastblock( S ); + memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */ + blake2s_compress( S, S->buf ); + + for( int i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ + store32( buffer + sizeof( S->h[i] ) * i, S->h[i] ); + + memcpy( out, buffer, outlen ); + return 0; +} + +int blake2s( uint8_t *out, const void *in, const void *key, const uint8_t outlen, const uint64_t inlen, uint8_t keylen ) +{ + blake2s_state S[1]; + + /* Verify parameters */ + if ( NULL == in ) return -1; + + if ( NULL == out ) return -1; + + if ( NULL == key ) keylen = 0; /* Fail here instead if keylen != 0 and key == NULL? */ + + if( keylen > 0 ) + { + if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1; + } + else + { + if( blake2s_init( S, outlen ) < 0 ) return -1; + } + + blake2s_update( S, ( uint8_t * )in, inlen ); + blake2s_final( S, out, outlen ); + return 0; +} + +#if defined(__cplusplus) +} +#endif + + +#if defined(BLAKE2S_SELFTEST) +#include +#include "blake2-kat.h" /* test data not included */ +int main( int argc, char **argv ) +{ + uint8_t key[BLAKE2S_KEYBYTES]; + uint8_t buf[KAT_LENGTH]; + + for( size_t i = 0; i < BLAKE2S_KEYBYTES; ++i ) + key[i] = ( uint8_t )i; + + for( size_t i = 0; i < KAT_LENGTH; ++i ) + buf[i] = ( uint8_t )i; + + for( size_t i = 0; i < KAT_LENGTH; ++i ) + { + uint8_t hash[BLAKE2S_OUTBYTES]; + blake2s( hash, buf, key, BLAKE2S_OUTBYTES, i, BLAKE2S_KEYBYTES ); + + if( 0 != memcmp( hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES ) ) + { + puts( "error" ); + return -1; + } + } + + puts( "ok" ); + return 0; +} +#endif diff --git a/src/Native/libmultihash/sha3/sph_blake2s.h b/src/Native/libmultihash/sha3/sph_blake2s.h new file mode 100644 index 000000000..64aa25b54 --- /dev/null +++ b/src/Native/libmultihash/sha3/sph_blake2s.h @@ -0,0 +1,150 @@ +/** + * BLAKE2 reference source code package - reference C implementations + * + * Written in 2012 by Samuel Neves + * + * To the extent possible under law, the author(s) have dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide. This software is distributed without any warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication along with + * this software. If not, see . + */ +#pragma once +#ifndef __BLAKE2_H__ +#define __BLAKE2_H__ + +#include +#include + +#if defined(_MSC_VER) +#include +#define inline __inline +#define ALIGN(x) __declspec(align(x)) +#else +#define ALIGN(x) __attribute__((aligned(x))) +#endif + +/* blake2-impl.h */ + +static inline uint32_t load32(const void *src) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + return *(uint32_t *)(src); +#else + const uint8_t *p = (uint8_t *)src; + uint32_t w = *p++; + w |= (uint32_t)(*p++) << 8; + w |= (uint32_t)(*p++) << 16; + w |= (uint32_t)(*p++) << 24; + return w; +#endif +} + +static inline void store32(void *dst, uint32_t w) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + *(uint32_t *)(dst) = w; +#else + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; w >>= 8; + *p++ = (uint8_t)w; w >>= 8; + *p++ = (uint8_t)w; w >>= 8; + *p++ = (uint8_t)w; +#endif +} + +static inline uint64_t load48(const void *src) +{ + const uint8_t *p = (const uint8_t *)src; + uint64_t w = *p++; + w |= (uint64_t)(*p++) << 8; + w |= (uint64_t)(*p++) << 16; + w |= (uint64_t)(*p++) << 24; + w |= (uint64_t)(*p++) << 32; + w |= (uint64_t)(*p++) << 40; + return w; +} + +static inline void store48(void *dst, uint64_t w) +{ + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; w >>= 8; + *p++ = (uint8_t)w; w >>= 8; + *p++ = (uint8_t)w; w >>= 8; + *p++ = (uint8_t)w; w >>= 8; + *p++ = (uint8_t)w; w >>= 8; + *p++ = (uint8_t)w; +} + +/* prevents compiler optimizing out memset() */ +static inline void secure_zero_memory(void *v, size_t n) +{ + volatile uint8_t *p = ( volatile uint8_t * )v; + + while( n-- ) *p++ = 0; +} + +/* blake2.h */ + +enum blake2s_constant +{ + BLAKE2S_BLOCKBYTES = 64, + BLAKE2S_OUTBYTES = 32, + BLAKE2S_KEYBYTES = 32, + BLAKE2S_SALTBYTES = 8, + BLAKE2S_PERSONALBYTES = 8 +}; + +#pragma pack(push, 1) +typedef struct __blake2s_param +{ + uint8_t digest_length; // 1 + uint8_t key_length; // 2 + uint8_t fanout; // 3 + uint8_t depth; // 4 + uint32_t leaf_length; // 8 + uint8_t node_offset[6];// 14 + uint8_t node_depth; // 15 + uint8_t inner_length; // 16 + // uint8_t reserved[0]; + uint8_t salt[BLAKE2S_SALTBYTES]; // 24 + uint8_t personal[BLAKE2S_PERSONALBYTES]; // 32 +} blake2s_param; + +ALIGN( 64 ) typedef struct __blake2s_state +{ + uint32_t h[8]; + uint32_t t[2]; + uint32_t f[2]; + uint8_t buf[2 * BLAKE2S_BLOCKBYTES]; + size_t buflen; + uint8_t last_node; +} blake2s_state; +#pragma pack(pop) + +#if defined(__cplusplus) +extern "C" { +#endif + + int blake2s_compress( blake2s_state *S, const uint8_t block[BLAKE2S_BLOCKBYTES] ); + + // Streaming API + int blake2s_init( blake2s_state *S, const uint8_t outlen ); + int blake2s_init_key( blake2s_state *S, const uint8_t outlen, const void *key, const uint8_t keylen ); + int blake2s_init_param( blake2s_state *S, const blake2s_param *P ); + int blake2s_update( blake2s_state *S, const uint8_t *in, uint64_t inlen ); + int blake2s_final( blake2s_state *S, uint8_t *out, uint8_t outlen ); + + // Simple API + int blake2s( uint8_t *out, const void *in, const void *key, const uint8_t outlen, const uint64_t inlen, uint8_t keylen ); + + // Direct Hash Mining Helpers + #define blake2s_salt32(out, in, inlen, key32) blake2s(out, in, key32, 32, inlen, 32) /* neoscrypt */ + #define blake2s_simple(out, in, inlen) blake2s(out, in, NULL, 32, inlen, 0) + +#if defined(__cplusplus) +} +#endif + +#endif From ef82e27d2b9219c4bf0d91b3bf5af5265aac1874 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 13 Feb 2018 15:36:20 +0100 Subject: [PATCH 061/348] Stats Pre-aggregation for performance critical APIs --- .../Bitcoin/BitcoinPayoutHandler.cs | 4 +- .../Ethereum/EthereumPayoutHandler.cs | 4 +- .../Blockchain/Monero/MoneroPayoutHandler.cs | 4 +- src/MiningCore/Mining/StatsRecorder.cs | 113 +++++++++++++++--- src/MiningCore/Payments/Abstractions.cs | 2 +- .../Model/MinerWorkerStatsPreAgg.cs | 37 ++++++ .../Postgres/Repositories/ShareRepository.cs | 12 +- .../Postgres/Repositories/StatsRepository.cs | 24 +++- .../Persistence/Postgres/Scripts/createdb.sql | 15 +++ .../Repositories/IShareRepository.cs | 3 +- .../Repositories/IStatsRepository.cs | 2 + 11 files changed, 193 insertions(+), 27 deletions(-) create mode 100644 src/MiningCore/Persistence/Model/MinerWorkerStatsPreAgg.cs diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs index e5d8c39c1..eee37c350 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs @@ -189,9 +189,9 @@ public virtual async Task ClassifyBlocksAsync(Block[] blocks) return result.ToArray(); } - public virtual Task CalculateBlockEffortAsync(Block block, ulong accumulatedBlockShareDiff) + public virtual Task CalculateBlockEffortAsync(Block block, double accumulatedBlockShareDiff) { - block.Effort = (double) accumulatedBlockShareDiff / block.NetworkDifficulty; + block.Effort = accumulatedBlockShareDiff / block.NetworkDifficulty; return Task.FromResult(true); } diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumPayoutHandler.cs b/src/MiningCore/Blockchain/Ethereum/EthereumPayoutHandler.cs index 771ac68be..8a6b00843 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumPayoutHandler.cs @@ -233,9 +233,9 @@ public async Task ClassifyBlocksAsync(Block[] blocks) return result.ToArray(); } - public Task CalculateBlockEffortAsync(Block block, ulong accumulatedBlockShareDiff) + public Task CalculateBlockEffortAsync(Block block, double accumulatedBlockShareDiff) { - block.Effort = (double) accumulatedBlockShareDiff / block.NetworkDifficulty; + block.Effort = accumulatedBlockShareDiff / block.NetworkDifficulty; return Task.FromResult(true); } diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index 5781f0870..dd463dd26 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -393,9 +393,9 @@ public async Task ClassifyBlocksAsync(Block[] blocks) return result.ToArray(); } - public Task CalculateBlockEffortAsync(Block block, ulong accumulatedBlockShareDiff) + public Task CalculateBlockEffortAsync(Block block, double accumulatedBlockShareDiff) { - block.Effort = (double) accumulatedBlockShareDiff / block.NetworkDifficulty; + block.Effort = accumulatedBlockShareDiff / block.NetworkDifficulty; return Task.FromResult(true); } diff --git a/src/MiningCore/Mining/StatsRecorder.cs b/src/MiningCore/Mining/StatsRecorder.cs index e7dc5a10b..601e0d16c 100644 --- a/src/MiningCore/Mining/StatsRecorder.cs +++ b/src/MiningCore/Mining/StatsRecorder.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data.Common; +using System.Diagnostics; using System.Linq; using System.Net.Sockets; using System.Threading; @@ -56,7 +57,8 @@ public StatsRecorder(IComponentContext ctx, private const int HashrateCalculationWindow = 1200; // seconds private const int MinHashrateCalculationWindow = 300; // seconds private ClusterConfig clusterConfig; - private Thread thread; + private Thread thread1; + private Thread thread2; private const int RetryCount = 4; private Policy readFaultPolicy; @@ -76,12 +78,12 @@ public void AttachPool(IMiningPool pool) public void Start() { - thread = new Thread(async () => - { - logger.Info(() => "Online"); + logger.Info(() => "Online"); + thread1 = new Thread(() => + { // warm-up delay - await Task.Delay(TimeSpan.FromSeconds(10)); + Thread.Sleep(TimeSpan.FromSeconds(10)); var interval = TimeSpan.FromMinutes(5); @@ -89,7 +91,37 @@ public void Start() { try { - await UpdatePoolsAsync(); + UpdatePoolHashrates(); + } + + catch (Exception ex) + { + logger.Error(ex); + } + + var waitResult = stopEvent.WaitOne(interval); + + // check if stop was signalled + if (waitResult) + break; + } + }); + + thread1.Name = "StatsRecorder"; + thread1.Start(); + + thread2 = new Thread(() => + { + // warm-up delay + Thread.Sleep(TimeSpan.FromSeconds(10)); + + var interval = TimeSpan.FromMinutes(15); + + while (true) + { + try + { + UpdatePoolPreAggs(); } catch (Exception ex) @@ -105,8 +137,8 @@ public void Start() } }); - thread.Name = "StatsRecorder"; - thread.Start(); + thread2.Name = "StatsRecorder - PreAggs"; + thread2.Start(); } public void Stop() @@ -114,21 +146,14 @@ public void Stop() logger.Info(() => "Stopping .."); stopEvent.Set(); - thread.Join(); + thread1.Join(); logger.Info(() => "Stopped"); } #endregion // API-Surface - private Task UpdatePoolsAsync() - { - UpdateHashrates(); - - return Task.FromResult(true); - } - - private void UpdateHashrates() + private void UpdatePoolHashrates() { var start = clock.Now; var target = start.AddSeconds(-HashrateCalculationWindow); @@ -218,6 +243,60 @@ private void UpdateHashrates() } } + private void UpdatePoolPreAggs() + { + var start = clock.Now; + + var stats = new MinerWorkerStatsPreAgg + { + Created = start, + Updated = start, + }; + + var sw = new Stopwatch(); + + foreach (var poolId in pools.Keys) + { + stats.PoolId = poolId; + + logger.Info(() => $"Updating pre-aggregated miner stats for pool {poolId}"); + + // fetch stats + var result = readFaultPolicy.Execute(() => + cf.Run(con => shareRepo.GetAccumulatedShareDifficultyTotal(con, poolId), sw, logger)); + + var byMiner = result.GroupBy(x => x.Miner).ToArray(); + + // update miner, worker + foreach (var minerHashes in byMiner) + { + cf.RunTx((con, tx) => + { + stats.Miner = minerHashes.Key; + + foreach (var item in minerHashes) + { + // update + stats.Worker = item.Worker ?? string.Empty; + stats.ShareCount = item.Count; + stats.SharesAccumulated = item.Sum; + + // persist + statsRepo.UpdateMinerWorkerStatsPreAgg(con, tx, stats); + } + + // delete records for workers that are no longer producing shares + var activeWorkers = minerHashes + .Select(x => x.Worker ?? string.Empty) + .Distinct() + .ToArray(); + + statsRepo.DeleteMinerWorkerStatsExcept(con, tx, poolId, stats.Miner, activeWorkers); + }); + } + } + } + private void BuildFaultHandlingPolicy() { var retry = Policy diff --git a/src/MiningCore/Payments/Abstractions.cs b/src/MiningCore/Payments/Abstractions.cs index b89474131..eff19d325 100644 --- a/src/MiningCore/Payments/Abstractions.cs +++ b/src/MiningCore/Payments/Abstractions.cs @@ -30,7 +30,7 @@ public interface IPayoutHandler Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolConfig); Task ClassifyBlocksAsync(Block[] blocks); - Task CalculateBlockEffortAsync(Block block, ulong accumulatedBlockShareDiff); + Task CalculateBlockEffortAsync(Block block, double accumulatedBlockShareDiff); Task UpdateBlockRewardBalancesAsync(IDbConnection con, IDbTransaction tx, Block block, PoolConfig pool); Task PayoutAsync(Balance[] balances); diff --git a/src/MiningCore/Persistence/Model/MinerWorkerStatsPreAgg.cs b/src/MiningCore/Persistence/Model/MinerWorkerStatsPreAgg.cs new file mode 100644 index 000000000..a008039dd --- /dev/null +++ b/src/MiningCore/Persistence/Model/MinerWorkerStatsPreAgg.cs @@ -0,0 +1,37 @@ +/* +Copyright 2017 Coin Foundry (coinfoundry.org) +Authors: Oliver Weichhold (oliver@weichhold.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +using System; + +namespace MiningCore.Persistence.Model +{ + public class MinerWorkerStatsPreAgg + { + public string PoolId { get; set; } + public string Miner { get; set; } + public string Worker { get; set; } + + public long ShareCount { get; set; } + public double SharesAccumulated { get; set; } + + public DateTime Created { get; set; } + public DateTime Updated { get; set; } + } +} diff --git a/src/MiningCore/Persistence/Postgres/Repositories/ShareRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/ShareRepository.cs index 5ea3b4ff2..f5a3c9526 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/ShareRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/ShareRepository.cs @@ -127,7 +127,7 @@ public long CountSharesBetweenCreated(IDbConnection con, string poolId, string m return con.QuerySingle(query, new { poolId, miner, start, end }); } - public ulong? GetAccumulatedShareDifficultyBetweenCreated(IDbConnection con, string poolId, DateTime start, DateTime end) + public double? GetAccumulatedShareDifficultyBetweenCreated(IDbConnection con, string poolId, DateTime start, DateTime end) { logger.LogInvoke(new[] { poolId }); @@ -136,6 +136,16 @@ public long CountSharesBetweenCreated(IDbConnection con, string poolId, string m return con.QuerySingle(query, new { poolId, start, end }); } + public MinerWorkerHashes[] GetAccumulatedShareDifficultyTotal(IDbConnection con, string poolId) + { + logger.LogInvoke(new[] { (object)poolId }); + + var query = "SELECT SUM(difficulty) AS sum, COUNT(difficulty) AS count, miner, worker FROM shares WHERE poolid = @poolid group by miner, worker"; + + return con.Query(query, new { poolId }) + .ToArray(); + } + public MinerWorkerHashes[] GetHashAccumulationBetweenCreated(IDbConnection con, string poolId, DateTime start, DateTime end) { logger.LogInvoke(new[] { poolId }); diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index e88e7a56e..b6c342dcc 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -119,7 +119,7 @@ public MinerStats GetMinerStats(IDbConnection con, IDbTransaction tx, string poo { logger.LogInvoke(new[] { poolId, miner }); - var query = "SELECT (SELECT SUM(difficulty) FROM shares WHERE poolid = @poolId AND miner = @miner) AS pendingshares, " + + var query = "SELECT (SELECT SUM(sharesaccumulated) FROM minerstats_pre_agg WHERE poolid = @poolId AND miner = @miner) AS pendingshares, " + "(SELECT amount FROM balances WHERE poolid = @poolId AND address = @miner) AS pendingbalance, " + "(SELECT SUM(amount) FROM payments WHERE poolid = @poolId and address = @miner) as totalpaid"; @@ -246,6 +246,28 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConn return result; } + public void UpdateMinerWorkerStatsPreAgg(IDbConnection con, IDbTransaction tx, MinerWorkerStatsPreAgg stats) + { + logger.LogInvoke(); + + var query = "INSERT INTO minerstats_pre_agg(poolid, miner, worker, sharecount, sharesaccumulated, created, updated) " + + "VALUES(@poolid, @miner, @worker, @sharecount, @sharesaccumulated, @created, @updated)" + + "ON CONFLICT ON CONSTRAINT minerstats_pre_agg_pkey " + + "DO UPDATE SET " + + "sharecount = @sharecount, sharesaccumulated = @sharesaccumulated, updated = @updated"; + + con.Execute(query, stats, tx); + } + + public void DeleteMinerWorkerStatsExcept(IDbConnection con, IDbTransaction tx, string poolId, string miner, string[] activeWorkers) + { + logger.LogInvoke(); + + var query = "DELETE FROM minerstats_pre_agg WHERE poolid = @poolid AND miner = @miner AND NOT(worker = ANY(@activeWorkers))"; + + con.Execute(query, new { poolId, miner, activeWorkers }, tx); + } + public MinerWorkerPerformanceStats[] PagePoolMinersByHashrate(IDbConnection con, string poolId, DateTime from, int page, int pageSize) { logger.LogInvoke(new[] { (object) poolId, from, page, pageSize }); diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index 9655093dd..8972b09b8 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -95,3 +95,18 @@ CREATE INDEX IDX_MINERSTATS_POOL_CREATED on minerstats(poolid, created); CREATE INDEX IDX_MINERSTATS_POOL_MINER_CREATED on minerstats(poolid, miner, created); CREATE INDEX IDX_MINERSTATS_POOL_MINER_CREATED_HOUR on minerstats(poolid, miner, date_trunc('hour',created)); CREATE INDEX IDX_MINERSTATS_POOL_MINER_CREATED_DAY on minerstats(poolid, miner, date_trunc('day',created)); + +CREATE TABLE minerstats_pre_agg +( + poolid TEXT NOT NULL, + miner TEXT NOT NULL, + worker TEXT NOT NULL, + + sharecount BIGINT NOT NULL, + sharesaccumulated DOUBLE PRECISION NOT NULL, + + created TIMESTAMP NOT NULL, + updated TIMESTAMP NOT NULL, + + primary key(poolid, miner, worker) +); diff --git a/src/MiningCore/Persistence/Repositories/IShareRepository.cs b/src/MiningCore/Persistence/Repositories/IShareRepository.cs index 3178a6bc8..3c48fb26e 100644 --- a/src/MiningCore/Persistence/Repositories/IShareRepository.cs +++ b/src/MiningCore/Persistence/Repositories/IShareRepository.cs @@ -36,7 +36,8 @@ public interface IShareRepository void DeleteSharesBeforeCreated(IDbConnection con, IDbTransaction tx, string poolId, DateTime before); long CountSharesBetweenCreated(IDbConnection con, string poolId, string miner, DateTime? start, DateTime? end); - ulong? GetAccumulatedShareDifficultyBetweenCreated(IDbConnection con, string poolId, DateTime start, DateTime end); + double? GetAccumulatedShareDifficultyBetweenCreated(IDbConnection con, string poolId, DateTime start, DateTime end); + MinerWorkerHashes[] GetAccumulatedShareDifficultyTotal(IDbConnection con, string poolId); MinerWorkerHashes[] GetHashAccumulationBetweenCreated(IDbConnection con, string poolId, DateTime start, DateTime end); } } diff --git a/src/MiningCore/Persistence/Repositories/IStatsRepository.cs b/src/MiningCore/Persistence/Repositories/IStatsRepository.cs index e6e4c8829..338a587d5 100644 --- a/src/MiningCore/Persistence/Repositories/IStatsRepository.cs +++ b/src/MiningCore/Persistence/Repositories/IStatsRepository.cs @@ -37,5 +37,7 @@ public interface IStatsRepository MinerWorkerPerformanceStats[] PagePoolMinersByHashrate(IDbConnection con, string poolId, DateTime from, int page, int pageSize); WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenHourly(IDbConnection con, string poolId, string miner, DateTime start, DateTime end); WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConnection con, string poolId, string miner, DateTime start, DateTime end); + void UpdateMinerWorkerStatsPreAgg(IDbConnection con, IDbTransaction tx, MinerWorkerStatsPreAgg stats); + void DeleteMinerWorkerStatsExcept(IDbConnection con, IDbTransaction tx, string poolId, string miner, string[] activeWorkers); } } From 410cd1451c1cc6b9270404022dc1e489e66c71ad Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 13 Feb 2018 20:20:06 +0100 Subject: [PATCH 062/348] Support for Legacy Bitcoin daemons and more complete Verge (XVG) integration --- .../Blockchain/Bitcoin/BitcoinConstants.cs | 3 + .../Blockchain/Bitcoin/BitcoinJobManager.cs | 87 ++++++++++++++++--- .../Blockchain/Bitcoin/BitcoinProperties.cs | 6 +- .../Configuration/BitcoinPoolConfigExtra.cs | 9 ++ .../DaemonResponses/GetInfoResponse.cs | 33 +++++++ .../Blockchain/Ethereum/EthereumJobManager.cs | 4 +- src/MiningCore/Blockchain/JobManagerBase.cs | 8 +- .../Blockchain/Monero/MoneroJobManager.cs | 4 +- 8 files changed, 135 insertions(+), 19 deletions(-) create mode 100644 src/MiningCore/Blockchain/Bitcoin/DaemonResponses/GetInfoResponse.cs diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs index ea30b2ab5..41f90ebdf 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs @@ -118,5 +118,8 @@ public static class BitcoinCommands public const string GetBlock = "getblock"; public const string GetTransaction = "gettransaction"; public const string SendMany = "sendmany"; + + // Legacy commands + public const string GetInfo = "getinfo"; } } diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 3fe61e1cc..0bd2a08fe 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -75,6 +75,7 @@ public BitcoinJobManager( protected readonly IHashAlgorithm sha256d = new Sha256D(); protected readonly IHashAlgorithm sha256dReverse = new DigestReverser(new Sha256D()); protected int maxActiveJobs = 4; + private bool hasLegacyDaemon; protected BitcoinPoolConfigExtra extraPoolConfig; protected readonly IHashAlgorithm sha256s = new Sha256S(); protected readonly List validJobs = new List(); @@ -96,6 +97,7 @@ public BitcoinJobManager( } }; + protected virtual void SetupJobUpdates() { if (poolConfig.ExternalStratum) @@ -203,6 +205,12 @@ protected virtual async Task> GetBlockTemplateAsy protected virtual async Task ShowDaemonSyncProgressAsync() { + if (hasLegacyDaemon) + { + await ShowDaemonSyncProgressLegacyAsync(); + return; + } + var infos = await daemon.ExecuteCmdAllAsync(BitcoinCommands.GetBlockchainInfo); if (infos.Length > 0) @@ -302,6 +310,50 @@ protected virtual void SetupCrypto() ShareMultiplier = coinProps.ShareMultiplier; } + protected virtual async Task AreDaemonsHealthyLegacyAsync() + { + var responses = await daemon.ExecuteCmdAllAsync(BitcoinCommands.GetInfo); + + if (responses.Where(x => x.Error?.InnerException?.GetType() == typeof(DaemonClientException)) + .Select(x => (DaemonClientException)x.Error.InnerException) + .Any(x => x.Code == HttpStatusCode.Unauthorized)) + logger.ThrowLogPoolStartupException($"Daemon reports invalid credentials", LogCat); + + return responses.All(x => x.Error == null); + } + + protected virtual async Task AreDaemonsConnectedLegacyAsync() + { + var response = await daemon.ExecuteCmdAnyAsync(BitcoinCommands.GetInfo); + + return response.Error == null && response.Response.Connections > 0; + } + + protected virtual async Task ShowDaemonSyncProgressLegacyAsync() + { + var infos = await daemon.ExecuteCmdAllAsync(BitcoinCommands.GetInfo); + + if (infos.Length > 0) + { + var blockCount = infos + .Max(x => x.Response?.Blocks); + + if (blockCount.HasValue) + { + // get list of peers and their highest block height to compare to ours + var peerInfo = await daemon.ExecuteCmdAnyAsync(BitcoinCommands.GetPeerInfo); + var peers = peerInfo.Response; + + if (peers != null && peers.Length > 0) + { + var totalBlocks = peers.Max(x => x.StartingHeight); + var percent = totalBlocks > 0 ? (double)blockCount / totalBlocks * 100 : 0; + logger.Info(() => $"[{LogCat}] Daemons have downloaded {percent:0.00}% of blockchain from {peers.Length} peers"); + } + } + } + } + #region API-Surface public IObservable Jobs { get; private set; } @@ -453,6 +505,8 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi if (extraPoolConfig?.MaxActiveJobs.HasValue == true) maxActiveJobs = extraPoolConfig.MaxActiveJobs.Value; + hasLegacyDaemon = extraPoolConfig?.HasLegacyDaemon == true; + base.Configure(poolConfig, clusterConfig); } @@ -464,20 +518,26 @@ protected override void ConfigureDaemons() daemon.Configure(poolConfig.Daemons); } - protected override async Task AreDaemonsHealthy() + protected override async Task AreDaemonsHealthyAsync() { + if (hasLegacyDaemon) + return await AreDaemonsHealthyLegacyAsync(); + var responses = await daemon.ExecuteCmdAllAsync(BitcoinCommands.GetBlockchainInfo); if (responses.Where(x => x.Error?.InnerException?.GetType() == typeof(DaemonClientException)) - .Select(x => (DaemonClientException) x.Error.InnerException) + .Select(x => (DaemonClientException)x.Error.InnerException) .Any(x => x.Code == HttpStatusCode.Unauthorized)) logger.ThrowLogPoolStartupException($"Daemon reports invalid credentials", LogCat); return responses.All(x => x.Error == null); } - protected override async Task AreDaemonsConnected() + protected override async Task AreDaemonsConnectedAsync() { + if (hasLegacyDaemon) + return await AreDaemonsConnectedLegacyAsync(); + var response = await daemon.ExecuteCmdAnyAsync(BitcoinCommands.GetNetworkInfo); return response.Error == null && response.Response.Connections > 0; @@ -520,7 +580,7 @@ protected override async Task PostStartInitAsync() new DaemonCmd(BitcoinCommands.ValidateAddress, new[] { poolConfig.Address }), new DaemonCmd(BitcoinCommands.GetDifficulty), new DaemonCmd(BitcoinCommands.SubmitBlock), - new DaemonCmd(BitcoinCommands.GetBlockchainInfo) + new DaemonCmd(!hasLegacyDaemon ? BitcoinCommands.GetBlockchainInfo : BitcoinCommands.GetInfo) }; var results = await daemon.ExecuteBatchAnyAsync(commands); @@ -539,7 +599,8 @@ protected override async Task PostStartInitAsync() var validateAddressResponse = results[0].Response.ToObject(); var difficultyResponse = results[1].Response.ToObject(); var submitBlockResponse = results[2]; - var blockchainInfoResponse = results[3].Response.ToObject(); + var blockchainInfoResponse = !hasLegacyDaemon ? results[3].Response.ToObject() : null; + var daemonInfoResponse = hasLegacyDaemon ? results[3].Response.ToObject() : null; // ensure pool owns wallet if (!validateAddressResponse.IsValid) @@ -557,12 +618,18 @@ protected override async Task PostStartInitAsync() poolAddressDestination = AddressToDestination(validateAddressResponse.Address); // chain detection - if (blockchainInfoResponse.Chain.ToLower() == "test") - networkType = BitcoinNetworkType.Test; - else if (blockchainInfoResponse.Chain.ToLower() == "regtest") - networkType = BitcoinNetworkType.RegTest; + 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 = BitcoinNetworkType.Main; + networkType = daemonInfoResponse.Testnet ? BitcoinNetworkType.Test : BitcoinNetworkType.Main; ConfigureRewards(); diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index ac9fe52b1..9e8cc2111 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -35,6 +35,7 @@ public class BitcoinProperties private static readonly IHashAlgorithm sha256D = new Sha256D(); private static readonly IHashAlgorithm sha256DReverse = new DigestReverser(sha256D); private static readonly IHashAlgorithm x11 = new X11(); + private static readonly IHashAlgorithm blake2s = new Blake2s(); private static readonly IHashAlgorithm x17 = new X17(); private static readonly IHashAlgorithm groestl = new Groestl(); private static readonly IHashAlgorithm lyra2Rev2 = new Lyra2Rev2(); @@ -62,6 +63,9 @@ public class BitcoinProperties private static readonly BitcoinCoinProperties x11Coin = new BitcoinCoinProperties(1, sha256D, x11, new DigestReverser(x11), "X11"); + private static readonly BitcoinCoinProperties blake2sCoin = + new BitcoinCoinProperties(1, sha256D, blake2s, new DigestReverser(blake2s), "Blake2s"); + private static readonly BitcoinCoinProperties skeinCoin = new BitcoinCoinProperties(1, sha256D, skein, sha256DReverse, "Skein"); @@ -171,7 +175,7 @@ private static BitcoinCoinProperties GetVergeProperties(string algorithm) return x17Coin; case "blake2s": - throw new NotSupportedException($"algorithm {algorithm} not yet supported"); + return blake2sCoin; default: // scrypt return scryptCoin; diff --git a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs index 56607d93c..7f3577292 100644 --- a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs +++ b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs @@ -22,6 +22,15 @@ namespace MiningCore.Blockchain.Bitcoin.Configuration { public class BitcoinPoolConfigExtra { + /// + /// Maximum number of tracked jobs. + /// Default: 12 - you should increase this value if your blockrefreshinterval is higher than 300ms + /// public int? MaxActiveJobs { get; set; } + + /// + /// Set to true to limit RPC commands to old Bitcoin command set + /// + public bool? HasLegacyDaemon { get; set; } } } diff --git a/src/MiningCore/Blockchain/Bitcoin/DaemonResponses/GetInfoResponse.cs b/src/MiningCore/Blockchain/Bitcoin/DaemonResponses/GetInfoResponse.cs new file mode 100644 index 000000000..153c6557c --- /dev/null +++ b/src/MiningCore/Blockchain/Bitcoin/DaemonResponses/GetInfoResponse.cs @@ -0,0 +1,33 @@ +/* +Copyright 2017 Coin Foundry (coinfoundry.org) +Authors: Oliver Weichhold (oliver@weichhold.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +namespace MiningCore.Blockchain.Bitcoin.DaemonResponses +{ + public class DaemonInfo + { + public string Version { get; set; } + public int ProtocolVersion { get; set; } + public int WalletVersion { get; set; } + public decimal Balance { get; set; } + public ulong Blocks { get; set; } + public bool Testnet { get; set; } + public int Connections { get; set; } + } +} diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs index 93f5a5d4d..b1fba4620 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs @@ -454,7 +454,7 @@ protected override void ConfigureDaemons() daemon.Configure(daemonEndpoints); } - protected override async Task AreDaemonsHealthy() + protected override async Task AreDaemonsHealthyAsync() { var responses = await daemon.ExecuteCmdAllAsync(EC.GetBlockByNumber, new[] { (object) "pending", true }); @@ -466,7 +466,7 @@ protected override async Task AreDaemonsHealthy() return responses.All(x => x.Error == null); } - protected override async Task AreDaemonsConnected() + protected override async Task AreDaemonsConnectedAsync() { var response = await daemon.ExecuteCmdAnyAsync(EC.GetPeerCount); diff --git a/src/MiningCore/Blockchain/JobManagerBase.cs b/src/MiningCore/Blockchain/JobManagerBase.cs index fa683e422..6b9af018d 100644 --- a/src/MiningCore/Blockchain/JobManagerBase.cs +++ b/src/MiningCore/Blockchain/JobManagerBase.cs @@ -55,7 +55,7 @@ protected JobManagerBase(IComponentContext ctx) protected virtual async Task StartDaemonAsync() { - while(!await AreDaemonsHealthy()) + while(!await AreDaemonsHealthyAsync()) { logger.Info(() => $"[{LogCat}] Waiting for daemons to come online ..."); @@ -64,7 +64,7 @@ protected virtual async Task StartDaemonAsync() logger.Info(() => $"[{LogCat}] All daemons online"); - while(!await AreDaemonsConnected()) + while(!await AreDaemonsConnectedAsync()) { logger.Info(() => $"[{LogCat}] Waiting for daemons to connect to peers ..."); @@ -83,8 +83,8 @@ protected string NextJobId(string format = null) return value.ToStringHex8(); } - protected abstract Task AreDaemonsHealthy(); - protected abstract Task AreDaemonsConnected(); + protected abstract Task AreDaemonsHealthyAsync(); + protected abstract Task AreDaemonsConnectedAsync(); protected abstract Task EnsureDaemonsSynchedAsync(); protected abstract Task PostStartInitAsync(); diff --git a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs index a3de79acd..8c3bcc98a 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs @@ -326,7 +326,7 @@ protected override void ConfigureDaemons() walletDaemon.Configure(walletDaemonEndpoints, MoneroConstants.DaemonRpcLocation); } - protected override async Task AreDaemonsHealthy() + protected override async Task AreDaemonsHealthyAsync() { // test daemons var responses = await daemon.ExecuteCmdAllAsync(MC.GetInfo); @@ -350,7 +350,7 @@ protected override async Task AreDaemonsHealthy() return responses2.All(x => x.Error == null); } - protected override async Task AreDaemonsConnected() + protected override async Task AreDaemonsConnectedAsync() { var response = await daemon.ExecuteCmdAnyAsync(MC.GetInfo); From 81b295e8f23e4528552f7a768e2421fadc78efe6 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 14 Feb 2018 10:00:05 +0100 Subject: [PATCH 063/348] Neoscrypt fix --- src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index 9e8cc2111..4693400f3 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -79,7 +79,7 @@ public class BitcoinProperties new BitcoinCoinProperties(1, new DummyHasher(), sha256D, sha256DReverse, "Equihash"); private static readonly BitcoinCoinProperties neoScryptCoin = - new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, neoScryptProfile1, sha256DReverse, "Neoscrypt"); + new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, neoScryptProfile1, new DigestReverser(neoScryptProfile1), "Neoscrypt"); private static readonly BitcoinCoinProperties x17Coin = new BitcoinCoinProperties(1, sha256D, x17, new DigestReverser(x17), "X17"); From c788a7b4c03995b3e10e175380ed793f38000b2a Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 14 Feb 2018 15:58:48 +0100 Subject: [PATCH 064/348] Refactor --- src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs | 3 +-- src/MiningCore/Blockchain/Ethereum/EthereumPool.cs | 3 +-- src/MiningCore/Blockchain/Monero/MoneroPool.cs | 3 +-- src/MiningCore/Mining/PoolBase.cs | 11 ++++++++--- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs index 9bfb4aec1..87942401e 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs @@ -183,8 +183,7 @@ protected virtual async Task OnSubmitAsync(StratumClient client, Timestamped $"[{LogCat}] [{client.ConnectionId}] Share rejected: {ex.Code}"); // banning - if (poolConfig.Banning?.Enabled == true && clusterConfig.Banning?.BanOnInvalidShares == true) - ConsiderBan(client, context, poolConfig.Banning); + ConsiderBan(client, context, poolConfig.Banning); } } diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs index 7a50087f4..f1a3b8f5a 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs @@ -198,8 +198,7 @@ private async Task OnSubmitAsync(StratumClient client, Timestamped $"[{LogCat}] [{client.ConnectionId}] Share rejected: {ex.Code}"); // banning - if (poolConfig.Banning?.Enabled == true && clusterConfig.Banning?.BanOnInvalidShares == true) - ConsiderBan(client, context, poolConfig.Banning); + ConsiderBan(client, context, poolConfig.Banning); } } diff --git a/src/MiningCore/Blockchain/Monero/MoneroPool.cs b/src/MiningCore/Blockchain/Monero/MoneroPool.cs index 635d81f03..ca23a8503 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPool.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPool.cs @@ -256,8 +256,7 @@ private async Task OnSubmitAsync(StratumClient client, Timestamped $"[{LogCat}] [{client.ConnectionId}] Share rejected: {ex.Message}"); // banning - if (poolConfig.Banning?.Enabled == true && clusterConfig.Banning?.BanOnInvalidShares == true) - ConsiderBan(client, context, poolConfig.Banning); + ConsiderBan(client, context, poolConfig.Banning); } } diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index a94667b21..d4c731046 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -355,11 +355,16 @@ protected void ConsiderBan(StratumClient client, WorkerContextBase context, Pool else { - logger.Info(() => $"[{LogCat}] [{client.ConnectionId}] Banning worker for {config.Time} sec: {Math.Floor(ratioBad * 100)}% of the last {totalShares} shares were invalid"); + if (poolConfig.Banning?.Enabled == true && + (clusterConfig.Banning?.BanOnInvalidShares.HasValue == false || + clusterConfig.Banning?.BanOnInvalidShares == true)) + { + logger.Info(() => $"[{LogCat}] [{client.ConnectionId}] Banning worker for {config.Time} sec: {Math.Floor(ratioBad * 100)}% of the last {totalShares} shares were invalid"); - banManager.Ban(client.RemoteEndpoint.Address, TimeSpan.FromSeconds(config.Time)); + banManager.Ban(client.RemoteEndpoint.Address, TimeSpan.FromSeconds(config.Time)); - DisconnectClient(client); + DisconnectClient(client); + } } } } From a4eb2d11897fa4b2dafe1859d3218ad0bf059a60 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Feb 2018 15:46:18 +0100 Subject: [PATCH 065/348] Legacy daemon fix --- .../Blockchain/Bitcoin/BitcoinConstants.cs | 3 +- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 31 +++++++++++++++++-- .../DaemonResponses/GetInfoResponse.cs | 1 + .../Persistence/Postgres/Scripts/createdb.sql | 1 - 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs index 41f90ebdf..9ab266014 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs @@ -110,7 +110,6 @@ public static class BitcoinCommands public const string GetMiningInfo = "getmininginfo"; public const string GetPeerInfo = "getpeerinfo"; public const string ValidateAddress = "validateaddress"; - public const string GetDifficulty = "getdifficulty"; public const string GetBlockTemplate = "getblocktemplate"; public const string GetBlockSubsidy = "getblocksubsidy"; public const string SubmitBlock = "submitblock"; @@ -121,5 +120,7 @@ public static class BitcoinCommands // Legacy commands public const string GetInfo = "getinfo"; + public const string GetDifficulty = "getdifficulty"; + public const string GetConnectionCount = "getconnectioncount"; } } diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 0bd2a08fe..1af4ecdd2 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -257,7 +257,6 @@ private async Task UpdateNetworkStatsAsync() var networkInfoResponse = results[2].Response.ToObject(); BlockchainStats.BlockHeight = infoResponse.Blocks; - BlockchainStats.NetworkDifficulty = miningInfoResponse.Difficulty; BlockchainStats.NetworkHashRate = miningInfoResponse.NetworkHashps; BlockchainStats.ConnectedPeers = networkInfoResponse.Connections; } @@ -354,6 +353,31 @@ protected virtual async Task ShowDaemonSyncProgressLegacyAsync() } } + private async Task UpdateNetworkStatsLegacyAsync() + { + logger.LogInvoke(LogCat); + + var results = await daemon.ExecuteBatchAnyAsync( + new DaemonCmd(BitcoinCommands.GetMiningInfo), + new DaemonCmd(BitcoinCommands.GetConnectionCount) + ); + + if (results.Any(x => x.Error != null)) + { + var errors = results.Where(x => x.Error != null).ToArray(); + + if (errors.Any()) + logger.Warn(() => $"[{LogCat}] Error(s) refreshing network stats: {string.Join(", ", errors.Select(y => y.Error.Message))}"); + } + + var miningInfoResponse = results[0].Response.ToObject(); + var connectionCountResponse = results[1].Response.ToObject(); + + BlockchainStats.BlockHeight = miningInfoResponse.Blocks; + //BlockchainStats.NetworkHashRate = miningInfoResponse.NetworkHashps; + BlockchainStats.ConnectedPeers = (int) (long) connectionCountResponse; + } + #region API-Surface public IObservable Jobs { get; private set; } @@ -645,7 +669,10 @@ protected override async Task PostStartInitAsync() else logger.ThrowLogPoolStartupException($"Unable detect block submission RPC method", LogCat); - await UpdateNetworkStatsAsync(); + if(!hasLegacyDaemon) + await UpdateNetworkStatsAsync(); + else + await UpdateNetworkStatsLegacyAsync(); SetupCrypto(); SetupJobUpdates(); diff --git a/src/MiningCore/Blockchain/Bitcoin/DaemonResponses/GetInfoResponse.cs b/src/MiningCore/Blockchain/Bitcoin/DaemonResponses/GetInfoResponse.cs index 153c6557c..f4aa9a7d0 100644 --- a/src/MiningCore/Blockchain/Bitcoin/DaemonResponses/GetInfoResponse.cs +++ b/src/MiningCore/Blockchain/Bitcoin/DaemonResponses/GetInfoResponse.cs @@ -29,5 +29,6 @@ public class DaemonInfo public ulong Blocks { get; set; } public bool Testnet { get; set; } public int Connections { get; set; } + public double Difficulty { get; set; } } } diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index 8972b09b8..4647353c3 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -18,7 +18,6 @@ CREATE TABLE shares CREATE INDEX IDX_SHARES_POOL_BLOCK on shares(poolid, blockheight); CREATE INDEX IDX_SHARES_POOL_MINER on shares(poolid, miner); CREATE INDEX IDX_SHARES_POOL_CREATED ON shares(poolid, created); -CREATE INDEX IDX_SHARES_POOL_MINER_DIFF on shares(poolid, miner, difficulty); CREATE TABLE blocks ( From ef387cb1b92a8306f2a53f9ed58d6e6a009fc8e6 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Feb 2018 16:09:32 +0100 Subject: [PATCH 066/348] WIP --- src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index 4693400f3..e9e360695 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -165,16 +165,16 @@ private static BitcoinCoinProperties GetVergeProperties(string algorithm) switch (algorithm.ToLower()) { - case "lyra2rev2": + case "lyra": return lyra2Rev2CoinVariantA; - case "groestl-myriad": + case "groestl": return groestlMyriadCoin; case "x17": return x17Coin; - case "blake2s": + case "blake": return blake2sCoin; default: // scrypt From 1ac0c9b60646404fed15a074ca069bfffdb4af8b Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Feb 2018 16:15:05 +0100 Subject: [PATCH 067/348] improved POW/POS check --- src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 1af4ecdd2..5af9d25ec 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -633,7 +633,8 @@ protected override async Task PostStartInitAsync() if (!validateAddressResponse.IsMine) logger.ThrowLogPoolStartupException($"Daemon does not own pool-address '{poolConfig.Address}'", LogCat); - isPoS = difficultyResponse.Values().Any(x => x.Path == "proof-of-stake"); + isPoS = difficultyResponse.Values().Any(x => x.Path == "proof-of-stake") && + !difficultyResponse.Values().Any(x => x.Path == "proof-of-work"); // Create pool address script from response if (isPoS) From f2041c23ab28673b55f7875763df329a7853e700 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Feb 2018 17:39:36 +0100 Subject: [PATCH 068/348] Disable bitcoin proof-of-stake auto-detect and moved to config parameter --- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 12 ++++-------- .../Bitcoin/Configuration/BitcoinPoolConfigExtra.cs | 5 +++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 5af9d25ec..7124e640d 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -530,6 +530,7 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi maxActiveJobs = extraPoolConfig.MaxActiveJobs.Value; hasLegacyDaemon = extraPoolConfig?.HasLegacyDaemon == true; + isPoS = extraPoolConfig?.IsPoS == true; base.Configure(poolConfig, clusterConfig); } @@ -602,7 +603,6 @@ 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(!hasLegacyDaemon ? BitcoinCommands.GetBlockchainInfo : BitcoinCommands.GetInfo) }; @@ -621,10 +621,9 @@ protected override async Task PostStartInitAsync() // extract results var validateAddressResponse = results[0].Response.ToObject(); - var difficultyResponse = results[1].Response.ToObject(); - var submitBlockResponse = results[2]; - var blockchainInfoResponse = !hasLegacyDaemon ? results[3].Response.ToObject() : null; - var daemonInfoResponse = hasLegacyDaemon ? results[3].Response.ToObject() : null; + var submitBlockResponse = results[1]; + var blockchainInfoResponse = !hasLegacyDaemon ? results[2].Response.ToObject() : null; + var daemonInfoResponse = hasLegacyDaemon ? results[2].Response.ToObject() : null; // ensure pool owns wallet if (!validateAddressResponse.IsValid) @@ -633,9 +632,6 @@ protected override async Task PostStartInitAsync() if (!validateAddressResponse.IsMine) logger.ThrowLogPoolStartupException($"Daemon does not own pool-address '{poolConfig.Address}'", LogCat); - isPoS = difficultyResponse.Values().Any(x => x.Path == "proof-of-stake") && - !difficultyResponse.Values().Any(x => x.Path == "proof-of-work"); - // Create pool address script from response if (isPoS) poolAddressDestination = new PubKey(validateAddressResponse.PubKey); diff --git a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs index 7f3577292..314ed261e 100644 --- a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs +++ b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs @@ -32,5 +32,10 @@ public class BitcoinPoolConfigExtra /// Set to true to limit RPC commands to old Bitcoin command set /// public bool? HasLegacyDaemon { get; set; } + + /// + /// Set to true to indicate Prof-of-stake + /// + public bool? IsPoS { get; set; } } } From fe120fae5bca65b40fec452889d650c1dc5e0085 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Feb 2018 20:21:39 +0100 Subject: [PATCH 069/348] WIP --- .../Api/Extensions/MiningPoolExtensions.cs | 2 +- .../Blockchain/Bitcoin/BitcoinProperties.cs | 6 ++++-- src/MiningCore/Blockchain/CoinMetaData.cs | 18 +++++++++--------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs b/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs index 96dacf9ad..f50c82510 100644 --- a/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs +++ b/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs @@ -47,7 +47,7 @@ private static string GetPoolAlgorithm(PoolConfig pool) if (string.IsNullOrEmpty(result)) { if (CoinMetaData.CoinAlgorithm.TryGetValue(pool.Coin.Type, out var getter)) - result = getter(pool.Coin.Type); + result = getter(pool.Coin.Type, pool.Coin.Algorithm); } // Capitalize diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index e9e360695..5a104d98f 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -182,9 +182,11 @@ private static BitcoinCoinProperties GetVergeProperties(string algorithm) } } - public static string GetAlgorithm(CoinType coin) + public static string GetAlgorithm(CoinType coin, string configuredAlgorithm) { - if (coinProperties.TryGetValue(coin, out var props)) + var props = GetCoinProperties(coin, configuredAlgorithm); + + if (props != null) return props.Algorithm; return string.Empty; diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index a60bd6cfc..d29fa8c2d 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -77,7 +77,7 @@ public static class CoinMetaData { CoinType.GBX, "http://gobyte.ezmine.io/tx/{0}" }, { CoinType.CRC, "http://explorer.cryptopros.us/tx/{0}" }, }; - + public static readonly Dictionary AddressInfoLinks = new Dictionary { { CoinType.ETH, "https://etherscan.io/address/{0}" }, @@ -110,10 +110,10 @@ public static class CoinMetaData private const string Cryptonight = "Cryptonight"; private const string CryptonightLight = "Cryptonight-Light"; - public static readonly Dictionary> CoinAlgorithm = new Dictionary> + public static readonly Dictionary> CoinAlgorithm = new Dictionary> { - { CoinType.ETH, (coin)=> Ethash }, - { CoinType.ETC, (coin)=> Ethash }, + { CoinType.ETH, (coin, alg)=> Ethash }, + { CoinType.ETC, (coin, alg)=> Ethash }, { CoinType.LTC, BitcoinProperties.GetAlgorithm }, { CoinType.BCH, BitcoinProperties.GetAlgorithm }, { CoinType.DASH, BitcoinProperties.GetAlgorithm }, @@ -131,13 +131,13 @@ public static class CoinMetaData { CoinType.GLT, BitcoinProperties.GetAlgorithm }, { CoinType.VTC, BitcoinProperties.GetAlgorithm }, { CoinType.BTG, BitcoinProperties.GetAlgorithm }, - { CoinType.ELLA, (coin)=> Ethash }, - { CoinType.EXP, (coin)=> Ethash }, + { CoinType.ELLA, (coin, alg)=> Ethash }, + { CoinType.EXP, (coin, alg)=> Ethash }, { CoinType.MOON, BitcoinProperties.GetAlgorithm }, { CoinType.XVG, BitcoinProperties.GetAlgorithm }, - { CoinType.XMR, (coin)=> Cryptonight }, - { CoinType.ETN, (coin)=> Cryptonight }, - { CoinType.AEON, (coin)=> CryptonightLight }, + { CoinType.XMR, (coin, alg)=> Cryptonight }, + { CoinType.ETN, (coin, alg)=> Cryptonight }, + { CoinType.AEON, (coin, alg)=> CryptonightLight }, { CoinType.GBX, BitcoinProperties.GetAlgorithm }, { CoinType.CRC, BitcoinProperties.GetAlgorithm }, }; From 70c4433a488fdb09eb608478b45cee033c235899 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Feb 2018 21:58:13 +0100 Subject: [PATCH 070/348] WIP --- src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs index 87942401e..8e372ac26 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs @@ -354,7 +354,9 @@ public override double HashrateFromShares(double shares, double interval) var result = shares * multiplier / interval; // OW: tmp hotfix - if (poolConfig.Coin.Type == CoinType.MONA || poolConfig.Coin.Type == CoinType.VTC || poolConfig.Coin.Type == CoinType.STAK) + if (poolConfig.Coin.Type == CoinType.MONA || poolConfig.Coin.Type == CoinType.VTC || + poolConfig.Coin.Type == CoinType.STAK || + (poolConfig.Coin.Type == CoinType.XVG && poolConfig.Coin.Algorithm.ToLower() == "lyra")) result *= 4; return result; From 6894fad169c06caf82392a4ea48b261552dd5d15 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 16 Feb 2018 11:58:15 +0100 Subject: [PATCH 071/348] Scrap stats pre-agg --- src/MiningCore/Mining/StatsRecorder.cs | 84 ------------------- .../Postgres/Repositories/StatsRepository.cs | 28 +------ .../Persistence/Postgres/Scripts/createdb.sql | 15 ---- .../Repositories/IStatsRepository.cs | 2 - 4 files changed, 3 insertions(+), 126 deletions(-) diff --git a/src/MiningCore/Mining/StatsRecorder.cs b/src/MiningCore/Mining/StatsRecorder.cs index 601e0d16c..5c6a38856 100644 --- a/src/MiningCore/Mining/StatsRecorder.cs +++ b/src/MiningCore/Mining/StatsRecorder.cs @@ -109,36 +109,6 @@ public void Start() thread1.Name = "StatsRecorder"; thread1.Start(); - - thread2 = new Thread(() => - { - // warm-up delay - Thread.Sleep(TimeSpan.FromSeconds(10)); - - var interval = TimeSpan.FromMinutes(15); - - while (true) - { - try - { - UpdatePoolPreAggs(); - } - - catch (Exception ex) - { - logger.Error(ex); - } - - var waitResult = stopEvent.WaitOne(interval); - - // check if stop was signalled - if (waitResult) - break; - } - }); - - thread2.Name = "StatsRecorder - PreAggs"; - thread2.Start(); } public void Stop() @@ -243,60 +213,6 @@ private void UpdatePoolHashrates() } } - private void UpdatePoolPreAggs() - { - var start = clock.Now; - - var stats = new MinerWorkerStatsPreAgg - { - Created = start, - Updated = start, - }; - - var sw = new Stopwatch(); - - foreach (var poolId in pools.Keys) - { - stats.PoolId = poolId; - - logger.Info(() => $"Updating pre-aggregated miner stats for pool {poolId}"); - - // fetch stats - var result = readFaultPolicy.Execute(() => - cf.Run(con => shareRepo.GetAccumulatedShareDifficultyTotal(con, poolId), sw, logger)); - - var byMiner = result.GroupBy(x => x.Miner).ToArray(); - - // update miner, worker - foreach (var minerHashes in byMiner) - { - cf.RunTx((con, tx) => - { - stats.Miner = minerHashes.Key; - - foreach (var item in minerHashes) - { - // update - stats.Worker = item.Worker ?? string.Empty; - stats.ShareCount = item.Count; - stats.SharesAccumulated = item.Sum; - - // persist - statsRepo.UpdateMinerWorkerStatsPreAgg(con, tx, stats); - } - - // delete records for workers that are no longer producing shares - var activeWorkers = minerHashes - .Select(x => x.Worker ?? string.Empty) - .Distinct() - .ToArray(); - - statsRepo.DeleteMinerWorkerStatsExcept(con, tx, poolId, stats.Miner, activeWorkers); - }); - } - } - } - private void BuildFaultHandlingPolicy() { var retry = Policy diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index b6c342dcc..215e21b1c 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -119,9 +119,9 @@ public MinerStats GetMinerStats(IDbConnection con, IDbTransaction tx, string poo { logger.LogInvoke(new[] { poolId, miner }); - var query = "SELECT (SELECT SUM(sharesaccumulated) FROM minerstats_pre_agg WHERE poolid = @poolId AND miner = @miner) AS pendingshares, " + - "(SELECT amount FROM balances WHERE poolid = @poolId AND address = @miner) AS pendingbalance, " + - "(SELECT SUM(amount) FROM payments WHERE poolid = @poolId and address = @miner) as totalpaid"; + var query = "SELECT (SELECT SUM(difficulty) FROM shares WHERE poolid = @poolId AND miner = @miner) AS pendingshares, " + + "(SELECT amount FROM balances WHERE poolid = @poolId AND address = @miner) AS pendingbalance, " + + "(SELECT SUM(amount) FROM payments WHERE poolid = @poolId and address = @miner) as totalpaid"; var result = con.QuerySingleOrDefault(query, new { poolId, miner }, tx); @@ -246,28 +246,6 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConn return result; } - public void UpdateMinerWorkerStatsPreAgg(IDbConnection con, IDbTransaction tx, MinerWorkerStatsPreAgg stats) - { - logger.LogInvoke(); - - var query = "INSERT INTO minerstats_pre_agg(poolid, miner, worker, sharecount, sharesaccumulated, created, updated) " + - "VALUES(@poolid, @miner, @worker, @sharecount, @sharesaccumulated, @created, @updated)" + - "ON CONFLICT ON CONSTRAINT minerstats_pre_agg_pkey " + - "DO UPDATE SET " + - "sharecount = @sharecount, sharesaccumulated = @sharesaccumulated, updated = @updated"; - - con.Execute(query, stats, tx); - } - - public void DeleteMinerWorkerStatsExcept(IDbConnection con, IDbTransaction tx, string poolId, string miner, string[] activeWorkers) - { - logger.LogInvoke(); - - var query = "DELETE FROM minerstats_pre_agg WHERE poolid = @poolid AND miner = @miner AND NOT(worker = ANY(@activeWorkers))"; - - con.Execute(query, new { poolId, miner, activeWorkers }, tx); - } - public MinerWorkerPerformanceStats[] PagePoolMinersByHashrate(IDbConnection con, string poolId, DateTime from, int page, int pageSize) { logger.LogInvoke(new[] { (object) poolId, from, page, pageSize }); diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index 4647353c3..efc8fdbc4 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -94,18 +94,3 @@ CREATE INDEX IDX_MINERSTATS_POOL_CREATED on minerstats(poolid, created); CREATE INDEX IDX_MINERSTATS_POOL_MINER_CREATED on minerstats(poolid, miner, created); CREATE INDEX IDX_MINERSTATS_POOL_MINER_CREATED_HOUR on minerstats(poolid, miner, date_trunc('hour',created)); CREATE INDEX IDX_MINERSTATS_POOL_MINER_CREATED_DAY on minerstats(poolid, miner, date_trunc('day',created)); - -CREATE TABLE minerstats_pre_agg -( - poolid TEXT NOT NULL, - miner TEXT NOT NULL, - worker TEXT NOT NULL, - - sharecount BIGINT NOT NULL, - sharesaccumulated DOUBLE PRECISION NOT NULL, - - created TIMESTAMP NOT NULL, - updated TIMESTAMP NOT NULL, - - primary key(poolid, miner, worker) -); diff --git a/src/MiningCore/Persistence/Repositories/IStatsRepository.cs b/src/MiningCore/Persistence/Repositories/IStatsRepository.cs index 338a587d5..e6e4c8829 100644 --- a/src/MiningCore/Persistence/Repositories/IStatsRepository.cs +++ b/src/MiningCore/Persistence/Repositories/IStatsRepository.cs @@ -37,7 +37,5 @@ public interface IStatsRepository MinerWorkerPerformanceStats[] PagePoolMinersByHashrate(IDbConnection con, string poolId, DateTime from, int page, int pageSize); WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenHourly(IDbConnection con, string poolId, string miner, DateTime start, DateTime end); WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConnection con, string poolId, string miner, DateTime start, DateTime end); - void UpdateMinerWorkerStatsPreAgg(IDbConnection con, IDbTransaction tx, MinerWorkerStatsPreAgg stats); - void DeleteMinerWorkerStatsExcept(IDbConnection con, IDbTransaction tx, string poolId, string miner, string[] activeWorkers); } } From a8f389994ef2910e0e124a03d9c57c3f0e362b08 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 16 Feb 2018 12:00:21 +0100 Subject: [PATCH 072/348] WIP --- src/MiningCore/Mining/StatsRecorder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/MiningCore/Mining/StatsRecorder.cs b/src/MiningCore/Mining/StatsRecorder.cs index 5c6a38856..f9da24f65 100644 --- a/src/MiningCore/Mining/StatsRecorder.cs +++ b/src/MiningCore/Mining/StatsRecorder.cs @@ -58,7 +58,6 @@ public StatsRecorder(IComponentContext ctx, private const int MinHashrateCalculationWindow = 300; // seconds private ClusterConfig clusterConfig; private Thread thread1; - private Thread thread2; private const int RetryCount = 4; private Policy readFaultPolicy; From 10b168c0b41b739cf0c35322f3bd8d9bf166c15d Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 16 Feb 2018 13:15:26 +0100 Subject: [PATCH 073/348] WIP --- src/MiningCore/Api/Extensions/MiningPoolExtensions.cs | 9 +++------ src/MiningCore/Program.cs | 1 + 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs b/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs index f50c82510..4ca943b59 100644 --- a/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs +++ b/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs @@ -42,13 +42,10 @@ public static PoolInfo ToPoolInfo(this PoolConfig pool, IMapper mapper, Persiste private static string GetPoolAlgorithm(PoolConfig pool) { - var result = pool.Coin.Algorithm; + string result = null; - if (string.IsNullOrEmpty(result)) - { - if (CoinMetaData.CoinAlgorithm.TryGetValue(pool.Coin.Type, out var getter)) - result = getter(pool.Coin.Type, pool.Coin.Algorithm); - } + if (CoinMetaData.CoinAlgorithm.TryGetValue(pool.Coin.Type, out var getter)) + result = getter(pool.Coin.Type, pool.Coin.Algorithm); // Capitalize if (!string.IsNullOrEmpty(result) && result.Length > 1) diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index d8b85b704..28135790a 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -39,6 +39,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using Microsoft.Extensions.CommandLineUtils; using MiningCore.Api; using MiningCore.Api.Responses; +using MiningCore.Blockchain; using MiningCore.Configuration; using MiningCore.Crypto.Hashing.Algorithms; using MiningCore.Crypto.Hashing.Equihash; From 61d99373ab9cd4fcbc1fb3d382b3c576c0d2d570 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 17 Feb 2018 14:53:51 +0100 Subject: [PATCH 074/348] Trigger based share pre-agg --- .../Persistence/Postgres/Scripts/createdb.sql | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index efc8fdbc4..2429d9a99 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -94,3 +94,70 @@ CREATE INDEX IDX_MINERSTATS_POOL_CREATED on minerstats(poolid, created); CREATE INDEX IDX_MINERSTATS_POOL_MINER_CREATED on minerstats(poolid, miner, created); CREATE INDEX IDX_MINERSTATS_POOL_MINER_CREATED_HOUR on minerstats(poolid, miner, date_trunc('hour',created)); CREATE INDEX IDX_MINERSTATS_POOL_MINER_CREATED_DAY on minerstats(poolid, miner, date_trunc('day',created)); + +CREATE TABLE minerstats_pre_agg +( + poolid TEXT NOT NULL, + miner TEXT NOT NULL, + worker TEXT NOT NULL, + + sharecount BIGINT NOT NULL, + sharesaccumulated DOUBLE PRECISION NOT NULL, + + created TIMESTAMP NOT NULL, + updated TIMESTAMP NOT NULL, + + primary key(poolid, miner, worker) +); + +CREATE OR REPLACE FUNCTION aggregate_shares_diff_insert() RETURNS TRIGGER AS +$BODY$ +BEGIN + INSERT INTO minerstats_pre_agg AS d (poolid, miner, worker, sharecount, sharesaccumulated, created, updated) + VALUES(new.poolid, new.miner, COALESCE(new.worker, ''), 1, new.difficulty, now() at time zone 'utc', now() at time zone 'utc') + ON CONFLICT ON CONSTRAINT minerstats_pre_agg_pkey + DO UPDATE SET + sharecount = d.sharecount + 1, sharesaccumulated = d.sharesaccumulated + new.difficulty, updated = now() at time zone 'utc'; + + RETURN new; +END; +$BODY$ +language plpgsql; + +CREATE TRIGGER TRIG_SHARES_AGGREGATE_DIFF_INSERT +AFTER INSERT ON shares +FOR EACH ROW +EXECUTE PROCEDURE aggregate_shares_diff_insert(); + +CREATE OR REPLACE FUNCTION aggregate_shares_diff_delete() RETURNS TRIGGER AS +$BODY$ +BEGIN + UPDATE minerstats_pre_agg SET + sharecount = GREATEST(sharecount - 1, 0), sharesaccumulated = GREATEST(sharesaccumulated - old.difficulty, 0), updated = now() at time zone 'utc' + WHERE poolid = old.poolid AND miner = old.miner AND worker = old.worker; + + RETURN new; +END; +$BODY$ +language plpgsql; + +CREATE TRIGGER TRIG_SHARES_AGGREGATE_DIFF_DELETE +AFTER DELETE ON shares +FOR EACH ROW +EXECUTE PROCEDURE aggregate_shares_diff_delete(); + +CREATE OR REPLACE FUNCTION setup_miner_stats_pre_agg() + RETURNS void AS $$ + DECLARE + new record; + BEGIN + FOR new IN SELECT sum(difficulty) AS sum, count(difficulty) AS count, poolid, miner, worker from shares group by poolid, miner, worker + LOOP + INSERT INTO minerstats_pre_agg AS d (poolid, miner, worker, sharecount, sharesaccumulated, created, updated) + VALUES(new.poolid, new.miner, COALESCE(new.worker, ''), new.count, new.sum, now() at time zone 'utc', now() at time zone 'utc') + ON CONFLICT ON CONSTRAINT minerstats_pre_agg_pkey + DO UPDATE SET + sharecount = new.count, sharesaccumulated = new.sum, updated = now() at time zone 'utc'; + END LOOP; + END; +$$ LANGUAGE plpgsql; From a7e75b410a049569021d0dd9c730a57b37c0ca1d Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 17 Feb 2018 15:28:26 +0100 Subject: [PATCH 075/348] Fetch share counts from pre-aggregation table --- .../Persistence/Postgres/Repositories/StatsRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index 215e21b1c..e35f87a4c 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -119,7 +119,7 @@ public MinerStats GetMinerStats(IDbConnection con, IDbTransaction tx, string poo { logger.LogInvoke(new[] { poolId, miner }); - var query = "SELECT (SELECT SUM(difficulty) FROM shares WHERE poolid = @poolId AND miner = @miner) AS pendingshares, " + + var query = "SELECT (SELECT SUM(sharesaccumulated) FROM minerstats_pre_agg WHERE poolid = @poolId AND miner = @miner) AS pendingshares, " + "(SELECT amount FROM balances WHERE poolid = @poolId AND address = @miner) AS pendingbalance, " + "(SELECT SUM(amount) FROM payments WHERE poolid = @poolId and address = @miner) as totalpaid"; From b4d1afa2f3941a407a219a9766d871c80c0b7446 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 17 Feb 2018 15:54:35 +0100 Subject: [PATCH 076/348] WIP --- src/MiningCore/Persistence/Postgres/Scripts/createdb.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index 2429d9a99..7311a0a6f 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -134,7 +134,7 @@ $BODY$ BEGIN UPDATE minerstats_pre_agg SET sharecount = GREATEST(sharecount - 1, 0), sharesaccumulated = GREATEST(sharesaccumulated - old.difficulty, 0), updated = now() at time zone 'utc' - WHERE poolid = old.poolid AND miner = old.miner AND worker = old.worker; + WHERE poolid = old.poolid AND miner = old.miner AND worker = COALESCE(old.worker, ''); RETURN new; END; From 6e79ae794347095f0f7d62d6590578b94d27f51c Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 17 Feb 2018 20:29:27 +0100 Subject: [PATCH 077/348] Damn --- .../Persistence/Postgres/Repositories/StatsRepository.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index e35f87a4c..b1be72eae 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -119,10 +119,15 @@ public MinerStats GetMinerStats(IDbConnection con, IDbTransaction tx, string poo { logger.LogInvoke(new[] { poolId, miner }); +#if true + var query = "SELECT (SELECT SUM(difficulty) FROM shares WHERE poolid = @poolId AND miner = @miner) AS pendingshares, " + + "(SELECT amount FROM balances WHERE poolid = @poolId AND address = @miner) AS pendingbalance, " + + "(SELECT SUM(amount) FROM payments WHERE poolid = @poolId and address = @miner) as totalpaid"; +#else var query = "SELECT (SELECT SUM(sharesaccumulated) FROM minerstats_pre_agg WHERE poolid = @poolId AND miner = @miner) AS pendingshares, " + "(SELECT amount FROM balances WHERE poolid = @poolId AND address = @miner) AS pendingbalance, " + "(SELECT SUM(amount) FROM payments WHERE poolid = @poolId and address = @miner) as totalpaid"; - +#endif var result = con.QuerySingleOrDefault(query, new { poolId, miner }, tx); if (result != null) From 45833f4225cd75fbdee4676ed5a14f5086df375a Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sun, 18 Feb 2018 08:27:55 +0100 Subject: [PATCH 078/348] Logging submissions --- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 4 +- .../Persistence/Postgres/Scripts/createdb.sql | 52 ------------------- 2 files changed, 2 insertions(+), 54 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 7124e640d..484bce1c6 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -289,8 +289,8 @@ private async Task UpdateNetworkStatsAsync() if (!accepted) { - logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} submission failed because block was not found after submission"); - notificationService.NotifyAdmin("Block submission failed", $"Block {share.BlockHeight} submission failed because block was not found after submission"); + logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} submission failed for pool {poolConfig.Id} because block was not found after submission"); + notificationService.NotifyAdmin("Block submission failed", $"Block {share.BlockHeight} submission failed for pool {poolConfig.Id} because block was not found after submission"); } return (accepted, block?.Transactions.FirstOrDefault()); diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index 7311a0a6f..4647353c3 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -109,55 +109,3 @@ CREATE TABLE minerstats_pre_agg primary key(poolid, miner, worker) ); - -CREATE OR REPLACE FUNCTION aggregate_shares_diff_insert() RETURNS TRIGGER AS -$BODY$ -BEGIN - INSERT INTO minerstats_pre_agg AS d (poolid, miner, worker, sharecount, sharesaccumulated, created, updated) - VALUES(new.poolid, new.miner, COALESCE(new.worker, ''), 1, new.difficulty, now() at time zone 'utc', now() at time zone 'utc') - ON CONFLICT ON CONSTRAINT minerstats_pre_agg_pkey - DO UPDATE SET - sharecount = d.sharecount + 1, sharesaccumulated = d.sharesaccumulated + new.difficulty, updated = now() at time zone 'utc'; - - RETURN new; -END; -$BODY$ -language plpgsql; - -CREATE TRIGGER TRIG_SHARES_AGGREGATE_DIFF_INSERT -AFTER INSERT ON shares -FOR EACH ROW -EXECUTE PROCEDURE aggregate_shares_diff_insert(); - -CREATE OR REPLACE FUNCTION aggregate_shares_diff_delete() RETURNS TRIGGER AS -$BODY$ -BEGIN - UPDATE minerstats_pre_agg SET - sharecount = GREATEST(sharecount - 1, 0), sharesaccumulated = GREATEST(sharesaccumulated - old.difficulty, 0), updated = now() at time zone 'utc' - WHERE poolid = old.poolid AND miner = old.miner AND worker = COALESCE(old.worker, ''); - - RETURN new; -END; -$BODY$ -language plpgsql; - -CREATE TRIGGER TRIG_SHARES_AGGREGATE_DIFF_DELETE -AFTER DELETE ON shares -FOR EACH ROW -EXECUTE PROCEDURE aggregate_shares_diff_delete(); - -CREATE OR REPLACE FUNCTION setup_miner_stats_pre_agg() - RETURNS void AS $$ - DECLARE - new record; - BEGIN - FOR new IN SELECT sum(difficulty) AS sum, count(difficulty) AS count, poolid, miner, worker from shares group by poolid, miner, worker - LOOP - INSERT INTO minerstats_pre_agg AS d (poolid, miner, worker, sharecount, sharesaccumulated, created, updated) - VALUES(new.poolid, new.miner, COALESCE(new.worker, ''), new.count, new.sum, now() at time zone 'utc', now() at time zone 'utc') - ON CONFLICT ON CONSTRAINT minerstats_pre_agg_pkey - DO UPDATE SET - sharecount = new.count, sharesaccumulated = new.sum, updated = now() at time zone 'utc'; - END LOOP; - END; -$$ LANGUAGE plpgsql; From be7215a687a876840b4bb73cb55286c4e0040925 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sun, 18 Feb 2018 08:34:47 +0100 Subject: [PATCH 079/348] Verge hashing --- src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index 5a104d98f..1dc8ea152 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -64,7 +64,7 @@ public class BitcoinProperties new BitcoinCoinProperties(1, sha256D, x11, new DigestReverser(x11), "X11"); private static readonly BitcoinCoinProperties blake2sCoin = - new BitcoinCoinProperties(1, sha256D, blake2s, new DigestReverser(blake2s), "Blake2s"); + new BitcoinCoinProperties(1, sha256D, blake2s, sha256DReverse, "Blake2s"); private static readonly BitcoinCoinProperties skeinCoin = new BitcoinCoinProperties(1, sha256D, skein, sha256DReverse, "Skein"); @@ -166,7 +166,7 @@ private static BitcoinCoinProperties GetVergeProperties(string algorithm) switch (algorithm.ToLower()) { case "lyra": - return lyra2Rev2CoinVariantA; + return lyra2Rev2CoinVariantB; case "groestl": return groestlMyriadCoin; From c221d2f88c6968f2fdd5c8e37dc887ad50592ada Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sun, 18 Feb 2018 11:42:21 +0100 Subject: [PATCH 080/348] WIP --- src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index 1dc8ea152..5a104d98f 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -64,7 +64,7 @@ public class BitcoinProperties new BitcoinCoinProperties(1, sha256D, x11, new DigestReverser(x11), "X11"); private static readonly BitcoinCoinProperties blake2sCoin = - new BitcoinCoinProperties(1, sha256D, blake2s, sha256DReverse, "Blake2s"); + new BitcoinCoinProperties(1, sha256D, blake2s, new DigestReverser(blake2s), "Blake2s"); private static readonly BitcoinCoinProperties skeinCoin = new BitcoinCoinProperties(1, sha256D, skein, sha256DReverse, "Skein"); @@ -166,7 +166,7 @@ private static BitcoinCoinProperties GetVergeProperties(string algorithm) switch (algorithm.ToLower()) { case "lyra": - return lyra2Rev2CoinVariantB; + return lyra2Rev2CoinVariantA; case "groestl": return groestlMyriadCoin; From 7826cc46b83b19c0c47a979fea221ed05781f2f9 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sun, 18 Feb 2018 14:20:26 +0100 Subject: [PATCH 081/348] Reward POS stuff --- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 16 +++++++++------- .../Configuration/BitcoinPoolConfigExtra.cs | 5 ----- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 484bce1c6..12a4775c7 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -305,7 +305,7 @@ protected virtual void SetupCrypto() coinbaseHasher = coinProps.CoinbaseHasher; headerHasher = coinProps.HeaderHasher; - blockHasher = !isPoS ? coinProps.BlockHasher : coinProps.PoSBlockHasher; + blockHasher = !isPoS ? coinProps.BlockHasher : (coinProps.PoSBlockHasher ?? coinProps.BlockHasher); ShareMultiplier = coinProps.ShareMultiplier; } @@ -530,7 +530,6 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi maxActiveJobs = extraPoolConfig.MaxActiveJobs.Value; hasLegacyDaemon = extraPoolConfig?.HasLegacyDaemon == true; - isPoS = extraPoolConfig?.IsPoS == true; base.Configure(poolConfig, clusterConfig); } @@ -604,7 +603,8 @@ protected override async Task PostStartInitAsync() { new DaemonCmd(BitcoinCommands.ValidateAddress, new[] { poolConfig.Address }), new DaemonCmd(BitcoinCommands.SubmitBlock), - new DaemonCmd(!hasLegacyDaemon ? BitcoinCommands.GetBlockchainInfo : BitcoinCommands.GetInfo) + new DaemonCmd(!hasLegacyDaemon ? BitcoinCommands.GetBlockchainInfo : BitcoinCommands.GetInfo), + new DaemonCmd(BitcoinCommands.GetDifficulty), }; var results = await daemon.ExecuteBatchAnyAsync(commands); @@ -624,6 +624,7 @@ protected override async Task PostStartInitAsync() var submitBlockResponse = results[1]; var blockchainInfoResponse = !hasLegacyDaemon ? results[2].Response.ToObject() : null; var daemonInfoResponse = hasLegacyDaemon ? results[2].Response.ToObject() : null; + var difficultyResponse = results[3].Response.ToObject(); // ensure pool owns wallet if (!validateAddressResponse.IsValid) @@ -632,11 +633,12 @@ protected override async Task PostStartInitAsync() 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 = AddressToDestination(validateAddressResponse.Address); + poolAddressDestination = !isPoS ? + AddressToDestination(validateAddressResponse.Address) : + new PubKey(validateAddressResponse.PubKey); // chain detection if (!hasLegacyDaemon) diff --git a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs index 314ed261e..7f3577292 100644 --- a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs +++ b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs @@ -32,10 +32,5 @@ public class BitcoinPoolConfigExtra /// Set to true to limit RPC commands to old Bitcoin command set /// public bool? HasLegacyDaemon { get; set; } - - /// - /// Set to true to indicate Prof-of-stake - /// - public bool? IsPoS { get; set; } } } From c3132b2e26f0160f0751954c06b4efd9f702fec6 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 19 Feb 2018 15:05:42 +0100 Subject: [PATCH 082/348] Verge Blockhasher fixes --- .../Blockchain/Bitcoin/BitcoinProperties.cs | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index 5a104d98f..40e4a661d 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -63,9 +63,6 @@ public class BitcoinProperties private static readonly BitcoinCoinProperties x11Coin = new BitcoinCoinProperties(1, sha256D, x11, new DigestReverser(x11), "X11"); - private static readonly BitcoinCoinProperties blake2sCoin = - new BitcoinCoinProperties(1, sha256D, blake2s, new DigestReverser(blake2s), "Blake2s"); - private static readonly BitcoinCoinProperties skeinCoin = new BitcoinCoinProperties(1, sha256D, skein, sha256DReverse, "Skein"); @@ -81,8 +78,17 @@ public class BitcoinProperties private static readonly BitcoinCoinProperties neoScryptCoin = new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, neoScryptProfile1, new DigestReverser(neoScryptProfile1), "Neoscrypt"); - private static readonly BitcoinCoinProperties x17Coin = - new BitcoinCoinProperties(1, sha256D, x17, new DigestReverser(x17), "X17"); + private static readonly BitcoinCoinProperties vergeLyraCoin = + new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, new DigestReverser(scrypt_1024_1), "Lyra2re2"); + + private static readonly BitcoinCoinProperties vergeBlake2sCoin = + new BitcoinCoinProperties(1, sha256D, blake2s, new DigestReverser(scrypt_1024_1), "Blake2s"); + + private static readonly BitcoinCoinProperties vergeX17Coin = + new BitcoinCoinProperties(1, x17, blake2s, new DigestReverser(scrypt_1024_1), "X17"); + + private static readonly BitcoinCoinProperties vergeGroestlCoin = + new BitcoinCoinProperties(1, groestlMyriad, blake2s, new DigestReverser(scrypt_1024_1), "Groestl-Myriad"); private static readonly Dictionary coinProperties = new Dictionary { @@ -166,16 +172,16 @@ private static BitcoinCoinProperties GetVergeProperties(string algorithm) switch (algorithm.ToLower()) { case "lyra": - return lyra2Rev2CoinVariantA; + return vergeLyraCoin; case "groestl": - return groestlMyriadCoin; + return vergeGroestlCoin; case "x17": - return x17Coin; + return vergeX17Coin; case "blake": - return blake2sCoin; + return vergeBlake2sCoin; default: // scrypt return scryptCoin; From 323db8f3c5823670a78af385d7474cc99628cdf1 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 19 Feb 2018 16:54:13 +0100 Subject: [PATCH 083/348] BlockRewardMultiplier --- src/MiningCore.Tests/Blockchain/Bitcoin/BitcoinJobTests.cs | 4 ++-- src/MiningCore.Tests/Blockchain/ZCash/ZCashJobTests.cs | 2 +- src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs | 6 ++++-- src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs | 6 ++++-- .../BitcoinPoolPaymentProcessingConfigExtra.cs | 5 +++++ src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJob.cs | 2 +- src/MiningCore/Blockchain/Dash/DashJob.cs | 2 +- src/MiningCore/Blockchain/ZCash/ZCashJob.cs | 2 +- src/MiningCore/Program.cs | 1 + 9 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/MiningCore.Tests/Blockchain/Bitcoin/BitcoinJobTests.cs b/src/MiningCore.Tests/Blockchain/Bitcoin/BitcoinJobTests.cs index 02b0c0a2a..2282c2ead 100644 --- a/src/MiningCore.Tests/Blockchain/Bitcoin/BitcoinJobTests.cs +++ b/src/MiningCore.Tests/Blockchain/Bitcoin/BitcoinJobTests.cs @@ -42,7 +42,7 @@ public void BitcoinJob_Should_Accept_Valid_Share() var clock = new MockMasterClock { CurrentTime = DateTimeOffset.FromUnixTimeSeconds(1508869874).UtcDateTime }; job.Init(bt, "1", poolConfig, clusterConfig, clock, poolAddressDestination, BitcoinNetworkType.RegTest, - false, 1, sha256d, sha256d, sha256dReverse); + false, 1, 1, sha256d, sha256d, sha256dReverse); // set clock to submission time clock.CurrentTime = DateTimeOffset.FromUnixTimeSeconds(1508869907).UtcDateTime; @@ -78,7 +78,7 @@ public void BitcoinJob_Should_Not_Accept_Invalid_Share() var clock = new MockMasterClock { CurrentTime = DateTimeOffset.FromUnixTimeSeconds(1508869874).UtcDateTime }; job.Init(bt, "1", poolConfig, clusterConfig, clock, poolAddressDestination, BitcoinNetworkType.RegTest, - false, 1, sha256d, sha256d, sha256dReverse); + false, 1, 1, sha256d, sha256d, sha256dReverse); // set clock to submission time clock.CurrentTime = DateTimeOffset.FromUnixTimeSeconds(1508869907).UtcDateTime; diff --git a/src/MiningCore.Tests/Blockchain/ZCash/ZCashJobTests.cs b/src/MiningCore.Tests/Blockchain/ZCash/ZCashJobTests.cs index 5d3462fa2..1264b8be9 100644 --- a/src/MiningCore.Tests/Blockchain/ZCash/ZCashJobTests.cs +++ b/src/MiningCore.Tests/Blockchain/ZCash/ZCashJobTests.cs @@ -49,7 +49,7 @@ public void ZCashJob_Testnet_Validate_FoundersRewardAddress_At_Height() var clock = new MockMasterClock { CurrentTime = DateTimeOffset.FromUnixTimeSeconds(1508869874).UtcDateTime }; job.Init(bt, "1", poolConfig, clusterConfig, clock, poolAddressDestination, BitcoinNetworkType.Test, - false, 1, sha256d, sha256d, sha256dReverse); + false, 1, 1, sha256d, sha256d, sha256dReverse); bt.Height = 1; Assert.Equal(job.GetFoundersRewardAddress(), "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi"); diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs index 2d555fc80..f77a5e8c5 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs @@ -46,6 +46,7 @@ public class BitcoinJob protected IMasterClock clock; protected IHashAlgorithm coinbaseHasher; protected double shareMultiplier; + protected decimal blockRewardMultiplier; protected int extraNoncePlaceHolderLength; protected IHashAlgorithm headerHasher; protected bool isPoS; @@ -239,7 +240,7 @@ protected virtual Script GenerateScriptSigInitial() protected virtual Transaction CreateOutputTransaction() { - rewardToPool = new Money(BlockTemplate.CoinbaseValue, MoneyUnit.Satoshi); + rewardToPool = new Money((long) (BlockTemplate.CoinbaseValue * blockRewardMultiplier), MoneyUnit.Satoshi); var tx = new Transaction(); @@ -411,7 +412,7 @@ protected virtual byte[] BuildRawTransactionBuffer() public virtual void Init(TBlockTemplate blockTemplate, string jobId, PoolConfig poolConfig, ClusterConfig clusterConfig, IMasterClock clock, IDestination poolAddressDestination, BitcoinNetworkType networkType, - bool isPoS, double shareMultiplier, + bool isPoS, double shareMultiplier, decimal blockrewardMultiplier, IHashAlgorithm coinbaseHasher, IHashAlgorithm headerHasher, IHashAlgorithm blockHasher) { Contract.RequiresNonNull(blockTemplate, nameof(blockTemplate)); @@ -436,6 +437,7 @@ public virtual void Init(TBlockTemplate blockTemplate, string jobId, extraNoncePlaceHolderLength = BitcoinConstants.ExtranoncePlaceHolderLength; this.isPoS = isPoS; this.shareMultiplier = shareMultiplier; + this.blockRewardMultiplier = blockrewardMultiplier; this.coinbaseHasher = coinbaseHasher; this.headerHasher = headerHasher; diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 12a4775c7..eba90c04c 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -75,8 +75,9 @@ public BitcoinJobManager( protected readonly IHashAlgorithm sha256d = new Sha256D(); protected readonly IHashAlgorithm sha256dReverse = new DigestReverser(new Sha256D()); protected int maxActiveJobs = 4; - private bool hasLegacyDaemon; + protected bool hasLegacyDaemon; protected BitcoinPoolConfigExtra extraPoolConfig; + protected BitcoinPoolPaymentProcessingConfigExtra extraPoolPaymentProcessingConfig; protected readonly IHashAlgorithm sha256s = new Sha256S(); protected readonly List validJobs = new List(); protected IHashAlgorithm blockHasher; @@ -525,6 +526,7 @@ public virtual async Task SubmitShareAsync(StratumClient worker, o public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig) { extraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); + extraPoolPaymentProcessingConfig = poolConfig.PaymentProcessing.Extra.SafeExtensionDataAs(); if (extraPoolConfig?.MaxActiveJobs.HasValue == true) maxActiveJobs = extraPoolConfig.MaxActiveJobs.Value; @@ -738,7 +740,7 @@ protected virtual async Task UpdateJob(bool forceUpdate, string via = null job.Init(blockTemplate, NextJobId(), poolConfig, clusterConfig, clock, poolAddressDestination, networkType, isPoS, - ShareMultiplier, + ShareMultiplier, extraPoolPaymentProcessingConfig.BlockrewardMultiplier ?? 1.0m, coinbaseHasher, headerHasher, blockHasher); if (isNew) diff --git a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolPaymentProcessingConfigExtra.cs b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolPaymentProcessingConfigExtra.cs index e6d7fcdc8..1e78a2020 100644 --- a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolPaymentProcessingConfigExtra.cs +++ b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolPaymentProcessingConfigExtra.cs @@ -26,5 +26,10 @@ public class BitcoinPoolPaymentProcessingConfigExtra /// if True, miners pay payment tx fees /// public bool MinersPayTxFees { get; set; } + + /// + /// Multiply blockreward by this amount + /// + public decimal? BlockrewardMultiplier { get; set; } } } diff --git a/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJob.cs b/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJob.cs index 5906f9ad1..3069159dd 100644 --- a/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJob.cs +++ b/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJob.cs @@ -79,7 +79,7 @@ protected override byte[] SerializeHeader(uint nTime, string nonce) public override void Init(ZCashBlockTemplate blockTemplate, string jobId, PoolConfig poolConfig, ClusterConfig clusterConfig, IMasterClock clock, IDestination poolAddressDestination, BitcoinNetworkType networkType, - bool isPoS, double shareMultiplier, + bool isPoS, double shareMultiplier, decimal blockrewardMultiplier, IHashAlgorithm coinbaseHasher, IHashAlgorithm headerHasher, IHashAlgorithm blockHasher) { Contract.RequiresNonNull(blockTemplate, nameof(blockTemplate)); diff --git a/src/MiningCore/Blockchain/Dash/DashJob.cs b/src/MiningCore/Blockchain/Dash/DashJob.cs index a85747399..dd62dbd70 100644 --- a/src/MiningCore/Blockchain/Dash/DashJob.cs +++ b/src/MiningCore/Blockchain/Dash/DashJob.cs @@ -31,7 +31,7 @@ public class DashJob : BitcoinJob { protected override Transaction CreateOutputTransaction() { - var blockReward = new Money(BlockTemplate.CoinbaseValue, MoneyUnit.Satoshi); + var blockReward = new Money((long)(BlockTemplate.CoinbaseValue * blockRewardMultiplier), MoneyUnit.Satoshi); rewardToPool = new Money(BlockTemplate.CoinbaseValue, MoneyUnit.Satoshi); var tx = new Transaction(); diff --git a/src/MiningCore/Blockchain/ZCash/ZCashJob.cs b/src/MiningCore/Blockchain/ZCash/ZCashJob.cs index 889531fb4..2f39d4881 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashJob.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashJob.cs @@ -142,7 +142,7 @@ protected override void BuildCoinbase() public override void Init(ZCashBlockTemplate blockTemplate, string jobId, PoolConfig poolConfig, ClusterConfig clusterConfig, IMasterClock clock, IDestination poolAddressDestination, BitcoinNetworkType networkType, - bool isPoS, double shareMultiplier, + bool isPoS, double shareMultiplier, decimal blockrewardMultiplier, IHashAlgorithm coinbaseHasher, IHashAlgorithm headerHasher, IHashAlgorithm blockHasher) { Contract.RequiresNonNull(blockTemplate, nameof(blockTemplate)); diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index 28135790a..0aed5fd4d 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -50,6 +50,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Persistence.Postgres; using MiningCore.Persistence.Postgres.Repositories; using MiningCore.Util; +using NBitcoin; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using NLog; From 4b43e540ba3b26c99ccf28953371b5440150c4cc Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 19 Feb 2018 17:31:37 +0100 Subject: [PATCH 084/348] WIP --- src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs | 2 +- src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs | 2 +- src/MiningCore/Blockchain/Dash/DashJob.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs index f77a5e8c5..de532815b 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs @@ -240,7 +240,7 @@ protected virtual Script GenerateScriptSigInitial() protected virtual Transaction CreateOutputTransaction() { - rewardToPool = new Money((long) (BlockTemplate.CoinbaseValue * blockRewardMultiplier), MoneyUnit.Satoshi); + rewardToPool = new Money(BlockTemplate.CoinbaseValue * blockRewardMultiplier, MoneyUnit.Satoshi); var tx = new Transaction(); diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index eba90c04c..60752b458 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -740,7 +740,7 @@ protected virtual async Task UpdateJob(bool forceUpdate, string via = null job.Init(blockTemplate, NextJobId(), poolConfig, clusterConfig, clock, poolAddressDestination, networkType, isPoS, - ShareMultiplier, extraPoolPaymentProcessingConfig.BlockrewardMultiplier ?? 1.0m, + ShareMultiplier, extraPoolPaymentProcessingConfig?.BlockrewardMultiplier ?? 1.0m, coinbaseHasher, headerHasher, blockHasher); if (isNew) diff --git a/src/MiningCore/Blockchain/Dash/DashJob.cs b/src/MiningCore/Blockchain/Dash/DashJob.cs index dd62dbd70..c8143901a 100644 --- a/src/MiningCore/Blockchain/Dash/DashJob.cs +++ b/src/MiningCore/Blockchain/Dash/DashJob.cs @@ -31,7 +31,7 @@ public class DashJob : BitcoinJob { protected override Transaction CreateOutputTransaction() { - var blockReward = new Money((long)(BlockTemplate.CoinbaseValue * blockRewardMultiplier), MoneyUnit.Satoshi); + var blockReward = new Money(BlockTemplate.CoinbaseValue * blockRewardMultiplier, MoneyUnit.Satoshi); rewardToPool = new Money(BlockTemplate.CoinbaseValue, MoneyUnit.Satoshi); var tx = new Transaction(); From 67372a37740447d47ba2feabc8e2b57430a3e9e3 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 19 Feb 2018 18:03:29 +0100 Subject: [PATCH 085/348] Increase Pg Command Timeout --- src/MiningCore/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index 0aed5fd4d..4a40bba4e 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -517,7 +517,7 @@ private static void ConfigurePostgres(DatabaseConfig pgConfig, ContainerBuilder logger.ThrowLogPoolStartupException("Postgres configuration: invalid or missing 'user'"); // build connection string - var connectionString = $"Server={pgConfig.Host};Port={pgConfig.Port};Database={pgConfig.Database};User Id={pgConfig.User};Password={pgConfig.Password};CommandTimeout=300;"; + var connectionString = $"Server={pgConfig.Host};Port={pgConfig.Port};Database={pgConfig.Database};User Id={pgConfig.User};Password={pgConfig.Password};CommandTimeout=900;"; // register connection factory builder.RegisterInstance(new ConnectionFactory(connectionString)) From dc30c0bee8409ec7a6ea121c37798c1599e23239 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 19 Feb 2018 19:44:00 +0100 Subject: [PATCH 086/348] WIP --- src/MiningCore/Mining/StatsRecorder.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/MiningCore/Mining/StatsRecorder.cs b/src/MiningCore/Mining/StatsRecorder.cs index f9da24f65..b796b4a81 100644 --- a/src/MiningCore/Mining/StatsRecorder.cs +++ b/src/MiningCore/Mining/StatsRecorder.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; using System.Data.Common; -using System.Diagnostics; using System.Linq; using System.Net.Sockets; using System.Threading; -using System.Threading.Tasks; using Autofac; using AutoMapper; using MiningCore.Configuration; @@ -56,6 +54,7 @@ public StatsRecorder(IComponentContext ctx, private readonly Dictionary pools = new Dictionary(); private const int HashrateCalculationWindow = 1200; // seconds private const int MinHashrateCalculationWindow = 300; // seconds + private const double HashrateBoostFactor = 1.07d; private ClusterConfig clusterConfig; private Thread thread1; private const int RetryCount = 4; @@ -155,7 +154,7 @@ private void UpdatePoolHashrates() { var poolHashesAccumulated = result.Sum(x => x.Sum); var poolHashesCountAccumulated = result.Sum(x => x.Count); - var poolHashrate = pool.HashrateFromShares(poolHashesAccumulated, windowActual); + var poolHashrate = pool.HashrateFromShares(poolHashesAccumulated, windowActual) * HashrateBoostFactor; // update pool.PoolStats.ConnectedMiners = byMiner.Length; @@ -196,7 +195,7 @@ private void UpdatePoolHashrates() if (windowActual >= MinHashrateCalculationWindow) { - var hashrate = pool.HashrateFromShares(item.Sum, windowActual); + var hashrate = pool.HashrateFromShares(item.Sum, windowActual) * HashrateBoostFactor; // update stats.Hashrate = hashrate; From e9d8729a6800058b38e32ad6ebbcf7b3b0ad5b3c Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 21 Feb 2018 13:06:02 +0100 Subject: [PATCH 087/348] WIP --- src/MiningCore/Persistence/Postgres/Scripts/createdb.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index 4647353c3..12a4e8cee 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -18,6 +18,7 @@ CREATE TABLE shares CREATE INDEX IDX_SHARES_POOL_BLOCK on shares(poolid, blockheight); CREATE INDEX IDX_SHARES_POOL_MINER on shares(poolid, miner); CREATE INDEX IDX_SHARES_POOL_CREATED ON shares(poolid, created); +CREATE INDEX IDX_SHARES_POOL_MINER_DIFFICULTY on shares(poolid, miner, difficulty); CREATE TABLE blocks ( From df98b3c93e6cbf8e733cc931df010b04cd596cbc Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 21 Feb 2018 14:04:40 +0100 Subject: [PATCH 088/348] Added payment audit-trail --- .../Bitcoin/BitcoinPayoutHandler.cs | 2 +- .../Ethereum/EthereumPayoutHandler.cs | 2 +- .../Blockchain/Monero/MoneroPayoutHandler.cs | 2 +- src/MiningCore/Payments/PayoutHandlerBase.cs | 2 +- .../Payments/PayoutSchemes/PPLNS.cs | 2 +- .../Persistence/Model/BalanceChange.cs | 42 +++++++++++++++++++ .../Postgres/Entities/BalanceChange.cs | 35 ++++++++++++++++ .../Repositories/BalanceRepository.cs | 25 +++++++++-- .../Persistence/Postgres/Scripts/createdb.sql | 11 +++++ .../Repositories/IBalanceRepository.cs | 3 +- 10 files changed, 115 insertions(+), 11 deletions(-) create mode 100644 src/MiningCore/Persistence/Model/BalanceChange.cs create mode 100644 src/MiningCore/Persistence/Postgres/Entities/BalanceChange.cs diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs index eee37c350..ae09376c7 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs @@ -212,7 +212,7 @@ public virtual Task UpdateBlockRewardBalancesAsync(IDbConnection con, I if (address != poolConfig.Address) { logger.Info(() => $"Adding {FormatAmount(amount)} to balance of {address}"); - balanceRepo.AddAmount(con, tx, poolConfig.Id, poolConfig.Coin.Type, address, amount); + balanceRepo.AddAmount(con, tx, poolConfig.Id, poolConfig.Coin.Type, address, amount, $"Reward for block {block.BlockHeight}"); } } diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumPayoutHandler.cs b/src/MiningCore/Blockchain/Ethereum/EthereumPayoutHandler.cs index 8a6b00843..d69793894 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumPayoutHandler.cs @@ -256,7 +256,7 @@ public Task UpdateBlockRewardBalancesAsync(IDbConnection con, IDbTransa if (address != poolConfig.Address) { logger.Info(() => $"Adding {FormatAmount(amount)} to balance of {address}"); - balanceRepo.AddAmount(con, tx, poolConfig.Id, poolConfig.Coin.Type, address, amount); + balanceRepo.AddAmount(con, tx, poolConfig.Id, poolConfig.Coin.Type, address, amount, $"Reward for block {block.BlockHeight}"); } } diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index dd463dd26..6fdd55325 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -416,7 +416,7 @@ public Task UpdateBlockRewardBalancesAsync(IDbConnection con, IDbTransa if (address != poolConfig.Address) { logger.Info(() => $"Adding {FormatAmount(amount)} to balance of {address}"); - balanceRepo.AddAmount(con, tx, poolConfig.Id, poolConfig.Coin.Type, address, amount); + balanceRepo.AddAmount(con, tx, poolConfig.Id, poolConfig.Coin.Type, address, amount, $"Reward for block {block.BlockHeight}"); } } diff --git a/src/MiningCore/Payments/PayoutHandlerBase.cs b/src/MiningCore/Payments/PayoutHandlerBase.cs index 0fc2d0062..7aa791519 100644 --- a/src/MiningCore/Payments/PayoutHandlerBase.cs +++ b/src/MiningCore/Payments/PayoutHandlerBase.cs @@ -129,7 +129,7 @@ protected virtual void PersistPayments(Balance[] balances, string transactionCon // reset balance logger.Debug(() => $"[{LogCategory}] Resetting balance of {balance.Address}"); - balanceRepo.AddAmount(con, tx, poolConfig.Id, poolConfig.Coin.Type, balance.Address, -balance.Amount); + balanceRepo.AddAmount(con, tx, poolConfig.Id, poolConfig.Coin.Type, balance.Address, -balance.Amount, $"Balance reset after payment"); } }); }); diff --git a/src/MiningCore/Payments/PayoutSchemes/PPLNS.cs b/src/MiningCore/Payments/PayoutSchemes/PPLNS.cs index 99dc5c27d..085c08326 100644 --- a/src/MiningCore/Payments/PayoutSchemes/PPLNS.cs +++ b/src/MiningCore/Payments/PayoutSchemes/PPLNS.cs @@ -98,7 +98,7 @@ public Task UpdateBalancesAsync(IDbConnection con, IDbTransaction tx, PoolConfig if (amount > 0) { logger.Info(() => $"Adding {payoutHandler.FormatAmount(amount)} to balance of {address} for {FormatUtil.FormatQuantity(shares[address])} ({shares[address]}) shares for block {block.BlockHeight}"); - balanceRepo.AddAmount(con, tx, poolConfig.Id, poolConfig.Coin.Type, address, amount); + balanceRepo.AddAmount(con, tx, poolConfig.Id, poolConfig.Coin.Type, address, amount, $"Reward for {FormatUtil.FormatQuantity(shares[address])} shares for block {block.BlockHeight}"); } } diff --git a/src/MiningCore/Persistence/Model/BalanceChange.cs b/src/MiningCore/Persistence/Model/BalanceChange.cs new file mode 100644 index 000000000..39af85716 --- /dev/null +++ b/src/MiningCore/Persistence/Model/BalanceChange.cs @@ -0,0 +1,42 @@ +/* +Copyright 2017 Coin Foundry (coinfoundry.org) +Authors: Oliver Weichhold (oliver@weichhold.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +using System; +using MiningCore.Configuration; + +namespace MiningCore.Persistence.Model +{ + public class BalanceChange + { + public long Id { get; set; } + public string PoolId { get; set; } + public CoinType Coin { get; set; } + public string Address { get; set; } + + /// + /// Amount owed in pool-base-currency (ie. Bitcoin, not Satoshis) + /// + public decimal Amount { get; set; } + + public string Usage { get; set; } + + public DateTime Created { get; set; } + } +} diff --git a/src/MiningCore/Persistence/Postgres/Entities/BalanceChange.cs b/src/MiningCore/Persistence/Postgres/Entities/BalanceChange.cs new file mode 100644 index 000000000..0c2222ac4 --- /dev/null +++ b/src/MiningCore/Persistence/Postgres/Entities/BalanceChange.cs @@ -0,0 +1,35 @@ +/* +Copyright 2017 Coin Foundry (coinfoundry.org) +Authors: Oliver Weichhold (oliver@weichhold.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +using System; + +namespace MiningCore.Persistence.Postgres.Entities +{ + public class BalanceChange + { + public long Id { get; set; } + public string PoolId { get; set; } + public string Coin { get; set; } + public string Address { get; set; } + public decimal Amount { get; set; } + public string Usage { get; set; } + public DateTime Created { get; set; } + } +} diff --git a/src/MiningCore/Persistence/Postgres/Repositories/BalanceRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/BalanceRepository.cs index 8d363f6c1..014d8d984 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/BalanceRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/BalanceRepository.cs @@ -42,17 +42,34 @@ public BalanceRepository(IMapper mapper) private readonly IMapper mapper; private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); - public void AddAmount(IDbConnection con, IDbTransaction tx, string poolId, CoinType coin, string address, decimal amount) + public void AddAmount(IDbConnection con, IDbTransaction tx, string poolId, CoinType coin, string address, decimal amount, string usage) { logger.LogInvoke(); - var query = "SELECT * FROM balances WHERE poolid = @poolId AND coin = @coin AND address = @address"; + var now = DateTime.UtcNow; + + // record balance change + var query = "INSERT INTO balances_changes(poolid, coin, address, amount, usage, created) " + + "VALUES(@poolid, @coin, @address, @amount, @usage, @created)"; + + var balanceChange = new Entities.BalanceChange + { + PoolId = poolId, + Coin = coin.ToString(), + Created = now, + Address = address, + Amount = amount, + Usage = usage, + }; + + con.Execute(query, balanceChange, tx); + + // update balance + query = "SELECT * FROM balances WHERE poolid = @poolId AND coin = @coin AND address = @address"; var balance = con.Query(query, new { poolId, coin = coin.ToString(), address }, tx) .FirstOrDefault(); - var now = DateTime.UtcNow; - if (balance == null) { balance = new Entities.Balance diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index 12a4e8cee..aedab553a 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -50,6 +50,17 @@ CREATE TABLE balances primary key(poolid, address, coin) ); +CREATE TABLE balance_changes +( + id BIGSERIAL NOT NULL PRIMARY KEY, + poolid TEXT NOT NULL, + coin TEXT NOT NULL, + address TEXT NOT NULL, + amount decimal(28,12) NOT NULL DEFAULT 0, + usage TEXT NULL, + created TIMESTAMP NOT NULL +); + CREATE TABLE payments ( id BIGSERIAL NOT NULL PRIMARY KEY, diff --git a/src/MiningCore/Persistence/Repositories/IBalanceRepository.cs b/src/MiningCore/Persistence/Repositories/IBalanceRepository.cs index d04ed8d25..d5091b1ec 100644 --- a/src/MiningCore/Persistence/Repositories/IBalanceRepository.cs +++ b/src/MiningCore/Persistence/Repositories/IBalanceRepository.cs @@ -26,8 +26,7 @@ namespace MiningCore.Persistence.Repositories { public interface IBalanceRepository { - void AddAmount(IDbConnection con, IDbTransaction tx, string poolId, CoinType coin, string address, - decimal amount); + void AddAmount(IDbConnection con, IDbTransaction tx, string poolId, CoinType coin, string address, decimal amount, string usage); Balance[] GetPoolBalancesOverThreshold(IDbConnection con, string poolId, decimal minimum); } From 2a1c7d8d5d99b35f4e485b0745d3bc1e72726a9c Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 21 Feb 2018 14:29:35 +0100 Subject: [PATCH 089/348] WIP --- .../Postgres/Repositories/StatsRepository.cs | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index b1be72eae..43f6fdc19 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -19,6 +19,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; +using System.Collections.Generic; using System.Data; using System.Linq; using AutoMapper; @@ -207,7 +208,7 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenHourly(IDbCon var entitiesByDate = entities .GroupBy(x=> x.Created); - var result = entitiesByDate.Select(x => new WorkerPerformanceStatsContainer + var tmp = entitiesByDate.Select(x => new WorkerPerformanceStatsContainer { Created = x.Key, Workers = x.ToDictionary(y => y.Worker ?? string.Empty, y => new WorkerPerformanceStats @@ -219,7 +220,27 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenHourly(IDbCon .OrderBy(x=> x.Created) .ToArray(); - return result; + // fill in blanks + var result = new List(); + var lastCreated = start; + var maxItemCount = 24; + + foreach (var item in tmp) + { + while (result.Count < maxItemCount && + (item.Created - lastCreated > TimeSpan.FromHours(1))) + { + result.Add(new WorkerPerformanceStatsContainer { Created = lastCreated }); + lastCreated = lastCreated.AddHours(1); + } + + if (result.Count >= maxItemCount) + break; + + result.Add(item); + } + + return result.ToArray(); } public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConnection con, string poolId, string miner, DateTime start, DateTime end) @@ -236,7 +257,7 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConn .ToArray() .GroupBy(x => x.Created); - var result = entitiesByDate.Select(x => new WorkerPerformanceStatsContainer + var tmp = entitiesByDate.Select(x => new WorkerPerformanceStatsContainer { Created = x.Key, Workers = x.ToDictionary(y => y.Worker, y => new WorkerPerformanceStats @@ -248,7 +269,27 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConn .OrderBy(x => x.Created) .ToArray(); - return result; + // fill in blanks + var result = new List(); + var lastCreated = start; + var maxItemCount = 31; + + foreach (var item in tmp) + { + while (result.Count < maxItemCount && + (item.Created - lastCreated > TimeSpan.FromDays(1))) + { + result.Add(new WorkerPerformanceStatsContainer { Created = lastCreated }); + lastCreated = lastCreated.AddDays(1); + } + + if (result.Count >= maxItemCount) + break; + + result.Add(item); + } + + return result.ToArray(); } public MinerWorkerPerformanceStats[] PagePoolMinersByHashrate(IDbConnection con, string poolId, DateTime from, int page, int pageSize) From 80b2fcbc1bcb56a464c9bd480b025e8fa356d2cc Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 21 Feb 2018 14:32:00 +0100 Subject: [PATCH 090/348] WIP --- .../Persistence/Postgres/Repositories/StatsRepository.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index 43f6fdc19..de3e90256 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -238,6 +238,7 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenHourly(IDbCon break; result.Add(item); + lastCreated = item.Created; } return result.ToArray(); @@ -287,6 +288,7 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConn break; result.Add(item); + lastCreated = item.Created; } return result.ToArray(); From ee612c0fd1b8250ca4594c1909ae67ffcc4a2a5d Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 21 Feb 2018 14:36:07 +0100 Subject: [PATCH 091/348] WIP --- .../Postgres/Repositories/StatsRepository.cs | 81 ++++++++++--------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index de3e90256..ffb5bb77e 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -221,27 +221,28 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenHourly(IDbCon .ToArray(); // fill in blanks - var result = new List(); - var lastCreated = start; - var maxItemCount = 24; - - foreach (var item in tmp) - { - while (result.Count < maxItemCount && - (item.Created - lastCreated > TimeSpan.FromHours(1))) - { - result.Add(new WorkerPerformanceStatsContainer { Created = lastCreated }); - lastCreated = lastCreated.AddHours(1); - } - - if (result.Count >= maxItemCount) - break; - - result.Add(item); - lastCreated = item.Created; - } - - return result.ToArray(); + //var result = new List(); + //var lastCreated = start; + //var maxItemCount = 24; + + //foreach (var item in tmp) + //{ + // while (result.Count < maxItemCount && + // (item.Created - lastCreated > TimeSpan.FromHours(1))) + // { + // result.Add(new WorkerPerformanceStatsContainer { Created = lastCreated }); + // lastCreated = lastCreated.AddHours(1); + // } + + // if (result.Count >= maxItemCount) + // break; + + // result.Add(item); + // lastCreated = item.Created; + //} + + //return result.ToArray(); + return tmp; } public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConnection con, string poolId, string miner, DateTime start, DateTime end) @@ -270,28 +271,30 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConn .OrderBy(x => x.Created) .ToArray(); - // fill in blanks - var result = new List(); - var lastCreated = start; - var maxItemCount = 31; + //// fill in blanks + //var result = new List(); + //var lastCreated = start; + //var maxItemCount = 31; - foreach (var item in tmp) - { - while (result.Count < maxItemCount && - (item.Created - lastCreated > TimeSpan.FromDays(1))) - { - result.Add(new WorkerPerformanceStatsContainer { Created = lastCreated }); - lastCreated = lastCreated.AddDays(1); - } + //foreach (var item in tmp) + //{ + // while (result.Count < maxItemCount && + // (item.Created - lastCreated > TimeSpan.FromDays(1))) + // { + // result.Add(new WorkerPerformanceStatsContainer { Created = lastCreated }); + // lastCreated = lastCreated.AddDays(1); + // } - if (result.Count >= maxItemCount) - break; + // if (result.Count >= maxItemCount) + // break; - result.Add(item); - lastCreated = item.Created; - } + // result.Add(item); + // lastCreated = item.Created; + //} + + //return result.ToArray(); - return result.ToArray(); + return tmp; } public MinerWorkerPerformanceStats[] PagePoolMinersByHashrate(IDbConnection con, string poolId, DateTime from, int page, int pageSize) From 5433c7f7656f40644b76c81b289a5af425214052 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 21 Feb 2018 16:39:01 +0100 Subject: [PATCH 092/348] Cleanup --- src/MiningCore/Persistence/Postgres/Scripts/createdb.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index aedab553a..25debffd5 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -15,7 +15,6 @@ CREATE TABLE shares created TIMESTAMP NOT NULL ); -CREATE INDEX IDX_SHARES_POOL_BLOCK on shares(poolid, blockheight); CREATE INDEX IDX_SHARES_POOL_MINER on shares(poolid, miner); CREATE INDEX IDX_SHARES_POOL_CREATED ON shares(poolid, created); CREATE INDEX IDX_SHARES_POOL_MINER_DIFFICULTY on shares(poolid, miner, difficulty); From 06927949c06e13d49be4053d9284b603f221bbe2 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 21 Feb 2018 22:01:46 +0100 Subject: [PATCH 093/348] Cleanup --- .../Postgres/Repositories/BalanceRepository.cs | 2 +- .../Persistence/Postgres/Scripts/createdb.sql | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/MiningCore/Persistence/Postgres/Repositories/BalanceRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/BalanceRepository.cs index 014d8d984..d306a03bc 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/BalanceRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/BalanceRepository.cs @@ -49,7 +49,7 @@ public void AddAmount(IDbConnection con, IDbTransaction tx, string poolId, CoinT var now = DateTime.UtcNow; // record balance change - var query = "INSERT INTO balances_changes(poolid, coin, address, amount, usage, created) " + + var query = "INSERT INTO balance_changes(poolid, coin, address, amount, usage, created) " + "VALUES(@poolid, @coin, @address, @amount, @usage, @created)"; var balanceChange = new Entities.BalanceChange diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index 25debffd5..632906cb3 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -105,18 +105,3 @@ CREATE INDEX IDX_MINERSTATS_POOL_CREATED on minerstats(poolid, created); CREATE INDEX IDX_MINERSTATS_POOL_MINER_CREATED on minerstats(poolid, miner, created); CREATE INDEX IDX_MINERSTATS_POOL_MINER_CREATED_HOUR on minerstats(poolid, miner, date_trunc('hour',created)); CREATE INDEX IDX_MINERSTATS_POOL_MINER_CREATED_DAY on minerstats(poolid, miner, date_trunc('day',created)); - -CREATE TABLE minerstats_pre_agg -( - poolid TEXT NOT NULL, - miner TEXT NOT NULL, - worker TEXT NOT NULL, - - sharecount BIGINT NOT NULL, - sharesaccumulated DOUBLE PRECISION NOT NULL, - - created TIMESTAMP NOT NULL, - updated TIMESTAMP NOT NULL, - - primary key(poolid, miner, worker) -); From 5428f32b83d6597820adad8d56e799a3f53a062a Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 22 Feb 2018 07:23:06 +0100 Subject: [PATCH 094/348] Drop Id column for shares table Add index on table balance_changes --- src/MiningCore/Persistence/Postgres/Scripts/createdb.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index 632906cb3..b557f95fb 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -2,7 +2,6 @@ CREATE TABLE shares ( - id BIGSERIAL NOT NULL PRIMARY KEY, poolid TEXT NOT NULL, blockheight BIGINT NOT NULL, difficulty DOUBLE PRECISION NOT NULL, @@ -60,6 +59,8 @@ CREATE TABLE balance_changes created TIMESTAMP NOT NULL ); +CREATE INDEX IDX_BALANCE_CHANGES_POOL_ADDRESS_CREATED on balance_changes(poolid, miner, created desc); + CREATE TABLE payments ( id BIGSERIAL NOT NULL PRIMARY KEY, From 52ab3910683537dc14ae22cb3c159aa2eb531590 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 22 Feb 2018 07:25:13 +0100 Subject: [PATCH 095/348] WIP --- src/MiningCore/Persistence/Model/Share.cs | 1 - src/MiningCore/Persistence/Postgres/Entities/Share.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/MiningCore/Persistence/Model/Share.cs b/src/MiningCore/Persistence/Model/Share.cs index fd9036559..60497d00e 100644 --- a/src/MiningCore/Persistence/Model/Share.cs +++ b/src/MiningCore/Persistence/Model/Share.cs @@ -24,7 +24,6 @@ namespace MiningCore.Persistence.Model { public class Share { - public long Id { get; set; } public string PoolId { get; set; } public ulong BlockHeight { get; set; } public string PayoutInfo { get; set; } diff --git a/src/MiningCore/Persistence/Postgres/Entities/Share.cs b/src/MiningCore/Persistence/Postgres/Entities/Share.cs index ace70973f..a3a68961e 100644 --- a/src/MiningCore/Persistence/Postgres/Entities/Share.cs +++ b/src/MiningCore/Persistence/Postgres/Entities/Share.cs @@ -24,7 +24,6 @@ namespace MiningCore.Persistence.Postgres.Entities { public class Share { - public long Id { get; set; } public string PoolId { get; set; } public long BlockHeight { get; set; } public string PayoutInfo { get; set; } From 2393f11964371032828d1b92a1c887ba1dadc4db Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 22 Feb 2018 10:58:39 +0100 Subject: [PATCH 096/348] Hashrate case consolidation --- src/MiningCore/Api/Responses/GetPoolStatsResponse.cs | 2 +- src/MiningCore/Blockchain/Abstractions.cs | 2 +- src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs | 4 ++-- src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs | 2 +- src/MiningCore/Blockchain/Monero/MoneroJobManager.cs | 2 +- src/MiningCore/Mining/PoolBase.cs | 2 +- src/MiningCore/Mining/PoolStats.cs | 2 +- src/MiningCore/Mining/StatsRecorder.cs | 2 +- src/MiningCore/Persistence/Model/PoolStats.cs | 4 ++-- src/MiningCore/Persistence/Postgres/Entities/PoolStats.cs | 4 ++-- src/MiningCore/Util/FormatUtil.cs | 8 ++++---- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/MiningCore/Api/Responses/GetPoolStatsResponse.cs b/src/MiningCore/Api/Responses/GetPoolStatsResponse.cs index b704ed1f2..9493c2221 100644 --- a/src/MiningCore/Api/Responses/GetPoolStatsResponse.cs +++ b/src/MiningCore/Api/Responses/GetPoolStatsResponse.cs @@ -24,7 +24,7 @@ namespace MiningCore.Api.Responses { public partial class AggregatedPoolStats { - public float PoolHashRate { get; set; } + public float PoolHashrate { get; set; } public int ConnectedMiners { get; set; } public int ValidSharesPerSecond { get; set; } diff --git a/src/MiningCore/Blockchain/Abstractions.cs b/src/MiningCore/Blockchain/Abstractions.cs index c86a32cbc..38e70ccc0 100644 --- a/src/MiningCore/Blockchain/Abstractions.cs +++ b/src/MiningCore/Blockchain/Abstractions.cs @@ -25,7 +25,7 @@ namespace MiningCore.Blockchain public class BlockchainStats { public string NetworkType { get; set; } - public double NetworkHashRate { get; set; } + public double NetworkHashrate { get; set; } public double NetworkDifficulty { get; set; } public DateTime? LastNetworkBlockTime { get; set; } public long BlockHeight { get; set; } diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 60752b458..f857c37ac 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -258,7 +258,7 @@ private async Task UpdateNetworkStatsAsync() var networkInfoResponse = results[2].Response.ToObject(); BlockchainStats.BlockHeight = infoResponse.Blocks; - BlockchainStats.NetworkHashRate = miningInfoResponse.NetworkHashps; + BlockchainStats.NetworkHashrate = miningInfoResponse.NetworkHashps; BlockchainStats.ConnectedPeers = networkInfoResponse.Connections; } @@ -375,7 +375,7 @@ private async Task UpdateNetworkStatsLegacyAsync() var connectionCountResponse = results[1].Response.ToObject(); BlockchainStats.BlockHeight = miningInfoResponse.Blocks; - //BlockchainStats.NetworkHashRate = miningInfoResponse.NetworkHashps; + //BlockchainStats.NetworkHashrate = miningInfoResponse.NetworkHashps; BlockchainStats.ConnectedPeers = (int) (long) connectionCountResponse; } diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs index b1fba4620..9a9e8ef95 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs @@ -287,7 +287,7 @@ private async Task UpdateNetworkStatsAsync() BlockchainStats.BlockHeight = block.Height.HasValue ? (long)block.Height.Value : -1; BlockchainStats.NetworkDifficulty = block.Difficulty.IntegralFromHex(); - BlockchainStats.NetworkHashRate = 0; // TODO + BlockchainStats.NetworkHashrate = 0; // TODO BlockchainStats.ConnectedPeers = peerCount; } diff --git a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs index 8c3bcc98a..9ae7e71dd 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs @@ -165,7 +165,7 @@ private async Task UpdateNetworkStatsAsync() BlockchainStats.BlockHeight = (int)info.Height; BlockchainStats.NetworkDifficulty = info.Difficulty; - BlockchainStats.NetworkHashRate = info.Target > 0 ? (double)info.Difficulty / info.Target : 0; + BlockchainStats.NetworkHashrate = info.Target > 0 ? (double)info.Difficulty / info.Target : 0; BlockchainStats.ConnectedPeers = info.OutgoingConnectionsCount + info.IncomingConnectionsCount; } diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index d4c731046..637ad9b22 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -389,7 +389,7 @@ private void OutputPoolInfo() Current Block Height: {blockchainStats.BlockHeight} Current Connect Peers: {blockchainStats.ConnectedPeers} Network Difficulty: {blockchainStats.NetworkDifficulty} -Network Hash Rate: {FormatUtil.FormatHashRate(blockchainStats.NetworkHashRate)} +Network Hash Rate: {FormatUtil.FormatHashrate(blockchainStats.NetworkHashrate)} Stratum Port(s): {string.Join(", ", poolConfig.Ports.Keys)} Pool Fee: {poolConfig.RewardRecipients.Sum(x => x.Percentage)}% "; diff --git a/src/MiningCore/Mining/PoolStats.cs b/src/MiningCore/Mining/PoolStats.cs index c8562396a..e9ab12fbd 100644 --- a/src/MiningCore/Mining/PoolStats.cs +++ b/src/MiningCore/Mining/PoolStats.cs @@ -26,7 +26,7 @@ public class PoolStats { public DateTime? LastPoolBlockTime { get; set; } public int ConnectedMiners { get; set; } - public ulong PoolHashRate { get; set; } + public ulong PoolHashrate { get; set; } public int ValidSharesPerSecond { get; set; } } } diff --git a/src/MiningCore/Mining/StatsRecorder.cs b/src/MiningCore/Mining/StatsRecorder.cs index b796b4a81..f4eb52332 100644 --- a/src/MiningCore/Mining/StatsRecorder.cs +++ b/src/MiningCore/Mining/StatsRecorder.cs @@ -158,7 +158,7 @@ private void UpdatePoolHashrates() // update pool.PoolStats.ConnectedMiners = byMiner.Length; - pool.PoolStats.PoolHashRate = (ulong) Math.Ceiling(poolHashrate); + pool.PoolStats.PoolHashrate = (ulong) Math.Ceiling(poolHashrate); pool.PoolStats.ValidSharesPerSecond = (int) (poolHashesCountAccumulated / windowActual); } } diff --git a/src/MiningCore/Persistence/Model/PoolStats.cs b/src/MiningCore/Persistence/Model/PoolStats.cs index 176bf01c1..ea7a25911 100644 --- a/src/MiningCore/Persistence/Model/PoolStats.cs +++ b/src/MiningCore/Persistence/Model/PoolStats.cs @@ -28,8 +28,8 @@ public class PoolStats public string PoolId { get; set; } public int ConnectedMiners { get; set; } - public float PoolHashRate { get; set; } - public double NetworkHashRate { get; set; } + public float PoolHashrate { get; set; } + public double NetworkHashrate { get; set; } public double NetworkDifficulty { get; set; } public DateTime? LastNetworkBlockTime { get; set; } public long BlockHeight { get; set; } diff --git a/src/MiningCore/Persistence/Postgres/Entities/PoolStats.cs b/src/MiningCore/Persistence/Postgres/Entities/PoolStats.cs index 21b8e9bbc..56db732c3 100644 --- a/src/MiningCore/Persistence/Postgres/Entities/PoolStats.cs +++ b/src/MiningCore/Persistence/Postgres/Entities/PoolStats.cs @@ -28,8 +28,8 @@ public class PoolStats public string PoolId { get; set; } public int ConnectedMiners { get; set; } - public float PoolHashRate { get; set; } - public double NetworkHashRate { get; set; } + public float PoolHashrate { get; set; } + public double NetworkHashrate { get; set; } public double NetworkDifficulty { get; set; } public DateTime? LastNetworkBlockTime { get; set; } public long BlockHeight { get; set; } diff --git a/src/MiningCore/Util/FormatUtil.cs b/src/MiningCore/Util/FormatUtil.cs index 175d4010e..e81176c96 100644 --- a/src/MiningCore/Util/FormatUtil.cs +++ b/src/MiningCore/Util/FormatUtil.cs @@ -6,12 +6,12 @@ namespace MiningCore.Util { public static class FormatUtil { - public static readonly string[] HashRateUnits = { " KH/s", " MH/s", " GH/s", " TH/s", " PH/s" }; + public static readonly string[] HashrateUnits = { " KH/s", " MH/s", " GH/s", " TH/s", " PH/s" }; public static readonly string[] DifficultyUnits = { " K", " M", " G", " T", " P" }; public static readonly string[] CapacityUnits = { " KB", " MB", " GB", " TB", " PB" }; public static readonly string[] QuantityUnits = { "K", "M", "B", "T", "Q" }; - public static string FormatHashRate(double hashrate) + public static string FormatHashrate(double hashrate) { var i = -1; @@ -19,9 +19,9 @@ public static string FormatHashRate(double hashrate) { hashrate = hashrate / 1024; i++; - } while(hashrate > 1024 && i < HashRateUnits.Length - 1); + } while(hashrate > 1024 && i < HashrateUnits.Length - 1); - return (int) Math.Abs(hashrate) + HashRateUnits[i]; + return (int) Math.Abs(hashrate) + HashrateUnits[i]; } public static string FormatDifficulty(double difficulty) From 2399b47b946dc1d7027aa7dddc528e93e15656d9 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 22 Feb 2018 12:15:51 +0100 Subject: [PATCH 097/348] BalanceChange API --- src/MiningCore/Api/ApiServer.cs | 31 ++++++++++++++++ .../Responses/GetBalanceChangesResponse.cs | 35 +++++++++++++++++++ src/MiningCore/AutoMapperProfile.cs | 2 ++ .../Repositories/PaymentRepository.cs | 13 +++++++ .../Persistence/Postgres/Scripts/createdb.sql | 2 +- .../Repositories/IPaymentRepository.cs | 1 + 6 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/MiningCore/Api/Responses/GetBalanceChangesResponse.cs diff --git a/src/MiningCore/Api/ApiServer.cs b/src/MiningCore/Api/ApiServer.cs index 7138fa742..de106013e 100644 --- a/src/MiningCore/Api/ApiServer.cs +++ b/src/MiningCore/Api/ApiServer.cs @@ -84,6 +84,7 @@ public ApiServer( { new Regex("^/api/pools/(?[^/]+)/payments$", RegexOptions.Compiled), PagePoolPaymentsAsync }, { new Regex("^/api/pools/(?[^/]+)$", RegexOptions.Compiled), GetPoolInfoAsync }, { new Regex("^/api/pools/(?[^/]+)/miners/(?
    [^/]+)/payments$", RegexOptions.Compiled), PageMinerPaymentsAsync }, + { new Regex("^/api/pools/(?[^/]+)/miners/(?
    [^/]+)/balancechanges$", RegexOptions.Compiled), PageMinerBalanceChangesAsync }, { new Regex("^/api/pools/(?[^/]+)/miners/(?
    [^/]+)/performance$", RegexOptions.Compiled), GetMinerPerformanceAsync }, { new Regex("^/api/pools/(?[^/]+)/miners/(?
    [^/]+)$", RegexOptions.Compiled), GetMinerInfoAsync }, @@ -485,6 +486,36 @@ private async Task PageMinerPaymentsAsync(HttpContext context, Match m) await SendJson(context, payments); } + private async Task PageMinerBalanceChangesAsync(HttpContext context, Match m) + { + var pool = GetPool(context, m); + if (pool == null) + return; + + var address = m.Groups["address"]?.Value; + if (string.IsNullOrEmpty(address)) + { + context.Response.StatusCode = 404; + return; + } + + var page = context.GetQueryParameter("page", 0); + var pageSize = context.GetQueryParameter("pageSize", 20); + + if (pageSize == 0) + { + context.Response.StatusCode = 500; + return; + } + + var balanceChanges = cf.Run(con => paymentsRepo.PageBalanceChanges( + con, pool.Id, address, page, pageSize)) + .Select(mapper.Map) + .ToArray(); + + await SendJson(context, balanceChanges); + } + private async Task GetMinerPerformanceAsync(HttpContext context, Match m) { var pool = GetPool(context, m); diff --git a/src/MiningCore/Api/Responses/GetBalanceChangesResponse.cs b/src/MiningCore/Api/Responses/GetBalanceChangesResponse.cs new file mode 100644 index 000000000..62d6dda89 --- /dev/null +++ b/src/MiningCore/Api/Responses/GetBalanceChangesResponse.cs @@ -0,0 +1,35 @@ +/* +Copyright 2017 Coin Foundry (coinfoundry.org) +Authors: Oliver Weichhold (oliver@weichhold.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +using System; +using MiningCore.Configuration; + +namespace MiningCore.Api.Responses +{ + public class BalanceChange + { + public string PoolId { get; set; } + public string Coin { get; set; } + public string Address { get; set; } + public decimal Amount { get; set; } + public string Usage { get; set; } + public DateTime Created { get; set; } + } +} diff --git a/src/MiningCore/AutoMapperProfile.cs b/src/MiningCore/AutoMapperProfile.cs index daf0d37aa..2817ccb44 100644 --- a/src/MiningCore/AutoMapperProfile.cs +++ b/src/MiningCore/AutoMapperProfile.cs @@ -56,6 +56,7 @@ public AutoMapperProfile() CreateMap(); CreateMap(); CreateMap(); + CreateMap(); CreateMap(); CreateMap(); @@ -84,6 +85,7 @@ public AutoMapperProfile() CreateMap(); CreateMap(); CreateMap(); + CreateMap(); CreateMap(); CreateMap(); CreateMap(); diff --git a/src/MiningCore/Persistence/Postgres/Repositories/PaymentRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/PaymentRepository.cs index 2e4ae0f74..f87a86349 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/PaymentRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/PaymentRepository.cs @@ -67,5 +67,18 @@ public Payment[] PagePayments(IDbConnection con, string poolId, string address, .Select(mapper.Map) .ToArray(); } + + public BalanceChange[] PageBalanceChanges(IDbConnection con, string poolId, string address, int page, int pageSize) + { + logger.LogInvoke(new[] { poolId }); + + var query = "SELECT * FROM balance_changes WHERE poolid = @poolid " + + "AND address = @address " + + "ORDER BY created DESC OFFSET @offset FETCH NEXT (@pageSize) ROWS ONLY"; + + return con.Query(query, new { poolId, address, offset = page * pageSize, pageSize }) + .Select(mapper.Map) + .ToArray(); + } } } diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index b557f95fb..0f686fdd0 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -59,7 +59,7 @@ CREATE TABLE balance_changes created TIMESTAMP NOT NULL ); -CREATE INDEX IDX_BALANCE_CHANGES_POOL_ADDRESS_CREATED on balance_changes(poolid, miner, created desc); +CREATE INDEX IDX_BALANCE_CHANGES_POOL_ADDRESS_CREATED on balance_changes(poolid, address, created desc); CREATE TABLE payments ( diff --git a/src/MiningCore/Persistence/Repositories/IPaymentRepository.cs b/src/MiningCore/Persistence/Repositories/IPaymentRepository.cs index ed88d9c9d..7ef308038 100644 --- a/src/MiningCore/Persistence/Repositories/IPaymentRepository.cs +++ b/src/MiningCore/Persistence/Repositories/IPaymentRepository.cs @@ -28,5 +28,6 @@ public interface IPaymentRepository void Insert(IDbConnection con, IDbTransaction tx, Payment payment); Payment[] PagePayments(IDbConnection con, string poolId, string address, int page, int pageSize); + BalanceChange[] PageBalanceChanges(IDbConnection con, string poolId, string address, int page, int pageSize); } } From c074c60ec066ef94b4c7c1c47c587e53150049ee Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 22 Feb 2018 15:04:52 +0100 Subject: [PATCH 098/348] WIP --- .../Postgres/Repositories/StatsRepository.cs | 73 +++++++------------ 1 file changed, 26 insertions(+), 47 deletions(-) diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index ffb5bb77e..966f92566 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -217,32 +217,22 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenHourly(IDbCon SharesPerSecond = y.SharesPerSecond }) }) - .OrderBy(x=> x.Created) - .ToArray(); + .ToDictionary(x=> x.Created, x=> x); // fill in blanks - //var result = new List(); - //var lastCreated = start; - //var maxItemCount = 24; - - //foreach (var item in tmp) - //{ - // while (result.Count < maxItemCount && - // (item.Created - lastCreated > TimeSpan.FromHours(1))) - // { - // result.Add(new WorkerPerformanceStatsContainer { Created = lastCreated }); - // lastCreated = lastCreated.AddHours(1); - // } - - // if (result.Count >= maxItemCount) - // break; - - // result.Add(item); - // lastCreated = item.Created; - //} - - //return result.ToArray(); - return tmp; + var result = new List(); + + for (var i = 0; i < 24; i++) + { + if(tmp.TryGetValue(end, out var item)) + result.Insert(0, item); + else + result.Add(new WorkerPerformanceStatsContainer { Created = end }); + + end = end.AddHours(-1); + } + + return result.ToArray(); } public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConnection con, string poolId, string miner, DateTime start, DateTime end) @@ -268,33 +258,22 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConn SharesPerSecond = y.SharesPerSecond }) }) - .OrderBy(x => x.Created) - .ToArray(); + .ToDictionary(x => x.Created, x => x); - //// fill in blanks - //var result = new List(); - //var lastCreated = start; - //var maxItemCount = 31; - - //foreach (var item in tmp) - //{ - // while (result.Count < maxItemCount && - // (item.Created - lastCreated > TimeSpan.FromDays(1))) - // { - // result.Add(new WorkerPerformanceStatsContainer { Created = lastCreated }); - // lastCreated = lastCreated.AddDays(1); - // } - - // if (result.Count >= maxItemCount) - // break; + // fill in blanks + var result = new List(); - // result.Add(item); - // lastCreated = item.Created; - //} + for (var i = 0; i < 30; i++) + { + if (tmp.TryGetValue(end, out var item)) + result.Insert(0, item); + else + result.Add(new WorkerPerformanceStatsContainer { Created = end }); - //return result.ToArray(); + end = end.AddDays(-1); + } - return tmp; + return result.ToArray(); } public MinerWorkerPerformanceStats[] PagePoolMinersByHashrate(IDbConnection con, string poolId, DateTime from, int page, int pageSize) From b6532aeb55930f54f10283054b08a0709cf713f2 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 22 Feb 2018 15:07:50 +0100 Subject: [PATCH 099/348] WIP --- .../Persistence/Postgres/Repositories/StatsRepository.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index 966f92566..d700a0871 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -29,6 +29,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Persistence.Model.Projections; using MiningCore.Persistence.Repositories; using MiningCore.Time; +using NBitcoin; using NLog; using MinerStats = MiningCore.Persistence.Model.Projections.MinerStats; @@ -217,14 +218,14 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenHourly(IDbCon SharesPerSecond = y.SharesPerSecond }) }) - .ToDictionary(x=> x.Created, x=> x); + .ToDictionary(x=> x.Created.UnixTimestamp(), x=> x); // fill in blanks var result = new List(); for (var i = 0; i < 24; i++) { - if(tmp.TryGetValue(end, out var item)) + if(tmp.TryGetValue(end.UnixTimestamp(), out var item)) result.Insert(0, item); else result.Add(new WorkerPerformanceStatsContainer { Created = end }); @@ -258,14 +259,14 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConn SharesPerSecond = y.SharesPerSecond }) }) - .ToDictionary(x => x.Created, x => x); + .ToDictionary(x => x.Created.UnixTimestamp(), x => x); // fill in blanks var result = new List(); for (var i = 0; i < 30; i++) { - if (tmp.TryGetValue(end, out var item)) + if (tmp.TryGetValue(end.UnixTimestamp(), out var item)) result.Insert(0, item); else result.Add(new WorkerPerformanceStatsContainer { Created = end }); From b1705d66c51e3345660822dca24702c327c0ef88 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 22 Feb 2018 15:10:35 +0100 Subject: [PATCH 100/348] WIP --- .../Postgres/Repositories/StatsRepository.cs | 53 ++++++++++--------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index d700a0871..c37ac8811 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -218,22 +218,24 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenHourly(IDbCon SharesPerSecond = y.SharesPerSecond }) }) - .ToDictionary(x=> x.Created.UnixTimestamp(), x=> x); + .ToArray(); + //.ToDictionary(x=> x.Created.UnixTimestamp(), x=> x); - // fill in blanks - var result = new List(); + //// fill in blanks + //var result = new List(); - for (var i = 0; i < 24; i++) - { - if(tmp.TryGetValue(end.UnixTimestamp(), out var item)) - result.Insert(0, item); - else - result.Add(new WorkerPerformanceStatsContainer { Created = end }); + //for (var i = 0; i < 24; i++) + //{ + // if(tmp.TryGetValue(end.UnixTimestamp(), out var item)) + // result.Insert(0, item); + // else + // result.Add(new WorkerPerformanceStatsContainer { Created = end }); - end = end.AddHours(-1); - } + // end = end.AddHours(-1); + //} - return result.ToArray(); + //return result.ToArray(); + return tmp; } public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConnection con, string poolId, string miner, DateTime start, DateTime end) @@ -259,22 +261,25 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConn SharesPerSecond = y.SharesPerSecond }) }) - .ToDictionary(x => x.Created.UnixTimestamp(), x => x); + .ToArray(); + //.ToDictionary(x => x.Created.UnixTimestamp(), x => x); - // fill in blanks - var result = new List(); + //// fill in blanks + //var result = new List(); - for (var i = 0; i < 30; i++) - { - if (tmp.TryGetValue(end.UnixTimestamp(), out var item)) - result.Insert(0, item); - else - result.Add(new WorkerPerformanceStatsContainer { Created = end }); + //for (var i = 0; i < 30; i++) + //{ + // if (tmp.TryGetValue(end.UnixTimestamp(), out var item)) + // result.Insert(0, item); + // else + // result.Add(new WorkerPerformanceStatsContainer { Created = end }); - end = end.AddDays(-1); - } + // end = end.AddDays(-1); + //} + + //return result.ToArray(); - return result.ToArray(); + return tmp; } public MinerWorkerPerformanceStats[] PagePoolMinersByHashrate(IDbConnection con, string poolId, DateTime from, int page, int pageSize) From ef1a155e95b62e2981236df155a4574fb9515586 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 22 Feb 2018 15:36:47 +0100 Subject: [PATCH 101/348] WIP --- src/MiningCore/Api/ApiServer.cs | 12 ++--- .../Postgres/Repositories/StatsRepository.cs | 53 +++++++++---------- 2 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/MiningCore/Api/ApiServer.cs b/src/MiningCore/Api/ApiServer.cs index de106013e..17587ab0c 100644 --- a/src/MiningCore/Api/ApiServer.cs +++ b/src/MiningCore/Api/ApiServer.cs @@ -185,15 +185,14 @@ private async Task HandleRequest(HttpContext context) private WorkerPerformanceStatsContainer[] GetMinerPerformanceInternal(string mode, PoolConfig pool, string address) { Persistence.Model.Projections.WorkerPerformanceStatsContainer[] stats; + var end = clock.Now; if (mode == "day" || mode != "month") { // set range -#if DEBUG - var end = new DateTime(2018, 1, 7, 16, 0, 0); -#else - var end = clock.Now; // new DateTime(2018, 1, 7, 16, 0, 0); -#endif + end = end.AddMinutes(-end.Minute); + end = end.AddSeconds(-end.Second); + var start = end.AddDays(-1); stats = cf.Run(con => statsRepo.GetMinerPerformanceBetweenHourly( @@ -202,8 +201,9 @@ private WorkerPerformanceStatsContainer[] GetMinerPerformanceInternal(string mod else { + end = end.Date; + // set range - var end = clock.Now; var start = end.AddMonths(-1); stats = cf.Run(con => statsRepo.GetMinerPerformanceBetweenDaily( diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index c37ac8811..681839254 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -218,24 +218,22 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenHourly(IDbCon SharesPerSecond = y.SharesPerSecond }) }) - .ToArray(); - //.ToDictionary(x=> x.Created.UnixTimestamp(), x=> x); + .ToDictionary(x=> x.Created.ToUnixTimestamp(), x=> x); - //// fill in blanks - //var result = new List(); + // fill in blanks + var result = new List(); - //for (var i = 0; i < 24; i++) - //{ - // if(tmp.TryGetValue(end.UnixTimestamp(), out var item)) - // result.Insert(0, item); - // else - // result.Add(new WorkerPerformanceStatsContainer { Created = end }); + for (var i = 0; i < 24; i++) + { + if(tmp.TryGetValue(end.ToUnixTimestamp(), out var item)) + result.Insert(0, item); + else + result.Add(new WorkerPerformanceStatsContainer { Created = end, Workers = new Dictionary() }); - // end = end.AddHours(-1); - //} + end = end.AddHours(-1); + } - //return result.ToArray(); - return tmp; + return result.ToArray(); } public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConnection con, string poolId, string miner, DateTime start, DateTime end) @@ -261,25 +259,22 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConn SharesPerSecond = y.SharesPerSecond }) }) - .ToArray(); - //.ToDictionary(x => x.Created.UnixTimestamp(), x => x); - - //// fill in blanks - //var result = new List(); + .ToDictionary(x => x.Created.ToUnixTimestamp(), x => x); - //for (var i = 0; i < 30; i++) - //{ - // if (tmp.TryGetValue(end.UnixTimestamp(), out var item)) - // result.Insert(0, item); - // else - // result.Add(new WorkerPerformanceStatsContainer { Created = end }); + // fill in blanks + var result = new List(); - // end = end.AddDays(-1); - //} + for (var i = 0; i < 30; i++) + { + if (tmp.TryGetValue(end.ToUnixTimestamp(), out var item)) + result.Insert(0, item); + else + result.Add(new WorkerPerformanceStatsContainer { Created = end, Workers = new Dictionary() }); - //return result.ToArray(); + end = end.AddDays(-1); + } - return tmp; + return result.ToArray(); } public MinerWorkerPerformanceStats[] PagePoolMinersByHashrate(IDbConnection con, string poolId, DateTime from, int page, int pageSize) From 4a38bd257d910a69645510003fa4780cd1aa21fa Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 22 Feb 2018 15:41:01 +0100 Subject: [PATCH 102/348] WIP --- src/MiningCore/Api/ApiServer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/MiningCore/Api/ApiServer.cs b/src/MiningCore/Api/ApiServer.cs index 17587ab0c..87657d684 100644 --- a/src/MiningCore/Api/ApiServer.cs +++ b/src/MiningCore/Api/ApiServer.cs @@ -192,6 +192,7 @@ private WorkerPerformanceStatsContainer[] GetMinerPerformanceInternal(string mod // set range end = end.AddMinutes(-end.Minute); end = end.AddSeconds(-end.Second); + end = end.AddHours(-1); var start = end.AddDays(-1); @@ -202,6 +203,7 @@ private WorkerPerformanceStatsContainer[] GetMinerPerformanceInternal(string mod else { end = end.Date; + end = end.AddDays(-1); // set range var start = end.AddMonths(-1); From d0019a5bbaaf80f0f7403acfc5ecf31a52ee4426 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 22 Feb 2018 15:50:41 +0100 Subject: [PATCH 103/348] WIP --- src/MiningCore/Api/ApiServer.cs | 8 ++++++-- .../Persistence/Postgres/Repositories/StatsRepository.cs | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/MiningCore/Api/ApiServer.cs b/src/MiningCore/Api/ApiServer.cs index 87657d684..adcb685b5 100644 --- a/src/MiningCore/Api/ApiServer.cs +++ b/src/MiningCore/Api/ApiServer.cs @@ -190,9 +190,11 @@ private WorkerPerformanceStatsContainer[] GetMinerPerformanceInternal(string mod if (mode == "day" || mode != "month") { // set range + if(end.Minute < 30) + end = end.AddHours(-1); + end = end.AddMinutes(-end.Minute); end = end.AddSeconds(-end.Second); - end = end.AddHours(-1); var start = end.AddDays(-1); @@ -202,8 +204,10 @@ private WorkerPerformanceStatsContainer[] GetMinerPerformanceInternal(string mod else { + if(end.Hour < 12) + end = end.AddDays(-1); + end = end.Date; - end = end.AddDays(-1); // set range var start = end.AddMonths(-1); diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index 681839254..4c40222e5 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -218,7 +218,7 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenHourly(IDbCon SharesPerSecond = y.SharesPerSecond }) }) - .ToDictionary(x=> x.Created.ToUnixTimestamp(), x=> x); + .ToDictionary(x=> x.Created.ToUniversalTime().ToUnixTimestamp(), x=> x); // fill in blanks var result = new List(); @@ -259,7 +259,7 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConn SharesPerSecond = y.SharesPerSecond }) }) - .ToDictionary(x => x.Created.ToUnixTimestamp(), x => x); + .ToDictionary(x => x.Created.ToUniversalTime().ToUnixTimestamp(), x => x); // fill in blanks var result = new List(); From e08297c0afe299c6212a91e5c764b71bb9699773 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 22 Feb 2018 15:57:58 +0100 Subject: [PATCH 104/348] WIP --- .../Postgres/Repositories/StatsRepository.cs | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index 4c40222e5..0974ed0df 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -218,22 +218,24 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenHourly(IDbCon SharesPerSecond = y.SharesPerSecond }) }) - .ToDictionary(x=> x.Created.ToUniversalTime().ToUnixTimestamp(), x=> x); + .ToArray(); + //.ToDictionary(x=> x.Created.ToUniversalTime().ToUnixTimestamp(), x=> x); - // fill in blanks - var result = new List(); + //// fill in blanks + //var result = new List(); - for (var i = 0; i < 24; i++) - { - if(tmp.TryGetValue(end.ToUnixTimestamp(), out var item)) - result.Insert(0, item); - else - result.Add(new WorkerPerformanceStatsContainer { Created = end, Workers = new Dictionary() }); + //for (var i = 0; i < 24; i++) + //{ + // if(tmp.TryGetValue(end.ToUnixTimestamp(), out var item)) + // result.Insert(0, item); + // else + // result.Add(new WorkerPerformanceStatsContainer { Created = end, Workers = new Dictionary() }); - end = end.AddHours(-1); - } + // end = end.AddHours(-1); + //} - return result.ToArray(); + //return result.ToArray(); + return tmp; } public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConnection con, string poolId, string miner, DateTime start, DateTime end) @@ -259,22 +261,24 @@ public WorkerPerformanceStatsContainer[] GetMinerPerformanceBetweenDaily(IDbConn SharesPerSecond = y.SharesPerSecond }) }) - .ToDictionary(x => x.Created.ToUniversalTime().ToUnixTimestamp(), x => x); + .ToArray(); + //.ToDictionary(x => x.Created.ToUniversalTime().ToUnixTimestamp(), x => x); - // fill in blanks - var result = new List(); + //// fill in blanks + //var result = new List(); - for (var i = 0; i < 30; i++) - { - if (tmp.TryGetValue(end.ToUnixTimestamp(), out var item)) - result.Insert(0, item); - else - result.Add(new WorkerPerformanceStatsContainer { Created = end, Workers = new Dictionary() }); + //for (var i = 0; i < 30; i++) + //{ + // if (tmp.TryGetValue(end.ToUnixTimestamp(), out var item)) + // result.Insert(0, item); + // else + // result.Add(new WorkerPerformanceStatsContainer { Created = end, Workers = new Dictionary() }); - end = end.AddDays(-1); - } + // end = end.AddDays(-1); + //} - return result.ToArray(); + //return result.ToArray(); + return tmp; } public MinerWorkerPerformanceStats[] PagePoolMinersByHashrate(IDbConnection con, string poolId, DateTime from, int page, int pageSize) From b391f914c8c1bc811ff79c56e95c3809026ab435 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 23 Feb 2018 15:28:31 +0100 Subject: [PATCH 105/348] ZCash shielding for coins other than ZCash --- .../Blockchain/Bitcoin/BitcoinConstants.cs | 2 + .../Blockchain/Bitcoin/BitcoinJobManager.cs | 2 +- .../Blockchain/ZCash/ZCashPayoutHandler.cs | 128 +++++++++++++++++- 3 files changed, 129 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs index 9ab266014..ea8ea6925 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs @@ -69,6 +69,8 @@ public class BitcoinConstants public static readonly BigInteger Diff1 = BigInteger.Parse("00ffff0000000000000000000000000000000000000000000000000000", NumberStyles.HexNumber); public const int CoinbaseMinConfimations = 102; + public const int ErrorMethodNotFound = -32601; + public const string ZmqPublisherTopicBlockHash = "hashblock"; public const string ZmqPublisherTopicTxHash = "hashtx"; public const string ZmqPublisherTopicBlockRaw = "rawblock"; diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index f857c37ac..b2f470980 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -566,7 +566,7 @@ protected override async Task AreDaemonsConnectedAsync() var response = await daemon.ExecuteCmdAnyAsync(BitcoinCommands.GetNetworkInfo); - return response.Error == null && response.Response.Connections > 0; + return response.Error == null && response.Response?.Connections > 0; } protected override async Task EnsureDaemonsSynchedAsync() diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs index 4b764a623..95a6fef26 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs @@ -25,6 +25,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using Autofac; using AutoMapper; using MiningCore.Blockchain.Bitcoin; +using MiningCore.Blockchain.Bitcoin.DaemonResponses; using MiningCore.Blockchain.ZCash.Configuration; using MiningCore.Blockchain.ZCash.DaemonRequests; using MiningCore.Blockchain.ZCash.DaemonResponses; @@ -35,6 +36,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Persistence.Model; using MiningCore.Persistence.Repositories; using MiningCore.Time; +using Newtonsoft.Json.Linq; +using Block = MiningCore.Persistence.Model.Block; using Contract = MiningCore.Contracts.Contract; namespace MiningCore.Blockchain.ZCash @@ -56,7 +59,10 @@ public ZCashPayoutHandler( { } - private ZCashPoolConfigExtra poolExtraConfig; + protected ZCashPoolConfigExtra poolExtraConfig; + protected bool supportsNativeShielding; + protected BitcoinNetworkType networkType; + protected ZCashCoinbaseTxConfig coinbaseTxConfig; protected override string LogCategory => "ZCash Payout Handler"; protected const decimal TransferFee = 0.0001m; @@ -67,11 +73,32 @@ public override async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfi await base.ConfigureAsync(clusterConfig, poolConfig); poolExtraConfig = poolConfig.Extra.SafeExtensionDataAs(); + + // detect network + var blockchainInfoResponse = await daemon.ExecuteCmdSingleAsync(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.CoinbaseTxConfig.TryGetValue(poolConfig.Coin.Type, out var coinbaseTx)) + coinbaseTx.TryGetValue(networkType, out coinbaseTxConfig); + + // detect z_shieldcoinbase support + var response = await daemon.ExecuteCmdSingleAsync(ZCashCommands.ZShieldCoinbase); + supportsNativeShielding = response.Error.Code != BitcoinConstants.ErrorMethodNotFound; } public override async Task UpdateBlockRewardBalancesAsync(IDbConnection con, IDbTransaction tx, Block block, PoolConfig pool) { - await ShieldCoinbaseAsync(); + if (supportsNativeShielding) + await ShieldCoinbaseAsync(); + else + await ShieldCoinbaseEmulatedAsync(); return await base.UpdateBlockRewardBalancesAsync(con, tx, block, pool); } @@ -248,5 +275,102 @@ private async Task ShieldCoinbaseAsync() await Task.Delay(TimeSpan.FromSeconds(10)); } } + + private async Task ShieldCoinbaseEmulatedAsync() + { + logger.Info(() => $"[{LogCategory}] Shielding ZCash Coinbase funds (emulated)"); + + // get t-addr balance + var balanceResult = await daemon.ExecuteCmdSingleAsync(BitcoinCommands.GetBalance); + + if (balanceResult.Error != null) + { + logger.Error(() => $"[{LogCategory}] {BitcoinCommands.GetBalance} returned error: {balanceResult.Error.Message} code {balanceResult.Error.Code}"); + return; + } + + var balance = (decimal)(double)balanceResult.Response; + + // make sure there's enough balance to shield after reserves + if (balance - TransferFee <= TransferFee) + { + logger.Info(() => $"[{LogCategory}] Balance {FormatAmount(balance)} too small for emulated shielding"); + return; + } + + if (balance > 0) + { + logger.Info(() => $"[{LogCategory}] Transferring {FormatAmount(balance - TransferFee)} to pool's z-addr"); + + // transfer to z-addr + var recipient = new ZSendManyRecipient + { + Address = poolExtraConfig.ZAddress, + Amount = balance - TransferFee + }; + + var args = new object[] + { + poolConfig.Address, // default account + new object[] // addresses and associated amounts + { + recipient + } + }; + + // send command + var sendResult = await daemon.ExecuteCmdSingleAsync(ZCashCommands.ZSendMany, args); + + if (sendResult.Error != null) + { + logger.Error(() => $"[{LogCategory}] {ZCashCommands.ZSendMany} returned error: {balanceResult.Error.Message} code {balanceResult.Error.Code}"); + return; + } + + var operationId = sendResult.Response; + + logger.Info(() => $"[{LogCategory}] {ZCashCommands.ZSendMany} operation id: {operationId}"); + + var continueWaiting = true; + + while (continueWaiting) + { + var operationResultResponse = await daemon.ExecuteCmdSingleAsync( + ZCashCommands.ZGetOperationResult, new object[] { new object[] { operationId } }); + + if (operationResultResponse.Error == null && + operationResultResponse.Response?.Any(x => x.OperationId == operationId) == true) + { + var operationResult = operationResultResponse.Response.First(x => x.OperationId == operationId); + + if (!Enum.TryParse(operationResult.Status, true, out ZOperationStatus status)) + { + logger.Error(() => $"Unrecognized operation status: {operationResult.Status}"); + break; + } + + switch (status) + { + case ZOperationStatus.Success: + var txId = operationResult.Result?.Value("txid") ?? string.Empty; + logger.Info(() => $"[{LogCategory}] Transfer completed with transaction id: {txId}"); + + continueWaiting = false; + continue; + + case ZOperationStatus.Cancelled: + case ZOperationStatus.Failed: + logger.Error(() => $"{ZCashCommands.ZSendMany} failed: {operationResult.Error.Message} code {operationResult.Error.Code}"); + + continueWaiting = false; + continue; + } + } + + logger.Info(() => $"[{LogCategory}] Waiting for transfer completion: {operationId}"); + await Task.Delay(TimeSpan.FromSeconds(10)); + } + } + } } } From e1636afc2a18698f2545dbd79c0213c07961f087 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 23 Feb 2018 15:36:32 +0100 Subject: [PATCH 106/348] WIP --- .../Blockchain/ZCash/ZCashPayoutHandler.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs index 95a6fef26..23b0287b8 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs @@ -93,20 +93,16 @@ public override async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfi supportsNativeShielding = response.Error.Code != BitcoinConstants.ErrorMethodNotFound; } - public override async Task UpdateBlockRewardBalancesAsync(IDbConnection con, IDbTransaction tx, Block block, PoolConfig pool) + public override async Task PayoutAsync(Balance[] balances) { + Contract.RequiresNonNull(balances, nameof(balances)); + + // Shield first if (supportsNativeShielding) await ShieldCoinbaseAsync(); else await ShieldCoinbaseEmulatedAsync(); - return await base.UpdateBlockRewardBalancesAsync(con, tx, block, pool); - } - - public override async Task PayoutAsync(Balance[] balances) - { - Contract.RequiresNonNull(balances, nameof(balances)); - // send in batches with no more than 50 recipients to avoid running into tx size limits var pageSize = 50; var pageCount = (int)Math.Ceiling(balances.Length / (double)pageSize); From e0eeffa601242db6fcb4c156753e9ea3b9ac5052 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 23 Feb 2018 16:10:08 +0100 Subject: [PATCH 107/348] WIP --- .../Blockchain/ZCash/ZCashPayoutHandler.cs | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs index 23b0287b8..36ee920a1 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs @@ -19,7 +19,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; -using System.Data; using System.Linq; using System.Threading.Tasks; using Autofac; @@ -37,7 +36,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Persistence.Repositories; using MiningCore.Time; using Newtonsoft.Json.Linq; -using Block = MiningCore.Persistence.Model.Block; using Contract = MiningCore.Contracts.Contract; namespace MiningCore.Blockchain.ZCash @@ -65,6 +63,7 @@ public ZCashPayoutHandler( protected ZCashCoinbaseTxConfig coinbaseTxConfig; protected override string LogCategory => "ZCash Payout Handler"; protected const decimal TransferFee = 0.0001m; + protected const int ZMinConfirmations = 8; #region IPayoutHandler @@ -124,13 +123,28 @@ public override async Task PayoutAsync(Balance[] balances) if (amounts.Count == 0) return; - logger.Info(() => $"[{LogCategory}] Paying out {FormatAmount(page.Sum(x => x.Amount))} to {page.Length} addresses"); + var pageAmount = amounts.Sum(x => x.Amount); + + // check shielded balance + var balanceResult = await daemon.ExecuteCmdSingleAsync(ZCashCommands.ZGetBalance, new object[] + { + poolExtraConfig.ZAddress, // default account + ZMinConfirmations, // only spend funds covered by this many confirmations + }); + + if (balanceResult.Error != null || (decimal) (double) balanceResult.Response - TransferFee < pageAmount) + { + logger.Info(() => $"[{LogCategory}] Insufficient shielded balance for payment of {FormatAmount(pageAmount)}"); + return; + } + + logger.Info(() => $"[{LogCategory}] Paying out {FormatAmount(pageAmount)} to {page.Length} addresses"); var args = new object[] { poolExtraConfig.ZAddress, // default account amounts, // addresses and associated amounts - 10, // only spend funds covered by this many confirmations + ZMinConfirmations, // only spend funds covered by this many confirmations TransferFee }; From b834f6e40719dd8e949660007c8db7e9716bb70b Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 23 Feb 2018 16:16:26 +0100 Subject: [PATCH 108/348] WIP --- src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs index 36ee920a1..83f222b27 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs @@ -304,7 +304,9 @@ private async Task ShieldCoinbaseEmulatedAsync() // make sure there's enough balance to shield after reserves if (balance - TransferFee <= TransferFee) { - logger.Info(() => $"[{LogCategory}] Balance {FormatAmount(balance)} too small for emulated shielding"); + if(balance > 0) + logger.Info(() => $"[{LogCategory}] Balance {FormatAmount(balance)} too small for emulated shielding"); + return; } From aa99c627b1a8d403ec2f8ea3c4005abd78a8c3bb Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 23 Feb 2018 16:28:16 +0100 Subject: [PATCH 109/348] WIP --- src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs index 83f222b27..36b6a3577 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs @@ -184,7 +184,7 @@ public override async Task PayoutAsync(Balance[] balances) { case ZOperationStatus.Success: var txId = operationResult.Result?.Value("txid") ?? string.Empty; - logger.Info(() => $"[{LogCategory}] completed with transaction id: {txId}"); + logger.Info(() => $"[{LogCategory}] {ZCashCommands.ZSendMany} completed with transaction id: {txId}"); PersistPayments(page, txId); NotifyPayoutSuccess(poolConfig.Id, page, new[] {txId}, null); From a7a3b32697b301676fcfb2e53b3d5ea5ae7e334f Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 26 Feb 2018 14:58:44 +0100 Subject: [PATCH 110/348] Fixed t-addr balance calculation for ShieldCoinbaseEmulatedAsync --- .../Blockchain/Bitcoin/BitcoinConstants.cs | 1 + .../Blockchain/Bitcoin/DaemonResponses/Utxo.cs | 17 +++++++++++++++++ .../Blockchain/ZCash/ZCashPayoutHandler.cs | 14 ++++++++------ 3 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 src/MiningCore/Blockchain/Bitcoin/DaemonResponses/Utxo.cs diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs index ea8ea6925..86dfd9f70 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs @@ -108,6 +108,7 @@ public class KnownAddresses public static class BitcoinCommands { public const string GetBalance = "getbalance"; + public const string ListUnspent = "listunspent"; public const string GetNetworkInfo = "getnetworkinfo"; public const string GetMiningInfo = "getmininginfo"; public const string GetPeerInfo = "getpeerinfo"; diff --git a/src/MiningCore/Blockchain/Bitcoin/DaemonResponses/Utxo.cs b/src/MiningCore/Blockchain/Bitcoin/DaemonResponses/Utxo.cs new file mode 100644 index 000000000..bd1a87a2e --- /dev/null +++ b/src/MiningCore/Blockchain/Bitcoin/DaemonResponses/Utxo.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MiningCore.Blockchain.Bitcoin.DaemonResponses +{ + public class Utxo + { + public string TxId { get; set; } + public int Vout { get; set; } + public string Address { get; set; } + public decimal Amount { get; set; } + public string ScriptPubKey { get; set; } + public int Confirmations { get; set; } + public bool Spendable { get; set; } + } +} diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs index 36b6a3577..71a7f7b40 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs @@ -290,16 +290,18 @@ private async Task ShieldCoinbaseEmulatedAsync() { logger.Info(() => $"[{LogCategory}] Shielding ZCash Coinbase funds (emulated)"); - // get t-addr balance - var balanceResult = await daemon.ExecuteCmdSingleAsync(BitcoinCommands.GetBalance); + // get t-addr unspent balance for just the coinbase address (pool wallet) + var unspentResult = await daemon.ExecuteCmdSingleAsync(BitcoinCommands.ListUnspent); - if (balanceResult.Error != null) + if (unspentResult.Error != null) { - logger.Error(() => $"[{LogCategory}] {BitcoinCommands.GetBalance} returned error: {balanceResult.Error.Message} code {balanceResult.Error.Code}"); + logger.Error(() => $"[{LogCategory}] {BitcoinCommands.ListUnspent} returned error: {unspentResult.Error.Message} code {unspentResult.Error.Code}"); return; } - var balance = (decimal)(double)balanceResult.Response; + var balance = unspentResult.Response + .Where(x=> x.Spendable && x.Address == poolConfig.Address) + .Sum(x=> x.Amount); // make sure there's enough balance to shield after reserves if (balance - TransferFee <= TransferFee) @@ -335,7 +337,7 @@ private async Task ShieldCoinbaseEmulatedAsync() if (sendResult.Error != null) { - logger.Error(() => $"[{LogCategory}] {ZCashCommands.ZSendMany} returned error: {balanceResult.Error.Message} code {balanceResult.Error.Code}"); + logger.Error(() => $"[{LogCategory}] {ZCashCommands.ZSendMany} returned error: {unspentResult.Error.Message} code {unspentResult.Error.Code}"); return; } From 0b56d5c59e2f2b511d931ac22ed586eb81e2e092 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 26 Feb 2018 15:19:13 +0100 Subject: [PATCH 111/348] WIP --- .../Blockchain/ZCash/ZCashPayoutHandler.cs | 109 +++++++++--------- 1 file changed, 53 insertions(+), 56 deletions(-) diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs index 71a7f7b40..ae1b0fd43 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs @@ -306,84 +306,81 @@ private async Task ShieldCoinbaseEmulatedAsync() // make sure there's enough balance to shield after reserves if (balance - TransferFee <= TransferFee) { - if(balance > 0) - logger.Info(() => $"[{LogCategory}] Balance {FormatAmount(balance)} too small for emulated shielding"); - + logger.Info(() => $"[{LogCategory}] Balance {FormatAmount(balance)} too small for emulated shielding"); return; } - if (balance > 0) + logger.Info(() => $"[{LogCategory}] Transferring {FormatAmount(balance - TransferFee)} to pool's z-addr"); + + // transfer to z-addr + var recipient = new ZSendManyRecipient { - logger.Info(() => $"[{LogCategory}] Transferring {FormatAmount(balance - TransferFee)} to pool's z-addr"); + Address = poolExtraConfig.ZAddress, + Amount = balance - TransferFee + }; - // transfer to z-addr - var recipient = new ZSendManyRecipient + var args = new object[] + { + poolConfig.Address, // default account + new object[] // addresses and associated amounts { - Address = poolExtraConfig.ZAddress, - Amount = balance - TransferFee - }; + recipient + }, + 1, + TransferFee + }; - var args = new object[] - { - poolConfig.Address, // default account - new object[] // addresses and associated amounts - { - recipient - } - }; + // send command + var sendResult = await daemon.ExecuteCmdSingleAsync(ZCashCommands.ZSendMany, args); - // send command - var sendResult = await daemon.ExecuteCmdSingleAsync(ZCashCommands.ZSendMany, args); + if (sendResult.Error != null) + { + logger.Error(() => $"[{LogCategory}] {ZCashCommands.ZSendMany} returned error: {unspentResult.Error.Message} code {unspentResult.Error.Code}"); + return; + } - if (sendResult.Error != null) - { - logger.Error(() => $"[{LogCategory}] {ZCashCommands.ZSendMany} returned error: {unspentResult.Error.Message} code {unspentResult.Error.Code}"); - return; - } + var operationId = sendResult.Response; - var operationId = sendResult.Response; + logger.Info(() => $"[{LogCategory}] {ZCashCommands.ZSendMany} operation id: {operationId}"); - logger.Info(() => $"[{LogCategory}] {ZCashCommands.ZSendMany} operation id: {operationId}"); + var continueWaiting = true; - var continueWaiting = true; + while (continueWaiting) + { + var operationResultResponse = await daemon.ExecuteCmdSingleAsync( + ZCashCommands.ZGetOperationResult, new object[] { new object[] { operationId } }); - while (continueWaiting) + if (operationResultResponse.Error == null && + operationResultResponse.Response?.Any(x => x.OperationId == operationId) == true) { - var operationResultResponse = await daemon.ExecuteCmdSingleAsync( - ZCashCommands.ZGetOperationResult, new object[] { new object[] { operationId } }); + var operationResult = operationResultResponse.Response.First(x => x.OperationId == operationId); - if (operationResultResponse.Error == null && - operationResultResponse.Response?.Any(x => x.OperationId == operationId) == true) + if (!Enum.TryParse(operationResult.Status, true, out ZOperationStatus status)) { - var operationResult = operationResultResponse.Response.First(x => x.OperationId == operationId); - - if (!Enum.TryParse(operationResult.Status, true, out ZOperationStatus status)) - { - logger.Error(() => $"Unrecognized operation status: {operationResult.Status}"); - break; - } + logger.Error(() => $"Unrecognized operation status: {operationResult.Status}"); + break; + } - switch (status) - { - case ZOperationStatus.Success: - var txId = operationResult.Result?.Value("txid") ?? string.Empty; - logger.Info(() => $"[{LogCategory}] Transfer completed with transaction id: {txId}"); + switch (status) + { + case ZOperationStatus.Success: + var txId = operationResult.Result?.Value("txid") ?? string.Empty; + logger.Info(() => $"[{LogCategory}] Transfer completed with transaction id: {txId}"); - continueWaiting = false; - continue; + continueWaiting = false; + continue; - case ZOperationStatus.Cancelled: - case ZOperationStatus.Failed: - logger.Error(() => $"{ZCashCommands.ZSendMany} failed: {operationResult.Error.Message} code {operationResult.Error.Code}"); + case ZOperationStatus.Cancelled: + case ZOperationStatus.Failed: + logger.Error(() => $"{ZCashCommands.ZSendMany} failed: {operationResult.Error.Message} code {operationResult.Error.Code}"); - continueWaiting = false; - continue; - } + continueWaiting = false; + continue; } - - logger.Info(() => $"[{LogCategory}] Waiting for transfer completion: {operationId}"); - await Task.Delay(TimeSpan.FromSeconds(10)); } + + logger.Info(() => $"[{LogCategory}] Waiting for transfer completion: {operationId}"); + await Task.Delay(TimeSpan.FromSeconds(10)); } } } From 80f6fe0aea798edf83bd3b92ea2b53c08c65a5ca Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 26 Feb 2018 16:22:04 +0100 Subject: [PATCH 112/348] Support multiple external stratums --- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 3 +- .../Blockchain/Bitcoin/BitcoinPoolBase.cs | 2 +- .../Blockchain/Ethereum/EthereumJobManager.cs | 2 +- .../Blockchain/Ethereum/EthereumPool.cs | 2 +- .../Blockchain/Monero/MoneroJobManager.cs | 2 +- .../Blockchain/Monero/MoneroPool.cs | 2 +- src/MiningCore/Configuration/ClusterConfig.cs | 24 +- .../Configuration/ClusterConfigValidation.cs | 6 + src/MiningCore/Mining/PoolBase.cs | 206 +++++++++--------- 9 files changed, 134 insertions(+), 115 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index b2f470980..ec66151ab 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -98,10 +98,9 @@ public BitcoinJobManager( } }; - protected virtual void SetupJobUpdates() { - if (poolConfig.ExternalStratum) + if (poolConfig.ExternalStratumsOnly) return; jobRebroadcastTimeout = TimeSpan.FromSeconds(Math.Max(1, poolConfig.JobRebroadcastTimeout)); diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs index 8e372ac26..14a2b9cc8 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs @@ -287,7 +287,7 @@ protected override async Task SetupJobManager() await manager.StartAsync(); - if (!poolConfig.ExternalStratum) + if (!poolConfig.ExternalStratumsOnly) { disposables.Add(manager.Jobs.Subscribe(OnNewJob)); diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs index 9a9e8ef95..dc365b7ee 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs @@ -599,7 +599,7 @@ private void ConfigureRewards() protected virtual void SetupJobUpdates() { - if (poolConfig.ExternalStratum) + if (poolConfig.ExternalStratumsOnly) return; var enableStreaming = extraPoolConfig?.EnableDaemonWebsocketStreaming == true; diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs index f1a3b8f5a..35189e5d8 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs @@ -261,7 +261,7 @@ protected override async Task SetupJobManager() await manager.StartAsync(); - if (!poolConfig.ExternalStratum) + if (!poolConfig.ExternalStratumsOnly) { disposables.Add(manager.Jobs.Subscribe(OnNewJob)); diff --git a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs index 9ae7e71dd..2767c9a7d 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs @@ -468,7 +468,7 @@ private void ConfigureRewards() protected virtual void SetupJobUpdates() { - if (poolConfig.ExternalStratum) + if (poolConfig.ExternalStratumsOnly) return; // periodically update block-template from daemon diff --git a/src/MiningCore/Blockchain/Monero/MoneroPool.cs b/src/MiningCore/Blockchain/Monero/MoneroPool.cs index ca23a8503..99e212887 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPool.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPool.cs @@ -302,7 +302,7 @@ protected override async Task SetupJobManager() await manager.StartAsync(); - if (!poolConfig.ExternalStratum) + if (!poolConfig.ExternalStratumsOnly) { disposables.Add(manager.Blocks.Subscribe(_ => OnNewJob())); diff --git a/src/MiningCore/Configuration/ClusterConfig.cs b/src/MiningCore/Configuration/ClusterConfig.cs index 428d357d1..eb07e53bb 100644 --- a/src/MiningCore/Configuration/ClusterConfig.cs +++ b/src/MiningCore/Configuration/ClusterConfig.cs @@ -273,6 +273,12 @@ public partial class ApiConfig public int Port { get; set; } } + public partial class ZmqPubSubEndpointConfig + { + public string Url { get; set; } + public string Topic { get; set; } + } + public partial class PoolConfig { public string Id { get; set; } @@ -290,9 +296,15 @@ public partial class PoolConfig public int JobRebroadcastTimeout { get; set; } public int BlockRefreshInterval { get; set; } - public bool ExternalStratum { get; set; } - public string ExternalStratumZmqSocket { get; set; } - public string ExternalStratumZmqTopic { get; set; } + /// + /// If true, internal stratum ports are not initialized + /// + public bool ExternalStratumsOnly { get; set; } + + /// + /// External stratums (ZMQ based share publishers) + /// + public ZmqPubSubEndpointConfig[] ExternalStratums { get; set; } [JsonExtensionData] public IDictionary Extra { get; set; } @@ -309,6 +321,12 @@ public partial class ClusterConfig public ApiConfig Api { get; set; } public decimal? DevDonation { get; set; } + /// + /// If this is enabled, shares are not written to the database + /// but published on the specified ZeroMQ Url and Topic + /// + public ZmqPubSubEndpointConfig ShareRelayEndpoint { get; set; } + /// /// Maximum parallelism of Equihash solver /// Increasing this value by one, increases pool peak memory consumption by 1 GB diff --git a/src/MiningCore/Configuration/ClusterConfigValidation.cs b/src/MiningCore/Configuration/ClusterConfigValidation.cs index aefc7c572..6cec757a0 100644 --- a/src/MiningCore/Configuration/ClusterConfigValidation.cs +++ b/src/MiningCore/Configuration/ClusterConfigValidation.cs @@ -189,6 +189,12 @@ public PoolConfigValidator() .NotEmpty() .WithMessage("Pool: Daemons missing or empty"); + RuleFor(j => j.ExternalStratums) + .NotNull() + .NotEmpty() + .When(j=> j.ExternalStratumsOnly) + .WithMessage("Pool: You must configure external stratum endpoints when enabling externalStratumsOnly"); + RuleFor(j => j.Daemons) .SetCollectionValidator(new AuthenticatedNetworkEndpointConfigValidator()); } diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index 637ad9b22..541c785a3 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -108,7 +108,7 @@ protected override void OnConnect(StratumClient client) var context = CreateClientContext(); var poolEndpoint = poolConfig.Ports[client.PoolEndpoint.Port]; - context.Init(poolConfig, poolEndpoint.Difficulty, !poolConfig.ExternalStratum ? poolEndpoint.VarDiff : null, clock); + context.Init(poolConfig, poolEndpoint.Difficulty, !poolConfig.ExternalStratumsOnly ? poolEndpoint.VarDiff : null, clock); client.SetContext(context); // varDiff setup @@ -139,101 +139,104 @@ private void EnsureNoZombieClient(StratumClient client) }); } - private void StartExternalStratumPublisherListener() + private void StartExternalStratumPublisherListeners() { - var thread = new Thread(() => - { - var serializer = new JsonSerializer - { - ContractResolver = new CamelCasePropertyNamesContractResolver() - }; - - var currentHeight = 0L; - var lastBlockTime = clock.Now; - - while(true) - { - try - { - using (var subSocket = new SubscriberSocket()) - { - //subSocket.Options.ReceiveHighWatermark = 1000; - subSocket.Connect(poolConfig.ExternalStratumZmqSocket); - subSocket.Subscribe(poolConfig.ExternalStratumZmqTopic); - - logger.Info($"Monitoring external stratum {poolConfig.ExternalStratumZmqSocket}/{poolConfig.ExternalStratumZmqTopic}"); - - while (true) - { - var msg = subSocket.ReceiveMultipartMessage(2); - var topic = msg.First().ConvertToString(Encoding.UTF8); - var data = msg.Last().ConvertToString(Encoding.UTF8); - - // validate - if (topic != poolConfig.ExternalStratumZmqTopic) - { - logger.Warn(()=> $"Received non-matching topic {topic} on ZeroMQ subscriber socket"); - continue; - } - - if(string.IsNullOrEmpty(data)) - { - logger.Warn(() => $"Received empty data on ZeroMQ subscriber socket"); - continue; - } - - // deserialize - TShare share; - - using (var reader = new StringReader(data)) - { - using (var jreader = new JsonTextReader(reader)) - { - share = serializer.Deserialize(jreader); - } - } - - if (share == null) - { - logger.Error(() => "Unable to deserialize share received from ZeroMQ subscriber socket"); - continue; - } - - // update network stats - blockchainStats.BlockHeight = share.BlockHeight; - blockchainStats.NetworkDifficulty = share.NetworkDifficulty; - - if (currentHeight != share.BlockHeight) - { - blockchainStats.LastNetworkBlockTime = clock.Now; - currentHeight = share.BlockHeight; - lastBlockTime = clock.Now; - } - - else - blockchainStats.LastNetworkBlockTime = lastBlockTime; - - // fill in the blacks - share.PoolId = poolConfig.Id; - share.Created = clock.Now; - - // re-publish - shareSubject.OnNext(new ClientShare(null, share)); - - logger.Info(() => $"[{LogCat}] External share accepted: D={Math.Round(share.Difficulty, 3)}"); - } - } - } - - catch (Exception ex) - { - logger.Error(ex); - } - } - }); - - thread.Name = $"{poolConfig.Id} external stratum listener"; - thread.Start(); + foreach (var externalStratum in poolConfig.ExternalStratums) + { + var thread = new Thread(arg => + { + var serializer = new JsonSerializer + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + + var currentHeight = 0L; + var lastBlockTime = clock.Now; + var config = (ZmqPubSubEndpointConfig) arg; + + while (true) + { + try + { + using (var subSocket = new SubscriberSocket()) + { + //subSocket.Options.ReceiveHighWatermark = 1000; + subSocket.Connect(config.Url); + subSocket.Subscribe(config.Topic); + + logger.Info($"{LogCat}] Monitoring external stratum {config.Url}/{config.Topic}"); + + while (true) + { + var msg = subSocket.ReceiveMultipartMessage(2); + var topic = msg.First().ConvertToString(Encoding.UTF8); + var data = msg.Last().ConvertToString(Encoding.UTF8); + + // validate + if (topic != config.Topic) + { + logger.Warn(() => $"{LogCat}] Received non-matching topic {topic} on ZeroMQ subscriber socket"); + continue; + } + + if (string.IsNullOrEmpty(data)) + { + logger.Warn(() => $"{LogCat}] Received empty data on ZeroMQ subscriber socket"); + continue; + } + + // deserialize + TShare share; + + using (var reader = new StringReader(data)) + { + using (var jreader = new JsonTextReader(reader)) + { + share = serializer.Deserialize(jreader); + } + } + + if (share == null) + { + logger.Error(() => $"{LogCat}] Unable to deserialize share received from ZeroMQ subscriber socket"); + continue; + } + + // update network stats + blockchainStats.BlockHeight = share.BlockHeight; + blockchainStats.NetworkDifficulty = share.NetworkDifficulty; + + if (currentHeight != share.BlockHeight) + { + blockchainStats.LastNetworkBlockTime = clock.Now; + currentHeight = share.BlockHeight; + lastBlockTime = clock.Now; + } + + else + blockchainStats.LastNetworkBlockTime = lastBlockTime; + + // fill in the blacks + share.PoolId = poolConfig.Id; + share.Created = clock.Now; + + // re-publish + shareSubject.OnNext(new ClientShare(null, share)); + + logger.Info(() => $"[{LogCat}] External share accepted: D={Math.Round(share.Difficulty, 3)}"); + } + } + } + + catch (Exception ex) + { + logger.Error(ex); + } + } + }) {Name = $"{poolConfig.Id} external stratum listener"}; + + thread.Start(externalStratum); + } } #region VarDiff @@ -428,7 +431,7 @@ public virtual async Task StartAsync() await SetupJobManager(); InitStats(); - if (!poolConfig.ExternalStratum) + if (!poolConfig.ExternalStratumsOnly) { var ipEndpoints = poolConfig.Ports.Keys .Select(port => PoolEndpoint2IPEndpoint(port, poolConfig.Ports[port])) @@ -437,15 +440,8 @@ public virtual async Task StartAsync() StartListeners(poolConfig.Id, ipEndpoints); } - else - { - if (string.IsNullOrEmpty(poolConfig.ExternalStratumZmqSocket)) - logger.ThrowLogPoolStartupException($"[{LogCat}] Requested external stratum but no publisher socket specified", LogCat); - else if (string.IsNullOrEmpty(poolConfig.ExternalStratumZmqTopic)) - logger.ThrowLogPoolStartupException($"[{LogCat}] Requested external stratum but no publisher topic specified", LogCat); - - StartExternalStratumPublisherListener(); - } + if(poolConfig.ExternalStratums?.Length > 0) + StartExternalStratumPublisherListeners(); logger.Info(() => $"[{LogCat}] Online"); OutputPoolInfo(); From 6e8ec8ada5c078bb1dd4056926d690c2206ce5a0 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 26 Feb 2018 17:26:56 +0100 Subject: [PATCH 113/348] Share relay --- src/MiningCore/AutofacModule.cs | 3 + src/MiningCore/Configuration/ClusterConfig.cs | 5 +- src/MiningCore/Mining/PoolBase.cs | 7 +- src/MiningCore/Payments/ShareRecorder.cs | 4 +- src/MiningCore/Payments/ShareRelay.cs | 109 ++++++++++++++++++ src/MiningCore/Program.cs | 33 ++++-- 6 files changed, 145 insertions(+), 16 deletions(-) create mode 100644 src/MiningCore/Payments/ShareRelay.cs diff --git a/src/MiningCore/AutofacModule.cs b/src/MiningCore/AutofacModule.cs index 4172aecd7..a628d9dee 100644 --- a/src/MiningCore/AutofacModule.cs +++ b/src/MiningCore/AutofacModule.cs @@ -79,6 +79,9 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType() .SingleInstance(); + builder.RegisterType() + .SingleInstance(); + builder.RegisterType() .SingleInstance(); diff --git a/src/MiningCore/Configuration/ClusterConfig.cs b/src/MiningCore/Configuration/ClusterConfig.cs index eb07e53bb..a5b5de47f 100644 --- a/src/MiningCore/Configuration/ClusterConfig.cs +++ b/src/MiningCore/Configuration/ClusterConfig.cs @@ -323,9 +323,10 @@ public partial class ClusterConfig /// /// If this is enabled, shares are not written to the database - /// but published on the specified ZeroMQ Url and Topic + /// but published on the specified ZeroMQ Url and using the + /// poolid as topic /// - public ZmqPubSubEndpointConfig ShareRelayEndpoint { get; set; } + public string ShareRelayPublisherUrl { get; set; } /// /// Maximum parallelism of Equihash solver diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index 541c785a3..93e11e774 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -169,8 +169,8 @@ private void StartExternalStratumPublisherListeners() while (true) { var msg = subSocket.ReceiveMultipartMessage(2); - var topic = msg.First().ConvertToString(Encoding.UTF8); - var data = msg.Last().ConvertToString(Encoding.UTF8); + var topic = msg.Pop().ConvertToString(Encoding.UTF8); + var data = msg.Pop().ConvertToString(Encoding.UTF8); // validate if (topic != config.Topic) @@ -317,7 +317,8 @@ protected void SetupBanning(ClusterConfig clusterConfig) protected virtual void InitStats() { - LoadStats(); + if(string.IsNullOrEmpty(clusterConfig.ShareRelayPublisherUrl)) + LoadStats(); } private void LoadStats() diff --git a/src/MiningCore/Payments/ShareRecorder.cs b/src/MiningCore/Payments/ShareRecorder.cs index c7806abbd..56c5c5cbf 100644 --- a/src/MiningCore/Payments/ShareRecorder.cs +++ b/src/MiningCore/Payments/ShareRecorder.cs @@ -316,6 +316,8 @@ public void Stop() logger.Info(() => "Stopped"); } + #endregion // API-Surface + private void InitializeQueue() { queueSub = queue.GetConsumingEnumerable() @@ -393,7 +395,5 @@ private void BuildFaultHandlingPolicy() fallbackOnBrokenCircuit, Policy.Wrap(fallback, breaker, retry)); } - - #endregion // API-Surface } } diff --git a/src/MiningCore/Payments/ShareRelay.cs b/src/MiningCore/Payments/ShareRelay.cs new file mode 100644 index 000000000..6c106d1ee --- /dev/null +++ b/src/MiningCore/Payments/ShareRelay.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Text; +using MiningCore.Blockchain; +using MiningCore.Configuration; +using MiningCore.Mining; +using NetMQ; +using NetMQ.Sockets; +using Newtonsoft.Json; +using NLog; + +namespace MiningCore.Payments +{ + public class ShareRelay + { + public ShareRelay(JsonSerializerSettings serializerSettings) + { + this.serializerSettings = serializerSettings; + } + + private ClusterConfig clusterConfig; + private readonly BlockingCollection queue = new BlockingCollection(); + private IDisposable queueSub; + private readonly int QueueSizeWarningThreshold = 1024; + private bool hasWarnedAboutBacklogSize; + private PublisherSocket pubSocket; + private readonly JsonSerializerSettings serializerSettings; + + private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); + + #region API-Surface + + public void AttachPool(IMiningPool pool) + { + pool.Shares.Subscribe(x => { queue.Add(x.Share); }); + } + + public void Start(ClusterConfig clusterConfig) + { + this.clusterConfig = clusterConfig; + + pubSocket = new PublisherSocket(); + pubSocket.Bind(clusterConfig.ShareRelayPublisherUrl); + + InitializeQueue(); + + logger.Info(() => "Online"); + } + + public void Stop() + { + logger.Info(() => "Stopping .."); + + pubSocket.Dispose(); + + queueSub?.Dispose(); + queueSub = null; + + logger.Info(() => "Stopped"); + } + + #endregion // API-Surface + + private void InitializeQueue() + { + queueSub = queue.GetConsumingEnumerable() + .ToObservable(TaskPoolScheduler.Default) + .Do(_ => CheckQueueBacklog()) + .Subscribe(share => + { + try + { + var json = JsonConvert.SerializeObject(share, serializerSettings); + + var msg = new NetMQMessage(2); + msg.Push(json); + msg.Push(share.PoolId); + pubSocket.SendMultipartMessage(msg); + } + + catch (Exception ex) + { + logger.Error(ex); + } + }); + } + + private void CheckQueueBacklog() + { + if (queue.Count > QueueSizeWarningThreshold) + { + if (!hasWarnedAboutBacklogSize) + { + logger.Warn(() => $"Share relay queue backlog has crossed {QueueSizeWarningThreshold}"); + hasWarnedAboutBacklogSize = true; + } + } + + else if (hasWarnedAboutBacklogSize && queue.Count <= QueueSizeWarningThreshold / 2) + { + hasWarnedAboutBacklogSize = false; + } + } + } +} diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index 4a40bba4e..a957d069c 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -70,6 +70,7 @@ public class Program private static CommandOption dumpConfigOption; private static CommandOption shareRecoveryOption; private static ShareRecorder shareRecorder; + private static ShareRelay shareRelay; private static PayoutManager payoutManager; private static StatsRecorder statsRecorder; private static ClusterConfig clusterConfig; @@ -533,9 +534,19 @@ private static void ConfigurePostgres(DatabaseConfig pgConfig, ContainerBuilder private static async Task Start() { - // start share recorder - shareRecorder = container.Resolve(); - shareRecorder.Start(clusterConfig); + if (string.IsNullOrEmpty(clusterConfig.ShareRelayPublisherUrl)) + { + // start share recorder + shareRecorder = container.Resolve(); + shareRecorder.Start(clusterConfig); + } + + else + { + // start share relay + shareRelay = container.Resolve(); + shareRelay.Start(clusterConfig); + } // start API if (clusterConfig.Api == null || clusterConfig.Api.Enabled) @@ -557,10 +568,13 @@ private static async Task Start() else logger.Info("Payment processing is not enabled"); - // start pool stats updater - statsRecorder = container.Resolve(); - statsRecorder.Configure(clusterConfig); - statsRecorder.Start(); + if (string.IsNullOrEmpty(clusterConfig.ShareRelayPublisherUrl)) + { + // start pool stats updater + statsRecorder = container.Resolve(); + statsRecorder.Configure(clusterConfig); + statsRecorder.Start(); + } // start pools await Task.WhenAll(clusterConfig.Pools.Where(x => x.Enabled).Select(async poolConfig => @@ -574,8 +588,9 @@ await Task.WhenAll(clusterConfig.Pools.Where(x => x.Enabled).Select(async poolCo pool.Configure(poolConfig, clusterConfig); // pre-start attachments - shareRecorder.AttachPool(pool); - statsRecorder.AttachPool(pool); + shareRecorder?.AttachPool(pool); + shareRelay?.AttachPool(pool); + statsRecorder?.AttachPool(pool); await pool.StartAsync(); })); From e326b910f042deaa2399d6ee9534cc1abfe08f98 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 26 Feb 2018 18:31:56 +0100 Subject: [PATCH 114/348] Logging --- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 2 +- .../Blockchain/Bitcoin/BitcoinPoolBase.cs | 2 +- .../Blockchain/Ethereum/EthereumJobManager.cs | 2 +- .../Blockchain/Ethereum/EthereumPool.cs | 2 +- .../Blockchain/Monero/MoneroJobManager.cs | 2 +- .../Blockchain/Monero/MoneroPool.cs | 2 +- src/MiningCore/Configuration/ClusterConfig.cs | 2 +- .../Configuration/ClusterConfigValidation.cs | 4 +-- src/MiningCore/Mining/PoolBase.cs | 4 +-- src/MiningCore/Program.cs | 7 ++++ src/MiningCore/Stratum/StratumServer.cs | 35 ++++++++++++------- 11 files changed, 40 insertions(+), 24 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index ec66151ab..484368be4 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -100,7 +100,7 @@ public BitcoinJobManager( protected virtual void SetupJobUpdates() { - if (poolConfig.ExternalStratumsOnly) + if (!poolConfig.EnableInternalStratum) return; jobRebroadcastTimeout = TimeSpan.FromSeconds(Math.Max(1, poolConfig.JobRebroadcastTimeout)); diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs index 14a2b9cc8..411dbc3aa 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs @@ -287,7 +287,7 @@ protected override async Task SetupJobManager() await manager.StartAsync(); - if (!poolConfig.ExternalStratumsOnly) + if (poolConfig.EnableInternalStratum) { disposables.Add(manager.Jobs.Subscribe(OnNewJob)); diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs index dc365b7ee..30bc5fce3 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs @@ -599,7 +599,7 @@ private void ConfigureRewards() protected virtual void SetupJobUpdates() { - if (poolConfig.ExternalStratumsOnly) + if (!poolConfig.EnableInternalStratum) return; var enableStreaming = extraPoolConfig?.EnableDaemonWebsocketStreaming == true; diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs index 35189e5d8..5773303e9 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs @@ -261,7 +261,7 @@ protected override async Task SetupJobManager() await manager.StartAsync(); - if (!poolConfig.ExternalStratumsOnly) + if (poolConfig.EnableInternalStratum) { disposables.Add(manager.Jobs.Subscribe(OnNewJob)); diff --git a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs index 2767c9a7d..dd9cd7700 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs @@ -468,7 +468,7 @@ private void ConfigureRewards() protected virtual void SetupJobUpdates() { - if (poolConfig.ExternalStratumsOnly) + if (!poolConfig.EnableInternalStratum) return; // periodically update block-template from daemon diff --git a/src/MiningCore/Blockchain/Monero/MoneroPool.cs b/src/MiningCore/Blockchain/Monero/MoneroPool.cs index 99e212887..319fcf865 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPool.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPool.cs @@ -302,7 +302,7 @@ protected override async Task SetupJobManager() await manager.StartAsync(); - if (!poolConfig.ExternalStratumsOnly) + if (poolConfig.EnableInternalStratum) { disposables.Add(manager.Blocks.Subscribe(_ => OnNewJob())); diff --git a/src/MiningCore/Configuration/ClusterConfig.cs b/src/MiningCore/Configuration/ClusterConfig.cs index a5b5de47f..62d36b1fe 100644 --- a/src/MiningCore/Configuration/ClusterConfig.cs +++ b/src/MiningCore/Configuration/ClusterConfig.cs @@ -299,7 +299,7 @@ public partial class PoolConfig /// /// If true, internal stratum ports are not initialized /// - public bool ExternalStratumsOnly { get; set; } + public bool EnableInternalStratum { get; set; } /// /// External stratums (ZMQ based share publishers) diff --git a/src/MiningCore/Configuration/ClusterConfigValidation.cs b/src/MiningCore/Configuration/ClusterConfigValidation.cs index 6cec757a0..2ddf1e1e8 100644 --- a/src/MiningCore/Configuration/ClusterConfigValidation.cs +++ b/src/MiningCore/Configuration/ClusterConfigValidation.cs @@ -192,8 +192,8 @@ public PoolConfigValidator() RuleFor(j => j.ExternalStratums) .NotNull() .NotEmpty() - .When(j=> j.ExternalStratumsOnly) - .WithMessage("Pool: You must configure external stratum endpoints when enabling externalStratumsOnly"); + .When(j=> !j.EnableInternalStratum) + .WithMessage("Pool: You must configure external stratum endpoints when enabling disabling internal stratum"); RuleFor(j => j.Daemons) .SetCollectionValidator(new AuthenticatedNetworkEndpointConfigValidator()); diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index 93e11e774..6f40b13b8 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -108,7 +108,7 @@ protected override void OnConnect(StratumClient client) var context = CreateClientContext(); var poolEndpoint = poolConfig.Ports[client.PoolEndpoint.Port]; - context.Init(poolConfig, poolEndpoint.Difficulty, !poolConfig.ExternalStratumsOnly ? poolEndpoint.VarDiff : null, clock); + context.Init(poolConfig, poolEndpoint.Difficulty, poolConfig.EnableInternalStratum ? poolEndpoint.VarDiff : null, clock); client.SetContext(context); // varDiff setup @@ -432,7 +432,7 @@ public virtual async Task StartAsync() await SetupJobManager(); InitStats(); - if (!poolConfig.ExternalStratumsOnly) + if (poolConfig.EnableInternalStratum) { var ipEndpoints = poolConfig.Ports.Keys .Select(port => PoolEndpoint2IPEndpoint(port, poolConfig.Ports[port])) diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index a957d069c..3f353d9f1 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -162,6 +162,13 @@ private static void LogRuntimeInfo() private static void ValidateConfig() { + // set some defaults + foreach (var config in clusterConfig.Pools) + { + config.EnableInternalStratum = config.EnableInternalStratum || + config.ExternalStratums == null || config.ExternalStratums.Length == 0; + } + try { clusterConfig.Validate(); diff --git a/src/MiningCore/Stratum/StratumServer.cs b/src/MiningCore/Stratum/StratumServer.cs index 29469114c..ab0dde9e6 100644 --- a/src/MiningCore/Stratum/StratumServer.cs +++ b/src/MiningCore/Stratum/StratumServer.cs @@ -75,21 +75,30 @@ public void StartListeners(string id, params IPEndPoint[] stratumPorts) { var loop = new Loop(); - var listener = loop - .CreateTcp() - .NoDelay(true) - .SimultaneousAccepts(false) - .Listen(endpoint, (con, ex) => + try + { + var listener = loop + .CreateTcp() + .NoDelay(true) + .SimultaneousAccepts(false) + .Listen(endpoint, (con, ex) => + { + if (ex == null) + OnClientConnected(con, endpoint, loop); + else + logger.Error(() => $"[{LogCat}] Connection error state: {ex.Message}"); + }); + + lock (ports) { - if (ex == null) - OnClientConnected(con, endpoint, loop); - else - logger.Error(() => $"[{LogCat}] Connection error state: {ex.Message}"); - }); + ports[endpoint.Port] = listener; + } + } - lock(ports) + catch (Exception ex) { - ports[endpoint.Port] = listener; + logger.Error(ex, $"[{LogCat}] {ex}"); + throw; } logger.Info(() => $"[{LogCat}] Stratum port {endpoint.Address}:{endpoint.Port} online"); @@ -101,7 +110,7 @@ public void StartListeners(string id, params IPEndPoint[] stratumPorts) catch(Exception ex) { - logger.Error(ex, () => Thread.CurrentThread.Name); + logger.Error(ex, $"[{LogCat}] {ex}"); } }) { Name = $"UvLoopThread {id}:{endpoint.Port}" }; From 5b4ac91f67e6191f38010b73dafd6a01fbd3f70f Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 28 Feb 2018 17:32:22 +0100 Subject: [PATCH 115/348] Don't enforce daemon owning pool wallet if payment-processing is disabled --- src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs | 2 +- src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs | 2 +- src/MiningCore/Blockchain/Monero/MoneroJobManager.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 484368be4..ce61ff448 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -631,7 +631,7 @@ protected override async Task PostStartInitAsync() if (!validateAddressResponse.IsValid) logger.ThrowLogPoolStartupException($"Daemon reports pool-address '{poolConfig.Address}' as invalid", LogCat); - if (!validateAddressResponse.IsMine) + 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"); diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs index 30bc5fce3..a578d6b99 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs @@ -536,7 +536,7 @@ protected override async Task PostStartInitAsync() var parityChain = results[4].Response.ToObject(); // ensure pool owns wallet - if (!accounts.Contains(poolConfig.Address) || coinbase != poolConfig.Address) + if (clusterConfig.PaymentProcessing?.Enabled == true && !accounts.Contains(poolConfig.Address) || coinbase != poolConfig.Address) logger.ThrowLogPoolStartupException($"Daemon does not own pool-address '{poolConfig.Address}'", LogCat); EthereumUtils.DetectNetworkAndChain(netVersion, parityChain, out networkType, out chainType); diff --git a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs index dd9cd7700..88c2ff6bc 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs @@ -403,7 +403,7 @@ protected override async Task PostStartInitAsync() logger.ThrowLogPoolStartupException($"Init RPC failed: {infoResponse.Error.Message} (Code {infoResponse.Error.Code})", LogCat); // ensure pool owns wallet - if (addressResponse.Response?.Address != poolConfig.Address) + 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(); From e011b0eed5cec469fd63cd7e78dcdaa9d34db111 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 28 Feb 2018 20:46:29 +0100 Subject: [PATCH 116/348] Persistence configuration is optional if payment-processing is disabled and the pool is in relay-mode --- src/MiningCore/Program.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index 3f353d9f1..c7686213e 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -165,7 +165,7 @@ private static void ValidateConfig() // set some defaults foreach (var config in clusterConfig.Pools) { - config.EnableInternalStratum = config.EnableInternalStratum || + config.EnableInternalStratum = config.EnableInternalStratum || config.ExternalStratums == null || config.ExternalStratums.Length == 0; } @@ -502,10 +502,12 @@ private static void ConfigureMisc() private static void ConfigurePersistence(ContainerBuilder builder) { - if (clusterConfig.Persistence == null) + if (clusterConfig.Persistence == null && + clusterConfig.PaymentProcessing?.Enabled == true && + string.IsNullOrEmpty(clusterConfig.ShareRelayPublisherUrl)) logger.ThrowLogPoolStartupException("Persistence is not configured!"); - if (clusterConfig.Persistence.Postgres != null) + if (clusterConfig.Persistence?.Postgres != null) ConfigurePostgres(clusterConfig.Persistence.Postgres, builder); } From 6ee9e2bfb5ba8e3c0835bbd7a32195aedd88d832 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 28 Feb 2018 21:28:21 +0100 Subject: [PATCH 117/348] Share relay config --- src/MiningCore/Configuration/ClusterConfig.cs | 12 +++++++++--- src/MiningCore/Payments/ShareRelay.cs | 2 +- src/MiningCore/Program.cs | 6 +++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/MiningCore/Configuration/ClusterConfig.cs b/src/MiningCore/Configuration/ClusterConfig.cs index 62d36b1fe..0389efc42 100644 --- a/src/MiningCore/Configuration/ClusterConfig.cs +++ b/src/MiningCore/Configuration/ClusterConfig.cs @@ -279,6 +279,12 @@ public partial class ZmqPubSubEndpointConfig public string Topic { get; set; } } + public partial class ShareRelayConfig + { + public string PublishUrl { get; set; } + //public bool Compress { get; set; } + } + public partial class PoolConfig { public string Id { get; set; } @@ -322,11 +328,11 @@ public partial class ClusterConfig public decimal? DevDonation { get; set; } /// - /// If this is enabled, shares are not written to the database - /// but published on the specified ZeroMQ Url and using the + /// If this is enabled, shares are not written to the database + /// but published on the specified ZeroMQ Url and using the /// poolid as topic /// - public string ShareRelayPublisherUrl { get; set; } + public ShareRelayConfig ShareRelay { get; set; } /// /// Maximum parallelism of Equihash solver diff --git a/src/MiningCore/Payments/ShareRelay.cs b/src/MiningCore/Payments/ShareRelay.cs index 6c106d1ee..571b8c030 100644 --- a/src/MiningCore/Payments/ShareRelay.cs +++ b/src/MiningCore/Payments/ShareRelay.cs @@ -44,7 +44,7 @@ public void Start(ClusterConfig clusterConfig) this.clusterConfig = clusterConfig; pubSocket = new PublisherSocket(); - pubSocket.Bind(clusterConfig.ShareRelayPublisherUrl); + pubSocket.Bind(clusterConfig.ShareRelay.PublishUrl); InitializeQueue(); diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index c7686213e..3f0b0d883 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -504,7 +504,7 @@ private static void ConfigurePersistence(ContainerBuilder builder) { if (clusterConfig.Persistence == null && clusterConfig.PaymentProcessing?.Enabled == true && - string.IsNullOrEmpty(clusterConfig.ShareRelayPublisherUrl)) + clusterConfig.ShareRelay == null) logger.ThrowLogPoolStartupException("Persistence is not configured!"); if (clusterConfig.Persistence?.Postgres != null) @@ -543,7 +543,7 @@ private static void ConfigurePostgres(DatabaseConfig pgConfig, ContainerBuilder private static async Task Start() { - if (string.IsNullOrEmpty(clusterConfig.ShareRelayPublisherUrl)) + if (clusterConfig.ShareRelay != null) { // start share recorder shareRecorder = container.Resolve(); @@ -577,7 +577,7 @@ private static async Task Start() else logger.Info("Payment processing is not enabled"); - if (string.IsNullOrEmpty(clusterConfig.ShareRelayPublisherUrl)) + if (clusterConfig.ShareRelay != null) { // start pool stats updater statsRecorder = container.Resolve(); From 44d10f4fde5ad5705f1f8cc60138bb0ddeaf537e Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 28 Feb 2018 23:31:07 +0100 Subject: [PATCH 118/348] Dummy persistence --- src/MiningCore/Mining/PoolBase.cs | 2 +- .../Dummy/DummyConnectionFactory.cs | 37 +++++++++++++++++++ ...ctionFactory.cs => PgConnectionFactory.cs} | 4 +- src/MiningCore/Program.cs | 12 +++++- 4 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 src/MiningCore/Persistence/Dummy/DummyConnectionFactory.cs rename src/MiningCore/Persistence/Postgres/{ConnectionFactory.cs => PgConnectionFactory.cs} (91%) diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index 6f40b13b8..f7c2a6718 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -317,7 +317,7 @@ protected void SetupBanning(ClusterConfig clusterConfig) protected virtual void InitStats() { - if(string.IsNullOrEmpty(clusterConfig.ShareRelayPublisherUrl)) + if(clusterConfig.ShareRelay != null) LoadStats(); } diff --git a/src/MiningCore/Persistence/Dummy/DummyConnectionFactory.cs b/src/MiningCore/Persistence/Dummy/DummyConnectionFactory.cs new file mode 100644 index 000000000..d5098d987 --- /dev/null +++ b/src/MiningCore/Persistence/Dummy/DummyConnectionFactory.cs @@ -0,0 +1,37 @@ +/* +Copyright 2017 Coin Foundry (coinfoundry.org) +Authors: Oliver Weichhold (oliver@weichhold.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +using System; +using System.Data; + +namespace MiningCore.Persistence.Dummy +{ + public class DummyConnectionFactory : IConnectionFactory + { + /// + /// This implementation ensures that Glimpse.ADO is able to collect data + /// + /// + public IDbConnection OpenConnection() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/MiningCore/Persistence/Postgres/ConnectionFactory.cs b/src/MiningCore/Persistence/Postgres/PgConnectionFactory.cs similarity index 91% rename from src/MiningCore/Persistence/Postgres/ConnectionFactory.cs rename to src/MiningCore/Persistence/Postgres/PgConnectionFactory.cs index d17c24286..6c07edf68 100644 --- a/src/MiningCore/Persistence/Postgres/ConnectionFactory.cs +++ b/src/MiningCore/Persistence/Postgres/PgConnectionFactory.cs @@ -23,9 +23,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace MiningCore.Persistence.Postgres { - public class ConnectionFactory : IConnectionFactory + public class PgConnectionFactory : IConnectionFactory { - public ConnectionFactory(string connectionString) + public PgConnectionFactory(string connectionString) { this.connectionString = connectionString; } diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index 3f0b0d883..53e028d6c 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -47,6 +47,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Mining; using MiningCore.Native; using MiningCore.Payments; +using MiningCore.Persistence.Dummy; using MiningCore.Persistence.Postgres; using MiningCore.Persistence.Postgres.Repositories; using MiningCore.Util; @@ -509,6 +510,8 @@ private static void ConfigurePersistence(ContainerBuilder builder) if (clusterConfig.Persistence?.Postgres != null) ConfigurePostgres(clusterConfig.Persistence.Postgres, builder); + else + ConfigureDummyPersistence(builder); } private static void ConfigurePostgres(DatabaseConfig pgConfig, ContainerBuilder builder) @@ -530,7 +533,7 @@ private static void ConfigurePostgres(DatabaseConfig pgConfig, ContainerBuilder var connectionString = $"Server={pgConfig.Host};Port={pgConfig.Port};Database={pgConfig.Database};User Id={pgConfig.User};Password={pgConfig.Password};CommandTimeout=900;"; // register connection factory - builder.RegisterInstance(new ConnectionFactory(connectionString)) + builder.RegisterInstance(new PgConnectionFactory(connectionString)) .AsImplementedInterfaces(); // register repositories @@ -541,6 +544,13 @@ private static void ConfigurePostgres(DatabaseConfig pgConfig, ContainerBuilder .SingleInstance(); } + private static void ConfigureDummyPersistence(ContainerBuilder builder) + { + // register connection factory + builder.RegisterInstance(new DummyConnectionFactory()) + .AsImplementedInterfaces(); + } + private static async Task Start() { if (clusterConfig.ShareRelay != null) From f5e5b60b744bdc16e2cf1a478c72b4c42bcf8914 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 28 Feb 2018 23:35:57 +0100 Subject: [PATCH 119/348] WIP --- src/MiningCore/Program.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index 53e028d6c..00c53f1c2 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -549,6 +549,13 @@ private static void ConfigureDummyPersistence(ContainerBuilder builder) // register connection factory builder.RegisterInstance(new DummyConnectionFactory()) .AsImplementedInterfaces(); + + // register repositories + builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) + .Where(t => + t.Namespace.StartsWith(typeof(ShareRepository).Namespace)) + .AsImplementedInterfaces() + .SingleInstance(); } private static async Task Start() From b49c055bc8f8dbc573d78adce01bcd87fcee4d00 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 28 Feb 2018 23:43:14 +0100 Subject: [PATCH 120/348] WIP --- src/MiningCore/Persistence/Dummy/DummyConnectionFactory.cs | 4 ++++ src/MiningCore/Program.cs | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Persistence/Dummy/DummyConnectionFactory.cs b/src/MiningCore/Persistence/Dummy/DummyConnectionFactory.cs index d5098d987..ea892611f 100644 --- a/src/MiningCore/Persistence/Dummy/DummyConnectionFactory.cs +++ b/src/MiningCore/Persistence/Dummy/DummyConnectionFactory.cs @@ -25,6 +25,10 @@ namespace MiningCore.Persistence.Dummy { public class DummyConnectionFactory : IConnectionFactory { + public DummyConnectionFactory(string connectionString) + { + } + /// /// This implementation ensures that Glimpse.ADO is able to collect data /// diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index 00c53f1c2..359b6fa8e 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -547,7 +547,7 @@ private static void ConfigurePostgres(DatabaseConfig pgConfig, ContainerBuilder private static void ConfigureDummyPersistence(ContainerBuilder builder) { // register connection factory - builder.RegisterInstance(new DummyConnectionFactory()) + builder.RegisterInstance(new DummyConnectionFactory(string.Empty)) .AsImplementedInterfaces(); // register repositories @@ -560,7 +560,7 @@ private static void ConfigureDummyPersistence(ContainerBuilder builder) private static async Task Start() { - if (clusterConfig.ShareRelay != null) + if (clusterConfig.ShareRelay == null) { // start share recorder shareRecorder = container.Resolve(); @@ -594,7 +594,7 @@ private static async Task Start() else logger.Info("Payment processing is not enabled"); - if (clusterConfig.ShareRelay != null) + if (clusterConfig.ShareRelay == null) { // start pool stats updater statsRecorder = container.Resolve(); From e53bf0b6baa3dd8e98e9d6f62a423e33d01ac141 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 28 Feb 2018 23:49:30 +0100 Subject: [PATCH 121/348] WIP --- src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs | 2 +- src/MiningCore/Blockchain/Ethereum/EthereumPool.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index ce61ff448..67cfbcf48 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -525,7 +525,7 @@ public virtual async Task SubmitShareAsync(StratumClient worker, o public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig) { extraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); - extraPoolPaymentProcessingConfig = poolConfig.PaymentProcessing.Extra.SafeExtensionDataAs(); + extraPoolPaymentProcessingConfig = poolConfig.PaymentProcessing?.Extra?.SafeExtensionDataAs(); if (extraPoolConfig?.MaxActiveJobs.HasValue == true) maxActiveJobs = extraPoolConfig.MaxActiveJobs.Value; diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs index 5773303e9..2486b72f4 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs @@ -341,7 +341,7 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi base.Configure(poolConfig, clusterConfig); // validate mandatory extra config - var extraConfig = poolConfig.PaymentProcessing.Extra.SafeExtensionDataAs(); + var extraConfig = poolConfig.PaymentProcessing?.Extra?.SafeExtensionDataAs(); if (extraConfig?.CoinbasePassword == null) logger.ThrowLogPoolStartupException("\"paymentProcessing.coinbasePassword\" pool-configuration property missing or empty (required for unlocking wallet during payment processing)"); } From ee3eb28ab09f52db299ace73764cb488aec8ca66 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 28 Feb 2018 23:54:18 +0100 Subject: [PATCH 122/348] WIP --- src/MiningCore/Blockchain/Ethereum/EthereumPool.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs index 2486b72f4..3cf5b5670 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs @@ -342,7 +342,7 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi // validate mandatory extra config var extraConfig = poolConfig.PaymentProcessing?.Extra?.SafeExtensionDataAs(); - if (extraConfig?.CoinbasePassword == null) + if (clusterConfig.PaymentProcessing?.Enabled == true && extraConfig?.CoinbasePassword == null) logger.ThrowLogPoolStartupException("\"paymentProcessing.coinbasePassword\" pool-configuration property missing or empty (required for unlocking wallet during payment processing)"); } From c79d41d0e8c29b4f5ea230b8ee434d04deca8dfb Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 2 Mar 2018 11:50:02 +0100 Subject: [PATCH 123/348] Share source --- src/MiningCore/Blockchain/Abstractions.cs | 5 +++++ src/MiningCore/Blockchain/ShareBase.cs | 1 + src/MiningCore/Payments/ShareRecorder.cs | 2 ++ src/MiningCore/Payments/ShareRelay.cs | 2 ++ src/MiningCore/Persistence/Model/Share.cs | 1 + src/MiningCore/Persistence/Postgres/Entities/Share.cs | 1 + src/MiningCore/Persistence/Postgres/Scripts/createdb.sql | 2 ++ 7 files changed, 14 insertions(+) diff --git a/src/MiningCore/Blockchain/Abstractions.cs b/src/MiningCore/Blockchain/Abstractions.cs index 38e70ccc0..c750ed6db 100644 --- a/src/MiningCore/Blockchain/Abstractions.cs +++ b/src/MiningCore/Blockchain/Abstractions.cs @@ -70,6 +70,11 @@ public interface IShare /// string IpAddress { get; } + /// + /// Submission source (pool, external stratum etc) + /// + string Source { get; set; } + /// /// Stratum difficulty assigned to the miner at the time the share was submitted/accepted (used for payout /// calculations) diff --git a/src/MiningCore/Blockchain/ShareBase.cs b/src/MiningCore/Blockchain/ShareBase.cs index 209aa942f..0b2fb69c0 100644 --- a/src/MiningCore/Blockchain/ShareBase.cs +++ b/src/MiningCore/Blockchain/ShareBase.cs @@ -30,6 +30,7 @@ public class ShareBase : IShare public string PayoutInfo { get; set; } public string UserAgent { get; set; } public string IpAddress { get; set; } + public string Source { get; set; } public double Difficulty { get; set; } public double NetworkDifficulty { get; set; } public long BlockHeight { get; set; } diff --git a/src/MiningCore/Payments/ShareRecorder.cs b/src/MiningCore/Payments/ShareRecorder.cs index 56c5c5cbf..cdcfb7db6 100644 --- a/src/MiningCore/Payments/ShareRecorder.cs +++ b/src/MiningCore/Payments/ShareRecorder.cs @@ -109,6 +109,8 @@ private void PersistShares(IList shares) { foreach(var share in shares) { + share.Source = clusterConfig.ClusterName; + var shareEntity = mapper.Map(share); shareRepo.Insert(con, tx, shareEntity); diff --git a/src/MiningCore/Payments/ShareRelay.cs b/src/MiningCore/Payments/ShareRelay.cs index 571b8c030..628fc8627 100644 --- a/src/MiningCore/Payments/ShareRelay.cs +++ b/src/MiningCore/Payments/ShareRelay.cs @@ -72,6 +72,8 @@ private void InitializeQueue() .Do(_ => CheckQueueBacklog()) .Subscribe(share => { + share.Source = clusterConfig.ClusterName; + try { var json = JsonConvert.SerializeObject(share, serializerSettings); diff --git a/src/MiningCore/Persistence/Model/Share.cs b/src/MiningCore/Persistence/Model/Share.cs index 60497d00e..8c8514309 100644 --- a/src/MiningCore/Persistence/Model/Share.cs +++ b/src/MiningCore/Persistence/Model/Share.cs @@ -33,6 +33,7 @@ public class Share public double Difficulty { get; set; } public double NetworkDifficulty { get; set; } public string IpAddress { get; set; } + public string Source { get; set; } public DateTime Created { get; set; } } } diff --git a/src/MiningCore/Persistence/Postgres/Entities/Share.cs b/src/MiningCore/Persistence/Postgres/Entities/Share.cs index a3a68961e..a831472cc 100644 --- a/src/MiningCore/Persistence/Postgres/Entities/Share.cs +++ b/src/MiningCore/Persistence/Postgres/Entities/Share.cs @@ -33,6 +33,7 @@ public class Share public double Difficulty { get; set; } public double NetworkDifficulty { get; set; } public string IpAddress { get; set; } + public string Source { get; set; } public DateTime Created { get; set; } } } diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index 0f686fdd0..213900590 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -11,6 +11,7 @@ CREATE TABLE shares worker TEXT NULL, useragent TEXT NULL, ipaddress TEXT NOT NULL, + source TEXT NULL, created TIMESTAMP NOT NULL ); @@ -31,6 +32,7 @@ CREATE TABLE blocks transactionconfirmationdata TEXT NOT NULL, miner TEXT NULL, reward decimal(28,12) NULL, + source TEXT NULL, created TIMESTAMP NOT NULL ); From 954be8293a648a8616de3dcf839fe938c5b70956 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 2 Mar 2018 12:56:54 +0100 Subject: [PATCH 124/348] Bitcoin ZMQ block notify topic is now configurable --- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 12 ++++++++++-- .../BitcoinDaemonEndpointConfigExtra.cs | 6 ++++++ src/MiningCore/DaemonInterface/DaemonClient.cs | 10 ++++------ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 67cfbcf48..ac644f216 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -111,13 +111,21 @@ protected virtual void SetupJobUpdates() // collect ports var zmq = poolConfig.Daemons .Where(x => !string.IsNullOrEmpty(x.Extra.SafeExtensionDataAs()?.ZmqBlockNotifySocket)) - .ToDictionary(x => x, x => x.Extra.SafeExtensionDataAs().ZmqBlockNotifySocket); + .ToDictionary(x => x, x => + { + var extra = x.Extra.SafeExtensionDataAs(); + var topic = !string.IsNullOrEmpty(extra.ZmqBlockNotifyTopic) ? + extra.ZmqBlockNotifyTopic : + BitcoinConstants.ZmqPublisherTopicBlockHash; + + return (Socket: extra.ZmqBlockNotifySocket, Topic: topic); + }); if (zmq.Count > 0) { logger.Info(() => $"[{LogCat}] Subscribing to ZMQ push-updates from {string.Join(", ", zmq.Values)}"); - var newJobsPubSub = daemon.ZmqSubscribe(zmq, BitcoinConstants.ZmqPublisherTopicBlockHash, 2) + var newJobsPubSub = daemon.ZmqSubscribe(zmq, 2) .Select(frames => { try diff --git a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinDaemonEndpointConfigExtra.cs b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinDaemonEndpointConfigExtra.cs index 394fee48f..02a7e7c05 100644 --- a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinDaemonEndpointConfigExtra.cs +++ b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinDaemonEndpointConfigExtra.cs @@ -29,5 +29,11 @@ public class BitcoinDaemonEndpointConfigExtra /// Should match the value of -zmqpubhashblock daemon start parameter /// public string ZmqBlockNotifySocket { get; set; } + + /// + /// Optional: ZeroMQ block notify topic + /// Defaults to "hashblock" if left blank + /// + public string ZmqBlockNotifyTopic { get; set; } } } diff --git a/src/MiningCore/DaemonInterface/DaemonClient.cs b/src/MiningCore/DaemonInterface/DaemonClient.cs index f3d20f144..2a10b7283 100644 --- a/src/MiningCore/DaemonInterface/DaemonClient.cs +++ b/src/MiningCore/DaemonInterface/DaemonClient.cs @@ -230,14 +230,12 @@ public IObservable> WebsocketSubscribe(Dictionary[]> ZmqSubscribe(Dictionary portMap, string topic, int numMsgSegments = 2) + public IObservable[]> ZmqSubscribe(Dictionary portMap, int numMsgSegments = 2) { - Contract.Requires(!string.IsNullOrEmpty(topic), $"{nameof(topic)} must not be empty"); - - logger.LogInvoke(new[] { topic }); + logger.LogInvoke(); return Observable.Merge(portMap.Keys - .Select(endPoint => ZmqSubscribeEndpoint(endPoint, portMap[endPoint], topic, numMsgSegments))) + .Select(endPoint => ZmqSubscribeEndpoint(endPoint, portMap[endPoint].Socket, portMap[endPoint].Topic, numMsgSegments))) .Publish() .RefCount(); } @@ -508,7 +506,7 @@ private IObservable[]> ZmqSubscribeEndpoint(DaemonEndpo subSocket.Connect(url); subSocket.Subscribe(topic); - logger.Debug($"Subscribed to {url}/{BitcoinConstants.ZmqPublisherTopicBlockHash}"); + logger.Debug($"Subscribed to {url}/{topic}"); while (!tcs.IsCancellationRequested) { From 5e3ad73e8eac5b81a5164c45deb9d2a4b94e3c6e Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 2 Mar 2018 13:55:26 +0100 Subject: [PATCH 125/348] Share relay connect option --- src/MiningCore/Configuration/ClusterConfig.cs | 6 +++++- src/MiningCore/Payments/ShareRelay.cs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Configuration/ClusterConfig.cs b/src/MiningCore/Configuration/ClusterConfig.cs index 0389efc42..99d68530d 100644 --- a/src/MiningCore/Configuration/ClusterConfig.cs +++ b/src/MiningCore/Configuration/ClusterConfig.cs @@ -282,7 +282,11 @@ public partial class ZmqPubSubEndpointConfig public partial class ShareRelayConfig { public string PublishUrl { get; set; } - //public bool Compress { get; set; } + + /// + /// If set to true, the relay will "Connect" to the url, rather than "Bind" it + /// + public bool Connect { get; set; } } public partial class PoolConfig diff --git a/src/MiningCore/Payments/ShareRelay.cs b/src/MiningCore/Payments/ShareRelay.cs index 628fc8627..bf0a94e04 100644 --- a/src/MiningCore/Payments/ShareRelay.cs +++ b/src/MiningCore/Payments/ShareRelay.cs @@ -44,7 +44,11 @@ public void Start(ClusterConfig clusterConfig) this.clusterConfig = clusterConfig; pubSocket = new PublisherSocket(); - pubSocket.Bind(clusterConfig.ShareRelay.PublishUrl); + + if(!clusterConfig.ShareRelay.Connect) + pubSocket.Bind(clusterConfig.ShareRelay.PublishUrl); + else + pubSocket.Connect(clusterConfig.ShareRelay.PublishUrl); InitializeQueue(); From 709fefb3fafef91e8257116dfbb0879c2717d096 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 2 Mar 2018 15:03:15 +0100 Subject: [PATCH 126/348] WIP --- src/MiningCore/Mining/PoolBase.cs | 2 +- src/MiningCore/Payments/ShareRelay.cs | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index f7c2a6718..173a75a34 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -317,7 +317,7 @@ protected void SetupBanning(ClusterConfig clusterConfig) protected virtual void InitStats() { - if(clusterConfig.ShareRelay != null) + if(clusterConfig.ShareRelay == null) LoadStats(); } diff --git a/src/MiningCore/Payments/ShareRelay.cs b/src/MiningCore/Payments/ShareRelay.cs index bf0a94e04..10e82644c 100644 --- a/src/MiningCore/Payments/ShareRelay.cs +++ b/src/MiningCore/Payments/ShareRelay.cs @@ -45,10 +45,17 @@ public void Start(ClusterConfig clusterConfig) pubSocket = new PublisherSocket(); - if(!clusterConfig.ShareRelay.Connect) + if (!clusterConfig.ShareRelay.Connect) + { pubSocket.Bind(clusterConfig.ShareRelay.PublishUrl); + logger.Info(() => $"Bound to {clusterConfig.ShareRelay.PublishUrl}"); + } + else + { pubSocket.Connect(clusterConfig.ShareRelay.PublishUrl); + logger.Info(() => $"Connected to {clusterConfig.ShareRelay.PublishUrl}"); + } InitializeQueue(); From 37b8aa5b86b10c0f2f57096c932d36263cb3c66f Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 2 Mar 2018 15:10:43 +0100 Subject: [PATCH 127/348] WIP --- src/MiningCore/Mining/PoolBase.cs | 2 +- src/MiningCore/Payments/ShareRelay.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index 173a75a34..ae930f58c 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -395,7 +395,7 @@ private void OutputPoolInfo() Network Difficulty: {blockchainStats.NetworkDifficulty} Network Hash Rate: {FormatUtil.FormatHashrate(blockchainStats.NetworkHashrate)} Stratum Port(s): {string.Join(", ", poolConfig.Ports.Keys)} -Pool Fee: {poolConfig.RewardRecipients.Sum(x => x.Percentage)}% +Pool Fee: {(poolConfig.RewardRecipients.Any() ? poolConfig.RewardRecipients.Sum(x => x.Percentage) : 0)}% "; logger.Info(() => msg); diff --git a/src/MiningCore/Payments/ShareRelay.cs b/src/MiningCore/Payments/ShareRelay.cs index 10e82644c..d86c01c65 100644 --- a/src/MiningCore/Payments/ShareRelay.cs +++ b/src/MiningCore/Payments/ShareRelay.cs @@ -42,7 +42,7 @@ public void AttachPool(IMiningPool pool) public void Start(ClusterConfig clusterConfig) { this.clusterConfig = clusterConfig; - +logger.Warn(() => $"SR"); pubSocket = new PublisherSocket(); if (!clusterConfig.ShareRelay.Connect) From 864184481f5baf951545446c0df9796649977a61 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 2 Mar 2018 15:14:49 +0100 Subject: [PATCH 128/348] WIP --- src/MiningCore/Mining/PoolBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index ae930f58c..aa79998b8 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -395,7 +395,7 @@ private void OutputPoolInfo() Network Difficulty: {blockchainStats.NetworkDifficulty} Network Hash Rate: {FormatUtil.FormatHashrate(blockchainStats.NetworkHashrate)} Stratum Port(s): {string.Join(", ", poolConfig.Ports.Keys)} -Pool Fee: {(poolConfig.RewardRecipients.Any() ? poolConfig.RewardRecipients.Sum(x => x.Percentage) : 0)}% +Pool Fee: {(poolConfig?.RewardRecipients.Any() == true ? poolConfig.RewardRecipients.Sum(x => x.Percentage) : 0)}% "; logger.Info(() => msg); From a562957226176ee9488dbc7ed205b87933549424 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 2 Mar 2018 15:19:28 +0100 Subject: [PATCH 129/348] WIP --- src/MiningCore/Mining/PoolBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index aa79998b8..7a88f1132 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -395,7 +395,7 @@ private void OutputPoolInfo() Network Difficulty: {blockchainStats.NetworkDifficulty} Network Hash Rate: {FormatUtil.FormatHashrate(blockchainStats.NetworkHashrate)} Stratum Port(s): {string.Join(", ", poolConfig.Ports.Keys)} -Pool Fee: {(poolConfig?.RewardRecipients.Any() == true ? poolConfig.RewardRecipients.Sum(x => x.Percentage) : 0)}% +Pool Fee: {(poolConfig.RewardRecipients?.Any() == true ? poolConfig.RewardRecipients.Sum(x => x.Percentage) : 0)}% "; logger.Info(() => msg); From e1fb736ce24f6b312fedab14753b4ca364871664 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 2 Mar 2018 15:21:59 +0100 Subject: [PATCH 130/348] WIP --- src/MiningCore/Payments/ShareRelay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Payments/ShareRelay.cs b/src/MiningCore/Payments/ShareRelay.cs index d86c01c65..10e82644c 100644 --- a/src/MiningCore/Payments/ShareRelay.cs +++ b/src/MiningCore/Payments/ShareRelay.cs @@ -42,7 +42,7 @@ public void AttachPool(IMiningPool pool) public void Start(ClusterConfig clusterConfig) { this.clusterConfig = clusterConfig; -logger.Warn(() => $"SR"); + pubSocket = new PublisherSocket(); if (!clusterConfig.ShareRelay.Connect) From 639af47e0e7e86efd1b9725e9b3cc484063eb179 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 3 Mar 2018 10:21:35 +0100 Subject: [PATCH 131/348] BTCP explorer links --- src/MiningCore/Blockchain/CoinMetaData.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index d29fa8c2d..086ecc86d 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -30,6 +30,7 @@ public static class CoinMetaData { CoinType.BTC, new Dictionary { { string.Empty, "https://blockchain.info/block/{0}" }}}, { CoinType.DOGE, new Dictionary { { string.Empty, "https://dogechain.info/block/{0}" }}}, { CoinType.ZEC, new Dictionary { { string.Empty, "https://explorer.zcha.in/blocks/{0}" }}}, + { CoinType.BTCP, new Dictionary { { string.Empty, "http://explorer.btcprivate.org/blocks/{0}" } }}, { CoinType.ZCL, new Dictionary { { string.Empty, "http://explorer.zclmine.pro/blocks/{0}" }}}, { CoinType.ZEN, new Dictionary { { string.Empty, "http://explorer.zensystem.io/blocks/{0}" } }}, { CoinType.DGB, new Dictionary { { string.Empty, "https://digiexplorer.info/block/{0}" }}}, @@ -61,6 +62,7 @@ public static class CoinMetaData { CoinType.ZEC, "https://explorer.zcha.in/transactions/{0}" }, { CoinType.ZCL, "http://explorer.zclmine.pro/transactions/{0}" }, { CoinType.ZEN, "http://explorer.zensystem.io/transactions/{0}" }, + { CoinType.BTCP, "https://explorer.btcprivate.org/transactions/{0}" }, { CoinType.DGB, "https://digiexplorer.info/tx/{0}" }, { CoinType.NMC, "https://explorer.namecoin.info/tx/{0}" }, { CoinType.GRS, "https://groestlsight.groestlcoin.org/tx/{0}" }, From f89ab9c675e162b8eecf87f3320c7afa86b5ef33 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 3 Mar 2018 21:49:00 +0100 Subject: [PATCH 132/348] Fix: Persist share and block source --- .../Persistence/Postgres/Repositories/BlockRepository.cs | 4 ++-- .../Persistence/Postgres/Repositories/ShareRepository.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs index c15bd80dd..68118cfaa 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs @@ -48,8 +48,8 @@ public void Insert(IDbConnection con, IDbTransaction tx, Block block) var mapped = mapper.Map(block); var query = - "INSERT INTO blocks(poolid, blockheight, networkdifficulty, status, type, transactionconfirmationdata, miner, reward, effort, confirmationprogress, created) " + - "VALUES(@poolid, @blockheight, @networkdifficulty, @status, @type, @transactionconfirmationdata, @miner, @reward, @effort, @confirmationprogress, @created)"; + "INSERT INTO blocks(poolid, blockheight, networkdifficulty, status, type, transactionconfirmationdata, miner, reward, effort, confirmationprogress, source, created) " + + "VALUES(@poolid, @blockheight, @networkdifficulty, @status, @type, @transactionconfirmationdata, @miner, @reward, @effort, @confirmationprogress, @source, @created)"; con.Execute(query, mapped, tx); } diff --git a/src/MiningCore/Persistence/Postgres/Repositories/ShareRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/ShareRepository.cs index f5a3c9526..3643792a1 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/ShareRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/ShareRepository.cs @@ -49,9 +49,9 @@ public void Insert(IDbConnection con, IDbTransaction tx, Share share) var mapped = mapper.Map(share); var query = "INSERT INTO shares(poolid, blockheight, difficulty, " + - "networkdifficulty, miner, worker, payoutinfo, useragent, ipaddress, created) " + + "networkdifficulty, miner, worker, payoutinfo, useragent, ipaddress, source, created) " + "VALUES(@poolid, @blockheight, @difficulty, " + - "@networkdifficulty, @miner, @worker, @payoutinfo, @useragent, @ipaddress, @created)"; + "@networkdifficulty, @miner, @worker, @payoutinfo, @useragent, @ipaddress, @source, @created)"; con.Execute(query, mapped, tx); } From a41edf42663f81006e66b797438fd30655bc0dde Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 3 Mar 2018 23:23:56 +0100 Subject: [PATCH 133/348] WIP --- .../Persistence/Postgres/Repositories/BlockRepository.cs | 4 ++-- .../Persistence/Postgres/Repositories/ShareRepository.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs index 68118cfaa..c15bd80dd 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs @@ -48,8 +48,8 @@ public void Insert(IDbConnection con, IDbTransaction tx, Block block) var mapped = mapper.Map(block); var query = - "INSERT INTO blocks(poolid, blockheight, networkdifficulty, status, type, transactionconfirmationdata, miner, reward, effort, confirmationprogress, source, created) " + - "VALUES(@poolid, @blockheight, @networkdifficulty, @status, @type, @transactionconfirmationdata, @miner, @reward, @effort, @confirmationprogress, @source, @created)"; + "INSERT INTO blocks(poolid, blockheight, networkdifficulty, status, type, transactionconfirmationdata, miner, reward, effort, confirmationprogress, created) " + + "VALUES(@poolid, @blockheight, @networkdifficulty, @status, @type, @transactionconfirmationdata, @miner, @reward, @effort, @confirmationprogress, @created)"; con.Execute(query, mapped, tx); } diff --git a/src/MiningCore/Persistence/Postgres/Repositories/ShareRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/ShareRepository.cs index 3643792a1..f5a3c9526 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/ShareRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/ShareRepository.cs @@ -49,9 +49,9 @@ public void Insert(IDbConnection con, IDbTransaction tx, Share share) var mapped = mapper.Map(share); var query = "INSERT INTO shares(poolid, blockheight, difficulty, " + - "networkdifficulty, miner, worker, payoutinfo, useragent, ipaddress, source, created) " + + "networkdifficulty, miner, worker, payoutinfo, useragent, ipaddress, created) " + "VALUES(@poolid, @blockheight, @difficulty, " + - "@networkdifficulty, @miner, @worker, @payoutinfo, @useragent, @ipaddress, @source, @created)"; + "@networkdifficulty, @miner, @worker, @payoutinfo, @useragent, @ipaddress, @created)"; con.Execute(query, mapped, tx); } From de7d6bf4f61c45b7a53f3bbec8378378b5d86d48 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sun, 4 Mar 2018 15:05:28 +0100 Subject: [PATCH 134/348] ZCash & BTCP PowLimit configuration --- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 6 +- .../Blockchain/BitcoinGold/BitcoinGoldJob.cs | 5 +- .../Blockchain/ZCash/ZCashConstants.cs | 90 ++++++++++++++++++- src/MiningCore/Blockchain/ZCash/ZCashJob.cs | 4 +- .../Blockchain/ZCash/ZCashPoolBase.cs | 20 ++++- src/MiningCore/Program.cs | 3 + 6 files changed, 118 insertions(+), 10 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index ac644f216..85e4d3f9c 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -114,8 +114,8 @@ protected virtual void SetupJobUpdates() .ToDictionary(x => x, x => { var extra = x.Extra.SafeExtensionDataAs(); - var topic = !string.IsNullOrEmpty(extra.ZmqBlockNotifyTopic) ? - extra.ZmqBlockNotifyTopic : + var topic = !string.IsNullOrEmpty(extra.ZmqBlockNotifyTopic) ? + extra.ZmqBlockNotifyTopic : BitcoinConstants.ZmqPublisherTopicBlockHash; return (Socket: extra.ZmqBlockNotifySocket, Topic: topic); @@ -388,6 +388,8 @@ private async Task UpdateNetworkStatsLegacyAsync() #region API-Surface + public BitcoinNetworkType NetworkType => networkType; + public IObservable Jobs { get; private set; } public virtual async Task ValidateAddressAsync(string address) diff --git a/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJob.cs b/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJob.cs index 8731a149f..268c3934c 100644 --- a/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJob.cs +++ b/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJob.cs @@ -97,9 +97,12 @@ public override void Init(ZCashBlockTemplate blockTemplate, string jobId, 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(ZCashConstants.Diff1b, BlockTemplate.Target.HexToByteArray().ToBigInteger()); + Difficulty = (double)new BigRational(coinbaseTxConfig.Diff1b, BlockTemplate.Target.HexToByteArray().ToBigInteger()); this.isPoS = isPoS; this.shareMultiplier = shareMultiplier; diff --git a/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs b/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs index fa89154a6..ba7d0c6dd 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs @@ -28,6 +28,9 @@ namespace MiningCore.Blockchain.ZCash { public class ZCashCoinbaseTxConfig { + public BigInteger Diff1 { get; set; } + public System.Numerics.BigInteger Diff1b { get; set; } + public bool PayFoundersReward { get; set; } public decimal PercentFoundersReward { get; set; } public string[] FoundersRewardAddresses { get; set; } @@ -44,8 +47,6 @@ public class ZCashCoinbaseTxConfig public class ZCashConstants { - public static readonly BigInteger Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16); - public static readonly System.Numerics.BigInteger Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber); public const int TargetPaddingLength = 32; private static readonly Dictionary ZCashCoinbaseTxConfig = new Dictionary @@ -53,6 +54,9 @@ public class ZCashConstants { BitcoinNetworkType.Main, new ZCashCoinbaseTxConfig { + Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), + Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + PayFoundersReward = true, PercentFoundersReward = 20, FoundersRewardSubsidyHalvingInterval = 840000, @@ -82,6 +86,9 @@ public class ZCashConstants { BitcoinNetworkType.Test, new ZCashCoinbaseTxConfig { + Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), + Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + PayFoundersReward = true, PercentFoundersReward = 20, FoundersRewardSubsidyHalvingInterval = 840000, @@ -107,6 +114,9 @@ public class ZCashConstants { BitcoinNetworkType.RegTest, new ZCashCoinbaseTxConfig { + Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), + Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + PayFoundersReward = true, PercentFoundersReward = 20, FoundersRewardSubsidyHalvingInterval = 150, @@ -125,6 +135,9 @@ public class ZCashConstants { BitcoinNetworkType.Main, new ZCashCoinbaseTxConfig { + Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), + Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + PayFoundersReward = false, PercentFoundersReward = 0, FoundersRewardSubsidyHalvingInterval = 0, @@ -139,6 +152,9 @@ public class ZCashConstants { BitcoinNetworkType.Test, new ZCashCoinbaseTxConfig { + Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), + Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + PayFoundersReward = false, PercentFoundersReward = 0, FoundersRewardSubsidyHalvingInterval = 0, @@ -153,6 +169,9 @@ public class ZCashConstants { BitcoinNetworkType.RegTest, new ZCashCoinbaseTxConfig { + Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), + Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + PayFoundersReward = false, PercentFoundersReward = 0, FoundersRewardSubsidyHalvingInterval = 0, @@ -171,6 +190,9 @@ public class ZCashConstants { BitcoinNetworkType.Main, new ZCashCoinbaseTxConfig { + Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), + Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + PayFoundersReward = true, PercentFoundersReward = 8.5m, FoundersRewardSubsidyHalvingInterval = 840000, @@ -210,6 +232,9 @@ public class ZCashConstants { BitcoinNetworkType.Test, new ZCashCoinbaseTxConfig { + Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), + Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + PayFoundersReward = true, PercentFoundersReward = 8.5m, FoundersRewardSubsidyHalvingInterval = 840000, @@ -248,6 +273,9 @@ public class ZCashConstants { BitcoinNetworkType.RegTest, new ZCashCoinbaseTxConfig { + Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), + Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + PayFoundersReward = true, PercentFoundersReward = 8.5m, FoundersRewardSubsidyHalvingInterval = 2000, @@ -270,13 +298,69 @@ public class ZCashConstants }, }; + private static readonly Dictionary BTCPCoinbaseTxConfig = new Dictionary + { + { + BitcoinNetworkType.Main, new ZCashCoinbaseTxConfig + { + Diff1 = new BigInteger("007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), + Diff1b = System.Numerics.BigInteger.Parse("007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + + PayFoundersReward = false, + PercentFoundersReward = 0, + FoundersRewardSubsidyHalvingInterval = 0, + FoundersRewardSubsidySlowStartInterval = 0, + + FoundersRewardAddresses = new[] + { + "" + } + } + }, + { + BitcoinNetworkType.Test, new ZCashCoinbaseTxConfig + { + Diff1 = new BigInteger("007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), + Diff1b = System.Numerics.BigInteger.Parse("007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + + PayFoundersReward = false, + PercentFoundersReward = 0, + FoundersRewardSubsidyHalvingInterval = 0, + FoundersRewardSubsidySlowStartInterval = 0, + + FoundersRewardAddresses = new[] + { + "" + } + } + }, + { + BitcoinNetworkType.RegTest, new ZCashCoinbaseTxConfig + { + Diff1 = new BigInteger("007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), + Diff1b = System.Numerics.BigInteger.Parse("007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + + PayFoundersReward = false, + PercentFoundersReward = 0, + FoundersRewardSubsidyHalvingInterval = 0, + FoundersRewardSubsidySlowStartInterval = 0, + + FoundersRewardAddresses = new[] + { + "" + } + } + }, + }; + public static Dictionary> CoinbaseTxConfig = new Dictionary> { { CoinType.ZEC, ZCashCoinbaseTxConfig }, { CoinType.ZCL, ZCLCoinbaseTxConfig }, { CoinType.ZEN, ZencashCoinbaseTxConfig }, - { CoinType.BTCP, ZCLCoinbaseTxConfig }, + { CoinType.BTG, ZCLCoinbaseTxConfig }, + { CoinType.BTCP, BTCPCoinbaseTxConfig }, }; } diff --git a/src/MiningCore/Blockchain/ZCash/ZCashJob.cs b/src/MiningCore/Blockchain/ZCash/ZCashJob.cs index 456a315f7..ad4bb386d 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashJob.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashJob.cs @@ -166,7 +166,7 @@ public override void Init(ZCashBlockTemplate blockTemplate, string jobId, BlockTemplate = blockTemplate; JobId = jobId; - Difficulty = (double) new BigRational(ZCashConstants.Diff1b, BlockTemplate.Target.HexToByteArray().ReverseArray().ToBigInteger()); + Difficulty = (double) new BigRational(coinbaseTxConfig.Diff1b, BlockTemplate.Target.HexToByteArray().ReverseArray().ToBigInteger()); this.isPoS = isPoS; this.shareMultiplier = shareMultiplier; @@ -315,7 +315,7 @@ protected virtual BitcoinShare ProcessShareInternal(StratumClient worker, string var headerValue = new uint256(headerHash); // calc share-diff - var shareDiff = (double) new BigRational(ZCashConstants.Diff1b, headerHash.ToBigInteger()) * shareMultiplier; + var shareDiff = (double) new BigRational(coinbaseTxConfig.Diff1b, headerHash.ToBigInteger()) * shareMultiplier; var stratumDifficulty = context.Difficulty; var ratio = shareDiff / stratumDifficulty; diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs b/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs index e7d32d8ce..09391ae3c 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs @@ -55,12 +55,28 @@ public ZCashPoolBase(IComponentContext ctx, { } + private ZCashCoinbaseTxConfig coinbaseTxConfig; + protected override BitcoinJobManager CreateJobManager() { return ctx.Resolve>( new TypedParameter(typeof(IExtraNonceProvider), new ZCashExtraNonceProvider())); } + #region Overrides of BitcoinPoolBase + + /// + protected override async Task SetupJobManager() + { + await base.SetupJobManager(); + + if (ZCashConstants.CoinbaseTxConfig.TryGetValue(poolConfig.Coin.Type, out var coinbaseTx)) + coinbaseTx.TryGetValue(manager.NetworkType, out coinbaseTxConfig); + + } + + #endregion + protected override void OnSubscribe(StratumClient client, Timestamped tsRequest) { var request = tsRequest.Value; @@ -120,7 +136,7 @@ private void OnSuggestTarget(StratumClient client, Timestamped t { if (System.Numerics.BigInteger.TryParse(target, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var targetBig)) { - var newDiff = (double) new BigRational(ZCashConstants.Diff1b, targetBig); + var newDiff = (double) new BigRational(coinbaseTxConfig.Diff1b, targetBig); var poolEndpoint = poolConfig.Ports[client.PoolEndpoint.Port]; if (newDiff >= poolEndpoint.Difficulty) @@ -237,7 +253,7 @@ protected override void OnVarDiffUpdate(StratumClient client, double newDiff) private string EncodeTarget(double difficulty) { var diff = BigInteger.ValueOf((long) (difficulty * 255d)); - var quotient = ZCashConstants.Diff1.Divide(diff).Multiply(BigInteger.ValueOf(255)); + var quotient = coinbaseTxConfig.Diff1.Divide(diff).Multiply(BigInteger.ValueOf(255)); var bytes = quotient.ToByteArray(); var padded = ArrayPool.Shared.Rent(ZCashConstants.TargetPaddingLength); diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index 359b6fa8e..5e16d3db1 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -21,6 +21,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Linq; using System.Reactive; @@ -40,6 +41,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Api; using MiningCore.Api.Responses; using MiningCore.Blockchain; +using MiningCore.Blockchain.Bitcoin.DaemonResponses; +using MiningCore.Blockchain.ZCash; using MiningCore.Configuration; using MiningCore.Crypto.Hashing.Algorithms; using MiningCore.Crypto.Hashing.Equihash; From 41db2e9d5661874b7d3947baf955be024408840f Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sun, 4 Mar 2018 16:49:28 +0100 Subject: [PATCH 135/348] Fix share and block source --- src/MiningCore/Persistence/Model/Block.cs | 1 + src/MiningCore/Persistence/Postgres/Entities/Block.cs | 1 + .../Persistence/Postgres/Repositories/BlockRepository.cs | 4 ++-- .../Persistence/Postgres/Repositories/ShareRepository.cs | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/MiningCore/Persistence/Model/Block.cs b/src/MiningCore/Persistence/Model/Block.cs index f1bf0082c..5cf381f32 100644 --- a/src/MiningCore/Persistence/Model/Block.cs +++ b/src/MiningCore/Persistence/Model/Block.cs @@ -35,6 +35,7 @@ public class Block public string TransactionConfirmationData { get; set; } public string Miner { get; set; } public decimal Reward { get; set; } + public string Source { get; set; } public DateTime Created { get; set; } } } diff --git a/src/MiningCore/Persistence/Postgres/Entities/Block.cs b/src/MiningCore/Persistence/Postgres/Entities/Block.cs index 01d23e58d..b9abf683d 100644 --- a/src/MiningCore/Persistence/Postgres/Entities/Block.cs +++ b/src/MiningCore/Persistence/Postgres/Entities/Block.cs @@ -35,6 +35,7 @@ public class Block public string TransactionConfirmationData { get; set; } public string Miner { get; set; } public decimal Reward { get; set; } + public string Source { get; set; } public DateTime Created { get; set; } } } diff --git a/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs index c15bd80dd..68118cfaa 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs @@ -48,8 +48,8 @@ public void Insert(IDbConnection con, IDbTransaction tx, Block block) var mapped = mapper.Map(block); var query = - "INSERT INTO blocks(poolid, blockheight, networkdifficulty, status, type, transactionconfirmationdata, miner, reward, effort, confirmationprogress, created) " + - "VALUES(@poolid, @blockheight, @networkdifficulty, @status, @type, @transactionconfirmationdata, @miner, @reward, @effort, @confirmationprogress, @created)"; + "INSERT INTO blocks(poolid, blockheight, networkdifficulty, status, type, transactionconfirmationdata, miner, reward, effort, confirmationprogress, source, created) " + + "VALUES(@poolid, @blockheight, @networkdifficulty, @status, @type, @transactionconfirmationdata, @miner, @reward, @effort, @confirmationprogress, @source, @created)"; con.Execute(query, mapped, tx); } diff --git a/src/MiningCore/Persistence/Postgres/Repositories/ShareRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/ShareRepository.cs index f5a3c9526..3643792a1 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/ShareRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/ShareRepository.cs @@ -49,9 +49,9 @@ public void Insert(IDbConnection con, IDbTransaction tx, Share share) var mapped = mapper.Map(share); var query = "INSERT INTO shares(poolid, blockheight, difficulty, " + - "networkdifficulty, miner, worker, payoutinfo, useragent, ipaddress, created) " + + "networkdifficulty, miner, worker, payoutinfo, useragent, ipaddress, source, created) " + "VALUES(@poolid, @blockheight, @difficulty, " + - "@networkdifficulty, @miner, @worker, @payoutinfo, @useragent, @ipaddress, @created)"; + "@networkdifficulty, @miner, @worker, @payoutinfo, @useragent, @ipaddress, @source, @created)"; con.Execute(query, mapped, tx); } From 8af8bed6c563030dcc29f640a36b6a3b126d5f7d Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sun, 4 Mar 2018 17:23:45 +0100 Subject: [PATCH 136/348] Share logging --- src/MiningCore/Mining/PoolBase.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index 7a88f1132..9c0d9658b 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -223,7 +223,8 @@ private void StartExternalStratumPublisherListeners() // re-publish shareSubject.OnNext(new ClientShare(null, share)); - logger.Info(() => $"[{LogCat}] External share accepted: D={Math.Round(share.Difficulty, 3)}"); + var source = !string.IsNullOrEmpty(share.Source) ? $" [{share.Source.ToUpper()}]" : string.Empty; + logger.Info(() => $"[{LogCat}] External share accepted: D={Math.Round(share.Difficulty, 3)}{source}"); } } } From 56fb9e112490788630ac729ea9359b5005cd8d48 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sun, 4 Mar 2018 17:33:01 +0100 Subject: [PATCH 137/348] Share source --- src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs | 1 + src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs | 1 + src/MiningCore/Blockchain/Monero/MoneroJobManager.cs | 1 + src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs | 1 + src/MiningCore/Mining/PoolBase.cs | 4 ++-- src/MiningCore/Payments/ShareRecorder.cs | 2 -- 6 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 85e4d3f9c..03df41e41 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -518,6 +518,7 @@ public virtual async Task SubmitShareAsync(StratumClient worker, o share.Miner = minerName; share.Worker = workerName; share.UserAgent = context.UserAgent; + share.Source = clusterConfig.ClusterName; share.Created = clock.Now; return share; diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs index a578d6b99..d092bff2a 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs @@ -433,6 +433,7 @@ public async Task SubmitShareAsync(StratumClient worker, // enrich share with common data share.PoolId = poolConfig.Id; share.NetworkDifficulty = BlockchainStats.NetworkDifficulty; + share.Source = clusterConfig.ClusterName; share.Created = clock.Now; return share; diff --git a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs index 88c2ff6bc..9dd4374c4 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs @@ -302,6 +302,7 @@ public async Task SubmitShareAsync(StratumClient worker, share.Worker = context.WorkerName; share.PayoutInfo = context.PaymentId; share.UserAgent = context.UserAgent; + share.Source = clusterConfig.ClusterName; share.NetworkDifficulty = job.BlockTemplate.Difficulty; share.Created = clock.Now; diff --git a/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs b/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs index db5d00fae..b0c7ca7f4 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs @@ -182,6 +182,7 @@ public override async Task SubmitShareAsync(StratumClient worker, share.Miner = minerName; share.Worker = workerName; share.UserAgent = context.UserAgent; + share.Source = clusterConfig.ClusterName; share.NetworkDifficulty = job.Difficulty; share.Difficulty = share.Difficulty / ShareMultiplier; share.Created = clock.Now; diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index 9c0d9658b..db5843aa7 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -223,8 +223,8 @@ private void StartExternalStratumPublisherListeners() // re-publish shareSubject.OnNext(new ClientShare(null, share)); - var source = !string.IsNullOrEmpty(share.Source) ? $" [{share.Source.ToUpper()}]" : string.Empty; - logger.Info(() => $"[{LogCat}] External share accepted: D={Math.Round(share.Difficulty, 3)}{source}"); + var source = !string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}]" : string.Empty; + logger.Info(() => $"[{LogCat}] External {source} share accepted: D={Math.Round(share.Difficulty, 3)}"); } } } diff --git a/src/MiningCore/Payments/ShareRecorder.cs b/src/MiningCore/Payments/ShareRecorder.cs index cdcfb7db6..56c5c5cbf 100644 --- a/src/MiningCore/Payments/ShareRecorder.cs +++ b/src/MiningCore/Payments/ShareRecorder.cs @@ -109,8 +109,6 @@ private void PersistShares(IList shares) { foreach(var share in shares) { - share.Source = clusterConfig.ClusterName; - var shareEntity = mapper.Map(share); shareRepo.Insert(con, tx, shareEntity); From bdf6599d1962b193d7f88d21853c09e7bec8053f Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 5 Mar 2018 18:21:03 +0100 Subject: [PATCH 138/348] Block effort calc --- .../Blockchain/ZCash/ZCashPayoutHandler.cs | 15 +++++++++++++++ src/MiningCore/Program.cs | 5 ----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs index ae1b0fd43..493117a9e 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs @@ -35,7 +35,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Persistence.Model; using MiningCore.Persistence.Repositories; using MiningCore.Time; +using MiningCore.Util; using Newtonsoft.Json.Linq; +using Block = MiningCore.Persistence.Model.Block; using Contract = MiningCore.Contracts.Contract; namespace MiningCore.Blockchain.ZCash @@ -217,6 +219,19 @@ public override async Task PayoutAsync(Balance[] balances) } } + #region Overrides of BitcoinPayoutHandler + + public override Task CalculateBlockEffortAsync(Block block, double accumulatedBlockShareDiff) + { + var divisor = (double) new BigRational(coinbaseTxConfig.Diff1b, ZCashConstants.CoinbaseTxConfig[CoinType.ZEC][networkType].Diff1b); + var networkDiff = block.NetworkDifficulty / divisor; + + block.Effort = accumulatedBlockShareDiff / networkDiff; + return Task.FromResult(true); + } + + #endregion + #endregion // IPayoutHandler /// diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index 5e16d3db1..485f97b79 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -21,7 +21,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.IO; using System.Linq; using System.Reactive; @@ -40,9 +39,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using Microsoft.Extensions.CommandLineUtils; using MiningCore.Api; using MiningCore.Api.Responses; -using MiningCore.Blockchain; -using MiningCore.Blockchain.Bitcoin.DaemonResponses; -using MiningCore.Blockchain.ZCash; using MiningCore.Configuration; using MiningCore.Crypto.Hashing.Algorithms; using MiningCore.Crypto.Hashing.Equihash; @@ -54,7 +50,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Persistence.Postgres; using MiningCore.Persistence.Postgres.Repositories; using MiningCore.Util; -using NBitcoin; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using NLog; From be8137c288daab24853939fb78e102a8fa012a2b Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 5 Mar 2018 19:53:48 +0100 Subject: [PATCH 139/348] WIP --- .../Blockchain/ZCash/ZCashPayoutHandler.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs index 493117a9e..5bed9b274 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs @@ -219,19 +219,6 @@ public override async Task PayoutAsync(Balance[] balances) } } - #region Overrides of BitcoinPayoutHandler - - public override Task CalculateBlockEffortAsync(Block block, double accumulatedBlockShareDiff) - { - var divisor = (double) new BigRational(coinbaseTxConfig.Diff1b, ZCashConstants.CoinbaseTxConfig[CoinType.ZEC][networkType].Diff1b); - var networkDiff = block.NetworkDifficulty / divisor; - - block.Effort = accumulatedBlockShareDiff / networkDiff; - return Task.FromResult(true); - } - - #endregion - #endregion // IPayoutHandler /// From dad5e0e43792f76fc90d1b06af491d8ec5f3485f Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 5 Mar 2018 20:22:05 +0100 Subject: [PATCH 140/348] WIP --- src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs b/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs index 09391ae3c..86e63355f 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs @@ -28,6 +28,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using AutoMapper; using MiningCore.Blockchain.Bitcoin; using MiningCore.Blockchain.ZCash.DaemonResponses; +using MiningCore.Configuration; using MiningCore.Extensions; using MiningCore.JsonRpc; using MiningCore.Notifications; @@ -231,6 +232,10 @@ public override double HashrateFromShares(double shares, double interval) { var multiplier = BitcoinConstants.Pow2x32 / manager.ShareMultiplier; var result = shares * multiplier / interval / 1000000 * 2; + + if (poolConfig.Coin.Type == CoinType.BTCP) + result /= 256; + return result; } From b93bfc42862a33e7a642d4cb8fff1aa2b060bab9 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 6 Mar 2018 13:52:53 +0100 Subject: [PATCH 141/348] Fix BTCP hashrate hack --- src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs | 7 ++++--- src/MiningCore/Program.cs | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs b/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs index 86e63355f..8ccfdb678 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs @@ -57,6 +57,7 @@ public ZCashPoolBase(IComponentContext ctx, } private ZCashCoinbaseTxConfig coinbaseTxConfig; + private double hashrateDivisor; protected override BitcoinJobManager CreateJobManager() { @@ -74,6 +75,8 @@ protected override async Task SetupJobManager() if (ZCashConstants.CoinbaseTxConfig.TryGetValue(poolConfig.Coin.Type, out var coinbaseTx)) coinbaseTx.TryGetValue(manager.NetworkType, out coinbaseTxConfig); + hashrateDivisor = (double)new BigRational(coinbaseTxConfig.Diff1b, + ZCashConstants.CoinbaseTxConfig[CoinType.ZEC][manager.NetworkType].Diff1b); } #endregion @@ -233,9 +236,7 @@ public override double HashrateFromShares(double shares, double interval) var multiplier = BitcoinConstants.Pow2x32 / manager.ShareMultiplier; var result = shares * multiplier / interval / 1000000 * 2; - if (poolConfig.Coin.Type == CoinType.BTCP) - result /= 256; - + result /= hashrateDivisor; return result; } diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index 485f97b79..5e16d3db1 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -21,6 +21,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Linq; using System.Reactive; @@ -39,6 +40,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using Microsoft.Extensions.CommandLineUtils; using MiningCore.Api; using MiningCore.Api.Responses; +using MiningCore.Blockchain; +using MiningCore.Blockchain.Bitcoin.DaemonResponses; +using MiningCore.Blockchain.ZCash; using MiningCore.Configuration; using MiningCore.Crypto.Hashing.Algorithms; using MiningCore.Crypto.Hashing.Equihash; @@ -50,6 +54,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Persistence.Postgres; using MiningCore.Persistence.Postgres.Repositories; using MiningCore.Util; +using NBitcoin; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using NLog; From f54859de5bc1b9754ef9481817d48a78ac370b6d Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 6 Mar 2018 14:11:22 +0100 Subject: [PATCH 142/348] Store block hash --- src/MiningCore/Api/Responses/GetBlocksResponse.cs | 1 + src/MiningCore/AutoMapperProfile.cs | 1 + src/MiningCore/Blockchain/Abstractions.cs | 5 +++++ src/MiningCore/Blockchain/Bitcoin/BitcoinShare.cs | 1 - src/MiningCore/Blockchain/Ethereum/EthereumJob.cs | 1 + src/MiningCore/Blockchain/Monero/MoneroJob.cs | 3 ++- src/MiningCore/Blockchain/ShareBase.cs | 1 + src/MiningCore/Persistence/Model/Block.cs | 1 + src/MiningCore/Persistence/Postgres/Entities/Block.cs | 1 + .../Persistence/Postgres/Repositories/BlockRepository.cs | 4 ++-- src/MiningCore/Persistence/Postgres/Scripts/createdb.sql | 1 + 11 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/MiningCore/Api/Responses/GetBlocksResponse.cs b/src/MiningCore/Api/Responses/GetBlocksResponse.cs index 60eaa7ee9..e2d38a1a8 100644 --- a/src/MiningCore/Api/Responses/GetBlocksResponse.cs +++ b/src/MiningCore/Api/Responses/GetBlocksResponse.cs @@ -33,6 +33,7 @@ public class Block public string TransactionConfirmationData { get; set; } public decimal Reward { get; set; } public string InfoLink { get; set; } + public string Hash { get; set; } public DateTime Created { get; set; } } } diff --git a/src/MiningCore/AutoMapperProfile.cs b/src/MiningCore/AutoMapperProfile.cs index 2817ccb44..e76232781 100644 --- a/src/MiningCore/AutoMapperProfile.cs +++ b/src/MiningCore/AutoMapperProfile.cs @@ -38,6 +38,7 @@ public AutoMapperProfile() CreateMap() .ForMember(dest => dest.Reward, opt => opt.MapFrom(src => src.BlockReward)) + .ForMember(dest => dest.Hash, opt => opt.MapFrom(src => src.BlockHash)) .ForMember(dest => dest.Status, opt => opt.Ignore()); CreateMap().ConvertUsing(e => e.ToString().ToLower()); diff --git a/src/MiningCore/Blockchain/Abstractions.cs b/src/MiningCore/Blockchain/Abstractions.cs index c750ed6db..9b2c1ff45 100644 --- a/src/MiningCore/Blockchain/Abstractions.cs +++ b/src/MiningCore/Blockchain/Abstractions.cs @@ -91,6 +91,11 @@ public interface IShare /// decimal BlockReward { get; set; } + /// + /// Block hash + /// + string BlockHash { get; set; } + /// /// If this share presumably resulted in a block /// diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinShare.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinShare.cs index 3a16d43c9..d3bd3d317 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinShare.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinShare.cs @@ -23,6 +23,5 @@ namespace MiningCore.Blockchain.Bitcoin public class BitcoinShare : ShareBase { public string BlockHex { get; set; } - public string BlockHash { get; set; } } } diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs index f1b9d48e9..95d93b540 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs @@ -110,6 +110,7 @@ public async Task ProcessShareAsync(StratumClient worker, string MixHash = mixDigest.ToHexString(true), IsBlockCandidate = isBlockCandidate, Difficulty = stratumDifficulty * EthereumConstants.Pow2x32, + BlockHash = mixDigest.ToHexString(true) // OW: is this correct? }; if (share.IsBlockCandidate) diff --git a/src/MiningCore/Blockchain/Monero/MoneroJob.cs b/src/MiningCore/Blockchain/Monero/MoneroJob.cs index fbba2b5f8..6ea927106 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJob.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJob.cs @@ -206,7 +206,8 @@ public MoneroShare ProcessShare(string nonce, uint workerExtraNonce, string work IsBlockCandidate = isBlockCandidate, BlobHex = blob.ToHexString(), BlobHash = blockHash.ToHexString(), - Difficulty = stratumDifficulty, + BlockHash = blockHash.ToHexString(), + Difficulty = stratumDifficulty, }; return result; diff --git a/src/MiningCore/Blockchain/ShareBase.cs b/src/MiningCore/Blockchain/ShareBase.cs index 0b2fb69c0..fd00a6582 100644 --- a/src/MiningCore/Blockchain/ShareBase.cs +++ b/src/MiningCore/Blockchain/ShareBase.cs @@ -35,6 +35,7 @@ public class ShareBase : IShare public double NetworkDifficulty { get; set; } public long BlockHeight { get; set; } public decimal BlockReward { get; set; } + public string BlockHash { get; set; } public bool IsBlockCandidate { get; set; } public string TransactionConfirmationData { get; set; } public DateTime Created { get; set; } diff --git a/src/MiningCore/Persistence/Model/Block.cs b/src/MiningCore/Persistence/Model/Block.cs index 5cf381f32..881d08bf1 100644 --- a/src/MiningCore/Persistence/Model/Block.cs +++ b/src/MiningCore/Persistence/Model/Block.cs @@ -36,6 +36,7 @@ public class Block public string Miner { get; set; } public decimal Reward { get; set; } public string Source { get; set; } + public string Hash { get; set; } public DateTime Created { get; set; } } } diff --git a/src/MiningCore/Persistence/Postgres/Entities/Block.cs b/src/MiningCore/Persistence/Postgres/Entities/Block.cs index b9abf683d..0f99a0793 100644 --- a/src/MiningCore/Persistence/Postgres/Entities/Block.cs +++ b/src/MiningCore/Persistence/Postgres/Entities/Block.cs @@ -36,6 +36,7 @@ public class Block public string Miner { get; set; } public decimal Reward { get; set; } public string Source { get; set; } + public string Hash { get; set; } public DateTime Created { get; set; } } } diff --git a/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs index 68118cfaa..a8babd1a0 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/BlockRepository.cs @@ -48,8 +48,8 @@ public void Insert(IDbConnection con, IDbTransaction tx, Block block) var mapped = mapper.Map(block); var query = - "INSERT INTO blocks(poolid, blockheight, networkdifficulty, status, type, transactionconfirmationdata, miner, reward, effort, confirmationprogress, source, created) " + - "VALUES(@poolid, @blockheight, @networkdifficulty, @status, @type, @transactionconfirmationdata, @miner, @reward, @effort, @confirmationprogress, @source, @created)"; + "INSERT INTO blocks(poolid, blockheight, networkdifficulty, status, type, transactionconfirmationdata, miner, reward, effort, confirmationprogress, source, hash, created) " + + "VALUES(@poolid, @blockheight, @networkdifficulty, @status, @type, @transactionconfirmationdata, @miner, @reward, @effort, @confirmationprogress, @source, @hash, @created)"; con.Execute(query, mapped, tx); } diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index 213900590..b62a9f146 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -33,6 +33,7 @@ CREATE TABLE blocks miner TEXT NULL, reward decimal(28,12) NULL, source TEXT NULL, + hash TEXT NULL, created TIMESTAMP NOT NULL ); From fa1fbf5b4bee00538d96bccb7d4b7609f84d8710 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 6 Mar 2018 14:12:51 +0100 Subject: [PATCH 143/348] WIP --- src/MiningCore/Blockchain/Ethereum/EthereumJob.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs index 95d93b540..e6df3ac6b 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs @@ -110,7 +110,7 @@ public async Task ProcessShareAsync(StratumClient worker, string MixHash = mixDigest.ToHexString(true), IsBlockCandidate = isBlockCandidate, Difficulty = stratumDifficulty * EthereumConstants.Pow2x32, - BlockHash = mixDigest.ToHexString(true) // OW: is this correct? + BlockHash = mixDigest.ToHexString(true) // OW: is this correct? }; if (share.IsBlockCandidate) From 9ceb17b278403c2926020d663e8d934ce0a7f627 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 6 Mar 2018 15:04:40 +0100 Subject: [PATCH 144/348] Blockinfo API results --- src/MiningCore/Api/ApiServer.cs | 7 ++++ src/MiningCore/Blockchain/CoinMetaData.cs | 48 +++++++++++------------ 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/MiningCore/Api/ApiServer.cs b/src/MiningCore/Api/ApiServer.cs index adcb685b5..42a49faff 100644 --- a/src/MiningCore/Api/ApiServer.cs +++ b/src/MiningCore/Api/ApiServer.cs @@ -21,6 +21,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Data; +using System.Globalization; using System.IO; using System.Linq; using System.Net; @@ -361,7 +362,13 @@ private async Task PagePoolBlocksPagedAsync(HttpContext context, Match m) blockInfobaseDict.TryGetValue(!string.IsNullOrEmpty(block.Type) ? block.Type : string.Empty, out var blockInfobaseUrl); if (!string.IsNullOrEmpty(blockInfobaseUrl)) + { + blockInfobaseUrl = blockInfobaseUrl.Replace("{height}", block.BlockHeight.ToString(CultureInfo.InvariantCulture)); + if(!string.IsNullOrEmpty(block.Hash)) + blockInfobaseUrl = blockInfobaseUrl.Replace("{hash}", block.Hash); + block.InfoLink = string.Format(blockInfobaseUrl, block.BlockHeight); + } } } diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index 086ecc86d..937a4b90f 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -22,30 +22,30 @@ public static class CoinMetaData { EthereumConstants.BlockTypeUncle, "https://gastracker.io/uncle/{0}" } }}, - { CoinType.XMR, new Dictionary { { string.Empty, "https://chainradar.com/xmr/block/{0}" }}}, - { CoinType.ETN, new Dictionary { { string.Empty, "https://blockexplorer.electroneum.com/block/{0}" } }}, - { CoinType.LTC, new Dictionary { { string.Empty, "https://chainz.cryptoid.info/ltc/block.dws?{0}.htm" } }}, - { CoinType.BCH, new Dictionary { { string.Empty, "https://www.blocktrail.com/BCC/block/{0}" }}}, - { CoinType.DASH, new Dictionary { { string.Empty, "https://chainz.cryptoid.info/dash/block.dws?{0}.htm" }}}, - { CoinType.BTC, new Dictionary { { string.Empty, "https://blockchain.info/block/{0}" }}}, - { CoinType.DOGE, new Dictionary { { string.Empty, "https://dogechain.info/block/{0}" }}}, - { CoinType.ZEC, new Dictionary { { string.Empty, "https://explorer.zcha.in/blocks/{0}" }}}, - { CoinType.BTCP, new Dictionary { { string.Empty, "http://explorer.btcprivate.org/blocks/{0}" } }}, - { CoinType.ZCL, new Dictionary { { string.Empty, "http://explorer.zclmine.pro/blocks/{0}" }}}, - { CoinType.ZEN, new Dictionary { { string.Empty, "http://explorer.zensystem.io/blocks/{0}" } }}, - { CoinType.DGB, new Dictionary { { string.Empty, "https://digiexplorer.info/block/{0}" }}}, - { CoinType.NMC, new Dictionary { { string.Empty, "https://explorer.namecoin.info/b/{0}" }}}, - { CoinType.GRS, new Dictionary { { string.Empty, "https://groestlsight.groestlcoin.org/block/{0}" }}}, - { CoinType.MONA, new Dictionary { { string.Empty, "https://bchain.info/MONA/block/{0}" }}}, - { CoinType.GLT, new Dictionary { { string.Empty, "https://bchain.info/GLT/block/{0}" }}}, - { CoinType.VTC, new Dictionary { { string.Empty, "https://bchain.info/VTC/block/{0}" }}}, - { CoinType.BTG, new Dictionary { { string.Empty, "https://btg-bitcore2.trezor.io/block/{0}" }}}, - { CoinType.ELLA, new Dictionary { { string.Empty, "https://explorer.ellaism.org/block/{0}" }}}, - { CoinType.EXP, new Dictionary { { string.Empty, "http://www.gander.tech/blocks/{0}" }}}, - { CoinType.AEON, new Dictionary { { string.Empty, "https://chainradar.com/aeon/block/{0}" }}}, - { CoinType.STAK, new Dictionary { { string.Empty, "https://straks.info/block/{0}" }}}, - { CoinType.MOON, new Dictionary { { string.Empty, "https://chainz.cryptoid.info/moon/block.dws?{0}.htm" }}}, - { CoinType.XVG, new Dictionary { { string.Empty, "https://verge-blockchain.info/block/{0}" } }}, + { CoinType.XMR, new Dictionary { { string.Empty, "https://chainradar.com/xmr/block/{height}" }}}, + { CoinType.ETN, new Dictionary { { string.Empty, "https://blockexplorer.electroneum.com/block/{height}" } }}, + { CoinType.LTC, new Dictionary { { string.Empty, "https://chainz.cryptoid.info/ltc/block.dws?{height}.htm" } }}, + { CoinType.BCH, new Dictionary { { string.Empty, "https://www.blocktrail.com/BCC/block/{height}" }}}, + { CoinType.DASH, new Dictionary { { string.Empty, "https://chainz.cryptoid.info/dash/block.dws?{height}.htm" }}}, + { CoinType.BTC, new Dictionary { { string.Empty, "https://blockchain.info/block/{height}" }}}, + { CoinType.DOGE, new Dictionary { { string.Empty, "https://dogechain.info/block/{height}" }}}, + { CoinType.ZEC, new Dictionary { { string.Empty, "https://explorer.zcha.in/blocks/{hash}" } }}, + { CoinType.BTCP, new Dictionary { { string.Empty, "http://explorer.btcprivate.org/blocks/{hash}" } }}, + { CoinType.ZCL, new Dictionary { { string.Empty, "http://explorer.zclmine.pro/blocks/{height}" }}}, + { CoinType.ZEN, new Dictionary { { string.Empty, "http://explorer.zensystem.io/blocks/{hash}" } }}, + { CoinType.DGB, new Dictionary { { string.Empty, "https://digiexplorer.info/block/{height}" }}}, + { CoinType.NMC, new Dictionary { { string.Empty, "https://explorer.namecoin.info/b/{height}" }}}, + { CoinType.GRS, new Dictionary { { string.Empty, "https://groestlsight.groestlcoin.org/block/{height}" }}}, + { CoinType.MONA, new Dictionary { { string.Empty, "https://bchain.info/MONA/block/{height}" }}}, + { CoinType.GLT, new Dictionary { { string.Empty, "https://bchain.info/GLT/block/{height}" }}}, + { CoinType.VTC, new Dictionary { { string.Empty, "https://bchain.info/VTC/block/{height}" }}}, + { CoinType.BTG, new Dictionary { { string.Empty, "https://btg-bitcore2.trezor.io/block/{hash}" } }}, + { CoinType.ELLA, new Dictionary { { string.Empty, "https://explorer.ellaism.org/block/{height}" }}}, + { CoinType.EXP, new Dictionary { { string.Empty, "http://www.gander.tech/blocks/{height}" }}}, + { CoinType.AEON, new Dictionary { { string.Empty, "https://chainradar.com/aeon/block/{height}" }}}, + { CoinType.STAK, new Dictionary { { string.Empty, "https://straks.info/block/{height}" }}}, + { CoinType.MOON, new Dictionary { { string.Empty, "https://chainz.cryptoid.info/moon/block.dws?{height}.htm" }}}, + { CoinType.XVG, new Dictionary { { string.Empty, "https://verge-blockchain.info/block/{hash}" } }}, }; public static readonly Dictionary TxInfoLinks = new Dictionary From 4484736d83832bdddd5e85f9e1fd8be428744afb Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 6 Mar 2018 15:08:05 +0100 Subject: [PATCH 145/348] WIP --- src/MiningCore/Api/ApiServer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Api/ApiServer.cs b/src/MiningCore/Api/ApiServer.cs index 42a49faff..3b1a97dd2 100644 --- a/src/MiningCore/Api/ApiServer.cs +++ b/src/MiningCore/Api/ApiServer.cs @@ -367,7 +367,7 @@ private async Task PagePoolBlocksPagedAsync(HttpContext context, Match m) if(!string.IsNullOrEmpty(block.Hash)) blockInfobaseUrl = blockInfobaseUrl.Replace("{hash}", block.Hash); - block.InfoLink = string.Format(blockInfobaseUrl, block.BlockHeight); + block.InfoLink = blockInfobaseUrl; } } } From 479fb6d18d9c90c1a0713cca7f0165f5d1770efd Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 6 Mar 2018 15:15:39 +0100 Subject: [PATCH 146/348] WIP --- src/MiningCore/Api/ApiServer.cs | 9 ++-- src/MiningCore/Blockchain/CoinMetaData.cs | 51 ++++++++++++----------- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/MiningCore/Api/ApiServer.cs b/src/MiningCore/Api/ApiServer.cs index 3b1a97dd2..12f83b801 100644 --- a/src/MiningCore/Api/ApiServer.cs +++ b/src/MiningCore/Api/ApiServer.cs @@ -363,11 +363,10 @@ private async Task PagePoolBlocksPagedAsync(HttpContext context, Match m) if (!string.IsNullOrEmpty(blockInfobaseUrl)) { - blockInfobaseUrl = blockInfobaseUrl.Replace("{height}", block.BlockHeight.ToString(CultureInfo.InvariantCulture)); - if(!string.IsNullOrEmpty(block.Hash)) - blockInfobaseUrl = blockInfobaseUrl.Replace("{hash}", block.Hash); - - block.InfoLink = blockInfobaseUrl; + if(blockInfobaseUrl.Contains(CoinMetaData.BlockHeightPH)) + block.InfoLink = blockInfobaseUrl.Replace(CoinMetaData.BlockHeightPH, block.BlockHeight.ToString(CultureInfo.InvariantCulture)); + else if(blockInfobaseUrl.Contains(CoinMetaData.BlockHashPH) && !string.IsNullOrEmpty(block.Hash)) + block.InfoLink = blockInfobaseUrl.Replace(CoinMetaData.BlockHashPH, block.Hash); } } } diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index 937a4b90f..120a80b57 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -8,6 +8,9 @@ namespace MiningCore.Blockchain { public static class CoinMetaData { + public const string BlockHeightPH = "$height$"; + public const string BlockHashPH = "$hash$"; + public static readonly Dictionary> BlockInfoLinks = new Dictionary> { { CoinType.ETH, new Dictionary @@ -22,30 +25,30 @@ public static class CoinMetaData { EthereumConstants.BlockTypeUncle, "https://gastracker.io/uncle/{0}" } }}, - { CoinType.XMR, new Dictionary { { string.Empty, "https://chainradar.com/xmr/block/{height}" }}}, - { CoinType.ETN, new Dictionary { { string.Empty, "https://blockexplorer.electroneum.com/block/{height}" } }}, - { CoinType.LTC, new Dictionary { { string.Empty, "https://chainz.cryptoid.info/ltc/block.dws?{height}.htm" } }}, - { CoinType.BCH, new Dictionary { { string.Empty, "https://www.blocktrail.com/BCC/block/{height}" }}}, - { CoinType.DASH, new Dictionary { { string.Empty, "https://chainz.cryptoid.info/dash/block.dws?{height}.htm" }}}, - { CoinType.BTC, new Dictionary { { string.Empty, "https://blockchain.info/block/{height}" }}}, - { CoinType.DOGE, new Dictionary { { string.Empty, "https://dogechain.info/block/{height}" }}}, - { CoinType.ZEC, new Dictionary { { string.Empty, "https://explorer.zcha.in/blocks/{hash}" } }}, - { CoinType.BTCP, new Dictionary { { string.Empty, "http://explorer.btcprivate.org/blocks/{hash}" } }}, - { CoinType.ZCL, new Dictionary { { string.Empty, "http://explorer.zclmine.pro/blocks/{height}" }}}, - { CoinType.ZEN, new Dictionary { { string.Empty, "http://explorer.zensystem.io/blocks/{hash}" } }}, - { CoinType.DGB, new Dictionary { { string.Empty, "https://digiexplorer.info/block/{height}" }}}, - { CoinType.NMC, new Dictionary { { string.Empty, "https://explorer.namecoin.info/b/{height}" }}}, - { CoinType.GRS, new Dictionary { { string.Empty, "https://groestlsight.groestlcoin.org/block/{height}" }}}, - { CoinType.MONA, new Dictionary { { string.Empty, "https://bchain.info/MONA/block/{height}" }}}, - { CoinType.GLT, new Dictionary { { string.Empty, "https://bchain.info/GLT/block/{height}" }}}, - { CoinType.VTC, new Dictionary { { string.Empty, "https://bchain.info/VTC/block/{height}" }}}, - { CoinType.BTG, new Dictionary { { string.Empty, "https://btg-bitcore2.trezor.io/block/{hash}" } }}, - { CoinType.ELLA, new Dictionary { { string.Empty, "https://explorer.ellaism.org/block/{height}" }}}, - { CoinType.EXP, new Dictionary { { string.Empty, "http://www.gander.tech/blocks/{height}" }}}, - { CoinType.AEON, new Dictionary { { string.Empty, "https://chainradar.com/aeon/block/{height}" }}}, - { CoinType.STAK, new Dictionary { { string.Empty, "https://straks.info/block/{height}" }}}, - { CoinType.MOON, new Dictionary { { string.Empty, "https://chainz.cryptoid.info/moon/block.dws?{height}.htm" }}}, - { CoinType.XVG, new Dictionary { { string.Empty, "https://verge-blockchain.info/block/{hash}" } }}, + { CoinType.XMR, new Dictionary { { string.Empty, $"https://chainradar.com/xmr/block/{BlockHeightPH}" }}}, + { CoinType.ETN, new Dictionary { { string.Empty, $"https://blockexplorer.electroneum.com/block/{BlockHeightPH}" } }}, + { CoinType.LTC, new Dictionary { { string.Empty, $"https://chainz.cryptoid.info/ltc/block.dws?{BlockHeightPH}.htm" } }}, + { CoinType.BCH, new Dictionary { { string.Empty, $"https://www.blocktrail.com/BCC/block/{BlockHeightPH}" }}}, + { CoinType.DASH, new Dictionary { { string.Empty, $"https://chainz.cryptoid.info/dash/block.dws?{BlockHeightPH}.htm" }}}, + { CoinType.BTC, new Dictionary { { string.Empty, $"https://blockchain.info/block/{BlockHeightPH}" }}}, + { CoinType.DOGE, new Dictionary { { string.Empty, $"https://dogechain.info/block/{BlockHeightPH}" }}}, + { CoinType.ZEC, new Dictionary { { string.Empty, $"https://explorer.zcha.in/blocks/{BlockHashPH}" } }}, + { CoinType.BTCP, new Dictionary { { string.Empty, $"http://explorer.btcprivate.org/blocks/{BlockHashPH}" } }}, + { CoinType.ZCL, new Dictionary { { string.Empty, $"http://explorer.zclmine.pro/blocks/{BlockHeightPH}" }}}, + { CoinType.ZEN, new Dictionary { { string.Empty, $"http://explorer.zensystem.io/blocks/{BlockHashPH}" } }}, + { CoinType.DGB, new Dictionary { { string.Empty, $"https://digiexplorer.info/block/{BlockHeightPH}" }}}, + { CoinType.NMC, new Dictionary { { string.Empty, $"https://explorer.namecoin.info/b/{BlockHeightPH}" }}}, + { CoinType.GRS, new Dictionary { { string.Empty, $"https://groestlsight.groestlcoin.org/block/{BlockHeightPH}" }}}, + { CoinType.MONA, new Dictionary { { string.Empty, $"https://bchain.info/MONA/block/{BlockHeightPH}" }}}, + { CoinType.GLT, new Dictionary { { string.Empty, $"https://bchain.info/GLT/block/{BlockHeightPH}" }}}, + { CoinType.VTC, new Dictionary { { string.Empty, $"https://bchain.info/VTC/block/{BlockHeightPH}" }}}, + { CoinType.BTG, new Dictionary { { string.Empty, $"https://btg-bitcore2.trezor.io/block/{BlockHashPH}" } }}, + { CoinType.ELLA, new Dictionary { { string.Empty, $"https://explorer.ellaism.org/block/{BlockHeightPH}" }}}, + { CoinType.EXP, new Dictionary { { string.Empty, $"http://www.gander.tech/blocks/{BlockHeightPH}" }}}, + { CoinType.AEON, new Dictionary { { string.Empty, $"https://chainradar.com/aeon/block/{BlockHeightPH}" }}}, + { CoinType.STAK, new Dictionary { { string.Empty, $"https://straks.info/block/{BlockHeightPH}" }}}, + { CoinType.MOON, new Dictionary { { string.Empty, $"https://chainz.cryptoid.info/moon/block.dws?{BlockHeightPH}.htm" }}}, + { CoinType.XVG, new Dictionary { { string.Empty, $"https://verge-blockchain.info/block/{BlockHashPH}" } }}, }; public static readonly Dictionary TxInfoLinks = new Dictionary From cfb6ec970b669c966bca4858a5943926318973dc Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 6 Mar 2018 15:21:31 +0100 Subject: [PATCH 147/348] Fix block links --- src/MiningCore/Blockchain/CoinMetaData.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index 120a80b57..55d1898f2 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -33,9 +33,9 @@ public static class CoinMetaData { CoinType.BTC, new Dictionary { { string.Empty, $"https://blockchain.info/block/{BlockHeightPH}" }}}, { CoinType.DOGE, new Dictionary { { string.Empty, $"https://dogechain.info/block/{BlockHeightPH}" }}}, { CoinType.ZEC, new Dictionary { { string.Empty, $"https://explorer.zcha.in/blocks/{BlockHashPH}" } }}, - { CoinType.BTCP, new Dictionary { { string.Empty, $"http://explorer.btcprivate.org/blocks/{BlockHashPH}" } }}, - { CoinType.ZCL, new Dictionary { { string.Empty, $"http://explorer.zclmine.pro/blocks/{BlockHeightPH}" }}}, - { CoinType.ZEN, new Dictionary { { string.Empty, $"http://explorer.zensystem.io/blocks/{BlockHashPH}" } }}, + { CoinType.BTCP, new Dictionary { { string.Empty, $"http://explorer.btcprivate.org/block/{BlockHashPH}" } }}, + { CoinType.ZCL, new Dictionary { { string.Empty, $"http://explorer.zclmine.pro/block/{BlockHeightPH}" }}}, + { CoinType.ZEN, new Dictionary { { string.Empty, $"http://explorer.zensystem.io/block/{BlockHashPH}" } }}, { CoinType.DGB, new Dictionary { { string.Empty, $"https://digiexplorer.info/block/{BlockHeightPH}" }}}, { CoinType.NMC, new Dictionary { { string.Empty, $"https://explorer.namecoin.info/b/{BlockHeightPH}" }}}, { CoinType.GRS, new Dictionary { { string.Empty, $"https://groestlsight.groestlcoin.org/block/{BlockHeightPH}" }}}, From 03bc4a1e45e37026e59e440fd96ff07943498b2b Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 7 Mar 2018 09:30:15 +0100 Subject: [PATCH 148/348] Config changes --- src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs | 2 +- src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs | 2 +- src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs | 2 +- src/MiningCore/Blockchain/Ethereum/EthereumPool.cs | 2 +- src/MiningCore/Blockchain/Monero/MoneroJobManager.cs | 2 +- src/MiningCore/Blockchain/Monero/MoneroPool.cs | 2 +- src/MiningCore/Configuration/ClusterConfig.cs | 2 +- src/MiningCore/Configuration/ClusterConfigValidation.cs | 2 +- src/MiningCore/Mining/PoolBase.cs | 4 ++-- src/MiningCore/Program.cs | 4 ++-- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 03df41e41..71313b26b 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -100,7 +100,7 @@ public BitcoinJobManager( protected virtual void SetupJobUpdates() { - if (!poolConfig.EnableInternalStratum) + if (poolConfig.EnableInternalStratum == false) return; jobRebroadcastTimeout = TimeSpan.FromSeconds(Math.Max(1, poolConfig.JobRebroadcastTimeout)); diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs index 411dbc3aa..fa368b2c8 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs @@ -287,7 +287,7 @@ protected override async Task SetupJobManager() await manager.StartAsync(); - if (poolConfig.EnableInternalStratum) + if (poolConfig.EnableInternalStratum == true) { disposables.Add(manager.Jobs.Subscribe(OnNewJob)); diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs index d092bff2a..86044d553 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs @@ -600,7 +600,7 @@ private void ConfigureRewards() protected virtual void SetupJobUpdates() { - if (!poolConfig.EnableInternalStratum) + if (poolConfig.EnableInternalStratum == false) return; var enableStreaming = extraPoolConfig?.EnableDaemonWebsocketStreaming == true; diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs index 3cf5b5670..57a11c1e4 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs @@ -261,7 +261,7 @@ protected override async Task SetupJobManager() await manager.StartAsync(); - if (poolConfig.EnableInternalStratum) + if (poolConfig.EnableInternalStratum == true) { disposables.Add(manager.Jobs.Subscribe(OnNewJob)); diff --git a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs index 9dd4374c4..69e7ec02a 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs @@ -469,7 +469,7 @@ private void ConfigureRewards() protected virtual void SetupJobUpdates() { - if (!poolConfig.EnableInternalStratum) + if (poolConfig.EnableInternalStratum == false) return; // periodically update block-template from daemon diff --git a/src/MiningCore/Blockchain/Monero/MoneroPool.cs b/src/MiningCore/Blockchain/Monero/MoneroPool.cs index 319fcf865..4be9ade41 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPool.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPool.cs @@ -302,7 +302,7 @@ protected override async Task SetupJobManager() await manager.StartAsync(); - if (poolConfig.EnableInternalStratum) + if (poolConfig.EnableInternalStratum == true) { disposables.Add(manager.Blocks.Subscribe(_ => OnNewJob())); diff --git a/src/MiningCore/Configuration/ClusterConfig.cs b/src/MiningCore/Configuration/ClusterConfig.cs index 99d68530d..523dbaaf2 100644 --- a/src/MiningCore/Configuration/ClusterConfig.cs +++ b/src/MiningCore/Configuration/ClusterConfig.cs @@ -309,7 +309,7 @@ public partial class PoolConfig /// /// If true, internal stratum ports are not initialized /// - public bool EnableInternalStratum { get; set; } + public bool? EnableInternalStratum { get; set; } /// /// External stratums (ZMQ based share publishers) diff --git a/src/MiningCore/Configuration/ClusterConfigValidation.cs b/src/MiningCore/Configuration/ClusterConfigValidation.cs index 2ddf1e1e8..106cf62cf 100644 --- a/src/MiningCore/Configuration/ClusterConfigValidation.cs +++ b/src/MiningCore/Configuration/ClusterConfigValidation.cs @@ -192,7 +192,7 @@ public PoolConfigValidator() RuleFor(j => j.ExternalStratums) .NotNull() .NotEmpty() - .When(j=> !j.EnableInternalStratum) + .When(j=> j.EnableInternalStratum == false) .WithMessage("Pool: You must configure external stratum endpoints when enabling disabling internal stratum"); RuleFor(j => j.Daemons) diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index db5843aa7..58dd200ac 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -108,7 +108,7 @@ protected override void OnConnect(StratumClient client) var context = CreateClientContext(); var poolEndpoint = poolConfig.Ports[client.PoolEndpoint.Port]; - context.Init(poolConfig, poolEndpoint.Difficulty, poolConfig.EnableInternalStratum ? poolEndpoint.VarDiff : null, clock); + context.Init(poolConfig, poolEndpoint.Difficulty, poolConfig.EnableInternalStratum == true ? poolEndpoint.VarDiff : null, clock); client.SetContext(context); // varDiff setup @@ -433,7 +433,7 @@ public virtual async Task StartAsync() await SetupJobManager(); InitStats(); - if (poolConfig.EnableInternalStratum) + if (poolConfig.EnableInternalStratum == true) { var ipEndpoints = poolConfig.Ports.Keys .Select(port => PoolEndpoint2IPEndpoint(port, poolConfig.Ports[port])) diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index 5e16d3db1..0d2bc8677 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -169,8 +169,8 @@ private static void ValidateConfig() // set some defaults foreach (var config in clusterConfig.Pools) { - config.EnableInternalStratum = config.EnableInternalStratum || - config.ExternalStratums == null || config.ExternalStratums.Length == 0; + if (!config.EnableInternalStratum.HasValue) + config.EnableInternalStratum = config.ExternalStratums == null || config.ExternalStratums.Length == 0; } try From eb1c7b8774d29df9a25080afcdc8cc0e97d8f1a9 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 7 Mar 2018 09:39:54 +0100 Subject: [PATCH 149/348] WP --- .../Configuration/ClusterConfigValidation.cs | 11 +++-------- src/MiningCore/Mining/PoolBase.cs | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/MiningCore/Configuration/ClusterConfigValidation.cs b/src/MiningCore/Configuration/ClusterConfigValidation.cs index 106cf62cf..c8db2c091 100644 --- a/src/MiningCore/Configuration/ClusterConfigValidation.cs +++ b/src/MiningCore/Configuration/ClusterConfigValidation.cs @@ -160,12 +160,13 @@ public PoolConfigValidator() RuleFor(j => j.Ports) .NotNull() .NotEmpty() + .When(j=> j.EnableInternalStratum == true) .WithMessage("Pool: Stratum port config missing or empty"); RuleFor(j => j.Ports) .Must((pc, ports, ctx) => { - if (ports.Keys.Any(port => port < 0)) + if (ports?.Keys.Any(port => port < 0) == true) { ctx.MessageFormatter.AppendArgument("port", ports.Keys.First(port => port < 0)); return false; @@ -189,12 +190,6 @@ public PoolConfigValidator() .NotEmpty() .WithMessage("Pool: Daemons missing or empty"); - RuleFor(j => j.ExternalStratums) - .NotNull() - .NotEmpty() - .When(j=> j.EnableInternalStratum == false) - .WithMessage("Pool: You must configure external stratum endpoints when enabling disabling internal stratum"); - RuleFor(j => j.Daemons) .SetCollectionValidator(new AuthenticatedNetworkEndpointConfigValidator()); } @@ -236,7 +231,7 @@ public ClusterConfigValidator() RuleFor(j => j.Pools) .Must((pc, pools, ctx) => { - var ports = pools.SelectMany(x => x.Ports.Select(y => y.Key)) + var ports = pools.Where(x=> x.Ports?.Any() == true).SelectMany(x => x.Ports.Select(y => y.Key)) .GroupBy(x => x) .ToArray(); diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index 58dd200ac..2342b47e4 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -395,7 +395,7 @@ private void OutputPoolInfo() Current Connect Peers: {blockchainStats.ConnectedPeers} Network Difficulty: {blockchainStats.NetworkDifficulty} Network Hash Rate: {FormatUtil.FormatHashrate(blockchainStats.NetworkHashrate)} -Stratum Port(s): {string.Join(", ", poolConfig.Ports.Keys)} +Stratum Port(s): {(poolConfig.Ports?.Any() == true ? string.Join(", ", poolConfig.Ports.Keys) : string.Empty )} Pool Fee: {(poolConfig.RewardRecipients?.Any() == true ? poolConfig.RewardRecipients.Sum(x => x.Percentage) : 0)}% "; From 3804016b5d12a35371a4ae3b51585abcb94615eb Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 8 Mar 2018 11:41:14 +0100 Subject: [PATCH 150/348] Increase write throuput --- src/MiningCore/Payments/ShareRecorder.cs | 2 +- src/MiningCore/Persistence/Postgres/Scripts/createdb.sql | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MiningCore/Payments/ShareRecorder.cs b/src/MiningCore/Payments/ShareRecorder.cs index 56c5c5cbf..26257ce2c 100644 --- a/src/MiningCore/Payments/ShareRecorder.cs +++ b/src/MiningCore/Payments/ShareRecorder.cs @@ -323,7 +323,7 @@ private void InitializeQueue() queueSub = queue.GetConsumingEnumerable() .ToObservable(TaskPoolScheduler.Default) .Do(_ => CheckQueueBacklog()) - .Buffer(TimeSpan.FromSeconds(1), 20) + .Buffer(TimeSpan.FromSeconds(1), 100) .Where(shares => shares.Any()) .Subscribe(shares => { diff --git a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql index b62a9f146..6485eb5d9 100644 --- a/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql +++ b/src/MiningCore/Persistence/Postgres/Scripts/createdb.sql @@ -83,6 +83,7 @@ CREATE TABLE poolstats poolid TEXT NOT NULL, connectedminers INT NOT NULL DEFAULT 0, poolhashrate DOUBLE PRECISION NOT NULL DEFAULT 0, + sharespersecond DOUBLE PRECISION NOT NULL DEFAULT 0, networkhashrate DOUBLE PRECISION NOT NULL DEFAULT 0, networkdifficulty DOUBLE PRECISION NOT NULL DEFAULT 0, lastnetworkblocktime TIMESTAMP NULL, From fa9a4e8b5cb62476a57b93f792552944d8254ad3 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 8 Mar 2018 15:32:14 +0100 Subject: [PATCH 151/348] Monero double transfer RPC failure response handling --- .../Blockchain/Monero/MoneroPayoutHandler.cs | 48 ++----------------- 1 file changed, 3 insertions(+), 45 deletions(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index 6fdd55325..6b16488ab 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -171,54 +171,12 @@ private async Task PayoutBatch(Balance[] balances) var transferSplitResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.TransferSplit, request); - // gracefully handle error -4 (transaction would be too large. try /transfer_split) - if (transferResponse.Error?.Code != -4) - { - HandleTransferResponse(transferSplitResponse, balances); - return; - } - } - - // retry paged - logger.Info(() => $"[{LogCategory}] Retrying paged"); - - var validBalances = balances.Where(x => x.Amount > 0).ToArray(); - var pageSize = 10; - var pageCount = (int)Math.Ceiling((double)validBalances.Length / pageSize); - - for (var i = 0; i < pageCount; i++) - { - var page = validBalances - .Skip(i * pageSize) - .Take(pageSize) - .ToArray(); - - // update request - request.Destinations = page - .Where(x => x.Amount > 0) - .Select(x => - { - ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId); - - return new TransferDestination - { - Address = address, - Amount = (ulong)Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type]) - }; - }).ToArray(); - - logger.Info(() => $"[{LogCategory}] Page {i + 1}: Paying out {FormatAmount(page.Sum(x => x.Amount))} to {page.Length} addresses"); - - transferResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.Transfer, request); - HandleTransferResponse(transferResponse, page); - - if (transferResponse.Error != null) - break; + HandleTransferResponse(transferSplitResponse, balances); + return; } } - else - HandleTransferResponse(transferResponse, balances); + HandleTransferResponse(transferResponse, balances); } private void ExtractAddressAndPaymentId(string input, out string address, out string paymentId) From 191c6ed5a2a5b2942b881ea1a278373dbf8e3ed7 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 9 Mar 2018 09:25:35 +0100 Subject: [PATCH 152/348] Fix ethereum explorer links --- src/MiningCore/Blockchain/CoinMetaData.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index 55d1898f2..434f3fb01 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -16,13 +16,13 @@ public static class CoinMetaData { CoinType.ETH, new Dictionary { { string.Empty, "https://etherscan.io/block/{0}" }, - { EthereumConstants.BlockTypeUncle, "https://etherscan.io/uncle/{0}" }, + { EthereumConstants.BlockTypeUncle, "https://etherscan.io/uncle/{BlockHeightPH}" }, }}, { CoinType.ETC, new Dictionary { { string.Empty, "https://gastracker.io/block/{0}" }, - { EthereumConstants.BlockTypeUncle, "https://gastracker.io/uncle/{0}" } + { EthereumConstants.BlockTypeUncle, "https://gastracker.io/uncle/{BlockHeightPH}" } }}, { CoinType.XMR, new Dictionary { { string.Empty, $"https://chainradar.com/xmr/block/{BlockHeightPH}" }}}, From a77b200055a318d3e8da0175184ba7c24d17a192 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 9 Mar 2018 09:30:08 +0100 Subject: [PATCH 153/348] WIP --- src/MiningCore/Blockchain/CoinMetaData.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index 434f3fb01..be010cef4 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -16,13 +16,13 @@ public static class CoinMetaData { CoinType.ETH, new Dictionary { { string.Empty, "https://etherscan.io/block/{0}" }, - { EthereumConstants.BlockTypeUncle, "https://etherscan.io/uncle/{BlockHeightPH}" }, + { EthereumConstants.BlockTypeUncle, $"https://etherscan.io/uncle/{BlockHeightPH}" }, }}, { CoinType.ETC, new Dictionary { { string.Empty, "https://gastracker.io/block/{0}" }, - { EthereumConstants.BlockTypeUncle, "https://gastracker.io/uncle/{BlockHeightPH}" } + { EthereumConstants.BlockTypeUncle, $"https://gastracker.io/uncle/{BlockHeightPH}" } }}, { CoinType.XMR, new Dictionary { { string.Empty, $"https://chainradar.com/xmr/block/{BlockHeightPH}" }}}, From dd84c5f21262ed19b555f77d16a41ccabe7b1c01 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 9 Mar 2018 09:32:23 +0100 Subject: [PATCH 154/348] WIP --- src/MiningCore/Blockchain/CoinMetaData.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index be010cef4..f97494bbd 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -15,13 +15,13 @@ public static class CoinMetaData { { CoinType.ETH, new Dictionary { - { string.Empty, "https://etherscan.io/block/{0}" }, + { string.Empty, $"https://etherscan.io/block/{BlockHeightPH}" }, { EthereumConstants.BlockTypeUncle, $"https://etherscan.io/uncle/{BlockHeightPH}" }, }}, { CoinType.ETC, new Dictionary { - { string.Empty, "https://gastracker.io/block/{0}" }, + { string.Empty, $"https://gastracker.io/block/{BlockHeightPH}" }, { EthereumConstants.BlockTypeUncle, $"https://gastracker.io/uncle/{BlockHeightPH}" } }}, From d20abf356458722e99800e70e9db980f278a8ad6 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 10 Mar 2018 10:08:10 +0100 Subject: [PATCH 155/348] Added logging --- src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs | 2 +- src/MiningCore/Stratum/StratumClient.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index 6b16488ab..0088e2f95 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -167,7 +167,7 @@ private async Task PayoutBatch(Balance[] balances) { if (walletSupportsTransferSplit) { - logger.Info(() => $"[{LogCategory}] Retrying transfer using {MWC.TransferSplit}"); + logger.Info(() => $"[{LogCategory}] {MWC.Transfer} failed with {transferResponse.Error?.Code} [{transferResponse.Error?.Message}] - retrying transfer using {MWC.TransferSplit}"); var transferSplitResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.TransferSplit, request); diff --git a/src/MiningCore/Stratum/StratumClient.cs b/src/MiningCore/Stratum/StratumClient.cs index e78d6922f..394374a71 100644 --- a/src/MiningCore/Stratum/StratumClient.cs +++ b/src/MiningCore/Stratum/StratumClient.cs @@ -235,7 +235,7 @@ public JsonRpcRequest DeserializeRequest(PooledArraySegment data) #endregion // API-Surface - private void Receive(Tcp tcp, IMasterClock clock, + private void Receive(Tcp tcp, IMasterClock clock, Action> onNext, Action onCompleted, Action onError) { tcp.OnRead((handle, buffer) => From 5361a4417866365dd040b523c4166193a51816df Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 10 Mar 2018 10:11:22 +0100 Subject: [PATCH 156/348] Logging --- src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index 0088e2f95..0f3390111 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -167,7 +167,8 @@ private async Task PayoutBatch(Balance[] balances) { if (walletSupportsTransferSplit) { - logger.Info(() => $"[{LogCategory}] {MWC.Transfer} failed with {transferResponse.Error?.Code} [{transferResponse.Error?.Message}] - retrying transfer using {MWC.TransferSplit}"); + logger.Error(() => $"[{LogCategory}] Daemon command '{MWC.Transfer}' returned error: {transferResponse.Error.Message} code {transferResponse.Error.Code}"); + logger.Info(() => $"[{LogCategory}] Retrying transfer using {MWC.TransferSplit}"); var transferSplitResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.TransferSplit, request); From 5020e2fc4a270d6eee63d9a20e4b2bb7a4d96e62 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 10 Mar 2018 12:36:32 +0100 Subject: [PATCH 157/348] WIP --- src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs index 86044d553..e74ec4f40 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs @@ -189,7 +189,7 @@ private async Task GetBlockTemplateAsync() private EthereumBlockTemplate AssembleBlockTemplate(Block block, string[] work) { // only parity returns the 4th element (block height) - if (work.Length < 3) + if (work.Length < 4) { logger.Error(() => $"[{LogCat}] Error(s) refreshing blocktemplate: getWork did not return blockheight. Are you really connected to a Parity daemon?"); return null; From 809928a79020e942b2e74892e76cdb35a4661db5 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 10 Mar 2018 12:46:03 +0100 Subject: [PATCH 158/348] WIP --- .../Blockchain/Monero/MoneroPayoutHandler.cs | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index 0f3390111..7582eef3e 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -172,8 +172,48 @@ private async Task PayoutBatch(Balance[] balances) var transferSplitResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.TransferSplit, request); - HandleTransferResponse(transferSplitResponse, balances); - return; + if (transferResponse.Error == null) + { + HandleTransferResponse(transferSplitResponse, balances); + return; + } + } + + // retry paged - unfortunately this is necessary for cases like this: https://github.com/monero-project/monero/issues/3379 + logger.Info(() => $"[{LogCategory}] Retrying paged"); + + var validBalances = balances.Where(x => x.Amount > 0).ToArray(); + var pageSize = 10; + var pageCount = (int)Math.Ceiling((double)validBalances.Length / pageSize); + + for (var i = 0; i < pageCount; i++) + { + var page = validBalances + .Skip(i * pageSize) + .Take(pageSize) + .ToArray(); + + // update request + request.Destinations = page + .Where(x => x.Amount > 0) + .Select(x => + { + ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId); + + return new TransferDestination + { + Address = address, + Amount = (ulong)Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type]) + }; + }).ToArray(); + + logger.Info(() => $"[{LogCategory}] Page {i + 1}: Paying out {FormatAmount(page.Sum(x => x.Amount))} to {page.Length} addresses"); + + transferResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.Transfer, request); + HandleTransferResponse(transferResponse, balances); + + if (transferResponse.Error != null) + return; } } From 8fd6d0ebf1bcfcea7f55113f6e33da82dd221947 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 10 Mar 2018 12:52:49 +0100 Subject: [PATCH 159/348] WIP --- src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index 7582eef3e..427e4fc7b 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -177,6 +177,8 @@ private async Task PayoutBatch(Balance[] balances) HandleTransferResponse(transferSplitResponse, balances); return; } + + logger.Error(() => $"[{LogCategory}] Daemon command '{MWC.TransferSplit}' returned error: {transferSplitResponse.Error.Message} code {transferSplitResponse.Error.Code}"); } // retry paged - unfortunately this is necessary for cases like this: https://github.com/monero-project/monero/issues/3379 From c1bd1cc4c4a2a609587a2158dc416177022adf16 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Sat, 10 Mar 2018 13:55:08 +0100 Subject: [PATCH 160/348] Revert "WIP" This reverts commit 809928a79020e942b2e74892e76cdb35a4661db5. --- .../Blockchain/Monero/MoneroPayoutHandler.cs | 46 +------------------ 1 file changed, 2 insertions(+), 44 deletions(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index 427e4fc7b..0f3390111 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -172,50 +172,8 @@ private async Task PayoutBatch(Balance[] balances) var transferSplitResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.TransferSplit, request); - if (transferResponse.Error == null) - { - HandleTransferResponse(transferSplitResponse, balances); - return; - } - - logger.Error(() => $"[{LogCategory}] Daemon command '{MWC.TransferSplit}' returned error: {transferSplitResponse.Error.Message} code {transferSplitResponse.Error.Code}"); - } - - // retry paged - unfortunately this is necessary for cases like this: https://github.com/monero-project/monero/issues/3379 - logger.Info(() => $"[{LogCategory}] Retrying paged"); - - var validBalances = balances.Where(x => x.Amount > 0).ToArray(); - var pageSize = 10; - var pageCount = (int)Math.Ceiling((double)validBalances.Length / pageSize); - - for (var i = 0; i < pageCount; i++) - { - var page = validBalances - .Skip(i * pageSize) - .Take(pageSize) - .ToArray(); - - // update request - request.Destinations = page - .Where(x => x.Amount > 0) - .Select(x => - { - ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId); - - return new TransferDestination - { - Address = address, - Amount = (ulong)Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type]) - }; - }).ToArray(); - - logger.Info(() => $"[{LogCategory}] Page {i + 1}: Paying out {FormatAmount(page.Sum(x => x.Amount))} to {page.Length} addresses"); - - transferResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.Transfer, request); - HandleTransferResponse(transferResponse, balances); - - if (transferResponse.Error != null) - return; + HandleTransferResponse(transferSplitResponse, balances); + return; } } From e02c961cd042410fea0210b93b3f696a579c394f Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Mon, 12 Mar 2018 23:08:36 +0100 Subject: [PATCH 161/348] WIP --- .../Blockchain/Bitcoin/BitcoinProperties.cs | 5 +- src/MiningCore/Blockchain/CoinMetaData.cs | 304 +++++++++--------- 2 files changed, 157 insertions(+), 152 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index 40e4a661d..d6072df5c 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -97,7 +97,7 @@ public class BitcoinProperties { CoinType.BCH, sha256Coin }, { CoinType.NMC, sha256Coin }, { CoinType.PPC, sha256Coin }, - { CoinType.GLT, sha256Coin }, + { CoinType.GLT, sha256Coin }, // Scrypt { CoinType.LTC, scryptCoin }, @@ -145,9 +145,10 @@ private static BitcoinCoinProperties GetDigiByteProperties(string algorithm) { Contract.Requires(!string.IsNullOrEmpty(algorithm), $"{nameof(algorithm)} must not be empty"); - switch(algorithm.ToLower()) + switch (algorithm.ToLower()) { case "sha256d": + case "sha256": return sha256Coin; case "skein": diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index f97494bbd..76b24bd92 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -1,150 +1,154 @@ -using System; -using System.Collections.Generic; -using MiningCore.Blockchain.Bitcoin; -using MiningCore.Blockchain.Ethereum; -using MiningCore.Configuration; - -namespace MiningCore.Blockchain -{ - public static class CoinMetaData - { - public const string BlockHeightPH = "$height$"; - public const string BlockHashPH = "$hash$"; - - public static readonly Dictionary> BlockInfoLinks = new Dictionary> - { - { CoinType.ETH, new Dictionary - { - { string.Empty, $"https://etherscan.io/block/{BlockHeightPH}" }, - { EthereumConstants.BlockTypeUncle, $"https://etherscan.io/uncle/{BlockHeightPH}" }, - }}, - - { CoinType.ETC, new Dictionary - { - { string.Empty, $"https://gastracker.io/block/{BlockHeightPH}" }, - { EthereumConstants.BlockTypeUncle, $"https://gastracker.io/uncle/{BlockHeightPH}" } - }}, - - { CoinType.XMR, new Dictionary { { string.Empty, $"https://chainradar.com/xmr/block/{BlockHeightPH}" }}}, - { CoinType.ETN, new Dictionary { { string.Empty, $"https://blockexplorer.electroneum.com/block/{BlockHeightPH}" } }}, - { CoinType.LTC, new Dictionary { { string.Empty, $"https://chainz.cryptoid.info/ltc/block.dws?{BlockHeightPH}.htm" } }}, - { CoinType.BCH, new Dictionary { { string.Empty, $"https://www.blocktrail.com/BCC/block/{BlockHeightPH}" }}}, - { CoinType.DASH, new Dictionary { { string.Empty, $"https://chainz.cryptoid.info/dash/block.dws?{BlockHeightPH}.htm" }}}, - { CoinType.BTC, new Dictionary { { string.Empty, $"https://blockchain.info/block/{BlockHeightPH}" }}}, - { CoinType.DOGE, new Dictionary { { string.Empty, $"https://dogechain.info/block/{BlockHeightPH}" }}}, - { CoinType.ZEC, new Dictionary { { string.Empty, $"https://explorer.zcha.in/blocks/{BlockHashPH}" } }}, - { CoinType.BTCP, new Dictionary { { string.Empty, $"http://explorer.btcprivate.org/block/{BlockHashPH}" } }}, - { CoinType.ZCL, new Dictionary { { string.Empty, $"http://explorer.zclmine.pro/block/{BlockHeightPH}" }}}, - { CoinType.ZEN, new Dictionary { { string.Empty, $"http://explorer.zensystem.io/block/{BlockHashPH}" } }}, - { CoinType.DGB, new Dictionary { { string.Empty, $"https://digiexplorer.info/block/{BlockHeightPH}" }}}, - { CoinType.NMC, new Dictionary { { string.Empty, $"https://explorer.namecoin.info/b/{BlockHeightPH}" }}}, - { CoinType.GRS, new Dictionary { { string.Empty, $"https://groestlsight.groestlcoin.org/block/{BlockHeightPH}" }}}, - { CoinType.MONA, new Dictionary { { string.Empty, $"https://bchain.info/MONA/block/{BlockHeightPH}" }}}, - { CoinType.GLT, new Dictionary { { string.Empty, $"https://bchain.info/GLT/block/{BlockHeightPH}" }}}, - { CoinType.VTC, new Dictionary { { string.Empty, $"https://bchain.info/VTC/block/{BlockHeightPH}" }}}, - { CoinType.BTG, new Dictionary { { string.Empty, $"https://btg-bitcore2.trezor.io/block/{BlockHashPH}" } }}, - { CoinType.ELLA, new Dictionary { { string.Empty, $"https://explorer.ellaism.org/block/{BlockHeightPH}" }}}, - { CoinType.EXP, new Dictionary { { string.Empty, $"http://www.gander.tech/blocks/{BlockHeightPH}" }}}, - { CoinType.AEON, new Dictionary { { string.Empty, $"https://chainradar.com/aeon/block/{BlockHeightPH}" }}}, - { CoinType.STAK, new Dictionary { { string.Empty, $"https://straks.info/block/{BlockHeightPH}" }}}, - { CoinType.MOON, new Dictionary { { string.Empty, $"https://chainz.cryptoid.info/moon/block.dws?{BlockHeightPH}.htm" }}}, - { CoinType.XVG, new Dictionary { { string.Empty, $"https://verge-blockchain.info/block/{BlockHashPH}" } }}, - }; - - public static readonly Dictionary TxInfoLinks = new Dictionary - { - { CoinType.XMR, "https://chainradar.com/xmr/transaction/{0}" }, - { CoinType.ETN, "https://blockexplorer.electroneum.com/tx/{0}" }, - { CoinType.ETH, "https://etherscan.io/tx/{0}" }, - { CoinType.ETC, "https://gastracker.io/tx/{0}" }, - { CoinType.LTC, "https://chainz.cryptoid.info/ltc/tx.dws?{0}.htm" }, - { CoinType.BCH, "https://www.blocktrail.com/BCC/tx/{0}" }, - { CoinType.DASH, "https://chainz.cryptoid.info/dash/tx.dws?{0}.htm" }, - { CoinType.BTC, "https://blockchain.info/tx/{0}" }, - { CoinType.DOGE, "https://dogechain.info/tx/{0}" }, - { CoinType.ZEC, "https://explorer.zcha.in/transactions/{0}" }, - { CoinType.ZCL, "http://explorer.zclmine.pro/transactions/{0}" }, - { CoinType.ZEN, "http://explorer.zensystem.io/transactions/{0}" }, - { CoinType.BTCP, "https://explorer.btcprivate.org/transactions/{0}" }, - { CoinType.DGB, "https://digiexplorer.info/tx/{0}" }, - { CoinType.NMC, "https://explorer.namecoin.info/tx/{0}" }, - { CoinType.GRS, "https://groestlsight.groestlcoin.org/tx/{0}" }, - { CoinType.MONA, "https://bchain.info/MONA/tx/{0}" }, - { CoinType.STAK, "https://straks.info/transaction/{0}" }, - { CoinType.GLT, "https://bchain.info/GLT/tx/{0}" }, - { CoinType.VTC, "https://bchain.info/VTC/tx/{0}" }, - { CoinType.BTG, "https://btgexp.com/tx/{0}" }, - { CoinType.ELLA, "https://explorer.ellaism.org/tx/{0}" }, - { CoinType.EXP, "http://www.gander.tech/tx/{0}" }, - { CoinType.AEON, "https://chainradar.com/aeon/transaction/{0}" }, - { CoinType.MOON, "https://chainz.cryptoid.info/moon/tx.dws?{0}.htm" }, - { CoinType.XVG, "https://verge-blockchain.info/tx/{0}" }, - { CoinType.GBX, "http://gobyte.ezmine.io/tx/{0}" }, - { CoinType.CRC, "http://explorer.cryptopros.us/tx/{0}" }, - }; - - public static readonly Dictionary AddressInfoLinks = new Dictionary - { - { CoinType.ETH, "https://etherscan.io/address/{0}" }, - { CoinType.ETC, "https://gastracker.io/addr/{0}" }, - { CoinType.LTC, "https://chainz.cryptoid.info/ltc/address.dws?{0}.htm" }, - { CoinType.BCH, "https://www.blocktrail.com/BCC/address/{0}" }, - { CoinType.DASH, "https://chainz.cryptoid.info/dash/address.dws?{0}.htm" }, - { CoinType.BTC, "https://blockchain.info/address/{0}" }, - { CoinType.DOGE, "https://dogechain.info/address/{0}" }, - { CoinType.ZEC, "https://explorer.zcha.in/accounts/{0}" }, - { CoinType.ZCL, "http://explorer.zclmine.pro/accounts/{0}" }, - { CoinType.ZEN, "http://explorer.zensystem.io/accounts/{0}" }, - { CoinType.DGB, "https://digiexplorer.info/address/{0}" }, - { CoinType.NMC, "https://explorer.namecoin.info/a/{0}" }, - { CoinType.GRS, "https://groestlsight.groestlcoin.org/address/{0}" }, - { CoinType.MONA, "https://bchain.info/MONA/addr/{0}" }, - { CoinType.STAK, "https://straks.info/address/{0}" }, - { CoinType.GLT, "https://bchain.info/GLT/addr/{0}" }, - { CoinType.VTC, "https://bchain.info/VTC/addr/{0}" }, - { CoinType.BTG, "https://btgexp.com/address/{0}" }, - { CoinType.ELLA, "https://explorer.ellaism.org/addr/{0}" }, - { CoinType.EXP, "http://www.gander.tech/address/{0}" }, - { CoinType.MOON, "https://chainz.cryptoid.info/moon/address.dws?{0}.htm" }, - { CoinType.XVG, "https://verge-blockchain.info/address/{0}" }, - { CoinType.GBX, "http://gobyte.ezmine.io/address/{0}" }, - { CoinType.CRC, "http://explorer.cryptopros.us/address/{0}" }, - }; - - private const string Ethash = "Dagger-Hashimoto"; - private const string Cryptonight = "Cryptonight"; - private const string CryptonightLight = "Cryptonight-Light"; - - public static readonly Dictionary> CoinAlgorithm = new Dictionary> - { - { CoinType.ETH, (coin, alg)=> Ethash }, - { CoinType.ETC, (coin, alg)=> Ethash }, - { CoinType.LTC, BitcoinProperties.GetAlgorithm }, - { CoinType.BCH, BitcoinProperties.GetAlgorithm }, - { CoinType.DASH, BitcoinProperties.GetAlgorithm }, - { CoinType.BTC, BitcoinProperties.GetAlgorithm }, - { CoinType.DOGE, BitcoinProperties.GetAlgorithm }, - { CoinType.ZEC, BitcoinProperties.GetAlgorithm }, - { CoinType.ZCL, BitcoinProperties.GetAlgorithm }, - { CoinType.BTCP, BitcoinProperties.GetAlgorithm }, - { CoinType.ZEN, BitcoinProperties.GetAlgorithm }, - { CoinType.DGB, BitcoinProperties.GetAlgorithm }, - { CoinType.NMC, BitcoinProperties.GetAlgorithm }, - { CoinType.GRS, BitcoinProperties.GetAlgorithm }, - { CoinType.MONA, BitcoinProperties.GetAlgorithm }, - { CoinType.STAK, BitcoinProperties.GetAlgorithm }, - { CoinType.GLT, BitcoinProperties.GetAlgorithm }, - { CoinType.VTC, BitcoinProperties.GetAlgorithm }, - { CoinType.BTG, BitcoinProperties.GetAlgorithm }, - { CoinType.ELLA, (coin, alg)=> Ethash }, - { CoinType.EXP, (coin, alg)=> Ethash }, - { CoinType.MOON, BitcoinProperties.GetAlgorithm }, - { CoinType.XVG, BitcoinProperties.GetAlgorithm }, - { CoinType.XMR, (coin, alg)=> Cryptonight }, - { CoinType.ETN, (coin, alg)=> Cryptonight }, - { CoinType.AEON, (coin, alg)=> CryptonightLight }, - { CoinType.GBX, BitcoinProperties.GetAlgorithm }, - { CoinType.CRC, BitcoinProperties.GetAlgorithm }, - }; - } -} +using System; +using System.Collections.Generic; +using MiningCore.Blockchain.Bitcoin; +using MiningCore.Blockchain.Ethereum; +using MiningCore.Configuration; + +namespace MiningCore.Blockchain +{ + public static class CoinMetaData + { + public const string BlockHeightPH = "$height$"; + public const string BlockHashPH = "$hash$"; + + public static readonly Dictionary> BlockInfoLinks = new Dictionary> + { + { CoinType.ETH, new Dictionary + { + { string.Empty, $"https://etherscan.io/block/{BlockHeightPH}" }, + { EthereumConstants.BlockTypeUncle, $"https://etherscan.io/uncle/{BlockHeightPH}" }, + }}, + + { CoinType.ETC, new Dictionary + { + { string.Empty, $"https://gastracker.io/block/{BlockHeightPH}" }, + { EthereumConstants.BlockTypeUncle, $"https://gastracker.io/uncle/{BlockHeightPH}" } + }}, + + { CoinType.XMR, new Dictionary { { string.Empty, $"https://chainradar.com/xmr/block/{BlockHeightPH}" }}}, + { CoinType.ETN, new Dictionary { { string.Empty, $"https://blockexplorer.electroneum.com/block/{BlockHeightPH}" } }}, + { CoinType.LTC, new Dictionary { { string.Empty, $"https://chainz.cryptoid.info/ltc/block.dws?{BlockHeightPH}.htm" } }}, + { CoinType.PPC, new Dictionary { { string.Empty, $"https://chainz.cryptoid.info/ppc/block.dws?{BlockHeightPH}.htm" } }}, + { CoinType.BCH, new Dictionary { { string.Empty, $"https://www.blocktrail.com/BCC/block/{BlockHeightPH}" }}}, + { CoinType.DASH, new Dictionary { { string.Empty, $"https://chainz.cryptoid.info/dash/block.dws?{BlockHeightPH}.htm" }}}, + { CoinType.BTC, new Dictionary { { string.Empty, $"https://blockchain.info/block/{BlockHeightPH}" }}}, + { CoinType.DOGE, new Dictionary { { string.Empty, $"https://dogechain.info/block/{BlockHeightPH}" }}}, + { CoinType.ZEC, new Dictionary { { string.Empty, $"https://explorer.zcha.in/blocks/{BlockHashPH}" } }}, + { CoinType.BTCP, new Dictionary { { string.Empty, $"http://explorer.btcprivate.org/block/{BlockHashPH}" } }}, + { CoinType.ZCL, new Dictionary { { string.Empty, $"http://explorer.zclmine.pro/block/{BlockHeightPH}" }}}, + { CoinType.ZEN, new Dictionary { { string.Empty, $"http://explorer.zensystem.io/block/{BlockHashPH}" } }}, + { CoinType.DGB, new Dictionary { { string.Empty, $"https://digiexplorer.info/block/{BlockHeightPH}" }}}, + { CoinType.NMC, new Dictionary { { string.Empty, $"https://explorer.namecoin.info/b/{BlockHeightPH}" }}}, + { CoinType.GRS, new Dictionary { { string.Empty, $"https://groestlsight.groestlcoin.org/block/{BlockHeightPH}" }}}, + { CoinType.MONA, new Dictionary { { string.Empty, $"https://bchain.info/MONA/block/{BlockHeightPH}" }}}, + { CoinType.GLT, new Dictionary { { string.Empty, $"https://bchain.info/GLT/block/{BlockHeightPH}" }}}, + { CoinType.VTC, new Dictionary { { string.Empty, $"https://bchain.info/VTC/block/{BlockHeightPH}" }}}, + { CoinType.BTG, new Dictionary { { string.Empty, $"https://btg-bitcore2.trezor.io/block/{BlockHashPH}" } }}, + { CoinType.ELLA, new Dictionary { { string.Empty, $"https://explorer.ellaism.org/block/{BlockHeightPH}" }}}, + { CoinType.EXP, new Dictionary { { string.Empty, $"http://www.gander.tech/blocks/{BlockHeightPH}" }}}, + { CoinType.AEON, new Dictionary { { string.Empty, $"https://chainradar.com/aeon/block/{BlockHeightPH}" }}}, + { CoinType.STAK, new Dictionary { { string.Empty, $"https://straks.info/block/{BlockHeightPH}" }}}, + { CoinType.MOON, new Dictionary { { string.Empty, $"https://chainz.cryptoid.info/moon/block.dws?{BlockHeightPH}.htm" }}}, + { CoinType.XVG, new Dictionary { { string.Empty, $"https://verge-blockchain.info/block/{BlockHashPH}" } }}, + }; + + public static readonly Dictionary TxInfoLinks = new Dictionary + { + { CoinType.XMR, "https://chainradar.com/xmr/transaction/{0}" }, + { CoinType.ETN, "https://blockexplorer.electroneum.com/tx/{0}" }, + { CoinType.ETH, "https://etherscan.io/tx/{0}" }, + { CoinType.ETC, "https://gastracker.io/tx/{0}" }, + { CoinType.LTC, "https://chainz.cryptoid.info/ltc/tx.dws?{0}.htm" }, + { CoinType.PPC, "https://chainz.cryptoid.info/ppc/tx.dws?{0}.htm" }, + { CoinType.BCH, "https://www.blocktrail.com/BCC/tx/{0}" }, + { CoinType.DASH, "https://chainz.cryptoid.info/dash/tx.dws?{0}.htm" }, + { CoinType.BTC, "https://blockchain.info/tx/{0}" }, + { CoinType.DOGE, "https://dogechain.info/tx/{0}" }, + { CoinType.ZEC, "https://explorer.zcha.in/transactions/{0}" }, + { CoinType.ZCL, "http://explorer.zclmine.pro/transactions/{0}" }, + { CoinType.ZEN, "http://explorer.zensystem.io/transactions/{0}" }, + { CoinType.BTCP, "https://explorer.btcprivate.org/transactions/{0}" }, + { CoinType.DGB, "https://digiexplorer.info/tx/{0}" }, + { CoinType.NMC, "https://explorer.namecoin.info/tx/{0}" }, + { CoinType.GRS, "https://groestlsight.groestlcoin.org/tx/{0}" }, + { CoinType.MONA, "https://bchain.info/MONA/tx/{0}" }, + { CoinType.STAK, "https://straks.info/transaction/{0}" }, + { CoinType.GLT, "https://bchain.info/GLT/tx/{0}" }, + { CoinType.VTC, "https://bchain.info/VTC/tx/{0}" }, + { CoinType.BTG, "https://btgexp.com/tx/{0}" }, + { CoinType.ELLA, "https://explorer.ellaism.org/tx/{0}" }, + { CoinType.EXP, "http://www.gander.tech/tx/{0}" }, + { CoinType.AEON, "https://chainradar.com/aeon/transaction/{0}" }, + { CoinType.MOON, "https://chainz.cryptoid.info/moon/tx.dws?{0}.htm" }, + { CoinType.XVG, "https://verge-blockchain.info/tx/{0}" }, + { CoinType.GBX, "http://gobyte.ezmine.io/tx/{0}" }, + { CoinType.CRC, "http://explorer.cryptopros.us/tx/{0}" }, + }; + + public static readonly Dictionary AddressInfoLinks = new Dictionary + { + { CoinType.ETH, "https://etherscan.io/address/{0}" }, + { CoinType.ETC, "https://gastracker.io/addr/{0}" }, + { CoinType.LTC, "https://chainz.cryptoid.info/ltc/address.dws?{0}.htm" }, + { CoinType.PPC, "https://chainz.cryptoid.info/ppc/address.dws?{0}.htm" }, + { CoinType.BCH, "https://www.blocktrail.com/BCC/address/{0}" }, + { CoinType.DASH, "https://chainz.cryptoid.info/dash/address.dws?{0}.htm" }, + { CoinType.BTC, "https://blockchain.info/address/{0}" }, + { CoinType.DOGE, "https://dogechain.info/address/{0}" }, + { CoinType.ZEC, "https://explorer.zcha.in/accounts/{0}" }, + { CoinType.ZCL, "http://explorer.zclmine.pro/accounts/{0}" }, + { CoinType.ZEN, "http://explorer.zensystem.io/accounts/{0}" }, + { CoinType.DGB, "https://digiexplorer.info/address/{0}" }, + { CoinType.NMC, "https://explorer.namecoin.info/a/{0}" }, + { CoinType.GRS, "https://groestlsight.groestlcoin.org/address/{0}" }, + { CoinType.MONA, "https://bchain.info/MONA/addr/{0}" }, + { CoinType.STAK, "https://straks.info/address/{0}" }, + { CoinType.GLT, "https://bchain.info/GLT/addr/{0}" }, + { CoinType.VTC, "https://bchain.info/VTC/addr/{0}" }, + { CoinType.BTG, "https://btgexp.com/address/{0}" }, + { CoinType.ELLA, "https://explorer.ellaism.org/addr/{0}" }, + { CoinType.EXP, "http://www.gander.tech/address/{0}" }, + { CoinType.MOON, "https://chainz.cryptoid.info/moon/address.dws?{0}.htm" }, + { CoinType.XVG, "https://verge-blockchain.info/address/{0}" }, + { CoinType.GBX, "http://gobyte.ezmine.io/address/{0}" }, + { CoinType.CRC, "http://explorer.cryptopros.us/address/{0}" }, + }; + + private const string Ethash = "Dagger-Hashimoto"; + private const string Cryptonight = "Cryptonight"; + private const string CryptonightLight = "Cryptonight-Light"; + + public static readonly Dictionary> CoinAlgorithm = new Dictionary> + { + { CoinType.ETH, (coin, alg)=> Ethash }, + { CoinType.ETC, (coin, alg)=> Ethash }, + { CoinType.LTC, BitcoinProperties.GetAlgorithm }, + { CoinType.PPC, BitcoinProperties.GetAlgorithm }, + { CoinType.BCH, BitcoinProperties.GetAlgorithm }, + { CoinType.DASH, BitcoinProperties.GetAlgorithm }, + { CoinType.BTC, BitcoinProperties.GetAlgorithm }, + { CoinType.DOGE, BitcoinProperties.GetAlgorithm }, + { CoinType.ZEC, BitcoinProperties.GetAlgorithm }, + { CoinType.ZCL, BitcoinProperties.GetAlgorithm }, + { CoinType.BTCP, BitcoinProperties.GetAlgorithm }, + { CoinType.ZEN, BitcoinProperties.GetAlgorithm }, + { CoinType.DGB, BitcoinProperties.GetAlgorithm }, + { CoinType.NMC, BitcoinProperties.GetAlgorithm }, + { CoinType.GRS, BitcoinProperties.GetAlgorithm }, + { CoinType.MONA, BitcoinProperties.GetAlgorithm }, + { CoinType.STAK, BitcoinProperties.GetAlgorithm }, + { CoinType.GLT, BitcoinProperties.GetAlgorithm }, + { CoinType.VTC, BitcoinProperties.GetAlgorithm }, + { CoinType.BTG, BitcoinProperties.GetAlgorithm }, + { CoinType.ELLA, (coin, alg)=> Ethash }, + { CoinType.EXP, (coin, alg)=> Ethash }, + { CoinType.MOON, BitcoinProperties.GetAlgorithm }, + { CoinType.XVG, BitcoinProperties.GetAlgorithm }, + { CoinType.XMR, (coin, alg)=> Cryptonight }, + { CoinType.ETN, (coin, alg)=> Cryptonight }, + { CoinType.AEON, (coin, alg)=> CryptonightLight }, + { CoinType.GBX, BitcoinProperties.GetAlgorithm }, + { CoinType.CRC, BitcoinProperties.GetAlgorithm }, + }; + } +} From 2f890abf31caab61b5f2265d5b3b25419b39797b Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 13 Mar 2018 14:16:11 +0100 Subject: [PATCH 162/348] Coinbase --- src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs | 9 +++------ src/MiningCore/Properties/launchSettings.json | 14 +++++++------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs index de532815b..10824259d 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs @@ -99,15 +99,12 @@ protected virtual void BuildMerkleBranches() protected virtual void BuildCoinbase() { - var extraNoncePlaceHolderLengthByte = (byte) extraNoncePlaceHolderLength; - // generate script parts var sigScriptInitial = GenerateScriptSigInitial(); var sigScriptInitialBytes = sigScriptInitial.ToBytes(); var sigScriptLength = (uint) ( sigScriptInitial.Length + - 1 + // for extranonce-placeholder length after sigScriptInitial extraNoncePlaceHolderLength + scriptSigFinalBytes.Length); @@ -138,9 +135,6 @@ protected virtual void BuildCoinbase() bs.ReadWriteAsVarInt(ref sigScriptLength); bs.ReadWrite(ref sigScriptInitialBytes); - // emit a simulated OP_PUSH(n) just without the payload (which is filled in by the miner: extranonce1 and extranonce2) - bs.ReadWrite(ref extraNoncePlaceHolderLengthByte); - // done coinbaseInitial = stream.ToArray(); coinbaseInitialHex = coinbaseInitial.ToHexString(); @@ -235,6 +229,9 @@ protected virtual Script GenerateScriptSigInitial() // push timestamp ops.Add(Op.GetPushOp(now)); + // push placeholder + ops.Add(Op.GetPushOp((uint) 0)); + return new Script(ops); } diff --git a/src/MiningCore/Properties/launchSettings.json b/src/MiningCore/Properties/launchSettings.json index 72807c195..9218cd2b3 100644 --- a/src/MiningCore/Properties/launchSettings.json +++ b/src/MiningCore/Properties/launchSettings.json @@ -1,8 +1,8 @@ -{ - "profiles": { - "MiningCore": { - "commandName": "Project", - "commandLineArgs": "-c config.json" - } - } +{ + "profiles": { + "MiningCore": { + "commandName": "Project", + "commandLineArgs": "-c ..\\..\\..\\config.json" + } + } } \ No newline at end of file From 8ab32c8d73d5ad8b15493272ade818a754de4e75 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 13 Mar 2018 14:42:15 +0100 Subject: [PATCH 163/348] WIP --- src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs | 3 ++- src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs | 3 ++- src/MiningCore/Blockchain/Monero/MoneroJobManager.cs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 71313b26b..cf6971d96 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -666,7 +666,8 @@ protected override async Task PostStartInitAsync() else networkType = daemonInfoResponse.Testnet ? BitcoinNetworkType.Test : BitcoinNetworkType.Main; - ConfigureRewards(); + if(clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) + ConfigureRewards(); // update stats BlockchainStats.NetworkType = networkType.ToString(); diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs index e74ec4f40..862491b57 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs @@ -542,7 +542,8 @@ protected override async Task PostStartInitAsync() EthereumUtils.DetectNetworkAndChain(netVersion, parityChain, out networkType, out chainType); - ConfigureRewards(); + if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) + ConfigureRewards(); // update stats BlockchainStats.RewardType = "POW"; diff --git a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs index 69e7ec02a..dbfef9a13 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs @@ -430,7 +430,8 @@ protected override async Task PostStartInitAsync() break; } - ConfigureRewards(); + if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) + ConfigureRewards(); // update stats BlockchainStats.RewardType = "POW"; From 88064dcea01393dc837ad0c4b83f52b08b33b3bb Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Tue, 13 Mar 2018 20:46:17 +0100 Subject: [PATCH 164/348] Logging --- src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index cf6971d96..4c863f368 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -20,6 +20,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Net; using System.Reactive.Linq; @@ -280,12 +281,14 @@ private async Task UpdateNetworkStatsAsync() // did submission succeed? var submitResult = results[0]; - var submitError = submitResult.Error?.Message ?? submitResult.Response?.ToString(); + var submitError = submitResult.Error?.Message ?? + submitResult.Error?.Code.ToString(CultureInfo.InvariantCulture) ?? + submitResult.Response?.ToString(); if (!string.IsNullOrEmpty(submitError)) { logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} submission failed with: {submitError}"); - notificationService.NotifyAdmin("Block submission failed", $"Block {share.BlockHeight} submission failed with: {submitError}"); + notificationService.NotifyAdmin($"[{share.PoolId.ToUpper()}]-[{share.Source}] Block submission failed", $"[{share.PoolId.ToUpper()}]-[{share.Source}] Block {share.BlockHeight} submission failed with: {submitError}"); return (false, null); } @@ -298,7 +301,7 @@ private async Task UpdateNetworkStatsAsync() if (!accepted) { logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} submission failed for pool {poolConfig.Id} because block was not found after submission"); - notificationService.NotifyAdmin("Block submission failed", $"Block {share.BlockHeight} submission failed for pool {poolConfig.Id} because block was not found after submission"); + notificationService.NotifyAdmin($"[{share.PoolId.ToUpper()}]-[{share.Source}] Block submission failed", $"[{share.PoolId.ToUpper()}]-[{share.Source}] Block {share.BlockHeight} submission failed for pool {poolConfig.Id} because block was not found after submission"); } return (accepted, block?.Transactions.FirstOrDefault()); From 9eef21af50d70132c79d6792b7f5af349aea5171 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 14 Mar 2018 11:36:39 +0100 Subject: [PATCH 165/348] WIP --- ...s => PooledArraySegmentLineBufferTests.cs} | 32 ++-- .../DaemonInterface/DaemonClient.cs | 2 +- src/MiningCore/Stratum/StratumClient.cs | 2 +- ...fer.cs => PooledArraySegmentLineBuffer.cs} | 4 +- .../Util/PooledReadableLineBuffer.cs | 156 ++++++++++++++++++ 5 files changed, 176 insertions(+), 20 deletions(-) rename src/MiningCore.Tests/Util/{PooledLineBufferTests.cs => PooledArraySegmentLineBufferTests.cs} (87%) rename src/MiningCore/Util/{PooledLineBuffer.cs => PooledArraySegmentLineBuffer.cs} (94%) create mode 100644 src/MiningCore/Util/PooledReadableLineBuffer.cs diff --git a/src/MiningCore.Tests/Util/PooledLineBufferTests.cs b/src/MiningCore.Tests/Util/PooledArraySegmentLineBufferTests.cs similarity index 87% rename from src/MiningCore.Tests/Util/PooledLineBufferTests.cs rename to src/MiningCore.Tests/Util/PooledArraySegmentLineBufferTests.cs index 55dab1572..2bd9e743a 100644 --- a/src/MiningCore.Tests/Util/PooledLineBufferTests.cs +++ b/src/MiningCore.Tests/Util/PooledArraySegmentLineBufferTests.cs @@ -10,7 +10,7 @@ namespace MiningCore.Tests.Util { - public class PooledLineBufferTests : TestBase + public class PooledArraySegmentLineBufferTests : TestBase { private byte[] GetBuffer(string str) { @@ -25,7 +25,7 @@ private string GetString(PooledArraySegment seg) [Fact] public void PooledLineBuffer_Partial_Line() { - var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); var recvCount = 0; var errCount = 0; @@ -42,7 +42,7 @@ public void PooledLineBuffer_Partial_Line() [Fact] public void PooledLineBuffer_Partial_Line_Double() { - var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); var recvCount = 0; var errCount = 0; @@ -65,7 +65,7 @@ public void PooledLineBuffer_Partial_Line_Double() [Fact] public void PooledLineBuffer_Partial_Line_Double_With_NewLine() { - var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); var recvCount = 0; var errCount = 0; var result = string.Empty; @@ -94,7 +94,7 @@ public void PooledLineBuffer_Partial_Line_Double_With_NewLine() [Fact] public void PooledLineBuffer_Partial_Line_Double_With_NewLine_With_Leading_NewLines() { - var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); var recvCount = 0; var errCount = 0; var result = string.Empty; @@ -123,7 +123,7 @@ public void PooledLineBuffer_Partial_Line_Double_With_NewLine_With_Leading_NewLi [Fact] public void PooledLineBuffer_Partial_Line_Double_With_NewLine_With_Trailing_NewLines() { - var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); var recvCount = 0; var errCount = 0; var result = string.Empty; @@ -152,7 +152,7 @@ public void PooledLineBuffer_Partial_Line_Double_With_NewLine_With_Trailing_NewL [Fact] public void PooledLineBuffer_Partial_Dont_Emit_Empty_Lines() { - var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); var recvCount = 0; var errCount = 0; @@ -172,7 +172,7 @@ public void PooledLineBuffer_Partial_Dont_Emit_Empty_Lines() [Fact] public void PooledLineBuffer_Partial_Enforce_Limits() { - var plb = new PooledLineBuffer(LogManager.CreateNullLogger(), 3); + var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger(), 3); var recvCount = 0; var errCount = 0; @@ -192,7 +192,7 @@ public void PooledLineBuffer_Partial_Enforce_Limits() [Fact] public void PooledLineBuffer_Partial_Enforce_Limits_Queued() { - var plb = new PooledLineBuffer(LogManager.CreateNullLogger(), 5); + var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger(), 5); var recvCount = 0; var errCount = 0; @@ -224,7 +224,7 @@ public void PooledLineBuffer_Partial_Enforce_Limits_Queued() [Fact] public void PooledLineBuffer_Single_Line() { - var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); var recvCount = 0; var errCount = 0; var result = string.Empty; @@ -247,7 +247,7 @@ public void PooledLineBuffer_Single_Line() [Fact] public void PooledLineBuffer_Multi_Line_Batch() { - var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); var errCount = 0; var results = new List(); @@ -270,7 +270,7 @@ public void PooledLineBuffer_Multi_Line_Batch() [Fact] public void PooledLineBuffer_Single_Characters() { - var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); var errCount = 0; var results = new List(); @@ -309,7 +309,7 @@ public void PooledLineBuffer_Single_Characters() [Fact] public void PooledLineBuffer_Single_Character_Lines() { - var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); var errCount = 0; var results = new List(); @@ -350,7 +350,7 @@ public void PooledLineBuffer_Single_Character_Lines() [Fact] public void PooledLineBuffer_Combo1() { - var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); var errCount = 0; var results = new List(); @@ -371,7 +371,7 @@ public void PooledLineBuffer_Combo1() [Fact] public void PooledLineBuffer_Combo2() { - var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); var errCount = 0; var results = new List(); @@ -402,7 +402,7 @@ public void PooledLineBuffer_Combo2() [Fact] public void PooledLineBuffer_Combo3() { - var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); var errCount = 0; var results = new List(); diff --git a/src/MiningCore/DaemonInterface/DaemonClient.cs b/src/MiningCore/DaemonInterface/DaemonClient.cs index 2a10b7283..68410f4bb 100644 --- a/src/MiningCore/DaemonInterface/DaemonClient.cs +++ b/src/MiningCore/DaemonInterface/DaemonClient.cs @@ -420,7 +420,7 @@ private IObservable> WebsocketSubscribeEndpoint(DaemonE { try { - using (var plb = new PooledLineBuffer(logger)) + using (var plb = new PooledArraySegmentLineBuffer(logger)) { using(var client = new ClientWebSocket()) { diff --git a/src/MiningCore/Stratum/StratumClient.cs b/src/MiningCore/Stratum/StratumClient.cs index 394374a71..dcfe04e3c 100644 --- a/src/MiningCore/Stratum/StratumClient.cs +++ b/src/MiningCore/Stratum/StratumClient.cs @@ -47,7 +47,7 @@ public class StratumClient private ConcurrentQueue> sendQueue; private Async sendQueueDrainer; - private readonly PooledLineBuffer plb = new PooledLineBuffer(logger, MaxInboundRequestLength); + private readonly PooledArraySegmentLineBuffer plb = new PooledArraySegmentLineBuffer(logger, MaxInboundRequestLength); private IDisposable subscription; private bool isAlive = true; private WorkerContextBase context; diff --git a/src/MiningCore/Util/PooledLineBuffer.cs b/src/MiningCore/Util/PooledArraySegmentLineBuffer.cs similarity index 94% rename from src/MiningCore/Util/PooledLineBuffer.cs rename to src/MiningCore/Util/PooledArraySegmentLineBuffer.cs index 1a1d46487..ddc8bcd77 100644 --- a/src/MiningCore/Util/PooledLineBuffer.cs +++ b/src/MiningCore/Util/PooledArraySegmentLineBuffer.cs @@ -10,9 +10,9 @@ namespace MiningCore.Util { - public class PooledLineBuffer : IDisposable + public class PooledArraySegmentLineBuffer : IDisposable { - public PooledLineBuffer(ILogger logger, int? maxLength = null) + public PooledArraySegmentLineBuffer(ILogger logger, int? maxLength = null) { this.maxLength = maxLength; this.logger = logger ?? LogManager.GetCurrentClassLogger(); diff --git a/src/MiningCore/Util/PooledReadableLineBuffer.cs b/src/MiningCore/Util/PooledReadableLineBuffer.cs new file mode 100644 index 000000000..ffce6ae6f --- /dev/null +++ b/src/MiningCore/Util/PooledReadableLineBuffer.cs @@ -0,0 +1,156 @@ +using System; +using System.Buffers; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using MiningCore.Buffers; +using MiningCore.Extensions; +using NetUV.Core.Buffers; +using NLog; +/* +namespace MiningCore.Util +{ + public class PooledReadableLineBuffer : IDisposable + { + public PooledReadableLineBuffer(ILogger logger, int? maxLength = null) + { + this.maxLength = maxLength; + this.logger = logger ?? LogManager.GetCurrentClassLogger(); + } + + private readonly Queue recvQueue = new Queue(); + private readonly ILogger logger; + private int? maxLength; + private static readonly Encoding Encoding = Encoding.UTF8; + private static readonly ArrayPool ByteArrayPool = ArrayPool.Shared; + + #region IDisposable + + public void Dispose() + { + while (recvQueue.TryDequeue(out var fragment)) + fragment.Dispose(); + } + + #endregion + + public void Receive(ReadableBuffer buffer, + Action> onNext, + Action onError, + bool forceNewLine = false) + { + var bufferSize = buffer.Count; + + if (buffer.Count == 0) + return; + + // prevent flooding + if (maxLength.HasValue && bufferSize > maxLength) + { + onError(new InvalidDataException($"Incoming data exceeds maximum of {maxLength.Value}")); + return; + } + + buffer = buffer.Retain(); + + var remaining = bufferSize; + var prevIndex = 0; + var keepLease = false; + + try + { + + // diagnostics + logger.Trace(() => $"recv: {Encoding.GetString(buffer., 0, bufferSize)}"); + + while (remaining > 0) + { + // check if we got a newline + var index = buf.IndexOf(0xa, prevIndex, buf.Length - prevIndex); + var found = index != -1; + + if (found || forceNewLine) + { + // fastpath + if (!forceNewLine && index + 1 == bufferSize && recvQueue.Count == 0) + { + var length = index - prevIndex; + + if (length > 0) + { + onNext(new PooledArraySegment(buf, prevIndex, length)); + keepLease = true; + } + + break; + } + + // assemble line buffer + var queuedLength = recvQueue.Sum(x => x.Count); + var segmentLength = !forceNewLine ? index - prevIndex : bufferSize - prevIndex; + var lineLength = queuedLength + segmentLength; + var line = ArrayPool.Shared.Rent(lineLength); + var offset = 0; + + while (recvQueue.TryDequeue(out var segment)) + { + using (segment) + { + Array.Copy(segment.Array, 0, line, offset, segment.Size); + offset += segment.Size; + } + } + + // append remaining characters + if (segmentLength > 0) + Array.Copy(buf, prevIndex, line, offset, segmentLength); + + // emit + if (lineLength > 0) + onNext(new PooledArraySegment(line, 0, lineLength)); + + if (forceNewLine) + break; + + prevIndex = index + 1; + remaining -= segmentLength + 1; + continue; + } + + // store + if (prevIndex != 0) + { + var segmentLength = bufferSize - prevIndex; + + if (segmentLength > 0) + { + var fragment = ArrayPool.Shared.Rent(segmentLength); + Array.Copy(buf, prevIndex, fragment, 0, segmentLength); + recvQueue.Enqueue(new PooledArraySegment(fragment, 0, segmentLength)); + } + } + + else + { + recvQueue.Enqueue(new PooledArraySegment(buf, 0, remaining)); + keepLease = true; + } + + // prevent flooding + if (maxLength.HasValue && recvQueue.Sum(x => x.Size) > maxLength.Value) + onError(new InvalidDataException($"Incoming request size exceeds maximum of {maxLength.Value}")); + + break; + } + } + + finally + { + if (!keepLease) + ByteArrayPool.Return(buf); + } + } + } +} +*/ \ No newline at end of file From 5f88fd6b26f0e78cad3e950dc76c2648e22c8cbf Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 14 Mar 2018 16:21:02 +0100 Subject: [PATCH 166/348] Revert "WIP" This reverts commit 9eef21af50d70132c79d6792b7f5af349aea5171. --- ...ufferTests.cs => PooledLineBufferTests.cs} | 32 ++-- .../DaemonInterface/DaemonClient.cs | 2 +- src/MiningCore/Stratum/StratumClient.cs | 2 +- ...gmentLineBuffer.cs => PooledLineBuffer.cs} | 4 +- .../Util/PooledReadableLineBuffer.cs | 156 ------------------ 5 files changed, 20 insertions(+), 176 deletions(-) rename src/MiningCore.Tests/Util/{PooledArraySegmentLineBufferTests.cs => PooledLineBufferTests.cs} (87%) rename src/MiningCore/Util/{PooledArraySegmentLineBuffer.cs => PooledLineBuffer.cs} (94%) delete mode 100644 src/MiningCore/Util/PooledReadableLineBuffer.cs diff --git a/src/MiningCore.Tests/Util/PooledArraySegmentLineBufferTests.cs b/src/MiningCore.Tests/Util/PooledLineBufferTests.cs similarity index 87% rename from src/MiningCore.Tests/Util/PooledArraySegmentLineBufferTests.cs rename to src/MiningCore.Tests/Util/PooledLineBufferTests.cs index 2bd9e743a..55dab1572 100644 --- a/src/MiningCore.Tests/Util/PooledArraySegmentLineBufferTests.cs +++ b/src/MiningCore.Tests/Util/PooledLineBufferTests.cs @@ -10,7 +10,7 @@ namespace MiningCore.Tests.Util { - public class PooledArraySegmentLineBufferTests : TestBase + public class PooledLineBufferTests : TestBase { private byte[] GetBuffer(string str) { @@ -25,7 +25,7 @@ private string GetString(PooledArraySegment seg) [Fact] public void PooledLineBuffer_Partial_Line() { - var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); var recvCount = 0; var errCount = 0; @@ -42,7 +42,7 @@ public void PooledLineBuffer_Partial_Line() [Fact] public void PooledLineBuffer_Partial_Line_Double() { - var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); var recvCount = 0; var errCount = 0; @@ -65,7 +65,7 @@ public void PooledLineBuffer_Partial_Line_Double() [Fact] public void PooledLineBuffer_Partial_Line_Double_With_NewLine() { - var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); var recvCount = 0; var errCount = 0; var result = string.Empty; @@ -94,7 +94,7 @@ public void PooledLineBuffer_Partial_Line_Double_With_NewLine() [Fact] public void PooledLineBuffer_Partial_Line_Double_With_NewLine_With_Leading_NewLines() { - var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); var recvCount = 0; var errCount = 0; var result = string.Empty; @@ -123,7 +123,7 @@ public void PooledLineBuffer_Partial_Line_Double_With_NewLine_With_Leading_NewLi [Fact] public void PooledLineBuffer_Partial_Line_Double_With_NewLine_With_Trailing_NewLines() { - var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); var recvCount = 0; var errCount = 0; var result = string.Empty; @@ -152,7 +152,7 @@ public void PooledLineBuffer_Partial_Line_Double_With_NewLine_With_Trailing_NewL [Fact] public void PooledLineBuffer_Partial_Dont_Emit_Empty_Lines() { - var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); var recvCount = 0; var errCount = 0; @@ -172,7 +172,7 @@ public void PooledLineBuffer_Partial_Dont_Emit_Empty_Lines() [Fact] public void PooledLineBuffer_Partial_Enforce_Limits() { - var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger(), 3); + var plb = new PooledLineBuffer(LogManager.CreateNullLogger(), 3); var recvCount = 0; var errCount = 0; @@ -192,7 +192,7 @@ public void PooledLineBuffer_Partial_Enforce_Limits() [Fact] public void PooledLineBuffer_Partial_Enforce_Limits_Queued() { - var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger(), 5); + var plb = new PooledLineBuffer(LogManager.CreateNullLogger(), 5); var recvCount = 0; var errCount = 0; @@ -224,7 +224,7 @@ public void PooledLineBuffer_Partial_Enforce_Limits_Queued() [Fact] public void PooledLineBuffer_Single_Line() { - var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); var recvCount = 0; var errCount = 0; var result = string.Empty; @@ -247,7 +247,7 @@ public void PooledLineBuffer_Single_Line() [Fact] public void PooledLineBuffer_Multi_Line_Batch() { - var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); var errCount = 0; var results = new List(); @@ -270,7 +270,7 @@ public void PooledLineBuffer_Multi_Line_Batch() [Fact] public void PooledLineBuffer_Single_Characters() { - var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); var errCount = 0; var results = new List(); @@ -309,7 +309,7 @@ public void PooledLineBuffer_Single_Characters() [Fact] public void PooledLineBuffer_Single_Character_Lines() { - var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); var errCount = 0; var results = new List(); @@ -350,7 +350,7 @@ public void PooledLineBuffer_Single_Character_Lines() [Fact] public void PooledLineBuffer_Combo1() { - var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); var errCount = 0; var results = new List(); @@ -371,7 +371,7 @@ public void PooledLineBuffer_Combo1() [Fact] public void PooledLineBuffer_Combo2() { - var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); var errCount = 0; var results = new List(); @@ -402,7 +402,7 @@ public void PooledLineBuffer_Combo2() [Fact] public void PooledLineBuffer_Combo3() { - var plb = new PooledArraySegmentLineBuffer(LogManager.CreateNullLogger()); + var plb = new PooledLineBuffer(LogManager.CreateNullLogger()); var errCount = 0; var results = new List(); diff --git a/src/MiningCore/DaemonInterface/DaemonClient.cs b/src/MiningCore/DaemonInterface/DaemonClient.cs index 68410f4bb..2a10b7283 100644 --- a/src/MiningCore/DaemonInterface/DaemonClient.cs +++ b/src/MiningCore/DaemonInterface/DaemonClient.cs @@ -420,7 +420,7 @@ private IObservable> WebsocketSubscribeEndpoint(DaemonE { try { - using (var plb = new PooledArraySegmentLineBuffer(logger)) + using (var plb = new PooledLineBuffer(logger)) { using(var client = new ClientWebSocket()) { diff --git a/src/MiningCore/Stratum/StratumClient.cs b/src/MiningCore/Stratum/StratumClient.cs index dcfe04e3c..394374a71 100644 --- a/src/MiningCore/Stratum/StratumClient.cs +++ b/src/MiningCore/Stratum/StratumClient.cs @@ -47,7 +47,7 @@ public class StratumClient private ConcurrentQueue> sendQueue; private Async sendQueueDrainer; - private readonly PooledArraySegmentLineBuffer plb = new PooledArraySegmentLineBuffer(logger, MaxInboundRequestLength); + private readonly PooledLineBuffer plb = new PooledLineBuffer(logger, MaxInboundRequestLength); private IDisposable subscription; private bool isAlive = true; private WorkerContextBase context; diff --git a/src/MiningCore/Util/PooledArraySegmentLineBuffer.cs b/src/MiningCore/Util/PooledLineBuffer.cs similarity index 94% rename from src/MiningCore/Util/PooledArraySegmentLineBuffer.cs rename to src/MiningCore/Util/PooledLineBuffer.cs index ddc8bcd77..1a1d46487 100644 --- a/src/MiningCore/Util/PooledArraySegmentLineBuffer.cs +++ b/src/MiningCore/Util/PooledLineBuffer.cs @@ -10,9 +10,9 @@ namespace MiningCore.Util { - public class PooledArraySegmentLineBuffer : IDisposable + public class PooledLineBuffer : IDisposable { - public PooledArraySegmentLineBuffer(ILogger logger, int? maxLength = null) + public PooledLineBuffer(ILogger logger, int? maxLength = null) { this.maxLength = maxLength; this.logger = logger ?? LogManager.GetCurrentClassLogger(); diff --git a/src/MiningCore/Util/PooledReadableLineBuffer.cs b/src/MiningCore/Util/PooledReadableLineBuffer.cs deleted file mode 100644 index ffce6ae6f..000000000 --- a/src/MiningCore/Util/PooledReadableLineBuffer.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.Buffers; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using MiningCore.Buffers; -using MiningCore.Extensions; -using NetUV.Core.Buffers; -using NLog; -/* -namespace MiningCore.Util -{ - public class PooledReadableLineBuffer : IDisposable - { - public PooledReadableLineBuffer(ILogger logger, int? maxLength = null) - { - this.maxLength = maxLength; - this.logger = logger ?? LogManager.GetCurrentClassLogger(); - } - - private readonly Queue recvQueue = new Queue(); - private readonly ILogger logger; - private int? maxLength; - private static readonly Encoding Encoding = Encoding.UTF8; - private static readonly ArrayPool ByteArrayPool = ArrayPool.Shared; - - #region IDisposable - - public void Dispose() - { - while (recvQueue.TryDequeue(out var fragment)) - fragment.Dispose(); - } - - #endregion - - public void Receive(ReadableBuffer buffer, - Action> onNext, - Action onError, - bool forceNewLine = false) - { - var bufferSize = buffer.Count; - - if (buffer.Count == 0) - return; - - // prevent flooding - if (maxLength.HasValue && bufferSize > maxLength) - { - onError(new InvalidDataException($"Incoming data exceeds maximum of {maxLength.Value}")); - return; - } - - buffer = buffer.Retain(); - - var remaining = bufferSize; - var prevIndex = 0; - var keepLease = false; - - try - { - - // diagnostics - logger.Trace(() => $"recv: {Encoding.GetString(buffer., 0, bufferSize)}"); - - while (remaining > 0) - { - // check if we got a newline - var index = buf.IndexOf(0xa, prevIndex, buf.Length - prevIndex); - var found = index != -1; - - if (found || forceNewLine) - { - // fastpath - if (!forceNewLine && index + 1 == bufferSize && recvQueue.Count == 0) - { - var length = index - prevIndex; - - if (length > 0) - { - onNext(new PooledArraySegment(buf, prevIndex, length)); - keepLease = true; - } - - break; - } - - // assemble line buffer - var queuedLength = recvQueue.Sum(x => x.Count); - var segmentLength = !forceNewLine ? index - prevIndex : bufferSize - prevIndex; - var lineLength = queuedLength + segmentLength; - var line = ArrayPool.Shared.Rent(lineLength); - var offset = 0; - - while (recvQueue.TryDequeue(out var segment)) - { - using (segment) - { - Array.Copy(segment.Array, 0, line, offset, segment.Size); - offset += segment.Size; - } - } - - // append remaining characters - if (segmentLength > 0) - Array.Copy(buf, prevIndex, line, offset, segmentLength); - - // emit - if (lineLength > 0) - onNext(new PooledArraySegment(line, 0, lineLength)); - - if (forceNewLine) - break; - - prevIndex = index + 1; - remaining -= segmentLength + 1; - continue; - } - - // store - if (prevIndex != 0) - { - var segmentLength = bufferSize - prevIndex; - - if (segmentLength > 0) - { - var fragment = ArrayPool.Shared.Rent(segmentLength); - Array.Copy(buf, prevIndex, fragment, 0, segmentLength); - recvQueue.Enqueue(new PooledArraySegment(fragment, 0, segmentLength)); - } - } - - else - { - recvQueue.Enqueue(new PooledArraySegment(buf, 0, remaining)); - keepLease = true; - } - - // prevent flooding - if (maxLength.HasValue && recvQueue.Sum(x => x.Size) > maxLength.Value) - onError(new InvalidDataException($"Incoming request size exceeds maximum of {maxLength.Value}")); - - break; - } - } - - finally - { - if (!keepLease) - ByteArrayPool.Return(buf); - } - } - } -} -*/ \ No newline at end of file From 8968d147bcf973413e2af29f2f96085ba42e6277 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 14 Mar 2018 20:39:56 +0100 Subject: [PATCH 167/348] Refactored-out chain specific share DTOs --- .../Blockchain/Bitcoin/BitcoinJobTests.cs | 10 +-- .../Blockchain/Monero/MoneroJobTests.cs | 6 +- src/MiningCore/AutoMapperProfile.cs | 8 +- src/MiningCore/Blockchain/Abstractions.cs | 80 ------------------- .../Blockchain/Bitcoin/BitcoinJob.cs | 16 ++-- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 12 +-- .../Blockchain/Bitcoin/BitcoinPoolBase.cs | 2 +- .../Blockchain/Bitcoin/BitcoinShare.cs | 27 ------- .../Blockchain/Ethereum/EthereumJob.cs | 19 +++-- .../Blockchain/Ethereum/EthereumJobManager.cs | 14 ++-- .../Blockchain/Ethereum/EthereumPool.cs | 2 +- .../Blockchain/Ethereum/EthereumShare.cs | 29 ------- src/MiningCore/Blockchain/Monero/MoneroJob.cs | 14 ++-- .../Blockchain/Monero/MoneroJobManager.cs | 18 ++--- .../Blockchain/Monero/MoneroPool.cs | 2 +- .../Blockchain/Monero/MoneroShare.cs | 28 ------- .../Blockchain/{ShareBase.cs => Share.cs} | 65 ++++++++++++++- src/MiningCore/Blockchain/ZCash/ZCashJob.cs | 18 ++--- .../Blockchain/ZCash/ZCashJobManager.cs | 6 +- src/MiningCore/Mining/Abstractions.cs | 4 +- src/MiningCore/Mining/PoolBase.cs | 9 +-- src/MiningCore/Payments/ShareRecorder.cs | 17 ++-- src/MiningCore/Payments/ShareRelay.cs | 5 +- 23 files changed, 153 insertions(+), 258 deletions(-) delete mode 100644 src/MiningCore/Blockchain/Bitcoin/BitcoinShare.cs delete mode 100644 src/MiningCore/Blockchain/Ethereum/EthereumShare.cs delete mode 100644 src/MiningCore/Blockchain/Monero/MoneroShare.cs rename src/MiningCore/Blockchain/{ShareBase.cs => Share.cs} (52%) diff --git a/src/MiningCore.Tests/Blockchain/Bitcoin/BitcoinJobTests.cs b/src/MiningCore.Tests/Blockchain/Bitcoin/BitcoinJobTests.cs index 2282c2ead..faafe0fa8 100644 --- a/src/MiningCore.Tests/Blockchain/Bitcoin/BitcoinJobTests.cs +++ b/src/MiningCore.Tests/Blockchain/Bitcoin/BitcoinJobTests.cs @@ -47,12 +47,12 @@ public void BitcoinJob_Should_Accept_Valid_Share() // set clock to submission time clock.CurrentTime = DateTimeOffset.FromUnixTimeSeconds(1508869907).UtcDateTime; - var share = job.ProcessShare(worker, "01000000", "59ef86f2", "8d84ae6a"); + var (share, blockHex) = job.ProcessShare(worker, "01000000", "59ef86f2", "8d84ae6a"); Assert.NotNull(share); Assert.True(share.IsBlockCandidate); - Assert.Equal(share.BlockHash, "000000000fccf11cd0b7d9057441e430c320384b95b034bd28092c4553594b4a"); - Assert.Equal(share.BlockHex, "00000020bb76da6422b707a90831c421798123293bc5fd377bbeb51985570909000000008677145722cbe6f1ebec19fecc724cab5487f3292a69f6908bd512f645bb0635f286ef59ffff7f206aae848d0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff295e0c0b2f454231362f414431322f04f286ef590801000058010000000c2f4d696e696e67436f72652f000000000100f2052a010000001976a9142ebb5cccf9a6bb927661d2953655c43c04accc3788ac00000000"); + Assert.Equal(share.BlockHash, "601ed85039804bcecbbdb53e0ca358aeb8dabef2366fb64c216aac3aba02b716"); + Assert.Equal(blockHex, "00000020bb76da6422b707a90831c421798123293bc5fd377bbeb5198557090900000000fd5418fe788ef961678e4bacdd1fe3903185b9ec63865bb3d2d279bb0eb48c0bf286ef59ffff7f206aae848d0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff295e0c0b2f454231362f414431322f04f286ef590001000058010000000c2f4d696e696e67436f72652f000000000100f2052a010000001976a9142ebb5cccf9a6bb927661d2953655c43c04accc3788ac00000000"); Assert.Equal(share.BlockHeight, 14); Assert.Equal(share.BlockReward, 50); Assert.Equal(share.Difficulty, 0.5); @@ -87,14 +87,14 @@ public void BitcoinJob_Should_Not_Accept_Invalid_Share() Assert.Throws(() => job.ProcessShare(worker, "02000000", "59ef86f2", "8d84ae6a")); // make sure we don't accept case-sensitive duplicate shares as basically 0xdeadbeaf = 0xDEADBEAF. - var share = job.ProcessShare(worker, "01000000", "59ef86f2", "8d84ae6a"); + var (share, blockHex) = job.ProcessShare(worker, "01000000", "59ef86f2", "8d84ae6a"); Assert.Throws(() => job.ProcessShare(worker, "01000000", "59ef86f2", "8D84AE6A")); // invalid time Assert.Throws(() => job.ProcessShare(worker, "01000000", "69ef86f2", "8d84ae6a")); // invalid nonce - Assert.Throws(() => job.ProcessShare(worker, "01000000", "59ef86f2", "ad84be6a")); + Assert.Throws(() => job.ProcessShare(worker, "01000000", "59ef86f2", "4a84be6a")); // valid share data but invalid submission time clock.CurrentTime = DateTimeOffset.FromUnixTimeSeconds(1408869907).UtcDateTime; diff --git a/src/MiningCore.Tests/Blockchain/Monero/MoneroJobTests.cs b/src/MiningCore.Tests/Blockchain/Monero/MoneroJobTests.cs index 39f2ac5e8..b2e757de6 100644 --- a/src/MiningCore.Tests/Blockchain/Monero/MoneroJobTests.cs +++ b/src/MiningCore.Tests/Blockchain/Monero/MoneroJobTests.cs @@ -26,12 +26,12 @@ public void MoneroJob_Should_Accept_Valid_Share() "{\"blocktemplate_blob\":\"0106e7eabdcf058234351e2e6ea901a56b33bb531587424321873072d80a9e97295b6c43152b9d00000000019c0201ffe00106e3a1a0cc010275d92c0a057aa5f073079694a153d426f837f49fdb9654da10a5364e79a2086280a0d9e61d028b46dca0d04998500b40b046fd6f8bb33229e6380fd465dbb1327aa6f813d8bd80c0fc82aa0202372f076459e769116d604d30aabff7160782acc0d20e0c5cdc8963ed4e16372f8090cad2c60e02f009504ce65538bbb684b466b21be3a90e3740f185d7089d37b75f0cf62b6e7680e08d84ddcb0102cf01b85c0b592bb6e508e20b5d317052b75de121908390363201abff3476ef0180c0caf384a302024b81076c8ad0cfe84cc32fe0813d63cdd0f7d8d0e56d82aa3f58cbbe49d4c61e2b017aaf3074be7ecb30a769595758e4da7c7c87ead864baf89b679b73153dfa352c0208000000000000000000\",\"Difficulty\":2,\"Height\":224,\"prev_hash\":\"8234351e2e6ea901a56b33bb531587424321873072d80a9e97295b6c43152b9d\",\"reserved_offset\":322,\"Status\":\"OK\"}"); var job = new MoneroJob(bt, "d150da".HexToByteArray(), "1", poolConfig, clusterConfig); - var share = job.ProcessShare("040100a4", 1, "f29c7fbf57d97eeedb61555857d7a34314250da20742b8157f96e0be89530a00", worker); + var (share, blobHex, blobHash) = job.ProcessShare("040100a4", 1, "f29c7fbf57d97eeedb61555857d7a34314250da20742b8157f96e0be89530a00", worker); Assert.NotNull(share); Assert.True(share.IsBlockCandidate); - Assert.Equal(share.BlobHash, "9258faf2dff5daf026681b5fa5d94a34dbb5bade1d9e2070865ba8c68f8f0454"); - Assert.Equal(share.BlobHex, "0106e7eabdcf058234351e2e6ea901a56b33bb531587424321873072d80a9e97295b6c43152b9d040100a4019c0201ffe00106e3a1a0cc010275d92c0a057aa5f073079694a153d426f837f49fdb9654da10a5364e79a2086280a0d9e61d028b46dca0d04998500b40b046fd6f8bb33229e6380fd465dbb1327aa6f813d8bd80c0fc82aa0202372f076459e769116d604d30aabff7160782acc0d20e0c5cdc8963ed4e16372f8090cad2c60e02f009504ce65538bbb684b466b21be3a90e3740f185d7089d37b75f0cf62b6e7680e08d84ddcb0102cf01b85c0b592bb6e508e20b5d317052b75de121908390363201abff3476ef0180c0caf384a302024b81076c8ad0cfe84cc32fe0813d63cdd0f7d8d0e56d82aa3f58cbbe49d4c61e2b017aaf3074be7ecb30a769595758e4da7c7c87ead864baf89b679b73153dfa352c02080000000001d150da00"); + Assert.Equal(blobHash, "9258faf2dff5daf026681b5fa5d94a34dbb5bade1d9e2070865ba8c68f8f0454"); + Assert.Equal(blobHex, "0106e7eabdcf058234351e2e6ea901a56b33bb531587424321873072d80a9e97295b6c43152b9d040100a4019c0201ffe00106e3a1a0cc010275d92c0a057aa5f073079694a153d426f837f49fdb9654da10a5364e79a2086280a0d9e61d028b46dca0d04998500b40b046fd6f8bb33229e6380fd465dbb1327aa6f813d8bd80c0fc82aa0202372f076459e769116d604d30aabff7160782acc0d20e0c5cdc8963ed4e16372f8090cad2c60e02f009504ce65538bbb684b466b21be3a90e3740f185d7089d37b75f0cf62b6e7680e08d84ddcb0102cf01b85c0b592bb6e508e20b5d317052b75de121908390363201abff3476ef0180c0caf384a302024b81076c8ad0cfe84cc32fe0813d63cdd0f7d8d0e56d82aa3f58cbbe49d4c61e2b017aaf3074be7ecb30a769595758e4da7c7c87ead864baf89b679b73153dfa352c02080000000001d150da00"); Assert.Equal(share.BlockHeight, 224); Assert.Equal(share.Difficulty, 1000); } diff --git a/src/MiningCore/AutoMapperProfile.cs b/src/MiningCore/AutoMapperProfile.cs index e76232781..5dd268ac1 100644 --- a/src/MiningCore/AutoMapperProfile.cs +++ b/src/MiningCore/AutoMapperProfile.cs @@ -34,9 +34,9 @@ public AutoMapperProfile() ////////////////////// // outgoing mappings - CreateMap(); + CreateMap(); - CreateMap() + CreateMap() .ForMember(dest => dest.Reward, opt => opt.MapFrom(src => src.BlockReward)) .ForMember(dest => dest.Hash, opt => opt.MapFrom(src => src.BlockHash)) .ForMember(dest => dest.Status, opt => opt.Ignore()); @@ -69,7 +69,7 @@ public AutoMapperProfile() CreateMap(); // PostgreSQL - CreateMap(); + CreateMap(); CreateMap(); CreateMap(); CreateMap(); @@ -82,7 +82,7 @@ public AutoMapperProfile() // incoming mappings // PostgreSQL - CreateMap(); + CreateMap(); CreateMap(); CreateMap(); CreateMap(); diff --git a/src/MiningCore/Blockchain/Abstractions.cs b/src/MiningCore/Blockchain/Abstractions.cs index 9b2c1ff45..a583f83b0 100644 --- a/src/MiningCore/Blockchain/Abstractions.cs +++ b/src/MiningCore/Blockchain/Abstractions.cs @@ -37,84 +37,4 @@ public interface IExtraNonceProvider { string Next(); } - - public interface IShare - { - /// - /// The pool originating this share from - /// - string PoolId { get; set; } - - /// - /// Who mined it (wallet address) - /// - string Miner { get; } - - /// - /// Who mined it - /// - string Worker { get; } - - /// - /// Extra information for payout processing - /// - string PayoutInfo { get; set; } - - /// - /// Mining Software - /// - string UserAgent { get; } - - /// - /// From where was it submitted - /// - string IpAddress { get; } - - /// - /// Submission source (pool, external stratum etc) - /// - string Source { get; set; } - - /// - /// Stratum difficulty assigned to the miner at the time the share was submitted/accepted (used for payout - /// calculations) - /// - double Difficulty { get; set; } - - /// - /// Block this share refers to - /// - long BlockHeight { get; set; } - - /// - /// Block reward after deducting pool fee and donations - /// - decimal BlockReward { get; set; } - - /// - /// Block hash - /// - string BlockHash { get; set; } - - /// - /// If this share presumably resulted in a block - /// - bool IsBlockCandidate { get; set; } - - /// - /// Arbitrary data to be interpreted by the payment processor specialized - /// in this coin to verify this block candidate was accepted by the network - /// - string TransactionConfirmationData { get; set; } - - /// - /// Network difficulty at the time the share was submitted (used for some payout schemes like PPLNS) - /// - double NetworkDifficulty { get; set; } - - /// - /// When the share was found - /// - DateTime Created { get; set; } - } } diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs index 10824259d..33c9795a3 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs @@ -286,7 +286,7 @@ protected virtual byte[] SerializeHeader(byte[] coinbaseHash, uint nTime, uint n return blockHeader.ToBytes(); } - protected virtual BitcoinShare ProcessShareInternal(StratumClient worker, string extraNonce2, uint nTime, uint nonce) + protected virtual (Share Share, string BlockHex) ProcessShareInternal(StratumClient worker, string extraNonce2, uint nTime, uint nonce) { var context = worker.GetContextAs(); var extraNonce1 = context.ExtraNonce1; @@ -327,7 +327,7 @@ protected virtual BitcoinShare ProcessShareInternal(StratumClient worker, string throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})"); } - var result = new BitcoinShare + var result = new Share { BlockHeight = BlockTemplate.Height, BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC), @@ -335,16 +335,18 @@ protected virtual BitcoinShare ProcessShareInternal(StratumClient worker, string Difficulty = stratumDifficulty, }; - var blockBytes = SerializeBlock(headerBytes, coinbase); - if (isBlockCandidate) { result.IsBlockCandidate = true; - result.BlockHex = blockBytes.ToHexString(); result.BlockHash = blockHasher.Digest(headerBytes, nTime).ToHexString(); + + var blockBytes = SerializeBlock(headerBytes, coinbase); + var blockHex = blockBytes.ToHexString(); + + return (result, blockHex); } - return result; + return (result, null); } protected virtual byte[] SerializeCoinbase(string extraNonce1, string extraNonce2) @@ -476,7 +478,7 @@ public virtual object GetJobParams(bool isNew) return jobParams; } - public virtual BitcoinShare ProcessShare(StratumClient worker, + public virtual (Share Share, string BlockHex) ProcessShare(StratumClient worker, string extraNonce2, string nTime, string nonce) { Contract.RequiresNonNull(worker, nameof(worker)); diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 4c863f368..63244fa26 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -270,13 +270,13 @@ private async Task UpdateNetworkStatsAsync() BlockchainStats.ConnectedPeers = networkInfoResponse.Connections; } - protected virtual async Task<(bool Accepted, string CoinbaseTransaction)> SubmitBlockAsync(BitcoinShare share) + protected virtual async Task<(bool Accepted, string CoinbaseTransaction)> SubmitBlockAsync(Share share, string blockHex) { // execute command batch var results = await daemon.ExecuteBatchAnyAsync( hasSubmitBlockMethod - ? new DaemonCmd(BitcoinCommands.SubmitBlock, new[] { share.BlockHex }) - : new DaemonCmd(BitcoinCommands.GetBlockTemplate, new { mode = "submit", data = share.BlockHex }), + ? new DaemonCmd(BitcoinCommands.SubmitBlock, new[] { blockHex }) + : new DaemonCmd(BitcoinCommands.GetBlockTemplate, new { mode = "submit", data = blockHex }), new DaemonCmd(BitcoinCommands.GetBlock, new[] { share.BlockHash })); // did submission succeed? @@ -448,7 +448,7 @@ public string[] GetTransactions(StratumClient worker, object requestParams) return job.BlockTemplate.Transactions.Select(x => x.Data).ToArray(); } - public virtual async Task SubmitShareAsync(StratumClient worker, object submission, + public virtual async Task SubmitShareAsync(StratumClient worker, object submission, double stratumDifficultyBase) { Contract.RequiresNonNull(worker, nameof(worker)); @@ -487,14 +487,14 @@ public virtual async Task SubmitShareAsync(StratumClient worker, o var workerName = split.Length > 1 ? split[1] : null; // validate & process - var share = job.ProcessShare(worker, extraNonce2, nTime, nonce); + var (share, blockHex) = job.ProcessShare(worker, extraNonce2, nTime, nonce); // if block candidate, submit & check if accepted by network if (share.IsBlockCandidate) { logger.Info(() => $"[{LogCat}] Submitting block {share.BlockHeight} [{share.BlockHash}]"); - var acceptResponse = await SubmitBlockAsync(share); + var acceptResponse = await SubmitBlockAsync(share, blockHex); // is it still a block candidate? share.IsBlockCandidate = acceptResponse.Accepted; diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs index fa368b2c8..a7be6c34d 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs @@ -40,7 +40,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace MiningCore.Blockchain.Bitcoin { - public class BitcoinPoolBase : PoolBase + public class BitcoinPoolBase : PoolBase where TBlockTemplate : BlockTemplate where TJob : BitcoinJob, new() { diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinShare.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinShare.cs deleted file mode 100644 index d3bd3d317..000000000 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinShare.cs +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2017 Coin Foundry (coinfoundry.org) -Authors: Oliver Weichhold (oliver@weichhold.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -namespace MiningCore.Blockchain.Bitcoin -{ - public class BitcoinShare : ShareBase - { - public string BlockHex { get; set; } - } -} diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs index e6df3ac6b..5d263a308 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs @@ -49,7 +49,7 @@ private void RegisterNonce(StratumClient worker, string nonce) } } - public async Task ProcessShareAsync(StratumClient worker, string nonce, EthashFull ethash) + public async Task<(Share Share, string FullNonceHex, string HeaderHash, string MixHash)> ProcessShareAsync(StratumClient worker, string nonce, EthashFull ethash) { // duplicate nonce? lock(workerNonces) @@ -98,25 +98,30 @@ public async Task ProcessShareAsync(StratumClient worker, string } // create share - var share = new EthereumShare + var share = new Share { BlockHeight = (long) BlockTemplate.Height, IpAddress = worker.RemoteEndpoint?.Address?.ToString(), Miner = context.MinerName, Worker = context.WorkerName, UserAgent = context.UserAgent, - FullNonceHex = "0x" + fullNonceHex, - HeaderHash = BlockTemplate.Header, - MixHash = mixDigest.ToHexString(true), IsBlockCandidate = isBlockCandidate, Difficulty = stratumDifficulty * EthereumConstants.Pow2x32, BlockHash = mixDigest.ToHexString(true) // OW: is this correct? }; if (share.IsBlockCandidate) - share.TransactionConfirmationData = $"{mixDigest.ToHexString(true)}:{share.FullNonceHex}"; + { + fullNonceHex = "0x" + fullNonceHex; + var headerHash = BlockTemplate.Header; + var mixHash = mixDigest.ToHexString(true); + + share.TransactionConfirmationData = $"{mixDigest.ToHexString(true)}:{fullNonceHex}"; + + return (share, fullNonceHex, headerHash, mixHash); + } - return share; + return (share, null, null, null); } } } diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs index 862491b57..44cde25c8 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs @@ -291,14 +291,14 @@ private async Task UpdateNetworkStatsAsync() BlockchainStats.ConnectedPeers = peerCount; } - private async Task SubmitBlockAsync(EthereumShare share) + private async Task SubmitBlockAsync(Share share, string fullNonceHex, string headerHash, string mixHash) { // submit work var response = await daemon.ExecuteCmdAnyAsync(EC.SubmitWork, new[] { - share.FullNonceHex, - share.HeaderHash, - share.MixHash + fullNonceHex, + headerHash, + mixHash }); if (response.Error != null || (bool?) response.Response == false) @@ -393,7 +393,7 @@ public void PrepareWorker(StratumClient client) context.ExtraNonce1 = extraNonceProvider.Next(); } - public async Task SubmitShareAsync(StratumClient worker, + public async Task SubmitShareAsync(StratumClient worker, string[] request, double stratumDifficulty, double stratumDifficultyBase) { Contract.RequiresNonNull(worker, nameof(worker)); @@ -415,14 +415,14 @@ public async Task SubmitShareAsync(StratumClient worker, } // validate & process - var share = await job.ProcessShareAsync(worker, nonce, ethash); + var (share, fullNonceHex, headerHash, mixHash) = await job.ProcessShareAsync(worker, nonce, ethash); // if block candidate, submit & check if accepted by network if (share.IsBlockCandidate) { logger.Info(() => $"[{LogCat}] Submitting block {share.BlockHeight}"); - share.IsBlockCandidate = await SubmitBlockAsync(share); + share.IsBlockCandidate = await SubmitBlockAsync(share, fullNonceHex, headerHash, mixHash); if (share.IsBlockCandidate) { diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs index 57a11c1e4..d2bcd422f 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs @@ -42,7 +42,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace MiningCore.Blockchain.Ethereum { [CoinMetadata(CoinType.ETH, CoinType.ETC, CoinType.EXP, CoinType.ELLA)] - public class EthereumPool : PoolBase + public class EthereumPool : PoolBase { public EthereumPool(IComponentContext ctx, JsonSerializerSettings serializerSettings, diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumShare.cs b/src/MiningCore/Blockchain/Ethereum/EthereumShare.cs deleted file mode 100644 index 187a4f0e4..000000000 --- a/src/MiningCore/Blockchain/Ethereum/EthereumShare.cs +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2017 Coin Foundry (coinfoundry.org) -Authors: Oliver Weichhold (oliver@weichhold.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -namespace MiningCore.Blockchain.Ethereum -{ - public class EthereumShare : ShareBase - { - public string FullNonceHex { get; set; } - public string HeaderHash { get; set; } - public string MixHash { get; set; } - } -} diff --git a/src/MiningCore/Blockchain/Monero/MoneroJob.cs b/src/MiningCore/Blockchain/Monero/MoneroJob.cs index 6ea927106..ab5d4c2a8 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJob.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJob.cs @@ -19,10 +19,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; -using System.Buffers; -using System.Globalization; using System.Linq; -using Microsoft.AspNetCore.Server.Kestrel.Internal.System; using MiningCore.Blockchain.Monero.DaemonResponses; using MiningCore.Buffers; using MiningCore.Configuration; @@ -135,7 +132,7 @@ public void PrepareWorkerJob(MoneroWorkerJob workerJob, out string blob, out str target = EncodeTarget(workerJob.Difficulty); } - public MoneroShare ProcessShare(string nonce, uint workerExtraNonce, string workerHash, StratumClient worker) + public (Share Share, string BlobHex, string BlobHash) ProcessShare(string nonce, uint workerExtraNonce, string workerHash, StratumClient worker) { Contract.Requires(!string.IsNullOrEmpty(nonce), $"{nameof(nonce)} must not be empty"); Contract.Requires(!string.IsNullOrEmpty(workerHash), $"{nameof(workerHash)} must not be empty"); @@ -200,17 +197,18 @@ public MoneroShare ProcessShare(string nonce, uint workerExtraNonce, string work using (var blockHash = ComputeBlockHash(blobConverted)) { - var result = new MoneroShare + var result = new Share { BlockHeight = BlockTemplate.Height, IsBlockCandidate = isBlockCandidate, - BlobHex = blob.ToHexString(), - BlobHash = blockHash.ToHexString(), BlockHash = blockHash.ToHexString(), Difficulty = stratumDifficulty, }; - return result; + var blobHex = blob.ToHexString(); + var blobHash = blockHash.ToHexString(); + + return (result, blobHex, blobHash); } } } diff --git a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs index dbfef9a13..c0ebd892d 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs @@ -169,15 +169,15 @@ private async Task UpdateNetworkStatsAsync() BlockchainStats.ConnectedPeers = info.OutgoingConnectionsCount + info.IncomingConnectionsCount; } - private async Task SubmitBlockAsync(MoneroShare share) + private async Task SubmitBlockAsync(Share share, string blobHex, string blobHash) { - var response = await daemon.ExecuteCmdAnyAsync(MC.SubmitBlock, new[] { share.BlobHex }); + var response = await daemon.ExecuteCmdAnyAsync(MC.SubmitBlock, new[] { blobHex }); if (response.Error != null || response?.Response?.Status != "OK") { var error = response.Error?.Message ?? response.Response?.Status; - logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} [{share.BlobHash.Substring(0, 6)}] submission failed with: {error}"); + logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} [{blobHash.Substring(0, 6)}] submission failed with: {error}"); notificationService.NotifyAdmin("Block submission failed", $"Block {share.BlockHeight} submission failed with: {error}"); return false; @@ -258,7 +258,7 @@ public void PrepareWorkerJob(MoneroWorkerJob workerJob, out string blob, out str } } - public async Task SubmitShareAsync(StratumClient worker, + public async Task SubmitShareAsync(StratumClient worker, MoneroSubmitShareRequest request, MoneroWorkerJob workerJob, double stratumDifficultyBase) { Contract.RequiresNonNull(worker, nameof(worker)); @@ -272,20 +272,20 @@ public async Task SubmitShareAsync(StratumClient worker, throw new StratumException(StratumError.MinusOne, "block expired"); // validate & process - var share = job?.ProcessShare(request.Nonce, workerJob.ExtraNonce, request.Hash, worker); + var (share, blobHex, blobHash) = job.ProcessShare(request.Nonce, workerJob.ExtraNonce, request.Hash, worker); // if block candidate, submit & check if accepted by network if (share.IsBlockCandidate) { - logger.Info(() => $"[{LogCat}] Submitting block {share.BlockHeight} [{share.BlobHash.Substring(0, 6)}]"); + logger.Info(() => $"[{LogCat}] Submitting block {share.BlockHeight} [{blobHash.Substring(0, 6)}]"); - share.IsBlockCandidate = await SubmitBlockAsync(share); + share.IsBlockCandidate = await SubmitBlockAsync(share, blobHex, blobHash); if (share.IsBlockCandidate) { - logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{share.BlobHash.Substring(0, 6)}] submitted by {context.MinerName}"); + logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{blobHash.Substring(0, 6)}] submitted by {context.MinerName}"); - share.TransactionConfirmationData = share.BlobHash; + share.TransactionConfirmationData = blobHash; } else diff --git a/src/MiningCore/Blockchain/Monero/MoneroPool.cs b/src/MiningCore/Blockchain/Monero/MoneroPool.cs index 4be9ade41..b640c5050 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPool.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPool.cs @@ -43,7 +43,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace MiningCore.Blockchain.Monero { [CoinMetadata(CoinType.XMR, CoinType.AEON, CoinType.ETN)] - public class MoneroPool : PoolBase + public class MoneroPool : PoolBase { public MoneroPool(IComponentContext ctx, JsonSerializerSettings serializerSettings, diff --git a/src/MiningCore/Blockchain/Monero/MoneroShare.cs b/src/MiningCore/Blockchain/Monero/MoneroShare.cs deleted file mode 100644 index 7c35f14e1..000000000 --- a/src/MiningCore/Blockchain/Monero/MoneroShare.cs +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright 2017 Coin Foundry (coinfoundry.org) -Authors: Oliver Weichhold (oliver@weichhold.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -namespace MiningCore.Blockchain.Monero -{ - public class MoneroShare : ShareBase - { - public string BlobHex { get; set; } - public string BlobHash { get; set; } - } -} diff --git a/src/MiningCore/Blockchain/ShareBase.cs b/src/MiningCore/Blockchain/Share.cs similarity index 52% rename from src/MiningCore/Blockchain/ShareBase.cs rename to src/MiningCore/Blockchain/Share.cs index fd00a6582..7c065c0f1 100644 --- a/src/MiningCore/Blockchain/ShareBase.cs +++ b/src/MiningCore/Blockchain/Share.cs @@ -22,22 +22,83 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace MiningCore.Blockchain { - public class ShareBase : IShare + public class Share { + /// + /// The pool originating this share from + /// public string PoolId { get; set; } + + /// + /// Who mined it (wallet address) + /// public string Miner { get; set; } + + /// + /// Who mined it + /// public string Worker { get; set; } + + /// + /// Extra information for payout processing + /// public string PayoutInfo { get; set; } + + /// + /// Mining Software + /// public string UserAgent { get; set; } + + /// + /// From where was it submitted + /// public string IpAddress { get; set; } + + /// + /// Submission source (pool, external stratum etc) + /// public string Source { get; set; } + + /// + /// Stratum difficulty assigned to the miner at the time the share was submitted/accepted (used for payout + /// calculations) + /// public double Difficulty { get; set; } - public double NetworkDifficulty { get; set; } + + /// + /// Block this share refers to + /// public long BlockHeight { get; set; } + + /// + /// Block reward after deducting pool fee and donations + /// public decimal BlockReward { get; set; } + + /// + /// Block hash + /// public string BlockHash { get; set; } + + /// + /// If this share presumably resulted in a block + /// public bool IsBlockCandidate { get; set; } + + /// + /// Arbitrary data to be interpreted by the payment processor specialized + /// in this coin to verify this block candidate was accepted by the network + /// public string TransactionConfirmationData { get; set; } + + /// + /// Network difficulty at the time the share was submitted (used for some payout schemes like PPLNS) + /// + public double NetworkDifficulty { get; set; } + + /// + /// When the share was found + /// public DateTime Created { get; set; } } } diff --git a/src/MiningCore/Blockchain/ZCash/ZCashJob.cs b/src/MiningCore/Blockchain/ZCash/ZCashJob.cs index ad4bb386d..03ceb48ce 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashJob.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashJob.cs @@ -20,11 +20,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; -using System.Numerics; using MiningCore.Blockchain.Bitcoin; using MiningCore.Blockchain.ZCash.DaemonResponses; using MiningCore.Configuration; @@ -227,7 +225,7 @@ public override void Init(ZCashBlockTemplate blockTemplate, string jobId, #endregion - public override BitcoinShare ProcessShare(StratumClient worker, string extraNonce2, string nTime, string solution) + public override (Share Share, string BlockHex) ProcessShare(StratumClient worker, string extraNonce2, string nTime, string solution) { Contract.RequiresNonNull(worker, nameof(worker)); Contract.Requires(!string.IsNullOrEmpty(extraNonce2), $"{nameof(extraNonce2)} must not be empty"); @@ -295,7 +293,7 @@ protected byte[] SerializeBlock(byte[] header, byte[] coinbase, byte[] solution) } } - protected virtual BitcoinShare ProcessShareInternal(StratumClient worker, string nonce, + protected virtual (Share Share, string BlockHex) ProcessShareInternal(StratumClient worker, string nonce, uint nTime, string solution) { var context = worker.GetContextAs(); @@ -341,22 +339,22 @@ protected virtual BitcoinShare ProcessShareInternal(StratumClient worker, string throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})"); } - var result = new BitcoinShare + var result = new Share { BlockHeight = BlockTemplate.Height, IsBlockCandidate = isBlockCandidate, - Difficulty = stratumDifficulty + Difficulty = stratumDifficulty, + BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC) }; if (isBlockCandidate) { var blockBytes = SerializeBlock(headerBytes, coinbaseInitial, solutionBytes); - result.BlockHex = blockBytes.ToHexString(); - result.BlockHash = headerHashReversed.ToHexString(); - result.BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC); + var blockHex = blockBytes.ToHexString(); + return (result, blockHex); } - return result; + return (result, null); } protected bool RegisterSubmit(string nonce, string solution) diff --git a/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs b/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs index b0c7ca7f4..e091c411e 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs @@ -106,7 +106,7 @@ protected override IDestination AddressToDestination(string address) return result; } - public override async Task SubmitShareAsync(StratumClient worker, object submission, + public override async Task SubmitShareAsync(StratumClient worker, object submission, double stratumDifficultyBase) { Contract.RequiresNonNull(worker, nameof(worker)); @@ -148,14 +148,14 @@ public override async Task SubmitShareAsync(StratumClient worker, var workerName = split.Length > 1 ? split[1] : null; // validate & process - var share = job.ProcessShare(worker, extraNonce2, nTime, solution); + var (share, blockHex) = job.ProcessShare(worker, extraNonce2, nTime, solution); // if block candidate, submit & check if accepted by network if (share.IsBlockCandidate) { logger.Info(() => $"[{LogCat}] Submitting block {share.BlockHeight} [{share.BlockHash}]"); - var acceptResponse = await SubmitBlockAsync(share); + var acceptResponse = await SubmitBlockAsync(share, blockHex); // is it still a block candidate? share.IsBlockCandidate = acceptResponse.Accepted; diff --git a/src/MiningCore/Mining/Abstractions.cs b/src/MiningCore/Mining/Abstractions.cs index 686dde3a6..2264af804 100644 --- a/src/MiningCore/Mining/Abstractions.cs +++ b/src/MiningCore/Mining/Abstractions.cs @@ -28,14 +28,14 @@ namespace MiningCore.Mining { public struct ClientShare { - public ClientShare(StratumClient client, IShare share) + public ClientShare(StratumClient client, Share share) { Client = client; Share = share; } public StratumClient Client; - public IShare Share; + public Share Share; } public interface IMiningPool diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index 2342b47e4..f48d005b6 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -52,9 +52,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace MiningCore.Mining { - public abstract class PoolBase : StratumServer, + public abstract class PoolBase : StratumServer, IMiningPool - where TShare: IShare { protected PoolBase(IComponentContext ctx, JsonSerializerSettings serializerSettings, @@ -186,13 +185,13 @@ private void StartExternalStratumPublisherListeners() } // deserialize - TShare share; + Blockchain.Share share; using (var reader = new StringReader(data)) { using (var jreader = new JsonTextReader(reader)) { - share = serializer.Deserialize(jreader); + share = serializer.Deserialize(jreader); } } @@ -414,7 +413,7 @@ public virtual void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); - logger = LogUtil.GetPoolScopedLogger(typeof(PoolBase), poolConfig); + logger = LogUtil.GetPoolScopedLogger(typeof(PoolBase), poolConfig); this.poolConfig = poolConfig; this.clusterConfig = clusterConfig; } diff --git a/src/MiningCore/Payments/ShareRecorder.cs b/src/MiningCore/Payments/ShareRecorder.cs index 26257ce2c..0b2166d2a 100644 --- a/src/MiningCore/Payments/ShareRecorder.cs +++ b/src/MiningCore/Payments/ShareRecorder.cs @@ -28,9 +28,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Text; -using Autofac.Features.Metadata; using AutoMapper; -using MiningCore.Blockchain; using MiningCore.Configuration; using MiningCore.Extensions; using MiningCore.Mining; @@ -43,6 +41,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using Polly; using Polly.CircuitBreaker; using Contract = MiningCore.Contracts.Contract; +using Share = MiningCore.Blockchain.Share; namespace MiningCore.Payments { @@ -81,7 +80,7 @@ public ShareRecorder(IConnectionFactory cf, IMapper mapper, private readonly NotificationService notificationService; private ClusterConfig clusterConfig; private readonly IMapper mapper; - private readonly BlockingCollection queue = new BlockingCollection(); + private readonly BlockingCollection queue = new BlockingCollection(); private readonly int QueueSizeWarningThreshold = 1024; private readonly IShareRepository shareRepo; @@ -96,20 +95,20 @@ public ShareRecorder(IConnectionFactory cf, IMapper mapper, private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); - private void PersistSharesFaulTolerant(IList shares) + private void PersistSharesFaulTolerant(IList shares) { var context = new Dictionary { { PolicyContextKeyShares, shares } }; faultPolicy.Execute(() => { PersistShares(shares); }, context); } - private void PersistShares(IList shares) + private void PersistShares(IList shares) { cf.RunTx((con, tx) => { foreach(var share in shares) { - var shareEntity = mapper.Map(share); + var shareEntity = mapper.Map(share); shareRepo.Insert(con, tx, shareEntity); if (share.IsBlockCandidate) @@ -136,7 +135,7 @@ private void OnPolicyFallback(Exception ex, Context context) private void OnExecutePolicyFallback(Context context) { - var shares = (IList) context[PolicyContextKeyShares]; + var shares = (IList) context[PolicyContextKeyShares]; try { @@ -189,7 +188,7 @@ public void RecoverShares(ClusterConfig clusterConfig, string recoveryFilename) { using(var reader = new StreamReader(stream, new UTF8Encoding(false))) { - var shares = new List(); + var shares = new List(); var lastProgressUpdate = DateTime.UtcNow; while(!reader.EndOfStream) @@ -207,7 +206,7 @@ public void RecoverShares(ClusterConfig clusterConfig, string recoveryFilename) // parse try { - var share = JsonConvert.DeserializeObject(line, jsonSerializerSettings); + var share = JsonConvert.DeserializeObject(line, jsonSerializerSettings); shares.Add(share); } diff --git a/src/MiningCore/Payments/ShareRelay.cs b/src/MiningCore/Payments/ShareRelay.cs index 10e82644c..d62068789 100644 --- a/src/MiningCore/Payments/ShareRelay.cs +++ b/src/MiningCore/Payments/ShareRelay.cs @@ -1,10 +1,7 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; using System.Reactive.Concurrency; using System.Reactive.Linq; -using System.Text; using MiningCore.Blockchain; using MiningCore.Configuration; using MiningCore.Mining; @@ -23,7 +20,7 @@ public ShareRelay(JsonSerializerSettings serializerSettings) } private ClusterConfig clusterConfig; - private readonly BlockingCollection queue = new BlockingCollection(); + private readonly BlockingCollection queue = new BlockingCollection(); private IDisposable queueSub; private readonly int QueueSizeWarningThreshold = 1024; private bool hasWarnedAboutBacklogSize; From 7f35ceae9017afbdd2297b335ff409c1e3319d5c Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 14 Mar 2018 20:42:43 +0100 Subject: [PATCH 168/348] Moved ShareRecorder and ShareRelay to more fitting namespace --- src/MiningCore/{Payments => Mining}/ShareRecorder.cs | 3 +-- src/MiningCore/{Payments => Mining}/ShareRelay.cs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) rename src/MiningCore/{Payments => Mining}/ShareRecorder.cs (97%) rename src/MiningCore/{Payments => Mining}/ShareRelay.cs (95%) diff --git a/src/MiningCore/Payments/ShareRecorder.cs b/src/MiningCore/Mining/ShareRecorder.cs similarity index 97% rename from src/MiningCore/Payments/ShareRecorder.cs rename to src/MiningCore/Mining/ShareRecorder.cs index 0b2166d2a..489c14366 100644 --- a/src/MiningCore/Payments/ShareRecorder.cs +++ b/src/MiningCore/Mining/ShareRecorder.cs @@ -31,7 +31,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using AutoMapper; using MiningCore.Configuration; using MiningCore.Extensions; -using MiningCore.Mining; using MiningCore.Notifications; using MiningCore.Persistence; using MiningCore.Persistence.Model; @@ -43,7 +42,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using Contract = MiningCore.Contracts.Contract; using Share = MiningCore.Blockchain.Share; -namespace MiningCore.Payments +namespace MiningCore.Mining { /// /// Asynchronously persist shares produced by all pools for processing by coin-specific payment processor(s) diff --git a/src/MiningCore/Payments/ShareRelay.cs b/src/MiningCore/Mining/ShareRelay.cs similarity index 95% rename from src/MiningCore/Payments/ShareRelay.cs rename to src/MiningCore/Mining/ShareRelay.cs index d62068789..2aadfaafe 100644 --- a/src/MiningCore/Payments/ShareRelay.cs +++ b/src/MiningCore/Mining/ShareRelay.cs @@ -4,13 +4,12 @@ using System.Reactive.Linq; using MiningCore.Blockchain; using MiningCore.Configuration; -using MiningCore.Mining; using NetMQ; using NetMQ.Sockets; using Newtonsoft.Json; using NLog; -namespace MiningCore.Payments +namespace MiningCore.Mining { public class ShareRelay { From 318d3a1a175c6f1752ed3bc39fbd6892abfcc933 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Wed, 14 Mar 2018 21:40:01 +0100 Subject: [PATCH 169/348] Greatly reduce the number of threads for external stratum monitoring --- src/MiningCore/Mining/PoolBase.cs | 104 -------------------- src/MiningCore/Mining/ShareRecorder.cs | 131 +++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 104 deletions(-) diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index f48d005b6..b5125028c 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -138,107 +138,6 @@ private void EnsureNoZombieClient(StratumClient client) }); } - private void StartExternalStratumPublisherListeners() - { - foreach (var externalStratum in poolConfig.ExternalStratums) - { - var thread = new Thread(arg => - { - var serializer = new JsonSerializer - { - ContractResolver = new CamelCasePropertyNamesContractResolver() - }; - - var currentHeight = 0L; - var lastBlockTime = clock.Now; - var config = (ZmqPubSubEndpointConfig) arg; - - while (true) - { - try - { - using (var subSocket = new SubscriberSocket()) - { - //subSocket.Options.ReceiveHighWatermark = 1000; - subSocket.Connect(config.Url); - subSocket.Subscribe(config.Topic); - - logger.Info($"{LogCat}] Monitoring external stratum {config.Url}/{config.Topic}"); - - while (true) - { - var msg = subSocket.ReceiveMultipartMessage(2); - var topic = msg.Pop().ConvertToString(Encoding.UTF8); - var data = msg.Pop().ConvertToString(Encoding.UTF8); - - // validate - if (topic != config.Topic) - { - logger.Warn(() => $"{LogCat}] Received non-matching topic {topic} on ZeroMQ subscriber socket"); - continue; - } - - if (string.IsNullOrEmpty(data)) - { - logger.Warn(() => $"{LogCat}] Received empty data on ZeroMQ subscriber socket"); - continue; - } - - // deserialize - Blockchain.Share share; - - using (var reader = new StringReader(data)) - { - using (var jreader = new JsonTextReader(reader)) - { - share = serializer.Deserialize(jreader); - } - } - - if (share == null) - { - logger.Error(() => $"{LogCat}] Unable to deserialize share received from ZeroMQ subscriber socket"); - continue; - } - - // update network stats - blockchainStats.BlockHeight = share.BlockHeight; - blockchainStats.NetworkDifficulty = share.NetworkDifficulty; - - if (currentHeight != share.BlockHeight) - { - blockchainStats.LastNetworkBlockTime = clock.Now; - currentHeight = share.BlockHeight; - lastBlockTime = clock.Now; - } - - else - blockchainStats.LastNetworkBlockTime = lastBlockTime; - - // fill in the blacks - share.PoolId = poolConfig.Id; - share.Created = clock.Now; - - // re-publish - shareSubject.OnNext(new ClientShare(null, share)); - - var source = !string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}]" : string.Empty; - logger.Info(() => $"[{LogCat}] External {source} share accepted: D={Math.Round(share.Difficulty, 3)}"); - } - } - } - - catch (Exception ex) - { - logger.Error(ex); - } - } - }) {Name = $"{poolConfig.Id} external stratum listener"}; - - thread.Start(externalStratum); - } - } - #region VarDiff protected void UpdateVarDiff(StratumClient client, bool isIdleUpdate = false) @@ -441,9 +340,6 @@ public virtual async Task StartAsync() StartListeners(poolConfig.Id, ipEndpoints); } - if(poolConfig.ExternalStratums?.Length > 0) - StartExternalStratumPublisherListeners(); - logger.Info(() => $"[{LogCat}] Online"); OutputPoolInfo(); } diff --git a/src/MiningCore/Mining/ShareRecorder.cs b/src/MiningCore/Mining/ShareRecorder.cs index 489c14366..a5442e0a8 100644 --- a/src/MiningCore/Mining/ShareRecorder.cs +++ b/src/MiningCore/Mining/ShareRecorder.cs @@ -28,6 +28,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Text; +using System.Threading; using AutoMapper; using MiningCore.Configuration; using MiningCore.Extensions; @@ -35,8 +36,13 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Persistence; using MiningCore.Persistence.Model; using MiningCore.Persistence.Repositories; +using MiningCore.Time; +using NetMQ; +using NetMQ.Sockets; using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; using NLog; +using Org.BouncyCastle.Utilities.Collections; using Polly; using Polly.CircuitBreaker; using Contract = MiningCore.Contracts.Contract; @@ -52,6 +58,7 @@ public class ShareRecorder public ShareRecorder(IConnectionFactory cf, IMapper mapper, JsonSerializerSettings jsonSerializerSettings, IShareRepository shareRepo, IBlockRepository blockRepo, + IMasterClock clock, NotificationService notificationService) { Contract.RequiresNonNull(cf, nameof(cf)); @@ -59,11 +66,13 @@ public ShareRecorder(IConnectionFactory cf, IMapper mapper, Contract.RequiresNonNull(shareRepo, nameof(shareRepo)); Contract.RequiresNonNull(blockRepo, nameof(blockRepo)); Contract.RequiresNonNull(jsonSerializerSettings, nameof(jsonSerializerSettings)); + Contract.RequiresNonNull(clock, nameof(clock)); Contract.RequiresNonNull(notificationService, nameof(notificationService)); this.cf = cf; this.mapper = mapper; this.jsonSerializerSettings = jsonSerializerSettings; + this.clock = clock; this.notificationService = notificationService; this.shareRepo = shareRepo; @@ -76,9 +85,11 @@ public ShareRecorder(IConnectionFactory cf, IMapper mapper, private readonly IConnectionFactory cf; private readonly JsonSerializerSettings jsonSerializerSettings; + private readonly IMasterClock clock; private readonly NotificationService notificationService; private ClusterConfig clusterConfig; private readonly IMapper mapper; + private readonly ConcurrentDictionary pools = new ConcurrentDictionary(); private readonly BlockingCollection queue = new BlockingCollection(); private readonly int QueueSizeWarningThreshold = 1024; @@ -287,10 +298,127 @@ private void NotifyAdminOnPolicyFallback() } } + private void StartExternalStratumPublisherListeners() + { + var stratumsByUrl = clusterConfig.Pools.Where(x => x.ExternalStratums?.Any() == true) + .SelectMany(x => x.ExternalStratums) + .GroupBy(x => x.Url, x=> x.Topic); + + var serializer = new JsonSerializer + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + + foreach (var item in stratumsByUrl) + { + var thread = new Thread(arg => + { + var urlAndTopic = (IGrouping) arg; + var url = urlAndTopic.Key; + var topics = new HashSet(urlAndTopic.Distinct()); + + var currentHeight = 0L; + var lastBlockTime = clock.Now; + + while (true) + { + try + { + using (var subSocket = new SubscriberSocket()) + { + //subSocket.Options.ReceiveHighWatermark = 1000; + subSocket.Connect(url); + + foreach(var topic in topics) + { + subSocket.Subscribe(topic); + logger.Info($"Monitoring external stratum {url}/{topic}"); + } + + while (true) + { + var msg = subSocket.ReceiveMultipartMessage(2); + var topic = msg.Pop().ConvertToString(Encoding.UTF8); + var data = msg.Pop().ConvertToString(Encoding.UTF8); + + // validate + if (topics.Contains(topic)) + { + logger.Warn(() => $"Received non-matching topic {topic} on ZeroMQ subscriber socket"); + continue; + } + + if (string.IsNullOrEmpty(data)) + { + logger.Warn(() => $"Received empty data from {url}/{topic}"); + continue; + } + + // deserialize + Share share; + + using (var reader = new StringReader(data)) + { + using (var jreader = new JsonTextReader(reader)) + { + share = serializer.Deserialize(jreader); + } + } + + if (share == null) + { + logger.Error(() => $"Unable to deserialize share received from {url}/{topic}"); + continue; + } + + // fill in blanks + share.PoolId = topic; + share.Created = clock.Now; + + // persist + queue.Add(share); + + // log it + var source = !string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}]" : string.Empty; + logger.Info(() => $"[{share.PoolId}] External {source} share accepted: D={Math.Round(share.Difficulty, 3)}"); + + // update pool stats + if (pools.TryGetValue(topic, out var pool) && pool.NetworkStats != null) + { + pool.NetworkStats.BlockHeight = share.BlockHeight; + pool.NetworkStats.NetworkDifficulty = share.NetworkDifficulty; + + if (currentHeight != share.BlockHeight) + { + pool.NetworkStats.LastNetworkBlockTime = clock.Now; + currentHeight = share.BlockHeight; + lastBlockTime = clock.Now; + } + + else + pool.NetworkStats.LastNetworkBlockTime = lastBlockTime; + } + } + } + } + + catch (Exception ex) + { + logger.Error(ex); + } + } + }); + + thread.Start(item); + } + } + #region API-Surface public void AttachPool(IMiningPool pool) { + pools[pool.Config.Id] = pool; + pool.Shares.Subscribe(x => { queue.Add(x.Share); }); } @@ -301,6 +429,9 @@ public void Start(ClusterConfig clusterConfig) ConfigureRecovery(); InitializeQueue(); + if (clusterConfig.Pools.Any(x=> x.ExternalStratums?.Length > 0)) + StartExternalStratumPublisherListeners(); + logger.Info(() => "Online"); } From 098ac86e99e1d1f831fa9d3d05e16d5f2a9ea98f Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 07:10:45 +0100 Subject: [PATCH 170/348] WIP --- src/MiningCore/Mining/ShareRecorder.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/MiningCore/Mining/ShareRecorder.cs b/src/MiningCore/Mining/ShareRecorder.cs index a5442e0a8..af67d6fc8 100644 --- a/src/MiningCore/Mining/ShareRecorder.cs +++ b/src/MiningCore/Mining/ShareRecorder.cs @@ -428,9 +428,7 @@ public void Start(ClusterConfig clusterConfig) ConfigureRecovery(); InitializeQueue(); - - if (clusterConfig.Pools.Any(x=> x.ExternalStratums?.Length > 0)) - StartExternalStratumPublisherListeners(); + StartExternalStratumPublisherListeners(); logger.Info(() => "Online"); } From 6648095d1520ec058a7f6e9306db3ec48384a7f3 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 07:38:19 +0100 Subject: [PATCH 171/348] WIP --- src/MiningCore/Mining/ShareRecorder.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/MiningCore/Mining/ShareRecorder.cs b/src/MiningCore/Mining/ShareRecorder.cs index af67d6fc8..f5cb57144 100644 --- a/src/MiningCore/Mining/ShareRecorder.cs +++ b/src/MiningCore/Mining/ShareRecorder.cs @@ -302,7 +302,12 @@ private void StartExternalStratumPublisherListeners() { var stratumsByUrl = clusterConfig.Pools.Where(x => x.ExternalStratums?.Any() == true) .SelectMany(x => x.ExternalStratums) - .GroupBy(x => x.Url, x=> x.Topic); + .Where(x => x.Url != null && x.Topic != null) + .GroupBy(x => + { + var tmp = x.Url.Trim(); + return !tmp.EndsWith("/") ? tmp : tmp.Substring(0, tmp.Length - 1); + }, x=> x.Topic.Trim()); var serializer = new JsonSerializer { From f6f283cfe9402691c6e4cdae7668cf488979784c Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 08:32:46 +0100 Subject: [PATCH 172/348] Verge algo adjustments --- .../Blockchain/Bitcoin/BitcoinProperties.cs | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index d6072df5c..5da99f9ce 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -79,13 +79,22 @@ public class BitcoinProperties new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, neoScryptProfile1, new DigestReverser(neoScryptProfile1), "Neoscrypt"); private static readonly BitcoinCoinProperties vergeLyraCoin = - new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, new DigestReverser(scrypt_1024_1), "Lyra2re2"); + new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, new DigestReverser(scrypt_1024_1), "Lyra2re2", new DigestReverser(scrypt_1024_1)); + + private static readonly BitcoinCoinProperties vergeScryptCoin = + new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, scrypt_1024_1, sha256DReverse, "Scrypt", new DigestReverser(scrypt_1024_1)); private static readonly BitcoinCoinProperties vergeBlake2sCoin = - new BitcoinCoinProperties(1, sha256D, blake2s, new DigestReverser(scrypt_1024_1), "Blake2s"); + new BitcoinCoinProperties(1, sha256D, blake2s, new DigestReverser(scrypt_1024_1), "Blake2s", new DigestReverser(scrypt_1024_1)); private static readonly BitcoinCoinProperties vergeX17Coin = - new BitcoinCoinProperties(1, x17, blake2s, new DigestReverser(scrypt_1024_1), "X17"); + new BitcoinCoinProperties(1, x17, blake2s, new DigestReverser(scrypt_1024_1), "X17", new DigestReverser(scrypt_1024_1)); + + private static readonly BitcoinCoinProperties vergeSkeinCoin = + new BitcoinCoinProperties(1, sha256D, skein, sha256DReverse, "Skein", new DigestReverser(scrypt_1024_1)); + + private static readonly BitcoinCoinProperties vergeQubitCoin = + new BitcoinCoinProperties(1, sha256D, qubit, sha256DReverse, "Qubit", new DigestReverser(scrypt_1024_1)); private static readonly BitcoinCoinProperties vergeGroestlCoin = new BitcoinCoinProperties(1, groestlMyriad, blake2s, new DigestReverser(scrypt_1024_1), "Groestl-Myriad"); @@ -152,17 +161,17 @@ private static BitcoinCoinProperties GetDigiByteProperties(string algorithm) return sha256Coin; case "skein": - return skeinCoin; + return vergeSkeinCoin; case "qubit": - return qubitCoin; + return vergeQubitCoin; case "groestl": case "groestl-myriad": return groestlMyriadCoin; default: // scrypt - return scryptCoin; + return vergeScryptCoin; } } From b5b166134efcf0559f8386e902c35f0f15f4e67b Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 08:56:05 +0100 Subject: [PATCH 173/348] WIP --- .../Blockchain/Bitcoin/BitcoinProperties.cs | 14 +- src/MiningCore/config2.json | 734 ++++++++++++++++++ 2 files changed, 741 insertions(+), 7 deletions(-) create mode 100644 src/MiningCore/config2.json diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index 5da99f9ce..ff8527c4e 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -79,25 +79,25 @@ public class BitcoinProperties new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, neoScryptProfile1, new DigestReverser(neoScryptProfile1), "Neoscrypt"); private static readonly BitcoinCoinProperties vergeLyraCoin = - new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, new DigestReverser(scrypt_1024_1), "Lyra2re2", new DigestReverser(scrypt_1024_1)); + new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, new DigestReverser(scrypt_1024_1), "Lyra2re2"); private static readonly BitcoinCoinProperties vergeScryptCoin = - new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, scrypt_1024_1, sha256DReverse, "Scrypt", new DigestReverser(scrypt_1024_1)); + new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, scrypt_1024_1, sha256DReverse, "Scrypt"); private static readonly BitcoinCoinProperties vergeBlake2sCoin = - new BitcoinCoinProperties(1, sha256D, blake2s, new DigestReverser(scrypt_1024_1), "Blake2s", new DigestReverser(scrypt_1024_1)); + new BitcoinCoinProperties(1, sha256D, blake2s, new DigestReverser(scrypt_1024_1), "Blake2s"); private static readonly BitcoinCoinProperties vergeX17Coin = - new BitcoinCoinProperties(1, x17, blake2s, new DigestReverser(scrypt_1024_1), "X17", new DigestReverser(scrypt_1024_1)); + new BitcoinCoinProperties(1, sha256D, x17, new DigestReverser(scrypt_1024_1), "X17"); private static readonly BitcoinCoinProperties vergeSkeinCoin = - new BitcoinCoinProperties(1, sha256D, skein, sha256DReverse, "Skein", new DigestReverser(scrypt_1024_1)); + new BitcoinCoinProperties(1, sha256D, skein, new DigestReverser(scrypt_1024_1), "Skein"); private static readonly BitcoinCoinProperties vergeQubitCoin = - new BitcoinCoinProperties(1, sha256D, qubit, sha256DReverse, "Qubit", new DigestReverser(scrypt_1024_1)); + new BitcoinCoinProperties(1, sha256D, qubit, new DigestReverser(scrypt_1024_1), "Qubit"); private static readonly BitcoinCoinProperties vergeGroestlCoin = - new BitcoinCoinProperties(1, groestlMyriad, blake2s, new DigestReverser(scrypt_1024_1), "Groestl-Myriad"); + new BitcoinCoinProperties(1, sha256D, groestlMyriad, new DigestReverser(scrypt_1024_1), "Groestl-Myriad"); private static readonly Dictionary coinProperties = new Dictionary { diff --git a/src/MiningCore/config2.json b/src/MiningCore/config2.json new file mode 100644 index 000000000..f4d13450a --- /dev/null +++ b/src/MiningCore/config2.json @@ -0,0 +1,734 @@ +{ + "logging": { + "level": "info", + "enableConsoleLog": true, + "enableConsoleColors": true, + "logFile": "", + "logBaseDirectory": "", + "perPoolLogFile": false + }, + "shareRelay": { + "publishUrl": "tcp://0.0.0.0:6000" + }, + "banning": { + "manager": "integrated" + }, + "notifications": { + "enabled": true, + "email": { + "host": "smtp.example.com", + "port": 587, + "user": "user", + "password": "password", + "fromAddress": "info@poolmining.org", + "fromName": "poolmining support" + }, + "admin": { + "enabled": false, + "emailAddress": "user@example.com", + "notifyBlockFound": true + } + }, + "persistence": { + "postgres": { + "host": "127.0.0.1", + "port": 5432, + "user": "miningcore", + "password": "Qx6SJcOOlfVyoR66nZH1", + "database": "miningcore" + } + }, + "equihashMaxThreads": 4, + "paymentProcessing": { + "enabled": true, + "interval": 600, + "shareRecoveryFile": "recovered-shares.txt" + }, + "pools": [ + // { + // "id": "mona1", + // "enabled": true, + // "coin": { + // "type": "MONA" + // }, + // "address": "n3E5XwMhQnGKqxfhJBFP33ae6r2uhZy9qX", + // "rewardRecipients": [ + // { + // "type": "op", + // "address": "n3E5XwMhQnGKqxfhJBFP33ae6r2uhZy9qX", + // "percentage": 1.5 + // } + // ], + // "blockRefreshInterval": 1000, + // "jobRebroadcastTimeout": 55, + // "clientConnectionTimeout": 600, + // "banning": { + // "enabled": true, + // "time": 600, + // "invalidPercent": 50, + // "checkThreshold": 50 + // }, + // "ports": { + // "3092": { + // "difficulty": 2, + // "varDiff": { + // "minDiff": 0.01, + // "maxDiff": null, + // "targetTime": 15, + // "retargetTime": 90, + // "variancePercent": 30 + // } + // }, + // "3093": { + // "difficulty": 1 + // } + // }, + // "daemons": [ + // { + // "host": "127.0.0.1", + // "port": 9402, + // "user": "5d172d7b63e8c226db9595e67a949636", + // "password": "c6115cea1465d5c37bf59e1c5446183e" + // } + // ], + // "paymentProcessing": { + // "enabled": true, + // "minimumPayment": 0.01, + // "payoutScheme": "PPLNS", + // "payoutSchemeConfig": { + // "factor": 2.0 + // } + // } + // }, + // { + // "id": "zec1", + // "enabled": true, + // "coin": { + // "type": "ZEC" + // }, + // "address": "tmVcjHqYUrNMB3YFbBhUPJj4AnW4r8ErBJH", + // "rewardRecipients": [ + // { + // "type": "op", + // "address": "tmVcjHqYUrNMB3YFbBhUPJj4AnW4r8ErBJH", + // "percentage": 1.5 + // } + // ], + // "blockRefreshInterval": 1000, + // "jobRebroadcastTimeout": 55, + // "clientConnectionTimeout": 600, + // "banning": { + // "enabled": true, + // "time": 600, + // "invalidPercent": 50, + // "checkThreshold": 50 + // }, + // "ports": { + // "3092": { + // "difficulty": 0.1, + // "varDiff": { + // "minDiff": 0.01, + // "maxDiff": null, + // "targetTime": 15, + // "retargetTime": 90, + // "variancePercent": 30 + // } + // }, + // "3093": { + // "difficulty": 1 + // } + // }, + // "daemons": [ + // { + // "host": "127.0.0.1", + // "port": 8232, + // "user": "user", + // "password": "pass" + // } + // ], + // "paymentProcessing": { + // "enabled": true, + // "minimumPayment": 0.01, + // "payoutScheme": "PPLNS", + // "payoutSchemeConfig": { + // "factor": 2.0 + // } + // } + // }, + // { + // "id": "btcp1", + // "enabled": true, + // "coin": { + // "type": "BTCP" + // }, + // "address": "n1e1mZg5k8UgrpCASCk3jyxU6gGFRdYV4yY", + // "z-address": "zzBzEwPBZ3T6fcYN1QQdnh1Ytnp9g71ezAPtnCFVJ6XRGStpdZtsMX9hnzCPESQkTqcJyGEWZ9PrJAKs8SjyWtNLCmBEyih", + // "rewardRecipients": [ + // { + // "type": "op", + // "address": "n1e1mZg5k8UgrpCASCk3jyxU6gGFRdYV4yY", + // "percentage": 1.5 + // } + // ], + // "blockRefreshInterval": 1000, + // "jobRebroadcastTimeout": 55, + // "clientConnectionTimeout": 600, + // "banning": { + // "enabled": true, + // "time": 600, + // "invalidPercent": 50, + // "checkThreshold": 50 + // }, + // "ports": { + // "3092": { + // "difficulty": 0.1, + // "varDiff": { + // "minDiff": 0.01, + // "maxDiff": null, + // "targetTime": 15, + // "retargetTime": 90, + // "variancePercent": 30 + // } + // }, + // "3093": { + // "difficulty": 1 + // } + // }, + // "daemons": [ + // { + // "host": "127.0.0.1", + // "port": 7932, + // "user": "user", + // "password": "pass" + // } + // ], + // "paymentProcessing": { + // "enabled": true, + // "minimumPayment": 0.01, + // "payoutScheme": "PPLNS", + // "payoutSchemeConfig": { + // "factor": 2.0 + // } + // } + // }, + // { + // "id": "eth1", + // "enabled": true, + // "coin": { + // "type": "ETH" + // }, + // "address": "0x0942e9144606ad43f2e61a7ee332fe9914424712", + // "rewardRecipients": [ + // { + // "type": "op", + // "address": "0x0942e9144606ad43f2e61a7ee332fe9914424712", + // "percentage": 0 + // } + // ], + // "blockRefreshInterval": 1000, + // "jobRebroadcastTimeout": 55, + // "clientConnectionTimeout": 600, + // "banning": { + // "enabled": true, + // "time": 600, + // "invalidPercent": 50, + // "checkThreshold": 50 + // }, + // "ports": { + // "3072": { + // "difficulty": 0.1, + // "varDiff": { + // "minDiff": 0.05, + // "maxDiff": null, + // "targetTime": 15, + // "retargetTime": 90, + // "variancePercent": 30 + // } + // }, + // "3073": { + // "difficulty": 5000 + // } + // }, + // "daemons": [ + // { + // "host": "127.0.0.1", + // "port": 8545, + // "user": "user", + // "password": "password" + // } + // ], + // "paymentProcessing": { + // "enabled": true, + // "minimumPayment": 0.01, + // "minimumPaymentToPaymentId": 5.0, + // "payoutScheme": "PPLNS", + // "payoutSchemeConfig": { + // "factor": 2.0 + // }, + // "coinbasePassword": "", + // "keepUncles": false, + // "keepTransactionFees": false + // } + // }, + { + "id": "xmr1", + "enabled": true, + "coin": { + "type": "XMR" + }, + "address": "9wviCeWe2D8XS82k2ovp5EUYLzBt9pYNW2LXUFsZiv8S3Mt21FZ5qQaAroko1enzw3eGr9qC7X1D7Geoo2RrAotYPwq9Gm8", + "rewardRecipients": [ + { + "type": "op", + "address": "9wviCeWe2D8XS82k2ovp5EUYLzBt9pYNW2LXUFsZiv8S3Mt21FZ5qQaAroko1enzw3eGr9qC7X1D7Geoo2RrAotYPwq9Gm8", + "percentage": 0 + } + ], + "blockRefreshInterval": 1000, + "jobRebroadcastTimeout": 55, + "clientConnectionTimeout": 600, + "banning": { + "enabled": true, + "time": 600, + "invalidPercent": 50, + "checkThreshold": 50 + }, + // "externalStratums": [{ + // "url": "tcp://localhost:13333", + // "topic": "xmr1" + // }], + "ports": { + "4032": { + "difficulty": 1000, + "varDiff": { + "minDiff": 750, + "targetTime": 5, + "retargetTime": 45, + "variancePercent": 30 + } + }, + "4256": { + "difficulty": 750 + } + }, + "daemons": [ + { + "host": "127.0.0.1", + "port": 28081, + "user": "monerorpc", + "password": "rpcpassword" + }, + { + "host": "127.0.0.1", + "port": 28082, + "user": "monerorpc", + "password": "rpcpassword", + "category": "wallet" + } + ], + "paymentProcessing": { + "enabled": true, + "minimumPayment": 0.01, + "minimumPaymentToPaymentId": 5.0, + "payoutScheme": "PPLNS", + "payoutSchemeConfig": { + "factor": 2.0 + }, + "coinbasePassword": 1 + } + }, + // { + // "id": "ltc1", + // "enabled": true, + // "coin": { + // "type": "LTC" + // }, + // "address": "mwkc7fx4UWBDAjnpiGaHmkWh9RYiNZzsvN", + // "rewardRecipients": [ + // { + // "type": "op", + // "address": "mwkc7fx4UWBDAjnpiGaHmkWh9RYiNZzsvN", + // "percentage": 1.5 + // } + // ], + // "blockRefreshInterval": 1000, + // "jobRebroadcastTimeout": 55, + // "clientConnectionTimeout": 600, + // "banning": { + // "enabled": true, + // "time": 600, + // "invalidPercent": 50, + // "checkThreshold": 50 + // }, + // "ports": { + // "3042": { + // "difficulty": 20, + // "varDiff": { + // "minDiff": 10, + // "targetTime": 15, + // "retargetTime": 90, + // "variancePercent": 30 + // } + // }, + // "3043": { + // "difficulty": 100 + // } + // }, + // "daemons": [ + // { + // "host": "127.0.0.1", + // "port": 14001, + // "user": "user", + // "password": "pass" + // } + // ], + // "paymentProcessing": { + // "enabled": true, + // "minimumPayment": 0.01, + // "payoutScheme": "PPLNS", + // "payoutSchemeConfig": { + // "factor": 2.0 + // } + // } + // }, + // { + // "id": "btc1", + // "enabled": true, + // "coin": { + // "type": "BTC" + // }, + // "address": "myhrXHY9pPLS8wcsCrdpg4FxVEEaXRM3YU", + // "rewardRecipients": [ + // { + // "type": "op", + // "address": "myhrXHY9pPLS8wcsCrdpg4FxVEEaXRM3YU", + // "percentage": 1.5 + // } + // ], + // "hasLegacyDaemon": false, + // "blockRefreshInterval": 1000, + // "jobRebroadcastTimeout": 55, + // "clientConnectionTimeout": 600, + // "banning": { + // "enabled": true, + // "time": 600, + // "invalidPercent": 50, + // "checkThreshold": 50 + // }, + // "ports": { + // "3052": { + // "difficulty": 0.05, + // "varDiff": { + // "minDiff": 0.01, + // "maxDiff": null, + // "targetTime": 15, + // "retargetTime": 90, + // "variancePercent": 30 + // } + // }, + // "3053": { + // "difficulty": 100 + // } + // }, + // "daemons": [ + // { + // "host": "127.0.0.1", + // "port": 16001, + // "user": "user", + // "password": "pass", + // "zmqBlockNotifySocket": "tcp://127.0.0.1:26001" + // } + // ], + // "paymentProcessing": { + // "enabled": true, + // "minimumPayment": 0.01, + // "blockrewardMultiplier": 100, + // "payoutScheme": "PPLNS", + // "payoutSchemeConfig": { + // "factor": 2.0 + // } + // } + // }, + // { + // "id": "xvg1", + // "enabled": true, + // "coin": { + // "type": "XVG", + // "algorithm": "lyra" + // }, + // "address": "D5Y6MtvsrtdAWUM3zv9RvR4mt8c1MUuHKX", + // "rewardRecipients": [ + // { + // "address": "D5Y6MtvsrtdAWUM3zv9RvR4mt8c1MUuHKX", + // "percentage": 0.3 + // }, + // { + // "address": "DGWUdCL1XwaE2WTSJg8hgkQkXQW1VMmiWC", + // "percentage": 1.5 + // } + // ], + // "slackNotifications": { + // "enabled": true, + // "webHookUrl": "https://hooks.slack.com/services/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", + // "channel": "#verge", + // "notifyBlockFound": true, + // "notifyPaymentSuccess": true, + // "blockFoundUsername": "Block Notification", + // "paymentSuccessUsername": "Payment Notification" + // }, + // "hasLegacyDaemon": true, + // "maxActiveJobs": 20, + // "blockRefreshInterval": 500, + // "jobRebroadcastTimeout": 4, + // "clientConnectionTimeout": 600, + // "banning": { + // "enabled": true, + // "time": 600, + // "invalidPercent": 90, + // "checkThreshold": 150 + // }, + // "ports": { + // "3162": { + // "listenAddress": "0.0.0.0", + // "difficulty": 10, + // "name": "GPU Mining", + // "varDiff": { + // "minDiff": 20, + // "targetTime": 15, + // "retargetTime": 90, + // "variancePercent": 30, + // "maxDelta": 10 + // } + // }, + // "3163": { + // "listenAddress": "0.0.0.0", + // "difficulty": 100, + // "name": "High-End Multi GPU Mining", + // "varDiff": { + // "minDiff": 20, + // "targetTime": 15, + // "retargetTime": 90, + // "variancePercent": 30, + // "maxDelta": 10 + // } + // } + // }, + // "daemons": [ + // { + // "host": "heimdall", + // "port": 20102, + // "user": "ecVjoZHwSU26z7dlGMES", + // "password": "6e8c2ebc63748c71183c0188b91ba55b" + // } + // ], + // "paymentProcessing": { + // "enabled": true, + // "minersPayTxFees": true, + // "minimumPayment": 0.1, + // "payoutScheme": "PPLNS", + // "payoutSchemeConfig": { + // "factor": 1.0 + // } + // } + // }, + // { + // "id": "xvg2", + // "enabled": true, + // "coin": { + // "type": "XVG", + // "algorithm": "blake" + // }, + // "address": "D5Y6MtvsrtdAWUM3zv9RvR4mt8c1MUuHKX", + // "rewardRecipients": [ + // { + // "address": "D5Y6MtvsrtdAWUM3zv9RvR4mt8c1MUuHKX", + // "percentage": 0.3 + // }, + // { + // "address": "DGWUdCL1XwaE2WTSJg8hgkQkXQW1VMmiWC", + // "percentage": 1.5 + // } + // ], + // "slackNotifications": { + // "enabled": true, + // "webHookUrl": "https://hooks.slack.com/services/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", + // "channel": "#verge", + // "notifyBlockFound": true, + // "notifyPaymentSuccess": true, + // "blockFoundUsername": "Block Notification", + // "paymentSuccessUsername": "Payment Notification" + // }, + // "hasLegacyDaemon": true, + // "maxActiveJobs": 20, + // "blockRefreshInterval": 500, + // "jobRebroadcastTimeout": 4, + // "clientConnectionTimeout": 600, + // "banning": { + // "enabled": true, + // "time": 600, + // "invalidPercent": 90, + // "checkThreshold": 150 + // }, + // "ports": { + // "3262": { + // "listenAddress": "0.0.0.0", + // "difficulty": 10, + // "name": "GPU Mining", + // "varDiff": { + // "minDiff": 20, + // "targetTime": 15, + // "retargetTime": 90, + // "variancePercent": 30, + // "maxDelta": 10 + // } + // }, + // "3263": { + // "listenAddress": "0.0.0.0", + // "difficulty": 100, + // "name": "High-End Multi GPU Mining", + // "varDiff": { + // "minDiff": 20, + // "targetTime": 15, + // "retargetTime": 90, + // "variancePercent": 30, + // "maxDelta": 10 + // } + // } + // }, + // "daemons": [ + // { + // "host": "heimdall", + // "port": 20102, + // "user": "ecVjoZHwSU26z7dlGMES", + // "password": "6e8c2ebc63748c71183c0188b91ba55b" + // } + // ], + // "paymentProcessing": { + // "enabled": true, + // "minersPayTxFees": true, + // "minimumPayment": 0.1, + // "payoutScheme": "PPLNS", + // "payoutSchemeConfig": { + // "factor": 1.0 + // } + // } + // }, + // { + // "id": "dash1", + // "enabled": true, + // "coin": { + // "type": "DASH" + // }, + // "address": "yi7C5qaGrRnvpZ5dzdTynA1kuQ9THQC7o4", + // "rewardRecipients": [ + // { + // "type": "op", + // "address": "yi7C5qaGrRnvpZ5dzdTynA1kuQ9THQC7o4", + // "percentage": 1.5 + // } + // ], + // // "slackNotifications": { + // // "enabled": true, + // // "webHookUrl": "https://hooks.slack.com/services/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", + // // "channel": "#general", + // // "notifyBlockFound": true, + // // "notifyPaymentSuccess": true, + // // "blockFoundUsername": "Block Notification", + // // "paymentSuccessUsername": "Payment Notification" + // // }, + // "blockRefreshInterval": 1000, + // "jobRebroadcastTimeout": 55, + // "clientConnectionTimeout": 600, + // "banning": { + // "enabled": true, + // "time": 600, + // "invalidPercent": 50, + // "checkThreshold": 50 + // }, + // "ports": { + // "3062": { + // "difficulty": 0.001, + // "varDiff": { + // "minDiff": 0.0001, + // "maxDiff": null, + // "targetTime": 15, + // "retargetTime": 90, + // "variancePercent": 30 + // } + // }, + // "3063": { + // "difficulty": 100 + // } + // }, + // "daemons": [ + // { + // "host": "127.0.0.1", + // "port": 15001, + // "user": "user", + // "password": "pass" + // } + // ], + // "paymentProcessing": { + // "enabled": true, + // "minersPayTxFees": true, + // "minimumPayment": 0.01, + // "payoutScheme": "PPLNS", + // "payoutSchemeConfig": { + // "factor": 2.0 + // } + // } + // }, + // // { + // "id": "btg1", + // "enabled": true, + // "coin": { + // "type": "BTG" + // }, + // "address": "mjFTDcJXjBc45LtBKm4KZ9tDsi3LUX3wA8", + // "rewardRecipients": [ + // { + // "type": "op", + // "address": "mjFTDcJXjBc45LtBKm4KZ9tDsi3LUX3wA8", + // "percentage": 1.5 + // } + // ], + // "blockRefreshInterval": 1000, + // "jobRebroadcastTimeout": 55, + // "clientConnectionTimeout": 600, + // "banning": { + // "enabled": true, + // "time": 600, + // "invalidPercent": 50, + // "checkThreshold": 50 + // }, + // "ports": { + // "3046": { + // "difficulty": 0.1, + // "varDiff": { + // "minDiff": 0.1, + // "maxDiff": null, + // "targetTime": 15, + // "retargetTime": 90, + // "variancePercent": 30 + // } + // } + // }, + // "daemons": [ + // { + // "host": "127.0.0.1", + // "port": 18232, + // "user": "user", + // "password": "pass" + // } + // ], + // "paymentProcessing": { + // "enabled": true, + // "minimumPayment": 0.01, + // "payoutScheme": "PPLNS", + // "payoutSchemeConfig": { + // "factor": 2.0 + // } + // } + // } + ] +} \ No newline at end of file From 60dbd53fd1788f3f6aad2e676fe40096963f1b6f Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 09:06:18 +0100 Subject: [PATCH 174/348] WIP --- .../Blockchain/Bitcoin/BitcoinProperties.cs | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index ff8527c4e..c888b92ef 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -44,6 +44,7 @@ public class BitcoinProperties private static readonly IHashAlgorithm qubit = new Qubit(); private static readonly IHashAlgorithm groestlMyriad = new GroestlMyriad(); private static readonly IHashAlgorithm neoScryptProfile1 = new NeoScrypt(0x80000620); + private static readonly IHashAlgorithm vergeBlockHasher = new DigestReverser(scrypt_1024_1); private static readonly BitcoinCoinProperties sha256Coin = new BitcoinCoinProperties(1, sha256D, sha256D, sha256DReverse, "Sha256"); @@ -79,25 +80,19 @@ public class BitcoinProperties new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, neoScryptProfile1, new DigestReverser(neoScryptProfile1), "Neoscrypt"); private static readonly BitcoinCoinProperties vergeLyraCoin = - new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, new DigestReverser(scrypt_1024_1), "Lyra2re2"); - - private static readonly BitcoinCoinProperties vergeScryptCoin = - new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, scrypt_1024_1, sha256DReverse, "Scrypt"); + new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, vergeBlockHasher, "Lyra2re2"); private static readonly BitcoinCoinProperties vergeBlake2sCoin = - new BitcoinCoinProperties(1, sha256D, blake2s, new DigestReverser(scrypt_1024_1), "Blake2s"); + new BitcoinCoinProperties(1, sha256D, blake2s, vergeBlockHasher, "Blake2s"); private static readonly BitcoinCoinProperties vergeX17Coin = - new BitcoinCoinProperties(1, sha256D, x17, new DigestReverser(scrypt_1024_1), "X17"); + new BitcoinCoinProperties(1, sha256D, x17, vergeBlockHasher, "X17"); - private static readonly BitcoinCoinProperties vergeSkeinCoin = - new BitcoinCoinProperties(1, sha256D, skein, new DigestReverser(scrypt_1024_1), "Skein"); - - private static readonly BitcoinCoinProperties vergeQubitCoin = - new BitcoinCoinProperties(1, sha256D, qubit, new DigestReverser(scrypt_1024_1), "Qubit"); + private static readonly BitcoinCoinProperties vergeScryptCoin = + new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, scrypt_1024_1, vergeBlockHasher, "Scrypt"); private static readonly BitcoinCoinProperties vergeGroestlCoin = - new BitcoinCoinProperties(1, sha256D, groestlMyriad, new DigestReverser(scrypt_1024_1), "Groestl-Myriad"); + new BitcoinCoinProperties(1, sha256D, groestlMyriad, vergeBlockHasher, "Groestl-Myriad"); private static readonly Dictionary coinProperties = new Dictionary { @@ -161,17 +156,17 @@ private static BitcoinCoinProperties GetDigiByteProperties(string algorithm) return sha256Coin; case "skein": - return vergeSkeinCoin; + return skeinCoin; case "qubit": - return vergeQubitCoin; + return qubitCoin; case "groestl": case "groestl-myriad": return groestlMyriadCoin; default: // scrypt - return vergeScryptCoin; + return scryptCoin; } } @@ -194,7 +189,7 @@ private static BitcoinCoinProperties GetVergeProperties(string algorithm) return vergeBlake2sCoin; default: // scrypt - return scryptCoin; + return vergeScryptCoin; } } From 89567518498d2fa14f22d66ef101f59a592ba208 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 11:38:40 +0100 Subject: [PATCH 175/348] Add payload-type-flag-frame to share publisher/subscriber --- src/MiningCore/Mining/ShareRecorder.cs | 41 ++++++++++++++++++-------- src/MiningCore/Mining/ShareRelay.cs | 11 +++++++ 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/MiningCore/Mining/ShareRecorder.cs b/src/MiningCore/Mining/ShareRecorder.cs index f5cb57144..d71fb948c 100644 --- a/src/MiningCore/Mining/ShareRecorder.cs +++ b/src/MiningCore/Mining/ShareRecorder.cs @@ -335,19 +335,19 @@ private void StartExternalStratumPublisherListeners() subSocket.Connect(url); foreach(var topic in topics) - { subSocket.Subscribe(topic); - logger.Info($"Monitoring external stratum {url}/{topic}"); - } + + logger.Info($"Monitoring external stratum {url}/[{string.Join(", ", topics)}]"); while (true) { - var msg = subSocket.ReceiveMultipartMessage(2); + var msg = subSocket.ReceiveMultipartMessage(3); var topic = msg.Pop().ConvertToString(Encoding.UTF8); + var flags = msg.Pop().ConvertToInt32(); var data = msg.Pop().ConvertToString(Encoding.UTF8); // validate - if (topics.Contains(topic)) + if (!topics.Contains(topic)) { logger.Warn(() => $"Received non-matching topic {topic} on ZeroMQ subscriber socket"); continue; @@ -359,15 +359,30 @@ private void StartExternalStratumPublisherListeners() continue; } + // extract flags + var wireFormat = (ShareRelay.WireFormat) (flags & ShareRelay.WireFormatMask); + // deserialize - Share share; + Share share = null; - using (var reader = new StringReader(data)) + switch (wireFormat) { - using (var jreader = new JsonTextReader(reader)) - { - share = serializer.Deserialize(jreader); - } + case ShareRelay.WireFormat.Json: + using (var reader = new StringReader(data)) + { + using (var jreader = new JsonTextReader(reader)) + { + share = serializer.Deserialize(jreader); + } + } + break; + + case ShareRelay.WireFormat.ProtocolBuffers: + break; + + default: + logger.Error(() => $"Unsupported wire format {wireFormat} of share received from {url}/{topic} "); + break; } if (share == null) @@ -384,8 +399,8 @@ private void StartExternalStratumPublisherListeners() queue.Add(share); // log it - var source = !string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}]" : string.Empty; - logger.Info(() => $"[{share.PoolId}] External {source} share accepted: D={Math.Round(share.Difficulty, 3)}"); + var source = !string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}] " : string.Empty; + logger.Info(() => $"[{share.PoolId}] External {source}share accepted: D={Math.Round(share.Difficulty, 3)}"); // update pool stats if (pools.TryGetValue(topic, out var pool) && pool.NetworkStats != null) diff --git a/src/MiningCore/Mining/ShareRelay.cs b/src/MiningCore/Mining/ShareRelay.cs index 2aadfaafe..ac0039b96 100644 --- a/src/MiningCore/Mining/ShareRelay.cs +++ b/src/MiningCore/Mining/ShareRelay.cs @@ -28,6 +28,15 @@ public ShareRelay(JsonSerializerSettings serializerSettings) private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); + [Flags] + public enum WireFormat + { + Json = 1, + ProtocolBuffers = 2 + } + + public const int WireFormatMask = 0xF; + #region API-Surface public void AttachPool(IMiningPool pool) @@ -84,9 +93,11 @@ private void InitializeQueue() try { var json = JsonConvert.SerializeObject(share, serializerSettings); + var flags = (int) WireFormat.Json; var msg = new NetMQMessage(2); msg.Push(json); + msg.Push(flags); msg.Push(share.PoolId); pubSocket.SendMultipartMessage(msg); } From d5bb5b1173301ec41b3c7a356ccab975e429298d Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 12:45:23 +0100 Subject: [PATCH 176/348] ProtoBuf Share Relay & Recorder --- src/MiningCore/Blockchain/Share.cs | 17 +++++++++++++++++ src/MiningCore/Mining/ShareRecorder.cs | 18 +++++++++++++----- src/MiningCore/Mining/ShareRelay.cs | 14 ++++++++++---- src/MiningCore/MiningCore.csproj | 21 +++++++++++---------- 4 files changed, 51 insertions(+), 19 deletions(-) diff --git a/src/MiningCore/Blockchain/Share.cs b/src/MiningCore/Blockchain/Share.cs index 7c065c0f1..d29d97a4b 100644 --- a/src/MiningCore/Blockchain/Share.cs +++ b/src/MiningCore/Blockchain/Share.cs @@ -19,86 +19,103 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; +using ProtoBuf; namespace MiningCore.Blockchain { + [ProtoContract] public class Share { /// /// The pool originating this share from /// + [ProtoMember(1)] public string PoolId { get; set; } /// /// Who mined it (wallet address) /// + [ProtoMember(2)] public string Miner { get; set; } /// /// Who mined it /// + [ProtoMember(3)] public string Worker { get; set; } /// /// Extra information for payout processing /// + [ProtoMember(4)] public string PayoutInfo { get; set; } /// /// Mining Software /// + [ProtoMember(5)] public string UserAgent { get; set; } /// /// From where was it submitted /// + [ProtoMember(6)] public string IpAddress { get; set; } /// /// Submission source (pool, external stratum etc) /// + [ProtoMember(7)] public string Source { get; set; } /// /// Stratum difficulty assigned to the miner at the time the share was submitted/accepted (used for payout /// calculations) /// + [ProtoMember(8)] public double Difficulty { get; set; } /// /// Block this share refers to /// + [ProtoMember(9)] public long BlockHeight { get; set; } /// /// Block reward after deducting pool fee and donations /// + [ProtoMember(10)] public decimal BlockReward { get; set; } /// /// Block hash /// + [ProtoMember(11)] public string BlockHash { get; set; } /// /// If this share presumably resulted in a block /// + [ProtoMember(12)] public bool IsBlockCandidate { get; set; } /// /// Arbitrary data to be interpreted by the payment processor specialized /// in this coin to verify this block candidate was accepted by the network /// + [ProtoMember(13)] public string TransactionConfirmationData { get; set; } /// /// Network difficulty at the time the share was submitted (used for some payout schemes like PPLNS) /// + [ProtoMember(14)] public double NetworkDifficulty { get; set; } /// /// When the share was found /// + [ProtoMember(15)] public DateTime Created { get; set; } } } diff --git a/src/MiningCore/Mining/ShareRecorder.cs b/src/MiningCore/Mining/ShareRecorder.cs index d71fb948c..7e91e3f3e 100644 --- a/src/MiningCore/Mining/ShareRecorder.cs +++ b/src/MiningCore/Mining/ShareRecorder.cs @@ -45,6 +45,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using Org.BouncyCastle.Utilities.Collections; using Polly; using Polly.CircuitBreaker; +using ProtoBuf; using Contract = MiningCore.Contracts.Contract; using Share = MiningCore.Blockchain.Share; @@ -344,7 +345,7 @@ private void StartExternalStratumPublisherListeners() var msg = subSocket.ReceiveMultipartMessage(3); var topic = msg.Pop().ConvertToString(Encoding.UTF8); var flags = msg.Pop().ConvertToInt32(); - var data = msg.Pop().ConvertToString(Encoding.UTF8); + var data = msg.Pop().ToByteArray(); // validate if (!topics.Contains(topic)) @@ -353,7 +354,7 @@ private void StartExternalStratumPublisherListeners() continue; } - if (string.IsNullOrEmpty(data)) + if (data?.Length == 0) { logger.Warn(() => $"Received empty data from {url}/{topic}"); continue; @@ -368,16 +369,23 @@ private void StartExternalStratumPublisherListeners() switch (wireFormat) { case ShareRelay.WireFormat.Json: - using (var reader = new StringReader(data)) + using (var stream = new MemoryStream(data)) { - using (var jreader = new JsonTextReader(reader)) + using (var reader = new StreamReader(stream, Encoding.UTF8)) { - share = serializer.Deserialize(jreader); + using (var jreader = new JsonTextReader(reader)) + { + share = serializer.Deserialize(jreader); + } } } break; case ShareRelay.WireFormat.ProtocolBuffers: + using (var stream = new MemoryStream(data)) + { + share = Serializer.Deserialize(stream); + } break; default: diff --git a/src/MiningCore/Mining/ShareRelay.cs b/src/MiningCore/Mining/ShareRelay.cs index ac0039b96..3bd7669cc 100644 --- a/src/MiningCore/Mining/ShareRelay.cs +++ b/src/MiningCore/Mining/ShareRelay.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Concurrent; +using System.IO; using System.Reactive.Concurrency; using System.Reactive.Linq; using MiningCore.Blockchain; @@ -8,6 +9,7 @@ using NetMQ.Sockets; using Newtonsoft.Json; using NLog; +using ProtoBuf; namespace MiningCore.Mining { @@ -92,11 +94,15 @@ private void InitializeQueue() try { - var json = JsonConvert.SerializeObject(share, serializerSettings); - var flags = (int) WireFormat.Json; - + var flags = (int) WireFormat.ProtocolBuffers; var msg = new NetMQMessage(2); - msg.Push(json); + + using (var stream = new MemoryStream()) + { + Serializer.Serialize(stream, share); + msg.Push(stream.ToArray()); + } + msg.Push(flags); msg.Push(share.PoolId); pubSocket.SendMultipartMessage(msg); diff --git a/src/MiningCore/MiningCore.csproj b/src/MiningCore/MiningCore.csproj index 74b9172f3..797efb61d 100644 --- a/src/MiningCore/MiningCore.csproj +++ b/src/MiningCore/MiningCore.csproj @@ -46,21 +46,22 @@ - + - - - + + + - - - + + + - + - - + + + From 2bb7807c79144e2594cb9fc9b037c17c65299c79 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 12:51:45 +0100 Subject: [PATCH 177/348] WIP --- src/MiningCore/Mining/ShareRecorder.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/MiningCore/Mining/ShareRecorder.cs b/src/MiningCore/Mining/ShareRecorder.cs index 7e91e3f3e..985775cad 100644 --- a/src/MiningCore/Mining/ShareRecorder.cs +++ b/src/MiningCore/Mining/ShareRecorder.cs @@ -332,10 +332,9 @@ private void StartExternalStratumPublisherListeners() { using (var subSocket = new SubscriberSocket()) { - //subSocket.Options.ReceiveHighWatermark = 1000; subSocket.Connect(url); - foreach(var topic in topics) + foreach (var topic in topics) subSocket.Subscribe(topic); logger.Info($"Monitoring external stratum {url}/[{string.Join(", ", topics)}]"); From 67d8fc2ad22bbe2636ed5fc04896520574fad4f8 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 13:24:55 +0100 Subject: [PATCH 178/348] PoolPoolscoped share relay logging --- src/MiningCore/config2.json | 734 ------------------------------------ 1 file changed, 734 deletions(-) delete mode 100644 src/MiningCore/config2.json diff --git a/src/MiningCore/config2.json b/src/MiningCore/config2.json deleted file mode 100644 index f4d13450a..000000000 --- a/src/MiningCore/config2.json +++ /dev/null @@ -1,734 +0,0 @@ -{ - "logging": { - "level": "info", - "enableConsoleLog": true, - "enableConsoleColors": true, - "logFile": "", - "logBaseDirectory": "", - "perPoolLogFile": false - }, - "shareRelay": { - "publishUrl": "tcp://0.0.0.0:6000" - }, - "banning": { - "manager": "integrated" - }, - "notifications": { - "enabled": true, - "email": { - "host": "smtp.example.com", - "port": 587, - "user": "user", - "password": "password", - "fromAddress": "info@poolmining.org", - "fromName": "poolmining support" - }, - "admin": { - "enabled": false, - "emailAddress": "user@example.com", - "notifyBlockFound": true - } - }, - "persistence": { - "postgres": { - "host": "127.0.0.1", - "port": 5432, - "user": "miningcore", - "password": "Qx6SJcOOlfVyoR66nZH1", - "database": "miningcore" - } - }, - "equihashMaxThreads": 4, - "paymentProcessing": { - "enabled": true, - "interval": 600, - "shareRecoveryFile": "recovered-shares.txt" - }, - "pools": [ - // { - // "id": "mona1", - // "enabled": true, - // "coin": { - // "type": "MONA" - // }, - // "address": "n3E5XwMhQnGKqxfhJBFP33ae6r2uhZy9qX", - // "rewardRecipients": [ - // { - // "type": "op", - // "address": "n3E5XwMhQnGKqxfhJBFP33ae6r2uhZy9qX", - // "percentage": 1.5 - // } - // ], - // "blockRefreshInterval": 1000, - // "jobRebroadcastTimeout": 55, - // "clientConnectionTimeout": 600, - // "banning": { - // "enabled": true, - // "time": 600, - // "invalidPercent": 50, - // "checkThreshold": 50 - // }, - // "ports": { - // "3092": { - // "difficulty": 2, - // "varDiff": { - // "minDiff": 0.01, - // "maxDiff": null, - // "targetTime": 15, - // "retargetTime": 90, - // "variancePercent": 30 - // } - // }, - // "3093": { - // "difficulty": 1 - // } - // }, - // "daemons": [ - // { - // "host": "127.0.0.1", - // "port": 9402, - // "user": "5d172d7b63e8c226db9595e67a949636", - // "password": "c6115cea1465d5c37bf59e1c5446183e" - // } - // ], - // "paymentProcessing": { - // "enabled": true, - // "minimumPayment": 0.01, - // "payoutScheme": "PPLNS", - // "payoutSchemeConfig": { - // "factor": 2.0 - // } - // } - // }, - // { - // "id": "zec1", - // "enabled": true, - // "coin": { - // "type": "ZEC" - // }, - // "address": "tmVcjHqYUrNMB3YFbBhUPJj4AnW4r8ErBJH", - // "rewardRecipients": [ - // { - // "type": "op", - // "address": "tmVcjHqYUrNMB3YFbBhUPJj4AnW4r8ErBJH", - // "percentage": 1.5 - // } - // ], - // "blockRefreshInterval": 1000, - // "jobRebroadcastTimeout": 55, - // "clientConnectionTimeout": 600, - // "banning": { - // "enabled": true, - // "time": 600, - // "invalidPercent": 50, - // "checkThreshold": 50 - // }, - // "ports": { - // "3092": { - // "difficulty": 0.1, - // "varDiff": { - // "minDiff": 0.01, - // "maxDiff": null, - // "targetTime": 15, - // "retargetTime": 90, - // "variancePercent": 30 - // } - // }, - // "3093": { - // "difficulty": 1 - // } - // }, - // "daemons": [ - // { - // "host": "127.0.0.1", - // "port": 8232, - // "user": "user", - // "password": "pass" - // } - // ], - // "paymentProcessing": { - // "enabled": true, - // "minimumPayment": 0.01, - // "payoutScheme": "PPLNS", - // "payoutSchemeConfig": { - // "factor": 2.0 - // } - // } - // }, - // { - // "id": "btcp1", - // "enabled": true, - // "coin": { - // "type": "BTCP" - // }, - // "address": "n1e1mZg5k8UgrpCASCk3jyxU6gGFRdYV4yY", - // "z-address": "zzBzEwPBZ3T6fcYN1QQdnh1Ytnp9g71ezAPtnCFVJ6XRGStpdZtsMX9hnzCPESQkTqcJyGEWZ9PrJAKs8SjyWtNLCmBEyih", - // "rewardRecipients": [ - // { - // "type": "op", - // "address": "n1e1mZg5k8UgrpCASCk3jyxU6gGFRdYV4yY", - // "percentage": 1.5 - // } - // ], - // "blockRefreshInterval": 1000, - // "jobRebroadcastTimeout": 55, - // "clientConnectionTimeout": 600, - // "banning": { - // "enabled": true, - // "time": 600, - // "invalidPercent": 50, - // "checkThreshold": 50 - // }, - // "ports": { - // "3092": { - // "difficulty": 0.1, - // "varDiff": { - // "minDiff": 0.01, - // "maxDiff": null, - // "targetTime": 15, - // "retargetTime": 90, - // "variancePercent": 30 - // } - // }, - // "3093": { - // "difficulty": 1 - // } - // }, - // "daemons": [ - // { - // "host": "127.0.0.1", - // "port": 7932, - // "user": "user", - // "password": "pass" - // } - // ], - // "paymentProcessing": { - // "enabled": true, - // "minimumPayment": 0.01, - // "payoutScheme": "PPLNS", - // "payoutSchemeConfig": { - // "factor": 2.0 - // } - // } - // }, - // { - // "id": "eth1", - // "enabled": true, - // "coin": { - // "type": "ETH" - // }, - // "address": "0x0942e9144606ad43f2e61a7ee332fe9914424712", - // "rewardRecipients": [ - // { - // "type": "op", - // "address": "0x0942e9144606ad43f2e61a7ee332fe9914424712", - // "percentage": 0 - // } - // ], - // "blockRefreshInterval": 1000, - // "jobRebroadcastTimeout": 55, - // "clientConnectionTimeout": 600, - // "banning": { - // "enabled": true, - // "time": 600, - // "invalidPercent": 50, - // "checkThreshold": 50 - // }, - // "ports": { - // "3072": { - // "difficulty": 0.1, - // "varDiff": { - // "minDiff": 0.05, - // "maxDiff": null, - // "targetTime": 15, - // "retargetTime": 90, - // "variancePercent": 30 - // } - // }, - // "3073": { - // "difficulty": 5000 - // } - // }, - // "daemons": [ - // { - // "host": "127.0.0.1", - // "port": 8545, - // "user": "user", - // "password": "password" - // } - // ], - // "paymentProcessing": { - // "enabled": true, - // "minimumPayment": 0.01, - // "minimumPaymentToPaymentId": 5.0, - // "payoutScheme": "PPLNS", - // "payoutSchemeConfig": { - // "factor": 2.0 - // }, - // "coinbasePassword": "", - // "keepUncles": false, - // "keepTransactionFees": false - // } - // }, - { - "id": "xmr1", - "enabled": true, - "coin": { - "type": "XMR" - }, - "address": "9wviCeWe2D8XS82k2ovp5EUYLzBt9pYNW2LXUFsZiv8S3Mt21FZ5qQaAroko1enzw3eGr9qC7X1D7Geoo2RrAotYPwq9Gm8", - "rewardRecipients": [ - { - "type": "op", - "address": "9wviCeWe2D8XS82k2ovp5EUYLzBt9pYNW2LXUFsZiv8S3Mt21FZ5qQaAroko1enzw3eGr9qC7X1D7Geoo2RrAotYPwq9Gm8", - "percentage": 0 - } - ], - "blockRefreshInterval": 1000, - "jobRebroadcastTimeout": 55, - "clientConnectionTimeout": 600, - "banning": { - "enabled": true, - "time": 600, - "invalidPercent": 50, - "checkThreshold": 50 - }, - // "externalStratums": [{ - // "url": "tcp://localhost:13333", - // "topic": "xmr1" - // }], - "ports": { - "4032": { - "difficulty": 1000, - "varDiff": { - "minDiff": 750, - "targetTime": 5, - "retargetTime": 45, - "variancePercent": 30 - } - }, - "4256": { - "difficulty": 750 - } - }, - "daemons": [ - { - "host": "127.0.0.1", - "port": 28081, - "user": "monerorpc", - "password": "rpcpassword" - }, - { - "host": "127.0.0.1", - "port": 28082, - "user": "monerorpc", - "password": "rpcpassword", - "category": "wallet" - } - ], - "paymentProcessing": { - "enabled": true, - "minimumPayment": 0.01, - "minimumPaymentToPaymentId": 5.0, - "payoutScheme": "PPLNS", - "payoutSchemeConfig": { - "factor": 2.0 - }, - "coinbasePassword": 1 - } - }, - // { - // "id": "ltc1", - // "enabled": true, - // "coin": { - // "type": "LTC" - // }, - // "address": "mwkc7fx4UWBDAjnpiGaHmkWh9RYiNZzsvN", - // "rewardRecipients": [ - // { - // "type": "op", - // "address": "mwkc7fx4UWBDAjnpiGaHmkWh9RYiNZzsvN", - // "percentage": 1.5 - // } - // ], - // "blockRefreshInterval": 1000, - // "jobRebroadcastTimeout": 55, - // "clientConnectionTimeout": 600, - // "banning": { - // "enabled": true, - // "time": 600, - // "invalidPercent": 50, - // "checkThreshold": 50 - // }, - // "ports": { - // "3042": { - // "difficulty": 20, - // "varDiff": { - // "minDiff": 10, - // "targetTime": 15, - // "retargetTime": 90, - // "variancePercent": 30 - // } - // }, - // "3043": { - // "difficulty": 100 - // } - // }, - // "daemons": [ - // { - // "host": "127.0.0.1", - // "port": 14001, - // "user": "user", - // "password": "pass" - // } - // ], - // "paymentProcessing": { - // "enabled": true, - // "minimumPayment": 0.01, - // "payoutScheme": "PPLNS", - // "payoutSchemeConfig": { - // "factor": 2.0 - // } - // } - // }, - // { - // "id": "btc1", - // "enabled": true, - // "coin": { - // "type": "BTC" - // }, - // "address": "myhrXHY9pPLS8wcsCrdpg4FxVEEaXRM3YU", - // "rewardRecipients": [ - // { - // "type": "op", - // "address": "myhrXHY9pPLS8wcsCrdpg4FxVEEaXRM3YU", - // "percentage": 1.5 - // } - // ], - // "hasLegacyDaemon": false, - // "blockRefreshInterval": 1000, - // "jobRebroadcastTimeout": 55, - // "clientConnectionTimeout": 600, - // "banning": { - // "enabled": true, - // "time": 600, - // "invalidPercent": 50, - // "checkThreshold": 50 - // }, - // "ports": { - // "3052": { - // "difficulty": 0.05, - // "varDiff": { - // "minDiff": 0.01, - // "maxDiff": null, - // "targetTime": 15, - // "retargetTime": 90, - // "variancePercent": 30 - // } - // }, - // "3053": { - // "difficulty": 100 - // } - // }, - // "daemons": [ - // { - // "host": "127.0.0.1", - // "port": 16001, - // "user": "user", - // "password": "pass", - // "zmqBlockNotifySocket": "tcp://127.0.0.1:26001" - // } - // ], - // "paymentProcessing": { - // "enabled": true, - // "minimumPayment": 0.01, - // "blockrewardMultiplier": 100, - // "payoutScheme": "PPLNS", - // "payoutSchemeConfig": { - // "factor": 2.0 - // } - // } - // }, - // { - // "id": "xvg1", - // "enabled": true, - // "coin": { - // "type": "XVG", - // "algorithm": "lyra" - // }, - // "address": "D5Y6MtvsrtdAWUM3zv9RvR4mt8c1MUuHKX", - // "rewardRecipients": [ - // { - // "address": "D5Y6MtvsrtdAWUM3zv9RvR4mt8c1MUuHKX", - // "percentage": 0.3 - // }, - // { - // "address": "DGWUdCL1XwaE2WTSJg8hgkQkXQW1VMmiWC", - // "percentage": 1.5 - // } - // ], - // "slackNotifications": { - // "enabled": true, - // "webHookUrl": "https://hooks.slack.com/services/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", - // "channel": "#verge", - // "notifyBlockFound": true, - // "notifyPaymentSuccess": true, - // "blockFoundUsername": "Block Notification", - // "paymentSuccessUsername": "Payment Notification" - // }, - // "hasLegacyDaemon": true, - // "maxActiveJobs": 20, - // "blockRefreshInterval": 500, - // "jobRebroadcastTimeout": 4, - // "clientConnectionTimeout": 600, - // "banning": { - // "enabled": true, - // "time": 600, - // "invalidPercent": 90, - // "checkThreshold": 150 - // }, - // "ports": { - // "3162": { - // "listenAddress": "0.0.0.0", - // "difficulty": 10, - // "name": "GPU Mining", - // "varDiff": { - // "minDiff": 20, - // "targetTime": 15, - // "retargetTime": 90, - // "variancePercent": 30, - // "maxDelta": 10 - // } - // }, - // "3163": { - // "listenAddress": "0.0.0.0", - // "difficulty": 100, - // "name": "High-End Multi GPU Mining", - // "varDiff": { - // "minDiff": 20, - // "targetTime": 15, - // "retargetTime": 90, - // "variancePercent": 30, - // "maxDelta": 10 - // } - // } - // }, - // "daemons": [ - // { - // "host": "heimdall", - // "port": 20102, - // "user": "ecVjoZHwSU26z7dlGMES", - // "password": "6e8c2ebc63748c71183c0188b91ba55b" - // } - // ], - // "paymentProcessing": { - // "enabled": true, - // "minersPayTxFees": true, - // "minimumPayment": 0.1, - // "payoutScheme": "PPLNS", - // "payoutSchemeConfig": { - // "factor": 1.0 - // } - // } - // }, - // { - // "id": "xvg2", - // "enabled": true, - // "coin": { - // "type": "XVG", - // "algorithm": "blake" - // }, - // "address": "D5Y6MtvsrtdAWUM3zv9RvR4mt8c1MUuHKX", - // "rewardRecipients": [ - // { - // "address": "D5Y6MtvsrtdAWUM3zv9RvR4mt8c1MUuHKX", - // "percentage": 0.3 - // }, - // { - // "address": "DGWUdCL1XwaE2WTSJg8hgkQkXQW1VMmiWC", - // "percentage": 1.5 - // } - // ], - // "slackNotifications": { - // "enabled": true, - // "webHookUrl": "https://hooks.slack.com/services/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", - // "channel": "#verge", - // "notifyBlockFound": true, - // "notifyPaymentSuccess": true, - // "blockFoundUsername": "Block Notification", - // "paymentSuccessUsername": "Payment Notification" - // }, - // "hasLegacyDaemon": true, - // "maxActiveJobs": 20, - // "blockRefreshInterval": 500, - // "jobRebroadcastTimeout": 4, - // "clientConnectionTimeout": 600, - // "banning": { - // "enabled": true, - // "time": 600, - // "invalidPercent": 90, - // "checkThreshold": 150 - // }, - // "ports": { - // "3262": { - // "listenAddress": "0.0.0.0", - // "difficulty": 10, - // "name": "GPU Mining", - // "varDiff": { - // "minDiff": 20, - // "targetTime": 15, - // "retargetTime": 90, - // "variancePercent": 30, - // "maxDelta": 10 - // } - // }, - // "3263": { - // "listenAddress": "0.0.0.0", - // "difficulty": 100, - // "name": "High-End Multi GPU Mining", - // "varDiff": { - // "minDiff": 20, - // "targetTime": 15, - // "retargetTime": 90, - // "variancePercent": 30, - // "maxDelta": 10 - // } - // } - // }, - // "daemons": [ - // { - // "host": "heimdall", - // "port": 20102, - // "user": "ecVjoZHwSU26z7dlGMES", - // "password": "6e8c2ebc63748c71183c0188b91ba55b" - // } - // ], - // "paymentProcessing": { - // "enabled": true, - // "minersPayTxFees": true, - // "minimumPayment": 0.1, - // "payoutScheme": "PPLNS", - // "payoutSchemeConfig": { - // "factor": 1.0 - // } - // } - // }, - // { - // "id": "dash1", - // "enabled": true, - // "coin": { - // "type": "DASH" - // }, - // "address": "yi7C5qaGrRnvpZ5dzdTynA1kuQ9THQC7o4", - // "rewardRecipients": [ - // { - // "type": "op", - // "address": "yi7C5qaGrRnvpZ5dzdTynA1kuQ9THQC7o4", - // "percentage": 1.5 - // } - // ], - // // "slackNotifications": { - // // "enabled": true, - // // "webHookUrl": "https://hooks.slack.com/services/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", - // // "channel": "#general", - // // "notifyBlockFound": true, - // // "notifyPaymentSuccess": true, - // // "blockFoundUsername": "Block Notification", - // // "paymentSuccessUsername": "Payment Notification" - // // }, - // "blockRefreshInterval": 1000, - // "jobRebroadcastTimeout": 55, - // "clientConnectionTimeout": 600, - // "banning": { - // "enabled": true, - // "time": 600, - // "invalidPercent": 50, - // "checkThreshold": 50 - // }, - // "ports": { - // "3062": { - // "difficulty": 0.001, - // "varDiff": { - // "minDiff": 0.0001, - // "maxDiff": null, - // "targetTime": 15, - // "retargetTime": 90, - // "variancePercent": 30 - // } - // }, - // "3063": { - // "difficulty": 100 - // } - // }, - // "daemons": [ - // { - // "host": "127.0.0.1", - // "port": 15001, - // "user": "user", - // "password": "pass" - // } - // ], - // "paymentProcessing": { - // "enabled": true, - // "minersPayTxFees": true, - // "minimumPayment": 0.01, - // "payoutScheme": "PPLNS", - // "payoutSchemeConfig": { - // "factor": 2.0 - // } - // } - // }, - // // { - // "id": "btg1", - // "enabled": true, - // "coin": { - // "type": "BTG" - // }, - // "address": "mjFTDcJXjBc45LtBKm4KZ9tDsi3LUX3wA8", - // "rewardRecipients": [ - // { - // "type": "op", - // "address": "mjFTDcJXjBc45LtBKm4KZ9tDsi3LUX3wA8", - // "percentage": 1.5 - // } - // ], - // "blockRefreshInterval": 1000, - // "jobRebroadcastTimeout": 55, - // "clientConnectionTimeout": 600, - // "banning": { - // "enabled": true, - // "time": 600, - // "invalidPercent": 50, - // "checkThreshold": 50 - // }, - // "ports": { - // "3046": { - // "difficulty": 0.1, - // "varDiff": { - // "minDiff": 0.1, - // "maxDiff": null, - // "targetTime": 15, - // "retargetTime": 90, - // "variancePercent": 30 - // } - // } - // }, - // "daemons": [ - // { - // "host": "127.0.0.1", - // "port": 18232, - // "user": "user", - // "password": "pass" - // } - // ], - // "paymentProcessing": { - // "enabled": true, - // "minimumPayment": 0.01, - // "payoutScheme": "PPLNS", - // "payoutSchemeConfig": { - // "factor": 2.0 - // } - // } - // } - ] -} \ No newline at end of file From 8a200f4f9438fa2326a25cd8db751c202af3e703 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 13:25:34 +0100 Subject: [PATCH 179/348] WIP --- .gitignore | 1 + src/MiningCore/Mining/ShareRecorder.cs | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 370ac6467..5bcc43ed6 100644 --- a/.gitignore +++ b/.gitignore @@ -117,3 +117,4 @@ recovered-shares.json src/MiningCore/config\.json +/src/MiningCore/config2.json diff --git a/src/MiningCore/Mining/ShareRecorder.cs b/src/MiningCore/Mining/ShareRecorder.cs index 985775cad..de06001b6 100644 --- a/src/MiningCore/Mining/ShareRecorder.cs +++ b/src/MiningCore/Mining/ShareRecorder.cs @@ -37,6 +37,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Persistence.Model; using MiningCore.Persistence.Repositories; using MiningCore.Time; +using MiningCore.Util; using NetMQ; using NetMQ.Sockets; using Newtonsoft.Json; @@ -90,7 +91,7 @@ public ShareRecorder(IConnectionFactory cf, IMapper mapper, private readonly NotificationService notificationService; private ClusterConfig clusterConfig; private readonly IMapper mapper; - private readonly ConcurrentDictionary pools = new ConcurrentDictionary(); + private readonly ConcurrentDictionary> pools = new ConcurrentDictionary>(); private readonly BlockingCollection queue = new BlockingCollection(); private readonly int QueueSizeWarningThreshold = 1024; @@ -359,6 +360,16 @@ private void StartExternalStratumPublisherListeners() continue; } + // lookup pool + IMiningPool pool = null; + ILogger poolLogger = null; + + if (pools.TryGetValue(topic, out var poolData)) + { + pool = poolData.Item1; + poolLogger = poolData.Item2; + } + // extract flags var wireFormat = (ShareRelay.WireFormat) (flags & ShareRelay.WireFormatMask); @@ -407,10 +418,10 @@ private void StartExternalStratumPublisherListeners() // log it var source = !string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}] " : string.Empty; - logger.Info(() => $"[{share.PoolId}] External {source}share accepted: D={Math.Round(share.Difficulty, 3)}"); + poolLogger?.Info(() => $"External {source}share accepted: D={Math.Round(share.Difficulty, 3)}"); // update pool stats - if (pools.TryGetValue(topic, out var pool) && pool.NetworkStats != null) + if (pool?.NetworkStats != null) { pool.NetworkStats.BlockHeight = share.BlockHeight; pool.NetworkStats.NetworkDifficulty = share.NetworkDifficulty; @@ -444,7 +455,7 @@ private void StartExternalStratumPublisherListeners() public void AttachPool(IMiningPool pool) { - pools[pool.Config.Id] = pool; + pools[pool.Config.Id] = Tuple.Create(pool, LogUtil.GetPoolScopedLogger(typeof(ShareRecorder), pool.Config)); pool.Shares.Subscribe(x => { queue.Add(x.Share); }); } From 2534e1e1cef0870d7d43bd5dfee5ce345039e9a1 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 14:10:14 +0100 Subject: [PATCH 180/348] WIP --- .../Persistence/Postgres/Repositories/StatsRepository.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index 0974ed0df..4b6718031 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -46,7 +46,7 @@ public StatsRepository(IMapper mapper, IMasterClock clock) private readonly IMapper mapper; private readonly IMasterClock clock; private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); - private static readonly TimeSpan MinerStatsMaxAge = TimeSpan.FromMinutes(15); + private static readonly TimeSpan MinerStatsMaxAge = TimeSpan.FromMinutes(20); public void InsertPoolStats(IDbConnection con, IDbTransaction tx, PoolStats stats) { @@ -146,7 +146,7 @@ public MinerStats GetMinerStats(IDbConnection con, IDbTransaction tx, string poo var lastUpdate = con.QuerySingleOrDefault(query, new { poolId, miner }, tx); // ignore stale minerstats - if (lastUpdate.HasValue && (clock.Now - lastUpdate) > MinerStatsMaxAge) + if (lastUpdate.HasValue && (clock.Now - DateTime.SpecifyKind(lastUpdate.Value, DateTimeKind.Utc) > MinerStatsMaxAge)) lastUpdate = null; if (lastUpdate.HasValue) From f732e2a05053ffbe95693bab0a938d781458beab Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 14:43:53 +0100 Subject: [PATCH 181/348] Do not require monero wallet daemon config if payment processing is disabled --- .../Blockchain/Monero/MoneroJobManager.cs | 55 ++++++++++++------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs index c0ebd892d..86b873532 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs @@ -204,13 +204,16 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi .Where(x => string.IsNullOrEmpty(x.Category)) .ToArray(); - // extract wallet daemon endpoints - walletDaemonEndpoints = poolConfig.Daemons - .Where(x => x.Category?.ToLower() == MoneroConstants.WalletDaemonCategory) - .ToArray(); + if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) + { + // 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); + 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(); } @@ -322,9 +325,12 @@ protected override void ConfigureDaemons() daemon = new DaemonClient(jsonSerializerSettings); daemon.Configure(daemonEndpoints, MoneroConstants.DaemonRpcLocation); - // also setup wallet daemon - walletDaemon = new DaemonClient(jsonSerializerSettings); - walletDaemon.Configure(walletDaemonEndpoints, MoneroConstants.DaemonRpcLocation); + if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) + { + // also setup wallet daemon + walletDaemon = new DaemonClient(jsonSerializerSettings); + walletDaemon.Configure(walletDaemonEndpoints, MoneroConstants.DaemonRpcLocation); + } } protected override async Task AreDaemonsHealthyAsync() @@ -340,15 +346,20 @@ protected override async Task AreDaemonsHealthyAsync() if (!responses.All(x => x.Error == null)) return false; - // test wallet daemons - var responses2 = await walletDaemon.ExecuteCmdAllAsync(MWC.GetAddress); + if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) + { + // test wallet daemons + var responses2 = await walletDaemon.ExecuteCmdAllAsync(MWC.GetAddress); + + if (responses2.Where(x => x.Error?.InnerException?.GetType() == typeof(DaemonClientException)) + .Select(x => (DaemonClientException) x.Error.InnerException) + .Any(x => x.Code == HttpStatusCode.Unauthorized)) + logger.ThrowLogPoolStartupException($"Wallet-Daemon reports invalid credentials", LogCat); - if (responses2.Where(x => x.Error?.InnerException?.GetType() == typeof(DaemonClientException)) - .Select(x => (DaemonClientException) x.Error.InnerException) - .Any(x => x.Code == HttpStatusCode.Unauthorized)) - logger.ThrowLogPoolStartupException($"Wallet-Daemon reports invalid credentials", LogCat); + return responses2.All(x => x.Error == null); + } - return responses2.All(x => x.Error == null); + return true; } protected override async Task AreDaemonsConnectedAsync() @@ -398,14 +409,18 @@ protected override async Task EnsureDaemonsSynchedAsync() protected override async Task PostStartInitAsync() { var infoResponse = await daemon.ExecuteCmdAnyAsync(MC.GetInfo); - var addressResponse = await walletDaemon.ExecuteCmdAnyAsync(MWC.GetAddress); if (infoResponse.Error != null) logger.ThrowLogPoolStartupException($"Init RPC failed: {infoResponse.Error.Message} (Code {infoResponse.Error.Code})", LogCat); - // 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); + if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) + { + var addressResponse = await walletDaemon.ExecuteCmdAnyAsync(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(); From cfa3de2b85e19a8d0aa74c903cd6fcece4aa55b8 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 18:09:46 +0100 Subject: [PATCH 182/348] Reconnect to share relay on receive timeout --- src/MiningCore/Mining/ShareRecorder.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/MiningCore/Mining/ShareRecorder.cs b/src/MiningCore/Mining/ShareRecorder.cs index de06001b6..76ce1a85c 100644 --- a/src/MiningCore/Mining/ShareRecorder.cs +++ b/src/MiningCore/Mining/ShareRecorder.cs @@ -95,6 +95,7 @@ public ShareRecorder(IConnectionFactory cf, IMapper mapper, private readonly BlockingCollection queue = new BlockingCollection(); private readonly int QueueSizeWarningThreshold = 1024; + private readonly TimeSpan relayReceiveTimeout = TimeSpan.FromSeconds(15); private readonly IShareRepository shareRepo; private Policy faultPolicy; private bool hasLoggedPolicyFallbackFailure; @@ -340,9 +341,16 @@ private void StartExternalStratumPublisherListeners() logger.Info($"Monitoring external stratum {url}/[{string.Join(", ", topics)}]"); + var msg = (NetMQMessage) null; + while (true) { - var msg = subSocket.ReceiveMultipartMessage(3); + if (!subSocket.TryReceiveMultipartMessage(relayReceiveTimeout, ref msg, 3)) + { + logger.Warn(() => $"Timeout receiving message from {url}. Reconnecting ..."); + break; + } + var topic = msg.Pop().ConvertToString(Encoding.UTF8); var flags = msg.Pop().ConvertToInt32(); var data = msg.Pop().ToByteArray(); From 4f09fb8196e67581568999c966252c4a5a28a75b Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 18:26:46 +0100 Subject: [PATCH 183/348] WIP --- src/MiningCore/Mining/ShareRecorder.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/MiningCore/Mining/ShareRecorder.cs b/src/MiningCore/Mining/ShareRecorder.cs index 76ce1a85c..8e2b81481 100644 --- a/src/MiningCore/Mining/ShareRecorder.cs +++ b/src/MiningCore/Mining/ShareRecorder.cs @@ -95,7 +95,7 @@ public ShareRecorder(IConnectionFactory cf, IMapper mapper, private readonly BlockingCollection queue = new BlockingCollection(); private readonly int QueueSizeWarningThreshold = 1024; - private readonly TimeSpan relayReceiveTimeout = TimeSpan.FromSeconds(15); + private readonly TimeSpan relayReceiveTimeout = TimeSpan.FromSeconds(60); private readonly IShareRepository shareRepo; private Policy faultPolicy; private bool hasLoggedPolicyFallbackFailure; @@ -325,8 +325,8 @@ private void StartExternalStratumPublisherListeners() var url = urlAndTopic.Key; var topics = new HashSet(urlAndTopic.Distinct()); - var currentHeight = 0L; - var lastBlockTime = clock.Now; + var currentHeights = new Dictionary(); + var lastBlockTimes = new Dictionary(); while (true) { @@ -434,11 +434,14 @@ private void StartExternalStratumPublisherListeners() pool.NetworkStats.BlockHeight = share.BlockHeight; pool.NetworkStats.NetworkDifficulty = share.NetworkDifficulty; + currentHeights.TryGetValue(topic, out var currentHeight); + lastBlockTimes.TryGetValue(topic, out var lastBlockTime); + if (currentHeight != share.BlockHeight) { pool.NetworkStats.LastNetworkBlockTime = clock.Now; - currentHeight = share.BlockHeight; - lastBlockTime = clock.Now; + currentHeights[topic] = share.BlockHeight; + lastBlockTimes[topic] = clock.Now; } else From 1f25567769b598278143c4279de1a41a488ad65e Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 19:47:05 +0100 Subject: [PATCH 184/348] Tuning --- src/MiningCore/Mining/ShareRecorder.cs | 90 +++++++++++++------------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/src/MiningCore/Mining/ShareRecorder.cs b/src/MiningCore/Mining/ShareRecorder.cs index 8e2b81481..c9dd5c12d 100644 --- a/src/MiningCore/Mining/ShareRecorder.cs +++ b/src/MiningCore/Mining/ShareRecorder.cs @@ -83,17 +83,31 @@ public ShareRecorder(IConnectionFactory cf, IMapper mapper, BuildFaultHandlingPolicy(); } + private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); private readonly IBlockRepository blockRepo; - private readonly IConnectionFactory cf; private readonly JsonSerializerSettings jsonSerializerSettings; private readonly IMasterClock clock; private readonly NotificationService notificationService; private ClusterConfig clusterConfig; private readonly IMapper mapper; - private readonly ConcurrentDictionary> pools = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary pools = new ConcurrentDictionary(); private readonly BlockingCollection queue = new BlockingCollection(); + class PoolContext + { + public PoolContext(IMiningPool pool, ILogger logger) + { + Pool = pool; + Logger = logger; + } + + public IMiningPool Pool; + public ILogger Logger; + public DateTime? LastBlock; + public long BlockHeight; + } + private readonly int QueueSizeWarningThreshold = 1024; private readonly TimeSpan relayReceiveTimeout = TimeSpan.FromSeconds(60); private readonly IShareRepository shareRepo; @@ -106,8 +120,6 @@ public ShareRecorder(IConnectionFactory cf, IMapper mapper, private const string PolicyContextKeyShares = "share"; private bool notifiedAdminOnPolicyFallback = false; - private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); - private void PersistSharesFaulTolerant(IList shares) { var context = new Dictionary { { PolicyContextKeyShares, shares } }; @@ -324,9 +336,7 @@ private void StartExternalStratumPublisherListeners() var urlAndTopic = (IGrouping) arg; var url = urlAndTopic.Key; var topics = new HashSet(urlAndTopic.Distinct()); - - var currentHeights = new Dictionary(); - var lastBlockTimes = new Dictionary(); + var receivedOnce = false; while (true) { @@ -347,13 +357,19 @@ private void StartExternalStratumPublisherListeners() { if (!subSocket.TryReceiveMultipartMessage(relayReceiveTimeout, ref msg, 3)) { - logger.Warn(() => $"Timeout receiving message from {url}. Reconnecting ..."); - break; + if (receivedOnce) + { + logger.Warn(() => $"Timeout receiving message from {url}. Reconnecting ..."); + break; + } + + continue; } var topic = msg.Pop().ConvertToString(Encoding.UTF8); var flags = msg.Pop().ConvertToInt32(); var data = msg.Pop().ToByteArray(); + receivedOnce = true; // validate if (!topics.Contains(topic)) @@ -368,20 +384,8 @@ private void StartExternalStratumPublisherListeners() continue; } - // lookup pool - IMiningPool pool = null; - ILogger poolLogger = null; - - if (pools.TryGetValue(topic, out var poolData)) - { - pool = poolData.Item1; - poolLogger = poolData.Item2; - } - - // extract flags - var wireFormat = (ShareRelay.WireFormat) (flags & ShareRelay.WireFormatMask); - // deserialize + var wireFormat = (ShareRelay.WireFormat)(flags & ShareRelay.WireFormatMask); Share share = null; switch (wireFormat) @@ -417,35 +421,33 @@ private void StartExternalStratumPublisherListeners() continue; } - // fill in blanks + // store share.PoolId = topic; share.Created = clock.Now; - - // persist queue.Add(share); - // log it - var source = !string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}] " : string.Empty; - poolLogger?.Info(() => $"External {source}share accepted: D={Math.Round(share.Difficulty, 3)}"); - - // update pool stats - if (pool?.NetworkStats != null) + // misc + if (pools.TryGetValue(topic, out var poolContext)) { - pool.NetworkStats.BlockHeight = share.BlockHeight; - pool.NetworkStats.NetworkDifficulty = share.NetworkDifficulty; - - currentHeights.TryGetValue(topic, out var currentHeight); - lastBlockTimes.TryGetValue(topic, out var lastBlockTime); + var pool = poolContext.Pool; + poolContext.Logger.Info(() => $"External {(!string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}] " : string.Empty)}share accepted: D={Math.Round(share.Difficulty, 3)}"); - if (currentHeight != share.BlockHeight) + // update pool stats + if (pool.NetworkStats != null) { - pool.NetworkStats.LastNetworkBlockTime = clock.Now; - currentHeights[topic] = share.BlockHeight; - lastBlockTimes[topic] = clock.Now; - } + pool.NetworkStats.BlockHeight = share.BlockHeight; + pool.NetworkStats.NetworkDifficulty = share.NetworkDifficulty; - else - pool.NetworkStats.LastNetworkBlockTime = lastBlockTime; + if (poolContext.BlockHeight != share.BlockHeight) + { + pool.NetworkStats.LastNetworkBlockTime = clock.Now; + poolContext.BlockHeight = share.BlockHeight; + poolContext.LastBlock = clock.Now; + } + + else + pool.NetworkStats.LastNetworkBlockTime = poolContext.LastBlock; + } } } } @@ -466,7 +468,7 @@ private void StartExternalStratumPublisherListeners() public void AttachPool(IMiningPool pool) { - pools[pool.Config.Id] = Tuple.Create(pool, LogUtil.GetPoolScopedLogger(typeof(ShareRecorder), pool.Config)); + pools[pool.Config.Id] = new PoolContext(pool, LogUtil.GetPoolScopedLogger(typeof(ShareRecorder), pool.Config)); pool.Shares.Subscribe(x => { queue.Add(x.Share); }); } From 20dae16a7f9004d3318a49a9bb0a37d3d70073d6 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 22:03:39 +0100 Subject: [PATCH 185/348] ZCash fix --- src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs | 2 +- src/MiningCore/Blockchain/ZCash/ZCashJob.cs | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs index 33c9795a3..2fd135fef 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs @@ -330,7 +330,6 @@ protected virtual (Share Share, string BlockHex) ProcessShareInternal(StratumCli var result = new Share { BlockHeight = BlockTemplate.Height, - BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC), NetworkDifficulty = Difficulty * shareMultiplier, Difficulty = stratumDifficulty, }; @@ -338,6 +337,7 @@ protected virtual (Share Share, string BlockHex) ProcessShareInternal(StratumCli if (isBlockCandidate) { result.IsBlockCandidate = true; + result.BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC); result.BlockHash = blockHasher.Digest(headerBytes, nTime).ToHexString(); var blockBytes = SerializeBlock(headerBytes, coinbase); diff --git a/src/MiningCore/Blockchain/ZCash/ZCashJob.cs b/src/MiningCore/Blockchain/ZCash/ZCashJob.cs index 03ceb48ce..2049d9b05 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashJob.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashJob.cs @@ -342,15 +342,19 @@ protected virtual (Share Share, string BlockHex) ProcessShareInternal(StratumCli var result = new Share { BlockHeight = BlockTemplate.Height, - IsBlockCandidate = isBlockCandidate, + NetworkDifficulty = Difficulty, Difficulty = stratumDifficulty, - BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC) }; if (isBlockCandidate) { + result.IsBlockCandidate = true; + result.BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC); + result.BlockHash = headerHashReversed.ToHexString(); + var blockBytes = SerializeBlock(headerBytes, coinbaseInitial, solutionBytes); var blockHex = blockBytes.ToHexString(); + return (result, blockHex); } From 9d56277ea29a956e32b07fb93b18d0a05b693853 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 15 Mar 2018 22:08:13 +0100 Subject: [PATCH 186/348] WIP --- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 18 +++++++-------- .../Blockchain/Ethereum/EthereumJobManager.cs | 12 +++++----- .../Blockchain/Monero/MoneroJobManager.cs | 22 +++++++++---------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 63244fa26..398b72e16 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -489,6 +489,15 @@ public virtual async Task SubmitShareAsync(StratumClient worker, object s // validate & process var (share, blockHex) = job.ProcessShare(worker, extraNonce2, nTime, nonce); + // enrich share with common data + share.PoolId = poolConfig.Id; + share.IpAddress = worker.RemoteEndpoint.Address.ToString(); + share.Miner = minerName; + share.Worker = workerName; + share.UserAgent = context.UserAgent; + share.Source = clusterConfig.ClusterName; + share.Created = clock.Now; + // if block candidate, submit & check if accepted by network if (share.IsBlockCandidate) { @@ -515,15 +524,6 @@ public virtual async Task SubmitShareAsync(StratumClient worker, object s } } - // enrich share with common data - share.PoolId = poolConfig.Id; - share.IpAddress = worker.RemoteEndpoint.Address.ToString(); - share.Miner = minerName; - share.Worker = workerName; - share.UserAgent = context.UserAgent; - share.Source = clusterConfig.ClusterName; - share.Created = clock.Now; - return share; } diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs index 44cde25c8..a30e86c8f 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs @@ -417,6 +417,12 @@ public async Task SubmitShareAsync(StratumClient worker, // validate & process var (share, fullNonceHex, headerHash, mixHash) = await job.ProcessShareAsync(worker, nonce, ethash); + // enrich share with common data + share.PoolId = poolConfig.Id; + share.NetworkDifficulty = BlockchainStats.NetworkDifficulty; + share.Source = clusterConfig.ClusterName; + share.Created = clock.Now; + // if block candidate, submit & check if accepted by network if (share.IsBlockCandidate) { @@ -430,12 +436,6 @@ public async Task SubmitShareAsync(StratumClient worker, } } - // enrich share with common data - share.PoolId = poolConfig.Id; - share.NetworkDifficulty = BlockchainStats.NetworkDifficulty; - share.Source = clusterConfig.ClusterName; - share.Created = clock.Now; - return share; } diff --git a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs index 86b873532..044eb62ad 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs @@ -277,6 +277,17 @@ public async Task SubmitShareAsync(StratumClient worker, // validate & process var (share, blobHex, blobHash) = job.ProcessShare(request.Nonce, workerJob.ExtraNonce, request.Hash, worker); + // enrich share with common data + share.PoolId = poolConfig.Id; + share.IpAddress = worker.RemoteEndpoint.Address.ToString(); + share.Miner = context.MinerName; + share.Worker = context.WorkerName; + share.PayoutInfo = context.PaymentId; + share.UserAgent = context.UserAgent; + share.Source = clusterConfig.ClusterName; + share.NetworkDifficulty = job.BlockTemplate.Difficulty; + share.Created = clock.Now; + // if block candidate, submit & check if accepted by network if (share.IsBlockCandidate) { @@ -298,17 +309,6 @@ public async Task SubmitShareAsync(StratumClient worker, } } - // enrich share with common data - share.PoolId = poolConfig.Id; - share.IpAddress = worker.RemoteEndpoint.Address.ToString(); - share.Miner = context.MinerName; - share.Worker = context.WorkerName; - share.PayoutInfo = context.PaymentId; - share.UserAgent = context.UserAgent; - share.Source = clusterConfig.ClusterName; - share.NetworkDifficulty = job.BlockTemplate.Difficulty; - share.Created = clock.Now; - return share; } From 41ec87703a386ee9fd584681e70f6439988d8ded Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 16 Mar 2018 09:11:07 +0100 Subject: [PATCH 187/348] Comments --- src/MiningCore/Mining/ShareRecorder.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/MiningCore/Mining/ShareRecorder.cs b/src/MiningCore/Mining/ShareRecorder.cs index c9dd5c12d..b0a5d433d 100644 --- a/src/MiningCore/Mining/ShareRecorder.cs +++ b/src/MiningCore/Mining/ShareRecorder.cs @@ -346,15 +346,17 @@ private void StartExternalStratumPublisherListeners() { subSocket.Connect(url); + // subscribe to all topics foreach (var topic in topics) subSocket.Subscribe(topic); logger.Info($"Monitoring external stratum {url}/[{string.Join(", ", topics)}]"); - var msg = (NetMQMessage) null; - while (true) { + // receive + var msg = (NetMQMessage)null; + if (!subSocket.TryReceiveMultipartMessage(relayReceiveTimeout, ref msg, 3)) { if (receivedOnce) @@ -363,9 +365,11 @@ private void StartExternalStratumPublisherListeners() break; } + // retry continue; } + // extract frames var topic = msg.Pop().ConvertToString(Encoding.UTF8); var flags = msg.Pop().ConvertToInt32(); var data = msg.Pop().ToByteArray(); From fae057b57fe899f433b3bfa6890505616cf9bda9 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 16 Mar 2018 15:55:37 +0100 Subject: [PATCH 188/348] Cryptonote Tests --- src/MiningCore.Tests/Crypto/CrytonoteTests.cs | 63 +++++++++++++++++++ src/MiningCore.Tests/Crypto/HashingTests.cs | 36 +++++------ .../Native/LibCryptonoteTests.cs | 8 +-- 3 files changed, 85 insertions(+), 22 deletions(-) create mode 100644 src/MiningCore.Tests/Crypto/CrytonoteTests.cs diff --git a/src/MiningCore.Tests/Crypto/CrytonoteTests.cs b/src/MiningCore.Tests/Crypto/CrytonoteTests.cs new file mode 100644 index 000000000..093f059df --- /dev/null +++ b/src/MiningCore.Tests/Crypto/CrytonoteTests.cs @@ -0,0 +1,63 @@ +using MiningCore.Extensions; +using MiningCore.Native; +using Xunit; + +namespace MiningCore.Tests.Crypto +{ + public class CrytonoteTests : TestBase + { + [Fact] + public void Crytonote_Hash_Slow() + { + var blobConverted = "0106a2aaafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42580100a4b1e2f4baf6ab7109071ab59bc52dba740d1de99fa0ae0c4afd6ea9f40c5d87ec01".HexToByteArray(); + var result = LibCryptonote.CryptonightHashSlow(blobConverted).ToHexString(); + + Assert.Equal("a845ffbdf83ae9a8ffa504a1011efbd5ed2294bb9da591d3b583740568402c00", result); + } + + [Fact] + public void Crytonote_Hash_Fast() + { + var blobConverted = "0106a2aaafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42580100a4b1e2f4baf6ab7109071ab59bc52dba740d1de99fa0ae0c4afd6ea9f40c5d87ec01".HexToByteArray(); + var result = LibCryptonote.CryptonightHashFast(blobConverted).ToHexString(); + + Assert.Equal("ddc0e3a33b605ce39fa2d16a98d7634e33399ab1e4b56b3bdd3414b655fe9a98", result); + } + + [Fact] + public void Crytonote_Hash_Slow_Lite() + { + var blobConverted = "0106f1adafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42597710c48c6d885e2622f40f82ecd9b9fd538f28df9b0557e07cd3237a31c76569ada98001".HexToByteArray(); + var result = LibCryptonote.CryptonightHashSlowLite(blobConverted).ToHexString(); + + Assert.Equal("0769caee428a232cffb76fa200f174ff962734f24e7b3bf8d1b0d4e8ba6ceebf", result); + } + + [Fact] + public void Crytonote_ConvertBlob() + { + var blob = "0106e5b3afd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b421c0300a401d90101ff9d0106d6d6a88702023c62e43372a58cb588147e20be53a27083f5c522f33c722b082ab7518c48cda280b4c4c32102609ec96e2499ee267d70efefc49f26e330526d3ef455314b7b5ba268a6045f8c80c0fc82aa0202fe5cc0fa56c4277d1a47827edce4725571529d57f33c73ada481ef84c323f30a8090cad2c60e02d88bf5e72a611c8b8464ce29e3b1adbfe1ae163886d9150fe511171cada98fcb80e08d84ddcb0102441915aaf9fbaf70ff454c701a6ae2bd59bb94dc0b888bf7e5d06274ee9238ca80c0caf384a302024078526e2132def44bde2806242652f5944e632f7d94290dd6ee5dda1929f5ee2b016e29f25f07ec2a8df59f0e118a6c9a4b769b745dc0c729071f6e0399d2585745020800000000012e7f76000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".HexToByteArray(); + var result = LibCryptonote.ConvertBlob(blob, 330).ToHexString(); + + Assert.Equal("0106e5b3afd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b421c0300a4487286e262e95b8d2163a0c8b73527e8c9425adbdc4e532cf0ef4241f9ffbe9e01", result); + } + + [Fact] + public void Crytonote_DecodeAddress() + { + var address = "48nhyWcSey31ngSEhV8j8NPm6B8PistCQJBjjDjmTvRSTWYg6iocAw131vE2JPh3ps33vgQDKLrUx3fcErusYWcMJBxpm1d"; + var result = LibCryptonote.DecodeAddress(address); + + Assert.Equal(18ul, result); + } + + [Fact] + public void Crytonote_DecodeIntegratedAddress() + { + var address = "4BrL51JCc9NGQ71kWhnYoDRffsDZy7m1HUU7MRU4nUMXAHNFBEJhkTZV9HdaL4gfuNBxLPc3BeMkLGaPbF5vWtANQsGwTGg55Kq4p3ENE7"; + var result = LibCryptonote.DecodeIntegratedAddress(address); + + Assert.Equal(19ul, result); + } + } +} diff --git a/src/MiningCore.Tests/Crypto/HashingTests.cs b/src/MiningCore.Tests/Crypto/HashingTests.cs index a0be06bd4..ae8292e44 100644 --- a/src/MiningCore.Tests/Crypto/HashingTests.cs +++ b/src/MiningCore.Tests/Crypto/HashingTests.cs @@ -18,7 +18,7 @@ public class HashingTests : TestBase private static readonly byte[] testValue2 = Enumerable.Repeat((byte)0x80, 80).ToArray(); [Fact] - public void Blake_Hash_Should_Match() + public void Blake_Hash() { var hasher = new Blake(); var result = hasher.Digest(testValue).ToHexString(); @@ -34,7 +34,7 @@ public void Blake_Hash_Should_Throw_On_Null_Input() } [Fact] - public void Blake2s_Hash_Should_Match() + public void Blake2s_Hash() { var hasher = new Blake2s(); var result = hasher.Digest(testValue2).ToHexString(); @@ -50,7 +50,7 @@ public void Blake2s_Hash_Should_Throw_On_Null_Input() } [Fact] - public void Groestl_Hash_Should_Match() + public void Groestl_Hash() { var hasher = new Groestl(); var result = hasher.Digest(testValue).ToHexString(); @@ -66,7 +66,7 @@ public void Groestl_Hash_Should_Throw_On_Null_Input() } [Fact] - public void Kezzak_Hash_Should_Match() + public void Kezzak_Hash() { var hasher = new Kezzak(); var result = hasher.Digest(testValue, 0ul).ToHexString(); @@ -82,7 +82,7 @@ public void Kezzak_Hash_Should_Throw_On_Null_Input() } [Fact] - public void Scrypt_Hash_Should_Match() + public void Scrypt_Hash() { var hasher = new Scrypt(1024, 1); var result = hasher.Digest(testValue).ToHexString(); @@ -98,7 +98,7 @@ public void Scrypt_Hash_Should_Throw_On_Null_Input() } [Fact] - public void NeoScrypt_Hash_Should_Match() + public void NeoScrypt_Hash() { var hasher = new NeoScrypt(0); var result = hasher.Digest(testValue2).ToHexString(); @@ -114,7 +114,7 @@ public void NeoScrypt_Hash_Should_Throw_On_Null_Input() } [Fact] - public void ScryptN_Hash_Should_Match() + public void ScryptN_Hash() { var clock = new MockMasterClock { CurrentTime = new DateTime(2017, 10, 16) }; var hasher = new ScryptN(clock, new []{ Tuple.Create(2048L, 1389306217L) }); @@ -132,7 +132,7 @@ public void ScryptN_Hash_Should_Throw_On_Null_Input() } [Fact] - public void Lyra2Rev2_Hash_Should_Match() + public void Lyra2Rev2_Hash() { var hasher = new Lyra2Rev2(); var result = hasher.Digest(Enumerable.Repeat((byte) 5, 80).ToArray()).ToHexString(); @@ -155,7 +155,7 @@ public void Lyra2Rev2_Hash_Should_Throw_On_Short_Input() } [Fact] - public void Sha256D_Hash_Should_Match() + public void Sha256D_Hash() { var hasher = new Sha256D(); var result = hasher.Digest(testValue).ToHexString(); @@ -171,7 +171,7 @@ public void Sha256D_Hash_Should_Throw_On_Null_Input() } [Fact] - public void Sha256S_Hash_Should_Match() + public void Sha256S_Hash() { var hasher = new Sha256S(); var result = hasher.Digest(testValue).ToHexString(); @@ -187,7 +187,7 @@ public void Sha256S_Hash_Should_Throw_On_Null_Input() } [Fact] - public void X11_Hash_Should_Match() + public void X11_Hash() { var hasher = new X11(); var result = hasher.Digest(testValue).ToHexString(); @@ -203,7 +203,7 @@ public void X11_Hash_Should_Throw_On_Null_Input() } [Fact] - public void X17_Hash_Should_Match() + public void X17_Hash() { var hasher = new X17(); var result = hasher.Digest(testValue).ToHexString(); @@ -219,7 +219,7 @@ public void X17_Hash_Should_Throw_On_Null_Input() } [Fact] - public void Skein_Hash_Should_Match() + public void Skein_Hash() { var hasher = new Skein(); var result = hasher.Digest(testValue).ToHexString(); @@ -235,7 +235,7 @@ public void Skein_Hash_Should_Throw_On_Null_Input() } [Fact] - public void Qubit_Hash_Should_Match() + public void Qubit_Hash() { var hasher = new Qubit(); var result = hasher.Digest(testValue).ToHexString(); @@ -251,7 +251,7 @@ public void Qubit_Hash_Should_Throw_On_Null_Input() } [Fact] - public void GroestlMyriad_Hash_Should_Match() + public void GroestlMyriad_Hash() { var hasher = new GroestlMyriad(); var result = hasher.Digest(testValue).ToHexString(); @@ -267,7 +267,7 @@ public void GroestlMyriad_Hash_Should_Throw_On_Null_Input() } [Fact] - public void DigestReverser_Hash_Should_Match() + public void DigestReverser_Hash() { var hasher = new DigestReverser(new Sha256S()); var result = hasher.Digest(testValue).ToHexString(); @@ -317,7 +317,7 @@ public void EquihashVerifier_Should_Throw_On_Wrong_Argument_Length() } [Fact] - public void Sha3_256_Hash_Should_Match() + public void Sha3_256_Hash() { var hasher = new Sha3_256(); var result = hasher.Digest(testValue).ToHexString(); @@ -333,7 +333,7 @@ public void Sha3_256_Hash_Should_Throw_On_Null_Input() } [Fact] - public void Sha3_512_Hash_Should_Match() + public void Sha3_512_Hash() { var hasher = new Sha3_512(); var result = hasher.Digest(testValue).ToHexString(); diff --git a/src/MiningCore.Tests/Native/LibCryptonoteTests.cs b/src/MiningCore.Tests/Native/LibCryptonoteTests.cs index a699012e7..2e4e2cb0a 100644 --- a/src/MiningCore.Tests/Native/LibCryptonoteTests.cs +++ b/src/MiningCore.Tests/Native/LibCryptonoteTests.cs @@ -12,7 +12,7 @@ public class HashinLibCryptonoteTestsgTests : TestBase private static readonly byte[] testValue = Enumerable.Repeat((byte)0x80, 128).ToArray(); [Fact] - public void Cryptonote_SlowHash_Should_Match() + public void Cryptonote_SlowHash() { var result = LibCryptonote.CryptonightHashSlow(testValue).ToHexString(); @@ -26,7 +26,7 @@ public void Cryptonote_SlowHash_Should_Throw_On_Null_Argument() } [Fact] - public void Cryptonote_FastHash_Should_Match() + public void Cryptonote_FastHash() { var result = LibCryptonote.CryptonightHashFast(testValue).ToHexString(); @@ -40,7 +40,7 @@ public void Cryptonote_FastHash_Should_Throw_On_Null_Argument() } [Fact] - public void Cryptonote_DecodeAddress_Should_Match() + public void Cryptonote_DecodeAddress() { var result = LibCryptonote.DecodeAddress("9wviCeWe2D8XS82k2ovp5EUYLzBt9pYNW2LXUFsZiv8S3Mt21FZ5qQaAroko1enzw3eGr9qC7X1D7Geoo2RrAotYPwq9Gm8"); Assert.Equal(53u, result); @@ -54,7 +54,7 @@ public void Cryptonote_DecodeAddress_Should_Throw_On_Null_Or_Empty_Argument() } [Fact] - public void Cryptonote_ConvertBlob_Should_Match() + public void Cryptonote_ConvertBlob() { var blob = "0105bfdfcecd05583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b420000000001d90101ff9d0106d6d6a88702020a79e36c5f5ac69abb68daa616b70e4dc911ed2edf50133fc121447cc403cd6780b4c4c32102b3adc5521c68a35e2dd1934e30b5fada872b384dbbf8c4e8130e43bd0097b8b680c0fc82aa0202b186f6745517ec23a87df7811849d71914a222c937da3e3a39c7bde6f27d2dc98090cad2c60e02df3a6eed49d05b0163986888ebe7da3fae808a72f3beec97346e0a18a960a7b180e08d84ddcb0102f37220a0c601e2dfe78cfab584cabeecf59079b3b2ee045561fb83ebf67941ba80c0caf384a30202b5e50c62333f3237d497eac37b26bd1217b6996eeb7d45e099b71b0f0b5399162b011c2515730ca7e8bb9b79e177557a1fa8b41e9aee544b25d69dc46f12f66b13f102080000000001ff0d7500".HexToByteArray(); var result = LibCryptonote.ConvertBlob(blob, blob.Length).ToHexString(); From 43d6de8a0ccb31be2e47c2ca87e0bef9d0e15094 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 16 Mar 2018 15:58:25 +0100 Subject: [PATCH 189/348] Tests --- src/MiningCore.Tests/Crypto/CrytonoteTests.cs | 28 +++++++- .../Native/LibCryptonoteTests.cs | 71 ------------------- 2 files changed, 27 insertions(+), 72 deletions(-) delete mode 100644 src/MiningCore.Tests/Native/LibCryptonoteTests.cs diff --git a/src/MiningCore.Tests/Crypto/CrytonoteTests.cs b/src/MiningCore.Tests/Crypto/CrytonoteTests.cs index 093f059df..30bba965f 100644 --- a/src/MiningCore.Tests/Crypto/CrytonoteTests.cs +++ b/src/MiningCore.Tests/Crypto/CrytonoteTests.cs @@ -1,4 +1,5 @@ -using MiningCore.Extensions; +using System; +using MiningCore.Extensions; using MiningCore.Native; using Xunit; @@ -15,6 +16,12 @@ public void Crytonote_Hash_Slow() Assert.Equal("a845ffbdf83ae9a8ffa504a1011efbd5ed2294bb9da591d3b583740568402c00", result); } + [Fact] + public void Cryptonote_SlowHash_Should_Throw_On_Null_Argument() + { + Assert.Throws(() => LibCryptonote.CryptonightHashSlow(null)); + } + [Fact] public void Crytonote_Hash_Fast() { @@ -24,6 +31,12 @@ public void Crytonote_Hash_Fast() Assert.Equal("ddc0e3a33b605ce39fa2d16a98d7634e33399ab1e4b56b3bdd3414b655fe9a98", result); } + [Fact] + public void Cryptonote_FastHash_Should_Throw_On_Null_Argument() + { + Assert.Throws(() => LibCryptonote.CryptonightHashFast(null)); + } + [Fact] public void Crytonote_Hash_Slow_Lite() { @@ -42,6 +55,12 @@ public void Crytonote_ConvertBlob() Assert.Equal("0106e5b3afd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b421c0300a4487286e262e95b8d2163a0c8b73527e8c9425adbdc4e532cf0ef4241f9ffbe9e01", result); } + [Fact] + public void Cryptonote_ConvertBlob_Should_Throw_On_Null_Argument() + { + Assert.Throws(() => LibCryptonote.ConvertBlob(null, 0)); + } + [Fact] public void Crytonote_DecodeAddress() { @@ -51,6 +70,13 @@ public void Crytonote_DecodeAddress() Assert.Equal(18ul, result); } + [Fact] + public void Cryptonote_DecodeAddress_Should_Throw_On_Null_Or_Empty_Argument() + { + Assert.Throws(() => LibCryptonote.DecodeAddress(null)); + Assert.Throws(() => LibCryptonote.DecodeAddress("")); + } + [Fact] public void Crytonote_DecodeIntegratedAddress() { diff --git a/src/MiningCore.Tests/Native/LibCryptonoteTests.cs b/src/MiningCore.Tests/Native/LibCryptonoteTests.cs deleted file mode 100644 index 2e4e2cb0a..000000000 --- a/src/MiningCore.Tests/Native/LibCryptonoteTests.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using MiningCore.Extensions; -using MiningCore.Native; -using Xunit; - -namespace MiningCore.Tests.Native -{ - public class HashinLibCryptonoteTestsgTests : TestBase - { - private static readonly byte[] testValue = Enumerable.Repeat((byte)0x80, 128).ToArray(); - - [Fact] - public void Cryptonote_SlowHash() - { - var result = LibCryptonote.CryptonightHashSlow(testValue).ToHexString(); - - Assert.Equal("9a267e32aefcc40ab12a906fd3f2de45a24a5ccde9e8b84528e656577f14e0fe", result); - } - - [Fact] - public void Cryptonote_SlowHash_Should_Throw_On_Null_Argument() - { - Assert.Throws(() => LibCryptonote.CryptonightHashSlow(null)); - } - - [Fact] - public void Cryptonote_FastHash() - { - var result = LibCryptonote.CryptonightHashFast(testValue).ToHexString(); - - Assert.Equal("80ad002b1c333a29913d9edb5340412b121c0e9045e59fa9b2aabb53f9dcc92d", result); - } - - [Fact] - public void Cryptonote_FastHash_Should_Throw_On_Null_Argument() - { - Assert.Throws(() => LibCryptonote.CryptonightHashFast(null)); - } - - [Fact] - public void Cryptonote_DecodeAddress() - { - var result = LibCryptonote.DecodeAddress("9wviCeWe2D8XS82k2ovp5EUYLzBt9pYNW2LXUFsZiv8S3Mt21FZ5qQaAroko1enzw3eGr9qC7X1D7Geoo2RrAotYPwq9Gm8"); - Assert.Equal(53u, result); - } - - [Fact] - public void Cryptonote_DecodeAddress_Should_Throw_On_Null_Or_Empty_Argument() - { - Assert.Throws(() => LibCryptonote.DecodeAddress(null)); - Assert.Throws(() => LibCryptonote.DecodeAddress("")); - } - - [Fact] - public void Cryptonote_ConvertBlob() - { - var blob = "0105bfdfcecd05583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b420000000001d90101ff9d0106d6d6a88702020a79e36c5f5ac69abb68daa616b70e4dc911ed2edf50133fc121447cc403cd6780b4c4c32102b3adc5521c68a35e2dd1934e30b5fada872b384dbbf8c4e8130e43bd0097b8b680c0fc82aa0202b186f6745517ec23a87df7811849d71914a222c937da3e3a39c7bde6f27d2dc98090cad2c60e02df3a6eed49d05b0163986888ebe7da3fae808a72f3beec97346e0a18a960a7b180e08d84ddcb0102f37220a0c601e2dfe78cfab584cabeecf59079b3b2ee045561fb83ebf67941ba80c0caf384a30202b5e50c62333f3237d497eac37b26bd1217b6996eeb7d45e099b71b0f0b5399162b011c2515730ca7e8bb9b79e177557a1fa8b41e9aee544b25d69dc46f12f66b13f102080000000001ff0d7500".HexToByteArray(); - var result = LibCryptonote.ConvertBlob(blob, blob.Length).ToHexString(); - var expected = "0105bfdfcecd05583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b4200000000f262fa431f692fa1d8a6e89fb809487a2133dd6fd999d95c664b964df354ac4701"; - Assert.Equal(expected, result); - } - - [Fact] - public void Cryptonote_ConvertBlob_Should_Throw_On_Null_Argument() - { - Assert.Throws(() => LibCryptonote.ConvertBlob(null, 0)); - } - } -} From 2fb3c9c87586257b178630003997fe1a00c1e73a Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 16 Mar 2018 16:32:34 +0100 Subject: [PATCH 190/348] Monero v7 --- src/MiningCore.Tests/Crypto/CrytonoteTests.cs | 4 +- src/MiningCore/Blockchain/Monero/MoneroJob.cs | 19 +- src/MiningCore/Native/LibCryptonote.cs | 6 +- src/MiningCore/Program.cs | 2 +- .../runtimes/win-x64/native/libcryptonote.dll | Bin 412672 -> 439296 bytes .../runtimes/win-x86/native/libcryptonote.dll | Bin 366592 -> 394752 bytes src/Native/libcryptonote/Makefile | 3 +- src/Native/libcryptonote/Makefile.MSys2 | 3 +- src/Native/libcryptonote/common/base58.cpp | 18 +- src/Native/libcryptonote/common/base58.h | 2 +- src/Native/libcryptonote/common/int-util.h | 19 +- src/Native/libcryptonote/common/pod-class.h | 2 +- src/Native/libcryptonote/common/util.h | 215 +++++ src/Native/libcryptonote/common/varint.h | 6 +- .../contrib/epee/include/console_handler.h | 27 +- .../contrib/epee/include/copyable_atomic.h | 2 + .../contrib/epee/include/file_io_utils.h | 108 ++- .../libcryptonote/contrib/epee/include/hex.h | 2 +- .../contrib/epee/include/math_helper.h | 1 + .../contrib/epee/include/memwipe.h | 80 ++ .../contrib/epee/include/misc_os_dependent.h | 11 +- .../epee/include/net/abstract_tcp_server.h | 2 +- .../epee/include/net/abstract_tcp_server2.h | 16 +- .../epee/include/net/abstract_tcp_server2.inl | 67 +- .../epee/include/net/connection_basic.hpp | 141 +++ .../contrib/epee/include/net/http_auth.h | 15 +- .../contrib/epee/include/net/http_base.h | 6 +- .../contrib/epee/include/net/http_client.h | 199 +++-- .../epee/include/net/http_client_base.h | 6 +- .../epee/include/net/http_protocol_handler.h | 5 +- .../include/net/http_protocol_handler.inl | 65 +- .../epee/include/net/http_server_impl_base.h | 13 +- .../contrib/epee/include/net/levin_base.h | 1 + .../epee/include/net/levin_client_async.h | 12 +- .../epee/include/net/levin_protocol_handler.h | 2 + .../net/levin_protocol_handler_async.h | 118 ++- .../contrib/epee/include/net/net_helper.h | 155 +++- .../epee/include/net/net_parse_helpers.h | 4 +- .../contrib/epee/include/net/net_utils_base.h | 248 +++--- .../include/net/network_throttle-detail.hpp | 125 +++ .../epee/include/net/network_throttle.hpp | 171 ++++ .../contrib/epee/include/profile_tools.h | 2 + .../contrib/epee/include/readline_buffer.h | 2 - .../contrib/epee/include/reg_exp_definer.h | 2 +- .../serialization/keyvalue_serialization.h | 1 - .../keyvalue_serialization_overloads.h | 93 +- .../libcryptonote/contrib/epee/include/span.h | 4 +- .../include/storages/http_abstract_invoke.h | 7 +- .../include/storages/levin_abstract_invoke2.h | 28 +- .../storages/portable_storage_from_json.h | 2 + .../storages/portable_storage_to_bin.h | 4 + .../portable_storage_val_converters.h | 33 + .../contrib/epee/include/string_tools.h | 16 +- .../contrib/epee/include/wipeable_string.h | 67 ++ .../libcryptonote/contrib/epee/src/hex.cpp | 2 +- .../libcryptonote/contrib/epee/src/memwipe.c | 116 +++ .../contrib/epee/src/wipeable_string.cpp | 146 ++++ .../libcryptonote/crypto/CMakeLists.txt | 103 +++ src/Native/libcryptonote/crypto/blake256.c | 4 +- src/Native/libcryptonote/crypto/blake256.h | 2 +- src/Native/libcryptonote/crypto/chacha.c | 182 ++++ src/Native/libcryptonote/crypto/chacha.h | 91 ++ .../libcryptonote/crypto/crypto-ops-data.c | 3 +- src/Native/libcryptonote/crypto/crypto-ops.c | 817 +++++++++++++++++- src/Native/libcryptonote/crypto/crypto-ops.h | 9 +- src/Native/libcryptonote/crypto/crypto.cpp | 147 +++- src/Native/libcryptonote/crypto/crypto.h | 58 +- src/Native/libcryptonote/crypto/generic-ops.h | 2 +- src/Native/libcryptonote/crypto/groestl.h | 2 +- .../libcryptonote/crypto/groestl_tables.h | 2 +- .../libcryptonote/crypto/hash-extra-blake.c | 2 +- .../libcryptonote/crypto/hash-extra-groestl.c | 2 +- .../libcryptonote/crypto/hash-extra-jh.c | 2 +- .../libcryptonote/crypto/hash-extra-skein.c | 2 +- src/Native/libcryptonote/crypto/hash-ops.h | 19 +- src/Native/libcryptonote/crypto/hash.c | 2 +- src/Native/libcryptonote/crypto/hash.h | 31 +- src/Native/libcryptonote/crypto/initializer.h | 7 +- src/Native/libcryptonote/crypto/keccak.c | 31 +- src/Native/libcryptonote/crypto/keccak.h | 2 +- src/Native/libcryptonote/crypto/oaes_lib.c | 20 +- src/Native/libcryptonote/crypto/random.c | 7 +- src/Native/libcryptonote/crypto/random.h | 2 +- src/Native/libcryptonote/crypto/skein_port.h | 2 +- src/Native/libcryptonote/crypto/slow-hash.c | 120 ++- src/Native/libcryptonote/crypto/tree-hash.c | 10 +- .../cryptonote_basic/CMakeLists.txt | 79 ++ .../cryptonote_basic/account.cpp | 62 +- .../libcryptonote/cryptonote_basic/account.h | 21 +- .../account_boost_serialization.h | 2 +- .../cryptonote_basic/blobdatatype.h | 36 + .../cryptonote_basic/connection_context.h | 4 +- .../cryptonote_basic/cryptonote_basic.h | 72 +- .../cryptonote_basic_impl.cpp | 102 +-- .../cryptonote_basic/cryptonote_basic_impl.h | 65 +- .../cryptonote_boost_serialization.h | 53 +- .../cryptonote_format_utils.cpp | 238 +++-- .../cryptonote_format_utils.h | 35 +- .../cryptonote_basic/cryptonote_stat_info.h | 2 +- .../cryptonote_basic/difficulty.cpp | 2 +- .../cryptonote_basic/difficulty.h | 2 +- .../cryptonote_basic/hardfork.cpp | 2 +- .../libcryptonote/cryptonote_basic/hardfork.h | 3 +- .../libcryptonote/cryptonote_basic/miner.cpp | 162 +++- .../libcryptonote/cryptonote_basic/miner.h | 4 +- .../cryptonote_basic/subaddress_index.h | 102 +++ .../libcryptonote/cryptonote_basic/tx_extra.h | 16 +- .../cryptonote_basic/verification_context.h | 2 +- src/Native/libcryptonote/cryptonote_config.h | 46 +- src/Native/libcryptonote/device/device.hpp | 188 ++++ src/Native/libcryptonote/exports.cpp | 10 +- .../libcryptonote/libcryptonote.vcxproj | 14 +- .../libcryptonote.vcxproj.filters | 18 +- .../serialization/binary_archive.h | 4 +- .../serialization/binary_utils.h | 2 +- .../libcryptonote/serialization/container.h | 113 +++ .../libcryptonote/serialization/crypto.h | 6 +- .../serialization/debug_archive.h | 2 +- .../serialization/json_archive.h | 2 +- .../libcryptonote/serialization/json_utils.h | 2 +- src/Native/libcryptonote/serialization/list.h | 72 +- src/Native/libcryptonote/serialization/pair.h | 2 +- .../serialization/serialization.h | 36 +- .../libcryptonote/serialization/string.h | 2 +- .../libcryptonote/serialization/variant.h | 4 +- .../libcryptonote/serialization/vector.h | 73 +- src/Native/libcryptonote/stdafx.h | 30 +- 127 files changed, 4820 insertions(+), 981 deletions(-) create mode 100644 src/Native/libcryptonote/common/util.h create mode 100644 src/Native/libcryptonote/contrib/epee/include/memwipe.h create mode 100644 src/Native/libcryptonote/contrib/epee/include/net/connection_basic.hpp create mode 100644 src/Native/libcryptonote/contrib/epee/include/net/network_throttle-detail.hpp create mode 100644 src/Native/libcryptonote/contrib/epee/include/net/network_throttle.hpp create mode 100644 src/Native/libcryptonote/contrib/epee/include/wipeable_string.h create mode 100644 src/Native/libcryptonote/contrib/epee/src/memwipe.c create mode 100644 src/Native/libcryptonote/contrib/epee/src/wipeable_string.cpp create mode 100644 src/Native/libcryptonote/crypto/CMakeLists.txt create mode 100644 src/Native/libcryptonote/crypto/chacha.c create mode 100644 src/Native/libcryptonote/crypto/chacha.h create mode 100644 src/Native/libcryptonote/cryptonote_basic/CMakeLists.txt create mode 100644 src/Native/libcryptonote/cryptonote_basic/blobdatatype.h create mode 100644 src/Native/libcryptonote/cryptonote_basic/subaddress_index.h create mode 100644 src/Native/libcryptonote/device/device.hpp create mode 100644 src/Native/libcryptonote/serialization/container.h diff --git a/src/MiningCore.Tests/Crypto/CrytonoteTests.cs b/src/MiningCore.Tests/Crypto/CrytonoteTests.cs index 30bba965f..b8e32bacf 100644 --- a/src/MiningCore.Tests/Crypto/CrytonoteTests.cs +++ b/src/MiningCore.Tests/Crypto/CrytonoteTests.cs @@ -11,7 +11,7 @@ public class CrytonoteTests : TestBase public void Crytonote_Hash_Slow() { var blobConverted = "0106a2aaafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42580100a4b1e2f4baf6ab7109071ab59bc52dba740d1de99fa0ae0c4afd6ea9f40c5d87ec01".HexToByteArray(); - var result = LibCryptonote.CryptonightHashSlow(blobConverted).ToHexString(); + var result = LibCryptonote.CryptonightHashSlow(blobConverted, 0).ToHexString(); Assert.Equal("a845ffbdf83ae9a8ffa504a1011efbd5ed2294bb9da591d3b583740568402c00", result); } @@ -19,7 +19,7 @@ public void Crytonote_Hash_Slow() [Fact] public void Cryptonote_SlowHash_Should_Throw_On_Null_Argument() { - Assert.Throws(() => LibCryptonote.CryptonightHashSlow(null)); + Assert.Throws(() => LibCryptonote.CryptonightHashSlow(null, 0)); } [Fact] diff --git a/src/MiningCore/Blockchain/Monero/MoneroJob.cs b/src/MiningCore/Blockchain/Monero/MoneroJob.cs index ab5d4c2a8..7afb65d85 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJob.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJob.cs @@ -46,11 +46,15 @@ public MoneroJob(GetBlockTemplateResponse blockTemplate, byte[] instanceId, stri switch (poolConfig.Coin.Type) { case CoinType.AEON: - hashSlow = LibCryptonote.CryptonightHashSlowLite; + hashSlow = (buf, variant)=> LibCryptonote.CryptonightHashSlowLite(buf); break; - default: - hashSlow = LibCryptonote.CryptonightHashSlow; + case CoinType.XMR: + hashSlow = LibCryptonote.CryptonightHashSlow; + break; + + default: + hashSlow = (buf, variant) => LibCryptonote.CryptonightHashSlow(buf, 0); break; } @@ -58,7 +62,7 @@ public MoneroJob(GetBlockTemplateResponse blockTemplate, byte[] instanceId, stri PrepareBlobTemplate(instanceId); } - private readonly Func> hashSlow; + private readonly Func> hashSlow; private byte[] blobTemplate; private uint extraNonce; @@ -162,8 +166,11 @@ public void PrepareWorkerJob(MoneroWorkerJob workerJob, out string blob, out str if (blobConverted == null) throw new StratumException(StratumError.MinusOne, "malformed blob"); - // hash it - using (var hashSeg = hashSlow(blobConverted)) + // PoW variant + var hashVariant = blobConverted[0] >= 7 ? blobConverted[0] - 6 : 0; + + // hash it + using (var hashSeg = hashSlow(blobConverted, hashVariant)) { var hash = hashSeg.ToHexString(); if (hash != workerHash) diff --git a/src/MiningCore/Native/LibCryptonote.cs b/src/MiningCore/Native/LibCryptonote.cs index 165fcd11e..fbb36d543 100644 --- a/src/MiningCore/Native/LibCryptonote.cs +++ b/src/MiningCore/Native/LibCryptonote.cs @@ -39,7 +39,7 @@ public static unsafe class LibCryptonote private static extern UInt64 decode_integrated_address(byte* input, int inputSize); [DllImport("libcryptonote", EntryPoint = "cn_slow_hash_export", CallingConvention = CallingConvention.Cdecl)] - private static extern int cn_slow_hash(byte* input, byte* output, uint inputLength); + private static extern int cn_slow_hash(byte* input, byte* output, uint inputLength, int variant); [DllImport("libcryptonote", EntryPoint = "cn_slow_hash_lite_export", CallingConvention = CallingConvention.Cdecl)] private static extern int cn_slow_hash_lite(byte* input, byte* output, uint inputLength); @@ -124,7 +124,7 @@ public static UInt64 DecodeIntegratedAddress(string address) } } - public static PooledArraySegment CryptonightHashSlow(byte[] data) + public static PooledArraySegment CryptonightHashSlow(byte[] data, int variant) { Contract.RequiresNonNull(data, nameof(data)); @@ -134,7 +134,7 @@ public static PooledArraySegment CryptonightHashSlow(byte[] data) { fixed(byte* output = result.Array) { - cn_slow_hash(input, output, (uint) data.Length); + cn_slow_hash(input, output, (uint) data.Length, variant); } } diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index 0d2bc8677..cb991e461 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -647,7 +647,7 @@ private static void OnAppDomainUnhandledException(object sender, UnhandledExcept private static void TouchNativeLibs() { - Console.WriteLine(LibCryptonote.CryptonightHashSlow(Encoding.UTF8.GetBytes("test")).ToHexString()); + 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()); } diff --git a/src/MiningCore/runtimes/win-x64/native/libcryptonote.dll b/src/MiningCore/runtimes/win-x64/native/libcryptonote.dll index 15a282c4992bd2ed2ed2c5d69885516ca1c028ce..8d1a1fa94ba9206ace015b4774e98fc3f6163f47 100644 GIT binary patch delta 231579 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π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 diff --git a/src/MiningCore/runtimes/win-x86/native/libcryptonote.dll b/src/MiningCore/runtimes/win-x86/native/libcryptonote.dll index 1f4803a9d8fac460b99cfcbe58a468038c6eac97..bc0245ed5fbc61d00ec47ce92826c45b53d9d3f1 100644 GIT binary patch delta 189443 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 delta 160780 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 AYXATM diff --git a/src/Native/libcryptonote/Makefile b/src/Native/libcryptonote/Makefile index 17df8a373..0cd26e4c2 100644 --- a/src/Native/libcryptonote/Makefile +++ b/src/Native/libcryptonote/Makefile @@ -7,7 +7,8 @@ LDLIBS = -lboost_system -lboost_date_time TARGET = libcryptonote.so OBJECTS = contrib/epee/src/hex.o \ - common/base58.o crypto/aesb.o crypto/blake256.o crypto/chacha8.o \ + common/base58.o crypto/aesb.o crypto/blake256.o crypto/chacha.o \ + contrib/epee/src/memwipe.o \ crypto/crypto-ops-data.o crypto/crypto-ops.o crypto/crypto.o crypto/groestl.o crypto/hash-extra-blake.o \ crypto/hash-extra-groestl.o crypto/hash-extra-jh.o crypto/hash-extra-skein.o crypto/hash.o crypto/jh.o \ crypto/keccak.o crypto/oaes_lib.o crypto/random.o crypto/skein.o crypto/slow-hash.o crypto/tree-hash.o \ diff --git a/src/Native/libcryptonote/Makefile.MSys2 b/src/Native/libcryptonote/Makefile.MSys2 index 5b0eb9e5f..a04d38435 100644 --- a/src/Native/libcryptonote/Makefile.MSys2 +++ b/src/Native/libcryptonote/Makefile.MSys2 @@ -7,7 +7,8 @@ LDLIBS = -lboost_system-mt -lboost_date_time-mt TARGET = libcryptonote.dll OBJECTS = contrib/epee/src/hex.o \ - common/base58.o crypto/aesb.o crypto/blake256.o crypto/chacha8.o \ + common/base58.o crypto/aesb.o crypto/blake256.o crypto/chacha.o \ + contrib/epee/src/memwipe.o \ crypto/crypto-ops-data.o crypto/crypto-ops.o crypto/crypto.o crypto/groestl.o crypto/hash-extra-blake.o \ crypto/hash-extra-groestl.o crypto/hash-extra-jh.o crypto/hash-extra-skein.o crypto/hash.o crypto/jh.o \ crypto/keccak.o crypto/oaes_lib.o crypto/random.o crypto/skein.o crypto/slow-hash.o crypto/tree-hash.o \ diff --git a/src/Native/libcryptonote/common/base58.cpp b/src/Native/libcryptonote/common/base58.cpp index 43208c1bf..75556cad9 100644 --- a/src/Native/libcryptonote/common/base58.cpp +++ b/src/Native/libcryptonote/common/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -36,7 +36,7 @@ #include "crypto/hash.h" #include "int-util.h" -// OW: unused #include "util.h" +#include "util.h" #include "varint.h" namespace tools @@ -111,13 +111,13 @@ namespace tools uint64_t res = 0; switch (9 - size) { - case 1: res |= *data++; - case 2: res <<= 8; res |= *data++; - case 3: res <<= 8; res |= *data++; - case 4: res <<= 8; res |= *data++; - case 5: res <<= 8; res |= *data++; - case 6: res <<= 8; res |= *data++; - case 7: res <<= 8; res |= *data++; + case 1: res |= *data++; /* FALLTHRU */ + case 2: res <<= 8; res |= *data++; /* FALLTHRU */ + case 3: res <<= 8; res |= *data++; /* FALLTHRU */ + case 4: res <<= 8; res |= *data++; /* FALLTHRU */ + case 5: res <<= 8; res |= *data++; /* FALLTHRU */ + case 6: res <<= 8; res |= *data++; /* FALLTHRU */ + case 7: res <<= 8; res |= *data++; /* FALLTHRU */ case 8: res <<= 8; res |= *data; break; default: assert(false); } diff --git a/src/Native/libcryptonote/common/base58.h b/src/Native/libcryptonote/common/base58.h index 6dd850c03..02ca96956 100644 --- a/src/Native/libcryptonote/common/base58.h +++ b/src/Native/libcryptonote/common/base58.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/common/int-util.h b/src/Native/libcryptonote/common/int-util.h index ff1340dc2..3bcc085e2 100644 --- a/src/Native/libcryptonote/common/int-util.h +++ b/src/Native/libcryptonote/common/int-util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -35,7 +35,7 @@ #include #include -#if !defined(_MSC_VER) +#ifndef _MSC_VER #include #endif @@ -43,6 +43,10 @@ #include #endif +#if defined(__sun) && defined(__SVR4) +#include +#endif + #if defined(_MSC_VER) #include @@ -205,13 +209,14 @@ static inline void memcpy_swap64(void *dst, const void *src, size_t n) { } } +#ifdef _MSC_VER +# define LITTLE_ENDIAN 1234 +# define BIG_ENDIAN 4321 +# define BYTE_ORDER LITTLE_ENDIAN +#endif + #if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN) -#if !defined(_MSC_VER) static_assert(false, "BYTE_ORDER is undefined. Perhaps, GNU extensions are not enabled"); -#else -#define LITTLE_ENDIAN 1234 -#define BYTE_ORDER LITTLE_ENDIAN -#endif #endif #if BYTE_ORDER == LITTLE_ENDIAN diff --git a/src/Native/libcryptonote/common/pod-class.h b/src/Native/libcryptonote/common/pod-class.h index 3896d5c29..5f6709eef 100644 --- a/src/Native/libcryptonote/common/pod-class.h +++ b/src/Native/libcryptonote/common/pod-class.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/common/util.h b/src/Native/libcryptonote/common/util.h new file mode 100644 index 000000000..d3ba47a4f --- /dev/null +++ b/src/Native/libcryptonote/common/util.h @@ -0,0 +1,215 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include "windows.h" +#include "misc_log_ex.h" +#endif + +#include "crypto/hash.h" + +/*! \brief Various Tools + * + * + * + */ +namespace tools +{ + //! Functional class for closing C file handles. + struct close_file + { + void operator()(std::FILE* handle) const noexcept + { + if (handle) + { + std::fclose(handle); + } + } + }; + + //! A file restricted to process owner AND process. Deletes file on destruction. + class private_file { + std::unique_ptr m_handle; + std::string m_filename; + + private_file(std::FILE* handle, std::string&& filename) noexcept; + public: + + //! `handle() == nullptr && filename.empty()`. + private_file() noexcept; + + /*! \return File only readable by owner and only used by this process + OR `private_file{}` on error. */ + static private_file create(std::string filename); + + private_file(private_file&&) = default; + private_file& operator=(private_file&&) = default; + + //! Deletes `filename()` and closes `handle()`. + ~private_file() noexcept; + + std::FILE* handle() const noexcept { return m_handle.get(); } + const std::string& filename() const noexcept { return m_filename; } + }; + + /*! \brief Returns the default data directory. + * + * \details Windows < Vista: C:\\Documents and Settings\\Username\\Application Data\\CRYPTONOTE_NAME + * + * Windows >= Vista: C:\\Users\\Username\\AppData\\Roaming\\CRYPTONOTE_NAME + * + * Mac: ~/Library/Application Support/CRYPTONOTE_NAME + * + * Unix: ~/.CRYPTONOTE_NAME + */ + std::string get_default_data_dir(); + +#ifdef WIN32 + /** + * @brief + * + * @param nfolder + * @param iscreate + * + * @return + */ + std::string get_special_folder_path(int nfolder, bool iscreate); +#endif + + /*! \brief Returns the OS version string + * + * \details This is a wrapper around the primitives + * get_windows_version_display_string() and + * get_nix_version_display_string() + */ + std::string get_os_version_string(); + + /*! \brief creates directories for a path + * + * wrapper around boost::filesyste::create_directories. + * (ensure-directory-exists): greenspun's tenth rule in action! + */ + bool create_directories_if_necessary(const std::string& path); + /*! \brief std::rename wrapper for nix and something strange for windows. + */ + std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name); + + bool sanitize_locale(); + + bool on_startup(); + + /*! \brief Defines a signal handler for win32 and *nix + */ + class signal_handler + { + public: + /*! \brief installs a signal handler */ + template + static bool install(T t) + { +#if defined(WIN32) + bool r = TRUE == ::SetConsoleCtrlHandler(&win_handler, TRUE); + if (r) + { + m_handler = t; + } + return r; +#else + static struct sigaction sa; + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = posix_handler; + sa.sa_flags = 0; + /* Only blocks SIGINT, SIGTERM and SIGPIPE */ + sigaction(SIGINT, &sa, NULL); + signal(SIGTERM, posix_handler); + signal(SIGPIPE, SIG_IGN); + m_handler = t; + return true; +#endif + } + + private: +#if defined(WIN32) + /*! \brief Handler for win */ + static BOOL WINAPI win_handler(DWORD type) + { + if (CTRL_C_EVENT == type || CTRL_BREAK_EVENT == type) + { + handle_signal(type); + } + else + { + MGINFO_RED("Got control signal " << type << ". Exiting without saving..."); + return FALSE; + } + return TRUE; + } +#else + /*! \brief handler for NIX */ + static void posix_handler(int type) + { + handle_signal(type); + } +#endif + + /*! \brief calles m_handler */ + static void handle_signal(int type) + { + static boost::mutex m_mutex; + boost::unique_lock lock(m_mutex); + m_handler(type); + } + + /*! \brief where the installed handler is stored */ + static std::function m_handler; + }; + + void set_strict_default_file_permissions(bool strict); + + void set_max_concurrency(unsigned n); + unsigned get_max_concurrency(); + + bool is_local_address(const std::string &address); + int vercmp(const char *v0, const char *v1); // returns < 0, 0, > 0, similar to strcmp, but more human friendly than lexical - does not attempt to validate + + bool sha256sum(const uint8_t *data, size_t len, crypto::hash &hash); + bool sha256sum(const std::string &filename, crypto::hash &hash); +} diff --git a/src/Native/libcryptonote/common/varint.h b/src/Native/libcryptonote/common/varint.h index cb785e61a..151d13dbf 100644 --- a/src/Native/libcryptonote/common/varint.h +++ b/src/Native/libcryptonote/common/varint.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -45,7 +45,7 @@ * is as follows: Strip the msb of each byte, then from left to right, * read in what remains, placing it in reverse, into the buffer. Thus, * the following bit stream: 0xff02 would return 0x027f. 0xff turns - * into 0x7f, is placed on the beggining of the buffer, then 0x02 is + * into 0x7f, is placed on the beginning of the buffer, then 0x02 is * unchanged, since its msb is not set, and placed at the end of the * buffer. */ @@ -108,7 +108,7 @@ namespace tools { return EVARINT_REPRESENT; } - write |= static_cast(byte & 0x7f) << shift; /* Does the actualy placing into write, stripping the first bit */ + write |= static_cast(byte & 0x7f) << shift; /* Does the actually placing into write, stripping the first bit */ /* If there is no next */ if ((byte & 0x80) == 0) { diff --git a/src/Native/libcryptonote/contrib/epee/include/console_handler.h b/src/Native/libcryptonote/contrib/epee/include/console_handler.h index b8336b270..4ea3fa54b 100644 --- a/src/Native/libcryptonote/contrib/epee/include/console_handler.h +++ b/src/Native/libcryptonote/contrib/epee/include/console_handler.h @@ -27,6 +27,7 @@ #pragma once #include "misc_log_ex.h" +#include "string_tools.h" #include #include #include @@ -37,6 +38,8 @@ #include #endif #include +#include +#include #ifdef HAVE_READLINE #include "readline_buffer.h" @@ -456,29 +459,35 @@ namespace epee class command_handler { public: typedef boost::function &)> callback; - typedef std::map > lookup; + typedef std::map>> lookup; std::string get_usage() { std::stringstream ss; - size_t max_command_len = 0; - for(auto& x:m_command_handlers) - if(x.first.size() > max_command_len) - max_command_len = x.first.size(); for(auto& x:m_command_handlers) { - ss.width(max_command_len + 3); - ss << std::left << x.first << x.second.second << ENDL; + ss << x.second.second.first << ENDL; } return ss.str(); } - void set_handler(const std::string& cmd, const callback& hndlr, const std::string& usage = "") + std::pair get_documentation(const std::vector& cmd) + { + if(cmd.empty()) + return std::make_pair("", ""); + auto it = m_command_handlers.find(cmd.front()); + if(it == m_command_handlers.end()) + return std::make_pair("", ""); + return it->second.second; + } + + void set_handler(const std::string& cmd, const callback& hndlr, const std::string& usage = "", const std::string& description = "") { lookup::mapped_type & vt = m_command_handlers[cmd]; vt.first = hndlr; - vt.second = usage; + vt.second.first = description.empty() ? cmd : usage; + vt.second.second = description.empty() ? usage : description; #ifdef HAVE_READLINE rdln::readline_buffer::add_completion(cmd); #endif diff --git a/src/Native/libcryptonote/contrib/epee/include/copyable_atomic.h b/src/Native/libcryptonote/contrib/epee/include/copyable_atomic.h index 410b4b4ff..00a5f484b 100644 --- a/src/Native/libcryptonote/contrib/epee/include/copyable_atomic.h +++ b/src/Native/libcryptonote/contrib/epee/include/copyable_atomic.h @@ -35,6 +35,8 @@ namespace epee public: copyable_atomic() {}; + copyable_atomic(uint32_t value) + { store(value); } copyable_atomic(const copyable_atomic& a):std::atomic(a.load()) {} copyable_atomic& operator= (const copyable_atomic& a) diff --git a/src/Native/libcryptonote/contrib/epee/include/file_io_utils.h b/src/Native/libcryptonote/contrib/epee/include/file_io_utils.h index c387743a6..196610674 100644 --- a/src/Native/libcryptonote/contrib/epee/include/file_io_utils.h +++ b/src/Native/libcryptonote/contrib/epee/include/file_io_utils.h @@ -29,7 +29,33 @@ #define _FILE_IO_UTILS_H_ #include -#include +#include +#include +#ifdef WIN32 +#include +#endif + +// On Windows there is a problem with non-ASCII characters in path and file names +// as far as support by the standard components used is concerned: + +// The various file stream classes, e.g. std::ifstream and std::ofstream, are +// part of the GNU C++ Library / libstdc++. On the most basic level they use the +// fopen() call as defined / made accessible to programs compiled within MSYS2 +// by the stdio.h header file maintained by the MinGW project. + +// The critical point: The implementation of fopen() is part of MSVCRT, the +// Microsoft Visual C/C++ Runtime Library, and this method does NOT offer any +// Unicode support. + +// Monero code that would want to continue to use the normal file stream classes +// but WITH Unicode support could therefore not solve this problem on its own, +// but 2 different projects from 2 different maintaining groups would need changes +// in this particular direction - something probably difficult to achieve and +// with a long time to wait until all new versions / releases arrive. + +// Implemented solution approach: Circumvent the problem by stopping to use std +// file stream classes on Windows and directly use Unicode-capable WIN32 API +// calls. Most of the code doing so is concentrated in this header file here. namespace epee { @@ -45,7 +71,22 @@ namespace file_io_utils inline bool save_string_to_file(const std::string& path_to_file, const std::string& str) { - +#ifdef WIN32 + WCHAR wide_path[1000]; + int chars = MultiByteToWideChar(CP_UTF8, 0, path_to_file.c_str(), path_to_file.size() + 1, wide_path, 1000); + if (chars == 0) + return false; + HANDLE file_handle = CreateFileW(wide_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file_handle == INVALID_HANDLE_VALUE) + return false; + DWORD bytes_written; + DWORD bytes_to_write = (DWORD)str.size(); + BOOL result = WriteFile(file_handle, str.data(), bytes_to_write, &bytes_written, NULL); + CloseHandle(file_handle); + if (bytes_written != bytes_to_write) + result = FALSE; + return result; +#else try { std::ofstream fstream; @@ -60,10 +101,11 @@ namespace file_io_utils { return false; } +#endif } inline - bool get_file_time(const std::string& path_to_file, OUT time_t& ft) + bool get_file_time(const std::string& path_to_file, time_t& ft) { boost::system::error_code ec; ft = boost::filesystem::last_write_time(boost::filesystem::path(path_to_file), ec); @@ -88,6 +130,27 @@ namespace file_io_utils inline bool load_file_to_string(const std::string& path_to_file, std::string& target_str) { +#ifdef WIN32 + WCHAR wide_path[1000]; + int chars = MultiByteToWideChar(CP_UTF8, 0, path_to_file.c_str(), path_to_file.size() + 1, wide_path, 1000); + if (chars == 0) + return false; + HANDLE file_handle = CreateFileW(wide_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (file_handle == INVALID_HANDLE_VALUE) + return false; + DWORD file_size = GetFileSize(file_handle, NULL); + if ((file_size == INVALID_FILE_SIZE) || (file_size > 1000000000)) { + CloseHandle(file_handle); + return false; + } + target_str.resize(file_size); + DWORD bytes_read; + BOOL result = ReadFile(file_handle, &target_str[0], file_size, &bytes_read, NULL); + CloseHandle(file_handle); + if (bytes_read != file_size) + result = FALSE; + return result; +#else try { std::ifstream fstream; @@ -112,11 +175,13 @@ namespace file_io_utils { return false; } +#endif } inline bool append_string_to_file(const std::string& path_to_file, const std::string& str) { + // No special Windows implementation because so far not used in Monero code try { std::ofstream fstream; @@ -132,6 +197,43 @@ namespace file_io_utils return false; } } + + inline + bool get_file_size(const std::string& path_to_file, uint64_t &size) + { +#ifdef WIN32 + WCHAR wide_path[1000]; + int chars = MultiByteToWideChar(CP_UTF8, 0, path_to_file.c_str(), path_to_file.size() + 1, wide_path, 1000); + if (chars == 0) + return false; + HANDLE file_handle = CreateFileW(wide_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (file_handle == INVALID_HANDLE_VALUE) + return false; + LARGE_INTEGER file_size; + BOOL result = GetFileSizeEx(file_handle, &file_size); + CloseHandle(file_handle); + if (result) { + size = file_size.QuadPart; + } + return size; +#else + try + { + std::ifstream fstream; + fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + fstream.open(path_to_file, std::ios_base::binary | std::ios_base::in | std::ios::ate); + size = fstream.tellg(); + fstream.close(); + return true; + } + + catch(...) + { + return false; + } +#endif + } + } } diff --git a/src/Native/libcryptonote/contrib/epee/include/hex.h b/src/Native/libcryptonote/contrib/epee/include/hex.h index f8d6be048..e960da1d2 100644 --- a/src/Native/libcryptonote/contrib/epee/include/hex.h +++ b/src/Native/libcryptonote/contrib/epee/include/hex.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017, The Monero Project +// Copyright (c) 2017-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/contrib/epee/include/math_helper.h b/src/Native/libcryptonote/contrib/epee/include/math_helper.h index 90398acbb..ef839f609 100644 --- a/src/Native/libcryptonote/contrib/epee/include/math_helper.h +++ b/src/Native/libcryptonote/contrib/epee/include/math_helper.h @@ -37,6 +37,7 @@ #include #include "misc_os_dependent.h" +#include "syncobj.h" namespace epee { diff --git a/src/Native/libcryptonote/contrib/epee/include/memwipe.h b/src/Native/libcryptonote/contrib/epee/include/memwipe.h new file mode 100644 index 000000000..0d8f491b7 --- /dev/null +++ b/src/Native/libcryptonote/contrib/epee/include/memwipe.h @@ -0,0 +1,80 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#ifdef __cplusplus +#include + +extern "C" { +#endif + +void *memwipe(void *src, size_t n); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +namespace tools { + + /// Scrubs data in the contained type upon destruction. + /// + /// Primarily useful for making sure that private keys don't stick around in + /// memory after the objects that held them have gone out of scope. + template + struct scrubbed : public T { + using type = T; + + ~scrubbed() { + scrub(); + } + + /// Destroy the contents of the contained type. + void scrub() { + static_assert(std::is_pod::value, + "T cannot be auto-scrubbed. T must be POD."); + static_assert(std::is_trivially_destructible::value, + "T cannot be auto-scrubbed. T must be trivially destructable."); + memwipe(this, sizeof(T)); + } + }; + + template + T& unwrap(scrubbed& src) { return src; } + + template + const T& unwrap(scrubbed const& src) { return src; } + + template + using scrubbed_arr = scrubbed>; +} // namespace tools + +#endif // __cplusplus diff --git a/src/Native/libcryptonote/contrib/epee/include/misc_os_dependent.h b/src/Native/libcryptonote/contrib/epee/include/misc_os_dependent.h index 69ded09e5..ffe575501 100644 --- a/src/Native/libcryptonote/contrib/epee/include/misc_os_dependent.h +++ b/src/Native/libcryptonote/contrib/epee/include/misc_os_dependent.h @@ -23,6 +23,10 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +#ifdef _WIN32 +#include +#endif + #ifdef WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN @@ -42,6 +46,9 @@ #include #endif +#include +#include + #pragma once namespace epee { @@ -68,13 +75,13 @@ namespace misc_utils clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); - return (mts.tv_sec * 1000000000) + (mts.tv_nsec); + return ((uint64_t)mts.tv_sec * 1000000000) + (mts.tv_nsec); #else struct timespec ts; if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { return 0; } - return (ts.tv_sec * 1000000000) + (ts.tv_nsec); + return ((uint64_t)ts.tv_sec * 1000000000) + (ts.tv_nsec); #endif } diff --git a/src/Native/libcryptonote/contrib/epee/include/net/abstract_tcp_server.h b/src/Native/libcryptonote/contrib/epee/include/net/abstract_tcp_server.h index 000305cfa..cbad1717c 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/abstract_tcp_server.h +++ b/src/Native/libcryptonote/contrib/epee/include/net/abstract_tcp_server.h @@ -305,7 +305,7 @@ namespace net_utils m_connections.back().powner = this; m_connections.back().m_self_it = --m_connections.end(); m_connections.back().m_context.m_remote_address = remote_address; - m_connections.back().m_htread = threads_helper::create_thread(ConnectionHandlerProc, &m_connections.back()); + m_connections.back().m_htread = threads_helper::create_thread(ConnectionHandlerProc, &m_connections.back()); // ugh, seems very risky return true; } diff --git a/src/Native/libcryptonote/contrib/epee/include/net/abstract_tcp_server2.h b/src/Native/libcryptonote/contrib/epee/include/net/abstract_tcp_server2.h index ca58d5467..ccde928ba 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/abstract_tcp_server2.h +++ b/src/Native/libcryptonote/contrib/epee/include/net/abstract_tcp_server2.h @@ -54,8 +54,8 @@ #include #include "net_utils_base.h" #include "syncobj.h" -#include "../../../../src/p2p/connection_basic.hpp" -#include "../../../../src/p2p/network_throttle-detail.hpp" +#include "connection_basic.hpp" +#include "network_throttle-detail.hpp" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net" @@ -207,12 +207,18 @@ namespace net_utils bool connect(const std::string& adr, const std::string& port, uint32_t conn_timeot, t_connection_context& cn, const std::string& bind_ip = "0.0.0.0"); template - bool connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeot, t_callback cb, const std::string& bind_ip = "0.0.0.0"); + bool connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeot, const t_callback &cb, const std::string& bind_ip = "0.0.0.0"); typename t_protocol_handler::config_type& get_config_object(){return m_config;} int get_binded_port(){return m_port;} + long get_connections_count() const + { + auto connections_count = (m_sock_count > 0) ? (m_sock_count - 1) : 0; // Socket count minus listening socket + return connections_count; + } + boost::asio::io_service& get_io_service(){return io_service_;} struct idle_callback_conext_base @@ -281,8 +287,6 @@ namespace net_utils bool is_thread_worker(); - bool cleanup_connections(); - /// The io_service used to perform asynchronous operations. std::unique_ptr m_io_service_local_instance; boost::asio::io_service& io_service_; @@ -309,7 +313,7 @@ namespace net_utils connection_ptr new_connection_; boost::mutex connections_mutex; - std::deque> connections_; + std::set connections_; }; // class <>boosted_tcp_server diff --git a/src/Native/libcryptonote/contrib/epee/include/net/abstract_tcp_server2.inl b/src/Native/libcryptonote/contrib/epee/include/net/abstract_tcp_server2.inl index 61276e761..195ee2f0c 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/src/Native/libcryptonote/contrib/epee/include/net/abstract_tcp_server2.inl @@ -54,8 +54,6 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net" -#define CONNECTION_CLEANUP_TIME 30 // seconds - PRAGMA_WARNING_PUSH namespace epee { @@ -82,7 +80,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) m_throttle_speed_in("speed_in", "throttle_speed_in"), m_throttle_speed_out("speed_out", "throttle_speed_out") { - MINFO("test, connection constructor set m_connection_type="<(); - long ip_ = boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong()); + const unsigned long ip_{boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong())}; // create a random uuid boost::uuids::uuid random_uuid; // that stuff turns out to be included, even though it's from src... Taking advantage random_uuid = crypto::rand(); - context.set_details(random_uuid, new epee::net_utils::ipv4_network_address(ip_, remote_ep.port()), is_income); + context.set_details(random_uuid, epee::net_utils::ipv4_network_address(ip_, remote_ep.port()), is_income); _dbg3("[sock " << socket_.native_handle() << "] new connection from " << print_connection_context_short(context) << " to " << local_ep.address().to_string() << ':' << local_ep.port() << ", total sockets objects " << m_ref_sock_count); @@ -266,7 +264,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) address = endpoint.address().to_string(); port = boost::lexical_cast(endpoint.port()); } - MINFO(" connection type " << to_string( m_connection_type ) << " " + MDEBUG(" connection type " << to_string( m_connection_type ) << " " << socket_.local_endpoint().address().to_string() << ":" << socket_.local_endpoint().port() << " <--> " << address << ":" << port); } @@ -288,7 +286,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) { CRITICAL_REGION_LOCAL( epee::net_utils::network_throttle_manager::network_throttle_manager::m_lock_get_global_throttle_in ); - epee::net_utils::network_throttle_manager::network_throttle_manager::get_global_throttle_in().handle_trafic_exact(bytes_transferred * 1024); + epee::net_utils::network_throttle_manager::network_throttle_manager::get_global_throttle_in().handle_trafic_exact(bytes_transferred); } double delay=0; // will be calculated - how much we should sleep to obey speed limit etc @@ -299,7 +297,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) { { //_scope_dbg1("CRITICAL_REGION_LOCAL"); CRITICAL_REGION_LOCAL( epee::net_utils::network_throttle_manager::m_lock_get_global_throttle_in ); - delay = epee::net_utils::network_throttle_manager::get_global_throttle_in().get_sleep_time_after_tick( bytes_transferred ); // decission from global throttle + delay = epee::net_utils::network_throttle_manager::get_global_throttle_in().get_sleep_time_after_tick( bytes_transferred ); } delay *= 0.5; @@ -484,9 +482,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) //some data should be wrote to stream //request complete - if (speed_limit_is_enabled()) { - sleep_before_packet(cb, 1, 1); - } + // No sleeping here; sleeping is done once and for all in "handle_write" m_send_que_lock.lock(); // *** critical *** epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_send_que_lock.unlock();}); @@ -609,6 +605,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) } logger_handle_net_write(cb); + // The single sleeping that is needed for correctly handling "out" speed throttling if (speed_limit_is_enabled()) { sleep_before_packet(cb, 1, 1); } @@ -738,7 +735,17 @@ PRAGMA_WARNING_DISABLE_VS(4355) boost::asio::placeholders::error)); return true; - CATCH_ENTRY_L0("boosted_tcp_server::init_server", false); + } + catch (const std::exception &e) + { + MFATAL("Error starting server: " << e.what()); + return false; + } + catch (...) + { + MFATAL("Error starting server"); + return false; + } } //----------------------------------------------------------------------------- PUSH_WARNINGS @@ -808,7 +815,6 @@ POP_WARNINGS m_threads_count = threads_count; m_main_thread_id = boost::this_thread::get_id(); MLOG_SET_THREAD_NAME("[SRV_MAIN]"); - add_idle_handler(boost::bind(&boosted_tcp_server::cleanup_connections, this), 5000); while(!m_stop_signal_sent) { @@ -898,7 +904,7 @@ POP_WARNINGS connections_mutex.lock(); for (auto &c: connections_) { - c.second->cancel(); + c->cancel(); } connections_.clear(); connections_mutex.unlock(); @@ -907,19 +913,6 @@ POP_WARNINGS } //--------------------------------------------------------------------------------- template - bool boosted_tcp_server::cleanup_connections() - { - connections_mutex.lock(); - boost::system_time cutoff = boost::get_system_time() - boost::posix_time::seconds(CONNECTION_CLEANUP_TIME); - while (!connections_.empty() && connections_.front().first < cutoff) - { - connections_.pop_front(); - } - connections_mutex.unlock(); - return true; - } - //--------------------------------------------------------------------------------- - template bool boosted_tcp_server::is_stop_signal_sent() { return m_stop_signal_sent; @@ -942,6 +935,9 @@ POP_WARNINGS boost::bind(&boosted_tcp_server::handle_accept, this, boost::asio::placeholders::error)); + boost::asio::socket_base::keep_alive opt(true); + conn->socket().set_option(opt); + conn->start(true, 1 < m_threads_count); conn->save_dbg_log(); }else @@ -958,9 +954,10 @@ POP_WARNINGS connection_ptr new_connection_l(new connection(io_service_, m_config, m_sock_count, m_sock_number, m_pfilter, m_connection_type) ); connections_mutex.lock(); - connections_.push_back(std::make_pair(boost::get_system_time(), new_connection_l)); + connections_.insert(new_connection_l); MDEBUG("connections_ size now " << connections_.size()); connections_mutex.unlock(); + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ CRITICAL_REGION_LOCAL(connections_mutex); connections_.erase(new_connection_l); }); boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket(); ////////////////////////////////////////////////////////////////////////// @@ -1038,6 +1035,10 @@ POP_WARNINGS _dbg3("Connected success to " << adr << ':' << port); + // start adds the connection to the config object's list, so we don't need to have it locally anymore + connections_mutex.lock(); + connections_.erase(new_connection_l); + connections_mutex.unlock(); bool r = new_connection_l->start(false, 1 < m_threads_count); if (r) { @@ -1057,14 +1058,15 @@ POP_WARNINGS } //--------------------------------------------------------------------------------- template template - bool boosted_tcp_server::connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeout, t_callback cb, const std::string& bind_ip) + bool boosted_tcp_server::connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeout, const t_callback &cb, const std::string& bind_ip) { TRY_ENTRY(); connection_ptr new_connection_l(new connection(io_service_, m_config, m_sock_count, m_sock_number, m_pfilter, m_connection_type) ); connections_mutex.lock(); - connections_.push_back(std::make_pair(boost::get_system_time(), new_connection_l)); + connections_.insert(new_connection_l); MDEBUG("connections_ size now " << connections_.size()); connections_mutex.unlock(); + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ CRITICAL_REGION_LOCAL(connections_mutex); connections_.erase(new_connection_l); }); boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket(); ////////////////////////////////////////////////////////////////////////// @@ -1113,6 +1115,11 @@ POP_WARNINGS { _dbg3("[sock " << new_connection_l->socket().native_handle() << "] Connected success to " << adr << ':' << port << " from " << lep.address().to_string() << ':' << lep.port()); + + // start adds the connection to the config object's list, so we don't need to have it locally anymore + connections_mutex.lock(); + connections_.erase(new_connection_l); + connections_mutex.unlock(); bool r = new_connection_l->start(false, 1 < m_threads_count); if (r) { diff --git a/src/Native/libcryptonote/contrib/epee/include/net/connection_basic.hpp b/src/Native/libcryptonote/contrib/epee/include/net/connection_basic.hpp new file mode 100644 index 000000000..095e747a5 --- /dev/null +++ b/src/Native/libcryptonote/contrib/epee/include/net/connection_basic.hpp @@ -0,0 +1,141 @@ +/// @file +/// @author rfree (current maintainer in monero.cc project) +/// @brief base for connection, contains e.g. the ratelimit hooks + +// ! This file might contain variable names same as in template class connection<> +// ! from files contrib/epee/include/net/abstract_tcp_server2.* +// ! I am not a lawyer; afaik APIs, var names etc are not copyrightable ;) +// ! (how ever if in some wonderful juristdictions that is not the case, then why not make another sub-class withat that members and licence it as epee part) +// ! Working on above premise, IF this is valid in your juristdictions, then consider this code as released as: + +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* rfree: place for hanlers for the non-template base, can be used by connection<> template class in abstract_tcp_server2 file */ + +#ifndef INCLUDED_p2p_connection_basic_hpp +#define INCLUDED_p2p_connection_basic_hpp + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "net/net_utils_base.h" +#include "syncobj.h" + +namespace epee +{ +namespace net_utils +{ + + /************************************************************************/ + /* */ + /************************************************************************/ + /// Represents a single connection from a client. + +class connection_basic_pimpl; // PIMPL for this class + + enum t_connection_type { // type of the connection (of this server), e.g. so that we will know how to limit it + e_connection_type_NET = 0, // default (not used?) + e_connection_type_RPC = 1, // the rpc commands (probably not rate limited, not chunked, etc) + e_connection_type_P2P = 2 // to other p2p node (probably limited) + }; + + std::string to_string(t_connection_type type); + +class connection_basic { // not-templated base class for rapid developmet of some code parts + public: + std::unique_ptr< connection_basic_pimpl > mI; // my Implementation + + // moved here from orginal connecton<> - common member variables that do not depend on template in connection<> + volatile uint32_t m_want_close_connection; + std::atomic m_was_shutdown; + critical_section m_send_que_lock; + std::list m_send_que; + volatile bool m_is_multithreaded; + double m_start_time; + /// Strand to ensure the connection's handlers are not called concurrently. + boost::asio::io_service::strand strand_; + /// Socket for the connection. + boost::asio::ip::tcp::socket socket_; + + std::atomic &m_ref_sock_count; // reference to external counter of existing sockets that we will ++/-- + public: + // first counter is the ++/-- count of current sockets, the other socket_number is only-increasing ++ number generator + connection_basic(boost::asio::io_service& io_service, std::atomic &ref_sock_count, std::atomic &sock_number); + + virtual ~connection_basic() noexcept(false); + + // various handlers to be called from connection class: + void do_send_handler_write(const void * ptr , size_t cb); + void do_send_handler_write_from_queue(const boost::system::error_code& e, size_t cb , int q_len); // from handle_write, sending next part + + void logger_handle_net_write(size_t size); // network data written + void logger_handle_net_read(size_t size); // network data read + + void set_start_time(); + + // config for rate limit + + static void set_rate_up_limit(uint64_t limit); + static void set_rate_down_limit(uint64_t limit); + static uint64_t get_rate_up_limit(); + static uint64_t get_rate_down_limit(); + + // config misc + static void set_tos_flag(int tos); // ToS / QoS flag + static int get_tos_flag(); + + // handlers and sleep + void sleep_before_packet(size_t packet_size, int phase, int q_len); // execute a sleep ; phase is not really used now(?) + static void save_limit_to_file(int limit); ///< for dr-monero + static double get_sleep_time(size_t cb); + + static void set_save_graph(bool save_graph); +}; + +} // nameserver +} // nameserver + +#endif + + diff --git a/src/Native/libcryptonote/contrib/epee/include/net/http_auth.h b/src/Native/libcryptonote/contrib/epee/include/net/http_auth.h index bf368e6f4..4324c41fd 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/http_auth.h +++ b/src/Native/libcryptonote/contrib/epee/include/net/http_auth.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -33,7 +33,7 @@ #include #include #include - +#include "wipeable_string.h" #include "http_base.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -48,12 +48,12 @@ namespace net_utils struct login { login() : username(), password() {} - login(std::string username_, std::string password_) + login(std::string username_, wipeable_string password_) : username(std::move(username_)), password(std::move(password_)) {} std::string username; - std::string password; + wipeable_string password; }; //! Implements RFC 2617 digest auth. Digests from RFC 7616 can be added. @@ -71,8 +71,8 @@ namespace net_utils std::uint32_t counter; }; - http_server_auth() : user() {} - http_server_auth(login credentials); + http_server_auth() : user(), rng() {} + http_server_auth(login credentials, std::function r); //! \return Auth response, or `boost::none` iff `request` had valid auth. boost::optional get_response(const http_request_info& request) @@ -81,10 +81,13 @@ namespace net_utils return do_get_response(request); return boost::none; } + private: boost::optional do_get_response(const http_request_info& request); boost::optional user; + + std::function rng; }; //! Implements RFC 2617 digest auth. Digests from RFC 7616 can be added. diff --git a/src/Native/libcryptonote/contrib/epee/include/net/http_base.h b/src/Native/libcryptonote/contrib/epee/include/net/http_base.h index e5aa06cb4..a66fb7c23 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/http_base.h +++ b/src/Native/libcryptonote/contrib/epee/include/net/http_base.h @@ -46,6 +46,7 @@ namespace net_utils { enum http_method{ + http_method_options, http_method_get, http_method_post, http_method_put, @@ -115,6 +116,7 @@ namespace net_utils std::string m_host; //"Host:" std::string m_cookie; //"Cookie:" std::string m_user_agent; //"User-Agent:" + std::string m_origin; //"Origin:" fields_list m_etc_fields; void clear() @@ -128,6 +130,7 @@ namespace net_utils m_host.clear(); m_cookie.clear(); m_user_agent.clear(); + m_origin.clear(); m_etc_fields.clear(); } }; @@ -155,7 +158,8 @@ namespace net_utils http_request_info():m_http_method(http_method_unknown), m_http_ver_hi(0), m_http_ver_lo(0), - m_have_to_block(false) + m_have_to_block(false), + m_full_request_buf_size(0) {} http_method m_http_method; diff --git a/src/Native/libcryptonote/contrib/epee/include/net/http_client.h b/src/Native/libcryptonote/contrib/epee/include/net/http_client.h index 8e099e2bc..1edf65928 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/http_client.h +++ b/src/Native/libcryptonote/contrib/epee/include/net/http_client.h @@ -27,6 +27,7 @@ #pragma once +#include #include #include #include @@ -50,6 +51,7 @@ #include "http_auth.h" #include "to_nonconst_iterator.h" #include "net_parse_helpers.h" +#include "syncobj.h" //#include "shlwapi.h" @@ -66,8 +68,6 @@ namespace epee namespace net_utils { -using namespace std; - /*struct url { public: @@ -237,7 +237,8 @@ using namespace std; namespace http { - class http_simple_client: public i_target_handler + template + class http_simple_client_template: public i_target_handler { private: enum reciev_machine_state @@ -260,7 +261,7 @@ using namespace std; }; - blocked_mode_client m_net_client; + net_client_type m_net_client; std::string m_host_buff; std::string m_port; http_client_auth m_auth; @@ -274,9 +275,10 @@ using namespace std; chunked_state m_chunked_state; std::string m_chunked_cache; critical_section m_lock; + bool m_ssl; public: - explicit http_simple_client() + explicit http_simple_client_template() : i_target_handler() , m_net_client() , m_host_buff() @@ -291,33 +293,35 @@ using namespace std; , m_chunked_state() , m_chunked_cache() , m_lock() + , m_ssl(false) {} const std::string &get_host() const { return m_host_buff; }; const std::string &get_port() const { return m_port; }; - bool set_server(const std::string& address, boost::optional user) + bool set_server(const std::string& address, boost::optional user, bool ssl = false) { http::url_content parsed{}; const bool r = parse_url(address, parsed); CHECK_AND_ASSERT_MES(r, false, "failed to parse url: " << address); - set_server(std::move(parsed.host), std::to_string(parsed.port), std::move(user)); + set_server(std::move(parsed.host), std::to_string(parsed.port), std::move(user), ssl); return true; } - void set_server(std::string host, std::string port, boost::optional user) + void set_server(std::string host, std::string port, boost::optional user, bool ssl = false) { CRITICAL_REGION_LOCAL(m_lock); disconnect(); m_host_buff = std::move(host); m_port = std::move(port); m_auth = user ? http_client_auth{std::move(*user)} : http_client_auth{}; + m_ssl = ssl; } bool connect(std::chrono::milliseconds timeout) { CRITICAL_REGION_LOCAL(m_lock); - return m_net_client.connect(m_host_buff, m_port, timeout); + return m_net_client.connect(m_host_buff, m_port, timeout, m_ssl); } //--------------------------------------------------------------------------- bool disconnect() @@ -392,7 +396,6 @@ using namespace std; res = m_net_client.send(body, timeout); CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND"); - m_response_info.clear(); m_state = reciev_machine_state_header; if (!handle_reciev(timeout)) @@ -427,6 +430,15 @@ using namespace std; CRITICAL_REGION_LOCAL(m_lock); return invoke(uri, "POST", body, timeout, ppresponse_info, additional_params); } + //--------------------------------------------------------------------------- + bool test(const std::string &s, std::chrono::milliseconds timeout) // TEST FUNC ONLY + { + CRITICAL_REGION_LOCAL(m_lock); + m_net_client.set_test_data(s); + m_state = reciev_machine_state_header; + return handle_reciev(timeout); + } + //--------------------------------------------------------------------------- private: //--------------------------------------------------------------------------- inline bool handle_reciev(std::chrono::milliseconds timeout) @@ -741,85 +753,107 @@ using namespace std; return true; } //--------------------------------------------------------------------------- - inline - bool parse_header(http_header_info& body_info, const std::string& m_cache_to_process) - { + inline bool parse_header(http_header_info& body_info, const std::string& m_cache_to_process) + { MTRACE("http_stream_filter::parse_cached_header(*)"); - - STATIC_REGEXP_EXPR_1(rexp_mach_field, - "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)" - // 12 3 4 5 6 7 8 9 10 - "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]", - //11 1213 14 - boost::regex::icase | boost::regex::normal); - - boost::smatch result; - std::string::const_iterator it_current_bound = m_cache_to_process.begin(); - std::string::const_iterator it_end_bound = m_cache_to_process.end(); - - - //lookup all fields and fill well-known fields - while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched) + const char *ptr = m_cache_to_process.c_str(); + while (ptr[0] != '\r' || ptr[1] != '\n') { - const size_t field_val = 13; - //const size_t field_etc_name = 11; - - int i = 2; //start position = 2 - if(result[i++].matched)//"Connection" - body_info.m_connection = result[field_val]; - else if(result[i++].matched)//"Referrer" - body_info.m_referer = result[field_val]; - else if(result[i++].matched)//"Content-Length" - body_info.m_content_length = result[field_val]; - else if(result[i++].matched)//"Content-Type" - body_info.m_content_type = result[field_val]; - else if(result[i++].matched)//"Transfer-Encoding" - body_info.m_transfer_encoding = result[field_val]; - else if(result[i++].matched)//"Content-Encoding" - body_info.m_content_encoding = result[field_val]; - else if(result[i++].matched)//"Host" - { body_info.m_host = result[field_val]; - string_tools::trim(body_info.m_host); + // optional \n + if (*ptr == '\n') + ++ptr; + // an identifier composed of letters or - + const char *key_pos = ptr; + while (isalnum(*ptr) || *ptr == '_' || *ptr == '-') + ++ptr; + const char *key_end = ptr; + // optional space (not in RFC, but in previous code) + if (*ptr == ' ') + ++ptr; + CHECK_AND_ASSERT_MES(*ptr == ':', true, "http_stream_filter::parse_cached_header() invalid header in: " << m_cache_to_process); + ++ptr; + // optional whitespace, but not newlines - line folding is obsolete, let's ignore it + while (isblank(*ptr)) + ++ptr; + const char *value_pos = ptr; + while (*ptr != '\r' && *ptr != '\n') + ++ptr; + const char *value_end = ptr; + // optional trailing whitespace + while (value_end > value_pos && isblank(*(value_end-1))) + --value_end; + if (*ptr == '\r') + ++ptr; + CHECK_AND_ASSERT_MES(*ptr == '\n', true, "http_stream_filter::parse_cached_header() invalid header in: " << m_cache_to_process); + ++ptr; + + const std::string key = std::string(key_pos, key_end - key_pos); + const std::string value = std::string(value_pos, value_end - value_pos); + if (!key.empty()) + { + if (!string_tools::compare_no_case(key, "Connection")) + body_info.m_connection = value; + else if(!string_tools::compare_no_case(key, "Referrer")) + body_info.m_referer = value; + else if(!string_tools::compare_no_case(key, "Content-Length")) + body_info.m_content_length = value; + else if(!string_tools::compare_no_case(key, "Content-Type")) + body_info.m_content_type = value; + else if(!string_tools::compare_no_case(key, "Transfer-Encoding")) + body_info.m_transfer_encoding = value; + else if(!string_tools::compare_no_case(key, "Content-Encoding")) + body_info.m_content_encoding = value; + else if(!string_tools::compare_no_case(key, "Host")) + body_info.m_host = value; + else if(!string_tools::compare_no_case(key, "Cookie")) + body_info.m_cookie = value; + else if(!string_tools::compare_no_case(key, "User-Agent")) + body_info.m_user_agent = value; + else if(!string_tools::compare_no_case(key, "Origin")) + body_info.m_origin = value; + else + body_info.m_etc_fields.emplace_back(key, value); } - else if(result[i++].matched)//"Cookie" - body_info.m_cookie = result[field_val]; - else if(result[i++].matched)//"User-Agent" - body_info.m_user_agent = result[field_val]; - else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) - body_info.m_etc_fields.emplace_back(result[11], result[field_val]); - else - {CHECK_AND_ASSERT_MES(false, false, "http_stream_filter::parse_cached_header() not matched last entry in:"<(result[1]); - m_response_info.m_http_ver_lo = boost::lexical_cast(result[2]); - m_response_info.m_response_code = boost::lexical_cast(result[3]); - - m_header_cache.erase(to_nonsonst_iterator(m_header_cache, result[0].first), to_nonsonst_iterator(m_header_cache, result[0].second)); - return true; - }else - { - LOG_ERROR("http_stream_filter::handle_invoke_reply_line(): Failed to match first response line:" << m_header_cache); - return false; - } - + //First line response, look like this: "HTTP/1.1 200 OK" + const char *ptr = m_header_cache.c_str(); + CHECK_AND_ASSERT_MES(!memcmp(ptr, "HTTP/", 5), false, "Invalid first response line: " + m_header_cache); + ptr += 5; + CHECK_AND_ASSERT_MES(isdigit(*ptr), false, "Invalid first response line: " + m_header_cache); + unsigned long ul; + char *end; + ul = strtoul(ptr, &end, 10); + CHECK_AND_ASSERT_MES(ul <= INT_MAX && *end =='.', false, "Invalid first response line: " + m_header_cache); + m_response_info.m_http_ver_hi = ul; + ptr = end + 1; + CHECK_AND_ASSERT_MES(isdigit(*ptr), false, "Invalid first response line: " + m_header_cache + ", ptr: " << ptr); + ul = strtoul(ptr, &end, 10); + CHECK_AND_ASSERT_MES(ul <= INT_MAX && isblank(*end), false, "Invalid first response line: " + m_header_cache + ", ptr: " << ptr); + m_response_info.m_http_ver_lo = ul; + ptr = end + 1; + while (isblank(*ptr)) + ++ptr; + CHECK_AND_ASSERT_MES(isdigit(*ptr), false, "Invalid first response line: " + m_header_cache); + ul = strtoul(ptr, &end, 10); + CHECK_AND_ASSERT_MES(ul >= 100 && ul <= 999 && isspace(*end), false, "Invalid first response line: " + m_header_cache); + m_response_info.m_response_code = ul; + ptr = end; + // ignore the optional text, till the end + while (*ptr != '\r' && *ptr != '\n') + ++ptr; + if (*ptr == '\r') + ++ptr; + CHECK_AND_ASSERT_MES(*ptr == '\n', false, "Invalid first response line: " << m_header_cache); + ++ptr; + + m_header_cache.erase(0, ptr - m_header_cache.c_str()); + return true; } inline bool set_reply_content_encoder() @@ -954,6 +988,7 @@ using namespace std; return true; } }; + typedef http_simple_client_template http_simple_client; } } } diff --git a/src/Native/libcryptonote/contrib/epee/include/net/http_client_base.h b/src/Native/libcryptonote/contrib/epee/include/net/http_client_base.h index f5fb57d03..c3da28718 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/http_client_base.h +++ b/src/Native/libcryptonote/contrib/epee/include/net/http_client_base.h @@ -38,8 +38,8 @@ namespace epee virtual ~i_sub_handler(){} virtual bool update_in( std::string& piece_of_transfer)=0; - virtual void stop(std::string& OUT collect_remains)=0; - virtual bool update_and_stop(std::string& OUT collect_remains, bool& is_changed) + virtual void stop(std::string& collect_remains)=0; + virtual bool update_and_stop(std::string& collect_remains, bool& is_changed) { is_changed = true; bool res = this->update_in(collect_remains); @@ -66,7 +66,7 @@ namespace epee { return m_powner_filter->handle_target_data(piece_of_transfer); } - virtual void stop(std::string& OUT collect_remains) + virtual void stop(std::string& collect_remains) { } diff --git a/src/Native/libcryptonote/contrib/epee/include/net/http_protocol_handler.h b/src/Native/libcryptonote/contrib/epee/include/net/http_protocol_handler.h index babe49ad7..b4485d1cd 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/http_protocol_handler.h +++ b/src/Native/libcryptonote/contrib/epee/include/net/http_protocol_handler.h @@ -54,6 +54,7 @@ namespace net_utils struct http_server_config { std::string m_folder; + std::vector m_access_control_origins; boost::optional m_user; critical_section m_lock; }; @@ -159,6 +160,7 @@ namespace net_utils struct custum_handler_config: public http_server_config { i_http_server_handler* m_phandler; + std::function rng; }; /************************************************************************/ @@ -175,7 +177,7 @@ namespace net_utils : simple_http_connection_handler(psnd_hndlr, config), m_config(config), m_conn_context(conn_context), - m_auth(m_config.m_user ? http_server_auth{*m_config.m_user} : http_server_auth{}) + m_auth(m_config.m_user ? http_server_auth{*m_config.m_user, config.rng} : http_server_auth{}) {} inline bool handle_request(const http_request_info& query_info, http_response_info& response) { @@ -193,6 +195,7 @@ namespace net_utils response.m_response_code = 200; response.m_response_comment = "OK"; response.m_body.clear(); + return m_config.m_phandler->handle_http_request(query_info, response, m_conn_context); } diff --git a/src/Native/libcryptonote/contrib/epee/include/net/http_protocol_handler.inl b/src/Native/libcryptonote/contrib/epee/include/net/http_protocol_handler.inl index c92a13bcc..c18f7f706 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/http_protocol_handler.inl +++ b/src/Native/libcryptonote/contrib/epee/include/net/http_protocol_handler.inl @@ -316,7 +316,10 @@ namespace net_utils CHECK_AND_ASSERT_MES(result[0].matched, false, "simple_http_connection_handler::analize_http_method() assert failed..."); http_ver_major = boost::lexical_cast(result[11]); http_ver_minor = boost::lexical_cast(result[12]); - if(result[4].matched) + + if(result[3].matched) + method = http::http_method_options; + else if(result[4].matched) method = http::http_method_get; else if(result[5].matched) method = http::http_method_head; @@ -342,7 +345,12 @@ namespace net_utils { analize_http_method(result, m_query_info.m_http_method, m_query_info.m_http_ver_hi, m_query_info.m_http_ver_hi); m_query_info.m_URI = result[10]; - parse_uri(m_query_info.m_URI, m_query_info.m_uri_content); + if (!parse_uri(m_query_info.m_URI, m_query_info.m_uri_content)) + { + m_state = http_state_error; + MERROR("Failed to parse URI: m_query_info.m_URI"); + return false; + } m_query_info.m_http_method_str = result[2]; m_query_info.m_full_request_str = result[0]; @@ -472,8 +480,8 @@ namespace net_utils bool simple_http_connection_handler::parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos) { STATIC_REGEXP_EXPR_1(rexp_mach_field, - "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)" - // 12 3 4 5 6 7 8 9 10 + "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)|(Origin)" + // 12 3 4 5 6 7 8 9 10 11 "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]", //11 1213 14 boost::regex::icase | boost::regex::normal); @@ -487,8 +495,8 @@ namespace net_utils //lookup all fields and fill well-known fields while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched) { - const size_t field_val = 13; - const size_t field_etc_name = 11; + const size_t field_val = 14; + const size_t field_etc_name = 12; int i = 2; //start position = 2 if(result[i++].matched)//"Connection" @@ -509,6 +517,8 @@ namespace net_utils body_info.m_cookie = result[field_val]; else if(result[i++].matched)//"User-Agent" body_info.m_user_agent = result[field_val]; + else if(result[i++].matched)//"Origin" + body_info.m_origin = result[field_val]; else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) body_info.m_etc_fields.push_back(std::pair(result[field_etc_name], result[field_val])); else @@ -537,17 +547,27 @@ namespace net_utils template bool simple_http_connection_handler::handle_request_and_send_response(const http::http_request_info& query_info) { - http_response_info response; - bool res = handle_request(query_info, response); + http_response_info response{}; //CHECK_AND_ASSERT_MES(res, res, "handle_request(query_info, response) returned false" ); + bool res = true; + + if (query_info.m_http_method != http::http_method_options) + { + res = handle_request(query_info, response); + } + else + { + response.m_response_code = 200; + response.m_response_comment = "OK"; + } std::string response_data = get_response_header(response); - //LOG_PRINT_L0("HTTP_SEND: << \r\n" << response_data + response.m_body); + LOG_PRINT_L3("HTTP_RESPONSE_HEAD: << \r\n" << response_data); m_psnd_hndlr->do_send((void*)response_data.data(), response_data.size()); - if(response.m_body.size() && (query_info.m_http_method != http::http_method_head)) + if ((response.m_body.size() && (query_info.m_http_method != http::http_method_head)) || (query_info.m_http_method == http::http_method_options)) m_psnd_hndlr->do_send((void*)response.m_body.data(), response.m_body.size()); return res; } @@ -579,7 +599,6 @@ namespace net_utils response.m_response_comment = "OK"; response.m_mime_tipe = get_file_mime_tipe(uri_to_path); - return true; } //----------------------------------------------------------------------------------- @@ -591,8 +610,12 @@ namespace net_utils "Server: Epee-based\r\n" "Content-Length: "; buf += boost::lexical_cast(response.m_body.size()) + "\r\n"; - buf += "Content-Type: "; - buf += response.m_mime_tipe + "\r\n"; + + if(!response.m_mime_tipe.empty()) + { + buf += "Content-Type: "; + buf += response.m_mime_tipe + "\r\n"; + } buf += "Last-Modified: "; time_t tm; @@ -612,6 +635,22 @@ namespace net_utils m_want_close = true; } } + + // Cross-origin resource sharing + if(m_query_info.m_header_info.m_origin.size()) + { + if (std::binary_search(m_config.m_access_control_origins.begin(), m_config.m_access_control_origins.end(), m_query_info.m_header_info.m_origin)) + { + buf += "Access-Control-Allow-Origin: "; + buf += m_query_info.m_header_info.m_origin; + buf += "\r\n"; + buf += "Access-Control-Expose-Headers: www-authenticate\r\n"; + if (m_query_info.m_http_method == http::http_method_options) + buf += "Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With\r\n"; + buf += "Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS\r\n"; + } + } + //add additional fields, if it is for(fields_list::const_iterator it = response.m_additional_fields.begin(); it!=response.m_additional_fields.end(); it++) buf += it->first + ":" + it->second + "\r\n"; diff --git a/src/Native/libcryptonote/contrib/epee/include/net/http_server_impl_base.h b/src/Native/libcryptonote/contrib/epee/include/net/http_server_impl_base.h index acecbb2d4..1a97e610a 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/http_server_impl_base.h +++ b/src/Native/libcryptonote/contrib/epee/include/net/http_server_impl_base.h @@ -55,16 +55,22 @@ namespace epee : m_net_server(external_io_service) {} - bool init(const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0", + bool init(std::function rng, const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0", + std::vector access_control_origins = std::vector(), boost::optional user = boost::none) { //set self as callback handler m_net_server.get_config_object().m_phandler = static_cast(this); + m_net_server.get_config_object().rng = std::move(rng); //here set folder for hosting reqests m_net_server.get_config_object().m_folder = ""; + //set access control allow origins if configured + std::sort(access_control_origins.begin(), access_control_origins.end()); + m_net_server.get_config_object().m_access_control_origins = std::move(access_control_origins); + m_net_server.get_config_object().m_user = std::move(user); MGINFO("Binding on " << bind_ip << ":" << bind_port); @@ -112,6 +118,11 @@ namespace epee return m_net_server.get_binded_port(); } + long get_connections_count() const + { + return m_net_server.get_connections_count(); + } + protected: net_utils::boosted_tcp_server > m_net_server; }; diff --git a/src/Native/libcryptonote/contrib/epee/include/net/levin_base.h b/src/Native/libcryptonote/contrib/epee/include/net/levin_base.h index d630bff19..7d060f5ef 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/levin_base.h +++ b/src/Native/libcryptonote/contrib/epee/include/net/levin_base.h @@ -87,6 +87,7 @@ namespace levin virtual void on_connection_new(t_connection_context& context){}; virtual void on_connection_close(t_connection_context& context){}; + virtual ~levin_commands_handler(){} }; #define LEVIN_OK 0 diff --git a/src/Native/libcryptonote/contrib/epee/include/net/levin_client_async.h b/src/Native/libcryptonote/contrib/epee/include/net/levin_client_async.h index 337d345c4..6c8f9bcb3 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/levin_client_async.h +++ b/src/Native/libcryptonote/contrib/epee/include/net/levin_client_async.h @@ -52,6 +52,7 @@ namespace levin class levin_client_async { levin_commands_handler* m_pcommands_handler; + void (*commands_handler_destroy)(levin_commands_handler*); volatile uint32_t m_is_stop; volatile uint32_t m_threads_count; ::critical_section m_send_lock; @@ -85,9 +86,9 @@ namespace levin ::critical_section m_connection_lock; net_utils::blocked_mode_client m_transport; public: - levin_client_async():m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0) + levin_client_async():m_pcommands_handler(NULL), commands_handler_destroy(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0) {} - levin_client_async(const levin_client_async& /*v*/):m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0) + levin_client_async(const levin_client_async& /*v*/):m_pcommands_handler(NULL), commands_handler_destroy(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0) {} ~levin_client_async() { @@ -97,11 +98,16 @@ namespace levin while(boost::interprocess::ipcdetail::atomic_read32(&m_threads_count)) ::Sleep(100); + + set_handler(NULL); } - void set_handler(levin_commands_handler* phandler) + void set_handler(levin_commands_handler* phandler, void (*destroy)(levin_commands_handler*) = NULL) { + if (commands_handler_destroy && m_pcommands_handler) + (*commands_handler_destroy)(m_pcommands_handler); m_pcommands_handler = phandler; + m_pcommands_handler_destroy = destroy; } bool connect(uint32_t ip, uint32_t port, uint32_t timeout) diff --git a/src/Native/libcryptonote/contrib/epee/include/net/levin_protocol_handler.h b/src/Native/libcryptonote/contrib/epee/include/net/levin_protocol_handler.h index fbc9727e2..b3a75bedc 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/levin_protocol_handler.h +++ b/src/Native/libcryptonote/contrib/epee/include/net/levin_protocol_handler.h @@ -43,6 +43,8 @@ namespace levin struct protocl_handler_config { levin_commands_handler* m_pcommands_handler; + void (*m_pcommands_handler_destroy)(levin_commands_handler*); + ~protocl_handler_config() { if (m_pcommands_handler && m_pcommands_handler_destroy) (*m_pcommands_handler_destroy)(m_pcommands_handler); } }; template diff --git a/src/Native/libcryptonote/contrib/epee/include/net/levin_protocol_handler_async.h b/src/Native/libcryptonote/contrib/epee/include/net/levin_protocol_handler_async.h index 60a667690..0b1fe05fa 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/src/Native/libcryptonote/contrib/epee/include/net/levin_protocol_handler_async.h @@ -35,6 +35,8 @@ #include "levin_base.h" #include "misc_language.h" +#include "syncobj.h" +#include "misc_os_dependent.h" #include #include @@ -72,29 +74,36 @@ class async_protocol_handler_config friend class async_protocol_handler; + levin_commands_handler* m_pcommands_handler; + void (*m_pcommands_handler_destroy)(levin_commands_handler*); + + void delete_connections (size_t count, bool incoming); + public: typedef t_connection_context connection_context; - levin_commands_handler* m_pcommands_handler; uint64_t m_max_packet_size; uint64_t m_invoke_timeout; int invoke(int command, const std::string& in_buff, std::string& buff_out, boost::uuids::uuid connection_id); template - int invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, callback_t cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED); + int invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, const callback_t &cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED); int notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id); bool close(boost::uuids::uuid connection_id); bool update_connection_context(const t_connection_context& contxt); bool request_callback(boost::uuids::uuid connection_id); template - bool foreach_connection(callback_t cb); + bool foreach_connection(const callback_t &cb); template - bool for_connection(const boost::uuids::uuid &connection_id, callback_t cb); + bool for_connection(const boost::uuids::uuid &connection_id, const callback_t &cb); size_t get_connections_count(); + void set_handler(levin_commands_handler* handler, void (*destroy)(levin_commands_handler*) = NULL); - async_protocol_handler_config():m_pcommands_handler(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE) + async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE) {} + ~async_protocol_handler_config() { set_handler(NULL, NULL); } void del_out_connections(size_t count); + void del_in_connections(size_t count); }; @@ -239,7 +248,7 @@ class async_protocol_handler std::list > m_invoke_response_handlers; template - bool add_invoke_response_handler(callback_t cb, uint64_t timeout, async_protocol_handler& con, int command) + bool add_invoke_response_handler(const callback_t &cb, uint64_t timeout, async_protocol_handler& con, int command) { CRITICAL_REGION_LOCAL(m_invoke_response_handlers_lock); boost::shared_ptr handler(boost::make_shared>(cb, timeout, con, command)); @@ -373,12 +382,16 @@ class async_protocol_handler if(m_cache_in_buffer.size() < m_current_head.m_cb) { is_continue = false; - if(cb >= MIN_BYTES_WANTED && !m_invoke_response_handlers.empty()) + if(cb >= MIN_BYTES_WANTED) { - //async call scenario - boost::shared_ptr response_handler = m_invoke_response_handlers.front(); - response_handler->reset_timer(); - MDEBUG(m_connection_context << "LEVIN_PACKET partial msg received. len=" << cb); + CRITICAL_REGION_LOCAL(m_invoke_response_handlers_lock); + if (!m_invoke_response_handlers.empty()) + { + //async call scenario + boost::shared_ptr response_handler = m_invoke_response_handlers.front(); + response_handler->reset_timer(); + MDEBUG(m_connection_context << "LEVIN_PACKET partial msg received. len=" << cb); + } } break; } @@ -519,7 +532,7 @@ class async_protocol_handler } template - bool async_invoke(int command, const std::string& in_buff, callback_t cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) + bool async_invoke(int command, const std::string& in_buff, const callback_t &cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) { misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( boost::bind(&async_protocol_handler::finish_outer_call, this)); @@ -721,32 +734,50 @@ void async_protocol_handler_config::del_connection(async_p } //------------------------------------------------------------------------------------------ template +void async_protocol_handler_config::delete_connections(size_t count, bool incoming) +{ + std::vector connections; + CRITICAL_REGION_BEGIN(m_connects_lock); + for (auto& c: m_connects) + { + if (c.second->m_connection_context.m_is_income == incoming) + connections.push_back(c.first); + } + + // close random connections from the provided set + // TODO or better just keep removing random elements (performance) + unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); + shuffle(connections.begin(), connections.end(), std::default_random_engine(seed)); + while (count > 0 && connections.size() > 0) + { + try + { + auto i = connections.end() - 1; + async_protocol_handler *conn = m_connects.at(*i); + del_connection(conn); + close(*i); + connections.erase(i); + } + catch (const std::out_of_range &e) + { + MWARNING("Connection not found in m_connects, continuing"); + } + --count; + } + + CRITICAL_REGION_END(); +} +//------------------------------------------------------------------------------------------ +template void async_protocol_handler_config::del_out_connections(size_t count) { - std::vector out_connections; - CRITICAL_REGION_BEGIN(m_connects_lock); - for (auto& c: m_connects) - { - if (!c.second->m_connection_context.m_is_income) - out_connections.push_back(c.first); - } - - if (out_connections.size() == 0) - return; - - // close random out connections - // TODO or better just keep removing random elements (performance) - unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); - shuffle(out_connections.begin(), out_connections.end(), std::default_random_engine(seed)); - while (count > 0 && out_connections.size() > 0) - { - close(*out_connections.begin()); - del_connection(m_connects.at(*out_connections.begin())); - out_connections.erase(out_connections.begin()); - --count; - } - - CRITICAL_REGION_END(); + delete_connections(count, false); +} +//------------------------------------------------------------------------------------------ +template +void async_protocol_handler_config::del_in_connections(size_t count) +{ + delete_connections(count, true); } //------------------------------------------------------------------------------------------ template @@ -786,7 +817,7 @@ int async_protocol_handler_config::invoke(int command, con } //------------------------------------------------------------------------------------------ template template -int async_protocol_handler_config::invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, callback_t cb, size_t timeout) +int async_protocol_handler_config::invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, const callback_t &cb, size_t timeout) { async_protocol_handler* aph; int r = find_and_lock_connection(connection_id, aph); @@ -794,7 +825,7 @@ int async_protocol_handler_config::invoke_async(int comman } //------------------------------------------------------------------------------------------ template template -bool async_protocol_handler_config::foreach_connection(callback_t cb) +bool async_protocol_handler_config::foreach_connection(const callback_t &cb) { CRITICAL_REGION_LOCAL(m_connects_lock); for(auto& c: m_connects) @@ -807,7 +838,7 @@ bool async_protocol_handler_config::foreach_connection(cal } //------------------------------------------------------------------------------------------ template template -bool async_protocol_handler_config::for_connection(const boost::uuids::uuid &connection_id, callback_t cb) +bool async_protocol_handler_config::for_connection(const boost::uuids::uuid &connection_id, const callback_t &cb) { CRITICAL_REGION_LOCAL(m_connects_lock); async_protocol_handler* aph = find_connection(connection_id); @@ -826,6 +857,15 @@ size_t async_protocol_handler_config::get_connections_coun } //------------------------------------------------------------------------------------------ template +void async_protocol_handler_config::set_handler(levin_commands_handler* handler, void (*destroy)(levin_commands_handler*)) +{ + if (m_pcommands_handler && m_pcommands_handler_destroy) + (*m_pcommands_handler_destroy)(m_pcommands_handler); + m_pcommands_handler = handler; + m_pcommands_handler_destroy = destroy; +} +//------------------------------------------------------------------------------------------ +template int async_protocol_handler_config::notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id) { async_protocol_handler* aph; diff --git a/src/Native/libcryptonote/contrib/epee/include/net/net_helper.h b/src/Native/libcryptonote/contrib/epee/include/net/net_helper.h index 1d808cc4c..2c2efcd82 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/net_helper.h +++ b/src/Native/libcryptonote/contrib/epee/include/net/net_helper.h @@ -31,21 +31,16 @@ //#include //#include -#include -#include -#include -#include #include +#include #include +#include #include -#include #include #include #include #include "net/net_utils_base.h" #include "misc_language.h" -//#include "profile_tools.h" -#include "../string_tools.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net" @@ -85,11 +80,13 @@ namespace net_utils public: inline - blocked_mode_client():m_socket(m_io_service), - m_initialized(false), + blocked_mode_client():m_initialized(false), m_connected(false), m_deadline(m_io_service), - m_shutdowned(0) + m_shutdowned(0), + m_ssl(false), + m_ctx(boost::asio::ssl::context::sslv23), + m_ssl_socket(m_io_service,m_ctx) { @@ -113,18 +110,25 @@ namespace net_utils } inline - bool connect(const std::string& addr, int port, std::chrono::milliseconds timeout, const std::string& bind_ip = "0.0.0.0") + bool connect(const std::string& addr, int port, std::chrono::milliseconds timeout, bool ssl = false, const std::string& bind_ip = "0.0.0.0") { - return connect(addr, std::to_string(port), timeout, bind_ip); + return connect(addr, std::to_string(port), timeout, ssl, bind_ip); } inline - bool connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout, const std::string& bind_ip = "0.0.0.0") + bool connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout, bool ssl = false, const std::string& bind_ip = "0.0.0.0") { m_connected = false; + m_ssl = ssl; try { - m_socket.close(); + m_ssl_socket.next_layer().close(); + + // Set SSL options + // disable sslv2 + m_ctx.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2); + m_ctx.set_default_verify_paths(); + // Get a list of endpoints corresponding to the server name. @@ -147,11 +151,11 @@ namespace net_utils boost::asio::ip::tcp::endpoint remote_endpoint(*iterator); - m_socket.open(remote_endpoint.protocol()); + m_ssl_socket.next_layer().open(remote_endpoint.protocol()); if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) { boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(addr.c_str()), 0); - m_socket.bind(local_endpoint); + m_ssl_socket.next_layer().bind(local_endpoint); } @@ -160,17 +164,24 @@ namespace net_utils boost::system::error_code ec = boost::asio::error::would_block; - //m_socket.connect(remote_endpoint); - m_socket.async_connect(remote_endpoint, boost::lambda::var(ec) = boost::lambda::_1); + m_ssl_socket.next_layer().async_connect(remote_endpoint, boost::lambda::var(ec) = boost::lambda::_1); while (ec == boost::asio::error::would_block) { m_io_service.run_one(); } - if (!ec && m_socket.is_open()) + if (!ec && m_ssl_socket.next_layer().is_open()) { m_connected = true; m_deadline.expires_at(std::chrono::steady_clock::time_point::max()); + // SSL Options + if(m_ssl) { + // Disable verification of host certificate + m_ssl_socket.set_verify_mode(boost::asio::ssl::verify_peer); + // Handshake + m_ssl_socket.next_layer().set_option(boost::asio::ip::tcp::no_delay(true)); + m_ssl_socket.handshake(boost::asio::ssl::stream_base::client); + } return true; }else { @@ -193,7 +204,6 @@ namespace net_utils return true; } - inline bool disconnect() { @@ -202,8 +212,9 @@ namespace net_utils if(m_connected) { m_connected = false; - m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both); - + if(m_ssl) + shutdown_ssl(); + m_ssl_socket.next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both); } } @@ -240,7 +251,7 @@ namespace net_utils // object is used as a callback and will update the ec variable when the // operation completes. The blocking_udp_client.cpp example shows how you // can use boost::bind rather than boost::lambda. - boost::asio::async_write(m_socket, boost::asio::buffer(buff), boost::lambda::var(ec) = boost::lambda::_1); + async_write(buff.c_str(), buff.size(), ec); // Block until the asynchronous operation has completed. while (ec == boost::asio::error::would_block) @@ -302,9 +313,7 @@ namespace net_utils */ boost::system::error_code ec; - size_t writen = m_socket.write_some(boost::asio::buffer(data, sz), ec); - - + size_t writen = write(data, sz, ec); if (!writen || ec) { @@ -334,10 +343,7 @@ namespace net_utils bool is_connected() { - return m_connected && m_socket.is_open(); - //TRY_ENTRY() - //return m_socket.is_open(); - //CATCH_ENTRY_L0("is_connected", false) + return m_connected && m_ssl_socket.next_layer().is_open(); } inline @@ -369,8 +375,8 @@ namespace net_utils handler_obj hndlr(ec, bytes_transfered); char local_buff[10000] = {0}; - //m_socket.async_read_some(boost::asio::buffer(local_buff, sizeof(local_buff)), hndlr); - boost::asio::async_read(m_socket, boost::asio::buffer(local_buff, sizeof(local_buff)), boost::asio::transfer_at_least(1), hndlr); + + async_read(local_buff, sizeof(local_buff), boost::asio::transfer_at_least(1), hndlr); // Block until the asynchronous operation has completed. while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned)) @@ -451,10 +457,8 @@ namespace net_utils handler_obj hndlr(ec, bytes_transfered); - - //char local_buff[10000] = {0}; - boost::asio::async_read(m_socket, boost::asio::buffer((char*)buff.data(), buff.size()), boost::asio::transfer_at_least(buff.size()), hndlr); - + async_read((char*)buff.data(), buff.size(), boost::asio::transfer_at_least(buff.size()), hndlr); + // Block until the asynchronous operation has completed. while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned)) { @@ -500,10 +504,18 @@ namespace net_utils bool shutdown() { m_deadline.cancel(); - boost::system::error_code ignored_ec; - m_socket.cancel(ignored_ec); - m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); - m_socket.close(ignored_ec); + boost::system::error_code ec; + if(m_ssl) + shutdown_ssl(); + m_ssl_socket.next_layer().cancel(ec); + if(ec) + MDEBUG("Problems at cancel: " << ec.message()); + m_ssl_socket.next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + if(ec) + MDEBUG("Problems at shutdown: " << ec.message()); + m_ssl_socket.next_layer().close(ec); + if(ec) + MDEBUG("Problems at close: " << ec.message()); boost::interprocess::ipcdetail::atomic_write32(&m_shutdowned, 1); m_connected = false; return true; @@ -520,7 +532,7 @@ namespace net_utils boost::asio::ip::tcp::socket& get_socket() { - return m_socket; + return m_ssl_socket.next_layer(); } private: @@ -537,7 +549,7 @@ namespace net_utils // connect(), read_line() or write_line() functions to return. LOG_PRINT_L3("Timed out socket"); m_connected = false; - m_socket.close(); + m_ssl_socket.next_layer().close(); // There is no longer an active deadline. The expiry is set to positive // infinity so that the actor takes no action until a new deadline is set. @@ -547,12 +559,61 @@ namespace net_utils // Put the actor back to sleep. m_deadline.async_wait(boost::bind(&blocked_mode_client::check_deadline, this)); } - + void shutdown_ssl() { + // ssl socket shutdown blocks if server doesn't respond. We close after 2 secs + boost::system::error_code ec = boost::asio::error::would_block; + m_deadline.expires_from_now(std::chrono::milliseconds(2000)); + m_ssl_socket.async_shutdown(boost::lambda::var(ec) = boost::lambda::_1); + while (ec == boost::asio::error::would_block) + { + m_io_service.run_one(); + } + // Ignore "short read" error + if (ec.category() == boost::asio::error::get_ssl_category() && + ec.value() != +#if BOOST_VERSION >= 106200 + boost::asio::ssl::error::stream_truncated +#else // older Boost supports only OpenSSL 1.0, so 1.0-only macros are appropriate + ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ) +#endif + ) + MDEBUG("Problems at ssl shutdown: " << ec.message()); + } + + protected: + bool write(const void* data, size_t sz, boost::system::error_code& ec) + { + bool success; + if(m_ssl) + success = boost::asio::write(m_ssl_socket, boost::asio::buffer(data, sz), ec); + else + success = boost::asio::write(m_ssl_socket.next_layer(), boost::asio::buffer(data, sz), ec); + return success; + } + + void async_write(const void* data, size_t sz, boost::system::error_code& ec) + { + if(m_ssl) + boost::asio::async_write(m_ssl_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); + else + boost::asio::async_write(m_ssl_socket.next_layer(), boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); + } + + void async_read(char* buff, size_t sz, boost::asio::detail::transfer_at_least_t transfer_at_least, handler_obj& hndlr) + { + if(!m_ssl) + boost::asio::async_read(m_ssl_socket.next_layer(), boost::asio::buffer(buff, sz), transfer_at_least, hndlr); + else + boost::asio::async_read(m_ssl_socket, boost::asio::buffer(buff, sz), transfer_at_least, hndlr); + + } protected: boost::asio::io_service m_io_service; - boost::asio::ip::tcp::socket m_socket; + boost::asio::ssl::context m_ctx; + boost::asio::ssl::stream m_ssl_socket; + bool m_ssl; bool m_initialized; bool m_connected; boost::asio::steady_timer m_deadline; @@ -618,8 +679,8 @@ namespace net_utils boost::system::error_code ec; - size_t writen = m_socket.write_some(boost::asio::buffer(data, sz), ec); - + size_t writen = write(data, sz, ec); + if (!writen || ec) { LOG_PRINT_L3("Problems at write: " << ec.message()); @@ -660,7 +721,7 @@ namespace net_utils // asynchronous operations are cancelled. This allows the blocked // connect(), read_line() or write_line() functions to return. LOG_PRINT_L3("Timed out socket"); - m_socket.close(); + m_ssl_socket.next_layer().close(); // There is no longer an active deadline. The expiry is set to positive // infinity so that the actor takes no action until a new deadline is set. diff --git a/src/Native/libcryptonote/contrib/epee/include/net/net_parse_helpers.h b/src/Native/libcryptonote/contrib/epee/include/net/net_parse_helpers.h index 08d2a2000..708cce0ff 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/net_parse_helpers.h +++ b/src/Native/libcryptonote/contrib/epee/include/net/net_parse_helpers.h @@ -103,7 +103,7 @@ namespace net_utils STATIC_REGEXP_EXPR_1(rexp_match_uri, "^([^?#]*)(\\?([^#]*))?(#(.*))?", boost::regex::icase | boost::regex::normal); boost::smatch result; - if(!boost::regex_search(uri, result, rexp_match_uri, boost::match_default) && result[0].matched) + if(!(boost::regex_search(uri, result, rexp_match_uri, boost::match_default) && result[0].matched)) { LOG_PRINT_L1("[PARSE URI] regex not matched for uri: " << uri); content.m_path = uri; @@ -139,7 +139,7 @@ namespace net_utils // 12 34 5 6 7 content.port = 0; boost::smatch result; - if(!boost::regex_search(url_str, result, rexp_match_uri, boost::match_default) && result[0].matched) + if(!(boost::regex_search(url_str, result, rexp_match_uri, boost::match_default) && result[0].matched)) { LOG_PRINT_L1("[PARSE URI] regex not matched for uri: " << rexp_match_uri); //content.m_path = uri; diff --git a/src/Native/libcryptonote/contrib/epee/include/net/net_utils_base.h b/src/Native/libcryptonote/contrib/epee/include/net/net_utils_base.h index ef3a1d146..7615786be 100644 --- a/src/Native/libcryptonote/contrib/epee/include/net/net_utils_base.h +++ b/src/Native/libcryptonote/contrib/epee/include/net/net_utils_base.h @@ -29,12 +29,11 @@ #ifndef _NET_UTILS_BASE_H_ #define _NET_UTILS_BASE_H_ -#include -#include #include +#include +#include +#include #include "serialization/keyvalue_serialization.h" -#include "net/local_ip.h" -#include "string_tools.h" #include "misc_log_ex.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -44,108 +43,176 @@ #define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24)) #endif - namespace epee { namespace net_utils { - struct network_address_base + class ipv4_network_address { - public: - bool operator==(const network_address_base &other) const { return m_full_id == other.m_full_id; } - bool operator!=(const network_address_base &other) const { return !operator==(other); } - bool operator<(const network_address_base &other) const { return m_full_id < other.m_full_id; } - bool is_same_host(const network_address_base &other) const { return m_host_id == other.m_host_id; } - virtual std::string str() const = 0; - virtual std::string host_str() const = 0; - virtual bool is_loopback() const = 0; - virtual bool is_local() const = 0; - virtual uint8_t get_type_id() const = 0; - protected: - // A very simple non cryptographic hash function by Fowler, Noll, Vo - uint64_t fnv1a(const uint8_t *data, size_t len) const { - uint64_t h = 0xcbf29ce484222325; - while (len--) - h = (h ^ *data++) * 0x100000001b3; - return h; - } - uint64_t m_host_id; - uint64_t m_full_id; - }; - struct ipv4_network_address: public network_address_base - { - void init_ids() - { - m_host_id = fnv1a((const uint8_t*)&m_ip, sizeof(m_ip)); - m_full_id = fnv1a((const uint8_t*)&m_ip, sizeof(m_ip) + sizeof(m_port)); - } - public: - ipv4_network_address(uint32_t ip, uint16_t port): network_address_base(), m_ip(ip), m_port(port) { init_ids(); } - uint32_t ip() const { return m_ip; } - uint16_t port() const { return m_port; } - virtual std::string str() const { return epee::string_tools::get_ip_string_from_int32(m_ip) + ":" + std::to_string(m_port); } - virtual std::string host_str() const { return epee::string_tools::get_ip_string_from_int32(m_ip); } - virtual bool is_loopback() const { return epee::net_utils::is_ip_loopback(m_ip); } - virtual bool is_local() const { return epee::net_utils::is_ip_local(m_ip); } - virtual uint8_t get_type_id() const { return ID; } - public: // serialization - static const uint8_t ID = 1; -#pragma pack(push) -#pragma pack(1) uint32_t m_ip; uint16_t m_port; -#pragma pack(pop) + + public: + constexpr ipv4_network_address(uint32_t ip, uint16_t port) noexcept + : m_ip(ip), m_port(port) {} + + bool equal(const ipv4_network_address& other) const noexcept; + bool less(const ipv4_network_address& other) const noexcept; + constexpr bool is_same_host(const ipv4_network_address& other) const noexcept + { return ip() == other.ip(); } + + constexpr uint32_t ip() const noexcept { return m_ip; } + constexpr uint16_t port() const noexcept { return m_port; } + std::string str() const; + std::string host_str() const; + bool is_loopback() const; + bool is_local() const; + static constexpr uint8_t get_type_id() noexcept { return ID; } + + static const uint8_t ID = 1; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(m_ip) KV_SERIALIZE(m_port) - if (!is_store) - const_cast(this_ref).init_ids(); END_KV_SERIALIZE_MAP() }; - class network_address: public boost::shared_ptr + + inline bool operator==(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept + { return lhs.equal(rhs); } + inline bool operator!=(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept + { return !lhs.equal(rhs); } + inline bool operator<(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept + { return lhs.less(rhs); } + inline bool operator<=(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept + { return !rhs.less(lhs); } + inline bool operator>(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept + { return rhs.less(lhs); } + inline bool operator>=(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept + { return !lhs.less(rhs); } + + class network_address { + struct interface + { + virtual ~interface() {}; + + virtual bool equal(const interface&) const = 0; + virtual bool less(const interface&) const = 0; + virtual bool is_same_host(const interface&) const = 0; + + virtual std::string str() const = 0; + virtual std::string host_str() const = 0; + virtual bool is_loopback() const = 0; + virtual bool is_local() const = 0; + virtual uint8_t get_type_id() const = 0; + }; + + template + struct implementation final : interface + { + T value; + + implementation(const T& src) : value(src) {} + ~implementation() = default; + + // Type-checks for cast are done in cpp + static const T& cast(const interface& src) noexcept + { return static_cast&>(src).value; } + + virtual bool equal(const interface& other) const override + { return value.equal(cast(other)); } + + virtual bool less(const interface& other) const override + { return value.less(cast(other)); } + + virtual bool is_same_host(const interface& other) const override + { return value.is_same_host(cast(other)); } + + virtual std::string str() const override { return value.str(); } + virtual std::string host_str() const override { return value.host_str(); } + virtual bool is_loopback() const override { return value.is_loopback(); } + virtual bool is_local() const override { return value.is_local(); } + virtual uint8_t get_type_id() const override { return value.get_type_id(); } + }; + + std::shared_ptr self; + + template + Type& as_mutable() const + { + // types `implmentation` and `implementation` are unique + using Type_ = typename std::remove_const::type; + network_address::interface* const self_ = self.get(); // avoid clang warning in typeid + if (!self_ || typeid(implementation) != typeid(*self_)) + throw std::bad_cast{}; + return static_cast*>(self_)->value; + } public: - network_address() {} - network_address(ipv4_network_address *address): boost::shared_ptr(address) {} - bool operator==(const network_address &other) const { return (*this)->operator==(*other); } - bool operator!=(const network_address &other) const { return (*this)->operator!=(*other); } - bool operator<(const network_address &other) const { return (*this)->operator<(*other); } - bool is_same_host(const network_address &other) const { return (*this)->is_same_host(*other); } - std::string str() const { return (*this) ? (*this)->str() : ""; } - std::string host_str() const { return (*this) ? (*this)->host_str() : ""; } - bool is_loopback() const { return (*this)->is_loopback(); } - bool is_local() const { return (*this)->is_local(); } - uint8_t get_type_id() const { return (*this)->get_type_id(); } - template Type &as() { if (get_type_id() != Type::ID) throw std::runtime_error("Bad type"); return *(Type*)get(); } - template const Type &as() const { if (get_type_id() != Type::ID) throw std::runtime_error("Bad type"); return *(const Type*)get(); } + network_address() : self(nullptr) {} + template + network_address(const T& src) + : self(std::make_shared>(src)) {} + bool equal(const network_address &other) const; + bool less(const network_address &other) const; + bool is_same_host(const network_address &other) const; + std::string str() const { return self ? self->str() : ""; } + std::string host_str() const { return self ? self->host_str() : ""; } + bool is_loopback() const { return self ? self->is_loopback() : false; } + bool is_local() const { return self ? self->is_local() : false; } + uint8_t get_type_id() const { return self ? self->get_type_id() : 0; } + template const Type &as() const { return as_mutable(); } BEGIN_KV_SERIALIZE_MAP() uint8_t type = is_store ? this_ref.get_type_id() : 0; - epee::serialization::selector::serialize(type, stg, hparent_section, "type"); + if (!epee::serialization::selector::serialize(type, stg, hparent_section, "type")) + return false; switch (type) { case ipv4_network_address::ID: + { if (!is_store) - const_cast(this_ref).reset(new ipv4_network_address(0, 0)); - KV_SERIALIZE(template as()); + { + const_cast(this_ref) = ipv4_network_address{0, 0}; + auto &addr = this_ref.template as_mutable(); + if (epee::serialization::selector::serialize(addr, stg, hparent_section, "addr")) + MDEBUG("Found as addr: " << this_ref.str()); + else if (epee::serialization::selector::serialize(addr, stg, hparent_section, "template as()")) + MDEBUG("Found as template as(): " << this_ref.str()); + else if (epee::serialization::selector::serialize(addr, stg, hparent_section, "template as_mutable()")) + MDEBUG("Found as template as_mutable(): " << this_ref.str()); + else + { + MWARNING("Address not found"); + return false; + } + } + else + { + auto &addr = this_ref.template as_mutable(); + if (!epee::serialization::selector::serialize(addr, stg, hparent_section, "addr")) + return false; + } break; - default: MERROR("Unsupported network address type: " << type); return false; + } + default: MERROR("Unsupported network address type: " << (unsigned)type); return false; } END_KV_SERIALIZE_MAP() }; - inline bool create_network_address(network_address &address, const std::string &string, uint16_t default_port = 0) - { - uint32_t ip; - uint16_t port; - if (epee::string_tools::parse_peer_from_string(ip, port, string)) - { - if (default_port && !port) - port = default_port; - address.reset(new ipv4_network_address(ip, port)); - return true; - } - return false; - } + + inline bool operator==(const network_address& lhs, const network_address& rhs) + { return lhs.equal(rhs); } + inline bool operator!=(const network_address& lhs, const network_address& rhs) + { return !lhs.equal(rhs); } + inline bool operator<(const network_address& lhs, const network_address& rhs) + { return lhs.less(rhs); } + inline bool operator<=(const network_address& lhs, const network_address& rhs) + { return !rhs.less(lhs); } + inline bool operator>(const network_address& lhs, const network_address& rhs) + { return rhs.less(lhs); } + inline bool operator>=(const network_address& lhs, const network_address& rhs) + { return !lhs.less(rhs); } + + bool create_network_address(network_address &address, const std::string &string, uint16_t default_port = 0); + /************************************************************************/ /* */ /************************************************************************/ @@ -179,7 +246,7 @@ namespace net_utils {} connection_context_base(): m_connection_id(), - m_remote_address(new ipv4_network_address(0,0)), + m_remote_address(ipv4_network_address{0,0}), m_is_income(false), m_started(time(NULL)), m_last_recv(0), @@ -228,21 +295,8 @@ namespace net_utils //some helpers - inline - std::string print_connection_context(const connection_context_base& ctx) - { - std::stringstream ss; - ss << ctx.m_remote_address->str() << " " << epee::string_tools::get_str_from_guid_a(ctx.m_connection_id) << (ctx.m_is_income ? " INC":" OUT"); - return ss.str(); - } - - inline - std::string print_connection_context_short(const connection_context_base& ctx) - { - std::stringstream ss; - ss << ctx.m_remote_address->str() << (ctx.m_is_income ? " INC":" OUT"); - return ss.str(); - } + std::string print_connection_context(const connection_context_base& ctx); + std::string print_connection_context_short(const connection_context_base& ctx); inline MAKE_LOGGABLE(connection_context_base, ct, os) { diff --git a/src/Native/libcryptonote/contrib/epee/include/net/network_throttle-detail.hpp b/src/Native/libcryptonote/contrib/epee/include/net/network_throttle-detail.hpp new file mode 100644 index 000000000..955668d62 --- /dev/null +++ b/src/Native/libcryptonote/contrib/epee/include/net/network_throttle-detail.hpp @@ -0,0 +1,125 @@ +/// @file +/// @author rfree (current maintainer in monero.cc project) +/// @brief implementaion for throttling of connection (count and rate-limit speed etc) + +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* rfree: throttle details, implementing rate limiting */ + + +#ifndef INCLUDED_throttle_detail_hpp +#define INCLUDED_throttle_detail_hpp + +#include "network_throttle.hpp" + +namespace epee +{ +namespace net_utils +{ + + +class network_throttle : public i_network_throttle { + private: + struct packet_info { + size_t m_size; // octets sent. Summary for given small-window (e.g. for all packaged in 1 second) + packet_info(); + }; + + + network_speed_bps m_target_speed; + size_t m_network_add_cost; // estimated add cost of headers + size_t m_network_minimal_segment; // estimated minimal cost of sending 1 byte to round up to + size_t m_network_max_segment; // recommended max size of 1 TCP transmission + + const size_t m_window_size; // the number of samples to average over + network_time_seconds m_slot_size; // the size of one slot. TODO: now hardcoded for 1 second e.g. in time_to_slot() + // TODO for big window size, for performance better the substract on change of m_last_sample_time instead of recalculating average of eg >100 elements + + std::vector< packet_info > m_history; // the history of bw usage + network_time_seconds m_last_sample_time; // time of last history[0] - so we know when to rotate the buffer + network_time_seconds m_start_time; // when we were created + bool m_any_packet_yet; // did we yet got any packet to count + + std::string m_name; // my name for debug and logs + std::string m_nameshort; // my name for debug and logs (used in log file name) + + // each sample is now 1 second + public: + network_throttle(const std::string &nameshort, const std::string &name, int window_size=-1); + virtual ~network_throttle(); + virtual void set_name(const std::string &name); + virtual void set_target_speed( network_speed_kbps target ); + virtual network_speed_kbps get_target_speed(); + + // add information about events: + virtual void handle_trafic_exact(size_t packet_size); ///< count the new traffic/packet; the size is exact considering all network costs + virtual void handle_trafic_tcp(size_t packet_size); ///< count the new traffic/packet; the size is as TCP, we will consider MTU etc + + virtual void tick(); ///< poke and update timers/history (recalculates, moves the history if needed, checks the real clock etc) + + virtual double get_time_seconds() const ; ///< timer that we use, time in seconds, monotionic + + // time calculations: + virtual void calculate_times(size_t packet_size, calculate_times_struct &cts, bool dbg, double force_window) const; ///< MAIN LOGIC (see base class for info) + + virtual network_time_seconds get_sleep_time_after_tick(size_t packet_size); ///< increase the timer if needed, and get the package size + virtual network_time_seconds get_sleep_time(size_t packet_size) const; ///< gets the Delay (recommended Delay time) from calc. (not safe: only if time didnt change?) TODO + + virtual size_t get_recommended_size_of_planned_transport() const; ///< what should be the size (bytes) of next data block to be transported + virtual size_t get_recommended_size_of_planned_transport_window(double force_window) const; ///< ditto, but for given windows time frame + virtual double get_current_speed() const; + + private: + virtual network_time_seconds time_to_slot(network_time_seconds t) const { return std::floor( t ); } // convert exact time eg 13.7 to rounded time for slot number in history 13 + virtual void _handle_trafic_exact(size_t packet_size, size_t orginal_size); + virtual void logger_handle_net(const std::string &filename, double time, size_t size); +}; + +/*** + * The complete set of traffic throttle for one typical connection +*/ +struct network_throttle_bw { + public: + network_throttle m_in; ///< for incomming traffic (this we can not controll directly as it depends of what others send to us - usually) + network_throttle m_inreq; ///< for requesting incomming traffic (this is exact usually) + network_throttle m_out; ///< for outgoing traffic that we just sent (this is exact usually) + + public: + network_throttle_bw(const std::string &name1); +}; + + + +} // namespace net_utils +} // namespace epee + + +#endif + + diff --git a/src/Native/libcryptonote/contrib/epee/include/net/network_throttle.hpp b/src/Native/libcryptonote/contrib/epee/include/net/network_throttle.hpp new file mode 100644 index 000000000..7df79a908 --- /dev/null +++ b/src/Native/libcryptonote/contrib/epee/include/net/network_throttle.hpp @@ -0,0 +1,171 @@ +/// @file +/// @author rfree (current maintainer in monero.cc project) +/// @brief interface for throttling of connection (count and rate-limit speed etc) + +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* rfree: throttle basic interface */ +/* rfree: also includes the manager for singeton/global such objects */ + + +#ifndef INCLUDED_network_throttle_hpp +#define INCLUDED_network_throttle_hpp + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "syncobj.h" + +#include "net/net_utils_base.h" +#include "misc_log_ex.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "misc_language.h" +#include "pragma_comp_defs.h" +#include +#include +#include + +#include +#include +#include + +namespace epee +{ +namespace net_utils +{ + +// just typedefs to in code define the units used. TODO later it will be enforced that casts to other numericals are only explicit to avoid mistakes? use boost::chrono? +typedef double network_speed_kbps; // externally, for parameters and return values, all defined in kilobytes per second +typedef double network_speed_bps; // throttle-internally, bytes per second +typedef double network_time_seconds; +typedef double network_MB; + +class i_network_throttle; + +/*** +@brief All information about given throttle - speed calculations +*/ +struct calculate_times_struct { + double average; + double window; + double delay; + double recomendetDataSize; +}; +typedef calculate_times_struct calculate_times_struct; + + +/*** +@brief Access to simple throttles, with singlton to access global network limits +*/ +class network_throttle_manager { + // provides global (singleton) in/inreq/out throttle access + + // [[note1]] see also http://www.nuonsoft.com/blog/2012/10/21/implementing-a-thread-safe-singleton-with-c11/ + // [[note2]] _inreq is the requested in traffic - we anticipate we will get in-bound traffic soon as result of what we do (e.g. that we sent network downloads requests) + + //protected: + public: // XXX + + static boost::mutex m_lock_get_global_throttle_in; + static boost::mutex m_lock_get_global_throttle_inreq; + static boost::mutex m_lock_get_global_throttle_out; + + friend class connection_basic; // FRIEND - to directly access global throttle-s. !! REMEMBER TO USE LOCKS! + friend class connection_basic_pimpl; // ditto + + public: + static i_network_throttle & get_global_throttle_in(); ///< singleton ; for friend class ; caller MUST use proper locks! like m_lock_get_global_throttle_in + static i_network_throttle & get_global_throttle_inreq(); ///< ditto ; use lock ... use m_lock_get_global_throttle_inreq obviously + static i_network_throttle & get_global_throttle_out(); ///< ditto ; use lock ... use m_lock_get_global_throttle_out obviously +}; + + + +/*** +@brief interface for the throttle, see the derivated class +*/ +class i_network_throttle { + public: + virtual void set_name(const std::string &name)=0; + virtual void set_target_speed( network_speed_kbps target )=0; + virtual network_speed_kbps get_target_speed()=0; + + virtual void handle_trafic_exact(size_t packet_size) =0; // count the new traffic/packet; the size is exact considering all network costs + virtual void handle_trafic_tcp(size_t packet_size) =0; // count the new traffic/packet; the size is as TCP, we will consider MTU etc + virtual void tick() =0; // poke and update timers/history + + // time calculations: + + virtual void calculate_times(size_t packet_size, calculate_times_struct &cts, bool dbg, double force_window) const =0; // assuming sending new package (or 0), calculate: + // Average, Window, Delay, Recommended data size ; also gets dbg=debug flag, and forced widnow size if >0 or -1 for not forcing window size + + // Average speed, Window size, recommended Delay to sleep now, Recommended size of data to send now + + virtual network_time_seconds get_sleep_time(size_t packet_size) const =0; // gets the D (recommended Delay time) from calc + virtual network_time_seconds get_sleep_time_after_tick(size_t packet_size) =0; // ditto, but first tick the timer + + virtual size_t get_recommended_size_of_planned_transport() const =0; // what should be the recommended limit of data size that we can transport over current network_throttle in near future + + virtual double get_time_seconds() const =0; // a timer + virtual void logger_handle_net(const std::string &filename, double time, size_t size)=0; + + +}; + + +// ... more in the -advanced.h file + + +} // namespace net_utils +} // namespace epee + + +#endif + + + diff --git a/src/Native/libcryptonote/contrib/epee/include/profile_tools.h b/src/Native/libcryptonote/contrib/epee/include/profile_tools.h index f285fe48b..a0b5f77f4 100644 --- a/src/Native/libcryptonote/contrib/epee/include/profile_tools.h +++ b/src/Native/libcryptonote/contrib/epee/include/profile_tools.h @@ -28,6 +28,8 @@ #ifndef _PROFILE_TOOLS_H_ #define _PROFILE_TOOLS_H_ +#include "misc_os_dependent.h" + namespace epee { diff --git a/src/Native/libcryptonote/contrib/epee/include/readline_buffer.h b/src/Native/libcryptonote/contrib/epee/include/readline_buffer.h index cda7e34f9..87c8826cb 100644 --- a/src/Native/libcryptonote/contrib/epee/include/readline_buffer.h +++ b/src/Native/libcryptonote/contrib/epee/include/readline_buffer.h @@ -2,9 +2,7 @@ #include #include -#include #include -#include namespace rdln { diff --git a/src/Native/libcryptonote/contrib/epee/include/reg_exp_definer.h b/src/Native/libcryptonote/contrib/epee/include/reg_exp_definer.h index e2bed5c3f..eb11c9e10 100644 --- a/src/Native/libcryptonote/contrib/epee/include/reg_exp_definer.h +++ b/src/Native/libcryptonote/contrib/epee/include/reg_exp_definer.h @@ -29,7 +29,7 @@ #define _REG_EXP_DEFINER_H_ #include - +#include "syncobj.h" namespace epee { diff --git a/src/Native/libcryptonote/contrib/epee/include/serialization/keyvalue_serialization.h b/src/Native/libcryptonote/contrib/epee/include/serialization/keyvalue_serialization.h index d4413a71b..5791e1998 100644 --- a/src/Native/libcryptonote/contrib/epee/include/serialization/keyvalue_serialization.h +++ b/src/Native/libcryptonote/contrib/epee/include/serialization/keyvalue_serialization.h @@ -31,7 +31,6 @@ #include "misc_log_ex.h" #include "enableable.h" #include "keyvalue_serialization_overloads.h" -#include "serialization/serialization.h" namespace epee { diff --git a/src/Native/libcryptonote/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/src/Native/libcryptonote/contrib/epee/include/serialization/keyvalue_serialization_overloads.h index 1a58cab99..7087136cc 100644 --- a/src/Native/libcryptonote/contrib/epee/include/serialization/keyvalue_serialization_overloads.h +++ b/src/Native/libcryptonote/contrib/epee/include/serialization/keyvalue_serialization_overloads.h @@ -26,6 +26,13 @@ #pragma once +#include +#include +#include +#include +#include +#include + namespace epee { namespace serialization @@ -73,7 +80,7 @@ namespace epee template static bool unserialize_t_obj(serializible_type& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) { - typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true); + typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, false); if(!hchild_section) return false; return obj._load(stg, hchild_section); } @@ -90,7 +97,7 @@ namespace epee static bool unserialize_t_obj(enableable& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) { obj.enabled = false; - typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true); + typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, false); if(!hchild_section) return false; obj.enabled = true; return obj.v._load(stg, hchild_section); @@ -117,9 +124,9 @@ namespace epee typename stl_container::value_type exchange_val; typename t_storage::harray hval_array = stg.get_first_value(pname, exchange_val, hparent_section); if(!hval_array) return false; - container.push_back(std::move(exchange_val)); + container.insert(container.end(), std::move(exchange_val)); while(stg.get_next_value(hval_array, exchange_val)) - container.push_back(std::move(exchange_val)); + container.insert(container.end(), std::move(exchange_val)); return true; }//-------------------------------------------------------------------------------------------------------------------- template @@ -152,7 +159,7 @@ namespace epee "size in blob " << loaded_size << " not have not zero modulo for sizeof(value_type) = " << sizeof(typename stl_container::value_type)); size_t count = (loaded_size/sizeof(typename stl_container::value_type)); for(size_t i = 0; i < count; i++) - container.push_back(*(pelem++)); + container.insert(container.end(), *(pelem++)); } return res; } @@ -186,12 +193,12 @@ namespace epee typename t_storage::harray hsec_array = stg.get_first_section(pname, hchild_section, hparent_section); if(!hsec_array || !hchild_section) return false; res = val._load(stg, hchild_section); - container.push_back(val); + container.insert(container.end(), val); while(stg.get_next_section(hsec_array, hchild_section)) { typename stl_container::value_type val_l = typename stl_container::value_type(); res |= val_l._load(stg, hchild_section); - container.push_back(std::move(val_l)); + container.insert(container.end(), std::move(val_l)); } return res; } @@ -227,6 +234,18 @@ namespace epee } //------------------------------------------------------------------------------------------------------------------- template + static bool kv_serialize(const std::deque& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_stl_container_t_val(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool kv_unserialize(std::deque& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_stl_container_t_val(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template static bool kv_serialize(const std::list& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) { return serialize_stl_container_t_val(d, stg, hparent_section, pname); @@ -238,6 +257,18 @@ namespace epee return unserialize_stl_container_t_val(d, stg, hparent_section, pname); } //------------------------------------------------------------------------------------------------------------------- + template + static bool kv_serialize(const std::set& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_stl_container_t_val(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool kv_unserialize(std::set& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_stl_container_t_val(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- }; template<> struct kv_serialization_overloads_impl_is_base_serializable_types @@ -268,6 +299,18 @@ namespace epee } //------------------------------------------------------------------------------------------------------------------- template + static bool kv_serialize(const std::deque& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_stl_container_t_obj(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool kv_unserialize(std::deque& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_stl_container_t_obj(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template static bool kv_serialize(const std::list& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) { return serialize_stl_container_t_obj(d, stg, hparent_section, pname); @@ -278,6 +321,18 @@ namespace epee { return unserialize_stl_container_t_obj(d, stg, hparent_section, pname); } + //------------------------------------------------------------------------------------------------------------------- + template + static bool kv_serialize(const std::set& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_stl_container_t_obj(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool kv_unserialize(std::set& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_stl_container_t_obj(d, stg, hparent_section, pname); + } }; template struct base_serializable_types: public boost::mpl::vector::type @@ -353,6 +408,18 @@ namespace epee } //------------------------------------------------------------------------------------------------------------------- template + bool kv_serialize(const std::deque& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types, typename std::remove_const::type>::value>::kv_serialize(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + bool kv_unserialize(std::deque& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types, typename std::remove_const::type>::value>::kv_unserialize(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template bool kv_serialize(const std::list& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) { return kv_serialization_overloads_impl_is_base_serializable_types, typename std::remove_const::type>::value>::kv_serialize(d, stg, hparent_section, pname); @@ -363,5 +430,17 @@ namespace epee { return kv_serialization_overloads_impl_is_base_serializable_types, typename std::remove_const::type>::value>::kv_unserialize(d, stg, hparent_section, pname); } + //------------------------------------------------------------------------------------------------------------------- + template + bool kv_serialize(const std::set& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types, typename std::remove_const::type>::value>::kv_serialize(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + bool kv_unserialize(std::set& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types, typename std::remove_const::type>::value>::kv_unserialize(d, stg, hparent_section, pname); + } } } diff --git a/src/Native/libcryptonote/contrib/epee/include/span.h b/src/Native/libcryptonote/contrib/epee/include/span.h index ea4ba63dd..452cc088f 100644 --- a/src/Native/libcryptonote/contrib/epee/include/span.h +++ b/src/Native/libcryptonote/contrib/epee/include/span.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017, The Monero Project +// Copyright (c) 2017-2018, The Monero Project // // All rights reserved. // @@ -108,7 +108,7 @@ namespace epee template constexpr bool has_padding() noexcept { - return !std::is_pod() || alignof(T) != 1; + return !std::is_standard_layout() || alignof(T) != 1; } //! \return Cast data from `src` as `span`. diff --git a/src/Native/libcryptonote/contrib/epee/include/storages/http_abstract_invoke.h b/src/Native/libcryptonote/contrib/epee/include/storages/http_abstract_invoke.h index 823ce6731..d93084ab0 100644 --- a/src/Native/libcryptonote/contrib/epee/include/storages/http_abstract_invoke.h +++ b/src/Native/libcryptonote/contrib/epee/include/storages/http_abstract_invoke.h @@ -44,8 +44,11 @@ namespace epee if(!serialization::store_t_to_json(out_struct, req_param)) return false; + http::fields_list additional_params; + additional_params.push_back(std::make_pair("Content-Type","application/json; charset=utf-8")); + const http::http_response_info* pri = NULL; - if(!transport.invoke(uri, method, req_param, timeout, std::addressof(pri))) + if(!transport.invoke(uri, method, req_param, timeout, std::addressof(pri), std::move(additional_params))) { LOG_PRINT_L1("Failed to invoke http request to " << uri); return false; @@ -112,7 +115,7 @@ namespace epee } if(resp_t.error.code || resp_t.error.message.size()) { - LOG_ERROR("RPC call of \"" << method_name << "\" returned error: " << resp_t.error.code << ", message: " << resp_t.error.message); + LOG_ERROR("RPC call of \"" << req_t.method << "\" returned error: " << resp_t.error.code << ", message: " << resp_t.error.message); return false; } result_struct = resp_t.result; diff --git a/src/Native/libcryptonote/contrib/epee/include/storages/levin_abstract_invoke2.h b/src/Native/libcryptonote/contrib/epee/include/storages/levin_abstract_invoke2.h index 8ced9d689..d77e7a1f8 100644 --- a/src/Native/libcryptonote/contrib/epee/include/storages/levin_abstract_invoke2.h +++ b/src/Native/libcryptonote/contrib/epee/include/storages/levin_abstract_invoke2.h @@ -60,8 +60,7 @@ namespace epee LOG_ERROR("Failed to load_from_binary on command " << command); return false; } - result_struct.load(stg_ret); - return true; + return result_struct.load(stg_ret); } template @@ -105,13 +104,11 @@ namespace epee LOG_ERROR("Failed to load_from_binary on command " << command); return false; } - result_struct.load(stg_ret); - - return true; + return result_struct.load(stg_ret); } template - bool async_invoke_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_transport& transport, callback_t cb, size_t inv_timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) + bool async_invoke_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_transport& transport, const callback_t &cb, size_t inv_timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) { typename serialization::portable_storage stg; const_cast(out_struct).store(stg);//TODO: add true const support to searilzation @@ -133,7 +130,12 @@ namespace epee cb(LEVIN_ERROR_FORMAT, result_struct, context); return false; } - result_struct.load(stg_ret); + if (!result_struct.load(stg_ret)) + { + LOG_ERROR("Failed to load result struct on command " << command); + cb(LEVIN_ERROR_FORMAT, result_struct, context); + return false; + } cb(code, result_struct, context); return true; }, inv_timeout); @@ -176,7 +178,11 @@ namespace epee boost::value_initialized in_struct; boost::value_initialized out_struct; - static_cast(in_struct).load(strg); + if (!static_cast(in_struct).load(strg)) + { + LOG_ERROR("Failed to load in_struct in command " << command); + return -1; + } int res = cb(command, static_cast(in_struct), static_cast(out_struct), context); serialization::portable_storage strg_out; static_cast(out_struct).store(strg_out); @@ -200,7 +206,11 @@ namespace epee return -1; } boost::value_initialized in_struct; - static_cast(in_struct).load(strg); + if (!static_cast(in_struct).load(strg)) + { + LOG_ERROR("Failed to load in_struct in notify " << command); + return -1; + } return cb(command, in_struct, context); } diff --git a/src/Native/libcryptonote/contrib/epee/include/storages/portable_storage_from_json.h b/src/Native/libcryptonote/contrib/epee/include/storages/portable_storage_from_json.h index 04b57376c..727f36552 100644 --- a/src/Native/libcryptonote/contrib/epee/include/storages/portable_storage_from_json.h +++ b/src/Native/libcryptonote/contrib/epee/include/storages/portable_storage_from_json.h @@ -25,6 +25,8 @@ // #pragma once +#include +#include #include "parserse_base_utils.h" #include "file_io_utils.h" diff --git a/src/Native/libcryptonote/contrib/epee/include/storages/portable_storage_to_bin.h b/src/Native/libcryptonote/contrib/epee/include/storages/portable_storage_to_bin.h index 5695143b0..9501bbc2a 100644 --- a/src/Native/libcryptonote/contrib/epee/include/storages/portable_storage_to_bin.h +++ b/src/Native/libcryptonote/contrib/epee/include/storages/portable_storage_to_bin.h @@ -28,6 +28,7 @@ #pragma once +#include "pragma_comp_defs.h" #include "misc_language.h" #include "portable_storage_base.h" @@ -47,6 +48,9 @@ namespace epee PRAGMA_WARNING_PUSH PRAGMA_GCC("GCC diagnostic ignored \"-Wstrict-aliasing\"") +#ifdef __clang__ + PRAGMA_GCC("GCC diagnostic ignored \"-Wtautological-constant-out-of-range-compare\"") +#endif template size_t pack_varint(t_stream& strm, size_t val) { //the first two bits always reserved for size information diff --git a/src/Native/libcryptonote/contrib/epee/include/storages/portable_storage_val_converters.h b/src/Native/libcryptonote/contrib/epee/include/storages/portable_storage_val_converters.h index e9b91c82b..36bb28627 100644 --- a/src/Native/libcryptonote/contrib/epee/include/storages/portable_storage_val_converters.h +++ b/src/Native/libcryptonote/contrib/epee/include/storages/portable_storage_val_converters.h @@ -28,6 +28,9 @@ #pragma once +#include +#include + #include "misc_language.h" #include "portable_storage_base.h" #include "warnings.h" @@ -131,6 +134,36 @@ POP_WARNINGS } }; + // For MyMonero/OpenMonero backend compatibility + // MyMonero backend sends amount, fees and timestamp values as strings. + // Until MM backend is updated, this is needed for compatibility between OpenMonero and MyMonero. + template<> + struct convert_to_integral + { + static void convert(const std::string& from, uint64_t& to) + { + MTRACE("Converting std::string to uint64_t. Source: " << from); + // String only contains digits + if(std::all_of(from.begin(), from.end(), ::isdigit)) + to = boost::lexical_cast(from); + // MyMonero ISO 8061 timestamp (2017-05-06T16:27:06Z) + else if (boost::regex_match (from, boost::regex("\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\dZ"))) + { + // Convert to unix timestamp +#ifdef HAVE_STRPTIME + struct tm tm; + if (strptime(from.c_str(), "%Y-%m-%dT%H:%M:%S", &tm)) +#else + std::tm tm = {}; + std::istringstream ss(from); + if (ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S")) +#endif + to = std::mktime(&tm); + } else + ASSERT_AND_THROW_WRONG_CONVERSION(); + } + }; + template struct is_convertable: std::integral_constant::value && diff --git a/src/Native/libcryptonote/contrib/epee/include/string_tools.h b/src/Native/libcryptonote/contrib/epee/include/string_tools.h index ce7b2fb87..63705e401 100644 --- a/src/Native/libcryptonote/contrib/epee/include/string_tools.h +++ b/src/Native/libcryptonote/contrib/epee/include/string_tools.h @@ -35,6 +35,7 @@ # include #endif +#include #include #include #include @@ -42,8 +43,9 @@ #include #include #include -#include +#include #include "hex.h" +#include "memwipe.h" #include "span.h" #include "warnings.h" @@ -160,7 +162,7 @@ DISABLE_GCC_WARNING(maybe-uninitialized) val = boost::lexical_cast(str_id); return true; } - catch(std::exception& /*e*/) + catch(const std::exception& /*e*/) { //const char* pmsg = e.what(); return false; @@ -329,7 +331,7 @@ POP_WARNINGS template std::string pod_to_hex(const t_pod_type& s) { - static_assert(std::is_pod::value, "expected pod type"); + static_assert(std::is_standard_layout(), "expected standard layout type"); return to_hex::string(as_byte_span(s)); } //---------------------------------------------------------------------------- @@ -349,6 +351,14 @@ POP_WARNINGS s = *(t_pod_type*)bin_buff.data(); return true; } + //---------------------------------------------------------------------------- + template + bool hex_to_pod(const std::string& hex_str, tools::scrubbed& s) + { + return hex_to_pod(hex_str, unwrap(s)); + } + //---------------------------------------------------------------------------- + bool validate_hex(uint64_t length, const std::string& str); //---------------------------------------------------------------------------- inline std::string get_extension(const std::string& str) { diff --git a/src/Native/libcryptonote/contrib/epee/include/wipeable_string.h b/src/Native/libcryptonote/contrib/epee/include/wipeable_string.h new file mode 100644 index 000000000..70d1a9586 --- /dev/null +++ b/src/Native/libcryptonote/contrib/epee/include/wipeable_string.h @@ -0,0 +1,67 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include +#include +#include + +namespace epee +{ + class wipeable_string + { + public: + wipeable_string() {} + wipeable_string(const wipeable_string &other); + wipeable_string(wipeable_string &&other); + wipeable_string(const std::string &other); + wipeable_string(std::string &&other); + wipeable_string(const char *s); + ~wipeable_string(); + void wipe(); + void push_back(char c); + void pop_back(); + const char *data() const noexcept { return buffer.data(); } + size_t size() const noexcept { return buffer.size(); } + bool empty() const noexcept { return buffer.empty(); } + void resize(size_t sz); + void reserve(size_t sz); + void clear(); + bool operator==(const wipeable_string &other) const noexcept { return buffer == other.buffer; } + bool operator!=(const wipeable_string &other) const noexcept { return buffer != other.buffer; } + wipeable_string &operator=(wipeable_string &&other); + wipeable_string &operator=(const wipeable_string &other); + + private: + void grow(size_t sz, size_t reserved = 0); + + private: + std::vector buffer; + }; +} diff --git a/src/Native/libcryptonote/contrib/epee/src/hex.cpp b/src/Native/libcryptonote/contrib/epee/src/hex.cpp index cfbd3cf87..c143b2dc2 100644 --- a/src/Native/libcryptonote/contrib/epee/src/hex.cpp +++ b/src/Native/libcryptonote/contrib/epee/src/hex.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017, The Monero Project +// Copyright (c) 2017-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/contrib/epee/src/memwipe.c b/src/Native/libcryptonote/contrib/epee/src/memwipe.c new file mode 100644 index 000000000..026ef7277 --- /dev/null +++ b/src/Native/libcryptonote/contrib/epee/src/memwipe.c @@ -0,0 +1,116 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file Copyright (c) 2009-2015 The Bitcoin Core developers + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include + +#ifndef _WIN32 +#include +#endif + +#ifdef HAVE_EXPLICIT_BZERO +#include +#endif +#include "memwipe.h" + +#if defined(_MSC_VER) +#define SCARECROW +#else +#define SCARECROW \ + __asm__ __volatile__("" : : "r"(ptr) : "memory"); +#endif + +#ifdef HAVE_MEMSET_S + +void *memwipe(void *ptr, size_t n) +{ + if (memset_s(ptr, n, 0, n)) + { +#ifdef NDEBUG + fprintf(stderr, "Error: memset_s failed\n"); + _exit(1); +#else + abort(); +#endif + } + SCARECROW // might as well... + return ptr; +} + +#elif defined HAVE_EXPLICIT_BZERO + +void *memwipe(void *ptr, size_t n) +{ + explicit_bzero(ptr, n); + SCARECROW + return ptr; +} + +#else + +/* The memory_cleanse implementation is taken from Bitcoin */ + +/* Compilers have a bad habit of removing "superfluous" memset calls that + * are trying to zero memory. For example, when memset()ing a buffer and + * then free()ing it, the compiler might decide that the memset is + * unobservable and thus can be removed. + * + * Previously we used OpenSSL which tried to stop this by a) implementing + * memset in assembly on x86 and b) putting the function in its own file + * for other platforms. + * + * This change removes those tricks in favour of using asm directives to + * scare the compiler away. As best as our compiler folks can tell, this is + * sufficient and will continue to be so. + * + * Adam Langley + * Commit: ad1907fe73334d6c696c8539646c21b11178f20f + * BoringSSL (LICENSE: ISC) + */ +static void memory_cleanse(void *ptr, size_t len) +{ + memset(ptr, 0, len); + + /* As best as we can tell, this is sufficient to break any optimisations that + might try to eliminate "superfluous" memsets. If there's an easy way to + detect memset_s, it would be better to use that. */ + SCARECROW +} + +void *memwipe(void *ptr, size_t n) +{ + memory_cleanse(ptr, n); + SCARECROW + return ptr; +} + +#endif diff --git a/src/Native/libcryptonote/contrib/epee/src/wipeable_string.cpp b/src/Native/libcryptonote/contrib/epee/src/wipeable_string.cpp new file mode 100644 index 000000000..6ed4ee8a2 --- /dev/null +++ b/src/Native/libcryptonote/contrib/epee/src/wipeable_string.cpp @@ -0,0 +1,146 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include "memwipe.h" +#include "misc_log_ex.h" +#include "wipeable_string.h" + +namespace epee +{ + +wipeable_string::wipeable_string(const wipeable_string &other): + buffer(other.buffer) +{ +} + +wipeable_string::wipeable_string(wipeable_string &&other) +{ + if (&other == this) + return; + buffer = std::move(other.buffer); +} + +wipeable_string::wipeable_string(const std::string &other) +{ + grow(other.size()); + memcpy(buffer.data(), other.c_str(), size()); +} + +wipeable_string::wipeable_string(std::string &&other) +{ + grow(other.size()); + memcpy(buffer.data(), other.c_str(), size()); + if (!other.empty()) + { + memwipe(&other[0], other.size()); // we're kinda left with this again aren't we + other = std::string(); + } +} + +wipeable_string::wipeable_string(const char *s) +{ + grow(strlen(s)); + memcpy(buffer.data(), s, size()); +} + +wipeable_string::~wipeable_string() +{ + wipe(); +} + +void wipeable_string::wipe() +{ + if (!buffer.empty()) + memwipe(buffer.data(), buffer.size() * sizeof(char)); +} + +void wipeable_string::grow(size_t sz, size_t reserved) +{ + if (reserved < sz) + reserved = sz; + if (reserved <= buffer.capacity()) + { + if (sz < buffer.size()) + memwipe(buffer.data() + sz, buffer.size() - sz); + buffer.resize(sz); + return; + } + size_t old_sz = buffer.size(); + std::unique_ptr tmp{new char[old_sz]}; + memcpy(tmp.get(), buffer.data(), old_sz * sizeof(char)); + if (old_sz > 0) + memwipe(buffer.data(), old_sz * sizeof(char)); + buffer.reserve(reserved); + buffer.resize(sz); + memcpy(buffer.data(), tmp.get(), old_sz * sizeof(char)); + if (old_sz > 0) + memwipe(tmp.get(), old_sz * sizeof(char)); +} + +void wipeable_string::push_back(char c) +{ + grow(size() + 1); + buffer.back() = c; +} + +void wipeable_string::pop_back() +{ + resize(size() - 1); +} + +void wipeable_string::resize(size_t sz) +{ + grow(sz); +} + +void wipeable_string::reserve(size_t sz) +{ + grow(size(), sz); +} + +void wipeable_string::clear() +{ + resize(0); +} + +wipeable_string &wipeable_string::operator=(wipeable_string &&other) +{ + if (&other != this) + buffer = std::move(other.buffer); + return *this; +} + +wipeable_string &wipeable_string::operator=(const wipeable_string &other) +{ + if (&other != this) + buffer = other.buffer; + return *this; +} + +} diff --git a/src/Native/libcryptonote/crypto/CMakeLists.txt b/src/Native/libcryptonote/crypto/CMakeLists.txt new file mode 100644 index 000000000..71dcedcab --- /dev/null +++ b/src/Native/libcryptonote/crypto/CMakeLists.txt @@ -0,0 +1,103 @@ +# Copyright (c) 2014-2018, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set(crypto_sources + aesb.c + blake256.c + chacha.c + crypto-ops-data.c + crypto-ops.c + crypto.cpp + groestl.c + hash-extra-blake.c + hash-extra-groestl.c + hash-extra-jh.c + hash-extra-skein.c + hash.c + jh.c + keccak.c + oaes_lib.c + random.c + skein.c + slow-hash.c + tree-hash.c) + +set(crypto_headers) + +set(crypto_private_headers + blake256.h + chacha.h + crypto-ops.h + crypto.h + generic-ops.h + groestl.h + groestl_tables.h + hash-ops.h + hash.h + initializer.h + jh.h + keccak.h + oaes_config.h + oaes_lib.h + random.h + skein.h + skein_port.h) + +monero_private_headers(cncrypto + ${crypto_private_headers}) +monero_add_library(cncrypto + ${crypto_sources} + ${crypto_headers} + ${crypto_private_headers}) +target_link_libraries(cncrypto + PUBLIC + epee + ${Boost_SYSTEM_LIBRARY} + PRIVATE + ${EXTRA_LIBRARIES}) + +if (ARM) + option(NO_OPTIMIZED_MULTIPLY_ON_ARM + "Compute multiply using generic C implementation instead of ARM ASM" OFF) + if(NO_OPTIMIZED_MULTIPLY_ON_ARM) + message(STATUS "Using generic C implementation for multiply") + set_property(SOURCE slow-hash.c + PROPERTY COMPILE_DEFINITIONS "NO_OPTIMIZED_MULTIPLY_ON_ARM") + endif() +endif() + +# Because of the way Qt works on android with JNI, the code does not live in the main android thread +# So this code runs with a 1 MB default stack size. +# This will force the use of the heap for the allocation of the scratchpad +if (ANDROID OR IOS) + if( BUILD_GUI_DEPS ) + add_definitions(-DFORCE_USE_HEAP=1) + endif() +endif() + + diff --git a/src/Native/libcryptonote/crypto/blake256.c b/src/Native/libcryptonote/crypto/blake256.c index 1e43f9c4d..d503c47e0 100644 --- a/src/Native/libcryptonote/crypto/blake256.c +++ b/src/Native/libcryptonote/crypto/blake256.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -157,7 +157,7 @@ void blake256_update(state *S, const uint8_t *data, uint64_t datalen) { int left = S->buflen >> 3; int fill = 64 - left; - if (left && (((datalen >> 3) & 0x3F) >= (unsigned) fill)) { + if (left && (((datalen >> 3)) >= (unsigned) fill)) { memcpy((void *) (S->buf + left), (void *) data, fill); S->t[0] += 512; if (S->t[0] == 0) S->t[1]++; diff --git a/src/Native/libcryptonote/crypto/blake256.h b/src/Native/libcryptonote/crypto/blake256.h index 921fcd2fd..073772289 100644 --- a/src/Native/libcryptonote/crypto/blake256.h +++ b/src/Native/libcryptonote/crypto/blake256.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/crypto/chacha.c b/src/Native/libcryptonote/crypto/chacha.c new file mode 100644 index 000000000..5d3edb98d --- /dev/null +++ b/src/Native/libcryptonote/crypto/chacha.c @@ -0,0 +1,182 @@ +/* +chacha-merged.c version 20080118 +D. J. Bernstein +Public domain. +*/ + +#include +#include +#ifndef _MSC_VER +#include +#endif + +#include "chacha.h" +#include "common/int-util.h" +#include "warnings.h" + +/* + * The following macros are used to obtain exact-width results. + */ +#define U8V(v) ((uint8_t)(v) & UINT8_C(0xFF)) +#define U32V(v) ((uint32_t)(v) & UINT32_C(0xFFFFFFFF)) + +/* + * The following macros load words from an array of bytes with + * different types of endianness, and vice versa. + */ +#define U8TO32_LITTLE(p) SWAP32LE(((uint32_t*)(p))[0]) +#define U32TO8_LITTLE(p, v) (((uint32_t*)(p))[0] = SWAP32LE(v)) + +#define ROTATE(v,c) (rol32(v,c)) +#define XOR(v,w) ((v) ^ (w)) +#define PLUS(v,w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v),1)) + +#define QUARTERROUND(a,b,c,d) \ + a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ + a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); + +static const char sigma[] = "expand 32-byte k"; + +DISABLE_GCC_AND_CLANG_WARNING(strict-aliasing) + +static void chacha(unsigned rounds, const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher) { + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + char* ctarget = 0; + char tmp[64]; + int i; + + if (!length) return; + + j0 = U8TO32_LITTLE(sigma + 0); + j1 = U8TO32_LITTLE(sigma + 4); + j2 = U8TO32_LITTLE(sigma + 8); + j3 = U8TO32_LITTLE(sigma + 12); + j4 = U8TO32_LITTLE(key + 0); + j5 = U8TO32_LITTLE(key + 4); + j6 = U8TO32_LITTLE(key + 8); + j7 = U8TO32_LITTLE(key + 12); + j8 = U8TO32_LITTLE(key + 16); + j9 = U8TO32_LITTLE(key + 20); + j10 = U8TO32_LITTLE(key + 24); + j11 = U8TO32_LITTLE(key + 28); + j12 = 0; + j13 = 0; + j14 = U8TO32_LITTLE(iv + 0); + j15 = U8TO32_LITTLE(iv + 4); + + for (;;) { + if (length < 64) { + memcpy(tmp, data, length); + data = tmp; + ctarget = cipher; + cipher = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = rounds;i > 0;i -= 2) { + QUARTERROUND( x0, x4, x8,x12) + QUARTERROUND( x1, x5, x9,x13) + QUARTERROUND( x2, x6,x10,x14) + QUARTERROUND( x3, x7,x11,x15) + QUARTERROUND( x0, x5,x10,x15) + QUARTERROUND( x1, x6,x11,x12) + QUARTERROUND( x2, x7, x8,x13) + QUARTERROUND( x3, x4, x9,x14) + } + x0 = PLUS( x0, j0); + x1 = PLUS( x1, j1); + x2 = PLUS( x2, j2); + x3 = PLUS( x3, j3); + x4 = PLUS( x4, j4); + x5 = PLUS( x5, j5); + x6 = PLUS( x6, j6); + x7 = PLUS( x7, j7); + x8 = PLUS( x8, j8); + x9 = PLUS( x9, j9); + x10 = PLUS(x10,j10); + x11 = PLUS(x11,j11); + x12 = PLUS(x12,j12); + x13 = PLUS(x13,j13); + x14 = PLUS(x14,j14); + x15 = PLUS(x15,j15); + + x0 = XOR( x0,U8TO32_LITTLE((uint8_t*)data + 0)); + x1 = XOR( x1,U8TO32_LITTLE((uint8_t*)data + 4)); + x2 = XOR( x2,U8TO32_LITTLE((uint8_t*)data + 8)); + x3 = XOR( x3,U8TO32_LITTLE((uint8_t*)data + 12)); + x4 = XOR( x4,U8TO32_LITTLE((uint8_t*)data + 16)); + x5 = XOR( x5,U8TO32_LITTLE((uint8_t*)data + 20)); + x6 = XOR( x6,U8TO32_LITTLE((uint8_t*)data + 24)); + x7 = XOR( x7,U8TO32_LITTLE((uint8_t*)data + 28)); + x8 = XOR( x8,U8TO32_LITTLE((uint8_t*)data + 32)); + x9 = XOR( x9,U8TO32_LITTLE((uint8_t*)data + 36)); + x10 = XOR(x10,U8TO32_LITTLE((uint8_t*)data + 40)); + x11 = XOR(x11,U8TO32_LITTLE((uint8_t*)data + 44)); + x12 = XOR(x12,U8TO32_LITTLE((uint8_t*)data + 48)); + x13 = XOR(x13,U8TO32_LITTLE((uint8_t*)data + 52)); + x14 = XOR(x14,U8TO32_LITTLE((uint8_t*)data + 56)); + x15 = XOR(x15,U8TO32_LITTLE((uint8_t*)data + 60)); + + j12 = PLUSONE(j12); + if (!j12) + { + j13 = PLUSONE(j13); + /* stopping at 2^70 bytes per iv is user's responsibility */ + } + + U32TO8_LITTLE(cipher + 0,x0); + U32TO8_LITTLE(cipher + 4,x1); + U32TO8_LITTLE(cipher + 8,x2); + U32TO8_LITTLE(cipher + 12,x3); + U32TO8_LITTLE(cipher + 16,x4); + U32TO8_LITTLE(cipher + 20,x5); + U32TO8_LITTLE(cipher + 24,x6); + U32TO8_LITTLE(cipher + 28,x7); + U32TO8_LITTLE(cipher + 32,x8); + U32TO8_LITTLE(cipher + 36,x9); + U32TO8_LITTLE(cipher + 40,x10); + U32TO8_LITTLE(cipher + 44,x11); + U32TO8_LITTLE(cipher + 48,x12); + U32TO8_LITTLE(cipher + 52,x13); + U32TO8_LITTLE(cipher + 56,x14); + U32TO8_LITTLE(cipher + 60,x15); + + if (length <= 64) { + if (length < 64) { + memcpy(ctarget, cipher, length); + } + return; + } + length -= 64; + cipher += 64; + data = (uint8_t*)data + 64; + } +} + +void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher) +{ + chacha(8, data, length, key, iv, cipher); +} + +void chacha20(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher) +{ + chacha(20, data, length, key, iv, cipher); +} diff --git a/src/Native/libcryptonote/crypto/chacha.h b/src/Native/libcryptonote/crypto/chacha.h new file mode 100644 index 000000000..7a120931a --- /dev/null +++ b/src/Native/libcryptonote/crypto/chacha.h @@ -0,0 +1,91 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include +#include + +#define CHACHA_KEY_SIZE 32 +#define CHACHA_IV_SIZE 8 + +#if defined(__cplusplus) +#include + +#include "memwipe.h" +#include "hash.h" + +namespace crypto { + extern "C" { +#endif + void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher); + void chacha20(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher); +#if defined(__cplusplus) + } + + using chacha_key = tools::scrubbed_arr; + +#pragma pack(push, 1) + // MS VC 2012 doesn't interpret `class chacha_iv` as POD in spite of [9.0.10], so it is a struct + struct chacha_iv { + uint8_t data[CHACHA_IV_SIZE]; + }; +#pragma pack(pop) + + static_assert(sizeof(chacha_key) == CHACHA_KEY_SIZE && sizeof(chacha_iv) == CHACHA_IV_SIZE, "Invalid structure size"); + + inline void chacha8(const void* data, std::size_t length, const chacha_key& key, const chacha_iv& iv, char* cipher) { + chacha8(data, length, key.data(), reinterpret_cast(&iv), cipher); + } + + inline void chacha20(const void* data, std::size_t length, const chacha_key& key, const chacha_iv& iv, char* cipher) { + chacha20(data, length, key.data(), reinterpret_cast(&iv), cipher); + } + + inline void generate_chacha_key(const void *data, size_t size, chacha_key& key) { + static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); + tools::scrubbed_arr pwd_hash; + crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 0/*prehashed*/); + memcpy(&key, pwd_hash.data(), sizeof(key)); + } + + inline void generate_chacha_key_prehashed(const void *data, size_t size, chacha_key& key) { + static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); + tools::scrubbed_arr pwd_hash; + crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 1/*prehashed*/); + memcpy(&key, pwd_hash.data(), sizeof(key)); + } + + inline void generate_chacha_key(std::string password, chacha_key& key) { + return generate_chacha_key(password.data(), password.size(), key); + } +} + +#endif diff --git a/src/Native/libcryptonote/crypto/crypto-ops-data.c b/src/Native/libcryptonote/crypto/crypto-ops-data.c index 4bd75b77c..4ff4310de 100644 --- a/src/Native/libcryptonote/crypto/crypto-ops-data.c +++ b/src/Native/libcryptonote/crypto/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -870,3 +870,4 @@ const fe fe_fffb1 = {-31702527, -2466483, -26106795, -12203692, -12169197, -3210 const fe fe_fffb2 = {8166131, -6741800, -17040804, 3154616, 21461005, 1466302, -30876704, -6368709, 10503587, -13363080}; /* sqrt(2 * A * (A + 2)) */ const fe fe_fffb3 = {-13620103, 14639558, 4532995, 7679154, 16815101, -15883539, -22863840, -14813421, 13716513, -6477756}; /* sqrt(-sqrt(-1) * A * (A + 2)) */ const fe fe_fffb4 = {-21786234, -12173074, 21573800, 4524538, -4645904, 16204591, 8012863, -8444712, 3212926, 6885324}; /* sqrt(sqrt(-1) * A * (A + 2)) */ +const ge_p3 ge_p3_identity = { {0}, {1, 0}, {1, 0}, {0} }; diff --git a/src/Native/libcryptonote/crypto/crypto-ops.c b/src/Native/libcryptonote/crypto/crypto-ops.c index 4edfee0ce..45d412ac6 100644 --- a/src/Native/libcryptonote/crypto/crypto-ops.c +++ b/src/Native/libcryptonote/crypto/crypto-ops.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -1234,6 +1234,51 @@ void ge_double_scalarmult_base_vartime(ge_p2 *r, const unsigned char *a, const g } } +void ge_double_scalarmult_base_vartime_p3(ge_p3 *r3, const unsigned char *a, const ge_p3 *A, const unsigned char *b) { + signed char aslide[256]; + signed char bslide[256]; + ge_dsmp Ai; /* A, 3A, 5A, 7A, 9A, 11A, 13A, 15A */ + ge_p1p1 t; + ge_p3 u; + ge_p2 r; + int i; + + slide(aslide, a); + slide(bslide, b); + ge_dsm_precomp(Ai, A); + + ge_p2_0(&r); + + for (i = 255; i >= 0; --i) { + if (aslide[i] || bslide[i]) break; + } + + for (; i >= 0; --i) { + ge_p2_dbl(&t, &r); + + if (aslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_add(&t, &u, &Ai[aslide[i]/2]); + } else if (aslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_sub(&t, &u, &Ai[(-aslide[i])/2]); + } + + if (bslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_madd(&t, &u, &ge_Bi[bslide[i]/2]); + } else if (bslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_msub(&t, &u, &ge_Bi[(-bslide[i])/2]); + } + + if (i == 0) + ge_p1p1_to_p3(r3, &t); + else + ge_p1p1_to_p2(&r, &t); + } +} + /* From ge_frombytes.c, modified */ int ge_frombytes_vartime(ge_p3 *h, const unsigned char *s) { @@ -2000,17 +2045,79 @@ void ge_scalarmult(ge_p2 *r, const unsigned char *a, const ge_p3 *A) { } } -void ge_double_scalarmult_precomp_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b, const ge_dsmp Bi) { +void ge_scalarmult_p3(ge_p3 *r3, const unsigned char *a, const ge_p3 *A) { + signed char e[64]; + int carry, carry2, i; + ge_cached Ai[8]; /* 1 * A, 2 * A, ..., 8 * A */ + ge_p1p1 t; + ge_p3 u; + ge_p2 r; + + carry = 0; /* 0..1 */ + for (i = 0; i < 31; i++) { + carry += a[i]; /* 0..256 */ + carry2 = (carry + 8) >> 4; /* 0..16 */ + e[2 * i] = carry - (carry2 << 4); /* -8..7 */ + carry = (carry2 + 8) >> 4; /* 0..1 */ + e[2 * i + 1] = carry2 - (carry << 4); /* -8..7 */ + } + carry += a[31]; /* 0..128 */ + carry2 = (carry + 8) >> 4; /* 0..8 */ + e[62] = carry - (carry2 << 4); /* -8..7 */ + e[63] = carry2; /* 0..8 */ + + ge_p3_to_cached(&Ai[0], A); + for (i = 0; i < 7; i++) { + ge_add(&t, A, &Ai[i]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[i + 1], &u); + } + + ge_p2_0(&r); + for (i = 63; i >= 0; i--) { + signed char b = e[i]; + unsigned char bnegative = negative(b); + unsigned char babs = b - (((-bnegative) & b) << 1); + ge_cached cur, minuscur; + ge_p2_dbl(&t, &r); + ge_p1p1_to_p2(&r, &t); + ge_p2_dbl(&t, &r); + ge_p1p1_to_p2(&r, &t); + ge_p2_dbl(&t, &r); + ge_p1p1_to_p2(&r, &t); + ge_p2_dbl(&t, &r); + ge_p1p1_to_p3(&u, &t); + ge_cached_0(&cur); + ge_cached_cmov(&cur, &Ai[0], equal(babs, 1)); + ge_cached_cmov(&cur, &Ai[1], equal(babs, 2)); + ge_cached_cmov(&cur, &Ai[2], equal(babs, 3)); + ge_cached_cmov(&cur, &Ai[3], equal(babs, 4)); + ge_cached_cmov(&cur, &Ai[4], equal(babs, 5)); + ge_cached_cmov(&cur, &Ai[5], equal(babs, 6)); + ge_cached_cmov(&cur, &Ai[6], equal(babs, 7)); + ge_cached_cmov(&cur, &Ai[7], equal(babs, 8)); + fe_copy(minuscur.YplusX, cur.YminusX); + fe_copy(minuscur.YminusX, cur.YplusX); + fe_copy(minuscur.Z, cur.Z); + fe_neg(minuscur.T2d, cur.T2d); + ge_cached_cmov(&cur, &minuscur, bnegative); + ge_add(&t, &u, &cur); + if (i == 0) + ge_p1p1_to_p3(r3, &t); + else + ge_p1p1_to_p2(&r, &t); + } +} + +void ge_double_scalarmult_precomp_vartime2(ge_p2 *r, const unsigned char *a, const ge_dsmp Ai, const unsigned char *b, const ge_dsmp Bi) { signed char aslide[256]; signed char bslide[256]; - ge_dsmp Ai; /* A, 3A, 5A, 7A, 9A, 11A, 13A, 15A */ ge_p1p1 t; ge_p3 u; int i; slide(aslide, a); slide(bslide, b); - ge_dsm_precomp(Ai, A); ge_p2_0(r); @@ -2041,6 +2148,56 @@ void ge_double_scalarmult_precomp_vartime(ge_p2 *r, const unsigned char *a, cons } } +void ge_double_scalarmult_precomp_vartime2_p3(ge_p3 *r3, const unsigned char *a, const ge_dsmp Ai, const unsigned char *b, const ge_dsmp Bi) { + signed char aslide[256]; + signed char bslide[256]; + ge_p1p1 t; + ge_p3 u; + ge_p2 r; + int i; + + slide(aslide, a); + slide(bslide, b); + + ge_p2_0(&r); + + for (i = 255; i >= 0; --i) { + if (aslide[i] || bslide[i]) break; + } + + for (; i >= 0; --i) { + ge_p2_dbl(&t, &r); + + if (aslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_add(&t, &u, &Ai[aslide[i]/2]); + } else if (aslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_sub(&t, &u, &Ai[(-aslide[i])/2]); + } + + if (bslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_add(&t, &u, &Bi[bslide[i]/2]); + } else if (bslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_sub(&t, &u, &Bi[(-bslide[i])/2]); + } + + if (i == 0) + ge_p1p1_to_p3(r3, &t); + else + ge_p1p1_to_p2(&r, &t); + } +} + +void ge_double_scalarmult_precomp_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b, const ge_dsmp Bi) { + ge_dsmp Ai; /* A, 3A, 5A, 7A, 9A, 11A, 13A, 15A */ + + ge_dsm_precomp(Ai, A); + ge_double_scalarmult_precomp_vartime2(r, a, Ai, b, Bi); +} + void ge_mul8(ge_p1p1 *r, const ge_p2 *t) { ge_p2 u; ge_p2_dbl(r, t); @@ -2898,6 +3055,658 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s[31] = s11 >> 17; } +//copied from above and modified +/* +Input: + a[0]+256*a[1]+...+256^31*a[31] = a + b[0]+256*b[1]+...+256^31*b[31] = b + +Output: + s[0]+256*s[1]+...+256^31*s[31] = (ab) mod l + where l = 2^252 + 27742317777372353535851937790883648493. +*/ +void sc_mul(unsigned char *s, const unsigned char *a, const unsigned char *b) { + int64_t a0 = 2097151 & load_3(a); + int64_t a1 = 2097151 & (load_4(a + 2) >> 5); + int64_t a2 = 2097151 & (load_3(a + 5) >> 2); + int64_t a3 = 2097151 & (load_4(a + 7) >> 7); + int64_t a4 = 2097151 & (load_4(a + 10) >> 4); + int64_t a5 = 2097151 & (load_3(a + 13) >> 1); + int64_t a6 = 2097151 & (load_4(a + 15) >> 6); + int64_t a7 = 2097151 & (load_3(a + 18) >> 3); + int64_t a8 = 2097151 & load_3(a + 21); + int64_t a9 = 2097151 & (load_4(a + 23) >> 5); + int64_t a10 = 2097151 & (load_3(a + 26) >> 2); + int64_t a11 = (load_4(a + 28) >> 7); + int64_t b0 = 2097151 & load_3(b); + int64_t b1 = 2097151 & (load_4(b + 2) >> 5); + int64_t b2 = 2097151 & (load_3(b + 5) >> 2); + int64_t b3 = 2097151 & (load_4(b + 7) >> 7); + int64_t b4 = 2097151 & (load_4(b + 10) >> 4); + int64_t b5 = 2097151 & (load_3(b + 13) >> 1); + int64_t b6 = 2097151 & (load_4(b + 15) >> 6); + int64_t b7 = 2097151 & (load_3(b + 18) >> 3); + int64_t b8 = 2097151 & load_3(b + 21); + int64_t b9 = 2097151 & (load_4(b + 23) >> 5); + int64_t b10 = 2097151 & (load_3(b + 26) >> 2); + int64_t b11 = (load_4(b + 28) >> 7); + int64_t s0; + int64_t s1; + int64_t s2; + int64_t s3; + int64_t s4; + int64_t s5; + int64_t s6; + int64_t s7; + int64_t s8; + int64_t s9; + int64_t s10; + int64_t s11; + int64_t s12; + int64_t s13; + int64_t s14; + int64_t s15; + int64_t s16; + int64_t s17; + int64_t s18; + int64_t s19; + int64_t s20; + int64_t s21; + int64_t s22; + int64_t s23; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + int64_t carry17; + int64_t carry18; + int64_t carry19; + int64_t carry20; + int64_t carry21; + int64_t carry22; + + s0 = a0*b0; + s1 = (a0*b1 + a1*b0); + s2 = (a0*b2 + a1*b1 + a2*b0); + s3 = (a0*b3 + a1*b2 + a2*b1 + a3*b0); + s4 = (a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0); + s5 = (a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0); + s6 = (a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0); + s7 = (a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0); + s8 = (a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0); + s9 = (a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0); + s10 = (a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0); + s11 = (a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0); + s12 = (a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1); + s13 = (a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2); + s14 = (a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3); + s15 = (a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4); + s16 = (a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5); + s17 = (a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6); + s18 = (a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7); + s19 = (a8*b11 + a9*b10 + a10*b9 + a11*b8); + s20 = (a9*b11 + a10*b10 + a11*b9); + s21 = (a10*b11 + a11*b10); + s22 = a11*b11; + s23 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + carry18 = (s18 + (1<<20)) >> 21; s19 += carry18; s18 -= carry18 << 21; + carry20 = (s20 + (1<<20)) >> 21; s21 += carry20; s20 -= carry20 << 21; + carry22 = (s22 + (1<<20)) >> 21; s23 += carry22; s22 -= carry22 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + carry17 = (s17 + (1<<20)) >> 21; s18 += carry17; s17 -= carry17 << 21; + carry19 = (s19 + (1<<20)) >> 21; s20 += carry19; s19 -= carry19 << 21; + carry21 = (s21 + (1<<20)) >> 21; s22 += carry21; s21 -= carry21 << 21; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + + s[0] = s0 >> 0; + s[1] = s0 >> 8; + s[2] = (s0 >> 16) | (s1 << 5); + s[3] = s1 >> 3; + s[4] = s1 >> 11; + s[5] = (s1 >> 19) | (s2 << 2); + s[6] = s2 >> 6; + s[7] = (s2 >> 14) | (s3 << 7); + s[8] = s3 >> 1; + s[9] = s3 >> 9; + s[10] = (s3 >> 17) | (s4 << 4); + s[11] = s4 >> 4; + s[12] = s4 >> 12; + s[13] = (s4 >> 20) | (s5 << 1); + s[14] = s5 >> 7; + s[15] = (s5 >> 15) | (s6 << 6); + s[16] = s6 >> 2; + s[17] = s6 >> 10; + s[18] = (s6 >> 18) | (s7 << 3); + s[19] = s7 >> 5; + s[20] = s7 >> 13; + s[21] = s8 >> 0; + s[22] = s8 >> 8; + s[23] = (s8 >> 16) | (s9 << 5); + s[24] = s9 >> 3; + s[25] = s9 >> 11; + s[26] = (s9 >> 19) | (s10 << 2); + s[27] = s10 >> 6; + s[28] = (s10 >> 14) | (s11 << 7); + s[29] = s11 >> 1; + s[30] = s11 >> 9; + s[31] = s11 >> 17; +} + +//copied from above and modified +/* +Input: + a[0]+256*a[1]+...+256^31*a[31] = a + b[0]+256*b[1]+...+256^31*b[31] = b + c[0]+256*c[1]+...+256^31*c[31] = c + +Output: + s[0]+256*s[1]+...+256^31*s[31] = (c+ab) mod l + where l = 2^252 + 27742317777372353535851937790883648493. +*/ + +void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c) { + int64_t a0 = 2097151 & load_3(a); + int64_t a1 = 2097151 & (load_4(a + 2) >> 5); + int64_t a2 = 2097151 & (load_3(a + 5) >> 2); + int64_t a3 = 2097151 & (load_4(a + 7) >> 7); + int64_t a4 = 2097151 & (load_4(a + 10) >> 4); + int64_t a5 = 2097151 & (load_3(a + 13) >> 1); + int64_t a6 = 2097151 & (load_4(a + 15) >> 6); + int64_t a7 = 2097151 & (load_3(a + 18) >> 3); + int64_t a8 = 2097151 & load_3(a + 21); + int64_t a9 = 2097151 & (load_4(a + 23) >> 5); + int64_t a10 = 2097151 & (load_3(a + 26) >> 2); + int64_t a11 = (load_4(a + 28) >> 7); + int64_t b0 = 2097151 & load_3(b); + int64_t b1 = 2097151 & (load_4(b + 2) >> 5); + int64_t b2 = 2097151 & (load_3(b + 5) >> 2); + int64_t b3 = 2097151 & (load_4(b + 7) >> 7); + int64_t b4 = 2097151 & (load_4(b + 10) >> 4); + int64_t b5 = 2097151 & (load_3(b + 13) >> 1); + int64_t b6 = 2097151 & (load_4(b + 15) >> 6); + int64_t b7 = 2097151 & (load_3(b + 18) >> 3); + int64_t b8 = 2097151 & load_3(b + 21); + int64_t b9 = 2097151 & (load_4(b + 23) >> 5); + int64_t b10 = 2097151 & (load_3(b + 26) >> 2); + int64_t b11 = (load_4(b + 28) >> 7); + int64_t c0 = 2097151 & load_3(c); + int64_t c1 = 2097151 & (load_4(c + 2) >> 5); + int64_t c2 = 2097151 & (load_3(c + 5) >> 2); + int64_t c3 = 2097151 & (load_4(c + 7) >> 7); + int64_t c4 = 2097151 & (load_4(c + 10) >> 4); + int64_t c5 = 2097151 & (load_3(c + 13) >> 1); + int64_t c6 = 2097151 & (load_4(c + 15) >> 6); + int64_t c7 = 2097151 & (load_3(c + 18) >> 3); + int64_t c8 = 2097151 & load_3(c + 21); + int64_t c9 = 2097151 & (load_4(c + 23) >> 5); + int64_t c10 = 2097151 & (load_3(c + 26) >> 2); + int64_t c11 = (load_4(c + 28) >> 7); + int64_t s0; + int64_t s1; + int64_t s2; + int64_t s3; + int64_t s4; + int64_t s5; + int64_t s6; + int64_t s7; + int64_t s8; + int64_t s9; + int64_t s10; + int64_t s11; + int64_t s12; + int64_t s13; + int64_t s14; + int64_t s15; + int64_t s16; + int64_t s17; + int64_t s18; + int64_t s19; + int64_t s20; + int64_t s21; + int64_t s22; + int64_t s23; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + int64_t carry17; + int64_t carry18; + int64_t carry19; + int64_t carry20; + int64_t carry21; + int64_t carry22; + + s0 = c0 + a0*b0; + s1 = c1 + (a0*b1 + a1*b0); + s2 = c2 + (a0*b2 + a1*b1 + a2*b0); + s3 = c3 + (a0*b3 + a1*b2 + a2*b1 + a3*b0); + s4 = c4 + (a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0); + s5 = c5 + (a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0); + s6 = c6 + (a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0); + s7 = c7 + (a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0); + s8 = c8 + (a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0); + s9 = c9 + (a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0); + s10 = c10 + (a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0); + s11 = c11 + (a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0); + s12 = (a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1); + s13 = (a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2); + s14 = (a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3); + s15 = (a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4); + s16 = (a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5); + s17 = (a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6); + s18 = (a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7); + s19 = (a8*b11 + a9*b10 + a10*b9 + a11*b8); + s20 = (a9*b11 + a10*b10 + a11*b9); + s21 = (a10*b11 + a11*b10); + s22 = a11*b11; + s23 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + carry18 = (s18 + (1<<20)) >> 21; s19 += carry18; s18 -= carry18 << 21; + carry20 = (s20 + (1<<20)) >> 21; s21 += carry20; s20 -= carry20 << 21; + carry22 = (s22 + (1<<20)) >> 21; s23 += carry22; s22 -= carry22 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + carry17 = (s17 + (1<<20)) >> 21; s18 += carry17; s17 -= carry17 << 21; + carry19 = (s19 + (1<<20)) >> 21; s20 += carry19; s19 -= carry19 << 21; + carry21 = (s21 + (1<<20)) >> 21; s22 += carry21; s21 -= carry21 << 21; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + + s[0] = s0 >> 0; + s[1] = s0 >> 8; + s[2] = (s0 >> 16) | (s1 << 5); + s[3] = s1 >> 3; + s[4] = s1 >> 11; + s[5] = (s1 >> 19) | (s2 << 2); + s[6] = s2 >> 6; + s[7] = (s2 >> 14) | (s3 << 7); + s[8] = s3 >> 1; + s[9] = s3 >> 9; + s[10] = (s3 >> 17) | (s4 << 4); + s[11] = s4 >> 4; + s[12] = s4 >> 12; + s[13] = (s4 >> 20) | (s5 << 1); + s[14] = s5 >> 7; + s[15] = (s5 >> 15) | (s6 << 6); + s[16] = s6 >> 2; + s[17] = s6 >> 10; + s[18] = (s6 >> 18) | (s7 << 3); + s[19] = s7 >> 5; + s[20] = s7 >> 13; + s[21] = s8 >> 0; + s[22] = s8 >> 8; + s[23] = (s8 >> 16) | (s9 << 5); + s[24] = s9 >> 3; + s[25] = s9 >> 11; + s[26] = (s9 >> 19) | (s10 << 2); + s[27] = s10 >> 6; + s[28] = (s10 >> 14) | (s11 << 7); + s[29] = s11 >> 1; + s[30] = s11 >> 9; + s[31] = s11 >> 17; +} + /* Assumes that a != INT64_MIN */ static int64_t signum(int64_t a) { return (a >> 63) - ((-a) >> 63); diff --git a/src/Native/libcryptonote/crypto/crypto-ops.h b/src/Native/libcryptonote/crypto/crypto-ops.h index 37edf5b6d..dc3c60794 100644 --- a/src/Native/libcryptonote/crypto/crypto-ops.h +++ b/src/Native/libcryptonote/crypto/crypto-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -79,6 +79,7 @@ typedef ge_cached ge_dsmp[8]; extern const ge_precomp ge_Bi[8]; void ge_dsm_precomp(ge_dsmp r, const ge_p3 *s); void ge_double_scalarmult_base_vartime(ge_p2 *, const unsigned char *, const ge_p3 *, const unsigned char *); +void ge_double_scalarmult_base_vartime_p3(ge_p3 *, const unsigned char *, const ge_p3 *, const unsigned char *); /* From ge_frombytes.c, modified */ @@ -127,7 +128,10 @@ void sc_reduce(unsigned char *); /* New code */ void ge_scalarmult(ge_p2 *, const unsigned char *, const ge_p3 *); +void ge_scalarmult_p3(ge_p3 *, const unsigned char *, const ge_p3 *); void ge_double_scalarmult_precomp_vartime(ge_p2 *, const unsigned char *, const ge_p3 *, const unsigned char *, const ge_dsmp); +void ge_double_scalarmult_precomp_vartime2(ge_p2 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp); +void ge_double_scalarmult_precomp_vartime2_p3(ge_p3 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp); void ge_mul8(ge_p1p1 *, const ge_p2 *); extern const fe fe_ma2; extern const fe fe_ma; @@ -135,12 +139,15 @@ extern const fe fe_fffb1; extern const fe fe_fffb2; extern const fe fe_fffb3; extern const fe fe_fffb4; +extern const ge_p3 ge_p3_identity; void ge_fromfe_frombytes_vartime(ge_p2 *, const unsigned char *); void sc_0(unsigned char *); void sc_reduce32(unsigned char *); void sc_add(unsigned char *, const unsigned char *, const unsigned char *); void sc_sub(unsigned char *, const unsigned char *, const unsigned char *); void sc_mulsub(unsigned char *, const unsigned char *, const unsigned char *, const unsigned char *); +void sc_mul(unsigned char *, const unsigned char *, const unsigned char *); +void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c); int sc_check(const unsigned char *); int sc_isnonzero(const unsigned char *); /* Doesn't normalize */ diff --git a/src/Native/libcryptonote/crypto/crypto.cpp b/src/Native/libcryptonote/crypto/crypto.cpp index 5fb670f87..105b00c18 100644 --- a/src/Native/libcryptonote/crypto/crypto.cpp +++ b/src/Native/libcryptonote/crypto/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -43,6 +43,18 @@ #include "crypto.h" #include "hash.h" +namespace { + static void local_abort(const char *msg) + { + fprintf(stderr, "%s\n", msg); +#ifdef NDEBUG + _exit(1); +#else + abort(); +#endif + } +} + namespace crypto { using std::abort; @@ -87,14 +99,14 @@ namespace crypto { random_scalar_not_thread_safe(res); } - static inline void hash_to_scalar(const void *data, size_t length, ec_scalar &res) { + void hash_to_scalar(const void *data, size_t length, ec_scalar &res) { cn_fast_hash(data, length, reinterpret_cast(res)); sc_reduce32(&res); } /* * generate public and secret keys from a random 256-bit integer - * TODO: allow specifiying random value (for wallet recovery) + * TODO: allow specifying random value (for wallet recovery) * */ secret_key crypto_ops::generate_keys(public_key &pub, secret_key &sec, const secret_key& recovery_key, bool recover) { @@ -189,6 +201,25 @@ namespace crypto { sc_add(&derived_key, &base, &scalar); } + bool crypto_ops::derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, std::size_t output_index, public_key &derived_key) { + ec_scalar scalar; + ge_p3 point1; + ge_p3 point2; + ge_cached point3; + ge_p1p1 point4; + ge_p2 point5; + if (ge_frombytes_vartime(&point1, &out_key) != 0) { + return false; + } + derivation_to_scalar(derivation, output_index, scalar); + ge_scalarmult_base(&point2, &scalar); + ge_p3_to_cached(&point3, &point2); + ge_sub(&point4, &point1, &point3); + ge_p1p1_to_p2(&point5, &point4); + ge_tobytes(&derived_key, &point5); + return true; + } + struct s_comm { hash h; ec_point key; @@ -246,22 +277,33 @@ namespace crypto { return sc_isnonzero(&c) == 0; } - void crypto_ops::generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const public_key &D, const secret_key &r, signature &sig) { + void crypto_ops::generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional &B, const public_key &D, const secret_key &r, signature &sig) { // sanity check ge_p3 R_p3; ge_p3 A_p3; + ge_p3 B_p3; ge_p3 D_p3; if (ge_frombytes_vartime(&R_p3, &R) != 0) throw std::runtime_error("tx pubkey is invalid"); if (ge_frombytes_vartime(&A_p3, &A) != 0) throw std::runtime_error("recipient view pubkey is invalid"); + if (B && ge_frombytes_vartime(&B_p3, &*B) != 0) throw std::runtime_error("recipient spend pubkey is invalid"); if (ge_frombytes_vartime(&D_p3, &D) != 0) throw std::runtime_error("key derivation is invalid"); #if !defined(NDEBUG) { assert(sc_check(&r) == 0); - // check R == r*G - ge_p3 dbg_R_p3; - ge_scalarmult_base(&dbg_R_p3, &r); + // check R == r*G or R == r*B public_key dbg_R; - ge_p3_tobytes(&dbg_R, &dbg_R_p3); + if (B) + { + ge_p2 dbg_R_p2; + ge_scalarmult(&dbg_R_p2, &r, &B_p3); + ge_tobytes(&dbg_R, &dbg_R_p2); + } + else + { + ge_p3 dbg_R_p3; + ge_scalarmult_base(&dbg_R_p3, &r); + ge_p3_tobytes(&dbg_R, &dbg_R_p3); + } assert(R == dbg_R); // check D == r*A ge_p2 dbg_D_p2; @@ -276,43 +318,84 @@ namespace crypto { ec_scalar k; random_scalar(k); - // compute X = k*G - ge_p3 X_p3; - ge_scalarmult_base(&X_p3, &k); + s_comm_2 buf; + buf.msg = prefix_hash; + buf.D = D; + + if (B) + { + // compute X = k*B + ge_p2 X_p2; + ge_scalarmult(&X_p2, &k, &B_p3); + ge_tobytes(&buf.X, &X_p2); + } + else + { + // compute X = k*G + ge_p3 X_p3; + ge_scalarmult_base(&X_p3, &k); + ge_p3_tobytes(&buf.X, &X_p3); + } // compute Y = k*A ge_p2 Y_p2; ge_scalarmult(&Y_p2, &k, &A_p3); + ge_tobytes(&buf.Y, &Y_p2); // sig.c = Hs(Msg || D || X || Y) - s_comm_2 buf; - buf.msg = prefix_hash; - buf.D = D; - ge_p3_tobytes(&buf.X, &X_p3); - ge_tobytes(&buf.Y, &Y_p2); - hash_to_scalar(&buf, sizeof(s_comm_2), sig.c); + hash_to_scalar(&buf, sizeof(buf), sig.c); // sig.r = k - sig.c*r sc_mulsub(&sig.r, &sig.c, &r, &k); } - bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const public_key &D, const signature &sig) { + bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional &B, const public_key &D, const signature &sig) { // sanity check ge_p3 R_p3; ge_p3 A_p3; + ge_p3 B_p3; ge_p3 D_p3; if (ge_frombytes_vartime(&R_p3, &R) != 0) return false; if (ge_frombytes_vartime(&A_p3, &A) != 0) return false; + if (B && ge_frombytes_vartime(&B_p3, &*B) != 0) return false; if (ge_frombytes_vartime(&D_p3, &D) != 0) return false; if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0) return false; // compute sig.c*R - ge_p2 cR_p2; - ge_scalarmult(&cR_p2, &sig.c, &R_p3); + ge_p3 cR_p3; + { + ge_p2 cR_p2; + ge_scalarmult(&cR_p2, &sig.c, &R_p3); + public_key cR; + ge_tobytes(&cR, &cR_p2); + if (ge_frombytes_vartime(&cR_p3, &cR) != 0) return false; + } - // compute sig.r*G - ge_p3 rG_p3; - ge_scalarmult_base(&rG_p3, &sig.r); + ge_p1p1 X_p1p1; + if (B) + { + // compute X = sig.c*R + sig.r*B + ge_p2 rB_p2; + ge_scalarmult(&rB_p2, &sig.r, &B_p3); + public_key rB; + ge_tobytes(&rB, &rB_p2); + ge_p3 rB_p3; + if (ge_frombytes_vartime(&rB_p3, &rB) != 0) return false; + ge_cached rB_cached; + ge_p3_to_cached(&rB_cached, &rB_p3); + ge_add(&X_p1p1, &cR_p3, &rB_cached); + } + else + { + // compute X = sig.c*R + sig.r*G + ge_p3 rG_p3; + ge_scalarmult_base(&rG_p3, &sig.r); + ge_cached rG_cached; + ge_p3_to_cached(&rG_cached, &rG_p3); + ge_add(&X_p1p1, &cR_p3, &rG_cached); + } + ge_p2 X_p2; + ge_p1p1_to_p2(&X_p2, &X_p1p1); // compute sig.c*D ge_p2 cD_p2; @@ -322,18 +405,6 @@ namespace crypto { ge_p2 rA_p2; ge_scalarmult(&rA_p2, &sig.r, &A_p3); - // compute X = sig.c*R + sig.r*G - public_key cR; - ge_tobytes(&cR, &cR_p2); - ge_p3 cR_p3; - if (ge_frombytes_vartime(&cR_p3, &cR) != 0) return false; - ge_cached rG_cached; - ge_p3_to_cached(&rG_cached, &rG_p3); - ge_p1p1 X_p1p1; - ge_add(&X_p1p1, &cR_p3, &rG_cached); - ge_p2 X_p2; - ge_p1p1_to_p2(&X_p2, &X_p1p1); - // compute Y = sig.c*D + sig.r*A public_key cD; public_key rA; @@ -408,7 +479,7 @@ POP_WARNINGS ec_scalar sum, k, h; boost::shared_ptr buf(reinterpret_cast(malloc(rs_comm_size(pubs_count))), free); if (!buf) - abort(); + local_abort("malloc failure"); assert(sec_index < pubs_count); #if !defined(NDEBUG) { @@ -427,7 +498,7 @@ POP_WARNINGS } #endif if (ge_frombytes_vartime(&image_unp, &image) != 0) { - abort(); + local_abort("invalid key image"); } ge_dsm_precomp(image_pre, &image_unp); sc_0(&sum); @@ -446,7 +517,7 @@ POP_WARNINGS random_scalar(sig[i].c); random_scalar(sig[i].r); if (ge_frombytes_vartime(&tmp3, &*pubs[i]) != 0) { - abort(); + local_abort("invalid pubkey"); } ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r); ge_tobytes(&buf->ab[i].a, &tmp2); diff --git a/src/Native/libcryptonote/crypto/crypto.h b/src/Native/libcryptonote/crypto/crypto.h index e99b6651f..81ebfb9e2 100644 --- a/src/Native/libcryptonote/crypto/crypto.h +++ b/src/Native/libcryptonote/crypto/crypto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -31,12 +31,20 @@ #pragma once #include +#include #include #include +#include +#include +#include #include #include "common/pod-class.h" +#include "common/util.h" +#include "memwipe.h" #include "generic-ops.h" +#include "hex.h" +#include "span.h" #include "hash.h" namespace crypto { @@ -60,9 +68,7 @@ namespace crypto { friend class crypto_ops; }; - POD_CLASS secret_key: ec_scalar { - friend class crypto_ops; - }; + using secret_key = tools::scrubbed; POD_CLASS public_keyV { std::vector keys; @@ -94,6 +100,8 @@ namespace crypto { }; #pragma pack(pop) + void hash_to_scalar(const void *data, size_t length, ec_scalar &res); + static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 && sizeof(public_key) == 32 && sizeof(secret_key) == 32 && sizeof(key_derivation) == 32 && sizeof(key_image) == 32 && @@ -119,14 +127,16 @@ namespace crypto { friend bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); static void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &); friend void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &); + static bool derive_subaddress_public_key(const public_key &, const key_derivation &, std::size_t, public_key &); + friend bool derive_subaddress_public_key(const public_key &, const key_derivation &, std::size_t, public_key &); static void generate_signature(const hash &, const public_key &, const secret_key &, signature &); friend void generate_signature(const hash &, const public_key &, const secret_key &, signature &); static bool check_signature(const hash &, const public_key &, const signature &); friend bool check_signature(const hash &, const public_key &, const signature &); - static void generate_tx_proof(const hash &, const public_key &, const public_key &, const public_key &, const secret_key &, signature &); - friend void generate_tx_proof(const hash &, const public_key &, const public_key &, const public_key &, const secret_key &, signature &); - static bool check_tx_proof(const hash &, const public_key &, const public_key &, const public_key &, const signature &); - friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const public_key &, const signature &); + static void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional &, const public_key &, const secret_key &, signature &); + friend void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional &, const public_key &, const secret_key &, signature &); + static bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional &, const public_key &, const signature &); + friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional &, const public_key &, const signature &); static void generate_key_image(const public_key &, const secret_key &, key_image &); friend void generate_key_image(const public_key &, const secret_key &, key_image &); static void generate_ring_signature(const hash &, const key_image &, @@ -194,6 +204,9 @@ namespace crypto { const secret_key &base, secret_key &derived_key) { crypto_ops::derive_secret_key(derivation, output_index, base, derived_key); } + inline bool derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, std::size_t output_index, public_key &result) { + return crypto_ops::derive_subaddress_public_key(out_key, derivation, output_index, result); + } /* Generation and checking of a standard signature. */ @@ -206,12 +219,13 @@ namespace crypto { /* Generation and checking of a tx proof; given a tx pubkey R, the recipient's view pubkey A, and the key * derivation D, the signature proves the knowledge of the tx secret key r such that R=r*G and D=r*A + * When the recipient's address is a subaddress, the tx pubkey R is defined as R=r*B where B is the recipient's spend pubkey */ - inline void generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const public_key &D, const secret_key &r, signature &sig) { - crypto_ops::generate_tx_proof(prefix_hash, R, A, D, r, sig); + inline void generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional &B, const public_key &D, const secret_key &r, signature &sig) { + crypto_ops::generate_tx_proof(prefix_hash, R, A, B, D, r, sig); } - inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const public_key &D, const signature &sig) { - return crypto_ops::check_tx_proof(prefix_hash, R, A, D, sig); + inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional &B, const public_key &D, const signature &sig) { + return crypto_ops::check_tx_proof(prefix_hash, R, A, B, D, sig); } /* To send money to a key: @@ -248,8 +262,28 @@ namespace crypto { const signature *sig) { return check_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sig); } + + inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + + const static crypto::public_key null_pkey = boost::value_initialized(); + const static crypto::secret_key null_skey = boost::value_initialized(); } CRYPTO_MAKE_HASHABLE(public_key) +CRYPTO_MAKE_HASHABLE(secret_key) CRYPTO_MAKE_HASHABLE(key_image) CRYPTO_MAKE_COMPARABLE(signature) diff --git a/src/Native/libcryptonote/crypto/generic-ops.h b/src/Native/libcryptonote/crypto/generic-ops.h index 1a135ffcf..62bc758c9 100644 --- a/src/Native/libcryptonote/crypto/generic-ops.h +++ b/src/Native/libcryptonote/crypto/generic-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/crypto/groestl.h b/src/Native/libcryptonote/crypto/groestl.h index 89a073a4c..19837f309 100644 --- a/src/Native/libcryptonote/crypto/groestl.h +++ b/src/Native/libcryptonote/crypto/groestl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/crypto/groestl_tables.h b/src/Native/libcryptonote/crypto/groestl_tables.h index 8fa6d7a83..c4b368584 100644 --- a/src/Native/libcryptonote/crypto/groestl_tables.h +++ b/src/Native/libcryptonote/crypto/groestl_tables.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/crypto/hash-extra-blake.c b/src/Native/libcryptonote/crypto/hash-extra-blake.c index 236479880..d33103c97 100644 --- a/src/Native/libcryptonote/crypto/hash-extra-blake.c +++ b/src/Native/libcryptonote/crypto/hash-extra-blake.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/crypto/hash-extra-groestl.c b/src/Native/libcryptonote/crypto/hash-extra-groestl.c index b15075306..228853a44 100644 --- a/src/Native/libcryptonote/crypto/hash-extra-groestl.c +++ b/src/Native/libcryptonote/crypto/hash-extra-groestl.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/crypto/hash-extra-jh.c b/src/Native/libcryptonote/crypto/hash-extra-jh.c index 8950687d3..e765a18f3 100644 --- a/src/Native/libcryptonote/crypto/hash-extra-jh.c +++ b/src/Native/libcryptonote/crypto/hash-extra-jh.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/crypto/hash-extra-skein.c b/src/Native/libcryptonote/crypto/hash-extra-skein.c index e63e7da20..06d8f87cc 100644 --- a/src/Native/libcryptonote/crypto/hash-extra-skein.c +++ b/src/Native/libcryptonote/crypto/hash-extra-skein.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/crypto/hash-ops.h b/src/Native/libcryptonote/crypto/hash-ops.h index 6998da5a4..d77d55cf3 100644 --- a/src/Native/libcryptonote/crypto/hash-ops.h +++ b/src/Native/libcryptonote/crypto/hash-ops.h @@ -1,21 +1,21 @@ -// Copyright (c) 2014-2017, The Monero Project -// +// Copyright (c) 2014-2018, The Monero Project +// // All rights reserved. -// +// // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: -// +// // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. -// +// // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other // materials provided with the distribution. -// +// // 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. -// +// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL @@ -25,7 +25,7 @@ // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// +// // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #pragma once @@ -79,8 +79,7 @@ enum { }; void cn_fast_hash(const void *data, size_t length, char *hash); -void cn_slow_hash(const void *data, size_t length, char *hash); -void cn_slow_hash_lite(const void *data, size_t length, char *hash); +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed); void hash_extra_blake(const void *data, size_t length, char *hash); void hash_extra_groestl(const void *data, size_t length, char *hash); diff --git a/src/Native/libcryptonote/crypto/hash.c b/src/Native/libcryptonote/crypto/hash.c index ed95391d8..42f272e34 100644 --- a/src/Native/libcryptonote/crypto/hash.c +++ b/src/Native/libcryptonote/crypto/hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/crypto/hash.h b/src/Native/libcryptonote/crypto/hash.h index 22991e513..8519af1f7 100644 --- a/src/Native/libcryptonote/crypto/hash.h +++ b/src/Native/libcryptonote/crypto/hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -31,9 +31,13 @@ #pragma once #include +#include +#include #include "common/pod-class.h" #include "generic-ops.h" +#include "hex.h" +#include "span.h" namespace crypto { @@ -61,20 +65,41 @@ namespace crypto { cn_fast_hash(data, length, reinterpret_cast(&hash)); } + inline void cn_fast_hash_old_sig(const void *data, std::size_t length, char *hash) { + cn_fast_hash(data, length, hash); + } + inline hash cn_fast_hash(const void *data, std::size_t length) { hash h; cn_fast_hash(data, length, reinterpret_cast(&h)); return h; } - inline void cn_slow_hash(const void *data, std::size_t length, hash &hash) { - cn_slow_hash(data, length, reinterpret_cast(&hash)); + inline void cn_slow_hash(const void *data, std::size_t length, hash &hash, int variant = 0) { + cn_slow_hash(data, length, reinterpret_cast(&hash), variant, 0/*prehashed*/); + } + + inline void cn_slow_hash_old_sig(const void *data, std::size_t length, char *hash, int variant = 0) { + cn_slow_hash(data, length, hash, variant, 0/*prehashed*/); + } + + inline void cn_slow_hash_prehashed(const void *data, std::size_t length, hash &hash, int variant = 0) { + cn_slow_hash(data, length, reinterpret_cast(&hash), variant, 1/*prehashed*/); } inline void tree_hash(const hash *hashes, std::size_t count, hash &root_hash) { tree_hash(reinterpret_cast(hashes), count, reinterpret_cast(&root_hash)); } + inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::hash8 &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + + const static crypto::hash null_hash = boost::value_initialized(); + const static crypto::hash8 null_hash8 = boost::value_initialized(); } CRYPTO_MAKE_HASHABLE(hash) diff --git a/src/Native/libcryptonote/crypto/initializer.h b/src/Native/libcryptonote/crypto/initializer.h index 619038ae6..afbace726 100644 --- a/src/Native/libcryptonote/crypto/initializer.h +++ b/src/Native/libcryptonote/crypto/initializer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -31,8 +31,13 @@ #pragma once #if defined(__GNUC__) +#if defined(__sun) && defined(__SVR4) +#define INITIALIZER(name) __attribute__((constructor)) static void name(void) +#define FINALIZER(name) __attribute__((destructor)) static void name(void) +#else #define INITIALIZER(name) __attribute__((constructor(101))) static void name(void) #define FINALIZER(name) __attribute__((destructor(101))) static void name(void) +#endif #define REGISTER_FINALIZER(name) ((void) 0) #elif defined(_MSC_VER) diff --git a/src/Native/libcryptonote/crypto/keccak.c b/src/Native/libcryptonote/crypto/keccak.c index 090d563a2..95fb3d33d 100644 --- a/src/Native/libcryptonote/crypto/keccak.c +++ b/src/Native/libcryptonote/crypto/keccak.c @@ -2,9 +2,25 @@ // 19-Nov-11 Markku-Juhani O. Saarinen // A baseline Keccak (3rd round) implementation. +#include +#include #include "hash-ops.h" #include "keccak.h" +#ifndef _WIN32 +#include +#endif + +static void local_abort(const char *msg) +{ + fprintf(stderr, "%s\n", msg); +#ifdef NDEBUG + _exit(1); +#else + abort(); +#endif +} + const uint64_t keccakf_rndc[24] = { 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, @@ -73,12 +89,18 @@ void keccakf(uint64_t st[25], int rounds) // compute a keccak hash (md) of given byte length from "in" typedef uint64_t state_t[25]; -int keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) +void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) { state_t st; uint8_t temp[144]; size_t i, rsiz, rsizw; + static_assert(HASH_DATA_AREA <= sizeof(temp), "Bad keccak preconditions"); + if (mdlen <= 0 || (mdlen > 100 && sizeof(st) != (size_t)mdlen)) + { + local_abort("Bad keccak use"); + } + rsiz = sizeof(state_t) == mdlen ? HASH_DATA_AREA : 200 - 2 * mdlen; rsizw = rsiz / 8; @@ -91,6 +113,11 @@ int keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) } // last block and padding + if (inlen + 1 >= sizeof(temp) || inlen > rsiz || rsiz - inlen + inlen + 1 >= sizeof(temp) || rsiz == 0 || rsiz - 1 >= sizeof(temp) || rsizw * 8 > sizeof(temp)) + { + local_abort("Bad keccak use"); + } + memcpy(temp, in, inlen); temp[inlen++] = 1; memset(temp + inlen, 0, rsiz - inlen); @@ -102,8 +129,6 @@ int keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) keccakf(st, KECCAK_ROUNDS); memcpy(md, st, mdlen); - - return 0; } void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md) diff --git a/src/Native/libcryptonote/crypto/keccak.h b/src/Native/libcryptonote/crypto/keccak.h index fbd8e1904..fb9d8bd04 100644 --- a/src/Native/libcryptonote/crypto/keccak.h +++ b/src/Native/libcryptonote/crypto/keccak.h @@ -16,7 +16,7 @@ #endif // compute a keccak hash (md) of given byte length from "in" -int keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen); +void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen); // update the state void keccakf(uint64_t st[25], int norounds); diff --git a/src/Native/libcryptonote/crypto/oaes_lib.c b/src/Native/libcryptonote/crypto/oaes_lib.c index f83a44b08..aa7c3ee4b 100644 --- a/src/Native/libcryptonote/crypto/oaes_lib.c +++ b/src/Native/libcryptonote/crypto/oaes_lib.c @@ -53,6 +53,12 @@ #include #endif +#ifdef _MSC_VER +#define GETPID() _getpid() +#else +#define GETPID() getpid() +#endif + #include "oaes_config.h" #include "oaes_lib.h" @@ -478,7 +484,7 @@ static void oaes_get_seed( char buf[RANDSIZ + 1] ) sprintf( buf, "%04d%02d%02d%02d%02d%02d%03d%p%d", gmTimer->tm_year + 1900, gmTimer->tm_mon + 1, gmTimer->tm_mday, gmTimer->tm_hour, gmTimer->tm_min, gmTimer->tm_sec, timer.millitm, - _test + timer.millitm, getpid() ); + _test + timer.millitm, GETPID() ); #else struct timeval timer; struct tm *gmTimer; @@ -490,7 +496,7 @@ static void oaes_get_seed( char buf[RANDSIZ + 1] ) sprintf( buf, "%04d%02d%02d%02d%02d%02d%03d%p%d", gmTimer->tm_year + 1900, gmTimer->tm_mon + 1, gmTimer->tm_mday, gmTimer->tm_hour, gmTimer->tm_min, gmTimer->tm_sec, timer.tv_usec/1000, - _test + timer.tv_usec/1000, getpid() ); + _test + timer.tv_usec/1000, GETPID() ); #endif if( _test ) @@ -510,13 +516,7 @@ static uint32_t oaes_get_seed(void) _test = (char *) calloc( sizeof( char ), timer.millitm ); _ret = gmTimer->tm_year + 1900 + gmTimer->tm_mon + 1 + gmTimer->tm_mday + gmTimer->tm_hour + gmTimer->tm_min + gmTimer->tm_sec + timer.millitm + - (uintptr_t)(_test + timer.millitm) + -#if !defined(_MSC_VER) - getpid(); -#else - _getpid(); -#endif - + (uintptr_t) ( _test + timer.millitm ) + GETPID(); #else struct timeval timer; struct tm *gmTimer; @@ -528,7 +528,7 @@ static uint32_t oaes_get_seed(void) _test = (char *) calloc( sizeof( char ), timer.tv_usec/1000 ); _ret = gmTimer->tm_year + 1900 + gmTimer->tm_mon + 1 + gmTimer->tm_mday + gmTimer->tm_hour + gmTimer->tm_min + gmTimer->tm_sec + timer.tv_usec/1000 + - (uintptr_t) ( _test + timer.tv_usec/1000 ) + getpid(); + (uintptr_t) ( _test + timer.tv_usec/1000 ) + GETPID(); #endif if( _test ) diff --git a/src/Native/libcryptonote/crypto/random.c b/src/Native/libcryptonote/crypto/random.c index 691c31f62..9e1a70a2d 100644 --- a/src/Native/libcryptonote/crypto/random.c +++ b/src/Native/libcryptonote/crypto/random.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -42,10 +42,15 @@ static void generate_system_random_bytes(size_t n, void *result); #include #include +#include static void generate_system_random_bytes(size_t n, void *result) { HCRYPTPROV prov; +#ifdef NDEBUG +#define must_succeed(x) do if (!(x)) { fprintf(stderr, "Failed: " #x); _exit(1); } while (0) +#else #define must_succeed(x) do if (!(x)) abort(); while (0) +#endif must_succeed(CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)); must_succeed(CryptGenRandom(prov, (DWORD)n, result)); must_succeed(CryptReleaseContext(prov, 0)); diff --git a/src/Native/libcryptonote/crypto/random.h b/src/Native/libcryptonote/crypto/random.h index 75d23fd04..6468136cc 100644 --- a/src/Native/libcryptonote/crypto/random.h +++ b/src/Native/libcryptonote/crypto/random.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/crypto/skein_port.h b/src/Native/libcryptonote/crypto/skein_port.h index a06ef30a2..a50a28e6b 100644 --- a/src/Native/libcryptonote/crypto/skein_port.h +++ b/src/Native/libcryptonote/crypto/skein_port.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/crypto/slow-hash.c b/src/Native/libcryptonote/crypto/slow-hash.c index b92b6e6c3..4a8ebbf9d 100644 --- a/src/Native/libcryptonote/crypto/slow-hash.c +++ b/src/Native/libcryptonote/crypto/slow-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -32,6 +32,11 @@ #include #include #include +#include + +#ifndef _WIN32 +#include +#endif #include "common/int-util.h" #include "hash-ops.h" @@ -47,7 +52,47 @@ extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey); extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); -#if defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64)) +#define VARIANT1_1(p) \ + do if (variant > 0) \ + { \ + const uint8_t tmp = ((const uint8_t*)(p))[11]; \ + static const uint32_t table = 0x75310; \ + const uint8_t index = (((tmp >> 3) & 6) | (tmp & 1)) << 1; \ + ((uint8_t*)(p))[11] = tmp ^ ((table >> index) & 0x30); \ + } while(0) + +#define VARIANT1_2(p) \ + do if (variant > 0) \ + { \ + xor64(p, tweak1_2); \ + } while(0) + +#define VARIANT1_CHECK() \ + do if (length < 43) \ + { \ + fprintf(stderr, "Cryptonight variants need at least 43 bytes of data"); \ + _exit(1); \ + } while(0) + +#define NONCE_POINTER (((const uint8_t*)data)+35) + +#define VARIANT1_PORTABLE_INIT() \ + uint8_t tweak1_2[8]; \ + do if (variant > 0) \ + { \ + VARIANT1_CHECK(); \ + memcpy(&tweak1_2, &state.hs.b[192], sizeof(tweak1_2)); \ + xor64(tweak1_2, NONCE_POINTER); \ + } while(0) + +#define VARIANT1_INIT64() \ + if (variant > 0) \ + { \ + VARIANT1_CHECK(); \ + } \ + const uint64_t tweak1_2 = variant > 0 ? (state.hs.w[24] ^ (*((const uint64_t*)NONCE_POINTER))) : 0 + +#if !defined NO_AES && (defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64))) // Optimised code below, uses x86-specific intrinsics, SSE2, AES-NI // Fall back to more portable code is down at the bottom @@ -125,6 +170,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp _mm_store_si128(R128(c), _c); \ _b = _mm_xor_si128(_b, _c); \ _mm_store_si128(R128(&hp_state[j]), _b); \ + VARIANT1_1(&hp_state[j]); \ j = state_index(c); \ p = U64(&hp_state[j]); \ b[0] = p[0]; b[1] = p[1]; \ @@ -133,6 +179,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp p = U64(&hp_state[j]); \ p[0] = a[0]; p[1] = a[1]; \ a[0] ^= b[0]; a[1] ^= b[1]; \ + VARIANT1_2(p + 1); \ _b = _c; \ #if defined(_MSC_VER) @@ -183,6 +230,11 @@ STATIC INLINE void xor_blocks(uint8_t *a, const uint8_t *b) U64(a)[1] ^= U64(b)[1]; } +STATIC INLINE void xor64(uint64_t *a, const uint64_t b) +{ + *a ^= b; +} + /** * @brief uses cpuid to determine if the CPU supports the AES instructions * @return true if the CPU supports AES, false otherwise @@ -515,8 +567,7 @@ void slow_hash_free_state(void) * @param length the length in bytes of the data * @param hash a pointer to a buffer in which the final 256 bit hash will be stored */ - -void cn_slow_hash(const void *data, size_t length, char *hash) +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) { RDATA_ALIGN16 uint8_t expandedKey[240]; /* These buffers are aligned to use later with SSE functions */ @@ -543,10 +594,15 @@ void cn_slow_hash(const void *data, size_t length, char *hash) slow_hash_allocate_state(); /* CryptoNight Step 1: Use Keccak1600 to initialize the 'state' (and 'text') buffers from the data. */ - - hash_process(&state.hs, data, length); + if (prehashed) { + memcpy(&state.hs, data, length); + } else { + hash_process(&state.hs, data, length); + } memcpy(text, state.init, INIT_SIZE_BYTE); + VARIANT1_INIT64(); + /* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill * the 2MB large random access buffer. */ @@ -645,7 +701,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) extra_hashes[state.hs.b[0] & 3](&state, 200, hash); } -#elif defined(__arm__) || defined(__aarch64__) +#elif !defined NO_AES && (defined(__arm__) || defined(__aarch64__)) void slow_hash_allocate_state(void) { // Do nothing, this is just to maintain compatibility with the upgraded slow-hash.c @@ -670,6 +726,11 @@ void slow_hash_free_state(void) #define U64(x) ((uint64_t *) (x)) +STATIC INLINE void xor64(uint64_t *a, const uint64_t b) +{ + *a ^= b; +} + #pragma pack(push, 1) union cn_slow_hash_state { @@ -706,6 +767,7 @@ union cn_slow_hash_state vst1q_u8((uint8_t *)c, _c); \ _b = veorq_u8(_b, _c); \ vst1q_u8(&hp_state[j], _b); \ + VARIANT1_1(&hp_state[j]); \ j = state_index(c); \ p = U64(&hp_state[j]); \ b[0] = p[0]; b[1] = p[1]; \ @@ -714,6 +776,7 @@ union cn_slow_hash_state p = U64(&hp_state[j]); \ p[0] = a[0]; p[1] = a[1]; \ a[0] ^= b[0]; a[1] ^= b[1]; \ + VARIANT1_2(p + 1); \ _b = _c; \ @@ -845,7 +908,7 @@ STATIC INLINE void aes_pseudo_round_xor(const uint8_t *in, uint8_t *out, const u } } -void cn_slow_hash(const void *data, size_t length, char *hash) +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) { RDATA_ALIGN16 uint8_t expandedKey[240]; RDATA_ALIGN16 uint8_t hp_state[MEMORY]; @@ -868,9 +931,15 @@ void cn_slow_hash(const void *data, size_t length, char *hash) /* CryptoNight Step 1: Use Keccak1600 to initialize the 'state' (and 'text') buffers from the data. */ - hash_process(&state.hs, data, length); + if (prehashed) { + memcpy(&state.hs, data, length); + } else { + hash_process(&state.hs, data, length); + } memcpy(text, state.init, INIT_SIZE_BYTE); + VARIANT1_INIT64(); + /* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill * the 2MB large random access buffer. */ @@ -1039,7 +1108,7 @@ STATIC INLINE void xor_blocks(uint8_t* a, const uint8_t* b) U64(a)[1] ^= U64(b)[1]; } -void cn_slow_hash(const void *data, size_t length, char *hash) +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) { uint8_t text[INIT_SIZE_BYTE]; uint8_t a[AES_BLOCK_SIZE]; @@ -1065,9 +1134,15 @@ void cn_slow_hash(const void *data, size_t length, char *hash) long_state = (uint8_t *)malloc(MEMORY); #endif - hash_process(&state.hs, data, length); + if (prehashed) { + memcpy(&state.hs, data, length); + } else { + hash_process(&state.hs, data, length); + } memcpy(text, state.init, INIT_SIZE_BYTE); + VARIANT1_INIT64(); + aes_ctx = (oaes_ctx *) oaes_alloc(); oaes_key_import_data(aes_ctx, state.hs.b, AES_KEY_SIZE); @@ -1097,6 +1172,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) xor_blocks(b, p); swap_blocks(b, p); swap_blocks(a, b); + VARIANT1_1(p); // Iteration 2 p = &long_state[state_index(a)]; @@ -1106,6 +1182,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) swap_blocks(b, p); xor_blocks(b, p); swap_blocks(a, b); + VARIANT1_2(U64(p) + 1); } memcpy(text, state.init, INIT_SIZE_BYTE); @@ -1200,6 +1277,15 @@ static void xor_blocks(uint8_t* a, const uint8_t* b) { } } +static void xor64(uint8_t* left, const uint8_t* right) +{ + size_t i; + for (i = 0; i < 8; ++i) + { + left[i] ^= right[i]; + } +} + #pragma pack(push, 1) union cn_slow_hash_state { union hash_state hs; @@ -1210,7 +1296,7 @@ union cn_slow_hash_state { }; #pragma pack(pop) -void cn_slow_hash(const void *data, size_t length, char *hash) { +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) { uint8_t long_state[MEMORY]; union cn_slow_hash_state state; uint8_t text[INIT_SIZE_BYTE]; @@ -1222,11 +1308,17 @@ void cn_slow_hash(const void *data, size_t length, char *hash) { uint8_t aes_key[AES_KEY_SIZE]; oaes_ctx *aes_ctx; - hash_process(&state.hs, data, length); + if (prehashed) { + memcpy(&state.hs, data, length); + } else { + hash_process(&state.hs, data, length); + } memcpy(text, state.init, INIT_SIZE_BYTE); memcpy(aes_key, state.hs.b, AES_KEY_SIZE); aes_ctx = (oaes_ctx *) oaes_alloc(); + VARIANT1_PORTABLE_INIT(); + oaes_key_import_data(aes_ctx, aes_key, AES_KEY_SIZE); for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) { for (j = 0; j < INIT_SIZE_BLK; j++) { @@ -1254,6 +1346,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) { copy_block(&long_state[j * AES_BLOCK_SIZE], c); assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE)); swap_blocks(a, b); + VARIANT1_1(&long_state[j * AES_BLOCK_SIZE]); /* Iteration 2 */ j = e2i(a, MEMORY / AES_BLOCK_SIZE); copy_block(c, &long_state[j * AES_BLOCK_SIZE]); @@ -1261,6 +1354,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) { sum_half_blocks(b, d); swap_blocks(b, c); xor_blocks(b, c); + VARIANT1_2(c + 8); copy_block(&long_state[j * AES_BLOCK_SIZE], c); assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE)); swap_blocks(a, b); diff --git a/src/Native/libcryptonote/crypto/tree-hash.c b/src/Native/libcryptonote/crypto/tree-hash.c index 88561308f..e6d6a267c 100644 --- a/src/Native/libcryptonote/crypto/tree-hash.c +++ b/src/Native/libcryptonote/crypto/tree-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -34,15 +34,15 @@ #include "hash-ops.h" -#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__MINGW32__) && !defined(_MSC_VER) +#ifdef _MSC_VER +#include +#elif !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) #include #else #include #endif -#if defined(_MSC_VER) -#define alloca(x) _alloca(x) -#endif/*** +/*** * Round to power of two, for count>=3 and for count being not too large (as reasonable for tree hash calculations) */ size_t tree_hash_cnt(size_t count) { diff --git a/src/Native/libcryptonote/cryptonote_basic/CMakeLists.txt b/src/Native/libcryptonote/cryptonote_basic/CMakeLists.txt new file mode 100644 index 000000000..d50a9df67 --- /dev/null +++ b/src/Native/libcryptonote/cryptonote_basic/CMakeLists.txt @@ -0,0 +1,79 @@ +# Copyright (c) 2014-2018, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if(APPLE) + find_library(IOKIT_LIBRARY IOKit) + mark_as_advanced(IOKIT_LIBRARY) + list(APPEND EXTRA_LIBRARIES ${IOKIT_LIBRARY}) +endif() + +set(cryptonote_basic_sources + account.cpp + cryptonote_basic_impl.cpp + cryptonote_format_utils.cpp + difficulty.cpp + hardfork.cpp + miner.cpp) + +set(cryptonote_basic_headers) + +set(cryptonote_basic_private_headers + account.h + account_boost_serialization.h + connection_context.h + cryptonote_basic.h + cryptonote_basic_impl.h + cryptonote_boost_serialization.h + cryptonote_format_utils.h + cryptonote_stat_info.h + difficulty.h + hardfork.h + miner.h + tx_extra.h + verification_context.h) + +monero_private_headers(cryptonote_basic + ${cryptonote_basic_private_headers}) +monero_add_library(cryptonote_basic + ${cryptonote_basic_sources} + ${cryptonote_basic_headers} + ${cryptonote_basic_private_headers}) +target_link_libraries(cryptonote_basic + PUBLIC + common + cncrypto + checkpoints + device + ${Boost_DATE_TIME_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_SERIALIZATION_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + PRIVATE + ${EXTRA_LIBRARIES}) diff --git a/src/Native/libcryptonote/cryptonote_basic/account.cpp b/src/Native/libcryptonote/cryptonote_basic/account.cpp index dd875402f..bab991d19 100644 --- a/src/Native/libcryptonote/cryptonote_basic/account.cpp +++ b/src/Native/libcryptonote/cryptonote_basic/account.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -50,6 +50,17 @@ DISABLE_VS_WARNINGS(4244 4345) namespace cryptonote { + + //----------------------------------------------------------------- + hw::device& account_keys::get_device() const { + return *m_device; + } + //----------------------------------------------------------------- + void account_keys::set_device( hw::device &hwdev) { + m_device = &hwdev; + MCDEBUG("device", "account_keys::set_device device type: "< &multisig_keys) + { + m_keys.m_account_address.m_spend_public_key = spend_public_key; + m_keys.m_view_secret_key = view_secret_key; + m_keys.m_spend_secret_key = spend_secret_key; + m_keys.m_multisig_keys = multisig_keys; + return crypto::secret_key_to_public_key(view_secret_key, m_keys.m_account_address.m_view_public_key); + } + //----------------------------------------------------------------- + void account_base::finalize_multisig(const crypto::public_key &spend_public_key) + { + m_keys.m_account_address.m_spend_public_key = spend_public_key; + } + //----------------------------------------------------------------- const account_keys& account_base::get_keys() const { return m_keys; } //----------------------------------------------------------------- - std::string account_base::get_public_address_str(bool testnet) const + std::string account_base::get_public_address_str(network_type nettype) const { //TODO: change this code into base 58 - return get_account_address_as_str(testnet, m_keys.m_account_address); + return get_account_address_as_str(nettype, false, m_keys.m_account_address); } //----------------------------------------------------------------- - std::string account_base::get_public_integrated_address_str(const crypto::hash8 &payment_id, bool testnet) const + std::string account_base::get_public_integrated_address_str(const crypto::hash8 &payment_id, network_type nettype) const { //TODO: change this code into base 58 - return get_account_integrated_address_as_str(testnet, m_keys.m_account_address, payment_id); + return get_account_integrated_address_as_str(nettype, m_keys.m_account_address, payment_id); } //----------------------------------------------------------------- } diff --git a/src/Native/libcryptonote/cryptonote_basic/account.h b/src/Native/libcryptonote/cryptonote_basic/account.h index e0d5447a2..df4344730 100644 --- a/src/Native/libcryptonote/cryptonote_basic/account.h +++ b/src/Native/libcryptonote/cryptonote_basic/account.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -42,12 +42,20 @@ namespace cryptonote account_public_address m_account_address; crypto::secret_key m_spend_secret_key; crypto::secret_key m_view_secret_key; + std::vector m_multisig_keys; + hw::device *m_device = &hw::get_device("default"); BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(m_account_address) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_spend_secret_key) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_view_secret_key) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys) END_KV_SERIALIZE_MAP() + + account_keys& operator=(account_keys const&) = default; + + hw::device& get_device() const ; + void set_device( hw::device &hwdev) ; }; /************************************************************************/ @@ -58,11 +66,17 @@ namespace cryptonote public: account_base(); crypto::secret_key generate(const crypto::secret_key& recovery_key = crypto::secret_key(), bool recover = false, bool two_random = false); + void create_from_device(const std::string &device_name) ; void create_from_keys(const cryptonote::account_public_address& address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey); void create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey); + bool make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector &multisig_keys); + void finalize_multisig(const crypto::public_key &spend_public_key); const account_keys& get_keys() const; - std::string get_public_address_str(bool testnet) const; - std::string get_public_integrated_address_str(const crypto::hash8 &payment_id, bool testnet) const; + std::string get_public_address_str(uint8_t nettype) const; + std::string get_public_integrated_address_str(const crypto::hash8 &payment_id, uint8_t nettype) const; + + hw::device& get_device() const {return m_keys.get_device();} + void set_device( hw::device &hwdev) {m_keys.set_device(hwdev);} uint64_t get_createtime() const { return m_creation_timestamp; } void set_createtime(uint64_t val) { m_creation_timestamp = val; } @@ -71,6 +85,7 @@ namespace cryptonote bool store(const std::string& file_path); void forget_spend_key(); + const std::vector &get_multisig_keys() const { return m_keys.m_multisig_keys; } template inline void serialize(t_archive &a, const unsigned int /*ver*/) diff --git a/src/Native/libcryptonote/cryptonote_basic/account_boost_serialization.h b/src/Native/libcryptonote/cryptonote_basic/account_boost_serialization.h index d2f541638..7379d787f 100644 --- a/src/Native/libcryptonote/cryptonote_basic/account_boost_serialization.h +++ b/src/Native/libcryptonote/cryptonote_basic/account_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/cryptonote_basic/blobdatatype.h b/src/Native/libcryptonote/cryptonote_basic/blobdatatype.h new file mode 100644 index 000000000..7d6ff0187 --- /dev/null +++ b/src/Native/libcryptonote/cryptonote_basic/blobdatatype.h @@ -0,0 +1,36 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +namespace cryptonote +{ + typedef std::string blobdata; +} diff --git a/src/Native/libcryptonote/cryptonote_basic/connection_context.h b/src/Native/libcryptonote/cryptonote_basic/connection_context.h index 3283543e2..5cd1709ab 100644 --- a/src/Native/libcryptonote/cryptonote_basic/connection_context.h +++ b/src/Native/libcryptonote/cryptonote_basic/connection_context.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -40,7 +40,7 @@ namespace cryptonote struct cryptonote_connection_context: public epee::net_utils::connection_context_base { cryptonote_connection_context(): m_state(state_before_handshake), m_remote_blockchain_height(0), m_last_response_height(0), - m_last_known_hash(cryptonote::null_hash) {} + m_last_request_time(boost::posix_time::microsec_clock::universal_time()), m_callback_request_count(0), m_last_known_hash(crypto::null_hash) {} enum state { diff --git a/src/Native/libcryptonote/cryptonote_basic/cryptonote_basic.h b/src/Native/libcryptonote/cryptonote_basic/cryptonote_basic.h index 873bf5374..c5a2ea6f1 100644 --- a/src/Native/libcryptonote/cryptonote_basic/cryptonote_basic.h +++ b/src/Native/libcryptonote/cryptonote_basic/cryptonote_basic.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -36,7 +36,6 @@ #include // memcmp #include #include -#include "serialization/serialization.h" #include "serialization/variant.h" #include "serialization/vector.h" #include "serialization/binary_archive.h" @@ -50,14 +49,10 @@ #include "misc_language.h" #include "tx_extra.h" #include "ringct/rctTypes.h" +#include "device/device.hpp" namespace cryptonote { - - const static crypto::hash null_hash = AUTO_VAL_INIT(null_hash); - const static crypto::hash8 null_hash8 = AUTO_VAL_INIT(null_hash8); - const static crypto::public_key null_pkey = AUTO_VAL_INIT(null_pkey); - typedef std::vector ring_signature; @@ -264,7 +259,7 @@ namespace cryptonote ar.tag("rctsig_prunable"); ar.begin_object(); r = rct_signatures.p.serialize_rctsig_prunable(ar, rct_signatures.type, vin.size(), vout.size(), - vin[0].type() == typeid(txin_to_key) ? boost::get(vin[0]).key_offsets.size() - 1 : 0); + vin.size() > 0 && vin[0].type() == typeid(txin_to_key) ? boost::get(vin[0]).key_offsets.size() - 1 : 0); if (!r || !ar.stream().good()) return false; ar.end_object(); } @@ -416,33 +411,45 @@ namespace cryptonote KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_spend_public_key) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_view_public_key) END_KV_SERIALIZE_MAP() - }; - struct integrated_address - { - account_public_address adr; - crypto::hash8 payment_id; - - BEGIN_SERIALIZE_OBJECT() - FIELD(adr) - FIELD(payment_id) - END_SERIALIZE() - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(adr) - KV_SERIALIZE(payment_id) - END_KV_SERIALIZE_MAP() + bool operator==(const account_public_address& rhs) const + { + return m_spend_public_key == rhs.m_spend_public_key && + m_view_public_key == rhs.m_view_public_key; + } + + bool operator!=(const account_public_address& rhs) const + { + return !(*this == rhs); + } }; + struct integrated_address + { + account_public_address adr; + crypto::hash8 payment_id; + + BEGIN_SERIALIZE_OBJECT() + FIELD(adr) + FIELD(payment_id) + END_SERIALIZE() + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(adr) + KV_SERIALIZE(payment_id) + END_KV_SERIALIZE_MAP() + }; + + struct keypair { crypto::public_key pub; crypto::secret_key sec; - static inline keypair generate() + static inline keypair generate(hw::device &hwdev) { keypair k; - generate_keys(k.pub, k.sec); + hwdev.generate_keys(k.pub, k.sec); return k; } }; @@ -450,6 +457,21 @@ namespace cryptonote } +namespace std { + template <> + struct hash + { + std::size_t operator()(const cryptonote::account_public_address& addr) const + { + // https://stackoverflow.com/a/17017281 + size_t res = 17; + res = res * 31 + hash()(addr.m_spend_public_key); + res = res * 31 + hash()(addr.m_view_public_key); + return res; + } + }; +} + BLOB_SERIALIZER(cryptonote::txout_to_key); BLOB_SERIALIZER(cryptonote::txout_to_scripthash); diff --git a/src/Native/libcryptonote/cryptonote_basic/cryptonote_basic_impl.cpp b/src/Native/libcryptonote/cryptonote_basic/cryptonote_basic_impl.cpp index a59f96956..9a9362466 100644 --- a/src/Native/libcryptonote/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/Native/libcryptonote/cryptonote_basic/cryptonote_basic_impl.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -34,7 +34,7 @@ using namespace epee; #include "cryptonote_basic_impl.h" #include "string_tools.h" #include "serialization/binary_utils.h" -#include "serialization/vector.h" +#include "serialization/container.h" #include "cryptonote_format_utils.h" #include "cryptonote_config.h" #include "misc_language.h" @@ -157,24 +157,26 @@ namespace cryptonote { } //----------------------------------------------------------------------- std::string get_account_address_as_str( - bool testnet + uint8_t nettype + , bool subaddress , account_public_address const & adr ) { - uint64_t address_prefix = testnet ? - config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; + uint64_t address_prefix = nettype == TESTNET ? + (subaddress ? config::testnet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX) : nettype == STAGENET ? + (subaddress ? config::stagenet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::stagenet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX) : + (subaddress ? config::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX); return tools::base58::encode_addr(address_prefix, t_serializable_object_to_blob(adr)); } //----------------------------------------------------------------------- std::string get_account_integrated_address_as_str( - bool testnet + uint8_t nettype , account_public_address const & adr , crypto::hash8 const & payment_id ) { - uint64_t integrated_address_prefix = testnet ? - config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; + uint64_t integrated_address_prefix = nettype == TESTNET ? config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : nettype == STAGENET ? config::stagenet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; integrated_address iadr = { adr, payment_id @@ -193,18 +195,21 @@ namespace cryptonote { return true; } //----------------------------------------------------------------------- - bool get_account_integrated_address_from_str( - account_public_address& adr - , bool& has_payment_id - , crypto::hash8& payment_id - , bool testnet + bool get_account_address_from_str( + address_parse_info& info + , uint8_t nettype , std::string const & str ) { - uint64_t address_prefix = testnet ? - config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; - uint64_t integrated_address_prefix = testnet ? - config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; + uint64_t address_prefix = nettype == TESTNET ? + config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX : nettype == STAGENET ? + config::stagenet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; + uint64_t integrated_address_prefix = nettype == TESTNET ? + config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : nettype == STAGENET ? + config::stagenet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; + uint64_t subaddress_prefix = nettype == TESTNET ? + config::testnet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : nettype == STAGENET ? + config::stagenet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX; if (2 * sizeof(public_address_outer_blob) != str.size()) { @@ -218,18 +223,27 @@ namespace cryptonote { if (integrated_address_prefix == prefix) { - has_payment_id = true; + info.is_subaddress = false; + info.has_payment_id = true; } else if (address_prefix == prefix) { - has_payment_id = false; + info.is_subaddress = false; + info.has_payment_id = false; + } + else if (subaddress_prefix == prefix) + { + info.is_subaddress = true; + info.has_payment_id = false; } else { - LOG_PRINT_L1("Wrong address prefix: " << prefix << ", expected " << address_prefix << " or " << integrated_address_prefix); + LOG_PRINT_L1("Wrong address prefix: " << prefix << ", expected " << address_prefix + << " or " << integrated_address_prefix + << " or " << subaddress_prefix); return false; } - if (has_payment_id) + if (info.has_payment_id) { integrated_address iadr; if (!::serialization::parse_binary(data, iadr)) @@ -237,19 +251,19 @@ namespace cryptonote { LOG_PRINT_L1("Account public address keys can't be parsed"); return false; } - adr = iadr.adr; - payment_id = iadr.payment_id; + info.address = iadr.adr; + info.payment_id = iadr.payment_id; } else { - if (!::serialization::parse_binary(data, adr)) + if (!::serialization::parse_binary(data, info.address)) { LOG_PRINT_L1("Account public address keys can't be parsed"); return false; } } - if (!crypto::check_key(adr.m_spend_public_key) || !crypto::check_key(adr.m_view_public_key)) + if (!crypto::check_key(info.address.m_spend_public_key) || !crypto::check_key(info.address.m_view_public_key)) { LOG_PRINT_L1("Failed to validate address keys"); return false; @@ -284,51 +298,27 @@ namespace cryptonote { } //we success - adr = blob.m_address; - has_payment_id = false; + info.address = blob.m_address; + info.is_subaddress = false; + info.has_payment_id = false; } return true; } - //----------------------------------------------------------------------- - bool get_account_address_from_str( - account_public_address& adr - , bool testnet - , std::string const & str - ) - { - bool has_payment_id; - crypto::hash8 payment_id; - return get_account_integrated_address_from_str(adr, has_payment_id, payment_id, testnet, str); - } //-------------------------------------------------------------------------------- bool get_account_address_from_str_or_url( - cryptonote::account_public_address& address - , bool& has_payment_id - , crypto::hash8& payment_id - , bool testnet + address_parse_info& info + , uint8_t nettype , const std::string& str_or_url , std::function&, bool)> dns_confirm ) { - if (get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, str_or_url)) + if (get_account_address_from_str(info, nettype, str_or_url)) return true; bool dnssec_valid; std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(str_or_url, dnssec_valid, dns_confirm); return !address_str.empty() && - get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, address_str); - } - //-------------------------------------------------------------------------------- - bool get_account_address_from_str_or_url( - cryptonote::account_public_address& address - , bool testnet - , const std::string& str_or_url - , std::function&, bool)> dns_confirm - ) - { - bool has_payment_id; - crypto::hash8 payment_id; - return get_account_address_from_str_or_url(address, has_payment_id, payment_id, testnet, str_or_url, dns_confirm); + get_account_address_from_str(info, nettype, address_str); } //-------------------------------------------------------------------------------- bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b) { diff --git a/src/Native/libcryptonote/cryptonote_basic/cryptonote_basic_impl.h b/src/Native/libcryptonote/cryptonote_basic/cryptonote_basic_impl.h index 7a2259b32..4ec2b403d 100644 --- a/src/Native/libcryptonote/cryptonote_basic/cryptonote_basic_impl.h +++ b/src/Native/libcryptonote/cryptonote_basic/cryptonote_basic_impl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -33,8 +33,6 @@ #include "cryptonote_basic.h" #include "crypto/crypto.h" #include "crypto/hash.h" -#include "hex.h" -#include "span.h" namespace cryptonote { @@ -77,6 +75,14 @@ namespace cryptonote { } } + struct address_parse_info + { + account_public_address address; + bool is_subaddress; + bool has_payment_id; + crypto::hash8 payment_id; + }; + /************************************************************************/ /* Cryptonote helper functions */ /************************************************************************/ @@ -88,42 +94,26 @@ namespace cryptonote { uint8_t get_account_integrated_address_checksum(const public_integrated_address_outer_blob& bl); std::string get_account_address_as_str( - bool testnet + uint8_t nettype + , bool subaddress , const account_public_address& adr ); std::string get_account_integrated_address_as_str( - bool testnet + uint8_t nettype , const account_public_address& adr , const crypto::hash8& payment_id ); - bool get_account_integrated_address_from_str( - account_public_address& adr - , bool& has_payment_id - , crypto::hash8& payment_id - , bool testnet - , const std::string& str - ); - bool get_account_address_from_str( - account_public_address& adr - , bool testnet + address_parse_info& info + , uint8_t nettype , const std::string& str ); bool get_account_address_from_str_or_url( - cryptonote::account_public_address& address - , bool& has_payment_id - , crypto::hash8& payment_id - , bool testnet - , const std::string& str_or_url - , std::function&, bool)> dns_confirm = return_first_address - ); - - bool get_account_address_from_str_or_url( - cryptonote::account_public_address& address - , bool testnet + address_parse_info& info + , uint8_t nettype , const std::string& str_or_url , std::function&, bool)> dns_confirm = return_first_address ); @@ -136,26 +126,3 @@ namespace cryptonote { bool parse_hash256(const std::string str_hash, crypto::hash& hash); -namespace crypto { - inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { - epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; - } - inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { - epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; - } - inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { - epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; - } - inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { - epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; - } - inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { - epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; - } - inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { - epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; - } - inline std::ostream &operator <<(std::ostream &o, const crypto::hash8 &v) { - epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; - } -} diff --git a/src/Native/libcryptonote/cryptonote_basic/cryptonote_boost_serialization.h b/src/Native/libcryptonote/cryptonote_basic/cryptonote_boost_serialization.h index 6e4ac9b72..143133163 100644 --- a/src/Native/libcryptonote/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/Native/libcryptonote/cryptonote_basic/cryptonote_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -83,6 +83,11 @@ namespace boost { a & reinterpret_cast(x); } + template + inline void serialize(Archive &a, crypto::hash8 &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast(x); + } template inline void serialize(Archive &a, cryptonote::txout_to_script &x, const boost::serialization::version_type ver) @@ -206,6 +211,23 @@ namespace boost a & x.Ci; } + template + inline void serialize(Archive &a, rct::Bulletproof &x, const boost::serialization::version_type ver) + { + a & x.V; + a & x.A; + a & x.S; + a & x.T1; + a & x.T2; + a & x.taux; + a & x.mu; + a & x.L; + a & x.R; + a & x.a; + a & x.b; + a & x.t; + } + template inline void serialize(Archive &a, rct::boroSig &x, const boost::serialization::version_type ver) { @@ -230,6 +252,21 @@ namespace boost // a & x.senderPk; // not serialized, as we do not use it in monero currently } + template + inline void serialize(Archive &a, rct::multisig_kLRki &x, const boost::serialization::version_type ver) + { + a & x.k; + a & x.L; + a & x.R; + a & x.ki; + } + + template + inline void serialize(Archive &a, rct::multisig_out &x, const boost::serialization::version_type ver) + { + a & x.c; + } + template inline typename std::enable_if::type serializeOutPk(Archive &a, rct::ctkeyV &outPk_, const boost::serialization::version_type ver) { @@ -258,11 +295,11 @@ namespace boost a & x.type; if (x.type == rct::RCTTypeNull) return; - if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple) + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeSimpleBulletproof) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets - if (x.type == rct::RCTTypeSimple) + if (x.type == rct::RCTTypeSimple) // moved to prunable with bulletproofs a & x.pseudoOuts; a & x.ecdhInfo; serializeOutPk(a, x.outPk, ver); @@ -273,7 +310,11 @@ namespace boost inline void serialize(Archive &a, rct::rctSigPrunable &x, const boost::serialization::version_type ver) { a & x.rangeSigs; + if (x.rangeSigs.empty()) + a & x.bulletproofs; a & x.MGs; + if (x.rangeSigs.empty()) + a & x.pseudoOuts; } template @@ -282,7 +323,7 @@ namespace boost a & x.type; if (x.type == rct::RCTTypeNull) return; - if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple) + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeSimpleBulletproof) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets @@ -293,7 +334,11 @@ namespace boost a & x.txnFee; //-------------- a & x.p.rangeSigs; + if (x.p.rangeSigs.empty()) + a & x.p.bulletproofs; a & x.p.MGs; + if (x.type == rct::RCTTypeSimpleBulletproof) + a & x.p.pseudoOuts; } } } diff --git a/src/Native/libcryptonote/cryptonote_basic/cryptonote_format_utils.cpp b/src/Native/libcryptonote/cryptonote_basic/cryptonote_format_utils.cpp index 745dfb72e..934767960 100644 --- a/src/Native/libcryptonote/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/Native/libcryptonote/cryptonote_basic/cryptonote_format_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -32,8 +32,11 @@ using namespace epee; #include +#include +#include "wipeable_string.h" +#include "string_tools.h" +#include "serialization/string.h" #include "cryptonote_format_utils.h" -#include "cryptonote_config.h" #include "crypto/crypto.h" #include "crypto/hash.h" #include "ringct/rctSigs.h" @@ -45,6 +48,8 @@ using namespace epee; // #define ENABLE_HASH_CASH_INTEGRITY_CHECK +using namespace crypto; + static const uint64_t valid_decomposed_outputs[] = { (uint64_t)1, (uint64_t)2, (uint64_t)3, (uint64_t)4, (uint64_t)5, (uint64_t)6, (uint64_t)7, (uint64_t)8, (uint64_t)9, // 1 piconero (uint64_t)10, (uint64_t)20, (uint64_t)30, (uint64_t)40, (uint64_t)50, (uint64_t)60, (uint64_t)70, (uint64_t)80, (uint64_t)90, @@ -75,6 +80,31 @@ static std::atomic tx_hashes_cached_count(0); static std::atomic block_hashes_calculated_count(0); static std::atomic block_hashes_cached_count(0); +#define CHECK_AND_ASSERT_THROW_MES_L1(expr, message) {if(!(expr)) {MWARNING(message); throw std::runtime_error(message);}} + +namespace cryptonote +{ + static inline unsigned char *operator &(ec_point &point) { + return &reinterpret_cast(point); + } + static inline const unsigned char *operator &(const ec_point &point) { + return &reinterpret_cast(point); + } + + // a copy of rct::addKeys, since we can't link to libringct to avoid circular dependencies + static void add_public_key(crypto::public_key &AB, const crypto::public_key &A, const crypto::public_key &B) { + ge_p3 B2, A2; + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&B2, &B) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A2, &A) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); + ge_cached tmp2; + ge_p3_to_cached(&tmp2, &B2); + ge_p1p1 tmp3; + ge_add(&tmp3, &A2, &tmp2); + ge_p1p1_to_p3(&A2, &tmp3); + ge_p3_tobytes(&AB, &A2); + } +} + namespace cryptonote { //--------------------------------------------------------------- @@ -129,18 +159,58 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki) + bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev) { - crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation); - bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation); - CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")"); + if (ack.m_spend_secret_key == crypto::null_skey) + { + // for watch-only wallet, simply copy the known output pubkey + in_ephemeral.pub = out_key; + in_ephemeral.sec = crypto::null_skey; + } + else + { + // derive secret key with subaddress - step 1: original CN derivation + crypto::secret_key scalar_step1; + hwdev.derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, scalar_step1); // computes Hs(a*R || idx) + b + + // step 2: add Hs(a || index_major || index_minor) + crypto::secret_key subaddr_sk; + crypto::secret_key scalar_step2; + if (received_index.is_zero()) + { + scalar_step2 = scalar_step1; // treat index=(0,0) as a special case representing the main address + } + else + { + subaddr_sk = hwdev.get_subaddress_secret_key(ack.m_view_secret_key, received_index); + hwdev.sc_secret_add(scalar_step2, scalar_step1,subaddr_sk); + } - r = crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub); - CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to derive_public_key(" << recv_derivation << ", " << real_output_index << ", " << ack.m_account_address.m_spend_public_key << ")"); + in_ephemeral.sec = scalar_step2; + + if (ack.m_multisig_keys.empty()) + { + // when not in multisig, we know the full spend secret key, so the output pubkey can be obtained by scalarmultBase + CHECK_AND_ASSERT_MES(hwdev.secret_key_to_public_key(in_ephemeral.sec, in_ephemeral.pub), false, "Failed to derive public key"); + } + else + { + // when in multisig, we only know the partial spend secret key. but we do know the full spend public key, so the output pubkey can be obtained by using the standard CN key derivation + CHECK_AND_ASSERT_MES(hwdev.derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub), false, "Failed to derive public key"); + // and don't forget to add the contribution from the subaddress part + if (!received_index.is_zero()) + { + crypto::public_key subaddr_pk; + CHECK_AND_ASSERT_MES(hwdev.secret_key_to_public_key(subaddr_sk, subaddr_pk), false, "Failed to derive public key"); + add_public_key(in_ephemeral.pub, in_ephemeral.pub, subaddr_pk); + } + } - crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, in_ephemeral.sec); + CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_key, + false, "key image helper precomp: given output pubkey doesn't match the derived one"); + } - crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki); + hwdev.generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki); return true; } //--------------------------------------------------------------- @@ -271,9 +341,53 @@ namespace cryptonote //--------------------------------------------------------------- bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key) { - tx.extra.resize(tx.extra.size() + 1 + sizeof(crypto::public_key)); - tx.extra[tx.extra.size() - 1 - sizeof(crypto::public_key)] = TX_EXTRA_TAG_PUBKEY; - *reinterpret_cast(&tx.extra[tx.extra.size() - sizeof(crypto::public_key)]) = tx_pub_key; + return add_tx_pub_key_to_extra(tx.extra, tx_pub_key); + } + //--------------------------------------------------------------- + bool add_tx_pub_key_to_extra(transaction_prefix& tx, const crypto::public_key& tx_pub_key) + { + return add_tx_pub_key_to_extra(tx.extra, tx_pub_key); + } + //--------------------------------------------------------------- + bool add_tx_pub_key_to_extra(std::vector& tx_extra, const crypto::public_key& tx_pub_key) + { + tx_extra.resize(tx_extra.size() + 1 + sizeof(crypto::public_key)); + tx_extra[tx_extra.size() - 1 - sizeof(crypto::public_key)] = TX_EXTRA_TAG_PUBKEY; + *reinterpret_cast(&tx_extra[tx_extra.size() - sizeof(crypto::public_key)]) = tx_pub_key; + return true; + } + //--------------------------------------------------------------- + std::vector get_additional_tx_pub_keys_from_extra(const std::vector& tx_extra) + { + // parse + std::vector tx_extra_fields; + parse_tx_extra(tx_extra, tx_extra_fields); + // find corresponding field + tx_extra_additional_pub_keys additional_pub_keys; + if(!find_tx_extra_field_by_type(tx_extra_fields, additional_pub_keys)) + return {}; + return additional_pub_keys.data; + } + //--------------------------------------------------------------- + std::vector get_additional_tx_pub_keys_from_extra(const transaction_prefix& tx) + { + return get_additional_tx_pub_keys_from_extra(tx.extra); + } + //--------------------------------------------------------------- + bool add_additional_tx_pub_keys_to_extra(std::vector& tx_extra, const std::vector& additional_pub_keys) + { + // convert to variant + tx_extra_field field = tx_extra_additional_pub_keys{ additional_pub_keys }; + // serialize + std::ostringstream oss; + binary_archive ar(oss); + bool r = ::do_serialize(ar, field); + CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to serialize tx extra additional tx pub keys"); + // append + std::string tx_extra_str = oss.str(); + size_t pos = tx_extra.size(); + tx_extra.resize(tx_extra.size() + tx_extra_str.size()); + memcpy(&tx_extra[pos], tx_extra_str.data(), tx_extra_str.size()); return true; } //--------------------------------------------------------------- @@ -360,30 +474,6 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) - { - crypto::key_derivation derivation; - crypto::hash hash; - char data[33]; /* A hash, and an extra byte */ - - if (!generate_key_derivation(public_key, secret_key, derivation)) - return false; - - memcpy(data, &derivation, 32); - data[32] = ENCRYPTED_PAYMENT_ID_TAIL; - cn_fast_hash(data, 33, hash); - - for (size_t b = 0; b < 8; ++b) - payment_id.data[b] ^= hash.data[b]; - - return true; - } - bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) - { - // Encryption and decryption are the same operation (xor with a key) - return encrypt_payment_id(payment_id, public_key, secret_key); - } - //--------------------------------------------------------------- bool get_inputs_money_amount(const transaction& tx, uint64_t& money) { money = 0; @@ -479,48 +569,27 @@ namespace cryptonote res.insert(8, "...."); return res; } - //--------------------------------------------------------------- - bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index) - { - crypto::key_derivation derivation; - generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); - crypto::public_key pk; - derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk); - return pk == out_key.key; - } - //--------------------------------------------------------------- - bool is_out_to_acc_precomp(const crypto::public_key& spend_public_key, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index) - { - crypto::public_key pk; - derive_public_key(derivation, output_index, spend_public_key, pk); - return pk == out_key.key; - } - //--------------------------------------------------------------- - bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector& outs, uint64_t& money_transfered) - { - crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); - if(null_pkey == tx_pub_key) - return false; - return lookup_acc_outs(acc, tx, tx_pub_key, outs, money_transfered); - } - //--------------------------------------------------------------- - bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered) + boost::optional is_out_to_acc_precomp(const std::unordered_map& subaddresses, const crypto::public_key& out_key, const crypto::key_derivation& derivation, const std::vector& additional_derivations, size_t output_index, hw::device &hwdev) { - money_transfered = 0; - size_t i = 0; - for(const tx_out& o: tx.vout) + // try the shared tx pubkey + crypto::public_key subaddress_spendkey; + hwdev.derive_subaddress_public_key(out_key, derivation, output_index, subaddress_spendkey); + auto found = subaddresses.find(subaddress_spendkey); + if (found != subaddresses.end()) + return subaddress_receive_info{ found->second, derivation }; + // try additional tx pubkeys if available + if (!additional_derivations.empty()) { - CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "wrong type id in transaction out" ); - if(is_out_to_acc(acc, boost::get(o.target), tx_pub_key, i)) - { - outs.push_back(i); - money_transfered += o.amount; - } - i++; + CHECK_AND_ASSERT_MES(output_index < additional_derivations.size(), boost::none, "wrong number of additional derivations"); + hwdev.derive_subaddress_public_key(out_key, additional_derivations[output_index], output_index, subaddress_spendkey); + found = subaddresses.find(subaddress_spendkey); + if (found != subaddresses.end()) + return subaddress_receive_info{ found->second, additional_derivations[output_index] }; } - return true; + return boost::none; } //--------------------------------------------------------------- + //--------------------------------------------------------------- void get_blob_hash(const blobdata& blob, crypto::hash& res) { cn_fast_hash(blob.data(), blob.size(), res); @@ -632,7 +701,7 @@ namespace cryptonote // prunable rct if (t.rct_signatures.type == rct::RCTTypeNull) { - hashes[2] = cryptonote::null_hash; + hashes[2] = crypto::null_hash; } else { @@ -768,7 +837,8 @@ namespace cryptonote return true; } blobdata bd = get_block_hashing_blob(b); - crypto::cn_slow_hash(bd.data(), bd.size(), res); + const int cn_variant = b.major_version >= 7 ? b.major_version - 6 : 0; + crypto::cn_slow_hash(bd.data(), bd.size(), res, cn_variant); return true; } //--------------------------------------------------------------- @@ -869,4 +939,20 @@ namespace cryptonote block_hashes_calculated = block_hashes_calculated_count; block_hashes_cached = block_hashes_cached_count; } + //--------------------------------------------------------------- + crypto::secret_key encrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase) + { + crypto::hash hash; + crypto::cn_slow_hash(passphrase.data(), passphrase.size(), hash); + sc_add((unsigned char*)key.data, (const unsigned char*)key.data, (const unsigned char*)hash.data); + return key; + } + //--------------------------------------------------------------- + crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase) + { + crypto::hash hash; + crypto::cn_slow_hash(passphrase.data(), passphrase.size(), hash); + sc_sub((unsigned char*)key.data, (const unsigned char*)key.data, (const unsigned char*)hash.data); + return key; + } } diff --git a/src/Native/libcryptonote/cryptonote_basic/cryptonote_format_utils.h b/src/Native/libcryptonote/cryptonote_basic/cryptonote_format_utils.h index d8ccf8eec..79466e9c4 100644 --- a/src/Native/libcryptonote/cryptonote_basic/cryptonote_format_utils.h +++ b/src/Native/libcryptonote/cryptonote_basic/cryptonote_format_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -29,12 +29,19 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #pragma once -#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "blobdatatype.h" #include "cryptonote_basic_impl.h" #include "account.h" +#include "subaddress_index.h" #include "include_base_utils.h" #include "crypto/crypto.h" #include "crypto/hash.h" +#include + +namespace epee +{ + class wipeable_string; +} namespace cryptonote { @@ -44,8 +51,6 @@ namespace cryptonote bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash); bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx); bool parse_and_validate_tx_base_from_blob(const blobdata& tx_blob, transaction& tx); - bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key); - bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key); template bool find_tx_extra_field_by_type(const std::vector& tx_extra_fields, T& field, size_t index = 0) @@ -63,19 +68,30 @@ namespace cryptonote crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx, size_t pk_index = 0); crypto::public_key get_tx_pub_key_from_extra(const transaction& tx, size_t pk_index = 0); bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key); + bool add_tx_pub_key_to_extra(transaction_prefix& tx, const crypto::public_key& tx_pub_key); + bool add_tx_pub_key_to_extra(std::vector& tx_extra, const crypto::public_key& tx_pub_key); + std::vector get_additional_tx_pub_keys_from_extra(const std::vector& tx_extra); + std::vector get_additional_tx_pub_keys_from_extra(const transaction_prefix& tx); + bool add_additional_tx_pub_keys_to_extra(std::vector& tx_extra, const std::vector& additional_pub_keys); bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce); bool remove_field_from_tx_extra(std::vector& tx_extra, const std::type_info &type); void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id); void set_encrypted_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash8& payment_id); bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id); bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash8& payment_id); - bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index); - bool is_out_to_acc_precomp(const crypto::public_key& spend_public_key, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index); - bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered); + bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, const std::vector& additional_tx_public_keys, size_t output_index); + struct subaddress_receive_info + { + subaddress_index index; + crypto::key_derivation derivation; + }; + boost::optional is_out_to_acc_precomp(const std::unordered_map& subaddresses, const crypto::public_key& out_key, const crypto::key_derivation& derivation, const std::vector& additional_derivations, size_t output_index, hw::device &hwdev); + bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, const std::vector& additional_tx_public_keys, std::vector& outs, uint64_t& money_transfered); bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector& outs, uint64_t& money_transfered); bool get_tx_fee(const transaction& tx, uint64_t & fee); uint64_t get_tx_fee(const transaction& tx); - bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki); + bool generate_key_image_helper(const account_keys& ack, const std::unordered_map& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev); + bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev); void get_blob_hash(const blobdata& blob, crypto::hash& res); crypto::hash get_blob_hash(const blobdata& blob); std::string short_hash_str(const crypto::hash& h); @@ -212,8 +228,9 @@ namespace cryptonote bool is_valid_decomposed_amount(uint64_t amount); void get_hash_stats(uint64_t &tx_hashes_calculated, uint64_t &tx_hashes_cached, uint64_t &block_hashes_calculated, uint64_t & block_hashes_cached); + crypto::secret_key encrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase); + crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase); #define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \ CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \ specific_type& variable_name = boost::get(variant_var); - } diff --git a/src/Native/libcryptonote/cryptonote_basic/cryptonote_stat_info.h b/src/Native/libcryptonote/cryptonote_basic/cryptonote_stat_info.h index 7ebf86878..c0be2144e 100644 --- a/src/Native/libcryptonote/cryptonote_basic/cryptonote_stat_info.h +++ b/src/Native/libcryptonote/cryptonote_basic/cryptonote_stat_info.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/cryptonote_basic/difficulty.cpp b/src/Native/libcryptonote/cryptonote_basic/difficulty.cpp index 863aa4359..cb2a00a12 100644 --- a/src/Native/libcryptonote/cryptonote_basic/difficulty.cpp +++ b/src/Native/libcryptonote/cryptonote_basic/difficulty.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/cryptonote_basic/difficulty.h b/src/Native/libcryptonote/cryptonote_basic/difficulty.h index aeb1c030d..b06538467 100644 --- a/src/Native/libcryptonote/cryptonote_basic/difficulty.h +++ b/src/Native/libcryptonote/cryptonote_basic/difficulty.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/cryptonote_basic/hardfork.cpp b/src/Native/libcryptonote/cryptonote_basic/hardfork.cpp index 546af2076..95f1ecab9 100644 --- a/src/Native/libcryptonote/cryptonote_basic/hardfork.cpp +++ b/src/Native/libcryptonote/cryptonote_basic/hardfork.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/cryptonote_basic/hardfork.h b/src/Native/libcryptonote/cryptonote_basic/hardfork.h index 6c6fbcb84..ee5ec0596 100644 --- a/src/Native/libcryptonote/cryptonote_basic/hardfork.h +++ b/src/Native/libcryptonote/cryptonote_basic/hardfork.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -79,7 +79,6 @@ namespace cryptonote * returns true if no error, false otherwise * * @param version the major block version for the fork - * @param voting_version the minor block version for the fork, used for voting * @param height The height the hardfork takes effect * @param time Approximate time of the hardfork (seconds since epoch) */ diff --git a/src/Native/libcryptonote/cryptonote_basic/miner.cpp b/src/Native/libcryptonote/cryptonote_basic/miner.cpp index 3c5811d61..f949bbd2b 100644 --- a/src/Native/libcryptonote/cryptonote_basic/miner.cpp +++ b/src/Native/libcryptonote/cryptonote_basic/miner.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -32,14 +32,17 @@ #include #include #include +#include #include -#include "misc_language.h" #include "include_base_utils.h" +#include "misc_language.h" +#include "syncobj.h" #include "cryptonote_basic_impl.h" #include "cryptonote_format_utils.h" #include "file_io_utils.h" #include "common/command_line.h" #include "string_coding.h" +#include "string_tools.h" #include "storages/portable_storage_template_helper.h" #include "boost/logic/tribool.hpp" @@ -53,6 +56,19 @@ #include #endif +#ifdef __FreeBSD__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "miner" @@ -182,7 +198,8 @@ namespace cryptonote { uint64_t total_hr = std::accumulate(m_last_hash_rates.begin(), m_last_hash_rates.end(), 0); float hr = static_cast(total_hr)/static_cast(m_last_hash_rates.size()); - std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << ENDL; + const auto precision = std::cout.precision(); + std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << precision << ENDL; } } m_last_hr_merge_time = misc_utils::get_tick_count(); @@ -201,7 +218,7 @@ namespace cryptonote command_line::add_arg(desc, arg_bg_mining_miner_target_percentage); } //----------------------------------------------------------------------------------------------------- - bool miner::init(const boost::program_options::variables_map& vm, bool testnet) + bool miner::init(const boost::program_options::variables_map& vm, network_type nettype) { if(command_line::has_arg(vm, arg_extra_messages)) { @@ -228,11 +245,13 @@ namespace cryptonote if(command_line::has_arg(vm, arg_start_mining)) { - if(!cryptonote::get_account_address_from_str(m_mine_address, testnet, command_line::get_arg(vm, arg_start_mining))) + address_parse_info info; + if(!cryptonote::get_account_address_from_str(info, nettype, command_line::get_arg(vm, arg_start_mining)) || info.is_subaddress) { LOG_ERROR("Target account address " << command_line::get_arg(vm, arg_start_mining) << " has wrong format, starting daemon canceled"); return false; } + m_mine_address = info.address; m_threads_total = 1; m_do_mining = true; if(command_line::has_arg(vm, arg_mining_threads)) @@ -289,8 +308,7 @@ namespace cryptonote return false; } - if(!m_template_no) - request_block_template();//lets update block template + request_block_template();//lets update block template boost::interprocess::ipcdetail::atomic_write32(&m_stop, 0); boost::interprocess::ipcdetail::atomic_write32(&m_thread_index, 0); @@ -584,7 +602,7 @@ namespace cryptonote // this should take care of the case where mining is started with bg-enabled, // and then the user decides to un-check background mining, and just do // regular full-speed mining. I might just be over-doing it and thinking up - // non-existant use-cases, so if the concensus is to simplify, we can remove all this fluff. + // non-existant use-cases, so if the consensus is to simplify, we can remove all this fluff. /* while( !m_is_background_mining_enabled ) { @@ -606,21 +624,15 @@ namespace cryptonote continue; // if interrupted because stop called, loop should end .. } - boost::tribool battery_powered(on_battery_power()); - bool on_ac_power = false; - if(indeterminate( battery_powered )) + bool on_ac_power = m_ignore_battery; + if(!m_ignore_battery) { - // name could be better, only ignores battery requirement if we failed - // to get the status of the system - if( m_ignore_battery ) + boost::tribool battery_powered(on_battery_power()); + if(!indeterminate( battery_powered )) { - on_ac_power = true; + on_ac_power = !battery_powered; } } - else - { - on_ac_power = !battery_powered; - } if( m_is_background_mining_started ) { @@ -734,8 +746,6 @@ namespace cryptonote #elif defined(__linux__) - const std::string STR_CPU("cpu"); - const std::size_t STR_CPU_LEN = STR_CPU.size(); const std::string STAT_FILE_PATH = "/proc/stat"; if( !epee::file_io_utils::is_file_exist(STAT_FILE_PATH) ) @@ -784,9 +794,36 @@ namespace cryptonote return true; + #elif defined(__FreeBSD__) + + struct statinfo s; + size_t n = sizeof(s.cp_time); + if( sysctlbyname("kern.cp_time", s.cp_time, &n, NULL, 0) == -1 ) + { + LOG_ERROR("sysctlbyname(\"kern.cp_time\"): " << strerror(errno)); + return false; + } + if( n != sizeof(s.cp_time) ) + { + LOG_ERROR("sysctlbyname(\"kern.cp_time\") output is unexpectedly " + << n << " bytes instead of the expected " << sizeof(s.cp_time) + << " bytes."); + return false; + } + + idle_time = s.cp_time[CP_IDLE]; + total_time = + s.cp_time[CP_USER] + + s.cp_time[CP_NICE] + + s.cp_time[CP_SYS] + + s.cp_time[CP_INTR] + + s.cp_time[CP_IDLE]; + + return true; + #endif - return false; // unsupported systemm.. + return false; // unsupported system } //----------------------------------------------------------------------------------------------------- bool miner::get_process_time(uint64_t& total_time) @@ -806,7 +843,7 @@ namespace cryptonote return true; } - #elif (defined(__linux__) && defined(_SC_CLK_TCK)) || defined(__APPLE__) + #elif (defined(__linux__) && defined(_SC_CLK_TCK)) || defined(__APPLE__) || defined(__FreeBSD__) struct tms tms; if ( times(&tms) != (clock_t)-1 ) @@ -817,7 +854,7 @@ namespace cryptonote #endif - return false; // unsupported system.. + return false; // unsupported system } //----------------------------------------------------------------------------------------------------- uint8_t miner::get_percent_of_total(uint64_t other, uint64_t total) @@ -858,19 +895,6 @@ namespace cryptonote const boost::filesystem::path& power_supply_path = iter->path(); if (boost::filesystem::is_directory(power_supply_path)) { - std::ifstream power_supply_present_stream((power_supply_path / "present").string()); - if (power_supply_present_stream.fail()) - { - LOG_PRINT_L0("Unable to read from " << power_supply_path << " to check if power supply present"); - continue; - } - - if (power_supply_present_stream.get() != '1') - { - LOG_PRINT_L4("Power supply not present at " << power_supply_path); - continue; - } - boost::filesystem::path power_supply_type_path = power_supply_path / "type"; if (boost::filesystem::is_regular_file(power_supply_type_path)) { @@ -941,6 +965,70 @@ namespace cryptonote } return on_battery; + #elif defined(__FreeBSD__) + int ac; + size_t n = sizeof(ac); + if( sysctlbyname("hw.acpi.acline", &ac, &n, NULL, 0) == -1 ) + { + if( errno != ENOENT ) + { + LOG_ERROR("Cannot query battery status: " + << "sysctlbyname(\"hw.acpi.acline\"): " << strerror(errno)); + return boost::logic::tribool(boost::logic::indeterminate); + } + + // If sysctl fails with ENOENT, then try querying /dev/apm. + + static const char* dev_apm = "/dev/apm"; + const int fd = open(dev_apm, O_RDONLY); + if( fd == -1 ) { + LOG_ERROR("Cannot query battery status: " + << "open(): " << dev_apm << ": " << strerror(errno)); + return boost::logic::tribool(boost::logic::indeterminate); + } + + apm_info info; + if( ioctl(fd, APMIO_GETINFO, &info) == -1 ) { + close(fd); + LOG_ERROR("Cannot query battery status: " + << "ioctl(" << dev_apm << ", APMIO_GETINFO): " << strerror(errno)); + return boost::logic::tribool(boost::logic::indeterminate); + } + + close(fd); + + // See apm(8). + switch( info.ai_acline ) + { + case 0: // off-line + case 2: // backup power + return boost::logic::tribool(true); + case 1: // on-line + return boost::logic::tribool(false); + } + switch( info.ai_batt_stat ) + { + case 0: // high + case 1: // low + case 2: // critical + return boost::logic::tribool(true); + case 3: // charging + return boost::logic::tribool(false); + } + + LOG_ERROR("Cannot query battery status: " + << "sysctl hw.acpi.acline is not available and /dev/apm returns " + << "unexpected ac-line status (" << info.ai_acline << ") and " + << "battery status (" << info.ai_batt_stat << ")."); + return boost::logic::tribool(boost::logic::indeterminate); + } + if( n != sizeof(ac) ) + { + LOG_ERROR("sysctlbyname(\"hw.acpi.acline\") output is unexpectedly " + << n << " bytes instead of the expected " << sizeof(ac) << " bytes."); + return boost::logic::tribool(boost::logic::indeterminate); + } + return boost::logic::tribool(ac == 0); #endif LOG_ERROR("couldn't query power status"); diff --git a/src/Native/libcryptonote/cryptonote_basic/miner.h b/src/Native/libcryptonote/cryptonote_basic/miner.h index 964ee6a36..2bff784c7 100644 --- a/src/Native/libcryptonote/cryptonote_basic/miner.h +++ b/src/Native/libcryptonote/cryptonote_basic/miner.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -64,7 +64,7 @@ namespace cryptonote public: miner(i_miner_handler* phandler); ~miner(); - bool init(const boost::program_options::variables_map& vm, bool testnet); + bool init(const boost::program_options::variables_map& vm, network_type nettype); static void init_options(boost::program_options::options_description& desc); bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height); bool on_block_chain_update(); diff --git a/src/Native/libcryptonote/cryptonote_basic/subaddress_index.h b/src/Native/libcryptonote/cryptonote_basic/subaddress_index.h new file mode 100644 index 000000000..9b71448f9 --- /dev/null +++ b/src/Native/libcryptonote/cryptonote_basic/subaddress_index.h @@ -0,0 +1,102 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "serialization/keyvalue_serialization.h" +#include +#include +#include + +namespace cryptonote +{ + struct subaddress_index + { + uint32_t major; + uint32_t minor; + bool operator==(const subaddress_index& rhs) const { return !memcmp(this, &rhs, sizeof(subaddress_index)); } + bool operator!=(const subaddress_index& rhs) const { return !(*this == rhs); } + bool is_zero() const { return major == 0 && minor == 0; } + + BEGIN_SERIALIZE_OBJECT() + FIELD(major) + FIELD(minor) + END_SERIALIZE() + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(major) + KV_SERIALIZE(minor) + END_KV_SERIALIZE_MAP() + }; +} + +namespace cryptonote { + inline std::ostream& operator<<(std::ostream& out, const cryptonote::subaddress_index& subaddr_index) + { + return out << subaddr_index.major << '/' << subaddr_index.minor; + } +} + +namespace std +{ + template <> + struct hash + { + size_t operator()(const cryptonote::subaddress_index& index ) const + { + size_t res; + if (sizeof(size_t) == 8) + { + res = ((uint64_t)index.major << 32) | index.minor; + } + else + { + // https://stackoverflow.com/a/17017281 + res = 17; + res = res * 31 + hash()(index.major); + res = res * 31 + hash()(index.minor); + } + return res; + } + }; +} + +BOOST_CLASS_VERSION(cryptonote::subaddress_index, 0) + +namespace boost +{ + namespace serialization + { + template + inline void serialize(Archive &a, cryptonote::subaddress_index &x, const boost::serialization::version_type ver) + { + a & x.major; + a & x.minor; + } + } +} diff --git a/src/Native/libcryptonote/cryptonote_basic/tx_extra.h b/src/Native/libcryptonote/cryptonote_basic/tx_extra.h index 5a6c3176d..009e35ebe 100644 --- a/src/Native/libcryptonote/cryptonote_basic/tx_extra.h +++ b/src/Native/libcryptonote/cryptonote_basic/tx_extra.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -38,6 +38,7 @@ #define TX_EXTRA_TAG_PUBKEY 0x01 #define TX_EXTRA_NONCE 0x02 #define TX_EXTRA_MERGE_MINING_TAG 0x03 +#define TX_EXTRA_TAG_ADDITIONAL_PUBKEYS 0x04 #define TX_EXTRA_MYSTERIOUS_MINERGATE_TAG 0xDE #define TX_EXTRA_NONCE_PAYMENT_ID 0x00 @@ -159,6 +160,16 @@ namespace cryptonote } }; + // per-output additional tx pubkey for multi-destination transfers involving at least one subaddress + struct tx_extra_additional_pub_keys + { + std::vector data; + + BEGIN_SERIALIZE() + FIELD(data) + END_SERIALIZE() + }; + struct tx_extra_mysterious_minergate { std::string data; @@ -172,11 +183,12 @@ namespace cryptonote // varint tag; // varint size; // varint data[]; - typedef boost::variant tx_extra_field; + typedef boost::variant tx_extra_field; } VARIANT_TAG(binary_archive, cryptonote::tx_extra_padding, TX_EXTRA_TAG_PADDING); VARIANT_TAG(binary_archive, cryptonote::tx_extra_pub_key, TX_EXTRA_TAG_PUBKEY); VARIANT_TAG(binary_archive, cryptonote::tx_extra_nonce, TX_EXTRA_NONCE); VARIANT_TAG(binary_archive, cryptonote::tx_extra_merge_mining_tag, TX_EXTRA_MERGE_MINING_TAG); +VARIANT_TAG(binary_archive, cryptonote::tx_extra_additional_pub_keys, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS); VARIANT_TAG(binary_archive, cryptonote::tx_extra_mysterious_minergate, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG); diff --git a/src/Native/libcryptonote/cryptonote_basic/verification_context.h b/src/Native/libcryptonote/cryptonote_basic/verification_context.h index ce885ec1d..8d2b633a2 100644 --- a/src/Native/libcryptonote/cryptonote_basic/verification_context.h +++ b/src/Native/libcryptonote/cryptonote_basic/verification_context.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/cryptonote_config.h b/src/Native/libcryptonote/cryptonote_config.h index abfa665ff..0ad8a6005 100644 --- a/src/Native/libcryptonote/cryptonote_config.h +++ b/src/Native/libcryptonote/cryptonote_config.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -89,10 +89,10 @@ #define BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT 10000 //by default, blocks ids count in synchronizing -#define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 200 //by default, blocks count in blocks downloading -#define CRYPTONOTE_PROTOCOL_HOP_RELAX_COUNT 3 //value of hop, after which we use only announce of new block +#define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4 100 //by default, blocks count in blocks downloading +#define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 20 //by default, blocks count in blocks downloading -#define CRYPTONOTE_MEMPOOL_TX_LIVETIME 86400 //seconds, one day +#define CRYPTONOTE_MEMPOOL_TX_LIVETIME (86400*3) //seconds, three days #define CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME 604800 //seconds, one week #define COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT 1000 @@ -109,6 +109,7 @@ #define P2P_DEFAULT_INVOKE_TIMEOUT 60*2*1000 //2 minutes #define P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT 5000 //5 seconds #define P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT 70 +#define P2P_DEFAULT_ANCHOR_CONNECTIONS_COUNT 2 #define P2P_FAILED_ADDR_FORGET_SECONDS (60*60) //1 hour #define P2P_IP_BLOCKTIME (60*60*24) //24 hour @@ -131,10 +132,15 @@ #define HF_VERSION_DYNAMIC_FEE 4 #define HF_VERSION_MIN_MIXIN_4 6 +#define HF_VERSION_MIN_MIXIN_6 7 #define HF_VERSION_ENFORCE_RCT 6 #define PER_KB_FEE_QUANTIZATION_DECIMALS 8 +#define HASH_OF_HASHES_STEP 256 + +#define DEFAULT_TXPOOL_MAX_SIZE 648000000ull // 3 days at 300000, in bytes + // New constants are intended to go here namespace config { @@ -146,8 +152,10 @@ namespace config uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 18; uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 19; + uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 42; uint16_t const P2P_DEFAULT_PORT = 18080; uint16_t const RPC_DEFAULT_PORT = 18081; + uint16_t const ZMQ_RPC_DEFAULT_PORT = 18082; boost::uuids::uuid const NETWORK_ID = { { 0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x10 } }; // Bender's nightmare @@ -158,12 +166,40 @@ namespace config { uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 53; uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 54; + uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 63; uint16_t const P2P_DEFAULT_PORT = 28080; uint16_t const RPC_DEFAULT_PORT = 28081; + uint16_t const ZMQ_RPC_DEFAULT_PORT = 28082; boost::uuids::uuid const NETWORK_ID = { { 0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x11 } }; // Bender's daydream - std::string const GENESIS_TX = "013c01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd0880712101168d0c4ca86fb55a4cf6a36d31431be1c53a3bd7411bb24e8832410289fa6f3b"; + std::string const GENESIS_TX = "013c01ff0001ffffffffffff03029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121017767aafcde9be00dcfd098715ebcf7f410daebc582fda69d24a28e9d0bc890d1"; uint32_t const GENESIS_NONCE = 10001; } + + namespace stagenet + { + uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 24; + uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 25; + uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 36; + uint16_t const P2P_DEFAULT_PORT = 38080; + uint16_t const RPC_DEFAULT_PORT = 38081; + uint16_t const ZMQ_RPC_DEFAULT_PORT = 38082; + boost::uuids::uuid const NETWORK_ID = { { + 0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x12 + } }; // Bender's daydream + std::string const GENESIS_TX = "013c01ff0001ffffffffffff0302df5d56da0c7d643ddd1ce61901c7bdc5fb1738bfe39fbe69c28a3a7032729c0f2101168d0c4ca86fb55a4cf6a36d31431be1c53a3bd7411bb24e8832410289fa6f3b"; + uint32_t const GENESIS_NONCE = 10002; + } +} + +namespace cryptonote +{ + enum network_type : uint8_t + { + MAINNET = 0, + TESTNET, + STAGENET, + FAKECHAIN + }; } diff --git a/src/Native/libcryptonote/device/device.hpp b/src/Native/libcryptonote/device/device.hpp new file mode 100644 index 000000000..b47460472 --- /dev/null +++ b/src/Native/libcryptonote/device/device.hpp @@ -0,0 +1,188 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + + +/* Note about debug: + * To debug Device you can def the following : + * #define DEBUG_HWDEVICE + * Activate debug mechanism: + * - Add more trace + * - All computation done by device are checked by default device. + * Required IODUMMYCRYPT_HWDEVICE or IONOCRYPT_HWDEVICE for fully working + * #define IODUMMYCRYPT_HWDEVICE 1 + * - It assumes sensitive data encryption is is off on device side. a XOR with 0x55. This allow Ledger Class to make check on clear value + * #define IONOCRYPT_HWDEVICE 1 + * - It assumes sensitive data encryption is off on device side. + */ + + +#pragma once + +#include "crypto/crypto.h" +#include "crypto/chacha.h" +#include "ringct/rctTypes.h" + +#ifndef USE_DEVICE_LEDGER +#define USE_DEVICE_LEDGER 1 +#endif + +#if !defined(HAVE_PCSC) +#undef USE_DEVICE_LEDGER +#define USE_DEVICE_LEDGER 0 +#endif + +#if USE_DEVICE_LEDGER +#define WITH_DEVICE_LEDGER +#endif + +// forward declaration needed because this header is included by headers in libcryptonote_basic which depends on libdevice +namespace cryptonote +{ + struct account_public_address; + struct account_keys; + struct subaddress_index; +} + +namespace hw { + namespace { + //device funcion not supported + #define dfns() \ + throw std::runtime_error(std::string("device function not supported: ")+ std::string(__FUNCTION__) + \ + std::string(" (device.hpp line ")+std::to_string(__LINE__)+std::string(").")); \ + return false; + } + + + class device { + public: + + device() {} + device(const device &hwdev) {} + virtual ~device() {} + + explicit virtual operator bool() const = 0; + + static const int SIGNATURE_REAL = 0; + static const int SIGNATURE_FAKE = 1; + + + std::string name; + + /* ======================================================================= */ + /* SETUP/TEARDOWN */ + /* ======================================================================= */ + virtual bool set_name(const std::string &name) = 0; + virtual const std::string get_name() const = 0; + + virtual bool init(void) = 0; + virtual bool release() = 0; + + virtual bool connect(void) = 0; + virtual bool disconnect() = 0; + + /* ======================================================================= */ + /* WALLET & ADDRESS */ + /* ======================================================================= */ + virtual bool get_public_address(cryptonote::account_public_address &pubkey) = 0; + virtual bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) = 0; + virtual bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key) = 0; + + /* ======================================================================= */ + /* SUB ADDRESS */ + /* ======================================================================= */ + virtual bool derive_subaddress_public_key(const crypto::public_key &pub, const crypto::key_derivation &derivation, const std::size_t output_index, crypto::public_key &derived_pub) = 0; + virtual crypto::public_key get_subaddress_spend_public_key(const cryptonote::account_keys& keys, const cryptonote::subaddress_index& index) = 0; + virtual std::vector get_subaddress_spend_public_keys(const cryptonote::account_keys &keys, uint32_t account, uint32_t begin, uint32_t end) = 0; + virtual cryptonote::account_public_address get_subaddress(const cryptonote::account_keys& keys, const cryptonote::subaddress_index &index) = 0; + virtual crypto::secret_key get_subaddress_secret_key(const crypto::secret_key &sec, const cryptonote::subaddress_index &index) = 0; + + /* ======================================================================= */ + /* DERIVATION & KEY */ + /* ======================================================================= */ + virtual bool verify_keys(const crypto::secret_key &secret_key, const crypto::public_key &public_key) = 0; + virtual bool scalarmultKey(rct::key & aP, const rct::key &P, const rct::key &a) = 0; + virtual bool scalarmultBase(rct::key &aG, const rct::key &a) = 0; + virtual bool sc_secret_add( crypto::secret_key &r, const crypto::secret_key &a, const crypto::secret_key &b) = 0; + virtual crypto::secret_key generate_keys(crypto::public_key &pub, crypto::secret_key &sec, const crypto::secret_key& recovery_key = crypto::secret_key(), bool recover = false) = 0; + virtual bool generate_key_derivation(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_derivation &derivation) = 0; + virtual bool derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res) = 0; + virtual bool derive_secret_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::secret_key &sec, crypto::secret_key &derived_sec) = 0; + virtual bool derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, crypto::public_key &derived_pub) = 0; + virtual bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) = 0; + virtual bool generate_key_image(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image) = 0; + + // alternative prototypes available in libringct + rct::key scalarmultKey(const rct::key &P, const rct::key &a) + { + rct::key aP; + scalarmultKey(aP, P, a); + return aP; + } + + rct::key scalarmultBase(const rct::key &a) + { + rct::key aG; + scalarmultBase(aG, a); + return aG; + } + + /* ======================================================================= */ + /* TRANSACTION */ + /* ======================================================================= */ + + virtual bool open_tx(crypto::secret_key &tx_key) = 0; + + virtual bool set_signature_mode(unsigned int sig_mode) = 0; + + virtual bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) = 0; + bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) + { + // Encryption and decryption are the same operation (xor with a key) + return encrypt_payment_id(payment_id, public_key, secret_key); + } + + virtual bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec) = 0; + virtual bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec) = 0; + + virtual bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index, + const rct::key &amount_key, const crypto::public_key &out_eph_public_key) = 0; + + + virtual bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) = 0; + virtual bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) = 0; + virtual bool mlsag_prepare(rct::key &a, rct::key &aG) = 0; + virtual bool mlsag_hash(const rct::keyV &long_message, rct::key &c) = 0; + virtual bool mlsag_sign(const rct::key &c, const rct::keyV &xx, const rct::keyV &alpha, const size_t rows, const size_t dsRows, rct::keyV &ss) = 0; + + virtual bool close_tx(void) = 0; + } ; + + device& get_device(const std::string device_descriptor) ; +} + diff --git a/src/Native/libcryptonote/exports.cpp b/src/Native/libcryptonote/exports.cpp index 5081474df..2ea47f905 100644 --- a/src/Native/libcryptonote/exports.cpp +++ b/src/Native/libcryptonote/exports.cpp @@ -28,6 +28,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using namespace cryptonote; +extern "C" void cn_slow_hash_lite(const void *data, size_t length, char *hash); + #ifdef _WIN32 #define MODULE_API __declspec(dllexport) #else @@ -65,7 +67,7 @@ extern "C" MODULE_API bool convert_blob_export(const char* input, unsigned int i // now hash it result = get_block_hashing_blob(block); - *outputSize = result.length(); + *outputSize = (int) result.length(); // output buffer big enough? if (result.length() > originalOutputSize) @@ -115,14 +117,14 @@ extern "C" MODULE_API uint64_t decode_integrated_address_export(const char* inpu return prefix; } -extern "C" MODULE_API void cn_slow_hash_export(const char* input, unsigned char *output, uint32_t inputSize) +extern "C" MODULE_API void cn_slow_hash_export(const char* input, unsigned char *output, uint32_t inputSize, uint32_t variant) { - cn_slow_hash((const void *) input, (const size_t) inputSize, (char *) output); + cn_slow_hash_old_sig((const void *) input, (const size_t) inputSize, (char *) output, variant); } extern "C" MODULE_API void cn_fast_hash_export(const char* input, unsigned char *output, uint32_t inputSize) { - cn_fast_hash((const void *)input, (const size_t) inputSize, (char *) output); + cn_fast_hash_old_sig((const void *)input, (const size_t) inputSize, (char *) output); } extern "C" MODULE_API void cn_slow_hash_lite_export(const char* input, unsigned char *output, uint32_t inputSize) diff --git a/src/Native/libcryptonote/libcryptonote.vcxproj b/src/Native/libcryptonote/libcryptonote.vcxproj index f04d72c5c..263a574b9 100644 --- a/src/Native/libcryptonote/libcryptonote.vcxproj +++ b/src/Native/libcryptonote/libcryptonote.vcxproj @@ -97,7 +97,7 @@ Level3 Disabled - WIN32;_DEBUG;netmultihashnative_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);NEOSCRYPT_SHA256;NEOSCRYPT_BLAKE256;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS + WIN32;_DEBUG;netmultihashnative_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);NEOSCRYPT_SHA256;NEOSCRYPT_BLAKE256;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGSWIN32_LEAN_AND_MEAN;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_WARNINGS true MultiThreadedDebug $(ProjectDir);$(ProjectDir)contrib\epee\include;;$(ProjectDir)contrib\easylogging;%(AdditionalIncludeDirectories) @@ -114,7 +114,7 @@ Level3 Disabled - _DEBUG;netmultihashnative_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);NEOSCRYPT_SHA256;NEOSCRYPT_BLAKE256;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS + _DEBUG;netmultihashnative_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);NEOSCRYPT_SHA256;NEOSCRYPT_BLAKE256;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGSWIN32_LEAN_AND_MEAN;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_WARNINGS true MultiThreadedDebug $(ProjectDir);$(ProjectDir)contrib\epee\include;;$(ProjectDir)contrib\easylogging;%(AdditionalIncludeDirectories) @@ -133,7 +133,7 @@ MaxSpeed true true - WIN32;NDEBUG;netmultihashnative_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);NEOSCRYPT_SHA256;NEOSCRYPT_BLAKE256;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS + WIN32;NDEBUG;netmultihashnative_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);NEOSCRYPT_SHA256;NEOSCRYPT_BLAKE256;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGSWIN32_LEAN_AND_MEAN;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_WARNINGS true MultiThreaded $(ProjectDir);$(ProjectDir)contrib\epee\include;;$(ProjectDir)contrib\easylogging;%(AdditionalIncludeDirectories) @@ -154,7 +154,7 @@ MaxSpeed true true - NDEBUG;netmultihashnative_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);NEOSCRYPT_SHA256;NEOSCRYPT_BLAKE256;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS + NDEBUG;netmultihashnative_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);NEOSCRYPT_SHA256;NEOSCRYPT_BLAKE256;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGSWIN32_LEAN_AND_MEAN;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_WARNINGS true MultiThreaded $(ProjectDir);$(ProjectDir)contrib\epee\include;;$(ProjectDir)contrib\easylogging;%(AdditionalIncludeDirectories) @@ -172,10 +172,11 @@ + - + @@ -206,9 +207,10 @@ + - + diff --git a/src/Native/libcryptonote/libcryptonote.vcxproj.filters b/src/Native/libcryptonote/libcryptonote.vcxproj.filters index 02e8bd0bc..228959932 100644 --- a/src/Native/libcryptonote/libcryptonote.vcxproj.filters +++ b/src/Native/libcryptonote/libcryptonote.vcxproj.filters @@ -86,9 +86,6 @@ Header Files - - Header Files - Header Files @@ -113,6 +110,12 @@ Header Files + + Header Files + + + Header Files + @@ -181,9 +184,6 @@ Source Files - - Source Files - Source Files @@ -193,6 +193,12 @@ Source Files + + Source Files + + + Source Files + diff --git a/src/Native/libcryptonote/serialization/binary_archive.h b/src/Native/libcryptonote/serialization/binary_archive.h index 0a267b081..f47a4494d 100644 --- a/src/Native/libcryptonote/serialization/binary_archive.h +++ b/src/Native/libcryptonote/serialization/binary_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -82,7 +82,7 @@ struct binary_archive_base /* \struct binary_archive * - * \brief the actualy binary archive type + * \brief the actually binary archive type * * \detailed The boolean template argument /a W is the is_saving * parameter for binary_archive_base. diff --git a/src/Native/libcryptonote/serialization/binary_utils.h b/src/Native/libcryptonote/serialization/binary_utils.h index 08eba41da..79b30b337 100644 --- a/src/Native/libcryptonote/serialization/binary_utils.h +++ b/src/Native/libcryptonote/serialization/binary_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // diff --git a/src/Native/libcryptonote/serialization/container.h b/src/Native/libcryptonote/serialization/container.h new file mode 100644 index 000000000..978a59d2a --- /dev/null +++ b/src/Native/libcryptonote/serialization/container.h @@ -0,0 +1,113 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include "serialization.h" + +namespace serialization +{ + namespace detail + { + template + bool serialize_container_element(Archive& ar, T& e) + { + return ::do_serialize(ar, e); + } + + template + bool serialize_container_element(Archive& ar, uint32_t& e) + { + ar.serialize_varint(e); + return true; + } + + template + bool serialize_container_element(Archive& ar, uint64_t& e) + { + ar.serialize_varint(e); + return true; + } + + template + void do_reserve(C &c, size_t N) {} + } +} + +template
  • `uzIK-G$qiROgCw(&kl9!_|y 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%0Dd4c5Sb z4h1XVd4>UVcuWC{^{ZLsPNKE+N|#YddYK*&YeoRi=hxvmN~Nh(_81xPwEnq>zrH*L za{~`Cwumo<0$dAzr#mJ4<=R6zF)yVU{Ulov*Rq^V!*qcWnc%K&#w0%VQm%9&3UTOc z#r*PS^O4`bvHb*M;v_NEz(oF&*)Q)(foThxLQ;%p=v6;h;TVO2ccql!?3$yfE{Sg- zixqJWlzK!Z5~GjFa}@2w*BK(6m>dO@zm)K0*5Uq~|C@E7jo(4XU~J33TrZU-I_e!o zT=~)|MEW0#-};Us>A$U)=z&DSSD*VbenF%RQUuxu9segaODzpuJy%h^TplFtHw#cx zTuPZ}r*8fKE!U-lEho{a3H)cAG$6p2@?4VD*a4@Mv! zDK(KOiuGI(czuuhU0^F0+@iGaEC?qS3J2&NkWRWbPoa^Ff z9R&8@ghC?SVpTQ2&v!`sMex?m;TO>>YzTj3?ft4++;Uk0}WAqq-DK>20=E`F3SXb@>%6f-Q+2;Yhk%1)7D8b&d?aG^q3Uagh} zKfhOE8L<6yp?|ln2OGo#Aw94tU0l#EDc{1Ew|)e|#gYIUq+QYuKW30{k&TJP;wJEi z+UYO?|FXW|{fz;sVKxdc5ikUTFDNbq4|{vn!zJnji|xyL7$mr?zRwEB$u8pJvh^(-H71 z>9R$N%H>N9^Mj^O#EZ^CPv&jO!JJ)${aEE3Ou3RALoL8_rq4``1I&Tf{}-N=Gn{2c zfrnSsoonHL1VGpub0coJhC5kxFuzOOU2IN#Mas*T4 zWMGN(l_df9<2O0%)lQkc$zdIty`lL_{DwU@b7#P?C*}kXx-~E;lSu56!%Z+46aft} z=)aKezqg0^Ne?3_>4hLZ-{56^FGqWbl9WZ#Zc7!l%3BtQcA26IfsT+xjG)wC4p^&t-2?W$w_kz-wgqE>^l%YUM{#|z;`%(xSYQ40o&Rm% z6N7hmFze3Lx0y5&W2ipJ7?ykacP2n%ny_5ax_nWC)v<#4alN#!XL�~YpraL3uD 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 Ua63E6I4_V|tjnyIr&`kg0Rz-Y*8l(j From 318f151f2f01c719514ddd07d6865c9b1ade1d9b Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 25 Jan 2018 13:34:02 +0100 Subject: [PATCH 029/348] Hashtest fix for neoscrypt --- src/MiningCore.Tests/Crypto/HashingTests.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/MiningCore.Tests/Crypto/HashingTests.cs b/src/MiningCore.Tests/Crypto/HashingTests.cs index a0f4b87c5..8cff67e90 100644 --- a/src/MiningCore.Tests/Crypto/HashingTests.cs +++ b/src/MiningCore.Tests/Crypto/HashingTests.cs @@ -14,6 +14,9 @@ public class HashingTests : TestBase { private static readonly byte[] testValue = Enumerable.Repeat((byte) 0x80, 32).ToArray(); + // some algos need 80 byte input buffers + private static readonly byte[] testValue2 = Enumerable.Repeat((byte)0x80, 80).ToArray(); + [Fact] public void Blake_Hash_Should_Match() { @@ -78,16 +81,15 @@ public void Scrypt_Hash_Should_Throw_On_Null_Input() Assert.Throws(() => hasher.Digest(null)); } -/* [Fact] public void NeoScrypt_Hash_Should_Match() { var hasher = new NeoScrypt(0); - var result = hasher.Digest(testValue).ToHexString(); + var result = hasher.Digest(testValue2).ToHexString(); - Assert.Equal("2d48f6104ede1ecee0021b1e92f4c85aa6fbf38fe93c6b94bc4addf370ae8bb7", result); + Assert.Equal("7915d56de262bf23b1fb9104cf5d2a13fcbed2f6b4b9b657309c222b09f54bc0", result); } -*/ + [Fact] public void NeoScrypt_Hash_Should_Throw_On_Null_Input() { From b04d53b52e1fc8a50f865a81941d122a5aa7bd61 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 25 Jan 2018 16:42:53 +0100 Subject: [PATCH 030/348] GBX & CRC --- src/MiningCore/Api/ApiServer.cs | 6 +++--- src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs | 2 +- .../Blockchain/Bitcoin/BitcoinPayoutHandler.cs | 6 +++--- src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs | 2 +- src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs | 8 ++++++++ src/MiningCore/Blockchain/CoinMetaData.cs | 10 ++++++++-- src/MiningCore/Configuration/ClusterConfig.cs | 2 ++ src/MiningCore/Payments/PayoutHandlerBase.cs | 2 +- 8 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/MiningCore/Api/ApiServer.cs b/src/MiningCore/Api/ApiServer.cs index 0595d6577..bc347181d 100644 --- a/src/MiningCore/Api/ApiServer.cs +++ b/src/MiningCore/Api/ApiServer.cs @@ -382,7 +382,7 @@ private async Task PagePoolPaymentsAsync(HttpContext context, Match m) .ToArray(); // enrich payments - CoinMetaData.PaymentInfoLinks.TryGetValue(pool.Coin.Type, out var txInfobaseUrl); + CoinMetaData.TxInfoLinks.TryGetValue(pool.Coin.Type, out var txInfobaseUrl); CoinMetaData.AddressInfoLinks.TryGetValue(pool.Coin.Type, out var addressInfobaseUrl); foreach (var payment in payments) @@ -428,7 +428,7 @@ private async Task GetMinerInfoAsync(HttpContext context, Match m) stats.LastPayment = statsResult.LastPayment.Created; // Compute info link - if (CoinMetaData.PaymentInfoLinks.TryGetValue(pool.Coin.Type, out var baseUrl)) + if (CoinMetaData.TxInfoLinks.TryGetValue(pool.Coin.Type, out var baseUrl)) stats.LastPaymentLink = string.Format(baseUrl, statsResult.LastPayment.TransactionConfirmationData); } @@ -466,7 +466,7 @@ private async Task PageMinerPaymentsAsync(HttpContext context, Match m) .ToArray(); // enrich payments - CoinMetaData.PaymentInfoLinks.TryGetValue(pool.Coin.Type, out var txInfobaseUrl); + CoinMetaData.TxInfoLinks.TryGetValue(pool.Coin.Type, out var txInfobaseUrl); CoinMetaData.AddressInfoLinks.TryGetValue(pool.Coin.Type, out var addressInfobaseUrl); foreach (var payment in payments) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs index ed57574bd..ea30b2ab5 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs @@ -99,7 +99,7 @@ public class KnownAddresses {CoinType.MOON, "2QvpGimMYLyqKsczQXZjv56h6me3M8orwj" }, {CoinType.XVG, "D5xPoHLM6HPkwWSqAweECTSQirJBmRjS8i" }, {CoinType.XMR, "475YVJbPHPedudkhrcNp1wDcLMTGYusGPF5fqE7XjnragVLPdqbCHBdZg3dF4dN9hXMjjvGbykS6a77dTAQvGrpiQqHp2eH"}, - {CoinType.ETN, "etnkQJwBCjmR1MfkU8D355ZWxxLMhs8miPrtKHWN4U3uUowq9ugeKccVBoEG3n13n74us5AkT8tEoTog46w4HBgn8sMuBRhm9h"} + {CoinType.ETN, "etnkQJwBCjmR1MfkU8D355ZWxxLMhs8miPrtKHWN4U3uUowq9ugeKccVBoEG3n13n74us5AkT8tEoTog46w4HBgn8sMuBRhm9h"}, }; } diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs index 690dcae42..bc29715a4 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs @@ -46,9 +46,9 @@ namespace MiningCore.Blockchain.Bitcoin [CoinMetadata( CoinType.BTC, CoinType.BCH, CoinType.NMC, CoinType.PPC, CoinType.LTC, CoinType.DOGE, CoinType.DGB, CoinType.VIA, - CoinType.GRS, CoinType.MONA, CoinType.VTC, - CoinType.BTG, CoinType.GLT, CoinType.STAK, - CoinType.MOON, CoinType.XVG)] + CoinType.GRS, CoinType.MONA, CoinType.VTC, CoinType.BTG, + CoinType.GLT, CoinType.STAK, CoinType.MOON, CoinType.XVG, + CoinType.GBX, CoinType.CRC)] public class BitcoinPayoutHandler : PayoutHandlerBase, IPayoutHandler { diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs index c9c7940b6..a9525f54c 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs @@ -34,7 +34,7 @@ namespace MiningCore.Blockchain.Bitcoin CoinType.BTC, CoinType.BCH, CoinType.NMC, CoinType.PPC, CoinType.LTC, CoinType.DOGE, CoinType.DGB, CoinType.VIA, CoinType.GRS, CoinType.MONA, CoinType.VTC, CoinType.GLT, - CoinType.MOON, CoinType.XVG)] + CoinType.MOON, CoinType.XVG, CoinType.GBX, CoinType.CRC)] public class BitcoinPool : BitcoinPoolBase, BlockTemplate> { public BitcoinPool(IComponentContext ctx, diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index fa826bca5..bafb1c6e3 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -41,6 +41,7 @@ public class BitcoinProperties private static readonly IHashAlgorithm skein = new Skein(); private static readonly IHashAlgorithm qubit = new Qubit(); private static readonly IHashAlgorithm groestlMyriad = new GroestlMyriad(); + private static readonly IHashAlgorithm neoScrypt = new NeoScrypt(0); private static readonly BitcoinCoinProperties sha256Coin = new BitcoinCoinProperties(1, sha256D, sha256D, sha256DReverse, "Sha256"); @@ -72,6 +73,9 @@ public class BitcoinProperties private static readonly BitcoinCoinProperties equihashCoin = new BitcoinCoinProperties(1, new DummyHasher(), sha256D, sha256DReverse, "Equihash"); + private static readonly BitcoinCoinProperties neoScryptCoin = + new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, neoScrypt, sha256DReverse, "Neoscrypt"); + private static readonly Dictionary coinProperties = new Dictionary { // SHA256 @@ -105,6 +109,10 @@ public class BitcoinProperties { CoinType.BTG, equihashCoin }, { CoinType.ZCL, equihashCoin }, { CoinType.ZEN, equihashCoin }, + + // Neoscrypt + { CoinType.GBX, neoScryptCoin }, + { CoinType.CRC, neoScryptCoin }, }; public static BitcoinCoinProperties GetCoinProperties(CoinType coin, string algorithm = null) diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index 7f9c22412..85892af61 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -47,7 +47,7 @@ public static class CoinMetaData { CoinType.XVG, new Dictionary { { string.Empty, "https://verge-blockchain.info/block/{0}" } }}, }; - public static readonly Dictionary PaymentInfoLinks = new Dictionary + public static readonly Dictionary TxInfoLinks = new Dictionary { { CoinType.XMR, "https://chainradar.com/xmr/transaction/{0}" }, { CoinType.ETN, "https://blockexplorer.electroneum.com/tx/{0}" }, @@ -74,8 +74,10 @@ public static class CoinMetaData { CoinType.AEON, "https://chainradar.com/aeon/transaction/{0}" }, { CoinType.MOON, "https://chainz.cryptoid.info/moon/tx.dws?{0}.htm" }, { CoinType.XVG, "https://verge-blockchain.info/tx/{0}" }, + { CoinType.GBX, "http://gobyte.ezmine.io/tx/{0}" }, + { CoinType.CRC, "http://explorer.cryptopros.us/tx/{0}" }, }; - + public static readonly Dictionary AddressInfoLinks = new Dictionary { { CoinType.ETH, "https://etherscan.io/address/{0}" }, @@ -100,6 +102,8 @@ public static class CoinMetaData { CoinType.EXP, "http://www.gander.tech/address/{0}" }, { CoinType.MOON, "https://chainz.cryptoid.info/moon/address.dws?{0}.htm" }, { CoinType.XVG, "https://verge-blockchain.info/address/{0}" }, + { CoinType.GBX, "http://gobyte.ezmine.io/address/{0}" }, + { CoinType.CRC, "http://explorer.cryptopros.us/address/{0}" }, }; private const string Ethash = "Dagger-Hashimoto"; @@ -133,6 +137,8 @@ public static class CoinMetaData { CoinType.XMR, (coin)=> Cryptonight }, { CoinType.ETN, (coin)=> Cryptonight }, { CoinType.AEON, (coin)=> CryptonightLight }, + { CoinType.GBX, BitcoinProperties.GetAlgorithm }, + { CoinType.CRC, BitcoinProperties.GetAlgorithm }, }; } } diff --git a/src/MiningCore/Configuration/ClusterConfig.cs b/src/MiningCore/Configuration/ClusterConfig.cs index 5883e8528..69c15ed49 100644 --- a/src/MiningCore/Configuration/ClusterConfig.cs +++ b/src/MiningCore/Configuration/ClusterConfig.cs @@ -54,6 +54,8 @@ public enum CoinType ETN, // Electroneum MOON, // MoonCoin XVG, // Verge + GBX, // GoByte + CRC, // CrowdCoin } public class CoinConfig diff --git a/src/MiningCore/Payments/PayoutHandlerBase.cs b/src/MiningCore/Payments/PayoutHandlerBase.cs index 2d758c985..0fc2d0062 100644 --- a/src/MiningCore/Payments/PayoutHandlerBase.cs +++ b/src/MiningCore/Payments/PayoutHandlerBase.cs @@ -157,7 +157,7 @@ protected virtual void NotifyPayoutSuccess(string poolId, Balance[] balances, st // prepare tx link var txInfo = string.Join(", ", txHashes); - if (CoinMetaData.PaymentInfoLinks.TryGetValue(poolConfig.Coin.Type, out var baseUrl)) + if (CoinMetaData.TxInfoLinks.TryGetValue(poolConfig.Coin.Type, out var baseUrl)) txInfo = string.Join(", ", txHashes.Select(txHash => $"{txHash}")); notificationService.NotifyPaymentSuccess(poolId, balances.Sum(x => x.Amount), balances.Length, txInfo, txFee); From 93c1ceed0cd24b5b4f8b3430781a9c91d527dd73 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 25 Jan 2018 20:04:27 +0100 Subject: [PATCH 031/348] Neoscrypt fixes --- .../Blockchain/Bitcoin/BitcoinJob.cs | 17 +- .../Blockchain/Bitcoin/BitcoinProperties.cs | 4 +- src/Native/libmultihash/neoscrypt_asm.S | 17064 ---------------- 3 files changed, 12 insertions(+), 17073 deletions(-) delete mode 100644 src/Native/libmultihash/neoscrypt_asm.S diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs index bd677a4da..2d555fc80 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs @@ -332,16 +332,19 @@ protected virtual BitcoinShare ProcessShareInternal(StratumClient worker, string var result = new BitcoinShare { BlockHeight = BlockTemplate.Height, - IsBlockCandidate = isBlockCandidate + BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC), + NetworkDifficulty = Difficulty * shareMultiplier, + Difficulty = stratumDifficulty, }; var blockBytes = SerializeBlock(headerBytes, coinbase); - result.BlockHex = blockBytes.ToHexString(); - result.BlockHash = blockHasher.Digest(headerBytes, nTime).ToHexString(); - result.BlockHeight = BlockTemplate.Height; - result.BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC); - result.NetworkDifficulty = Difficulty * shareMultiplier; - result.Difficulty = stratumDifficulty; + + if (isBlockCandidate) + { + result.IsBlockCandidate = true; + result.BlockHex = blockBytes.ToHexString(); + result.BlockHash = blockHasher.Digest(headerBytes, nTime).ToHexString(); + } return result; } diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index bafb1c6e3..928cb53a7 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -41,7 +41,7 @@ public class BitcoinProperties private static readonly IHashAlgorithm skein = new Skein(); private static readonly IHashAlgorithm qubit = new Qubit(); private static readonly IHashAlgorithm groestlMyriad = new GroestlMyriad(); - private static readonly IHashAlgorithm neoScrypt = new NeoScrypt(0); + private static readonly IHashAlgorithm neoScryptProfile1 = new NeoScrypt(0x80000620); private static readonly BitcoinCoinProperties sha256Coin = new BitcoinCoinProperties(1, sha256D, sha256D, sha256DReverse, "Sha256"); @@ -74,7 +74,7 @@ public class BitcoinProperties new BitcoinCoinProperties(1, new DummyHasher(), sha256D, sha256DReverse, "Equihash"); private static readonly BitcoinCoinProperties neoScryptCoin = - new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, neoScrypt, sha256DReverse, "Neoscrypt"); + new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, neoScryptProfile1, sha256DReverse, "Neoscrypt"); private static readonly Dictionary coinProperties = new Dictionary { diff --git a/src/Native/libmultihash/neoscrypt_asm.S b/src/Native/libmultihash/neoscrypt_asm.S deleted file mode 100644 index 3889cb8a6..000000000 --- a/src/Native/libmultihash/neoscrypt_asm.S +++ /dev/null @@ -1,17064 +0,0 @@ -/* - * Copyright (c) 2014-2016 John Doering - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(ASM) && defined(__x86_64__) - -/* MOVQ_FIX addresses incorrect behaviour of old GNU assembler when transferring - * data between a 64-bit general purpose register and an MMX/SSE register: - * suffix or operands invalid for `movq' */ - -/* blake2s_compress(mem) - * AMD64 BLAKE2s block compression; - * the MMX registers are used as a temporal general purpose storage */ -.globl blake2s_compress -.globl _blake2s_compress -blake2s_compress: -_blake2s_compress: - pushq %rbx - pushq %rbp - pushq %r12 - pushq %r13 - pushq %r14 - pushq %r15 -#ifdef WIN64 - pushq %rdi - pushq %rsi - movq %rcx, %rdi -#endif - -#ifndef MOVQ_FIX - movq %rsp, %mm0 -#else - movd %esp, %mm0 - shrq $32, %rsp - movd %esp, %mm7 -#endif - -/* initialise */ - movl 0(%rdi), %eax - movl 4(%rdi), %ebx - movl 8(%rdi), %ecx - movl 12(%rdi), %edx - movl 16(%rdi), %ebp - movl 20(%rdi), %esp - movl 24(%rdi), %esi - movd 28(%rdi), %mm2 - movl 32(%rdi), %r12d - movl 36(%rdi), %r13d - movl 40(%rdi), %r14d - movl 44(%rdi), %r15d - addl 48(%rdi), %eax /* A */ - movl $0x6A09E667, %r8d - addl 64(%rdi), %ecx /* C */ - movl $0x3C6EF372, %r10d - addl %ebp, %eax /* A */ - movl $0xBB67AE85, %r9d - addl %esi, %ecx /* C */ - movl $0xA54FF53A, %r11d - xorl $0x510E527F, %r12d - xorl $0x1F83D9AB, %r14d - xorl %eax, %r12d /* A */ - xorl $0x9B05688C, %r13d - xorl %ecx, %r14d /* C */ - xorl $0x5BE0CD19, %r15d -/* round 0 (A and C) */ - rorl $16, %r12d /* A */ - addl 52(%rdi), %eax /* A */ - rorl $16, %r14d /* C */ - addl 68(%rdi), %ecx /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $12, %ebp /* A */ - addl 56(%rdi), %ebx /* B (initial) */ - rorl $12, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $8, %r12d /* A */ - addl 72(%rdi), %edx /* D (initial) */ - rorl $8, %r14d /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $7, %ebp /* A */ - rorl $7, %esi /* C */ - movd %esi, %mm1 /* C */ -/* round 0 (B and D) */ - movd %mm2, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $16, %r13d /* B */ - addl 60(%rdi), %ebx /* B */ - rorl $16, %r15d /* D */ - addl 76(%rdi), %edx /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $12, %esp /* B */ - addl 80(%rdi), %eax /* E (initial) */ - rorl $12, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $8, %r13d /* B */ - addl 96(%rdi), %ecx /* G (initial) */ - rorl $8, %r15d /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $7, %esp /* B */ - rorl $7, %esi /* D */ -/* round 0 (E and G) */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $16, %r15d /* E */ - addl 84(%rdi), %eax /* E */ - rorl $16, %r13d /* G */ - addl 100(%rdi), %ecx /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $12, %esp /* E */ - addl 88(%rdi), %ebx /* F (initial) */ - rorl $12, %esi /* G */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $8, %r15d /* E */ - addl 104(%rdi), %edx /* H (initial) */ - rorl $8, %r13d /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $7, %esp /* E */ - rorl $7, %esi /* G */ - movd %esi, %mm2 /* G */ -/* round 0 (F and H) */ - movd %mm1, %esi /* F */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $16, %r12d /* F */ - addl 92(%rdi), %ebx /* F */ - rorl $16, %r14d /* H */ - addl 108(%rdi), %edx /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $12, %esi /* F */ - addl 104(%rdi), %eax /* A (initial) */ - rorl $12, %ebp /* H */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $8, %r12d /* F */ - addl 84(%rdi), %ecx /* C (initial) */ - rorl $8, %r14d /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $7, %esi /* F */ - rorl $7, %ebp /* H */ - movd %esi, %mm1 /* F */ -/* round 1 (A and C) */ - movd %mm1, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $16, %r12d /* A */ - addl 88(%rdi), %eax /* A */ - rorl $16, %r14d /* C */ - addl 108(%rdi), %ecx /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $12, %ebp /* A */ - addl 64(%rdi), %ebx /* B (initial) */ - rorl $12, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $8, %r12d /* A */ - addl 100(%rdi), %edx /* D (initial) */ - rorl $8, %r14d /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $7, %ebp /* A */ - rorl $7, %esi /* C */ - movd %esi, %mm1 /* C */ -/* round 1 (B and D) */ - movd %mm2, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $16, %r13d /* B */ - addl 80(%rdi), %ebx /* B */ - rorl $16, %r15d /* D */ - addl 72(%rdi), %edx /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $12, %esp /* B */ - addl 52(%rdi), %eax /* E (initial) */ - rorl $12, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $8, %r13d /* B */ - addl 92(%rdi), %ecx /* G (initial) */ - rorl $8, %r15d /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $7, %esp /* B */ - rorl $7, %esi /* D */ -/* round 1 (E and G) */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $16, %r15d /* E */ - addl 96(%rdi), %eax /* E */ - rorl $16, %r13d /* G */ - addl 76(%rdi), %ecx /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $12, %esp /* E */ - addl 48(%rdi), %ebx /* F (initial) */ - rorl $12, %esi /* G */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $8, %r15d /* E */ - addl 68(%rdi), %edx /* H (initial) */ - rorl $8, %r13d /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $7, %esp /* E */ - rorl $7, %esi /* G */ - movd %esi, %mm2 /* G */ -/* round 1 (F and H) */ - movd %mm1, %esi /* F */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $16, %r12d /* F */ - addl 56(%rdi), %ebx /* F */ - rorl $16, %r14d /* H */ - addl 60(%rdi), %edx /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $12, %esi /* F */ - addl 92(%rdi), %eax /* A (initial) */ - rorl $12, %ebp /* H */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $8, %r12d /* F */ - addl 68(%rdi), %ecx /* C (initial) */ - rorl $8, %r14d /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $7, %esi /* F */ - rorl $7, %ebp /* H */ - movd %esi, %mm1 /* F */ -/* round 2 (A and C) */ - movd %mm1, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $16, %r12d /* A */ - addl 80(%rdi), %eax /* A */ - rorl $16, %r14d /* C */ - addl 56(%rdi), %ecx /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $12, %ebp /* A */ - addl 96(%rdi), %ebx /* B (initial) */ - rorl $12, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $8, %r12d /* A */ - addl 108(%rdi), %edx /* D (initial) */ - rorl $8, %r14d /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $7, %ebp /* A */ - rorl $7, %esi /* C */ - movd %esi, %mm1 /* C */ -/* round 2 (B and D) */ - movd %mm2, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $16, %r13d /* B */ - addl 48(%rdi), %ebx /* B */ - rorl $16, %r15d /* D */ - addl 100(%rdi), %edx /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $12, %esp /* B */ - addl 88(%rdi), %eax /* E (initial) */ - rorl $12, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $8, %r13d /* B */ - addl 76(%rdi), %ecx /* G (initial) */ - rorl $8, %r15d /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $7, %esp /* B */ - rorl $7, %esi /* D */ -/* round 2 (E and G) */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $16, %r15d /* E */ - addl 104(%rdi), %eax /* E */ - rorl $16, %r13d /* G */ - addl 52(%rdi), %ecx /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $12, %esp /* E */ - addl 60(%rdi), %ebx /* F (initial) */ - rorl $12, %esi /* G */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $8, %r15d /* E */ - addl 84(%rdi), %edx /* H (initial) */ - rorl $8, %r13d /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $7, %esp /* E */ - rorl $7, %esi /* G */ - movd %esi, %mm2 /* G */ -/* round 2 (F and H) */ - movd %mm1, %esi /* F */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $16, %r12d /* F */ - addl 72(%rdi), %ebx /* F */ - rorl $16, %r14d /* H */ - addl 64(%rdi), %edx /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $12, %esi /* F */ - addl 76(%rdi), %eax /* A (initial) */ - rorl $12, %ebp /* H */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $8, %r12d /* F */ - addl 100(%rdi), %ecx /* C (initial) */ - rorl $8, %r14d /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $7, %esi /* F */ - rorl $7, %ebp /* H */ - movd %esi, %mm1 /* F */ -/* round 3 (A and C) */ - movd %mm1, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $16, %r12d /* A */ - addl 84(%rdi), %eax /* A */ - rorl $16, %r14d /* C */ - addl 96(%rdi), %ecx /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $12, %ebp /* A */ - addl 60(%rdi), %ebx /* B (initial) */ - rorl $12, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $8, %r12d /* A */ - addl 92(%rdi), %edx /* D (initial) */ - rorl $8, %r14d /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $7, %ebp /* A */ - rorl $7, %esi /* C */ - movd %esi, %mm1 /* C */ -/* round 3 (B and D) */ - movd %mm2, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $16, %r13d /* B */ - addl 52(%rdi), %ebx /* B */ - rorl $16, %r15d /* D */ - addl 104(%rdi), %edx /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $12, %esp /* B */ - addl 56(%rdi), %eax /* E (initial) */ - rorl $12, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $8, %r13d /* B */ - addl 64(%rdi), %ecx /* G (initial) */ - rorl $8, %r15d /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $7, %esp /* B */ - rorl $7, %esi /* D */ -/* round 3 (E and G) */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $16, %r15d /* E */ - addl 72(%rdi), %eax /* E */ - rorl $16, %r13d /* G */ - addl 48(%rdi), %ecx /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $12, %esp /* E */ - addl 68(%rdi), %ebx /* F (initial) */ - rorl $12, %esi /* G */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $8, %r15d /* E */ - addl 108(%rdi), %edx /* H (initial) */ - rorl $8, %r13d /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $7, %esp /* E */ - rorl $7, %esi /* G */ - movd %esi, %mm2 /* G */ -/* round 3 (F and H) */ - movd %mm1, %esi /* F */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $16, %r12d /* F */ - addl 88(%rdi), %ebx /* F */ - rorl $16, %r14d /* H */ - addl 80(%rdi), %edx /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $12, %esi /* F */ - addl 84(%rdi), %eax /* A (initial) */ - rorl $12, %ebp /* H */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $8, %r12d /* F */ - addl 56(%rdi), %ecx /* C (initial) */ - rorl $8, %r14d /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $7, %esi /* F */ - rorl $7, %ebp /* H */ - movd %esi, %mm1 /* F */ -/* round 4 (A and C) */ - movd %mm1, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $16, %r12d /* A */ - addl 48(%rdi), %eax /* A */ - rorl $16, %r14d /* C */ - addl 64(%rdi), %ecx /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $12, %ebp /* A */ - addl 68(%rdi), %ebx /* B (initial) */ - rorl $12, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $8, %r12d /* A */ - addl 88(%rdi), %edx /* D (initial) */ - rorl $8, %r14d /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $7, %ebp /* A */ - rorl $7, %esi /* C */ - movd %esi, %mm1 /* C */ -/* round 4 (B and D) */ - movd %mm2, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $16, %r13d /* B */ - addl 76(%rdi), %ebx /* B */ - rorl $16, %r15d /* D */ - addl 108(%rdi), %edx /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $12, %esp /* B */ - addl 104(%rdi), %eax /* E (initial) */ - rorl $12, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $8, %r13d /* B */ - addl 72(%rdi), %ecx /* G (initial) */ - rorl $8, %r15d /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $7, %esp /* B */ - rorl $7, %esi /* D */ -/* round 4 (E and G) */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $16, %r15d /* E */ - addl 52(%rdi), %eax /* E */ - rorl $16, %r13d /* G */ - addl 80(%rdi), %ecx /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $12, %esp /* E */ - addl 92(%rdi), %ebx /* F (initial) */ - rorl $12, %esi /* G */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $8, %r15d /* E */ - addl 60(%rdi), %edx /* H (initial) */ - rorl $8, %r13d /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $7, %esp /* E */ - rorl $7, %esi /* G */ - movd %esi, %mm2 /* G */ -/* round 4 (F and H) */ - movd %mm1, %esi /* F */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $16, %r12d /* F */ - addl 96(%rdi), %ebx /* F */ - rorl $16, %r14d /* H */ - addl 100(%rdi), %edx /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $12, %esi /* F */ - addl 56(%rdi), %eax /* A (initial) */ - rorl $12, %ebp /* H */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $8, %r12d /* F */ - addl 48(%rdi), %ecx /* C (initial) */ - rorl $8, %r14d /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $7, %esi /* F */ - rorl $7, %ebp /* H */ - movd %esi, %mm1 /* F */ -/* round 5 (A and C) */ - movd %mm1, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $16, %r12d /* A */ - addl 96(%rdi), %eax /* A */ - rorl $16, %r14d /* C */ - addl 92(%rdi), %ecx /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $12, %ebp /* A */ - addl 72(%rdi), %ebx /* B (initial) */ - rorl $12, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $8, %r12d /* A */ - addl 80(%rdi), %edx /* D (initial) */ - rorl $8, %r14d /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $7, %ebp /* A */ - rorl $7, %esi /* C */ - movd %esi, %mm1 /* C */ -/* round 5 (B and D) */ - movd %mm2, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $16, %r13d /* B */ - addl 88(%rdi), %ebx /* B */ - rorl $16, %r15d /* D */ - addl 60(%rdi), %edx /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $12, %esp /* B */ - addl 64(%rdi), %eax /* E (initial) */ - rorl $12, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $8, %r13d /* B */ - addl 108(%rdi), %ecx /* G (initial) */ - rorl $8, %r15d /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $7, %esp /* B */ - rorl $7, %esi /* D */ -/* round 5 (E and G) */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $16, %r15d /* E */ - addl 100(%rdi), %eax /* E */ - rorl $16, %r13d /* G */ - addl 104(%rdi), %ecx /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $12, %esp /* E */ - addl 76(%rdi), %ebx /* F (initial) */ - rorl $12, %esi /* G */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $8, %r15d /* E */ - addl 52(%rdi), %edx /* H (initial) */ - rorl $8, %r13d /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $7, %esp /* E */ - rorl $7, %esi /* G */ - movd %esi, %mm2 /* G */ -/* round 5 (F and H) */ - movd %mm1, %esi /* F */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $16, %r12d /* F */ - addl 68(%rdi), %ebx /* F */ - rorl $16, %r14d /* H */ - addl 84(%rdi), %edx /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $12, %esi /* F */ - addl 96(%rdi), %eax /* A (initial) */ - rorl $12, %ebp /* H */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $8, %r12d /* F */ - addl 104(%rdi), %ecx /* C (initial) */ - rorl $8, %r14d /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $7, %esi /* F */ - rorl $7, %ebp /* H */ - movd %esi, %mm1 /* F */ -/* round 6 (A and C) */ - movd %mm1, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $16, %r12d /* A */ - addl 68(%rdi), %eax /* A */ - rorl $16, %r14d /* C */ - addl 100(%rdi), %ecx /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $12, %ebp /* A */ - addl 52(%rdi), %ebx /* B (initial) */ - rorl $12, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $8, %r12d /* A */ - addl 64(%rdi), %edx /* D (initial) */ - rorl $8, %r14d /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $7, %ebp /* A */ - rorl $7, %esi /* C */ - movd %esi, %mm1 /* C */ -/* round 6 (B and D) */ - movd %mm2, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $16, %r13d /* B */ - addl 108(%rdi), %ebx /* B */ - rorl $16, %r15d /* D */ - addl 88(%rdi), %edx /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $12, %esp /* B */ - addl 48(%rdi), %eax /* E (initial) */ - rorl $12, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $8, %r13d /* B */ - addl 84(%rdi), %ecx /* G (initial) */ - rorl $8, %r15d /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $7, %esp /* B */ - rorl $7, %esi /* D */ -/* round 6 (E and G) */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $16, %r15d /* E */ - addl 76(%rdi), %eax /* E */ - rorl $16, %r13d /* G */ - addl 56(%rdi), %ecx /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $12, %esp /* E */ - addl 72(%rdi), %ebx /* F (initial) */ - rorl $12, %esi /* G */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $8, %r15d /* E */ - addl 80(%rdi), %edx /* H (initial) */ - rorl $8, %r13d /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $7, %esp /* E */ - rorl $7, %esi /* G */ - movd %esi, %mm2 /* G */ -/* round 6 (F and H) */ - movd %mm1, %esi /* F */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $16, %r12d /* F */ - addl 60(%rdi), %ebx /* F */ - rorl $16, %r14d /* H */ - addl 92(%rdi), %edx /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $12, %esi /* F */ - addl 100(%rdi), %eax /* A (initial) */ - rorl $12, %ebp /* H */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $8, %r12d /* F */ - addl 96(%rdi), %ecx /* C (initial) */ - rorl $8, %r14d /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $7, %esi /* F */ - rorl $7, %ebp /* H */ - movd %esi, %mm1 /* F */ -/* round 7 (A and C) */ - movd %mm1, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $16, %r12d /* A */ - addl 92(%rdi), %eax /* A */ - rorl $16, %r14d /* C */ - addl 52(%rdi), %ecx /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $12, %ebp /* A */ - addl 76(%rdi), %ebx /* B (initial) */ - rorl $12, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $8, %r12d /* A */ - addl 60(%rdi), %edx /* D (initial) */ - rorl $8, %r14d /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $7, %ebp /* A */ - rorl $7, %esi /* C */ - movd %esi, %mm1 /* C */ -/* round 7 (B and D) */ - movd %mm2, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $16, %r13d /* B */ - addl 104(%rdi), %ebx /* B */ - rorl $16, %r15d /* D */ - addl 84(%rdi), %edx /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $12, %esp /* B */ - addl 68(%rdi), %eax /* E (initial) */ - rorl $12, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $8, %r13d /* B */ - addl 80(%rdi), %ecx /* G (initial) */ - rorl $8, %r15d /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $7, %esp /* B */ - rorl $7, %esi /* D */ -/* round 7 (E and G) */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $16, %r15d /* E */ - addl 48(%rdi), %eax /* E */ - rorl $16, %r13d /* G */ - addl 72(%rdi), %ecx /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $12, %esp /* E */ - addl 108(%rdi), %ebx /* F (initial) */ - rorl $12, %esi /* G */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $8, %r15d /* E */ - addl 56(%rdi), %edx /* H (initial) */ - rorl $8, %r13d /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $7, %esp /* E */ - rorl $7, %esi /* G */ - movd %esi, %mm2 /* G */ -/* round 7 (F and H) */ - movd %mm1, %esi /* F */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $16, %r12d /* F */ - addl 64(%rdi), %ebx /* F */ - rorl $16, %r14d /* H */ - addl 88(%rdi), %edx /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $12, %esi /* F */ - addl 72(%rdi), %eax /* A (initial) */ - rorl $12, %ebp /* H */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $8, %r12d /* F */ - addl 92(%rdi), %ecx /* C (initial) */ - rorl $8, %r14d /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $7, %esi /* F */ - rorl $7, %ebp /* H */ - movd %esi, %mm1 /* F */ -/* round 8 (A and C) */ - movd %mm1, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $16, %r12d /* A */ - addl 108(%rdi), %eax /* A */ - rorl $16, %r14d /* C */ - addl 60(%rdi), %ecx /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $12, %ebp /* A */ - addl 104(%rdi), %ebx /* B (initial) */ - rorl $12, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $8, %r12d /* A */ - addl 48(%rdi), %edx /* D (initial) */ - rorl $8, %r14d /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $7, %ebp /* A */ - rorl $7, %esi /* C */ - movd %esi, %mm1 /* C */ -/* round 8 (B and D) */ - movd %mm2, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $16, %r13d /* B */ - addl 84(%rdi), %ebx /* B */ - rorl $16, %r15d /* D */ - addl 80(%rdi), %edx /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $12, %esp /* B */ - addl 96(%rdi), %eax /* E (initial) */ - rorl $12, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $8, %r13d /* B */ - addl 52(%rdi), %ecx /* G (initial) */ - rorl $8, %r15d /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $7, %esp /* B */ - rorl $7, %esi /* D */ -/* round 8 (E and G) */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $16, %r15d /* E */ - addl 56(%rdi), %eax /* E */ - rorl $16, %r13d /* G */ - addl 64(%rdi), %ecx /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $12, %esp /* E */ - addl 100(%rdi), %ebx /* F (initial) */ - rorl $12, %esi /* G */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $8, %r15d /* E */ - addl 88(%rdi), %edx /* H (initial) */ - rorl $8, %r13d /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $7, %esp /* E */ - rorl $7, %esi /* G */ - movd %esi, %mm2 /* G */ -/* round 8 (F and H) */ - movd %mm1, %esi /* F */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $16, %r12d /* F */ - addl 76(%rdi), %ebx /* F */ - rorl $16, %r14d /* H */ - addl 68(%rdi), %edx /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $12, %esi /* F */ - addl 88(%rdi), %eax /* A (initial) */ - rorl $12, %ebp /* H */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $8, %r12d /* F */ - addl 76(%rdi), %ecx /* C (initial) */ - rorl $8, %r14d /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $7, %esi /* F */ - rorl $7, %ebp /* H */ - movd %esi, %mm1 /* F */ -/* round 9 (A and C) */ - movd %mm1, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $16, %r12d /* A */ - addl 56(%rdi), %eax /* A */ - rorl $16, %r14d /* C */ - addl 72(%rdi), %ecx /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $12, %ebp /* A */ - addl 80(%rdi), %ebx /* B (initial) */ - rorl $12, %esi /* C */ - addl %ebp, %eax /* A */ - addl %esi, %ecx /* C */ - xorl %eax, %r12d /* A */ - xorl %ecx, %r14d /* C */ - rorl $8, %r12d /* A */ - addl 52(%rdi), %edx /* D (initial) */ - rorl $8, %r14d /* C */ - addl %r12d, %r8d /* A */ - addl %r14d, %r10d /* C */ - xorl %r8d, %ebp /* A */ - xorl %r10d, %esi /* C */ - rorl $7, %ebp /* A */ - rorl $7, %esi /* C */ - movd %esi, %mm1 /* C */ -/* round 9 (B and D) */ - movd %mm2, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $16, %r13d /* B */ - addl 64(%rdi), %ebx /* B */ - rorl $16, %r15d /* D */ - addl 68(%rdi), %edx /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $12, %esp /* B */ - addl 108(%rdi), %eax /* E (initial) */ - rorl $12, %esi /* D */ - addl %esp, %ebx /* B */ - addl %esi, %edx /* D */ - xorl %ebx, %r13d /* B */ - xorl %edx, %r15d /* D */ - rorl $8, %r13d /* B */ - addl 60(%rdi), %ecx /* G (initial) */ - rorl $8, %r15d /* D */ - addl %r13d, %r9d /* B */ - addl %r15d, %r11d /* D */ - xorl %r9d, %esp /* B */ - xorl %r11d, %esi /* D */ - rorl $7, %esp /* B */ - rorl $7, %esi /* D */ -/* round 9 (E and G) */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $16, %r15d /* E */ - addl 92(%rdi), %eax /* E */ - rorl $16, %r13d /* G */ - addl 96(%rdi), %ecx /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - rorl $12, %esp /* E */ - addl 84(%rdi), %ebx /* F (initial) */ - rorl $12, %esi /* G */ - addl %esp, %eax /* E */ - addl %esi, %ecx /* G */ - xorl %eax, %r15d /* E */ - xorl %ecx, %r13d /* G */ - rorl $8, %r15d /* E */ - addl 100(%rdi), %edx /* H (initial) */ - rorl $8, %r13d /* G */ - addl %r15d, %r10d /* E */ - addl %r13d, %r8d /* G */ - xorl %r10d, %esp /* E */ - xorl %r8d, %esi /* G */ - xorl %ecx, %r10d /* finalise */ - rorl $7, %esp /* E */ - xorl %eax, %r8d /* finalise */ - rorl $7, %esi /* G */ - xorl %r10d, 8(%rdi) /* finalise */ - xorl %r8d, 0(%rdi) /* finalise */ - xorl %esi, %r15d /* finalise */ - xorl %esp, %r13d /* finalise */ -/* round 9 (F and H) */ - movd %mm1, %esi /* F */ - xorl %r15d, 28(%rdi) /* finalise */ - xorl %r13d, 20(%rdi) /* finalise */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $16, %r12d /* F */ - addl 104(%rdi), %ebx /* F */ - rorl $16, %r14d /* H */ - addl 48(%rdi), %edx /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - rorl $12, %esi /* F */ - rorl $12, %ebp /* H */ - addl %esi, %ebx /* F */ - addl %ebp, %edx /* H */ - xorl %ebx, %r12d /* F */ - xorl %edx, %r14d /* H */ - rorl $8, %r12d /* F */ - rorl $8, %r14d /* H */ - addl %r12d, %r11d /* F */ - addl %r14d, %r9d /* H */ - xorl %r11d, %esi /* F */ - xorl %r9d, %ebp /* H */ - xorl %edx, %r11d /* finalise */ - rorl $7, %esi /* F */ - xorl %ebx, %r9d /* finalise */ - rorl $7, %ebp /* H */ - xorl %esi, %r14d /* finalise */ - xorl %ebp, %r12d /* finalise */ - xorl %r9d, 4(%rdi) /* finalise */ - xorl %r11d, 12(%rdi) /* finalise */ - xorl %r12d, 16(%rdi) /* finalise */ - xorl %r14d, 24(%rdi) /* finalise */ - -#ifndef MOVQ_FIX - movq %mm0, %rsp -#else - movd %mm0, %esp - movd %mm7, %eax - shlq $32, %rax - orq %rax, %rsp -#endif - -#ifdef WIN64 - popq %rsi - popq %rdi -#endif - popq %r15 - popq %r14 - popq %r13 - popq %r12 - popq %rbp - popq %rbx - emms - ret - - -/* neoscrypt_copy(dst, src, len) - * AMD64 memcpy() */ -.globl neoscrypt_copy -.globl _neoscrypt_copy -neoscrypt_copy: -_neoscrypt_copy: -#ifdef WIN64 - movq %rdi, %r10 - movq %rsi, %r11 - movq %rcx, %rdi - movq %rdx, %rsi - movq %r8, %rdx -#endif - xorq %rcx, %rcx - movl %edx, %ecx - shrq $4, %rcx - xorq %r9, %r9 - cmpq %r9, %rcx - jz .4byte_copy_test -.16byte_copy: - movq 0(%rsi), %rax - movq 8(%rsi), %r8 - movq %rax, 0(%rdi) - movq %r8, 8(%rdi) - addq $16, %rsi - addq $16, %rdi - decq %rcx - jnz .16byte_copy - -.4byte_copy_test: - movl %edx, %ecx - shrq $2, %rcx - andq $0x3, %rcx - cmpq %r9, %rcx - jz .byte_copy_test -.4byte_copy: - movl 0(%rsi), %eax - movl %eax, 0(%rdi) - addq $4, %rsi - addq $4, %rdi - decq %rcx - jnz .4byte_copy - -.byte_copy_test: - movl %edx, %ecx - andq $0x3, %rcx - cmpq %r9, %rcx - jz .copy_finish -.byte_copy: - movb 0(%rsi), %al - movb %al, 0(%rdi) - incq %rsi - incq %rdi - decq %rcx - jnz .byte_copy - -.copy_finish: -#ifdef WIN64 - movq %r10, %rdi - movq %r11, %rsi -#endif - ret - - -/* neoscrypt_erase(dst, len) - * AMD64 memory eraser */ -.globl neoscrypt_erase -.globl _neoscrypt_erase -neoscrypt_erase: -_neoscrypt_erase: -#ifdef WIN64 - movq %rdi, %r10 - movq %rsi, %r11 - movq %rcx, %rdi - movq %rdx, %rsi -#endif - xorq %rcx, %rcx - movl %esi, %ecx - shrq $4, %rcx - xorq %rax, %rax - cmpq %rax, %rcx - jz .4byte_erase_test -.16byte_erase: - movq %rax, 0(%rdi) - movq %rax, 8(%rdi) - addq $16, %rdi - decq %rcx - jnz .16byte_erase - -.4byte_erase_test: - movl %esi, %ecx - shrq $2, %rcx - andq $0x3, %rcx - cmpq %rax, %rcx - jz .byte_erase_test -.4byte_erase: - movl %eax, 0(%rdi) - addq $4, %rdi - decq %rcx - jnz .4byte_erase - -.byte_erase_test: - movl %esi, %ecx - andq $0x3, %rcx - cmpq %rax, %rcx - jz .erase_finish -.byte_erase: - movb %al, 0(%rdi) - incq %rdi - decq %rcx - jnz .byte_erase - -.erase_finish: -#ifdef WIN64 - movq %r10, %rdi - movq %r11, %rsi -#endif - ret - - -/* neoscrypt_xor(dst, src, len) - * AMD64 XOR engine */ -.globl neoscrypt_xor -.globl _neoscrypt_xor -neoscrypt_xor: -_neoscrypt_xor: -#ifdef WIN64 - movq %rdi, %r10 - movq %rsi, %r11 - movq %rcx, %rdi - movq %rdx, %rsi - movq %r8, %rdx -#endif - xorq %rcx, %rcx - movl %edx, %ecx - shrq $4, %rcx - xorq %r9, %r9 - cmpq %r9, %rcx - jz .4byte_xor_test -.16byte_xor: - movq 0(%rsi), %rax - movq 8(%rsi), %r8 - xorq 0(%rdi), %rax - xorq 8(%rdi), %r8 - movq %rax, 0(%rdi) - movq %r8, 8(%rdi) - addq $16, %rsi - addq $16, %rdi - decq %rcx - jnz .16byte_xor - -.4byte_xor_test: - movl %edx, %ecx - shrq $2, %rcx - andq $0x3, %rcx - cmpq %r9, %rcx - jz .byte_xor_test -.4byte_xor: - movl 0(%rsi), %eax - xorl 0(%rdi), %eax - movl %eax, 0(%rdi) - addq $4, %rsi - addq $4, %rdi - decq %rcx - jnz .4byte_xor - -.byte_xor_test: - movl %edx, %ecx - andq $0x3, %rcx - cmpq %r9, %rcx - jz .xor_finish -.byte_xor: - movb 0(%rsi), %al - xorb 0(%rdi), %al - movb %al, 0(%rdi) - incq %rsi - incq %rdi - decq %rcx - jnz .byte_xor - -.xor_finish: -#ifdef WIN64 - movq %r10, %rdi - movq %r11, %rsi -#endif - ret - - -/* neoscrypt_fastkdf_opt(password, salt, output, output_len) - * AMD64 SSE2 FastKDF optimised */ -.globl neoscrypt_fastkdf_opt -.globl _neoscrypt_fastkdf_opt -neoscrypt_fastkdf_opt: -_neoscrypt_fastkdf_opt: - pushq %rbx - pushq %rbp - pushq %r12 - pushq %r13 - pushq %r14 - pushq %r15 -#ifdef WIN64 - pushq %rdi - pushq %rsi - subq $160, %rsp - movdqu %xmm6, 144(%rsp) - movdqu %xmm7, 128(%rsp) - movdqu %xmm8, 112(%rsp) - movdqu %xmm9, 96(%rsp) - movdqu %xmm10, 80(%rsp) - movdqu %xmm11, 64(%rsp) - movdqu %xmm12, 48(%rsp) - movdqu %xmm13, 32(%rsp) - movdqu %xmm14, 16(%rsp) - movdqu %xmm15, 0(%rsp) - movq %rcx, %rdi - movq %rdx, %rsi - movq %r8, %rdx - movq %r9, %rcx -#endif - -/* 64 bytes (local variables) + 64 bytes (alignment space) + 320 bytes (password - * buffer) + 288 bytes (salt buffer) + 112 bytes (BLAKE2s space) = 848 bytes */ - subq $848, %rsp - leaq 128(%rsp), %rbp - andq $0xFFFFFFFFFFFFFFC0, %rbp - movq %rdx, 48(%rsp) - movq %rcx, 56(%rsp) - - movdqu 0(%rdi), %xmm0 - movdqu 16(%rdi), %xmm1 - movdqu 32(%rdi), %xmm2 - movdqu 48(%rdi), %xmm3 - movdqu 64(%rdi), %xmm4 - movdqa %xmm0, 0(%rbp) - movdqa %xmm1, 16(%rbp) - movdqa %xmm2, 32(%rbp) - movdqa %xmm3, 48(%rbp) - movdqa %xmm4, 64(%rbp) - movdqa %xmm0, 80(%rbp) - movdqa %xmm1, 96(%rbp) - movdqa %xmm2, 112(%rbp) - movdqa %xmm3, 128(%rbp) - movdqa %xmm4, 144(%rbp) - movdqa %xmm0, 160(%rbp) - movdqa %xmm1, 176(%rbp) - movdqa %xmm2, 192(%rbp) - movdqa %xmm3, 208(%rbp) - movdqa %xmm4, 224(%rbp) - movdqa %xmm0, 240(%rbp) - movdqa %xmm0, 256(%rbp) - movdqa %xmm1, 272(%rbp) - movdqa %xmm2, 288(%rbp) - movdqa %xmm3, 304(%rbp) - - leaq 320(%rbp), %rbx - leaq 608(%rbp), %r14 - movq %rbp, %r12 - xorq %r13, %r13 - movq $32, %r15 - testl $0x01, 56(%rsp) - jnz .fastkdf_mode_one - - movl $256, 56(%rsp) - movdqu 0(%rsi), %xmm0 - movdqu 16(%rsi), %xmm1 - movdqu 32(%rsi), %xmm2 - movdqu 48(%rsi), %xmm3 - movdqu 64(%rsi), %xmm4 - movdqa %xmm0, 0(%rbx) - movdqa %xmm1, 16(%rbx) - movdqa %xmm2, 32(%rbx) - movdqa %xmm3, 48(%rbx) - movdqa %xmm4, 64(%rbx) - movdqa %xmm0, 80(%rbx) - movdqa %xmm1, 96(%rbx) - movdqa %xmm2, 112(%rbx) - movdqa %xmm3, 128(%rbx) - movdqa %xmm4, 144(%rbx) - movdqa %xmm0, 160(%rbx) - movdqa %xmm1, 176(%rbx) - movdqa %xmm2, 192(%rbx) - movdqa %xmm3, 208(%rbx) - movdqa %xmm4, 224(%rbx) - movdqa %xmm0, 240(%rbx) - movdqa %xmm0, 256(%rbx) - movdqa %xmm1, 272(%rbx) - jmp .fastkdf_loop - -.fastkdf_mode_one: - movl $32, 56(%rsp) - movdqa 0(%rsi), %xmm0 - movdqa 16(%rsi), %xmm1 - movdqa 32(%rsi), %xmm2 - movdqa 48(%rsi), %xmm3 - movdqa 64(%rsi), %xmm4 - movdqa 80(%rsi), %xmm5 - movdqa 96(%rsi), %xmm6 - movdqa 112(%rsi), %xmm7 - movdqa 128(%rsi), %xmm8 - movdqa 144(%rsi), %xmm9 - movdqa 160(%rsi), %xmm10 - movdqa 176(%rsi), %xmm11 - movdqa 192(%rsi), %xmm12 - movdqa 208(%rsi), %xmm13 - movdqa 224(%rsi), %xmm14 - movdqa 240(%rsi), %xmm15 - movdqa %xmm0, 0(%rbx) - movdqa %xmm1, 16(%rbx) - movdqa %xmm2, 32(%rbx) - movdqa %xmm3, 48(%rbx) - movdqa %xmm4, 64(%rbx) - movdqa %xmm5, 80(%rbx) - movdqa %xmm6, 96(%rbx) - movdqa %xmm7, 112(%rbx) - movdqa %xmm8, 128(%rbx) - movdqa %xmm9, 144(%rbx) - movdqa %xmm10, 160(%rbx) - movdqa %xmm11, 176(%rbx) - movdqa %xmm12, 192(%rbx) - movdqa %xmm13, 208(%rbx) - movdqa %xmm14, 224(%rbx) - movdqa %xmm15, 240(%rbx) - movdqa %xmm0, 256(%rbx) - movdqa %xmm1, 272(%rbx) - -.fastkdf_loop: - leaq 0(%r12, %r13), %rbp - leaq 320(%r12, %r13), %rbx - pxor %xmm5, %xmm5 - - movq $0xBB67AE856B08C647, %r8 - movq %r8, 0(%r14) - movq $0xA54FF53A3C6EF372, %r9 - movq %r9, 8(%r14) - movq $0x9B05688C510E527F, %r10 - movq %r10, 16(%r14) - movq $0x5BE0CD191F83D9AB, %r11 - movq %r11, 24(%r14) - movdqa %xmm5, 32(%r14) - movl $64, 32(%r14) - - movdqu 0(%rbx), %xmm0 - movdqu 16(%rbx), %xmm1 - movdqa %xmm0, 48(%r14) - movdqa %xmm1, 64(%r14) - movdqa %xmm5, 80(%r14) - movdqa %xmm5, 96(%r14) - -#ifdef WIN64 - movq %r14, %rcx -#else - movq %r14, %rdi -#endif - call blake2s_compress - - movdqu 0(%rbp), %xmm0 - movdqu 16(%rbp), %xmm1 - movdqu 32(%rbp), %xmm2 - movdqu 48(%rbp), %xmm3 - movdqa %xmm0, 48(%r14) - movdqa %xmm1, 64(%r14) - movdqa %xmm2, 80(%r14) - movdqa %xmm3, 96(%r14) - - movl $128, 32(%r14) - movl $0xFFFFFFFF, 40(%r14) - -#ifdef WIN64 - movq %r14, %rcx -#else - movq %r14, %rdi -#endif - call blake2s_compress - - pxor %xmm5, %xmm5 - movdqa 0(%r14), %xmm0 - movdqa 16(%r14), %xmm1 - movdqa %xmm0, %xmm2 - movdqa %xmm1, %xmm3 - paddb %xmm1, %xmm0 - psadbw %xmm5, %xmm0 - movhlps %xmm0, %xmm1 - paddq %xmm1, %xmm0 -#ifndef MOVQ_FIX - movq %xmm0, %r13 -#else - movq %xmm0, 0(%r14) - movq 0(%r14), %r13 -#endif - andq $0xFF, %r13 - leaq 320(%r12, %r13), %rbx - movdqu 0(%rbx), %xmm0 - movdqu 16(%rbx), %xmm1 - pxor %xmm2, %xmm0 - pxor %xmm3, %xmm1 - movdqu %xmm0, 0(%rbx) - movdqu %xmm1, 16(%rbx) - -/* tail update */ - movq $32, %rdx - cmpq %r13, %rdx - jc .fastkdf_headupd -#ifdef WIN64 - movq %rdx, %r8 - leaq 256(%rbx), %rcx - movq %rbx, %rdx - subq %r13, %r8 -#else - leaq 256(%rbx), %rdi - movq %rbx, %rsi - subq %r13, %rdx -#endif - call neoscrypt_copy - jmp .fastkdf_loop_end - -/* head update */ -.fastkdf_headupd: - movq $224, %rdx - cmpq %r13, %rdx - jnc .fastkdf_loop_end - movq %r13, %rax - subq %rdx, %rax -#ifdef WIN64 - leaq 320(%r12), %rcx - leaq 576(%r12), %rdx - movq %rax, %r8 -#else - leaq 320(%r12), %rdi - leaq 576(%r12), %rsi - movq %rax, %rdx -#endif - call neoscrypt_copy - -.fastkdf_loop_end: - decq %r15 - jnz .fastkdf_loop - - movq 48(%rsp), %r14 - movq 56(%rsp), %r15 - movq $256, %rbp - subq %r13, %rbp - cmpq %r15, %rbp - jc .fastkdf_crosscopy - - leaq 320(%r12, %r13), %rbp -#ifdef WIN64 - movq %rbp, %rcx - movq %r12, %rdx - movq %r15, %r8 -#else - movq %rbp, %rdi - movq %r12, %rsi - movq %r15, %rdx -#endif - call neoscrypt_xor -#ifdef WIN64 - movq %r14, %rcx - movq %rbp, %rdx - movq %r15, %r8 -#else - movq %r14, %rdi - movq %rbp, %rsi - movq %r15, %rdx -#endif - call neoscrypt_copy - jmp .fastkdf_finish - -.fastkdf_crosscopy: - leaq 320(%r12, %r13), %rbx -#ifdef WIN64 - movq %rbx, %rcx - movq %r12, %rdx - movq %rbp, %r8 -#else - movq %rbx, %rdi - movq %r12, %rsi - movq %rbp, %rdx -#endif - call neoscrypt_xor - leaq 320(%r12), %rdi - leaq 0(%r12, %rbp), %rsi -#ifdef WIN64 - movq %rdi, %rcx - movq %rsi, %rdx - movq %r15, %r8 - subq %rbp, %r8 -#else - movq %r15, %rdx - subq %rbp, %rdx -#endif - call neoscrypt_xor -#ifdef WIN64 - movq %r14, %rcx - movq %rbx, %rdx - movq %rbp, %r8 -#else - movq %r14, %rdi - movq %rbx, %rsi - movq %rbp, %rdx -#endif - call neoscrypt_copy -#ifdef WIN64 - leaq 0(%r14, %rbp), %rcx - leaq 320(%r12), %rdx - movq %r15, %r8 - subq %rbp, %r8 -#else - leaq 0(%r14, %rbp), %rdi - leaq 320(%r12), %rsi - movq %r15, %rdx - subq %rbp, %rdx -#endif - call neoscrypt_copy - -.fastkdf_finish: - addq $848, %rsp - -#ifdef WIN64 - movdqu 0(%rsp), %xmm15 - movdqu 16(%rsp), %xmm14 - movdqu 32(%rsp), %xmm13 - movdqu 48(%rsp), %xmm12 - movdqu 64(%rsp), %xmm11 - movdqu 80(%rsp), %xmm10 - movdqu 96(%rsp), %xmm9 - movdqu 112(%rsp), %xmm8 - movdqu 128(%rsp), %xmm7 - movdqu 144(%rsp), %xmm6 - addq $160, %rsp - popq %rsi - popq %rdi -#endif - popq %r15 - popq %r14 - popq %r13 - popq %r12 - popq %rbp - popq %rbx - ret - - -/* neoscrypt_salsa_tangle(mem, count) - * AMD64 (SSE2) Salsa20 map switcher; - * correct map: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - * SSE2 map: 0 5 10 15 12 1 6 11 8 13 2 7 4 9 14 3 - * NOTE: arguments passed in %r8 and %r9; %rbx not preserved */ -neoscrypt_salsa_tangle_sse2: -.salsa_tangle_sse2: - movl 4(%r8), %eax - movl 20(%r8), %ebx - movl 8(%r8), %ecx - movl 40(%r8), %edx - movl %eax, 20(%r8) - movl %ebx, 4(%r8) - movl %ecx, 40(%r8) - movl %edx, 8(%r8) - movl 12(%r8), %eax - movl 60(%r8), %ebx - movl 16(%r8), %ecx - movl 48(%r8), %edx - movl %eax, 60(%r8) - movl %ebx, 12(%r8) - movl %ecx, 48(%r8) - movl %edx, 16(%r8) - movl 28(%r8), %eax - movl 44(%r8), %ebx - movl 36(%r8), %ecx - movl 52(%r8), %edx - movl %eax, 44(%r8) - movl %ebx, 28(%r8) - movl %ecx, 52(%r8) - movl %edx, 36(%r8) - addq $64, %r8 - decq %r9 - jnz .salsa_tangle_sse2 - - ret - - -/* neoscrypt_xor_salsa_sse2(mem, xormem, double_rounds) - * AMD64 (SSE2) Salsa20 with XOR; - * mem and xormem must be aligned properly; - * NOTE: arguments passed in %r8, %r9, %r10 */ -neoscrypt_xor_salsa_sse2: - movdqa 0(%r8), %xmm0 - movdqa 16(%r8), %xmm1 - movdqa 32(%r8), %xmm2 - movdqa 48(%r8), %xmm3 - pxor 0(%r9), %xmm0 - pxor 16(%r9), %xmm1 - pxor 32(%r9), %xmm2 - pxor 48(%r9), %xmm3 - movdqa %xmm0, %xmm12 - movdqa %xmm1, %xmm13 - movdqa %xmm2, %xmm14 - movdqa %xmm3, %xmm15 -.xor_salsa_sse2: - movdqa %xmm1, %xmm4 - paddd %xmm0, %xmm4 - movdqa %xmm4, %xmm5 - pslld $7, %xmm4 - psrld $25, %xmm5 - pxor %xmm4, %xmm3 - movdqa %xmm0, %xmm4 - pxor %xmm5, %xmm3 - paddd %xmm3, %xmm4 - movdqa %xmm4, %xmm5 - pslld $9, %xmm4 - psrld $23, %xmm5 - pxor %xmm4, %xmm2 - movdqa %xmm3, %xmm4 - pxor %xmm5, %xmm2 - pshufd $0x93, %xmm3, %xmm3 - paddd %xmm2, %xmm4 - movdqa %xmm4, %xmm5 - pslld $13, %xmm4 - psrld $19, %xmm5 - pxor %xmm4, %xmm1 - movdqa %xmm2, %xmm4 - pxor %xmm5, %xmm1 - pshufd $0x4E, %xmm2, %xmm2 - paddd %xmm1, %xmm4 - movdqa %xmm4, %xmm5 - pslld $18, %xmm4 - psrld $14, %xmm5 - pxor %xmm4, %xmm0 - movdqa %xmm3, %xmm4 - pxor %xmm5, %xmm0 - pshufd $0x39, %xmm1, %xmm1 - paddd %xmm0, %xmm4 - movdqa %xmm4, %xmm5 - pslld $7, %xmm4 - psrld $25, %xmm5 - pxor %xmm4, %xmm1 - movdqa %xmm0, %xmm4 - pxor %xmm5, %xmm1 - paddd %xmm1, %xmm4 - movdqa %xmm4, %xmm5 - pslld $9, %xmm4 - psrld $23, %xmm5 - pxor %xmm4, %xmm2 - movdqa %xmm1, %xmm4 - pxor %xmm5, %xmm2 - pshufd $0x93, %xmm1, %xmm1 - paddd %xmm2, %xmm4 - movdqa %xmm4, %xmm5 - pslld $13, %xmm4 - psrld $19, %xmm5 - pxor %xmm4, %xmm3 - movdqa %xmm2, %xmm4 - pxor %xmm5, %xmm3 - pshufd $0x4E, %xmm2, %xmm2 - paddd %xmm3, %xmm4 - movdqa %xmm4, %xmm5 - pslld $18, %xmm4 - psrld $14, %xmm5 - pxor %xmm4, %xmm0 - pshufd $0x39, %xmm3, %xmm3 - pxor %xmm5, %xmm0 - decq %r10 - jnz .xor_salsa_sse2 - - paddd %xmm12, %xmm0 - paddd %xmm13, %xmm1 - paddd %xmm14, %xmm2 - paddd %xmm15, %xmm3 - movdqa %xmm0, 0(%r8) - movdqa %xmm1, 16(%r8) - movdqa %xmm2, 32(%r8) - movdqa %xmm3, 48(%r8) - - ret - - -/* neoscrypt_xor_chacha_sse2(mem, xormem, double_rounds) - * AMD64 (SSE2) ChaCha20 with XOR; - * mem and xormem must be aligned properly; - * NOTE: arguments passed in %r8, %r9, %r10 */ -neoscrypt_xor_chacha_sse2: - movdqa 0(%r8), %xmm0 - movdqa 16(%r8), %xmm1 - movdqa 32(%r8), %xmm2 - movdqa 48(%r8), %xmm3 - pxor 0(%r9), %xmm0 - pxor 16(%r9), %xmm1 - pxor 32(%r9), %xmm2 - pxor 48(%r9), %xmm3 - movdqa %xmm0, %xmm12 - movdqa %xmm1, %xmm13 - movdqa %xmm2, %xmm14 - movdqa %xmm3, %xmm15 -.xor_chacha_sse2: - paddd %xmm1, %xmm0 - pxor %xmm0, %xmm3 - pshuflw $0xB1, %xmm3, %xmm3 - pshufhw $0xB1, %xmm3, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm4 - pslld $12, %xmm1 - psrld $20, %xmm4 - pxor %xmm4, %xmm1 - paddd %xmm1, %xmm0 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm4 - pslld $8, %xmm3 - psrld $24, %xmm4 - pxor %xmm4, %xmm3 - pshufd $0x93, %xmm0, %xmm0 - paddd %xmm3, %xmm2 - pshufd $0x4E, %xmm3, %xmm3 - pxor %xmm2, %xmm1 - pshufd $0x39, %xmm2, %xmm2 - movdqa %xmm1, %xmm4 - pslld $7, %xmm1 - psrld $25, %xmm4 - pxor %xmm4, %xmm1 - paddd %xmm1, %xmm0 - pxor %xmm0, %xmm3 - pshuflw $0xB1, %xmm3, %xmm3 - pshufhw $0xB1, %xmm3, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm4 - pslld $12, %xmm1 - psrld $20, %xmm4 - pxor %xmm4, %xmm1 - paddd %xmm1, %xmm0 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm4 - pslld $8, %xmm3 - psrld $24, %xmm4 - pxor %xmm4, %xmm3 - pshufd $0x39, %xmm0, %xmm0 - paddd %xmm3, %xmm2 - pshufd $0x4E, %xmm3, %xmm3 - pxor %xmm2, %xmm1 - pshufd $0x93, %xmm2, %xmm2 - movdqa %xmm1, %xmm4 - pslld $7, %xmm1 - psrld $25, %xmm4 - pxor %xmm4, %xmm1 - decq %r10 - jnz .xor_chacha_sse2 - - paddd %xmm12, %xmm0 - paddd %xmm13, %xmm1 - paddd %xmm14, %xmm2 - paddd %xmm15, %xmm3 - movdqa %xmm0, 0(%r8) - movdqa %xmm1, 16(%r8) - movdqa %xmm2, 32(%r8) - movdqa %xmm3, 48(%r8) - - ret - - -/* neoscrypt_xor_salsa(mem, xormem, tempmem, double_rounds) - * AMD64 (INT) Salsa20 with XOR (SSE2 support required); - * NOTE: arguments passed in %r8, %r9, %r10, %r11 */ -neoscrypt_xor_salsa: -/* XOR and copy to temporary memory */ - movdqa 0(%r8), %xmm0 - movdqa 16(%r8), %xmm1 - movdqa 32(%r8), %xmm2 - movdqa 48(%r8), %xmm3 - pxor 0(%r9), %xmm0 - pxor 16(%r9), %xmm1 - pxor 32(%r9), %xmm2 - pxor 48(%r9), %xmm3 - movdqa %xmm0, 0(%r10) - movdqa %xmm1, 16(%r10) - movdqa %xmm2, 32(%r10) - movdqa %xmm3, 48(%r10) - movdqa %xmm0, %xmm12 - movdqa %xmm1, %xmm13 - movdqa %xmm2, %xmm14 - movdqa %xmm3, %xmm15 -.xor_salsa: -/* quarters A and B, initial C and D */ - movl 0(%r10), %eax /* A: load a */ - movl 20(%r10), %ebx /* B: load a */ - addl 48(%r10), %eax /* A: t = a + d */ - addl 4(%r10), %ebx /* B: t = a + d */ - roll $7, %eax /* A: rotate t */ - roll $7, %ebx /* B: rotate t */ - xorl 16(%r10), %eax /* A: b = b ^ t */ - xorl 36(%r10), %ebx /* B: b = b ^ t */ - movl %eax, %esi /* A: copy b */ - movl %ebx, %edi /* B: copy b */ - movl %esi, 16(%r10) /* A: store b */ - movl %edi, 36(%r10) /* B: store b */ - addl 0(%r10), %eax /* A: t = b + a */ - addl 20(%r10), %ebx /* B: t = b + a */ - roll $9, %eax /* A: rotate t */ - roll $9, %ebx /* B: rotate t */ - xorl 32(%r10), %eax /* A: c = c ^ t */ - xorl 52(%r10), %ebx /* B: c = c ^ t */ - movl %eax, %ecx /* A: copy c */ - movl %ebx, %edx /* B: copy c */ - movl %ecx, 32(%r10) /* A: store c */ - movl %edx, 52(%r10) /* B: store c */ - addl %esi, %eax /* A: t = c + b */ - addl %edi, %ebx /* B: t = c + b */ - roll $13, %eax /* A: rotate t */ - roll $13, %ebx /* B: rotate t */ - xorl 48(%r10), %eax /* A: d = d ^ t */ - xorl 4(%r10), %ebx /* B: d = d ^ t */ - movl %eax, 48(%r10) /* A: store d */ - movl %ebx, 4(%r10) /* B: store d */ - addl %eax, %ecx /* A: t = d + c */ - movl 40(%r10), %eax /* C: load a */ - addl %ebx, %edx /* B: t = d + c */ - movl 60(%r10), %ebx /* D: load a */ - roll $18, %ecx /* A: rotate t */ - addl 24(%r10), %eax /* C: t = a + d */ - roll $18, %edx /* B: rotate t */ - addl 44(%r10), %ebx /* D: t = a + d */ - xorl 0(%r10), %ecx /* A: a = a ^ t */ - roll $7, %eax /* C: rotate t */ - xorl 20(%r10), %edx /* B: a = a ^ t */ - roll $7, %ebx /* D: rotate t */ - movl %ecx, 0(%r10) /* A: store a */ - movl %edx, 20(%r10) /* B: store a */ -/* quarters C and D, initial E and F */ - xorl 56(%r10), %eax /* C: b = b ^ t */ - xorl 12(%r10), %ebx /* D: b = b ^ t */ - movl %eax, %esi /* C: copy b */ - movl %ebx, %edi /* D: copy b */ - movl %esi, 56(%r10) /* C: store b */ - movl %edi, 12(%r10) /* D: store b */ - addl 40(%r10), %eax /* C: t = b + a */ - addl 60(%r10), %ebx /* D: t = b + a */ - roll $9, %eax /* C: rotate t */ - roll $9, %ebx /* D: rotate t */ - xorl 8(%r10), %eax /* C: c = c ^ t */ - xorl 28(%r10), %ebx /* D: c = c ^ t */ - movl %eax, %ecx /* C: copy c */ - movl %ebx, %edx /* D: copy c */ - movl %ecx, 8(%r10) /* C: store c */ - movl %edx, 28(%r10) /* D: store c */ - addl %esi, %eax /* C: t = c + b */ - addl %edi, %ebx /* D: t = c + b */ - roll $13, %eax /* C: rotate t */ - roll $13, %ebx /* D: rotate t */ - xorl 24(%r10), %eax /* C: d = d ^ t */ - xorl 44(%r10), %ebx /* D: d = d ^ t */ - movl %eax, 24(%r10) /* C: store d */ - movl %ebx, 44(%r10) /* D: store d */ - addl %eax, %ecx /* C: t = d + c */ - movl 0(%r10), %eax /* E: load a */ - addl %ebx, %edx /* D: t = d + c */ - movl 20(%r10), %ebx /* F: load a */ - roll $18, %ecx /* C: rotate t */ - addl 12(%r10), %eax /* E: t = a + d */ - roll $18, %edx /* D: rotate t */ - addl 16(%r10), %ebx /* F: t = a + d */ - xorl 40(%r10), %ecx /* C: a = a ^ t */ - roll $7, %eax /* E: rotate t */ - xorl 60(%r10), %edx /* D: a = a ^ t */ - roll $7, %ebx /* F: rotate t */ - movl %ecx, 40(%r10) /* C: store a */ - movl %edx, 60(%r10) /* D: store a */ -/* quarters E and F, initial G and H */ - xorl 4(%r10), %eax /* E: b = b ^ t */ - xorl 24(%r10), %ebx /* F: b = b ^ t */ - movl %eax, %esi /* E: copy b */ - movl %ebx, %edi /* F: copy b */ - movl %esi, 4(%r10) /* E: store b */ - movl %edi, 24(%r10) /* F: store b */ - addl 0(%r10), %eax /* E: t = b + a */ - addl 20(%r10), %ebx /* F: t = b + a */ - roll $9, %eax /* E: rotate t */ - roll $9, %ebx /* F: rotate t */ - xorl 8(%r10), %eax /* E: c = c ^ t */ - xorl 28(%r10), %ebx /* F: c = c ^ t */ - movl %eax, %ecx /* E: copy c */ - movl %ebx, %edx /* F: copy c */ - movl %ecx, 8(%r10) /* E: store c */ - movl %edx, 28(%r10) /* F: store c */ - addl %esi, %eax /* E: t = c + b */ - addl %edi, %ebx /* F: t = c + b */ - roll $13, %eax /* E: rotate t */ - roll $13, %ebx /* F: rotate t */ - xorl 12(%r10), %eax /* E: d = d ^ t */ - xorl 16(%r10), %ebx /* F: d = d ^ t */ - movl %eax, 12(%r10) /* E: store d */ - movl %ebx, 16(%r10) /* F: store d */ - addl %eax, %ecx /* E: t = d + c */ - movl 40(%r10), %eax /* G: load a */ - addl %ebx, %edx /* F: t = d + c */ - movl 60(%r10), %ebx /* H: load a */ - roll $18, %ecx /* E: rotate t */ - addl 36(%r10), %eax /* G: t = a + d */ - roll $18, %edx /* F: rotate t */ - addl 56(%r10), %ebx /* H: t = a + d */ - xorl 0(%r10), %ecx /* E: a = a ^ t */ - roll $7, %eax /* G: rotate t */ - xorl 20(%r10), %edx /* F: a = a ^ t */ - roll $7, %ebx /* H: rotate t */ - movl %ecx, 0(%r10) /* E: store a */ - movl %edx, 20(%r10) /* F: store a */ -/* quarters G and H */ - xorl 44(%r10), %eax /* G: b = b ^ t */ - xorl 48(%r10), %ebx /* H: b = b ^ t */ - movl %eax, %esi /* G: copy b */ - movl %ebx, %edi /* H: copy b */ - movl %esi, 44(%r10) /* G: store b */ - movl %edi, 48(%r10) /* H: store b */ - addl 40(%r10), %eax /* G: t = b + a */ - addl 60(%r10), %ebx /* H: t = b + a */ - roll $9, %eax /* G: rotate t */ - roll $9, %ebx /* H: rotate t */ - xorl 32(%r10), %eax /* G: c = c ^ t */ - xorl 52(%r10), %ebx /* H: c = c ^ t */ - movl %eax, %ecx /* G: copy c */ - movl %ebx, %edx /* H: copy c */ - movl %ecx, 32(%r10) /* G: store c */ - movl %edx, 52(%r10) /* H: store c */ - addl %esi, %eax /* G: t = c + b */ - addl %edi, %ebx /* H: t = c + b */ - roll $13, %eax /* G: rotate t */ - roll $13, %ebx /* H: rotate t */ - xorl 36(%r10), %eax /* G: d = d ^ t */ - xorl 56(%r10), %ebx /* H: d = d ^ t */ - movl %eax, 36(%r10) /* G: store d */ - movl %ebx, 56(%r10) /* H: store d */ - addl %eax, %ecx /* G: t = d + c */ - addl %ebx, %edx /* H: t = d + c */ - roll $18, %ecx /* G: rotate t */ - roll $18, %edx /* H: rotate t */ - xorl 40(%r10), %ecx /* G: a = a ^ t */ - xorl 60(%r10), %edx /* H: a = a ^ t */ - movl %ecx, 40(%r10) /* G: store a */ - movl %edx, 60(%r10) /* H: store a */ - decq %r11 - jnz .xor_salsa - - movdqa 0(%r10), %xmm0 - movdqa 16(%r10), %xmm1 - movdqa 32(%r10), %xmm2 - movdqa 48(%r10), %xmm3 - paddd %xmm12, %xmm0 - paddd %xmm13, %xmm1 - paddd %xmm14, %xmm2 - paddd %xmm15, %xmm3 - movdqa %xmm0, 0(%r8) - movdqa %xmm1, 16(%r8) - movdqa %xmm2, 32(%r8) - movdqa %xmm3, 48(%r8) - - ret - - -/* neoscrypt_xor_chacha(mem, xormem, tempmem, double_rounds) - * AMD64 (INT) ChaCha20 with XOR (SSE2 support required); - * NOTE: arguments passed in %r8, %r9, %r10, %r11 */ -neoscrypt_xor_chacha: -/* XOR and copy to temporary memory */ - movdqa 0(%r8), %xmm0 - movdqa 16(%r8), %xmm1 - movdqa 32(%r8), %xmm2 - movdqa 48(%r8), %xmm3 - pxor 0(%r9), %xmm0 - pxor 16(%r9), %xmm1 - pxor 32(%r9), %xmm2 - pxor 48(%r9), %xmm3 - movdqa %xmm0, 0(%r10) - movdqa %xmm1, 16(%r10) - movdqa %xmm2, 32(%r10) - movdqa %xmm3, 48(%r10) - movdqa %xmm0, %xmm12 - movdqa %xmm1, %xmm13 - movdqa %xmm2, %xmm14 - movdqa %xmm3, %xmm15 -.xor_chacha: -/* quarters A and B, initial C */ - movl 0(%r10), %eax /* A: load a */ - movl 16(%r10), %ebx /* A: load b */ - addl %ebx, %eax /* A: a = a + b */ - movl 32(%r10), %ecx /* A: load c */ - movl 48(%r10), %edx /* A: load d */ - xorl %eax, %edx /* A: d = d ^ a */ - movl 4(%r10), %edi /* B: load a */ - roll $16, %edx /* A: rotate d */ - movl 20(%r10), %esi /* B: load b */ - addl %edx, %ecx /* A: c = c + d */ - xorl %ecx, %ebx /* A: b = b ^ c */ - addl %esi, %edi /* B: a = a + b */ - roll $12, %ebx /* A: rotate b */ - addl %ebx, %eax /* A: a = a + b */ - movl %eax, 0(%r10) /* A: store a */ - xorl %eax, %edx /* A: d = d ^ a */ - movl 52(%r10), %eax /* B: load d */ - roll $8, %edx /* A: rotate d */ - xorl %edi, %eax /* B: d = d ^ a */ - movl %edx, 48(%r10) /* A: store d */ - addl %edx, %ecx /* A: c = c + d */ - movl 36(%r10), %edx /* B: load c */ - movl %ecx, 32(%r10) /* A: store c */ - xorl %ecx, %ebx /* A: b = b ^ c */ - roll $16, %eax /* B: rotate d */ - movl 40(%r10), %ecx /* C: load c */ - roll $7, %ebx /* A: rotate b */ - addl %eax, %edx /* B: c = c + d */ - movl %ebx, 16(%r10) /* A: store b */ - xorl %edx, %esi /* B: b = b ^ c */ - movl 24(%r10), %ebx /* C: load b */ - roll $12, %esi /* B: rotate b */ - addl %esi, %edi /* B: a = a + b */ - movl %edi, 4(%r10) /* B: store a */ - xorl %edi, %eax /* B: d = d ^ a */ - roll $8, %eax /* B: rotate d */ - movl %eax, 52(%r10) /* B: store d */ - addl %eax, %edx /* B: c = c + d */ - movl 8(%r10), %eax /* C: load a */ - movl %edx, 36(%r10) /* B: store c */ - xorl %edx, %esi /* B: b = b ^ c */ - movl 56(%r10), %edx /* C: load d */ - roll $7, %esi /* B: rotate b */ - addl %ebx, %eax /* C: a = a + b */ - movl %esi, 20(%r10) /* B: store b */ -/* quarters C and D, initial E */ - xorl %eax, %edx /* C: d = d ^ a */ - movl 12(%r10), %edi /* D: load a */ - roll $16, %edx /* C: rotate d */ - movl 28(%r10), %esi /* D: load b */ - addl %edx, %ecx /* C: c = c + d */ - xorl %ecx, %ebx /* C: b = b ^ c */ - addl %esi, %edi /* D: a = a + b */ - roll $12, %ebx /* C: rotate b */ - addl %ebx, %eax /* C: a = a + b */ - movl %eax, 8(%r10) /* C: store a */ - xorl %eax, %edx /* C: d = d ^ a */ - movl 60(%r10), %eax /* D: load d */ - roll $8, %edx /* C: rotate d */ - xorl %edi, %eax /* D: d = d ^ a */ - movl %edx, 56(%r10) /* C: store d */ - addl %edx, %ecx /* C: c = c + d */ - movl 44(%r10), %edx /* D: load c */ - movl %ecx, 40(%r10) /* C: store c */ - xorl %ecx, %ebx /* C: b = b ^ c */ - roll $16, %eax /* D: rotate d */ - movl 40(%r10), %ecx /* E: load c */ - roll $7, %ebx /* C: rotate b */ - addl %eax, %edx /* D: c = c + d */ - movl %ebx, 24(%r10) /* C: store b */ - xorl %edx, %esi /* D: b = b ^ c */ - movl 20(%r10), %ebx /* E: load b */ - roll $12, %esi /* D: rotate b */ - addl %esi, %edi /* D: a = a + b */ - movl %edi, 12(%r10) /* D: store a */ - xorl %edi, %eax /* D: d = d ^ a */ - roll $8, %eax /* D: rotate d */ - movl %eax, 60(%r10) /* D: store d */ - addl %eax, %edx /* D: c = c + d */ - movl 0(%r10), %eax /* E: load a */ - movl %edx, 44(%r10) /* D: store c */ - xorl %edx, %esi /* D: b = b ^ c */ - movl 60(%r10), %edx /* E: load d */ - roll $7, %esi /* D: rotate b */ - addl %ebx, %eax /* E: a = a + b */ - movl %esi, 28(%r10) /* D: store b */ -/* quarters E and F, initial G */ - xorl %eax, %edx /* E: d = d ^ a */ - movl 4(%r10), %edi /* F: load a */ - roll $16, %edx /* E: rotate d */ - movl 24(%r10), %esi /* F: load b */ - addl %edx, %ecx /* E: c = c + d */ - xorl %ecx, %ebx /* E: b = b ^ c */ - addl %esi, %edi /* F: a = a + b */ - roll $12, %ebx /* E: rotate b */ - addl %ebx, %eax /* E: a = a + b */ - movl %eax, 0(%r10) /* E: store a */ - xorl %eax, %edx /* E: d = d ^ a */ - movl 48(%r10), %eax /* F: load d */ - roll $8, %edx /* E: rotate d */ - xorl %edi, %eax /* F: d = d ^ a */ - movl %edx, 60(%r10) /* E: store d */ - addl %edx, %ecx /* E: c = c + d */ - movl 44(%r10), %edx /* F: load c */ - movl %ecx, 40(%r10) /* E: store c */ - xorl %ecx, %ebx /* E: b = b ^ c */ - roll $16, %eax /* F: rotate d */ - movl 32(%r10), %ecx /* G: load c */ - roll $7, %ebx /* E: rotate b */ - addl %eax, %edx /* F: c = c + d */ - movl %ebx, 20(%r10) /* E: store b */ - xorl %edx, %esi /* F: b = b ^ c */ - movl 28(%r10), %ebx /* G: load b */ - roll $12, %esi /* F: rotate b */ - addl %esi, %edi /* F: a = a + b */ - movl %edi, 4(%r10) /* F: store a */ - xorl %edi, %eax /* F: d = d ^ a */ - roll $8, %eax /* F: rotate d */ - movl %eax, 48(%r10) /* F: store d */ - addl %eax, %edx /* F: c = c + d */ - movl 8(%r10), %eax /* G: load a */ - movl %edx, 44(%r10) /* F: store c */ - xorl %edx, %esi /* F: b = b ^ c */ - movl 52(%r10), %edx /* G: load d */ - roll $7, %esi /* F: rotate b */ - addl %ebx, %eax /* G: a = a + b */ - movl %esi, 24(%r10) /* F: store b */ -/* quarters G and H */ - xorl %eax, %edx /* G: d = d ^ a */ - movl 12(%r10), %edi /* H: load a */ - roll $16, %edx /* G: rotate d */ - movl 16(%r10), %esi /* H: load b */ - addl %edx, %ecx /* G: c = c + d */ - xorl %ecx, %ebx /* G: b = b ^ c */ - addl %esi, %edi /* H: a = a + b */ - roll $12, %ebx /* G: rotate b */ - addl %ebx, %eax /* G: a = a + b */ - movl %eax, 8(%r10) /* G: store a */ - xorl %eax, %edx /* G: d = d ^ a */ - movl 56(%r10), %eax /* H: load d */ - roll $8, %edx /* G: rotate d */ - xorl %edi, %eax /* H: d = d ^ a */ - movl %edx, 52(%r10) /* G: store d */ - addl %edx, %ecx /* G: c = c + d */ - movl 36(%r10), %edx /* H: load c */ - movl %ecx, 32(%r10) /* G: store c */ - xorl %ecx, %ebx /* G: b = b ^ c */ - roll $16, %eax /* H: rotate d */ - roll $7, %ebx /* G: rotate b */ - addl %eax, %edx /* H: c = c + d */ - movl %ebx, 28(%r10) /* G: store b */ - xorl %edx, %esi /* H: b = b ^ c */ - roll $12, %esi /* H: rotate b */ - addl %esi, %edi /* H: a = a + b */ - movl %edi, 12(%r10) /* H: store a */ - xorl %edi, %eax /* H: d = d ^ a */ - roll $8, %eax /* H: rotate d */ - movl %eax, 56(%r10) /* H: store d */ - addl %eax, %edx /* H: c = c + d */ - movl %edx, 36(%r10) /* H: store c */ - xorl %edx, %esi /* H: b = b ^ c */ - roll $7, %esi /* H: rotate b */ - movl %esi, 16(%r10) /* H: store b */ - decq %r11 - jnz .xor_chacha - - movdqa 0(%r10), %xmm0 - movdqa 16(%r10), %xmm1 - movdqa 32(%r10), %xmm2 - movdqa 48(%r10), %xmm3 - paddd %xmm12, %xmm0 - paddd %xmm13, %xmm1 - paddd %xmm14, %xmm2 - paddd %xmm15, %xmm3 - movdqa %xmm0, 0(%r8) - movdqa %xmm1, 16(%r8) - movdqa %xmm2, 32(%r8) - movdqa %xmm3, 48(%r8) - - ret - - -/* neoscrypt(input, output, profile) - * AMD64 (INT, SSE2) NeoScrypt engine (SSE2 required for INT); - * supports NeoScrypt and Scrypt only */ -.globl neoscrypt -.globl _neoscrypt -neoscrypt: -_neoscrypt: -#ifdef WIN64 - pushq %rdi - pushq %rsi - movq %rcx, %rdi - movq %rdx, %rsi - movq %r8, %rdx -#endif - pushq %rbx - pushq %rbp - pushq %r12 - pushq %r13 - pushq %r14 - pushq %r15 -/* save input, output and profile */ - movq %rdi, %r14 - movq %rsi, %r15 - movq %rdx, %rbx - -#ifdef SHA256 -/* Scrypt mode */ - testl $0x01, %ebx - jnz .scrypt -#endif - -#ifdef WIN64 -/* attempt to allocate 33280 + 128 bytes of stack space fails miserably; - * have to use malloc() and free() instead */ - subq $128, %rsp -/* allocate memory (9 pages of 4Kb each) */ - movq $0x9000, %rcx - call malloc -/* save memory address */ - movq %rax, 64(%rsp) -/* align memory */ - addq $64, %rax - andq $0xFFFFFFFFFFFFFFC0, %rax -/* memory base: X, Z, V */ - leaq 64(%rax), %rbp -#else -/* align stack */ - movq %rsp, %rax - andq $0xFFFFFFFFFFFFFFC0, %rsp - subq $0x8280, %rsp -/* save unaligned stack */ - movq %rax, 32(%rsp) -/* memory base: X, Z, V */ - leaq 128(%rsp), %rbp -#endif /* WIN64 */ - -/* FastKDF */ -#ifdef WIN64 -#ifdef OPT - movq %r14, %rcx - movq %r14, %rdx - movq %rbp, %r8 - xorq %r9, %r9 - call neoscrypt_fastkdf_opt -#else - movq $80, %rax - movq %r14, %rcx - movq %rax, %rdx - movq %r14, %r8 - movq %rax, %r9 - movq $32, 32(%rsp) - movq %rbp, 40(%rsp) - movq $256, 48(%rsp) - call neoscrypt_fastkdf -#endif /* OPT */ -#else -#ifdef OPT - movq %r14, %rdi - movq %r14, %rsi - movq %rbp, %rdx - xorq %rcx, %rcx -#ifdef __APPLE__ - call _neoscrypt_fastkdf_opt -#else - call neoscrypt_fastkdf_opt -#endif /* __APPLE__ */ -#else - movq $80, %rax - movq %r14, %rdi - movq %rax, %rsi - movq %r14, %rdx - movq %rax, %rcx - movq $32, %r8 - movq %rbp, %r9 - movq $256, 0(%rsp) -#ifdef __APPLE__ - call _neoscrypt_fastkdf -#else - call neoscrypt_fastkdf -#endif /* __APPLE__ */ -#endif /* OPT */ -#endif /* WIN64 */ - -/* blkcpy(Z, X) */ - leaq 256(%rbp), %rax - movdqa 0(%rbp), %xmm0 - movdqa 16(%rbp), %xmm1 - movdqa 32(%rbp), %xmm2 - movdqa 48(%rbp), %xmm3 - movdqa 64(%rbp), %xmm4 - movdqa 80(%rbp), %xmm5 - movdqa 96(%rbp), %xmm6 - movdqa 112(%rbp), %xmm7 - movdqa 128(%rbp), %xmm8 - movdqa 144(%rbp), %xmm9 - movdqa 160(%rbp), %xmm10 - movdqa 176(%rbp), %xmm11 - movdqa 192(%rbp), %xmm12 - movdqa 208(%rbp), %xmm13 - movdqa 224(%rbp), %xmm14 - movdqa 240(%rbp), %xmm15 - movdqa %xmm0, 0(%rax) - movdqa %xmm1, 16(%rax) - movdqa %xmm2, 32(%rax) - movdqa %xmm3, 48(%rax) - movdqa %xmm4, 64(%rax) - movdqa %xmm5, 80(%rax) - movdqa %xmm6, 96(%rax) - movdqa %xmm7, 112(%rax) - movdqa %xmm8, 128(%rax) - movdqa %xmm9, 144(%rax) - movdqa %xmm10, 160(%rax) - movdqa %xmm11, 176(%rax) - movdqa %xmm12, 192(%rax) - movdqa %xmm13, 208(%rax) - movdqa %xmm14, 224(%rax) - movdqa %xmm15, 240(%rax) - -/* SSE2 switch */ - testl $0x1000, %ebx - jnz .neoscrypt_sse2 - -/* tempmem and double rounds */ - leaq -64(%rbp), %r10 - movq $10, %r12 - - xorq %r13, %r13 -.chacha_ns1: -/* blkcpy(V, Z) */ - leaq 512(%rbp), %rax - movq %r13, %rdx - movb $8, %cl - shlq %cl, %rdx - leaq 256(%rbp), %rcx - addq %rdx, %rax - movdqa 0(%rcx), %xmm0 - movdqa 16(%rcx), %xmm1 - movdqa 32(%rcx), %xmm2 - movdqa 48(%rcx), %xmm3 - movdqa 64(%rcx), %xmm4 - movdqa 80(%rcx), %xmm5 - movdqa 96(%rcx), %xmm6 - movdqa 112(%rcx), %xmm7 - movdqa 128(%rcx), %xmm8 - movdqa 144(%rcx), %xmm9 - movdqa 160(%rcx), %xmm10 - movdqa 176(%rcx), %xmm11 - movdqa 192(%rcx), %xmm12 - movdqa 208(%rcx), %xmm13 - movdqa 224(%rcx), %xmm14 - movdqa 240(%rcx), %xmm15 - movdqa %xmm0, 0(%rax) - movdqa %xmm1, 16(%rax) - movdqa %xmm2, 32(%rax) - movdqa %xmm3, 48(%rax) - movdqa %xmm4, 64(%rax) - movdqa %xmm5, 80(%rax) - movdqa %xmm6, 96(%rax) - movdqa %xmm7, 112(%rax) - movdqa %xmm8, 128(%rax) - movdqa %xmm9, 144(%rax) - movdqa %xmm10, 160(%rax) - movdqa %xmm11, 176(%rax) - movdqa %xmm12, 192(%rax) - movdqa %xmm13, 208(%rax) - movdqa %xmm14, 224(%rax) - movdqa %xmm15, 240(%rax) -/* blkmix(Z) */ - leaq 256(%rbp), %r8 - leaq 448(%rbp), %r9 - movq %r12, %r11 - call neoscrypt_xor_chacha - leaq 320(%rbp), %r8 - leaq 256(%rbp), %r9 - movq %r12, %r11 - call neoscrypt_xor_chacha - leaq 384(%rbp), %r8 - leaq 320(%rbp), %r9 - movq %r12, %r11 - call neoscrypt_xor_chacha - leaq 448(%rbp), %r8 - leaq 384(%rbp), %r9 - movq %r12, %r11 - call neoscrypt_xor_chacha - leaq 320(%rbp), %rax - leaq 384(%rbp), %rdx - movdqa 0(%rax), %xmm0 - movdqa 16(%rax), %xmm1 - movdqa 32(%rax), %xmm2 - movdqa 48(%rax), %xmm3 - movdqa 0(%rdx), %xmm4 - movdqa 16(%rdx), %xmm5 - movdqa 32(%rdx), %xmm6 - movdqa 48(%rdx), %xmm7 - movdqa %xmm0, 0(%rdx) - movdqa %xmm1, 16(%rdx) - movdqa %xmm2, 32(%rdx) - movdqa %xmm3, 48(%rdx) - movdqa %xmm4, 0(%rax) - movdqa %xmm5, 16(%rax) - movdqa %xmm6, 32(%rax) - movdqa %xmm7, 48(%rax) - incq %r13 - cmpq $128, %r13 - jnz .chacha_ns1 - - xorq %r13, %r13 -.chacha_ns2: -/* integerify(Z) mod 128 */ - leaq 256(%rbp), %rax - leaq 512(%rbp), %rcx - xorq %rdx, %rdx - movl 448(%rbp), %edx - andl $0x7F, %edx - shlq $8, %rdx - addq %rdx, %rcx -/* blkxor(Z, V) */ - movdqa 0(%rax), %xmm0 - movdqa 16(%rax), %xmm1 - movdqa 32(%rax), %xmm2 - movdqa 48(%rax), %xmm3 - movdqa 64(%rax), %xmm4 - movdqa 80(%rax), %xmm5 - movdqa 96(%rax), %xmm6 - movdqa 112(%rax), %xmm7 - movdqa 128(%rax), %xmm8 - movdqa 144(%rax), %xmm9 - movdqa 160(%rax), %xmm10 - movdqa 176(%rax), %xmm11 - movdqa 192(%rax), %xmm12 - movdqa 208(%rax), %xmm13 - movdqa 224(%rax), %xmm14 - movdqa 240(%rax), %xmm15 - pxor 0(%rcx), %xmm0 - pxor 16(%rcx), %xmm1 - pxor 32(%rcx), %xmm2 - pxor 48(%rcx), %xmm3 - pxor 64(%rcx), %xmm4 - pxor 80(%rcx), %xmm5 - pxor 96(%rcx), %xmm6 - pxor 112(%rcx), %xmm7 - pxor 128(%rcx), %xmm8 - pxor 144(%rcx), %xmm9 - pxor 160(%rcx), %xmm10 - pxor 176(%rcx), %xmm11 - pxor 192(%rcx), %xmm12 - pxor 208(%rcx), %xmm13 - pxor 224(%rcx), %xmm14 - pxor 240(%rcx), %xmm15 - movdqa %xmm0, 0(%rax) - movdqa %xmm1, 16(%rax) - movdqa %xmm2, 32(%rax) - movdqa %xmm3, 48(%rax) - movdqa %xmm4, 64(%rax) - movdqa %xmm5, 80(%rax) - movdqa %xmm6, 96(%rax) - movdqa %xmm7, 112(%rax) - movdqa %xmm8, 128(%rax) - movdqa %xmm9, 144(%rax) - movdqa %xmm10, 160(%rax) - movdqa %xmm11, 176(%rax) - movdqa %xmm12, 192(%rax) - movdqa %xmm13, 208(%rax) - movdqa %xmm14, 224(%rax) - movdqa %xmm15, 240(%rax) -/* blkmix(Z) */ - leaq 256(%rbp), %r8 - leaq 448(%rbp), %r9 - movq %r12, %r11 - call neoscrypt_xor_chacha - leaq 320(%rbp), %r8 - leaq 256(%rbp), %r9 - movq %r12, %r11 - call neoscrypt_xor_chacha - leaq 384(%rbp), %r8 - leaq 320(%rbp), %r9 - movq %r12, %r11 - call neoscrypt_xor_chacha - leaq 448(%rbp), %r8 - leaq 384(%rbp), %r9 - movq %r12, %r11 - call neoscrypt_xor_chacha - leaq 320(%rbp), %rax - leaq 384(%rbp), %rdx - movdqa 0(%rax), %xmm0 - movdqa 16(%rax), %xmm1 - movdqa 32(%rax), %xmm2 - movdqa 48(%rax), %xmm3 - movdqa 0(%rdx), %xmm4 - movdqa 16(%rdx), %xmm5 - movdqa 32(%rdx), %xmm6 - movdqa 48(%rdx), %xmm7 - movdqa %xmm0, 0(%rdx) - movdqa %xmm1, 16(%rdx) - movdqa %xmm2, 32(%rdx) - movdqa %xmm3, 48(%rdx) - movdqa %xmm4, 0(%rax) - movdqa %xmm5, 16(%rax) - movdqa %xmm6, 32(%rax) - movdqa %xmm7, 48(%rax) - incq %r13 - cmpq $128, %r13 - jnz .chacha_ns2 - - xorq %r13, %r13 -.salsa_ns1: -/* blkcpy(V, X) */ - leaq 512(%rbp), %rax - movq %r13, %rdx - movb $8, %cl - shlq %cl, %rdx - addq %rdx, %rax - movdqa 0(%rbp), %xmm0 - movdqa 16(%rbp), %xmm1 - movdqa 32(%rbp), %xmm2 - movdqa 48(%rbp), %xmm3 - movdqa 64(%rbp), %xmm4 - movdqa 80(%rbp), %xmm5 - movdqa 96(%rbp), %xmm6 - movdqa 112(%rbp), %xmm7 - movdqa 128(%rbp), %xmm8 - movdqa 144(%rbp), %xmm9 - movdqa 160(%rbp), %xmm10 - movdqa 176(%rbp), %xmm11 - movdqa 192(%rbp), %xmm12 - movdqa 208(%rbp), %xmm13 - movdqa 224(%rbp), %xmm14 - movdqa 240(%rbp), %xmm15 - movdqa %xmm0, 0(%rax) - movdqa %xmm1, 16(%rax) - movdqa %xmm2, 32(%rax) - movdqa %xmm3, 48(%rax) - movdqa %xmm4, 64(%rax) - movdqa %xmm5, 80(%rax) - movdqa %xmm6, 96(%rax) - movdqa %xmm7, 112(%rax) - movdqa %xmm8, 128(%rax) - movdqa %xmm9, 144(%rax) - movdqa %xmm10, 160(%rax) - movdqa %xmm11, 176(%rax) - movdqa %xmm12, 192(%rax) - movdqa %xmm13, 208(%rax) - movdqa %xmm14, 224(%rax) - movdqa %xmm15, 240(%rax) -/* blkmix(X) */ - movq %rbp, %r8 - leaq 192(%rbp), %r9 - movq %r12, %r11 - call neoscrypt_xor_salsa - leaq 64(%rbp), %r8 - movq %rbp, %r9 - movq %r12, %r11 - call neoscrypt_xor_salsa - leaq 128(%rbp), %r8 - leaq 64(%rbp), %r9 - movq %r12, %r11 - call neoscrypt_xor_salsa - leaq 192(%rbp), %r8 - leaq 128(%rbp), %r9 - movq %r12, %r11 - call neoscrypt_xor_salsa - leaq 64(%rbp), %rax - leaq 128(%rbp), %rdx - movdqa 0(%rax), %xmm0 - movdqa 16(%rax), %xmm1 - movdqa 32(%rax), %xmm2 - movdqa 48(%rax), %xmm3 - movdqa 0(%rdx), %xmm4 - movdqa 16(%rdx), %xmm5 - movdqa 32(%rdx), %xmm6 - movdqa 48(%rdx), %xmm7 - movdqa %xmm0, 0(%rdx) - movdqa %xmm1, 16(%rdx) - movdqa %xmm2, 32(%rdx) - movdqa %xmm3, 48(%rdx) - movdqa %xmm4, 0(%rax) - movdqa %xmm5, 16(%rax) - movdqa %xmm6, 32(%rax) - movdqa %xmm7, 48(%rax) - incq %r13 - cmpq $128, %r13 - jnz .salsa_ns1 - - xorq %r13, %r13 -.salsa_ns2: -/* integerify(X) mod 128 */ - leaq 512(%rbp), %rcx - xorq %rdx, %rdx - movl 192(%rbp), %edx - andl $0x7F, %edx - shlq $8, %rdx - addq %rdx, %rcx -/* blkxor(X, V) */ - movdqa 0(%rbp), %xmm0 - movdqa 16(%rbp), %xmm1 - movdqa 32(%rbp), %xmm2 - movdqa 48(%rbp), %xmm3 - movdqa 64(%rbp), %xmm4 - movdqa 80(%rbp), %xmm5 - movdqa 96(%rbp), %xmm6 - movdqa 112(%rbp), %xmm7 - movdqa 128(%rbp), %xmm8 - movdqa 144(%rbp), %xmm9 - movdqa 160(%rbp), %xmm10 - movdqa 176(%rbp), %xmm11 - movdqa 192(%rbp), %xmm12 - movdqa 208(%rbp), %xmm13 - movdqa 224(%rbp), %xmm14 - movdqa 240(%rbp), %xmm15 - pxor 0(%rcx), %xmm0 - pxor 16(%rcx), %xmm1 - pxor 32(%rcx), %xmm2 - pxor 48(%rcx), %xmm3 - pxor 64(%rcx), %xmm4 - pxor 80(%rcx), %xmm5 - pxor 96(%rcx), %xmm6 - pxor 112(%rcx), %xmm7 - pxor 128(%rcx), %xmm8 - pxor 144(%rcx), %xmm9 - pxor 160(%rcx), %xmm10 - pxor 176(%rcx), %xmm11 - pxor 192(%rcx), %xmm12 - pxor 208(%rcx), %xmm13 - pxor 224(%rcx), %xmm14 - pxor 240(%rcx), %xmm15 - movdqa %xmm0, 0(%rbp) - movdqa %xmm1, 16(%rbp) - movdqa %xmm2, 32(%rbp) - movdqa %xmm3, 48(%rbp) - movdqa %xmm4, 64(%rbp) - movdqa %xmm5, 80(%rbp) - movdqa %xmm6, 96(%rbp) - movdqa %xmm7, 112(%rbp) - movdqa %xmm8, 128(%rbp) - movdqa %xmm9, 144(%rbp) - movdqa %xmm10, 160(%rbp) - movdqa %xmm11, 176(%rbp) - movdqa %xmm12, 192(%rbp) - movdqa %xmm13, 208(%rbp) - movdqa %xmm14, 224(%rbp) - movdqa %xmm15, 240(%rbp) -/* blkmix(X) */ - movq %rbp, %r8 - leaq 192(%rbp), %r9 - movq %r12, %r11 - call neoscrypt_xor_salsa - leaq 64(%rbp), %r8 - movq %rbp, %r9 - movq %r12, %r11 - call neoscrypt_xor_salsa - leaq 128(%rbp), %r8 - leaq 64(%rbp), %r9 - movq %r12, %r11 - call neoscrypt_xor_salsa - leaq 192(%rbp), %r8 - leaq 128(%rbp), %r9 - movq %r12, %r11 - call neoscrypt_xor_salsa - leaq 64(%rbp), %rax - leaq 128(%rbp), %rdx - movdqa 0(%rax), %xmm0 - movdqa 16(%rax), %xmm1 - movdqa 32(%rax), %xmm2 - movdqa 48(%rax), %xmm3 - movdqa 0(%rdx), %xmm4 - movdqa 16(%rdx), %xmm5 - movdqa 32(%rdx), %xmm6 - movdqa 48(%rdx), %xmm7 - movdqa %xmm0, 0(%rdx) - movdqa %xmm1, 16(%rdx) - movdqa %xmm2, 32(%rdx) - movdqa %xmm3, 48(%rdx) - movdqa %xmm4, 0(%rax) - movdqa %xmm5, 16(%rax) - movdqa %xmm6, 32(%rax) - movdqa %xmm7, 48(%rax) - incq %r13 - cmpq $128, %r13 - jnz .salsa_ns2 - -/* blkxor(X, Z) */ - leaq 256(%rbp), %rcx - movdqa 0(%rbp), %xmm0 - movdqa 16(%rbp), %xmm1 - movdqa 32(%rbp), %xmm2 - movdqa 48(%rbp), %xmm3 - movdqa 64(%rbp), %xmm4 - movdqa 80(%rbp), %xmm5 - movdqa 96(%rbp), %xmm6 - movdqa 112(%rbp), %xmm7 - movdqa 128(%rbp), %xmm8 - movdqa 144(%rbp), %xmm9 - movdqa 160(%rbp), %xmm10 - movdqa 176(%rbp), %xmm11 - movdqa 192(%rbp), %xmm12 - movdqa 208(%rbp), %xmm13 - movdqa 224(%rbp), %xmm14 - movdqa 240(%rbp), %xmm15 - pxor 0(%rcx), %xmm0 - pxor 16(%rcx), %xmm1 - pxor 32(%rcx), %xmm2 - pxor 48(%rcx), %xmm3 - pxor 64(%rcx), %xmm4 - pxor 80(%rcx), %xmm5 - pxor 96(%rcx), %xmm6 - pxor 112(%rcx), %xmm7 - pxor 128(%rcx), %xmm8 - pxor 144(%rcx), %xmm9 - pxor 160(%rcx), %xmm10 - pxor 176(%rcx), %xmm11 - pxor 192(%rcx), %xmm12 - pxor 208(%rcx), %xmm13 - pxor 224(%rcx), %xmm14 - pxor 240(%rcx), %xmm15 - movdqa %xmm0, 0(%rbp) - movdqa %xmm1, 16(%rbp) - movdqa %xmm2, 32(%rbp) - movdqa %xmm3, 48(%rbp) - movdqa %xmm4, 64(%rbp) - movdqa %xmm5, 80(%rbp) - movdqa %xmm6, 96(%rbp) - movdqa %xmm7, 112(%rbp) - movdqa %xmm8, 128(%rbp) - movdqa %xmm9, 144(%rbp) - movdqa %xmm10, 160(%rbp) - movdqa %xmm11, 176(%rbp) - movdqa %xmm12, 192(%rbp) - movdqa %xmm13, 208(%rbp) - movdqa %xmm14, 224(%rbp) - movdqa %xmm15, 240(%rbp) - -/* FastKDF */ -#ifdef WIN64 -#ifdef OPT - movq %r14, %rcx - movq %rbp, %rdx - movq %r15, %r8 - xorq %r9, %r9 - incq %r9 - call neoscrypt_fastkdf_opt -#else - movq %r14, %rcx - movq $80, %rdx - movq %rbp, %r8 - movq $256, %r9 - movq $32, %rax - movq %rax, 32(%rsp) - movq %r15, 40(%rsp) - movq %rax, 48(%rsp) - call neoscrypt_fastkdf -#endif /* OPT */ -#else -#ifdef OPT - movq %r14, %rdi - movq %rbp, %rsi - movq %r15, %rdx - xorq %rcx, %rcx - incq %rcx -#ifdef __APPLE__ - call _neoscrypt_fastkdf_opt -#else - call neoscrypt_fastkdf_opt -#endif /* __APPLE__ */ -#else - movq %r14, %rdi - movq $80, %rsi - movq %rbp, %rdx - movq $256, %rcx - movq $32, %r8 - movq %r15, %r9 - movq $32, 0(%rsp) -#ifdef __APPLE__ - call _neoscrypt_fastkdf -#else - call neoscrypt_fastkdf -#endif /* __APPLE__ */ -#endif /* OPT */ -#endif /* WIN64 */ - -#ifdef WIN64 -/* free memory */ - movq 64(%rsp), %rcx - call free -/* restore stack */ - addq $128, %rsp -#else -/* restore stack */ - movq 32(%rsp), %rsp -#endif - popq %r15 - popq %r14 - popq %r13 - popq %r12 - popq %rbp - popq %rbx -#ifdef WIN64 - popq %rsi - popq %rdi -#endif - ret - -.neoscrypt_sse2: - movq $10, %r12 - - xorq %r13, %r13 -.chacha_ns1_sse2: -/* blkcpy(V, Z) */ - leaq 512(%rbp), %rax - movq %r13, %rdx - movb $8, %cl - shlq %cl, %rdx - leaq 256(%rbp), %rcx - addq %rdx, %rax - movdqa 0(%rcx), %xmm0 - movdqa 16(%rcx), %xmm1 - movdqa 32(%rcx), %xmm2 - movdqa 48(%rcx), %xmm3 - movdqa 64(%rcx), %xmm4 - movdqa 80(%rcx), %xmm5 - movdqa 96(%rcx), %xmm6 - movdqa 112(%rcx), %xmm7 - movdqa 128(%rcx), %xmm8 - movdqa 144(%rcx), %xmm9 - movdqa 160(%rcx), %xmm10 - movdqa 176(%rcx), %xmm11 - movdqa 192(%rcx), %xmm12 - movdqa 208(%rcx), %xmm13 - movdqa 224(%rcx), %xmm14 - movdqa 240(%rcx), %xmm15 - movdqa %xmm0, 0(%rax) - movdqa %xmm1, 16(%rax) - movdqa %xmm2, 32(%rax) - movdqa %xmm3, 48(%rax) - movdqa %xmm4, 64(%rax) - movdqa %xmm5, 80(%rax) - movdqa %xmm6, 96(%rax) - movdqa %xmm7, 112(%rax) - movdqa %xmm8, 128(%rax) - movdqa %xmm9, 144(%rax) - movdqa %xmm10, 160(%rax) - movdqa %xmm11, 176(%rax) - movdqa %xmm12, 192(%rax) - movdqa %xmm13, 208(%rax) - movdqa %xmm14, 224(%rax) - movdqa %xmm15, 240(%rax) -/* blkmix(Z) */ - leaq 256(%rbp), %r8 - leaq 448(%rbp), %r9 - movq %r12, %r10 - call neoscrypt_xor_chacha_sse2 - leaq 320(%rbp), %r8 - leaq 256(%rbp), %r9 - movq %r12, %r10 - call neoscrypt_xor_chacha_sse2 - leaq 384(%rbp), %r8 - leaq 320(%rbp), %r9 - movq %r12, %r10 - call neoscrypt_xor_chacha_sse2 - leaq 448(%rbp), %r8 - leaq 384(%rbp), %r9 - movq %r12, %r10 - call neoscrypt_xor_chacha_sse2 - leaq 320(%rbp), %rax - leaq 384(%rbp), %rdx - movdqa 0(%rax), %xmm0 - movdqa 16(%rax), %xmm1 - movdqa 32(%rax), %xmm2 - movdqa 48(%rax), %xmm3 - movdqa 0(%rdx), %xmm4 - movdqa 16(%rdx), %xmm5 - movdqa 32(%rdx), %xmm6 - movdqa 48(%rdx), %xmm7 - movdqa %xmm0, 0(%rdx) - movdqa %xmm1, 16(%rdx) - movdqa %xmm2, 32(%rdx) - movdqa %xmm3, 48(%rdx) - movdqa %xmm4, 0(%rax) - movdqa %xmm5, 16(%rax) - movdqa %xmm6, 32(%rax) - movdqa %xmm7, 48(%rax) - incq %r13 - cmpq $128, %r13 - jnz .chacha_ns1_sse2 - - xorq %r13, %r13 -.chacha_ns2_sse2: -/* integerify(Z) mod 128 */ - leaq 256(%rbp), %rax - leaq 512(%rbp), %rcx - xorq %rdx, %rdx - movl 448(%rbp), %edx - andl $0x7F, %edx - shlq $8, %rdx - addq %rdx, %rcx -/* blkxor(Z, V) */ - movdqa 0(%rax), %xmm0 - movdqa 16(%rax), %xmm1 - movdqa 32(%rax), %xmm2 - movdqa 48(%rax), %xmm3 - movdqa 64(%rax), %xmm4 - movdqa 80(%rax), %xmm5 - movdqa 96(%rax), %xmm6 - movdqa 112(%rax), %xmm7 - movdqa 128(%rax), %xmm8 - movdqa 144(%rax), %xmm9 - movdqa 160(%rax), %xmm10 - movdqa 176(%rax), %xmm11 - movdqa 192(%rax), %xmm12 - movdqa 208(%rax), %xmm13 - movdqa 224(%rax), %xmm14 - movdqa 240(%rax), %xmm15 - pxor 0(%rcx), %xmm0 - pxor 16(%rcx), %xmm1 - pxor 32(%rcx), %xmm2 - pxor 48(%rcx), %xmm3 - pxor 64(%rcx), %xmm4 - pxor 80(%rcx), %xmm5 - pxor 96(%rcx), %xmm6 - pxor 112(%rcx), %xmm7 - pxor 128(%rcx), %xmm8 - pxor 144(%rcx), %xmm9 - pxor 160(%rcx), %xmm10 - pxor 176(%rcx), %xmm11 - pxor 192(%rcx), %xmm12 - pxor 208(%rcx), %xmm13 - pxor 224(%rcx), %xmm14 - pxor 240(%rcx), %xmm15 - movdqa %xmm0, 0(%rax) - movdqa %xmm1, 16(%rax) - movdqa %xmm2, 32(%rax) - movdqa %xmm3, 48(%rax) - movdqa %xmm4, 64(%rax) - movdqa %xmm5, 80(%rax) - movdqa %xmm6, 96(%rax) - movdqa %xmm7, 112(%rax) - movdqa %xmm8, 128(%rax) - movdqa %xmm9, 144(%rax) - movdqa %xmm10, 160(%rax) - movdqa %xmm11, 176(%rax) - movdqa %xmm12, 192(%rax) - movdqa %xmm13, 208(%rax) - movdqa %xmm14, 224(%rax) - movdqa %xmm15, 240(%rax) -/* blkmix(Z) */ - leaq 256(%rbp), %r8 - leaq 448(%rbp), %r9 - movq %r12, %r10 - call neoscrypt_xor_chacha_sse2 - leaq 320(%rbp), %r8 - leaq 256(%rbp), %r9 - movq %r12, %r10 - call neoscrypt_xor_chacha_sse2 - leaq 384(%rbp), %r8 - leaq 320(%rbp), %r9 - movq %r12, %r10 - call neoscrypt_xor_chacha_sse2 - leaq 448(%rbp), %r8 - leaq 384(%rbp), %r9 - movq %r12, %r10 - call neoscrypt_xor_chacha_sse2 - leaq 320(%rbp), %rax - leaq 384(%rbp), %rdx - movdqa 0(%rax), %xmm0 - movdqa 16(%rax), %xmm1 - movdqa 32(%rax), %xmm2 - movdqa 48(%rax), %xmm3 - movdqa 0(%rdx), %xmm4 - movdqa 16(%rdx), %xmm5 - movdqa 32(%rdx), %xmm6 - movdqa 48(%rdx), %xmm7 - movdqa %xmm0, 0(%rdx) - movdqa %xmm1, 16(%rdx) - movdqa %xmm2, 32(%rdx) - movdqa %xmm3, 48(%rdx) - movdqa %xmm4, 0(%rax) - movdqa %xmm5, 16(%rax) - movdqa %xmm6, 32(%rax) - movdqa %xmm7, 48(%rax) - incq %r13 - cmpq $128, %r13 - jnz .chacha_ns2_sse2 - - movq %rbp, %r8 - movq $4, %r9 - call neoscrypt_salsa_tangle_sse2 - - xorq %r13, %r13 -.salsa_ns1_sse2: -/* blkcpy(V, X) */ - leaq 512(%rbp), %rax - movq %r13, %rdx - movb $8, %cl - shlq %cl, %rdx - addq %rdx, %rax - movdqa 0(%rbp), %xmm0 - movdqa 16(%rbp), %xmm1 - movdqa 32(%rbp), %xmm2 - movdqa 48(%rbp), %xmm3 - movdqa 64(%rbp), %xmm4 - movdqa 80(%rbp), %xmm5 - movdqa 96(%rbp), %xmm6 - movdqa 112(%rbp), %xmm7 - movdqa 128(%rbp), %xmm8 - movdqa 144(%rbp), %xmm9 - movdqa 160(%rbp), %xmm10 - movdqa 176(%rbp), %xmm11 - movdqa 192(%rbp), %xmm12 - movdqa 208(%rbp), %xmm13 - movdqa 224(%rbp), %xmm14 - movdqa 240(%rbp), %xmm15 - movdqa %xmm0, 0(%rax) - movdqa %xmm1, 16(%rax) - movdqa %xmm2, 32(%rax) - movdqa %xmm3, 48(%rax) - movdqa %xmm4, 64(%rax) - movdqa %xmm5, 80(%rax) - movdqa %xmm6, 96(%rax) - movdqa %xmm7, 112(%rax) - movdqa %xmm8, 128(%rax) - movdqa %xmm9, 144(%rax) - movdqa %xmm10, 160(%rax) - movdqa %xmm11, 176(%rax) - movdqa %xmm12, 192(%rax) - movdqa %xmm13, 208(%rax) - movdqa %xmm14, 224(%rax) - movdqa %xmm15, 240(%rax) -/* blkmix(X) */ - movq %rbp, %r8 - leaq 192(%rbp), %r9 - movq %r12, %r10 - call neoscrypt_xor_salsa_sse2 - leaq 64(%rbp), %r8 - movq %rbp, %r9 - movq %r12, %r10 - call neoscrypt_xor_salsa_sse2 - leaq 128(%rbp), %r8 - leaq 64(%rbp), %r9 - movq %r12, %r10 - call neoscrypt_xor_salsa_sse2 - leaq 192(%rbp), %r8 - leaq 128(%rbp), %r9 - movq %r12, %r10 - call neoscrypt_xor_salsa_sse2 - leaq 64(%rbp), %rax - leaq 128(%rbp), %rdx - movdqa 0(%rax), %xmm0 - movdqa 16(%rax), %xmm1 - movdqa 32(%rax), %xmm2 - movdqa 48(%rax), %xmm3 - movdqa 0(%rdx), %xmm4 - movdqa 16(%rdx), %xmm5 - movdqa 32(%rdx), %xmm6 - movdqa 48(%rdx), %xmm7 - movdqa %xmm0, 0(%rdx) - movdqa %xmm1, 16(%rdx) - movdqa %xmm2, 32(%rdx) - movdqa %xmm3, 48(%rdx) - movdqa %xmm4, 0(%rax) - movdqa %xmm5, 16(%rax) - movdqa %xmm6, 32(%rax) - movdqa %xmm7, 48(%rax) - incq %r13 - cmpq $128, %r13 - jnz .salsa_ns1_sse2 - - xorq %r13, %r13 -.salsa_ns2_sse2: -/* integerify(X) mod 128 */ - leaq 512(%rbp), %rcx - xorq %rdx, %rdx - movl 192(%rbp), %edx - andl $0x7F, %edx - shlq $8, %rdx - addq %rdx, %rcx -/* blkxor(X, V) */ - movdqa 0(%rbp), %xmm0 - movdqa 16(%rbp), %xmm1 - movdqa 32(%rbp), %xmm2 - movdqa 48(%rbp), %xmm3 - movdqa 64(%rbp), %xmm4 - movdqa 80(%rbp), %xmm5 - movdqa 96(%rbp), %xmm6 - movdqa 112(%rbp), %xmm7 - movdqa 128(%rbp), %xmm8 - movdqa 144(%rbp), %xmm9 - movdqa 160(%rbp), %xmm10 - movdqa 176(%rbp), %xmm11 - movdqa 192(%rbp), %xmm12 - movdqa 208(%rbp), %xmm13 - movdqa 224(%rbp), %xmm14 - movdqa 240(%rbp), %xmm15 - pxor 0(%rcx), %xmm0 - pxor 16(%rcx), %xmm1 - pxor 32(%rcx), %xmm2 - pxor 48(%rcx), %xmm3 - pxor 64(%rcx), %xmm4 - pxor 80(%rcx), %xmm5 - pxor 96(%rcx), %xmm6 - pxor 112(%rcx), %xmm7 - pxor 128(%rcx), %xmm8 - pxor 144(%rcx), %xmm9 - pxor 160(%rcx), %xmm10 - pxor 176(%rcx), %xmm11 - pxor 192(%rcx), %xmm12 - pxor 208(%rcx), %xmm13 - pxor 224(%rcx), %xmm14 - pxor 240(%rcx), %xmm15 - movdqa %xmm0, 0(%rbp) - movdqa %xmm1, 16(%rbp) - movdqa %xmm2, 32(%rbp) - movdqa %xmm3, 48(%rbp) - movdqa %xmm4, 64(%rbp) - movdqa %xmm5, 80(%rbp) - movdqa %xmm6, 96(%rbp) - movdqa %xmm7, 112(%rbp) - movdqa %xmm8, 128(%rbp) - movdqa %xmm9, 144(%rbp) - movdqa %xmm10, 160(%rbp) - movdqa %xmm11, 176(%rbp) - movdqa %xmm12, 192(%rbp) - movdqa %xmm13, 208(%rbp) - movdqa %xmm14, 224(%rbp) - movdqa %xmm15, 240(%rbp) -/* blkmix(X) */ - movq %rbp, %r8 - leaq 192(%rbp), %r9 - movq %r12, %r10 - call neoscrypt_xor_salsa_sse2 - leaq 64(%rbp), %r8 - movq %rbp, %r9 - movq %r12, %r10 - call neoscrypt_xor_salsa_sse2 - leaq 128(%rbp), %r8 - leaq 64(%rbp), %r9 - movq %r12, %r10 - call neoscrypt_xor_salsa_sse2 - leaq 192(%rbp), %r8 - leaq 128(%rbp), %r9 - movq %r12, %r10 - call neoscrypt_xor_salsa_sse2 - leaq 64(%rbp), %rax - leaq 128(%rbp), %rdx - movdqa 0(%rax), %xmm0 - movdqa 16(%rax), %xmm1 - movdqa 32(%rax), %xmm2 - movdqa 48(%rax), %xmm3 - movdqa 0(%rdx), %xmm4 - movdqa 16(%rdx), %xmm5 - movdqa 32(%rdx), %xmm6 - movdqa 48(%rdx), %xmm7 - movdqa %xmm0, 0(%rdx) - movdqa %xmm1, 16(%rdx) - movdqa %xmm2, 32(%rdx) - movdqa %xmm3, 48(%rdx) - movdqa %xmm4, 0(%rax) - movdqa %xmm5, 16(%rax) - movdqa %xmm6, 32(%rax) - movdqa %xmm7, 48(%rax) - incq %r13 - cmpq $128, %r13 - jnz .salsa_ns2_sse2 - - movq %rbp, %r8 - movq $4, %r9 - call neoscrypt_salsa_tangle_sse2 - -/* blkxor(X, Z) */ - leaq 256(%rbp), %rcx - movdqa 0(%rbp), %xmm0 - movdqa 16(%rbp), %xmm1 - movdqa 32(%rbp), %xmm2 - movdqa 48(%rbp), %xmm3 - movdqa 64(%rbp), %xmm4 - movdqa 80(%rbp), %xmm5 - movdqa 96(%rbp), %xmm6 - movdqa 112(%rbp), %xmm7 - movdqa 128(%rbp), %xmm8 - movdqa 144(%rbp), %xmm9 - movdqa 160(%rbp), %xmm10 - movdqa 176(%rbp), %xmm11 - movdqa 192(%rbp), %xmm12 - movdqa 208(%rbp), %xmm13 - movdqa 224(%rbp), %xmm14 - movdqa 240(%rbp), %xmm15 - pxor 0(%rcx), %xmm0 - pxor 16(%rcx), %xmm1 - pxor 32(%rcx), %xmm2 - pxor 48(%rcx), %xmm3 - pxor 64(%rcx), %xmm4 - pxor 80(%rcx), %xmm5 - pxor 96(%rcx), %xmm6 - pxor 112(%rcx), %xmm7 - pxor 128(%rcx), %xmm8 - pxor 144(%rcx), %xmm9 - pxor 160(%rcx), %xmm10 - pxor 176(%rcx), %xmm11 - pxor 192(%rcx), %xmm12 - pxor 208(%rcx), %xmm13 - pxor 224(%rcx), %xmm14 - pxor 240(%rcx), %xmm15 - movdqa %xmm0, 0(%rbp) - movdqa %xmm1, 16(%rbp) - movdqa %xmm2, 32(%rbp) - movdqa %xmm3, 48(%rbp) - movdqa %xmm4, 64(%rbp) - movdqa %xmm5, 80(%rbp) - movdqa %xmm6, 96(%rbp) - movdqa %xmm7, 112(%rbp) - movdqa %xmm8, 128(%rbp) - movdqa %xmm9, 144(%rbp) - movdqa %xmm10, 160(%rbp) - movdqa %xmm11, 176(%rbp) - movdqa %xmm12, 192(%rbp) - movdqa %xmm13, 208(%rbp) - movdqa %xmm14, 224(%rbp) - movdqa %xmm15, 240(%rbp) - -/* FastKDF */ -#ifdef WIN64 -#ifdef OPT - movq %r14, %rcx - movq %rbp, %rdx - movq %r15, %r8 - xorq %r9, %r9 - incq %r9 - call neoscrypt_fastkdf_opt -#else - movq %r14, %rcx - movq $80, %rdx - movq %rbp, %r8 - movq $256, %r9 - movq $32, %rax - movq %rax, 32(%rsp) - movq %r15, 40(%rsp) - movq %rax, 48(%rsp) - call neoscrypt_fastkdf -#endif /* OPT */ -#else -#ifdef OPT - movq %r14, %rdi - movq %rbp, %rsi - movq %r15, %rdx - xorq %rcx, %rcx - incq %rcx -#ifdef __APPLE__ - call _neoscrypt_fastkdf_opt -#else - call neoscrypt_fastkdf_opt -#endif /* __APPLE__ */ -#else - movq %r14, %rdi - movq $80, %rsi - movq %rbp, %rdx - movq $256, %rcx - movq $32, %rax - movq %rax, %r8 - movq %r15, %r9 - movq %rax, 0(%rsp) -#ifdef __APPLE__ - call _neoscrypt_fastkdf -#else - call neoscrypt_fastkdf -#endif /* __APPLE__ */ -#endif /* OPT */ -#endif /* WIN64 */ - -#ifdef WIN64 -/* free memory */ - movq 64(%rsp), %rcx - call free -/* restore stack */ - addq $128, %rsp -#else -/* restore stack */ - movq 32(%rsp), %rsp -#endif - popq %r15 - popq %r14 - popq %r13 - popq %r12 - popq %rbp - popq %rbx -#ifdef WIN64 - popq %rsi - popq %rdi -#endif - ret - -#ifdef SHA256 - -.scrypt: -#ifdef WIN64 -/* attempt to allocate 131200 + 128 bytes of stack space fails miserably; - * have to use malloc() and free() instead */ - subq $128, %rsp -/* allocate memory (33 pages of 4Kb each) */ - movq $0x21000, %rcx - call malloc -/* save memory address */ - movq %rax, 64(%rsp) -/* align memory */ - addq $64, %rax - andq $0xFFFFFFFFFFFFFFC0, %rax -/* memory base: X, Z, V */ - leaq 64(%rax), %rbp -#else -/* align stack */ - movq %rsp, %rax - andq $0xFFFFFFFFFFFFFFC0, %rsp - subq $0x20100, %rsp -/* save unaligned stack */ - movq %rax, 32(%rsp) -/* memory base: X, Z, V */ - leaq 128(%rsp), %rbp -#endif /* WIN64 */ - -/* PBKDF2-HMAC-SHA256 */ -#ifdef WIN64 - movq $80, %rax - movq %r14, %rcx - movq %rax, %rdx - movq %r14, %r8 - movq %rax, %r9 - movq $1, 32(%rsp) - movq %rbp, 40(%rsp) - movq $128, 48(%rsp) - call neoscrypt_pbkdf2_sha256 -#else - movq $80, %rax - movq %r14, %rdi - movq %rax, %rsi - movq %r14, %rdx - movq %rax, %rcx - movq $1, %r8 - movq %rbp, %r9 - movq $128, 0(%rsp) -#ifdef __APPLE__ - call _neoscrypt_pbkdf2_sha256 -#else - call neoscrypt_pbkdf2_sha256 -#endif /* __APPLE__ */ -#endif /* WIN64 */ - -/* SSE2 switch */ - testl $0x1000, %ebx - jnz .scrypt_sse2 - -/* tempmem and double rounds */ - leaq -64(%rbp), %r10 - movq $4, %r12 - - xorq %r13, %r13 -.salsa_s1: -/* blkcpy(V, X) */ - leaq 128(%rbp), %rax - movq %r13, %rdx - movb $7, %cl - shlq %cl, %rdx - addq %rdx, %rax - movdqa 0(%rbp), %xmm0 - movdqa 16(%rbp), %xmm1 - movdqa 32(%rbp), %xmm2 - movdqa 48(%rbp), %xmm3 - movdqa 64(%rbp), %xmm4 - movdqa 80(%rbp), %xmm5 - movdqa 96(%rbp), %xmm6 - movdqa 112(%rbp), %xmm7 - movdqa %xmm0, 0(%rax) - movdqa %xmm1, 16(%rax) - movdqa %xmm2, 32(%rax) - movdqa %xmm3, 48(%rax) - movdqa %xmm4, 64(%rax) - movdqa %xmm5, 80(%rax) - movdqa %xmm6, 96(%rax) - movdqa %xmm7, 112(%rax) -/* blkmix(X) */ - movq %rbp, %r8 - leaq 64(%rbp), %r9 - movq %r12, %r11 - call neoscrypt_xor_salsa - leaq 64(%rbp), %r8 - movq %rbp, %r9 - movq %r12, %r11 - call neoscrypt_xor_salsa - incq %r13 - cmpq $1024, %r13 - jnz .salsa_s1 - - xorq %r13, %r13 -.salsa_s2: -/* integerify(X) mod 1024 */ - leaq 128(%rbp), %rcx - xorq %rdx, %rdx - movl 64(%rbp), %edx - andl $0x03FF, %edx - shlq $7, %rdx - addq %rdx, %rcx -/* blkxor(X, V) */ - movdqa 0(%rbp), %xmm0 - movdqa 16(%rbp), %xmm1 - movdqa 32(%rbp), %xmm2 - movdqa 48(%rbp), %xmm3 - movdqa 64(%rbp), %xmm4 - movdqa 80(%rbp), %xmm5 - movdqa 96(%rbp), %xmm6 - movdqa 112(%rbp), %xmm7 - pxor 0(%rcx), %xmm0 - pxor 16(%rcx), %xmm1 - pxor 32(%rcx), %xmm2 - pxor 48(%rcx), %xmm3 - pxor 64(%rcx), %xmm4 - pxor 80(%rcx), %xmm5 - pxor 96(%rcx), %xmm6 - pxor 112(%rcx), %xmm7 - movdqa %xmm0, 0(%rbp) - movdqa %xmm1, 16(%rbp) - movdqa %xmm2, 32(%rbp) - movdqa %xmm3, 48(%rbp) - movdqa %xmm4, 64(%rbp) - movdqa %xmm5, 80(%rbp) - movdqa %xmm6, 96(%rbp) - movdqa %xmm7, 112(%rbp) -/* blkmix(X) */ - movq %rbp, %r8 - leaq 64(%rbp), %r9 - movq %r12, %r11 - call neoscrypt_xor_salsa - leaq 64(%rbp), %r8 - movq %rbp, %r9 - movq %r12, %r11 - call neoscrypt_xor_salsa - incq %r13 - cmpq $1024, %r13 - jnz .salsa_s2 - -/* PBKDF2-HMAC-SHA256 */ -#ifdef WIN64 - movq %r14, %rcx - movq $80, %rdx - movq %rbp, %r8 - movq $128, %r9 - movq $1, 32(%rsp) - movq %r15, 40(%rsp) - movq $32, 48(%rsp) - call neoscrypt_pbkdf2_sha256 -#else - movq %r14, %rdi - movq $80, %rsi - movq %rbp, %rdx - movq $128, %rcx - movq $1, %r8 - movq %r15, %r9 - movq $32, 0(%rsp) -#ifdef __APPLE__ - call _neoscrypt_pbkdf2_sha256 -#else - call neoscrypt_pbkdf2_sha256 -#endif /* __APPLE__ */ - -#endif /* WIN64 */ - -#ifdef WIN64 -/* free memory */ - movq 64(%rsp), %rcx - call free -/* restore stack */ - addq $128, %rsp -#else -/* restore stack */ - movq 32(%rsp), %rsp -#endif - popq %r15 - popq %r14 - popq %r13 - popq %r12 - popq %rbp - popq %rbx -#ifdef WIN64 - popq %rsi - popq %rdi -#endif - ret - -.scrypt_sse2: - movq %rbp, %r8 - movq $2, %r9 - call neoscrypt_salsa_tangle_sse2 - - movq $4, %r12 - - xorq %r13, %r13 -.salsa_s1_sse2: -/* blkcpy(V, X) */ - leaq 128(%rbp), %rax - movq %r13, %rdx - movb $7, %cl - shlq %cl, %rdx - addq %rdx, %rax - movdqa 0(%rbp), %xmm0 - movdqa 16(%rbp), %xmm1 - movdqa 32(%rbp), %xmm2 - movdqa 48(%rbp), %xmm3 - movdqa 64(%rbp), %xmm4 - movdqa 80(%rbp), %xmm5 - movdqa 96(%rbp), %xmm6 - movdqa 112(%rbp), %xmm7 - movdqa %xmm0, 0(%rax) - movdqa %xmm1, 16(%rax) - movdqa %xmm2, 32(%rax) - movdqa %xmm3, 48(%rax) - movdqa %xmm4, 64(%rax) - movdqa %xmm5, 80(%rax) - movdqa %xmm6, 96(%rax) - movdqa %xmm7, 112(%rax) -/* blkmix(X) */ - movq %rbp, %r8 - leaq 64(%rbp), %r9 - movq %r12, %r10 - call neoscrypt_xor_salsa_sse2 - leaq 64(%rbp), %r8 - movq %rbp, %r9 - movq %r12, %r10 - call neoscrypt_xor_salsa_sse2 - incq %r13 - cmpq $1024, %r13 - jnz .salsa_s1_sse2 - - xorq %r13, %r13 -.salsa_s2_sse2: -/* integerify(X) mod 1024 */ - leaq 128(%rbp), %rcx - xorq %rdx, %rdx - movl 64(%rbp), %edx - andl $0x03FF, %edx - shlq $7, %rdx - addq %rdx, %rcx -/* blkxor(X, V) */ - movdqa 0(%rbp), %xmm0 - movdqa 16(%rbp), %xmm1 - movdqa 32(%rbp), %xmm2 - movdqa 48(%rbp), %xmm3 - movdqa 64(%rbp), %xmm4 - movdqa 80(%rbp), %xmm5 - movdqa 96(%rbp), %xmm6 - movdqa 112(%rbp), %xmm7 - pxor 0(%rcx), %xmm0 - pxor 16(%rcx), %xmm1 - pxor 32(%rcx), %xmm2 - pxor 48(%rcx), %xmm3 - pxor 64(%rcx), %xmm4 - pxor 80(%rcx), %xmm5 - pxor 96(%rcx), %xmm6 - pxor 112(%rcx), %xmm7 - movdqa %xmm0, 0(%rbp) - movdqa %xmm1, 16(%rbp) - movdqa %xmm2, 32(%rbp) - movdqa %xmm3, 48(%rbp) - movdqa %xmm4, 64(%rbp) - movdqa %xmm5, 80(%rbp) - movdqa %xmm6, 96(%rbp) - movdqa %xmm7, 112(%rbp) -/* blkmix(X) */ - movq %rbp, %r8 - leaq 64(%rbp), %r9 - movq %r12, %r10 - call neoscrypt_xor_salsa_sse2 - leaq 64(%rbp), %r8 - movq %rbp, %r9 - movq %r12, %r10 - call neoscrypt_xor_salsa_sse2 - incq %r13 - cmpq $1024, %r13 - jnz .salsa_s2_sse2 - - movq %rbp, %r8 - movq $2, %r9 - call neoscrypt_salsa_tangle_sse2 - -/* PBKDF2-HMAC-SHA256 */ -#ifdef WIN64 - movq %r14, %rcx - movq $80, %rdx - movq %rbp, %r8 - movq $128, %r9 - movq $1, 32(%rsp) - movq %r15, 40(%rsp) - movq $32, 48(%rsp) - call neoscrypt_pbkdf2_sha256 -#else - movq %r14, %rdi - movq $80, %rsi - movq %rbp, %rdx - movq $128, %rcx - movq $1, %r8 - movq %r15, %r9 - movq $32, 0(%rsp) -#ifdef __APPLE__ - call _neoscrypt_pbkdf2_sha256 -#else - call neoscrypt_pbkdf2_sha256 -#endif /* __APPLE__ */ -#endif /* WIN64 */ - -#ifdef WIN64 -/* free memory */ - movq 64(%rsp), %rcx - call free -/* restore stack */ - addq $128, %rsp -#else -/* restore stack */ - movq 32(%rsp), %rsp -#endif - popq %r15 - popq %r14 - popq %r13 - popq %r12 - popq %rbp - popq %rbx -#ifdef WIN64 - popq %rsi - popq %rdi -#endif - ret - -#endif /* SHA256 */ - -#ifdef MINER_4WAY - -/* blake2s_compress_4way(mem) - * AMD64 (SSE2) BLAKE2s 4-way block compression */ -.globl blake2s_compress_4way -.globl _blake2s_compress_4way -blake2s_compress_4way: -_blake2s_compress_4way: - pushq %rbx - pushq %rbp - pushq %r12 - pushq %r13 - pushq %r14 - pushq %r15 -#ifdef WIN64 - pushq %rdi - pushq %rsi - subq $160, %rsp - movdqu %xmm6, 144(%rsp) - movdqu %xmm7, 128(%rsp) - movdqu %xmm8, 112(%rsp) - movdqu %xmm9, 96(%rsp) - movdqu %xmm10, 80(%rsp) - movdqu %xmm11, 64(%rsp) - movdqu %xmm12, 48(%rsp) - movdqu %xmm13, 32(%rsp) - movdqu %xmm14, 16(%rsp) - movdqu %xmm15, 0(%rsp) - movq %rcx, %rdi -#endif - -/* initialise */ - leaq 448(%rdi), %rsi - movdqa 0(%rdi), %xmm0 - movdqa 16(%rdi), %xmm8 - movdqa 32(%rdi), %xmm4 - movdqa 48(%rdi), %xmm10 - movdqa 64(%rdi), %xmm12 - movdqa 80(%rdi), %xmm1 - movdqa 96(%rdi), %xmm13 - movdqa 112(%rdi), %xmm5 -/* movdqa %xmm0, 0(%rsi) */ -/* movdqa %xmm8, 16(%rsi) */ -/* movdqa %xmm4, 32(%rsi) */ -/* movdqa %xmm10, 48(%rsi) */ -/* movdqa %xmm12, 64(%rsi) */ -/* movdqa %xmm1, 80(%rsi) */ -/* movdqa %xmm13, 96(%rsi) */ -/* movdqa %xmm5, 112(%rsi) */ - movq $0x6A09E6676A09E667, %r8 - movq $0xBB67AE85BB67AE85, %r9 - movq $0x3C6EF3723C6EF372, %r10 - movq $0xA54FF53AA54FF53A, %r11 - movq %r8, 128(%rsi) - movq %r8, 136(%rsi) -/* movq %r9, 144(%rsi) */ -/* movq %r9, 152(%rsi) */ -#ifndef MOVQ_FIX - movq %r9, %xmm9 - movlhps %xmm9, %xmm9 -#else - movq %r9, 0(%rsi) - movq %r9, 8(%rsi) - movdqa 0(%rsi), %xmm9 -#endif - movq %r10, 160(%rsi) - movq %r10, 168(%rsi) -/* movq %r11, 176(%rsi) */ -/* movq %r11, 184(%rsi) */ -#ifndef MOVQ_FIX - movq %r11, %xmm11 - movlhps %xmm11, %xmm11 -#else - movq %r11, 0(%rsi) - movq %r11, 8(%rsi) - movdqa 0(%rsi), %xmm11 -#endif - movq $0x510E527F510E527F, %r12 - movq $0x9B05688C9B05688C, %r13 - movq $0x1F83D9AB1F83D9AB, %r14 - movq $0x5BE0CD195BE0CD19, %r15 - movq 128(%rdi), %rax - movq 136(%rdi), %rbx - movq 144(%rdi), %rcx - movq 152(%rdi), %rdx - movq 160(%rdi), %r8 - movq 168(%rdi), %r9 - movq 176(%rdi), %r10 - movq 184(%rdi), %r11 -/* movdqa 0(%rsi), %xmm0 */ /* A */ - paddd 192(%rdi), %xmm0 /* A */ - xorq %r12, %rax -/* movdqa 32(%rsi), %xmm4 */ /* C */ - paddd 256(%rdi), %xmm4 /* C */ - xorq %r12, %rbx -/* movdqa 64(%rsi), %xmm12 */ /* A */ - paddd %xmm12, %xmm0 /* A */ - xorq %r13, %rcx -/* movdqa 96(%rsi), %xmm13 */ /* C */ - paddd %xmm13, %xmm4 /* C */ - xorq %r13, %rdx - movdqa 128(%rsi), %xmm2 /* A */ - xorq %r14, %r8 - xorq %r14, %r9 - movdqa 160(%rsi), %xmm6 /* C */ - xorq %r15, %r10 - xorq %r15, %r11 -/* movq %rax, 192(%rsi) */ -/* movq %rbx, 200(%rsi) */ -/* movdqa 192(%rsi), %xmm3 */ /* A */ -#ifndef MOVQ_FIX - movq %rax, %xmm3 - movq %rbx, %xmm15 - movlhps %xmm15, %xmm3 -#else - movq %rax, 0(%rsi) - movq %rbx, 8(%rsi) - movdqa 0(%rsi), %xmm3 -#endif - movq %rcx, 208(%rsi) - pxor %xmm0, %xmm3 /* A */ - movq %rdx, 216(%rsi) -/* movq %r8, 224(%rsi) */ -/* movq %r9, 232(%rsi) */ -/* movdqa 224(%rsi), %xmm7 */ /* C */ -#ifndef MOVQ_FIX - movq %r8, %xmm7 - movq %r9, %xmm15 - movlhps %xmm15, %xmm7 -#else - movq %r8, 0(%rsi) - movq %r9, 8(%rsi) - movdqa 0(%rsi), %xmm7 -#endif - movq %r10, 240(%rsi) - pxor %xmm4, %xmm7 /* C */ - movq %r11, 248(%rsi) -/* round 0 (A and C) */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - paddd 208(%rdi), %xmm0 /* A */ - paddd 272(%rdi), %xmm4 /* C */ - pslld $16, %xmm3 /* A */ - psrld $16, %xmm14 /* A */ - pslld $16, %xmm7 /* C */ - psrld $16, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ - pslld $20, %xmm12 /* A */ - psrld $12, %xmm14 /* A */ - pslld $20, %xmm13 /* C */ - psrld $12, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa %xmm0, 0(%rsi) */ /* A */ -/* movdqa %xmm4, 32(%rsi) */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - pslld $24, %xmm3 /* A */ - psrld $8, %xmm14 /* A */ - pslld $24, %xmm7 /* C */ - psrld $8, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - movdqa %xmm3, 192(%rsi) /* A */ - movdqa %xmm7, 224(%rsi) /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ -/* movdqa %xmm2, 128(%rsi) */ /* A */ -/* movdqa %xmm6, 160(%rsi) */ /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ -/* movdqa 16(%rsi), %xmm8 */ /* B */ -/* movdqa 48(%rsi), %xmm10 */ /* D */ - paddd 224(%rdi), %xmm8 /* B */ - paddd 288(%rdi), %xmm10 /* D */ - pslld $25, %xmm12 /* A */ - psrld $7, %xmm14 /* A */ - pslld $25, %xmm13 /* C */ - psrld $7, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ -/* movdqa %xmm12, 64(%rsi) */ /* A */ -/* movdqa %xmm13, 96(%rsi) */ /* C */ -/* round 0 (B and D) */ -/* movdqa 80(%rsi), %xmm1 */ /* B */ -/* movdqa 112(%rsi), %xmm5 */ /* D */ - movdqa 208(%rsi), %xmm3 /* B */ - movdqa 240(%rsi), %xmm7 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa 144(%rsi), %xmm9 */ /* B */ -/* movdqa 176(%rsi), %xmm11 */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - paddd 240(%rdi), %xmm8 /* B */ - paddd 304(%rdi), %xmm10 /* D */ - pslld $16, %xmm3 /* B */ - psrld $16, %xmm14 /* B */ - pslld $16, %xmm7 /* D */ - psrld $16, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ - pslld $20, %xmm1 /* B */ - psrld $12, %xmm14 /* B */ - pslld $20, %xmm5 /* D */ - psrld $12, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa %xmm8, 16(%rsi) */ /* B */ -/* movdqa %xmm10, 48(%rsi) */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - pslld $24, %xmm3 /* B */ - psrld $8, %xmm14 /* B */ - pslld $24, %xmm7 /* D */ - psrld $8, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ -/* movdqa %xmm3, 208(%rsi) */ /* B */ -/* movdqa %xmm7, 240(%rsi) */ /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ -/* movdqa %xmm9, 144(%rsi) */ /* B */ -/* movdqa %xmm11, 176(%rsi) */ /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ -/* movdqa 0(%rsi), %xmm0 */ /* E */ -/* movdqa 32(%rsi), %xmm4 */ /* G */ - paddd 320(%rdi), %xmm0 /* E */ - paddd 384(%rdi), %xmm4 /* G */ - pslld $25, %xmm1 /* B */ - psrld $7, %xmm14 /* B */ - pslld $25, %xmm5 /* D */ - psrld $7, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ -/* movdqa %xmm1, 80(%rsi) */ /* B */ -/* movdqa %xmm5, 112(%rsi) */ /* D */ -/* round 0 (E and G) */ -/* movdqa 80(%rsi), %xmm1 */ /* E */ -/* movdqa 112(%rsi), %xmm5 */ /* G */ -/* movdqa 240(%rsi), %xmm7 */ /* E */ -/* movdqa 208(%rsi), %xmm3 */ /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa 160(%rsi), %xmm6 */ /* E */ -/* movdqa 128(%rsi), %xmm2 */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - paddd 336(%rdi), %xmm0 /* E */ - paddd 400(%rdi), %xmm4 /* G */ - pslld $16, %xmm7 /* E */ - psrld $16, %xmm14 /* E */ - pslld $16, %xmm3 /* G */ - psrld $16, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ - pslld $20, %xmm1 /* E */ - psrld $12, %xmm14 /* E */ - pslld $20, %xmm5 /* G */ - psrld $12, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa %xmm0, 0(%rsi) */ /* E */ -/* movdqa %xmm4, 32(%rsi) */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - pslld $24, %xmm7 /* E */ - psrld $8, %xmm14 /* E */ - pslld $24, %xmm3 /* G */ - psrld $8, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - movdqa %xmm7, 240(%rsi) /* E */ - movdqa %xmm3, 208(%rsi) /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ -/* movdqa %xmm6, 160(%rsi) */ /* E */ -/* movdqa %xmm2, 128(%rsi) */ /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ -/* movdqa 16(%rsi), %xmm8 */ /* F */ -/* movdqa 48(%rsi), %xmm10 */ /* H */ - paddd 352(%rdi), %xmm8 /* F */ - paddd 416(%rdi), %xmm10 /* H */ - pslld $25, %xmm1 /* E */ - psrld $7, %xmm14 /* E */ - pslld $25, %xmm5 /* G */ - psrld $7, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ -/* movdqa %xmm1, 80(%rsi) */ /* E */ -/* movdqa %xmm5, 112(%rsi) */ /* G */ -/* round 0 (F and H) */ -/* movdqa 96(%rsi), %xmm13 */ /* F */ -/* movdqa 64(%rsi), %xmm12 */ /* H */ - movdqa 192(%rsi), %xmm3 /* F */ - movdqa 224(%rsi), %xmm7 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa 176(%rsi), %xmm11 */ /* F */ -/* movdqa 144(%rsi), %xmm9 */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - paddd 368(%rdi), %xmm8 /* F */ - paddd 432(%rdi), %xmm10 /* H */ - pslld $16, %xmm3 /* F */ - psrld $16, %xmm14 /* F */ - pslld $16, %xmm7 /* H */ - psrld $16, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - pslld $20, %xmm13 /* F */ - psrld $12, %xmm14 /* F */ - pslld $20, %xmm12 /* H */ - psrld $12, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa %xmm8, 16(%rsi) */ /* F */ -/* movdqa %xmm10, 48(%rsi) */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - pslld $24, %xmm3 /* F */ - psrld $8, %xmm14 /* F */ - pslld $24, %xmm7 /* H */ - psrld $8, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ -/* movdqa %xmm3, 192(%rsi) */ /* F */ -/* movdqa %xmm7, 224(%rsi) */ /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ -/* movdqa %xmm11, 176(%rsi) */ /* F */ -/* movdqa %xmm9, 144(%rsi) */ /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ -/* movdqa 0(%rsi), %xmm0 */ /* A */ -/* movdqa 32(%rsi), %xmm4 */ /* C */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - paddd 416(%rdi), %xmm0 /* A */ - paddd 336(%rdi), %xmm4 /* C */ - pslld $25, %xmm13 /* F */ - psrld $7, %xmm14 /* F */ - pslld $25, %xmm12 /* H */ - psrld $7, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ -/* movdqa %xmm13, 96(%rsi) */ /* F */ -/* movdqa %xmm12, 64(%rsi) */ /* H */ -/* round 1 (A and C) */ -/* movdqa 64(%rsi), %xmm12 */ /* A */ -/* movdqa 96(%rsi), %xmm13 */ /* C */ -/* movdqa 192(%rsi), %xmm3 */ /* A */ -/* movdqa 224(%rsi), %xmm7 */ /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa 128(%rsi), %xmm2 */ /* A */ -/* movdqa 160(%rsi), %xmm6 */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - paddd 352(%rdi), %xmm0 /* A */ - paddd 432(%rdi), %xmm4 /* C */ - pslld $16, %xmm3 /* A */ - psrld $16, %xmm14 /* A */ - pslld $16, %xmm7 /* C */ - psrld $16, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ - pslld $20, %xmm12 /* A */ - psrld $12, %xmm14 /* A */ - pslld $20, %xmm13 /* C */ - psrld $12, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa %xmm0, 0(%rsi) */ /* A */ -/* movdqa %xmm4, 32(%rsi) */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - pslld $24, %xmm3 /* A */ - psrld $8, %xmm14 /* A */ - pslld $24, %xmm7 /* C */ - psrld $8, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - movdqa %xmm3, 192(%rsi) /* A */ - movdqa %xmm7, 224(%rsi) /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ -/* movdqa %xmm2, 128(%rsi) */ /* A */ -/* movdqa %xmm6, 160(%rsi) */ /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ -/* movdqa 16(%rsi), %xmm8 */ /* B */ -/* movdqa 48(%rsi), %xmm10 */ /* D */ - paddd 256(%rdi), %xmm8 /* B */ - paddd 400(%rdi), %xmm10 /* D */ - pslld $25, %xmm12 /* A */ - psrld $7, %xmm14 /* A */ - pslld $25, %xmm13 /* C */ - psrld $7, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ -/* movdqa %xmm12, 64(%rsi) */ /* A */ -/* movdqa %xmm13, 96(%rsi) */ /* C */ -/* round 1 (B and D) */ -/* movdqa 80(%rsi), %xmm1 */ /* B */ -/* movdqa 112(%rsi), %xmm5 */ /* D */ - movdqa 208(%rsi), %xmm3 /* B */ - movdqa 240(%rsi), %xmm7 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa 144(%rsi), %xmm9 */ /* B */ -/* movdqa 176(%rsi), %xmm11 */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - paddd 320(%rdi), %xmm8 /* B */ - paddd 288(%rdi), %xmm10 /* D */ - pslld $16, %xmm3 /* B */ - psrld $16, %xmm14 /* B */ - pslld $16, %xmm7 /* D */ - psrld $16, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ - pslld $20, %xmm1 /* B */ - psrld $12, %xmm14 /* B */ - pslld $20, %xmm5 /* D */ - psrld $12, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa %xmm8, 16(%rsi) */ /* B */ -/* movdqa %xmm10, 48(%rsi) */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - pslld $24, %xmm3 /* B */ - psrld $8, %xmm14 /* B */ - pslld $24, %xmm7 /* D */ - psrld $8, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ -/* movdqa %xmm3, 208(%rsi) */ /* B */ -/* movdqa %xmm7, 240(%rsi) */ /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ -/* movdqa %xmm9, 144(%rsi) */ /* B */ -/* movdqa %xmm11, 176(%rsi) */ /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ -/* movdqa 0(%rsi), %xmm0 */ /* E */ -/* movdqa 32(%rsi), %xmm4 */ /* G */ - paddd 208(%rdi), %xmm0 /* E */ - paddd 368(%rdi), %xmm4 /* G */ - pslld $25, %xmm1 /* B */ - psrld $7, %xmm14 /* B */ - pslld $25, %xmm5 /* D */ - psrld $7, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ -/* movdqa %xmm1, 80(%rsi) */ /* B */ -/* movdqa %xmm5, 112(%rsi) */ /* D */ -/* round 1 (E and G) */ -/* movdqa 80(%rsi), %xmm1 */ /* E */ -/* movdqa 112(%rsi), %xmm5 */ /* G */ -/* movdqa 240(%rsi), %xmm7 */ /* E */ -/* movdqa 208(%rsi), %xmm3 */ /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa 160(%rsi), %xmm6 */ /* E */ -/* movdqa 128(%rsi), %xmm2 */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - paddd 384(%rdi), %xmm0 /* E */ - paddd 304(%rdi), %xmm4 /* G */ - pslld $16, %xmm7 /* E */ - psrld $16, %xmm14 /* E */ - pslld $16, %xmm3 /* G */ - psrld $16, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ - pslld $20, %xmm1 /* E */ - psrld $12, %xmm14 /* E */ - pslld $20, %xmm5 /* G */ - psrld $12, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa %xmm0, 0(%rsi) */ /* E */ -/* movdqa %xmm4, 32(%rsi) */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - pslld $24, %xmm7 /* E */ - psrld $8, %xmm14 /* E */ - pslld $24, %xmm3 /* G */ - psrld $8, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - movdqa %xmm7, 240(%rsi) /* E */ - movdqa %xmm3, 208(%rsi) /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ -/* movdqa %xmm6, 160(%rsi) */ /* E */ -/* movdqa %xmm2, 128(%rsi) */ /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ -/* movdqa 16(%rsi), %xmm8 */ /* F */ -/* movdqa 48(%rsi), %xmm10 */ /* H */ - paddd 192(%rdi), %xmm8 /* F */ - paddd 272(%rdi), %xmm10 /* H */ - pslld $25, %xmm1 /* E */ - psrld $7, %xmm14 /* E */ - pslld $25, %xmm5 /* G */ - psrld $7, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ -/* movdqa %xmm1, 80(%rsi) */ /* E */ -/* movdqa %xmm5, 112(%rsi) */ /* G */ -/* round 1 (F and H) */ -/* movdqa 96(%rsi), %xmm13 */ /* F */ -/* movdqa 64(%rsi), %xmm12 */ /* H */ - movdqa 192(%rsi), %xmm3 /* F */ - movdqa 224(%rsi), %xmm7 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa 176(%rsi), %xmm11 */ /* F */ -/* movdqa 144(%rsi), %xmm9 */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - paddd 224(%rdi), %xmm8 /* F */ - paddd 240(%rdi), %xmm10 /* H */ - pslld $16, %xmm3 /* F */ - psrld $16, %xmm14 /* F */ - pslld $16, %xmm7 /* H */ - psrld $16, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - pslld $20, %xmm13 /* F */ - psrld $12, %xmm14 /* F */ - pslld $20, %xmm12 /* H */ - psrld $12, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa %xmm8, 16(%rsi) */ /* F */ -/* movdqa %xmm10, 48(%rsi) */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - pslld $24, %xmm3 /* F */ - psrld $8, %xmm14 /* F */ - pslld $24, %xmm7 /* H */ - psrld $8, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ -/* movdqa %xmm3, 192(%rsi) */ /* F */ -/* movdqa %xmm7, 224(%rsi) */ /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ -/* movdqa %xmm11, 176(%rsi) */ /* F */ -/* movdqa %xmm9, 144(%rsi) */ /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ -/* movdqa 0(%rsi), %xmm0 */ /* A */ -/* movdqa 32(%rsi), %xmm4 */ /* C */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - paddd 368(%rdi), %xmm0 /* A */ - paddd 272(%rdi), %xmm4 /* C */ - pslld $25, %xmm13 /* F */ - psrld $7, %xmm14 /* F */ - pslld $25, %xmm12 /* H */ - psrld $7, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ -/* movdqa %xmm13, 96(%rsi) */ /* F */ -/* movdqa %xmm12, 64(%rsi) */ /* H */ -/* round 2 (A and C) */ -/* movdqa 64(%rsi), %xmm12 */ /* A */ -/* movdqa 96(%rsi), %xmm13 */ /* C */ -/* movdqa 192(%rsi), %xmm3 */ /* A */ -/* movdqa 224(%rsi), %xmm7 */ /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa 128(%rsi), %xmm2 */ /* A */ -/* movdqa 160(%rsi), %xmm6 */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - paddd 320(%rdi), %xmm0 /* A */ - paddd 224(%rdi), %xmm4 /* C */ - pslld $16, %xmm3 /* A */ - psrld $16, %xmm14 /* A */ - pslld $16, %xmm7 /* C */ - psrld $16, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ - pslld $20, %xmm12 /* A */ - psrld $12, %xmm14 /* A */ - pslld $20, %xmm13 /* C */ - psrld $12, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa %xmm0, 0(%rsi) */ /* A */ -/* movdqa %xmm4, 32(%rsi) */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - pslld $24, %xmm3 /* A */ - psrld $8, %xmm14 /* A */ - pslld $24, %xmm7 /* C */ - psrld $8, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - movdqa %xmm3, 192(%rsi) /* A */ - movdqa %xmm7, 224(%rsi) /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ -/* movdqa %xmm2, 128(%rsi) */ /* A */ -/* movdqa %xmm6, 160(%rsi) */ /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ -/* movdqa 16(%rsi), %xmm8 */ /* B */ -/* movdqa 48(%rsi), %xmm10 */ /* D */ - paddd 384(%rdi), %xmm8 /* B */ - paddd 432(%rdi), %xmm10 /* D */ - pslld $25, %xmm12 /* A */ - psrld $7, %xmm14 /* A */ - pslld $25, %xmm13 /* C */ - psrld $7, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ -/* movdqa %xmm12, 64(%rsi) */ /* A */ -/* movdqa %xmm13, 96(%rsi) */ /* C */ -/* round 2 (B and D) */ -/* movdqa 80(%rsi), %xmm1 */ /* B */ -/* movdqa 112(%rsi), %xmm5 */ /* D */ - movdqa 208(%rsi), %xmm3 /* B */ - movdqa 240(%rsi), %xmm7 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa 144(%rsi), %xmm9 */ /* B */ -/* movdqa 176(%rsi), %xmm11 */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - paddd 192(%rdi), %xmm8 /* B */ - paddd 400(%rdi), %xmm10 /* D */ - pslld $16, %xmm3 /* B */ - psrld $16, %xmm14 /* B */ - pslld $16, %xmm7 /* D */ - psrld $16, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ - pslld $20, %xmm1 /* B */ - psrld $12, %xmm14 /* B */ - pslld $20, %xmm5 /* D */ - psrld $12, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa %xmm8, 16(%rsi) */ /* B */ -/* movdqa %xmm10, 48(%rsi) */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - pslld $24, %xmm3 /* B */ - psrld $8, %xmm14 /* B */ - pslld $24, %xmm7 /* D */ - psrld $8, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ -/* movdqa %xmm3, 208(%rsi) */ /* B */ -/* movdqa %xmm7, 240(%rsi) */ /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ -/* movdqa %xmm9, 144(%rsi) */ /* B */ -/* movdqa %xmm11, 176(%rsi) */ /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ -/* movdqa 0(%rsi), %xmm0 */ /* E */ -/* movdqa 32(%rsi), %xmm4 */ /* G */ - paddd 352(%rdi), %xmm0 /* E */ - paddd 304(%rdi), %xmm4 /* G */ - pslld $25, %xmm1 /* B */ - psrld $7, %xmm14 /* B */ - pslld $25, %xmm5 /* D */ - psrld $7, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ -/* movdqa %xmm1, 80(%rsi) */ /* B */ -/* movdqa %xmm5, 112(%rsi) */ /* D */ -/* round 2 (E and G) */ -/* movdqa 80(%rsi), %xmm1 */ /* E */ -/* movdqa 112(%rsi), %xmm5 */ /* G */ -/* movdqa 240(%rsi), %xmm7 */ /* E */ -/* movdqa 208(%rsi), %xmm3 */ /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa 160(%rsi), %xmm6 */ /* E */ -/* movdqa 128(%rsi), %xmm2 */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - paddd 416(%rdi), %xmm0 /* E */ - paddd 208(%rdi), %xmm4 /* G */ - pslld $16, %xmm7 /* E */ - psrld $16, %xmm14 /* E */ - pslld $16, %xmm3 /* G */ - psrld $16, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ - pslld $20, %xmm1 /* E */ - psrld $12, %xmm14 /* E */ - pslld $20, %xmm5 /* G */ - psrld $12, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa %xmm0, 0(%rsi) */ /* E */ -/* movdqa %xmm4, 32(%rsi) */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - pslld $24, %xmm7 /* E */ - psrld $8, %xmm14 /* E */ - pslld $24, %xmm3 /* G */ - psrld $8, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - movdqa %xmm7, 240(%rsi) /* E */ - movdqa %xmm3, 208(%rsi) /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ -/* movdqa %xmm6, 160(%rsi) */ /* E */ -/* movdqa %xmm2, 128(%rsi) */ /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ -/* movdqa 16(%rsi), %xmm8 */ /* F */ -/* movdqa 48(%rsi), %xmm10 */ /* H */ - paddd 240(%rdi), %xmm8 /* F */ - paddd 336(%rdi), %xmm10 /* H */ - pslld $25, %xmm1 /* E */ - psrld $7, %xmm14 /* E */ - pslld $25, %xmm5 /* G */ - psrld $7, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ -/* movdqa %xmm1, 80(%rsi) */ /* E */ -/* movdqa %xmm5, 112(%rsi) */ /* G */ -/* round 2 (F and H) */ -/* movdqa 96(%rsi), %xmm13 */ /* F */ -/* movdqa 64(%rsi), %xmm12 */ /* H */ - movdqa 192(%rsi), %xmm3 /* F */ - movdqa 224(%rsi), %xmm7 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa 176(%rsi), %xmm11 */ /* F */ -/* movdqa 144(%rsi), %xmm9 */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - paddd 288(%rdi), %xmm8 /* F */ - paddd 256(%rdi), %xmm10 /* H */ - pslld $16, %xmm3 /* F */ - psrld $16, %xmm14 /* F */ - pslld $16, %xmm7 /* H */ - psrld $16, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - pslld $20, %xmm13 /* F */ - psrld $12, %xmm14 /* F */ - pslld $20, %xmm12 /* H */ - psrld $12, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa %xmm8, 16(%rsi) */ /* F */ -/* movdqa %xmm10, 48(%rsi) */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - pslld $24, %xmm3 /* F */ - psrld $8, %xmm14 /* F */ - pslld $24, %xmm7 /* H */ - psrld $8, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ -/* movdqa %xmm3, 192(%rsi) */ /* F */ -/* movdqa %xmm7, 224(%rsi) */ /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ -/* movdqa %xmm11, 176(%rsi) */ /* F */ -/* movdqa %xmm9, 144(%rsi) */ /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ -/* movdqa 0(%rsi), %xmm0 */ /* A */ -/* movdqa 32(%rsi), %xmm4 */ /* C */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - paddd 304(%rdi), %xmm0 /* A */ - paddd 400(%rdi), %xmm4 /* C */ - pslld $25, %xmm13 /* F */ - psrld $7, %xmm14 /* F */ - pslld $25, %xmm12 /* H */ - psrld $7, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ -/* movdqa %xmm13, 96(%rsi) */ /* F */ -/* movdqa %xmm12, 64(%rsi) */ /* H */ -/* round 3 (A and C) */ -/* movdqa 64(%rsi), %xmm12 */ /* A */ -/* movdqa 96(%rsi), %xmm13 */ /* C */ -/* movdqa 192(%rsi), %xmm3 */ /* A */ -/* movdqa 224(%rsi), %xmm7 */ /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa 128(%rsi), %xmm2 */ /* A */ -/* movdqa 160(%rsi), %xmm6 */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - paddd 336(%rdi), %xmm0 /* A */ - paddd 384(%rdi), %xmm4 /* C */ - pslld $16, %xmm3 /* A */ - psrld $16, %xmm14 /* A */ - pslld $16, %xmm7 /* C */ - psrld $16, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ - pslld $20, %xmm12 /* A */ - psrld $12, %xmm14 /* A */ - pslld $20, %xmm13 /* C */ - psrld $12, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa %xmm0, 0(%rsi) */ /* A */ -/* movdqa %xmm4, 32(%rsi) */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - pslld $24, %xmm3 /* A */ - psrld $8, %xmm14 /* A */ - pslld $24, %xmm7 /* C */ - psrld $8, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - movdqa %xmm3, 192(%rsi) /* A */ - movdqa %xmm7, 224(%rsi) /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ -/* movdqa %xmm2, 128(%rsi) */ /* A */ -/* movdqa %xmm6, 160(%rsi) */ /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ -/* movdqa 16(%rsi), %xmm8 */ /* B */ -/* movdqa 48(%rsi), %xmm10 */ /* D */ - paddd 240(%rdi), %xmm8 /* B */ - paddd 368(%rdi), %xmm10 /* D */ - pslld $25, %xmm12 /* A */ - psrld $7, %xmm14 /* A */ - pslld $25, %xmm13 /* C */ - psrld $7, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ -/* movdqa %xmm12, 64(%rsi) */ /* A */ -/* movdqa %xmm13, 96(%rsi) */ /* C */ -/* round 3 (B and D) */ -/* movdqa 80(%rsi), %xmm1 */ /* B */ -/* movdqa 112(%rsi), %xmm5 */ /* D */ - movdqa 208(%rsi), %xmm3 /* B */ - movdqa 240(%rsi), %xmm7 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa 144(%rsi), %xmm9 */ /* B */ -/* movdqa 176(%rsi), %xmm11 */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - paddd 208(%rdi), %xmm8 /* B */ - paddd 416(%rdi), %xmm10 /* D */ - pslld $16, %xmm3 /* B */ - psrld $16, %xmm14 /* B */ - pslld $16, %xmm7 /* D */ - psrld $16, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ - pslld $20, %xmm1 /* B */ - psrld $12, %xmm14 /* B */ - pslld $20, %xmm5 /* D */ - psrld $12, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa %xmm8, 16(%rsi) */ /* B */ -/* movdqa %xmm10, 48(%rsi) */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - pslld $24, %xmm3 /* B */ - psrld $8, %xmm14 /* B */ - pslld $24, %xmm7 /* D */ - psrld $8, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ -/* movdqa %xmm3, 208(%rsi) */ /* B */ -/* movdqa %xmm7, 240(%rsi) */ /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ -/* movdqa %xmm9, 144(%rsi) */ /* B */ -/* movdqa %xmm11, 176(%rsi) */ /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ -/* movdqa 0(%rsi), %xmm0 */ /* E */ -/* movdqa 32(%rsi), %xmm4 */ /* G */ - paddd 224(%rdi), %xmm0 /* E */ - paddd 256(%rdi), %xmm4 /* G */ - pslld $25, %xmm1 /* B */ - psrld $7, %xmm14 /* B */ - pslld $25, %xmm5 /* D */ - psrld $7, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ -/* movdqa %xmm1, 80(%rsi) */ /* B */ -/* movdqa %xmm5, 112(%rsi) */ /* D */ -/* round 3 (E and G) */ -/* movdqa 80(%rsi), %xmm1 */ /* E */ -/* movdqa 112(%rsi), %xmm5 */ /* G */ -/* movdqa 240(%rsi), %xmm7 */ /* E */ -/* movdqa 208(%rsi), %xmm3 */ /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa 160(%rsi), %xmm6 */ /* E */ -/* movdqa 128(%rsi), %xmm2 */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - paddd 288(%rdi), %xmm0 /* E */ - paddd 192(%rdi), %xmm4 /* G */ - pslld $16, %xmm7 /* E */ - psrld $16, %xmm14 /* E */ - pslld $16, %xmm3 /* G */ - psrld $16, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ - pslld $20, %xmm1 /* E */ - psrld $12, %xmm14 /* E */ - pslld $20, %xmm5 /* G */ - psrld $12, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa %xmm0, 0(%rsi) */ /* E */ -/* movdqa %xmm4, 32(%rsi) */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - pslld $24, %xmm7 /* E */ - psrld $8, %xmm14 /* E */ - pslld $24, %xmm3 /* G */ - psrld $8, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - movdqa %xmm7, 240(%rsi) /* E */ - movdqa %xmm3, 208(%rsi) /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ -/* movdqa %xmm6, 160(%rsi) */ /* E */ -/* movdqa %xmm2, 128(%rsi) */ /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ -/* movdqa 16(%rsi), %xmm8 */ /* F */ -/* movdqa 48(%rsi), %xmm10 */ /* H */ - paddd 272(%rdi), %xmm8 /* F */ - paddd 432(%rdi), %xmm10 /* H */ - pslld $25, %xmm1 /* E */ - psrld $7, %xmm14 /* E */ - pslld $25, %xmm5 /* G */ - psrld $7, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ -/* movdqa %xmm1, 80(%rsi) */ /* E */ -/* movdqa %xmm5, 112(%rsi) */ /* G */ -/* round 3 (F and H) */ -/* movdqa 96(%rsi), %xmm13 */ /* F */ -/* movdqa 64(%rsi), %xmm12 */ /* H */ - movdqa 192(%rsi), %xmm3 /* F */ - movdqa 224(%rsi), %xmm7 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa 176(%rsi), %xmm11 */ /* F */ -/* movdqa 144(%rsi), %xmm9 */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - paddd 352(%rdi), %xmm8 /* F */ - paddd 320(%rdi), %xmm10 /* H */ - pslld $16, %xmm3 /* F */ - psrld $16, %xmm14 /* F */ - pslld $16, %xmm7 /* H */ - psrld $16, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - pslld $20, %xmm13 /* F */ - psrld $12, %xmm14 /* F */ - pslld $20, %xmm12 /* H */ - psrld $12, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa %xmm8, 16(%rsi) */ /* F */ -/* movdqa %xmm10, 48(%rsi) */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - pslld $24, %xmm3 /* F */ - psrld $8, %xmm14 /* F */ - pslld $24, %xmm7 /* H */ - psrld $8, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ -/* movdqa %xmm3, 192(%rsi) */ /* F */ -/* movdqa %xmm7, 224(%rsi) */ /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ -/* movdqa %xmm11, 176(%rsi) */ /* F */ -/* movdqa %xmm9, 144(%rsi) */ /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ -/* movdqa 0(%rsi), %xmm0 */ /* A */ -/* movdqa 32(%rsi), %xmm4 */ /* C */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - paddd 336(%rdi), %xmm0 /* A */ - paddd 224(%rdi), %xmm4 /* C */ - pslld $25, %xmm13 /* F */ - psrld $7, %xmm14 /* F */ - pslld $25, %xmm12 /* H */ - psrld $7, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ -/* movdqa %xmm13, 96(%rsi) */ /* F */ -/* movdqa %xmm12, 64(%rsi) */ /* H */ -/* round 4 (A and C) */ -/* movdqa 64(%rsi), %xmm12 */ /* A */ -/* movdqa 96(%rsi), %xmm13 */ /* C */ -/* movdqa 192(%rsi), %xmm3 */ /* A */ -/* movdqa 224(%rsi), %xmm7 */ /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa 128(%rsi), %xmm2 */ /* A */ -/* movdqa 160(%rsi), %xmm6 */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - paddd 192(%rdi), %xmm0 /* A */ - paddd 256(%rdi), %xmm4 /* C */ - pslld $16, %xmm3 /* A */ - psrld $16, %xmm14 /* A */ - pslld $16, %xmm7 /* C */ - psrld $16, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ - pslld $20, %xmm12 /* A */ - psrld $12, %xmm14 /* A */ - pslld $20, %xmm13 /* C */ - psrld $12, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa %xmm0, 0(%rsi) */ /* A */ -/* movdqa %xmm4, 32(%rsi) */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - pslld $24, %xmm3 /* A */ - psrld $8, %xmm14 /* A */ - pslld $24, %xmm7 /* C */ - psrld $8, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - movdqa %xmm3, 192(%rsi) /* A */ - movdqa %xmm7, 224(%rsi) /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ -/* movdqa %xmm2, 128(%rsi) */ /* A */ -/* movdqa %xmm6, 160(%rsi) */ /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ -/* movdqa 16(%rsi), %xmm8 */ /* B */ -/* movdqa 48(%rsi), %xmm10 */ /* D */ - paddd 272(%rdi), %xmm8 /* B */ - paddd 352(%rdi), %xmm10 /* D */ - pslld $25, %xmm12 /* A */ - psrld $7, %xmm14 /* A */ - pslld $25, %xmm13 /* C */ - psrld $7, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ -/* movdqa %xmm12, 64(%rsi) */ /* A */ -/* movdqa %xmm13, 96(%rsi) */ /* C */ -/* round 4 (B and D) */ -/* movdqa 80(%rsi), %xmm1 */ /* B */ -/* movdqa 112(%rsi), %xmm5 */ /* D */ - movdqa 208(%rsi), %xmm3 /* B */ - movdqa 240(%rsi), %xmm7 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa 144(%rsi), %xmm9 */ /* B */ -/* movdqa 176(%rsi), %xmm11 */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - paddd 304(%rdi), %xmm8 /* B */ - paddd 432(%rdi), %xmm10 /* D */ - pslld $16, %xmm3 /* B */ - psrld $16, %xmm14 /* B */ - pslld $16, %xmm7 /* D */ - psrld $16, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ - pslld $20, %xmm1 /* B */ - psrld $12, %xmm14 /* B */ - pslld $20, %xmm5 /* D */ - psrld $12, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa %xmm8, 16(%rsi) */ /* B */ -/* movdqa %xmm10, 48(%rsi) */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - pslld $24, %xmm3 /* B */ - psrld $8, %xmm14 /* B */ - pslld $24, %xmm7 /* D */ - psrld $8, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ -/* movdqa %xmm3, 208(%rsi) */ /* B */ -/* movdqa %xmm7, 240(%rsi) */ /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ -/* movdqa %xmm9, 144(%rsi) */ /* B */ -/* movdqa %xmm11, 176(%rsi) */ /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ -/* movdqa 0(%rsi), %xmm0 */ /* E */ -/* movdqa 32(%rsi), %xmm4 */ /* G */ - paddd 416(%rdi), %xmm0 /* E */ - paddd 288(%rdi), %xmm4 /* G */ - pslld $25, %xmm1 /* B */ - psrld $7, %xmm14 /* B */ - pslld $25, %xmm5 /* D */ - psrld $7, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ -/* movdqa %xmm1, 80(%rsi) */ /* B */ -/* movdqa %xmm5, 112(%rsi) */ /* D */ -/* round 4 (E and G) */ -/* movdqa 80(%rsi), %xmm1 */ /* E */ -/* movdqa 112(%rsi), %xmm5 */ /* G */ -/* movdqa 240(%rsi), %xmm7 */ /* E */ -/* movdqa 208(%rsi), %xmm3 */ /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa 160(%rsi), %xmm6 */ /* E */ -/* movdqa 128(%rsi), %xmm2 */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - paddd 208(%rdi), %xmm0 /* E */ - paddd 320(%rdi), %xmm4 /* G */ - pslld $16, %xmm7 /* E */ - psrld $16, %xmm14 /* E */ - pslld $16, %xmm3 /* G */ - psrld $16, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ - pslld $20, %xmm1 /* E */ - psrld $12, %xmm14 /* E */ - pslld $20, %xmm5 /* G */ - psrld $12, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa %xmm0, 0(%rsi) */ /* E */ -/* movdqa %xmm4, 32(%rsi) */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - pslld $24, %xmm7 /* E */ - psrld $8, %xmm14 /* E */ - pslld $24, %xmm3 /* G */ - psrld $8, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - movdqa %xmm7, 240(%rsi) /* E */ - movdqa %xmm3, 208(%rsi) /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ -/* movdqa %xmm6, 160(%rsi) */ /* E */ -/* movdqa %xmm2, 128(%rsi) */ /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ -/* movdqa 16(%rsi), %xmm8 */ /* F */ -/* movdqa 48(%rsi), %xmm10 */ /* H */ - paddd 368(%rdi), %xmm8 /* F */ - paddd 240(%rdi), %xmm10 /* H */ - pslld $25, %xmm1 /* E */ - psrld $7, %xmm14 /* E */ - pslld $25, %xmm5 /* G */ - psrld $7, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ -/* movdqa %xmm1, 80(%rsi) */ /* E */ -/* movdqa %xmm5, 112(%rsi) */ /* G */ -/* round 4 (F and H) */ -/* movdqa 96(%rsi), %xmm13 */ /* F */ -/* movdqa 64(%rsi), %xmm12 */ /* H */ - movdqa 192(%rsi), %xmm3 /* F */ - movdqa 224(%rsi), %xmm7 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa 176(%rsi), %xmm11 */ /* F */ -/* movdqa 144(%rsi), %xmm9 */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - paddd 384(%rdi), %xmm8 /* F */ - paddd 400(%rdi), %xmm10 /* H */ - pslld $16, %xmm3 /* F */ - psrld $16, %xmm14 /* F */ - pslld $16, %xmm7 /* H */ - psrld $16, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - pslld $20, %xmm13 /* F */ - psrld $12, %xmm14 /* F */ - pslld $20, %xmm12 /* H */ - psrld $12, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa %xmm8, 16(%rsi) */ /* F */ -/* movdqa %xmm10, 48(%rsi) */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - pslld $24, %xmm3 /* F */ - psrld $8, %xmm14 /* F */ - pslld $24, %xmm7 /* H */ - psrld $8, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ -/* movdqa %xmm3, 192(%rsi) */ /* F */ -/* movdqa %xmm7, 224(%rsi) */ /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ -/* movdqa %xmm11, 176(%rsi) */ /* F */ -/* movdqa %xmm9, 144(%rsi) */ /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ -/* movdqa 0(%rsi), %xmm0 */ /* A */ -/* movdqa 32(%rsi), %xmm4 */ /* C */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - paddd 224(%rdi), %xmm0 /* A */ - paddd 192(%rdi), %xmm4 /* C */ - pslld $25, %xmm13 /* F */ - psrld $7, %xmm14 /* F */ - pslld $25, %xmm12 /* H */ - psrld $7, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ -/* movdqa %xmm13, 96(%rsi) */ /* F */ -/* movdqa %xmm12, 64(%rsi) */ /* H */ -/* round 5 (A and C) */ -/* movdqa 64(%rsi), %xmm12 */ /* A */ -/* movdqa 96(%rsi), %xmm13 */ /* C */ -/* movdqa 192(%rsi), %xmm3 */ /* A */ -/* movdqa 224(%rsi), %xmm7 */ /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa 128(%rsi), %xmm2 */ /* A */ -/* movdqa 160(%rsi), %xmm6 */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - paddd 384(%rdi), %xmm0 /* A */ - paddd 368(%rdi), %xmm4 /* C */ - pslld $16, %xmm3 /* A */ - psrld $16, %xmm14 /* A */ - pslld $16, %xmm7 /* C */ - psrld $16, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ - pslld $20, %xmm12 /* A */ - psrld $12, %xmm14 /* A */ - pslld $20, %xmm13 /* C */ - psrld $12, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa %xmm0, 0(%rsi) */ /* A */ -/* movdqa %xmm4, 32(%rsi) */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - pslld $24, %xmm3 /* A */ - psrld $8, %xmm14 /* A */ - pslld $24, %xmm7 /* C */ - psrld $8, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - movdqa %xmm3, 192(%rsi) /* A */ - movdqa %xmm7, 224(%rsi) /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ -/* movdqa %xmm2, 128(%rsi) */ /* A */ -/* movdqa %xmm6, 160(%rsi) */ /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ -/* movdqa 16(%rsi), %xmm8 */ /* B */ -/* movdqa 48(%rsi), %xmm10 */ /* D */ - paddd 288(%rdi), %xmm8 /* B */ - paddd 320(%rdi), %xmm10 /* D */ - pslld $25, %xmm12 /* A */ - psrld $7, %xmm14 /* A */ - pslld $25, %xmm13 /* C */ - psrld $7, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ -/* movdqa %xmm12, 64(%rsi) */ /* A */ -/* movdqa %xmm13, 96(%rsi) */ /* C */ -/* round 5 (B and D) */ -/* movdqa 80(%rsi), %xmm1 */ /* B */ -/* movdqa 112(%rsi), %xmm5 */ /* D */ - movdqa 208(%rsi), %xmm3 /* B */ - movdqa 240(%rsi), %xmm7 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa 144(%rsi), %xmm9 */ /* B */ -/* movdqa 176(%rsi), %xmm11 */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - paddd 352(%rdi), %xmm8 /* B */ - paddd 240(%rdi), %xmm10 /* D */ - pslld $16, %xmm3 /* B */ - psrld $16, %xmm14 /* B */ - pslld $16, %xmm7 /* D */ - psrld $16, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ - pslld $20, %xmm1 /* B */ - psrld $12, %xmm14 /* B */ - pslld $20, %xmm5 /* D */ - psrld $12, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa %xmm8, 16(%rsi) */ /* B */ -/* movdqa %xmm10, 48(%rsi) */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - pslld $24, %xmm3 /* B */ - psrld $8, %xmm14 /* B */ - pslld $24, %xmm7 /* D */ - psrld $8, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ -/* movdqa %xmm3, 208(%rsi) */ /* B */ -/* movdqa %xmm7, 240(%rsi) */ /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ -/* movdqa %xmm9, 144(%rsi) */ /* B */ -/* movdqa %xmm11, 176(%rsi) */ /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ -/* movdqa 0(%rsi), %xmm0 */ /* E */ -/* movdqa 32(%rsi), %xmm4 */ /* G */ - paddd 256(%rdi), %xmm0 /* E */ - paddd 432(%rdi), %xmm4 /* G */ - pslld $25, %xmm1 /* B */ - psrld $7, %xmm14 /* B */ - pslld $25, %xmm5 /* D */ - psrld $7, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ -/* movdqa %xmm1, 80(%rsi) */ /* B */ -/* movdqa %xmm5, 112(%rsi) */ /* D */ -/* round 5 (E and G) */ -/* movdqa 80(%rsi), %xmm1 */ /* E */ -/* movdqa 112(%rsi), %xmm5 */ /* G */ -/* movdqa 240(%rsi), %xmm7 */ /* E */ -/* movdqa 208(%rsi), %xmm3 */ /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa 160(%rsi), %xmm6 */ /* E */ -/* movdqa 128(%rsi), %xmm2 */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - paddd 400(%rdi), %xmm0 /* E */ - paddd 416(%rdi), %xmm4 /* G */ - pslld $16, %xmm7 /* E */ - psrld $16, %xmm14 /* E */ - pslld $16, %xmm3 /* G */ - psrld $16, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ - pslld $20, %xmm1 /* E */ - psrld $12, %xmm14 /* E */ - pslld $20, %xmm5 /* G */ - psrld $12, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa %xmm0, 0(%rsi) */ /* E */ -/* movdqa %xmm4, 32(%rsi) */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - pslld $24, %xmm7 /* E */ - psrld $8, %xmm14 /* E */ - pslld $24, %xmm3 /* G */ - psrld $8, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - movdqa %xmm7, 240(%rsi) /* E */ - movdqa %xmm3, 208(%rsi) /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ -/* movdqa %xmm6, 160(%rsi) */ /* E */ -/* movdqa %xmm2, 128(%rsi) */ /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ -/* movdqa 16(%rsi), %xmm8 */ /* F */ -/* movdqa 48(%rsi), %xmm10 */ /* H */ - paddd 304(%rdi), %xmm8 /* F */ - paddd 208(%rdi), %xmm10 /* H */ - pslld $25, %xmm1 /* E */ - psrld $7, %xmm14 /* E */ - pslld $25, %xmm5 /* G */ - psrld $7, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ -/* movdqa %xmm1, 80(%rsi) */ /* E */ -/* movdqa %xmm5, 112(%rsi) */ /* G */ -/* round 5 (F and H) */ -/* movdqa 96(%rsi), %xmm13 */ /* F */ -/* movdqa 64(%rsi), %xmm12 */ /* H */ - movdqa 192(%rsi), %xmm3 /* F */ - movdqa 224(%rsi), %xmm7 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa 176(%rsi), %xmm11 */ /* F */ -/* movdqa 144(%rsi), %xmm9 */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - paddd 272(%rdi), %xmm8 /* F */ - paddd 336(%rdi), %xmm10 /* H */ - pslld $16, %xmm3 /* F */ - psrld $16, %xmm14 /* F */ - pslld $16, %xmm7 /* H */ - psrld $16, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - pslld $20, %xmm13 /* F */ - psrld $12, %xmm14 /* F */ - pslld $20, %xmm12 /* H */ - psrld $12, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa %xmm8, 16(%rsi) */ /* F */ -/* movdqa %xmm10, 48(%rsi) */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - pslld $24, %xmm3 /* F */ - psrld $8, %xmm14 /* F */ - pslld $24, %xmm7 /* H */ - psrld $8, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ -/* movdqa %xmm3, 192(%rsi) */ /* F */ -/* movdqa %xmm7, 224(%rsi) */ /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ -/* movdqa %xmm11, 176(%rsi) */ /* F */ -/* movdqa %xmm9, 144(%rsi) */ /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ -/* movdqa 0(%rsi), %xmm0 */ /* A */ -/* movdqa 32(%rsi), %xmm4 */ /* C */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - paddd 384(%rdi), %xmm0 /* A */ - paddd 416(%rdi), %xmm4 /* C */ - pslld $25, %xmm13 /* F */ - psrld $7, %xmm14 /* F */ - pslld $25, %xmm12 /* H */ - psrld $7, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ -/* movdqa %xmm13, 96(%rsi) */ /* F */ -/* movdqa %xmm12, 64(%rsi) */ /* H */ -/* round 6 (A and C) */ -/* movdqa 64(%rsi), %xmm12 */ /* A */ -/* movdqa 96(%rsi), %xmm13 */ /* C */ -/* movdqa 192(%rsi), %xmm3 */ /* A */ -/* movdqa 224(%rsi), %xmm7 */ /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa 128(%rsi), %xmm2 */ /* A */ -/* movdqa 160(%rsi), %xmm6 */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - paddd 272(%rdi), %xmm0 /* A */ - paddd 400(%rdi), %xmm4 /* C */ - pslld $16, %xmm3 /* A */ - psrld $16, %xmm14 /* A */ - pslld $16, %xmm7 /* C */ - psrld $16, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ - pslld $20, %xmm12 /* A */ - psrld $12, %xmm14 /* A */ - pslld $20, %xmm13 /* C */ - psrld $12, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa %xmm0, 0(%rsi) */ /* A */ -/* movdqa %xmm4, 32(%rsi) */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - pslld $24, %xmm3 /* A */ - psrld $8, %xmm14 /* A */ - pslld $24, %xmm7 /* C */ - psrld $8, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - movdqa %xmm3, 192(%rsi) /* A */ - movdqa %xmm7, 224(%rsi) /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ -/* movdqa %xmm2, 128(%rsi) */ /* A */ -/* movdqa %xmm6, 160(%rsi) */ /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ -/* movdqa 16(%rsi), %xmm8 */ /* B */ -/* movdqa 48(%rsi), %xmm10 */ /* D */ - paddd 208(%rdi), %xmm8 /* B */ - paddd 256(%rdi), %xmm10 /* D */ - pslld $25, %xmm12 /* A */ - psrld $7, %xmm14 /* A */ - pslld $25, %xmm13 /* C */ - psrld $7, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ -/* movdqa %xmm12, 64(%rsi) */ /* A */ -/* movdqa %xmm13, 96(%rsi) */ /* C */ -/* round 6 (B and D) */ -/* movdqa 80(%rsi), %xmm1 */ /* B */ -/* movdqa 112(%rsi), %xmm5 */ /* D */ - movdqa 208(%rsi), %xmm3 /* B */ - movdqa 240(%rsi), %xmm7 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa 144(%rsi), %xmm9 */ /* B */ -/* movdqa 176(%rsi), %xmm11 */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - paddd 432(%rdi), %xmm8 /* B */ - paddd 352(%rdi), %xmm10 /* D */ - pslld $16, %xmm3 /* B */ - psrld $16, %xmm14 /* B */ - pslld $16, %xmm7 /* D */ - psrld $16, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ - pslld $20, %xmm1 /* B */ - psrld $12, %xmm14 /* B */ - pslld $20, %xmm5 /* D */ - psrld $12, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa %xmm8, 16(%rsi) */ /* B */ -/* movdqa %xmm10, 48(%rsi) */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - pslld $24, %xmm3 /* B */ - psrld $8, %xmm14 /* B */ - pslld $24, %xmm7 /* D */ - psrld $8, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ -/* movdqa %xmm3, 208(%rsi) */ /* B */ -/* movdqa %xmm7, 240(%rsi) */ /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ -/* movdqa %xmm9, 144(%rsi) */ /* B */ -/* movdqa %xmm11, 176(%rsi) */ /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ -/* movdqa 0(%rsi), %xmm0 */ /* E */ -/* movdqa 32(%rsi), %xmm4 */ /* G */ - paddd 192(%rdi), %xmm0 /* E */ - paddd 336(%rdi), %xmm4 /* G */ - pslld $25, %xmm1 /* B */ - psrld $7, %xmm14 /* B */ - pslld $25, %xmm5 /* D */ - psrld $7, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ -/* movdqa %xmm1, 80(%rsi) */ /* B */ -/* movdqa %xmm5, 112(%rsi) */ /* D */ -/* round 6 (E and G) */ -/* movdqa 80(%rsi), %xmm1 */ /* E */ -/* movdqa 112(%rsi), %xmm5 */ /* G */ -/* movdqa 240(%rsi), %xmm7 */ /* E */ -/* movdqa 208(%rsi), %xmm3 */ /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa 160(%rsi), %xmm6 */ /* E */ -/* movdqa 128(%rsi), %xmm2 */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - paddd 304(%rdi), %xmm0 /* E */ - paddd 224(%rdi), %xmm4 /* G */ - pslld $16, %xmm7 /* E */ - psrld $16, %xmm14 /* E */ - pslld $16, %xmm3 /* G */ - psrld $16, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ - pslld $20, %xmm1 /* E */ - psrld $12, %xmm14 /* E */ - pslld $20, %xmm5 /* G */ - psrld $12, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa %xmm0, 0(%rsi) */ /* E */ -/* movdqa %xmm4, 32(%rsi) */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - pslld $24, %xmm7 /* E */ - psrld $8, %xmm14 /* E */ - pslld $24, %xmm3 /* G */ - psrld $8, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - movdqa %xmm7, 240(%rsi) /* E */ - movdqa %xmm3, 208(%rsi) /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ -/* movdqa %xmm6, 160(%rsi) */ /* E */ -/* movdqa %xmm2, 128(%rsi) */ /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ -/* movdqa 16(%rsi), %xmm8 */ /* F */ -/* movdqa 48(%rsi), %xmm10 */ /* H */ - paddd 288(%rdi), %xmm8 /* F */ - paddd 320(%rdi), %xmm10 /* H */ - pslld $25, %xmm1 /* E */ - psrld $7, %xmm14 /* E */ - pslld $25, %xmm5 /* G */ - psrld $7, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ -/* movdqa %xmm1, 80(%rsi) */ /* E */ -/* movdqa %xmm5, 112(%rsi) */ /* G */ -/* round 6 (F and H) */ -/* movdqa 96(%rsi), %xmm13 */ /* F */ -/* movdqa 64(%rsi), %xmm12 */ /* H */ - movdqa 192(%rsi), %xmm3 /* F */ - movdqa 224(%rsi), %xmm7 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa 176(%rsi), %xmm11 */ /* F */ -/* movdqa 144(%rsi), %xmm9 */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - paddd 240(%rdi), %xmm8 /* F */ - paddd 368(%rdi), %xmm10 /* H */ - pslld $16, %xmm3 /* F */ - psrld $16, %xmm14 /* F */ - pslld $16, %xmm7 /* H */ - psrld $16, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - pslld $20, %xmm13 /* F */ - psrld $12, %xmm14 /* F */ - pslld $20, %xmm12 /* H */ - psrld $12, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa %xmm8, 16(%rsi) */ /* F */ -/* movdqa %xmm10, 48(%rsi) */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - pslld $24, %xmm3 /* F */ - psrld $8, %xmm14 /* F */ - pslld $24, %xmm7 /* H */ - psrld $8, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ -/* movdqa %xmm3, 192(%rsi) */ /* F */ -/* movdqa %xmm7, 224(%rsi) */ /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ -/* movdqa %xmm11, 176(%rsi) */ /* F */ -/* movdqa %xmm9, 144(%rsi) */ /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ -/* movdqa 0(%rsi), %xmm0 */ /* A */ -/* movdqa 32(%rsi), %xmm4 */ /* C */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - paddd 400(%rdi), %xmm0 /* A */ - paddd 384(%rdi), %xmm4 /* C */ - pslld $25, %xmm13 /* F */ - psrld $7, %xmm14 /* F */ - pslld $25, %xmm12 /* H */ - psrld $7, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ -/* movdqa %xmm13, 96(%rsi) */ /* F */ -/* movdqa %xmm12, 64(%rsi) */ /* H */ -/* round 7 (A and C) */ -/* movdqa 64(%rsi), %xmm12 */ /* A */ -/* movdqa 96(%rsi), %xmm13 */ /* C */ -/* movdqa 192(%rsi), %xmm3 */ /* A */ -/* movdqa 224(%rsi), %xmm7 */ /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa 128(%rsi), %xmm2 */ /* A */ -/* movdqa 160(%rsi), %xmm6 */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - paddd 368(%rdi), %xmm0 /* A */ - paddd 208(%rdi), %xmm4 /* C */ - pslld $16, %xmm3 /* A */ - psrld $16, %xmm14 /* A */ - pslld $16, %xmm7 /* C */ - psrld $16, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ - pslld $20, %xmm12 /* A */ - psrld $12, %xmm14 /* A */ - pslld $20, %xmm13 /* C */ - psrld $12, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa %xmm0, 0(%rsi) */ /* A */ -/* movdqa %xmm4, 32(%rsi) */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - pslld $24, %xmm3 /* A */ - psrld $8, %xmm14 /* A */ - pslld $24, %xmm7 /* C */ - psrld $8, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - movdqa %xmm3, 192(%rsi) /* A */ - movdqa %xmm7, 224(%rsi) /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ -/* movdqa %xmm2, 128(%rsi) */ /* A */ -/* movdqa %xmm6, 160(%rsi) */ /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ -/* movdqa 16(%rsi), %xmm8 */ /* B */ -/* movdqa 48(%rsi), %xmm10 */ /* D */ - paddd 304(%rdi), %xmm8 /* B */ - paddd 240(%rdi), %xmm10 /* D */ - pslld $25, %xmm12 /* A */ - psrld $7, %xmm14 /* A */ - pslld $25, %xmm13 /* C */ - psrld $7, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ -/* movdqa %xmm12, 64(%rsi) */ /* A */ -/* movdqa %xmm13, 96(%rsi) */ /* C */ -/* round 7 (B and D) */ -/* movdqa 80(%rsi), %xmm1 */ /* B */ -/* movdqa 112(%rsi), %xmm5 */ /* D */ - movdqa 208(%rsi), %xmm3 /* B */ - movdqa 240(%rsi), %xmm7 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa 144(%rsi), %xmm9 */ /* B */ -/* movdqa 176(%rsi), %xmm11 */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - paddd 416(%rdi), %xmm8 /* B */ - paddd 336(%rdi), %xmm10 /* D */ - pslld $16, %xmm3 /* B */ - psrld $16, %xmm14 /* B */ - pslld $16, %xmm7 /* D */ - psrld $16, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ - pslld $20, %xmm1 /* B */ - psrld $12, %xmm14 /* B */ - pslld $20, %xmm5 /* D */ - psrld $12, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa %xmm8, 16(%rsi) */ /* B */ -/* movdqa %xmm10, 48(%rsi) */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - pslld $24, %xmm3 /* B */ - psrld $8, %xmm14 /* B */ - pslld $24, %xmm7 /* D */ - psrld $8, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ -/* movdqa %xmm3, 208(%rsi) */ /* B */ -/* movdqa %xmm7, 240(%rsi) */ /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ -/* movdqa %xmm9, 144(%rsi) */ /* B */ -/* movdqa %xmm11, 176(%rsi) */ /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ -/* movdqa 0(%rsi), %xmm0 */ /* E */ -/* movdqa 32(%rsi), %xmm4 */ /* G */ - paddd 272(%rdi), %xmm0 /* E */ - paddd 320(%rdi), %xmm4 /* G */ - pslld $25, %xmm1 /* B */ - psrld $7, %xmm14 /* B */ - pslld $25, %xmm5 /* D */ - psrld $7, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ -/* movdqa %xmm1, 80(%rsi) */ /* B */ -/* movdqa %xmm5, 112(%rsi) */ /* D */ -/* round 7 (E and G) */ -/* movdqa 80(%rsi), %xmm1 */ /* E */ -/* movdqa 112(%rsi), %xmm5 */ /* G */ -/* movdqa 240(%rsi), %xmm7 */ /* E */ -/* movdqa 208(%rsi), %xmm3 */ /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa 160(%rsi), %xmm6 */ /* E */ -/* movdqa 128(%rsi), %xmm2 */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - paddd 192(%rdi), %xmm0 /* E */ - paddd 288(%rdi), %xmm4 /* G */ - pslld $16, %xmm7 /* E */ - psrld $16, %xmm14 /* E */ - pslld $16, %xmm3 /* G */ - psrld $16, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ - pslld $20, %xmm1 /* E */ - psrld $12, %xmm14 /* E */ - pslld $20, %xmm5 /* G */ - psrld $12, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa %xmm0, 0(%rsi) */ /* E */ -/* movdqa %xmm4, 32(%rsi) */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - pslld $24, %xmm7 /* E */ - psrld $8, %xmm14 /* E */ - pslld $24, %xmm3 /* G */ - psrld $8, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - movdqa %xmm7, 240(%rsi) /* E */ - movdqa %xmm3, 208(%rsi) /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ -/* movdqa %xmm6, 160(%rsi) */ /* E */ -/* movdqa %xmm2, 128(%rsi) */ /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ -/* movdqa 16(%rsi), %xmm8 */ /* F */ -/* movdqa 48(%rsi), %xmm10 */ /* H */ - paddd 432(%rdi), %xmm8 /* F */ - paddd 224(%rdi), %xmm10 /* H */ - pslld $25, %xmm1 /* E */ - psrld $7, %xmm14 /* E */ - pslld $25, %xmm5 /* G */ - psrld $7, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ -/* movdqa %xmm1, 80(%rsi) */ /* E */ -/* movdqa %xmm5, 112(%rsi) */ /* G */ -/* round 7 (F and H) */ -/* movdqa 96(%rsi), %xmm13 */ /* F */ -/* movdqa 64(%rsi), %xmm12 */ /* H */ - movdqa 192(%rsi), %xmm3 /* F */ - movdqa 224(%rsi), %xmm7 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa 176(%rsi), %xmm11 */ /* F */ -/* movdqa 144(%rsi), %xmm9 */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - paddd 256(%rdi), %xmm8 /* F */ - paddd 352(%rdi), %xmm10 /* H */ - pslld $16, %xmm3 /* F */ - psrld $16, %xmm14 /* F */ - pslld $16, %xmm7 /* H */ - psrld $16, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - pslld $20, %xmm13 /* F */ - psrld $12, %xmm14 /* F */ - pslld $20, %xmm12 /* H */ - psrld $12, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa %xmm8, 16(%rsi) */ /* F */ -/* movdqa %xmm10, 48(%rsi) */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - pslld $24, %xmm3 /* F */ - psrld $8, %xmm14 /* F */ - pslld $24, %xmm7 /* H */ - psrld $8, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ -/* movdqa %xmm3, 192(%rsi) */ /* F */ -/* movdqa %xmm7, 224(%rsi) */ /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ -/* movdqa %xmm11, 176(%rsi) */ /* F */ -/* movdqa %xmm9, 144(%rsi) */ /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ -/* movdqa 0(%rsi), %xmm0 */ /* A */ -/* movdqa 32(%rsi), %xmm4 */ /* C */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - paddd 288(%rdi), %xmm0 /* A */ - paddd 368(%rdi), %xmm4 /* C */ - pslld $25, %xmm13 /* F */ - psrld $7, %xmm14 /* F */ - pslld $25, %xmm12 /* H */ - psrld $7, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ -/* movdqa %xmm13, 96(%rsi) */ /* F */ -/* movdqa %xmm12, 64(%rsi) */ /* H */ -/* round 8 (A and C) */ -/* movdqa 64(%rsi), %xmm12 */ /* A */ -/* movdqa 96(%rsi), %xmm13 */ /* C */ -/* movdqa 192(%rsi), %xmm3 */ /* A */ -/* movdqa 224(%rsi), %xmm7 */ /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa 128(%rsi), %xmm2 */ /* A */ -/* movdqa 160(%rsi), %xmm6 */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - paddd 432(%rdi), %xmm0 /* A */ - paddd 240(%rdi), %xmm4 /* C */ - pslld $16, %xmm3 /* A */ - psrld $16, %xmm14 /* A */ - pslld $16, %xmm7 /* C */ - psrld $16, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ - pslld $20, %xmm12 /* A */ - psrld $12, %xmm14 /* A */ - pslld $20, %xmm13 /* C */ - psrld $12, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa %xmm0, 0(%rsi) */ /* A */ -/* movdqa %xmm4, 32(%rsi) */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - pslld $24, %xmm3 /* A */ - psrld $8, %xmm14 /* A */ - pslld $24, %xmm7 /* C */ - psrld $8, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - movdqa %xmm3, 192(%rsi) /* A */ - movdqa %xmm7, 224(%rsi) /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ -/* movdqa %xmm2, 128(%rsi) */ /* A */ -/* movdqa %xmm6, 160(%rsi) */ /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ -/* movdqa 16(%rsi), %xmm8 */ /* B */ -/* movdqa 48(%rsi), %xmm10 */ /* D */ - paddd 416(%rdi), %xmm8 /* B */ - paddd 192(%rdi), %xmm10 /* D */ - pslld $25, %xmm12 /* A */ - psrld $7, %xmm14 /* A */ - pslld $25, %xmm13 /* C */ - psrld $7, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ -/* movdqa %xmm12, 64(%rsi) */ /* A */ -/* movdqa %xmm13, 96(%rsi) */ /* C */ -/* round 8 (B and D) */ -/* movdqa 80(%rsi), %xmm1 */ /* B */ -/* movdqa 112(%rsi), %xmm5 */ /* D */ - movdqa 208(%rsi), %xmm3 /* B */ - movdqa 240(%rsi), %xmm7 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa 144(%rsi), %xmm9 */ /* B */ -/* movdqa 176(%rsi), %xmm11 */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - paddd 336(%rdi), %xmm8 /* B */ - paddd 320(%rdi), %xmm10 /* D */ - pslld $16, %xmm3 /* B */ - psrld $16, %xmm14 /* B */ - pslld $16, %xmm7 /* D */ - psrld $16, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ - pslld $20, %xmm1 /* B */ - psrld $12, %xmm14 /* B */ - pslld $20, %xmm5 /* D */ - psrld $12, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa %xmm8, 16(%rsi) */ /* B */ -/* movdqa %xmm10, 48(%rsi) */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - pslld $24, %xmm3 /* B */ - psrld $8, %xmm14 /* B */ - pslld $24, %xmm7 /* D */ - psrld $8, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ -/* movdqa %xmm3, 208(%rsi) */ /* B */ -/* movdqa %xmm7, 240(%rsi) */ /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ -/* movdqa %xmm9, 144(%rsi) */ /* B */ -/* movdqa %xmm11, 176(%rsi) */ /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ -/* movdqa 0(%rsi), %xmm0 */ /* E */ -/* movdqa 32(%rsi), %xmm4 */ /* G */ - paddd 384(%rdi), %xmm0 /* E */ - paddd 208(%rdi), %xmm4 /* G */ - pslld $25, %xmm1 /* B */ - psrld $7, %xmm14 /* B */ - pslld $25, %xmm5 /* D */ - psrld $7, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ -/* movdqa %xmm1, 80(%rsi) */ /* B */ -/* movdqa %xmm5, 112(%rsi) */ /* D */ -/* round 8 (E and G) */ -/* movdqa 80(%rsi), %xmm1 */ /* E */ -/* movdqa 112(%rsi), %xmm5 */ /* G */ -/* movdqa 240(%rsi), %xmm7 */ /* E */ -/* movdqa 208(%rsi), %xmm3 */ /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa 160(%rsi), %xmm6 */ /* E */ -/* movdqa 128(%rsi), %xmm2 */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - paddd 224(%rdi), %xmm0 /* E */ - paddd 256(%rdi), %xmm4 /* G */ - pslld $16, %xmm7 /* E */ - psrld $16, %xmm14 /* E */ - pslld $16, %xmm3 /* G */ - psrld $16, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ - pslld $20, %xmm1 /* E */ - psrld $12, %xmm14 /* E */ - pslld $20, %xmm5 /* G */ - psrld $12, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa %xmm0, 0(%rsi) */ /* E */ -/* movdqa %xmm4, 32(%rsi) */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - pslld $24, %xmm7 /* E */ - psrld $8, %xmm14 /* E */ - pslld $24, %xmm3 /* G */ - psrld $8, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - movdqa %xmm7, 240(%rsi) /* E */ - movdqa %xmm3, 208(%rsi) /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ -/* movdqa %xmm6, 160(%rsi) */ /* E */ -/* movdqa %xmm2, 128(%rsi) */ /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ -/* movdqa 16(%rsi), %xmm8 */ /* F */ -/* movdqa 48(%rsi), %xmm10 */ /* H */ - paddd 400(%rdi), %xmm8 /* F */ - paddd 352(%rdi), %xmm10 /* H */ - pslld $25, %xmm1 /* E */ - psrld $7, %xmm14 /* E */ - pslld $25, %xmm5 /* G */ - psrld $7, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ -/* movdqa %xmm1, 80(%rsi) */ /* E */ -/* movdqa %xmm5, 112(%rsi) */ /* G */ -/* round 8 (F and H) */ -/* movdqa 96(%rsi), %xmm13 */ /* F */ -/* movdqa 64(%rsi), %xmm12 */ /* H */ - movdqa 192(%rsi), %xmm3 /* F */ - movdqa 224(%rsi), %xmm7 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa 176(%rsi), %xmm11 */ /* F */ -/* movdqa 144(%rsi), %xmm9 */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - paddd 304(%rdi), %xmm8 /* F */ - paddd 272(%rdi), %xmm10 /* H */ - pslld $16, %xmm3 /* F */ - psrld $16, %xmm14 /* F */ - pslld $16, %xmm7 /* H */ - psrld $16, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - pslld $20, %xmm13 /* F */ - psrld $12, %xmm14 /* F */ - pslld $20, %xmm12 /* H */ - psrld $12, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa %xmm8, 16(%rsi) */ /* F */ -/* movdqa %xmm10, 48(%rsi) */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - pslld $24, %xmm3 /* F */ - psrld $8, %xmm14 /* F */ - pslld $24, %xmm7 /* H */ - psrld $8, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ -/* movdqa %xmm3, 192(%rsi) */ /* F */ -/* movdqa %xmm7, 224(%rsi) */ /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ -/* movdqa %xmm11, 176(%rsi) */ /* F */ -/* movdqa %xmm9, 144(%rsi) */ /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ -/* movdqa 0(%rsi), %xmm0 */ /* A */ -/* movdqa 32(%rsi), %xmm4 */ /* C */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - paddd 352(%rdi), %xmm0 /* A */ - paddd 304(%rdi), %xmm4 /* C */ - pslld $25, %xmm13 /* F */ - psrld $7, %xmm14 /* F */ - pslld $25, %xmm12 /* H */ - psrld $7, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ -/* movdqa %xmm13, 96(%rsi) */ /* F */ -/* movdqa %xmm12, 64(%rsi) */ /* H */ -/* round 9 (A and C) */ -/* movdqa 64(%rsi), %xmm12 */ /* A */ -/* movdqa 96(%rsi), %xmm13 */ /* C */ -/* movdqa 192(%rsi), %xmm3 */ /* A */ -/* movdqa 224(%rsi), %xmm7 */ /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa 128(%rsi), %xmm2 */ /* A */ -/* movdqa 160(%rsi), %xmm6 */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - paddd 224(%rdi), %xmm0 /* A */ - paddd 288(%rdi), %xmm4 /* C */ - pslld $16, %xmm3 /* A */ - psrld $16, %xmm14 /* A */ - pslld $16, %xmm7 /* C */ - psrld $16, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ - pslld $20, %xmm12 /* A */ - psrld $12, %xmm14 /* A */ - pslld $20, %xmm13 /* C */ - psrld $12, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ - paddd %xmm12, %xmm0 /* A */ - paddd %xmm13, %xmm4 /* C */ -/* movdqa %xmm0, 0(%rsi) */ /* A */ -/* movdqa %xmm4, 32(%rsi) */ /* C */ - pxor %xmm0, %xmm3 /* A */ - pxor %xmm4, %xmm7 /* C */ - movdqa %xmm3, %xmm14 /* A */ - movdqa %xmm7, %xmm15 /* C */ - pslld $24, %xmm3 /* A */ - psrld $8, %xmm14 /* A */ - pslld $24, %xmm7 /* C */ - psrld $8, %xmm15 /* C */ - por %xmm14, %xmm3 /* A */ - por %xmm15, %xmm7 /* C */ - movdqa %xmm3, 192(%rsi) /* A */ - movdqa %xmm7, 224(%rsi) /* C */ - paddd %xmm3, %xmm2 /* A */ - paddd %xmm7, %xmm6 /* C */ -/* movdqa %xmm2, 128(%rsi) */ /* A */ -/* movdqa %xmm6, 160(%rsi) */ /* C */ - pxor %xmm2, %xmm12 /* A */ - pxor %xmm6, %xmm13 /* C */ - movdqa %xmm12, %xmm14 /* A */ - movdqa %xmm13, %xmm15 /* C */ -/* movdqa 16(%rsi), %xmm8 */ /* B */ -/* movdqa 48(%rsi), %xmm10 */ /* D */ - paddd 320(%rdi), %xmm8 /* B */ - paddd 208(%rdi), %xmm10 /* D */ - pslld $25, %xmm12 /* A */ - psrld $7, %xmm14 /* A */ - pslld $25, %xmm13 /* C */ - psrld $7, %xmm15 /* C */ - por %xmm14, %xmm12 /* A */ - por %xmm15, %xmm13 /* C */ -/* movdqa %xmm12, 64(%rsi) */ /* A */ -/* movdqa %xmm13, 96(%rsi) */ /* C */ -/* round 9 (B and D) */ -/* movdqa 80(%rsi), %xmm1 */ /* B */ -/* movdqa 112(%rsi), %xmm5 */ /* D */ - movdqa 208(%rsi), %xmm3 /* B */ - movdqa 240(%rsi), %xmm7 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa 144(%rsi), %xmm9 */ /* B */ -/* movdqa 176(%rsi), %xmm11 */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - paddd 256(%rdi), %xmm8 /* B */ - paddd 272(%rdi), %xmm10 /* D */ - pslld $16, %xmm3 /* B */ - psrld $16, %xmm14 /* B */ - pslld $16, %xmm7 /* D */ - psrld $16, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ - pslld $20, %xmm1 /* B */ - psrld $12, %xmm14 /* B */ - pslld $20, %xmm5 /* D */ - psrld $12, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ - paddd %xmm1, %xmm8 /* B */ - paddd %xmm5, %xmm10 /* D */ -/* movdqa %xmm8, 16(%rsi) */ /* B */ -/* movdqa %xmm10, 48(%rsi) */ /* D */ - pxor %xmm8, %xmm3 /* B */ - pxor %xmm10, %xmm7 /* D */ - movdqa %xmm3, %xmm14 /* B */ - movdqa %xmm7, %xmm15 /* D */ - pslld $24, %xmm3 /* B */ - psrld $8, %xmm14 /* B */ - pslld $24, %xmm7 /* D */ - psrld $8, %xmm15 /* D */ - por %xmm14, %xmm3 /* B */ - por %xmm15, %xmm7 /* D */ -/* movdqa %xmm3, 208(%rsi) */ /* B */ -/* movdqa %xmm7, 240(%rsi) */ /* D */ - paddd %xmm3, %xmm9 /* B */ - paddd %xmm7, %xmm11 /* D */ -/* movdqa %xmm9, 144(%rsi) */ /* B */ -/* movdqa %xmm11, 176(%rsi) */ /* D */ - pxor %xmm9, %xmm1 /* B */ - pxor %xmm11, %xmm5 /* D */ - movdqa %xmm1, %xmm14 /* B */ - movdqa %xmm5, %xmm15 /* D */ -/* movdqa 0(%rsi), %xmm0 */ /* E */ -/* movdqa 32(%rsi), %xmm4 */ /* G */ - paddd 432(%rdi), %xmm0 /* E */ - paddd 240(%rdi), %xmm4 /* G */ - pslld $25, %xmm1 /* B */ - psrld $7, %xmm14 /* B */ - pslld $25, %xmm5 /* D */ - psrld $7, %xmm15 /* D */ - por %xmm14, %xmm1 /* B */ - por %xmm15, %xmm5 /* D */ -/* movdqa %xmm1, 80(%rsi) */ /* B */ -/* movdqa %xmm5, 112(%rsi) */ /* D */ -/* round 9 (E and G) */ -/* movdqa 80(%rsi), %xmm1 */ /* E */ -/* movdqa 112(%rsi), %xmm5 */ /* G */ -/* movdqa 240(%rsi), %xmm7 */ /* E */ -/* movdqa 208(%rsi), %xmm3 */ /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa 160(%rsi), %xmm6 */ /* E */ -/* movdqa 128(%rsi), %xmm2 */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - paddd 368(%rdi), %xmm0 /* E */ - paddd 384(%rdi), %xmm4 /* G */ - pslld $16, %xmm7 /* E */ - psrld $16, %xmm14 /* E */ - pslld $16, %xmm3 /* G */ - psrld $16, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ - pslld $20, %xmm1 /* E */ - psrld $12, %xmm14 /* E */ - pslld $20, %xmm5 /* G */ - psrld $12, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ - paddd %xmm1, %xmm0 /* E */ - paddd %xmm5, %xmm4 /* G */ -/* movdqa %xmm0, 0(%rsi) */ /* E */ -/* movdqa %xmm4, 32(%rsi) */ /* G */ - pxor %xmm0, %xmm7 /* E */ - pxor %xmm4, %xmm3 /* G */ - movdqa %xmm7, %xmm14 /* E */ - movdqa %xmm3, %xmm15 /* G */ - pslld $24, %xmm7 /* E */ - psrld $8, %xmm14 /* E */ - pslld $24, %xmm3 /* G */ - psrld $8, %xmm15 /* G */ - por %xmm14, %xmm7 /* E */ - por %xmm15, %xmm3 /* G */ - movdqa %xmm7, 240(%rsi) /* E */ - movdqa %xmm3, 208(%rsi) /* G */ - paddd %xmm7, %xmm6 /* E */ - paddd %xmm3, %xmm2 /* G */ -/* movdqa %xmm6, 160(%rsi) */ /* E */ -/* movdqa %xmm2, 128(%rsi) */ /* G */ - pxor %xmm6, %xmm1 /* E */ - pxor %xmm2, %xmm5 /* G */ - movdqa %xmm1, %xmm14 /* E */ - movdqa %xmm5, %xmm15 /* G */ -/* movdqa 16(%rsi), %xmm8 */ /* F */ -/* movdqa 48(%rsi), %xmm10 */ /* H */ - paddd 336(%rdi), %xmm8 /* F */ - paddd 400(%rdi), %xmm10 /* H */ - pslld $25, %xmm1 /* E */ - psrld $7, %xmm14 /* E */ - pslld $25, %xmm5 /* G */ - psrld $7, %xmm15 /* G */ - por %xmm14, %xmm1 /* E */ - por %xmm15, %xmm5 /* G */ -/* movdqa %xmm1, 80(%rsi) */ /* E */ -/* movdqa %xmm5, 112(%rsi) */ /* G */ -/* round 9 (F and H) */ -/* movdqa 96(%rsi), %xmm13 */ /* F */ -/* movdqa 64(%rsi), %xmm12 */ /* H */ - movdqa 192(%rsi), %xmm3 /* F */ - movdqa 224(%rsi), %xmm7 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa 176(%rsi), %xmm11 */ /* F */ -/* movdqa 144(%rsi), %xmm9 */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - paddd 416(%rdi), %xmm8 /* F */ - paddd 192(%rdi), %xmm10 /* H */ - pslld $16, %xmm3 /* F */ - psrld $16, %xmm14 /* F */ - pslld $16, %xmm7 /* H */ - psrld $16, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - pslld $20, %xmm13 /* F */ - psrld $12, %xmm14 /* F */ - pslld $20, %xmm12 /* H */ - psrld $12, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ - paddd %xmm13, %xmm8 /* F */ - paddd %xmm12, %xmm10 /* H */ -/* movdqa %xmm8, 16(%rsi) */ /* F */ -/* movdqa %xmm10, 48(%rsi) */ /* H */ - pxor %xmm8, %xmm3 /* F */ - pxor %xmm10, %xmm7 /* H */ - movdqa %xmm3, %xmm14 /* F */ - movdqa %xmm7, %xmm15 /* H */ - pslld $24, %xmm3 /* F */ - psrld $8, %xmm14 /* F */ - pslld $24, %xmm7 /* H */ - psrld $8, %xmm15 /* H */ - por %xmm14, %xmm3 /* F */ - por %xmm15, %xmm7 /* H */ -/* movdqa %xmm3, 192(%rsi) */ /* F */ -/* movdqa %xmm7, 224(%rsi) */ /* H */ - paddd %xmm3, %xmm11 /* F */ - paddd %xmm7, %xmm9 /* H */ -/* movdqa %xmm11, 176(%rsi) */ /* F */ -/* movdqa %xmm9, 144(%rsi) */ /* H */ - pxor %xmm11, %xmm13 /* F */ - pxor %xmm9, %xmm12 /* H */ - movdqa %xmm13, %xmm14 /* F */ - movdqa %xmm12, %xmm15 /* H */ - pslld $25, %xmm13 /* F */ - psrld $7, %xmm14 /* F */ - pslld $25, %xmm12 /* H */ - psrld $7, %xmm15 /* H */ - por %xmm14, %xmm13 /* F */ - por %xmm15, %xmm12 /* H */ -/* movdqa %xmm13, 96(%rsi) */ /* F */ -/* movdqa %xmm12, 64(%rsi) */ /* H */ -/* finalise */ - movdqa 208(%rsi), %xmm14 - pxor %xmm2, %xmm0 /* 0() ^ 128() */ - pxor %xmm9, %xmm8 /* 16() ^ 144() */ - movdqa 240(%rsi), %xmm15 - pxor %xmm6, %xmm4 /* 32() ^ 160() */ - pxor %xmm11, %xmm10 /* 48() ^ 176() */ - pxor %xmm3, %xmm12 /* 64() ^ 192() */ - pxor %xmm14, %xmm1 /* 80() ^ 208() */ - pxor %xmm7, %xmm13 /* 96() ^ 224() */ - pxor %xmm15, %xmm5 /* 112() ^ 240() */ - pxor 0(%rdi), %xmm0 - pxor 16(%rdi), %xmm8 - pxor 32(%rdi), %xmm4 - pxor 48(%rdi), %xmm10 - pxor 64(%rdi), %xmm12 - pxor 80(%rdi), %xmm1 - pxor 96(%rdi), %xmm13 - pxor 112(%rdi), %xmm5 - movdqa %xmm0, 0(%rdi) - movdqa %xmm8, 16(%rdi) - movdqa %xmm4, 32(%rdi) - movdqa %xmm10, 48(%rdi) - movdqa %xmm12, 64(%rdi) - movdqa %xmm1, 80(%rdi) - movdqa %xmm13, 96(%rdi) - movdqa %xmm5, 112(%rdi) - -#ifdef WIN64 - movdqu 0(%rsp), %xmm15 - movdqu 16(%rsp), %xmm14 - movdqu 32(%rsp), %xmm13 - movdqu 48(%rsp), %xmm12 - movdqu 64(%rsp), %xmm11 - movdqu 80(%rsp), %xmm10 - movdqu 96(%rsp), %xmm9 - movdqu 112(%rsp), %xmm8 - movdqu 128(%rsp), %xmm7 - movdqu 144(%rsp), %xmm6 - addq $160, %rsp - popq %rsi - popq %rdi -#endif - popq %r15 - popq %r14 - popq %r13 - popq %r12 - popq %rbp - popq %rbx - ret - - -/* neoscrypt_blkcpy(dst, src, len) - * AMD64 (SSE2) block memcpy(); - * len must be a multiple of 64 bytes aligned properly */ -.globl neoscrypt_blkcpy -.globl _neoscrypt_blkcpy -neoscrypt_blkcpy: -_neoscrypt_blkcpy: -#ifdef WIN64 - movq %rdi, %r10 - movq %rsi, %r11 - movq %rcx, %rdi - movq %rdx, %rsi - movq %r8, %rdx -#endif - xorq %rcx, %rcx - movl %edx, %ecx - shrl $6, %ecx - movq $64, %rax -.blkcpy: - movdqa 0(%rsi), %xmm0 - movdqa 16(%rsi), %xmm1 - movdqa 32(%rsi), %xmm2 - movdqa 48(%rsi), %xmm3 - movdqa %xmm0, 0(%rdi) - movdqa %xmm1, 16(%rdi) - movdqa %xmm2, 32(%rdi) - movdqa %xmm3, 48(%rdi) - addq %rax, %rdi - addq %rax, %rsi - decl %ecx - jnz .blkcpy -#ifdef WIN64 - movq %r10, %rdi - movq %r11, %rsi -#endif - ret - - -/* neoscrypt_blkswp(blkA, blkB, len) - * AMD64 (SSE2) block swapper; - * len must be a multiple of 64 bytes aligned properly */ -.globl neoscrypt_blkswp -.globl _neoscrypt_blkswp -neoscrypt_blkswp: -_neoscrypt_blkswp: -#ifdef WIN64 - movq %rdi, %r10 - movq %rsi, %r11 - movq %rcx, %rdi - movq %rdx, %rsi - movq %r8, %rdx -#endif - xorq %rcx, %rcx - movl %edx, %ecx - shrl $6, %ecx - movq $64, %rax -.blkswp: - movdqa 0(%rdi), %xmm0 - movdqa 16(%rdi), %xmm1 - movdqa 32(%rdi), %xmm2 - movdqa 48(%rdi), %xmm3 - movdqa 0(%rsi), %xmm4 - movdqa 16(%rsi), %xmm5 - movdqa 32(%rsi), %xmm8 - movdqa 48(%rsi), %xmm9 - movdqa %xmm0, 0(%rsi) - movdqa %xmm1, 16(%rsi) - movdqa %xmm2, 32(%rsi) - movdqa %xmm3, 48(%rsi) - movdqa %xmm4, 0(%rdi) - movdqa %xmm5, 16(%rdi) - movdqa %xmm8, 32(%rdi) - movdqa %xmm9, 48(%rdi) - addq %rax, %rdi - addq %rax, %rsi - decl %ecx - jnz .blkswp -#ifdef WIN64 - movq %r10, %rdi - movq %r11, %rsi -#endif - ret - - -/* neoscrypt_blkxor(dst, src, len) - * AMD64 (SSE2) block XOR engine; - * len must be a multiple of 64 bytes aligned properly */ -.globl neoscrypt_blkxor -.globl _neoscrypt_blkxor -neoscrypt_blkxor: -_neoscrypt_blkxor: -#ifdef WIN64 - movq %rdi, %r10 - movq %rsi, %r11 - movq %rcx, %rdi - movq %rdx, %rsi - movq %r8, %rdx -#endif - xorq %rcx, %rcx - movl %edx, %ecx - shrl $6, %ecx - movq $64, %rax -.blkxor: - movdqa 0(%rdi), %xmm0 - movdqa 16(%rdi), %xmm1 - movdqa 32(%rdi), %xmm2 - movdqa 48(%rdi), %xmm3 - pxor 0(%rsi), %xmm0 - pxor 16(%rsi), %xmm1 - pxor 32(%rsi), %xmm2 - pxor 48(%rsi), %xmm3 - movdqa %xmm0, 0(%rdi) - movdqa %xmm1, 16(%rdi) - movdqa %xmm2, 32(%rdi) - movdqa %xmm3, 48(%rdi) - addq %rax, %rdi - addq %rax, %rsi - decl %ecx - jnz .blkxor -#ifdef WIN64 - movq %r10, %rdi - movq %r11, %rsi -#endif - ret - - -/* neoscrypt_pack_4way(dst, src, len) - * AMD64 4-way data packer */ -.globl neoscrypt_pack_4way -.globl _neoscrypt_pack_4way -neoscrypt_pack_4way: -_neoscrypt_pack_4way: -#ifdef WIN64 - pushq %rdi - pushq %rsi - movq %rcx, %rdi - movq %rdx, %rsi - movq %r8, %rdx -#endif - - movq %rdx, %rcx - shrq $2, %rdx - leaq 0(%rsi, %rdx, 2), %rax - shrq $4, %rcx -.pack_4way: - movl 0(%rsi), %r8d - movl 0(%rsi, %rdx), %r9d - addq $4, %rsi - movl 0(%rax), %r10d - movl 0(%rax, %rdx), %r11d - addq $4, %rax - movl %r8d, 0(%rdi) - movl %r9d, 4(%rdi) - movl %r10d, 8(%rdi) - movl %r11d, 12(%rdi) - addq $16, %rdi - decq %rcx - jnz .pack_4way - -#ifdef WIN64 - popq %rsi - popq %rdi -#endif - ret - - -/* neoscrypt_unpack_4way(dst, src, len) - * AMD64 4-way data unpacker */ -.globl neoscrypt_unpack_4way -.globl _neoscrypt_unpack_4way -neoscrypt_unpack_4way: -_neoscrypt_unpack_4way: -#ifdef WIN64 - pushq %rdi - pushq %rsi - movq %rcx, %rdi - movq %rdx, %rsi - movq %r8, %rdx -#endif - - movq %rdx, %rcx - shrq $2, %rdx - leaq 0(%rdi, %rdx, 2), %rax - shrq $4, %rcx -.unpack_4way: - movq 0(%rsi), %r8 - movq 8(%rsi), %r9 - addq $16, %rsi - movl %r8d, 0(%rdi) - shrq $32, %r8 - movl %r9d, 0(%rax) - shrq $32, %r9 - movl %r8d, 0(%rdi, %rdx) - addq $4, %rdi - movl %r9d, 0(%rax, %rdx) - addq $4, %rax - decq %rcx - jnz .unpack_4way - -#ifdef WIN64 - popq %rsi - popq %rdi -#endif - ret - - -/* neoscrypt_xor_4way(dst, srcA, srcB, srcC, srcD, len) - * AMD64 4-way XOR engine */ -.globl neoscrypt_xor_4way -.globl _neoscrypt_xor_4way -neoscrypt_xor_4way: -_neoscrypt_xor_4way: - pushq %rbx - pushq %rbp -#ifdef WIN64 - pushq %rdi - pushq %rsi - movq %rcx, %rdi - movq %rdx, %rsi - movq %r8, %rdx - movq %r9, %rbx - movq 72(%rsp), %rax - movq 80(%rsp), %rcx -#else - movq %rcx, %rbx - movq %r8, %rax - movq %r9, %rcx -#endif - - xorq %rbp, %rbp - shrq $4, %rcx -.xor_4way: - movl 0(%rsi, %rbp), %r8d - movl 4(%rdx, %rbp), %r9d - movl 8(%rbx, %rbp), %r10d - movl 12(%rax, %rbp), %r11d - xorl %r8d, 0(%rdi) - xorl %r9d, 4(%rdi) - xorl %r10d, 8(%rdi) - xorl %r11d, 12(%rdi) - addl $16, %ebp - addq $16, %rdi - decq %rcx - jnz .xor_4way - -#ifdef WIN64 - popq %rsi - popq %rdi -#endif - popq %rbp - popq %rbx - ret - - -/* neoscrypt_xor_salsa_4way(mem, xormem, workmem, double_rounds) - * AMD64 (SSE2) Salsa20 4-way with XOR */ -.globl neoscrypt_xor_salsa_4way -.globl _neoscrypt_xor_salsa_4way -neoscrypt_xor_salsa_4way: -_neoscrypt_xor_salsa_4way: -#ifdef WIN64 - movq %rdi, %r10 - movq %rsi, %r11 - movq %rcx, %rdi - movq %rdx, %rsi - movq %r8, %rdx - movq %r9, %rcx -#endif -/* XOR and copy to temporary memory */ - movdqa 0(%rdi), %xmm0 - movdqa 16(%rdi), %xmm1 - movdqa 32(%rdi), %xmm2 - movdqa 48(%rdi), %xmm3 - movdqa 64(%rdi), %xmm4 - movdqa 80(%rdi), %xmm5 - movdqa 96(%rdi), %xmm6 - movdqa 112(%rdi), %xmm7 - movdqa 128(%rdi), %xmm8 - movdqa 144(%rdi), %xmm9 - movdqa 160(%rdi), %xmm10 - movdqa 176(%rdi), %xmm11 - movdqa 192(%rdi), %xmm12 - movdqa 208(%rdi), %xmm13 - movdqa 224(%rdi), %xmm14 - movdqa 240(%rdi), %xmm15 - pxor 0(%rsi), %xmm0 - pxor 16(%rsi), %xmm1 - pxor 32(%rsi), %xmm2 - pxor 48(%rsi), %xmm3 - pxor 64(%rsi), %xmm4 - pxor 80(%rsi), %xmm5 - pxor 96(%rsi), %xmm6 - pxor 112(%rsi), %xmm7 - pxor 128(%rsi), %xmm8 - pxor 144(%rsi), %xmm9 - pxor 160(%rsi), %xmm10 - pxor 176(%rsi), %xmm11 - pxor 192(%rsi), %xmm12 - pxor 208(%rsi), %xmm13 - pxor 224(%rsi), %xmm14 - pxor 240(%rsi), %xmm15 - movdqa %xmm0, 0(%rdi) - movdqa %xmm1, 16(%rdi) - movdqa %xmm2, 32(%rdi) - movdqa %xmm3, 48(%rdi) - movdqa %xmm4, 64(%rdi) - movdqa %xmm5, 80(%rdi) - movdqa %xmm6, 96(%rdi) - movdqa %xmm7, 112(%rdi) - movdqa %xmm8, 128(%rdi) - movdqa %xmm9, 144(%rdi) - movdqa %xmm10, 160(%rdi) - movdqa %xmm11, 176(%rdi) - movdqa %xmm12, 192(%rdi) - movdqa %xmm13, 208(%rdi) - movdqa %xmm14, 224(%rdi) - movdqa %xmm15, 240(%rdi) - movdqa %xmm0, 0(%rdx) - movdqa %xmm1, 16(%rdx) - movdqa %xmm2, 32(%rdx) - movdqa %xmm3, 48(%rdx) - movdqa %xmm4, 64(%rdx) - movdqa %xmm5, 80(%rdx) - movdqa %xmm6, 96(%rdx) - movdqa %xmm7, 112(%rdx) - movdqa %xmm8, 128(%rdx) - movdqa %xmm9, 144(%rdx) - movdqa %xmm10, 160(%rdx) - movdqa %xmm11, 176(%rdx) - movdqa %xmm12, 192(%rdx) - movdqa %xmm13, 208(%rdx) - movdqa %xmm14, 224(%rdx) - movdqa %xmm15, 240(%rdx) -.xor_salsa_4way: -/* quarters A and B */ - movdqa 0(%rdx), %xmm0 /* A: load a */ - movdqa 80(%rdx), %xmm4 /* B: load a */ - paddd 192(%rdx), %xmm0 /* A: t = a + d */ - paddd 16(%rdx), %xmm4 /* B: t = a + d */ - movdqa %xmm0, %xmm3 /* A: rotate t (0) */ - movdqa %xmm4, %xmm7 /* B: rotate t (0) */ - pslld $7, %xmm0 /* A: rotate t (1) */ - psrld $25, %xmm3 /* A: rotate t (2) */ - pslld $7, %xmm4 /* B: rotate t (1) */ - psrld $25, %xmm7 /* B: rotate t (2) */ - por %xmm3, %xmm0 /* A: rotate t (3) */ - por %xmm7, %xmm4 /* B: rotate t (3) */ - pxor 64(%rdx), %xmm0 /* A: b = b ^ t */ - pxor 144(%rdx), %xmm4 /* B: b = b ^ t */ - movdqa %xmm0, %xmm1 /* A: copy b */ - movdqa %xmm4, %xmm5 /* B: copy b */ - movdqa %xmm1, 64(%rdx) /* A: store b */ - movdqa %xmm5, 144(%rdx) /* B: store b */ - paddd 0(%rdx), %xmm0 /* A: t = b + a */ - paddd 80(%rdx), %xmm4 /* B: t = b + a */ - movdqa %xmm0, %xmm3 /* A: rotate t (0) */ - movdqa %xmm4, %xmm7 /* B: rotate t (0) */ - pslld $9, %xmm0 /* A: rotate t (1) */ - psrld $23, %xmm3 /* A: rotate t (2) */ - pslld $9, %xmm4 /* B: rotate t (1) */ - psrld $23, %xmm7 /* B: rotate t (2) */ - por %xmm3, %xmm0 /* A: rotate t (3) */ - por %xmm7, %xmm4 /* B: rotate t (3) */ - pxor 128(%rdx), %xmm0 /* A: c = c ^ t */ - pxor 208(%rdx), %xmm4 /* B: c = c ^ t */ - movdqa %xmm0, %xmm2 /* A: copy c */ - movdqa %xmm4, %xmm6 /* B: copy c */ - movdqa %xmm2, 128(%rdx) /* A: store c */ - movdqa %xmm6, 208(%rdx) /* B: store c */ - paddd %xmm1, %xmm0 /* A: t = c + b */ - paddd %xmm5, %xmm4 /* B: t = c + b */ - movdqa %xmm0, %xmm3 /* A: rotate t (0) */ - movdqa %xmm4, %xmm7 /* B: rotate t (0) */ - pslld $13, %xmm0 /* A: rotate t (1) */ - psrld $19, %xmm3 /* A: rotate t (2) */ - pslld $13, %xmm4 /* B: rotate t (1) */ - psrld $19, %xmm7 /* B: rotate t (2) */ - por %xmm3, %xmm0 /* A: rotate t (3) */ - por %xmm7, %xmm4 /* B: rotate t (3) */ - pxor 192(%rdx), %xmm0 /* A: d = d ^ t */ - pxor 16(%rdx), %xmm4 /* B: d = d ^ t */ - movdqa %xmm0, 192(%rdx) /* A: store d */ - movdqa %xmm4, 16(%rdx) /* B: store d */ - paddd %xmm2, %xmm0 /* A: t = d + c */ - paddd %xmm6, %xmm4 /* B: t = d + c */ - movdqa %xmm0, %xmm3 /* A: rotate t (0) */ - movdqa %xmm4, %xmm7 /* B: rotate t (0) */ - pslld $18, %xmm0 /* A: rotate t (1) */ - psrld $14, %xmm3 /* A: rotate t (2) */ - pslld $18, %xmm4 /* B: rotate t (1) */ - psrld $14, %xmm7 /* B: rotate t (2) */ - por %xmm3, %xmm0 /* A: rotate t (3) */ - por %xmm7, %xmm4 /* B: rotate t (3) */ - pxor 0(%rdx), %xmm0 /* A: a = a ^ t */ - pxor 80(%rdx), %xmm4 /* B: a = a ^ t */ - movdqa %xmm0, 0(%rdx) /* A: store a */ - movdqa %xmm4, 80(%rdx) /* B: store a */ -/* quarters C and D*/ - movdqa 160(%rdx), %xmm0 /* C: load a */ - movdqa 240(%rdx), %xmm4 /* D: load a */ - paddd 96(%rdx), %xmm0 /* C: t = a + d */ - paddd 176(%rdx), %xmm4 /* D: t = a + d */ - movdqa %xmm0, %xmm3 /* C: rotate t (0) */ - movdqa %xmm4, %xmm7 /* D: rotate t (0) */ - pslld $7, %xmm0 /* C: rotate t (1) */ - psrld $25, %xmm3 /* C: rotate t (2) */ - pslld $7, %xmm4 /* D: rotate t (1) */ - psrld $25, %xmm7 /* D: rotate t (2) */ - por %xmm3, %xmm0 /* C: rotate t (3) */ - por %xmm7, %xmm4 /* D: rotate t (3) */ - pxor 224(%rdx), %xmm0 /* C: b = b ^ t */ - pxor 48(%rdx), %xmm4 /* D: b = b ^ t */ - movdqa %xmm0, %xmm1 /* C: copy b */ - movdqa %xmm4, %xmm5 /* D: copy b */ - movdqa %xmm1, 224(%rdx) /* C: store b */ - movdqa %xmm5, 48(%rdx) /* D: store b */ - paddd 160(%rdx), %xmm0 /* C: t = b + a */ - paddd 240(%rdx), %xmm4 /* D: t = b + a */ - movdqa %xmm0, %xmm3 /* C: rotate t (0) */ - movdqa %xmm4, %xmm7 /* D: rotate t (0) */ - pslld $9, %xmm0 /* C: rotate t (1) */ - psrld $23, %xmm3 /* C: rotate t (2) */ - pslld $9, %xmm4 /* D: rotate t (1) */ - psrld $23, %xmm7 /* D: rotate t (2) */ - por %xmm3, %xmm0 /* C: rotate t (3) */ - por %xmm7, %xmm4 /* D: rotate t (3) */ - pxor 32(%rdx), %xmm0 /* C: c = c ^ t */ - pxor 112(%rdx), %xmm4 /* D: c = c ^ t */ - movdqa %xmm0, %xmm2 /* C: copy c */ - movdqa %xmm4, %xmm6 /* D: copy c */ - movdqa %xmm2, 32(%rdx) /* C: store c */ - movdqa %xmm6, 112(%rdx) /* D: store c */ - paddd %xmm1, %xmm0 /* C: t = c + b */ - paddd %xmm5, %xmm4 /* D: t = c + b */ - movdqa %xmm0, %xmm3 /* C: rotate t (0) */ - movdqa %xmm4, %xmm7 /* D: rotate t (0) */ - pslld $13, %xmm0 /* C: rotate t (1) */ - psrld $19, %xmm3 /* C: rotate t (2) */ - pslld $13, %xmm4 /* D: rotate t (1) */ - psrld $19, %xmm7 /* D: rotate t (2) */ - por %xmm3, %xmm0 /* C: rotate t (3) */ - por %xmm7, %xmm4 /* D: rotate t (3) */ - pxor 96(%rdx), %xmm0 /* C: d = d ^ t */ - pxor 176(%rdx), %xmm4 /* D: d = d ^ t */ - movdqa %xmm0, 96(%rdx) /* C: store d */ - movdqa %xmm4, 176(%rdx) /* D: store d */ - paddd %xmm2, %xmm0 /* C: t = d + c */ - paddd %xmm6, %xmm4 /* D: t = d + c */ - movdqa %xmm0, %xmm3 /* C: rotate t (0) */ - movdqa %xmm4, %xmm7 /* D: rotate t (0) */ - pslld $18, %xmm0 /* C: rotate t (1) */ - psrld $14, %xmm3 /* C: rotate t (2) */ - pslld $18, %xmm4 /* D: rotate t (1) */ - psrld $14, %xmm7 /* D: rotate t (2) */ - por %xmm3, %xmm0 /* C: rotate t (3) */ - por %xmm7, %xmm4 /* D: rotate t (3) */ - pxor 160(%rdx), %xmm0 /* C: a = a ^ t */ - pxor 240(%rdx), %xmm4 /* D: a = a ^ t */ - movdqa %xmm0, 160(%rdx) /* C: store a */ - movdqa %xmm4, 240(%rdx) /* D: store a */ -/* quarters E and F */ - movdqa 0(%rdx), %xmm0 /* E: load a */ - movdqa 80(%rdx), %xmm4 /* F: load a */ - paddd 48(%rdx), %xmm0 /* E: t = a + d */ - paddd 64(%rdx), %xmm4 /* F: t = a + d */ - movdqa %xmm0, %xmm3 /* E: rotate t (0) */ - movdqa %xmm4, %xmm7 /* F: rotate t (0) */ - pslld $7, %xmm0 /* E: rotate t (1) */ - psrld $25, %xmm3 /* E: rotate t (2) */ - pslld $7, %xmm4 /* F: rotate t (1) */ - psrld $25, %xmm7 /* F: rotate t (2) */ - por %xmm3, %xmm0 /* E: rotate t (3) */ - por %xmm7, %xmm4 /* F: rotate t (3) */ - pxor 16(%rdx), %xmm0 /* E: b = b ^ t */ - pxor 96(%rdx), %xmm4 /* F: b = b ^ t */ - movdqa %xmm0, %xmm1 /* E: copy b */ - movdqa %xmm4, %xmm5 /* F: copy b */ - movdqa %xmm1, 16(%rdx) /* E: store b */ - movdqa %xmm5, 96(%rdx) /* F: store b */ - paddd 0(%rdx), %xmm0 /* E: t = b + a */ - paddd 80(%rdx), %xmm4 /* F: t = b + a */ - movdqa %xmm0, %xmm3 /* E: rotate t (0) */ - movdqa %xmm4, %xmm7 /* F: rotate t (0) */ - pslld $9, %xmm0 /* E: rotate t (1) */ - psrld $23, %xmm3 /* E: rotate t (2) */ - pslld $9, %xmm4 /* F: rotate t (1) */ - psrld $23, %xmm7 /* F: rotate t (2) */ - por %xmm3, %xmm0 /* E: rotate t (3) */ - por %xmm7, %xmm4 /* F: rotate t (3) */ - pxor 32(%rdx), %xmm0 /* E: c = c ^ t */ - pxor 112(%rdx), %xmm4 /* F: c = c ^ t */ - movdqa %xmm0, %xmm2 /* E: copy c */ - movdqa %xmm4, %xmm6 /* F: copy c */ - movdqa %xmm2, 32(%rdx) /* E: store c */ - movdqa %xmm6, 112(%rdx) /* F: store c */ - paddd %xmm1, %xmm0 /* E: t = c + b */ - paddd %xmm5, %xmm4 /* F: t = c + b */ - movdqa %xmm0, %xmm3 /* E: rotate t (0) */ - movdqa %xmm4, %xmm7 /* F: rotate t (0) */ - pslld $13, %xmm0 /* E: rotate t (1) */ - psrld $19, %xmm3 /* E: rotate t (2) */ - pslld $13, %xmm4 /* F: rotate t (1) */ - psrld $19, %xmm7 /* F: rotate t (2) */ - por %xmm3, %xmm0 /* E: rotate t (3) */ - por %xmm7, %xmm4 /* F: rotate t (3) */ - pxor 48(%rdx), %xmm0 /* E: d = d ^ t */ - pxor 64(%rdx), %xmm4 /* F: d = d ^ t */ - movdqa %xmm0, 48(%rdx) /* E: store d */ - movdqa %xmm4, 64(%rdx) /* F: store d */ - paddd %xmm2, %xmm0 /* E: t = d + c */ - paddd %xmm6, %xmm4 /* F: t = d + c */ - movdqa %xmm0, %xmm3 /* E: rotate t (0) */ - movdqa %xmm4, %xmm7 /* F: rotate t (0) */ - pslld $18, %xmm0 /* E: rotate t (1) */ - psrld $14, %xmm3 /* E: rotate t (2) */ - pslld $18, %xmm4 /* F: rotate t (1) */ - psrld $14, %xmm7 /* F: rotate t (2) */ - por %xmm3, %xmm0 /* E: rotate t (3) */ - por %xmm7, %xmm4 /* F: rotate t (3) */ - pxor 0(%rdx), %xmm0 /* E: a = a ^ t */ - pxor 80(%rdx), %xmm4 /* F: a = a ^ t */ - movdqa %xmm0, 0(%rdx) /* E: store a */ - movdqa %xmm4, 80(%rdx) /* F: store a */ -/* quarters G and H */ - movdqa 160(%rdx), %xmm0 /* G: load a */ - movdqa 240(%rdx), %xmm4 /* H: load a */ - paddd 144(%rdx), %xmm0 /* G: t = a + d */ - paddd 224(%rdx), %xmm4 /* H: t = a + d */ - movdqa %xmm0, %xmm3 /* G: rotate t (0) */ - movdqa %xmm4, %xmm7 /* H: rotate t (0) */ - pslld $7, %xmm0 /* G: rotate t (1) */ - psrld $25, %xmm3 /* G: rotate t (2) */ - pslld $7, %xmm4 /* H: rotate t (1) */ - psrld $25, %xmm7 /* H: rotate t (2) */ - por %xmm3, %xmm0 /* G: rotate t (3) */ - por %xmm7, %xmm4 /* H: rotate t (3) */ - pxor 176(%rdx), %xmm0 /* G: b = b ^ t */ - pxor 192(%rdx), %xmm4 /* H: b = b ^ t */ - movdqa %xmm0, %xmm1 /* G: copy b */ - movdqa %xmm4, %xmm5 /* H: copy b */ - movdqa %xmm1, 176(%rdx) /* G: store b */ - movdqa %xmm5, 192(%rdx) /* H: store b */ - paddd 160(%rdx), %xmm0 /* G: t = b + a */ - paddd 240(%rdx), %xmm4 /* H: t = b + a */ - movdqa %xmm0, %xmm3 /* G: rotate t (0) */ - movdqa %xmm4, %xmm7 /* H: rotate t (0) */ - pslld $9, %xmm0 /* G: rotate t (1) */ - psrld $23, %xmm3 /* G: rotate t (2) */ - pslld $9, %xmm4 /* H: rotate t (1) */ - psrld $23, %xmm7 /* H: rotate t (2) */ - por %xmm3, %xmm0 /* G: rotate t (3) */ - por %xmm7, %xmm4 /* H: rotate t (3) */ - pxor 128(%rdx), %xmm0 /* G: c = c ^ t */ - pxor 208(%rdx), %xmm4 /* H: c = c ^ t */ - movdqa %xmm0, %xmm2 /* G: copy c */ - movdqa %xmm4, %xmm6 /* H: copy c */ - movdqa %xmm2, 128(%rdx) /* G: store c */ - movdqa %xmm6, 208(%rdx) /* H: store c */ - paddd %xmm1, %xmm0 /* G: t = c + b */ - paddd %xmm5, %xmm4 /* H: t = c + b */ - movdqa %xmm0, %xmm3 /* G: rotate t (0) */ - movdqa %xmm4, %xmm7 /* H: rotate t (0) */ - pslld $13, %xmm0 /* G: rotate t (1) */ - psrld $19, %xmm3 /* G: rotate t (2) */ - pslld $13, %xmm4 /* H: rotate t (1) */ - psrld $19, %xmm7 /* H: rotate t (2) */ - por %xmm3, %xmm0 /* G: rotate t (3) */ - por %xmm7, %xmm4 /* H: rotate t (3) */ - pxor 144(%rdx), %xmm0 /* G: d = d ^ t */ - pxor 224(%rdx), %xmm4 /* H: d = d ^ t */ - movdqa %xmm0, 144(%rdx) /* G: store d */ - movdqa %xmm4, 224(%rdx) /* H: store d */ - paddd %xmm2, %xmm0 /* G: t = d + c */ - paddd %xmm6, %xmm4 /* H: t = d + c */ - movdqa %xmm0, %xmm3 /* G: rotate t (0) */ - movdqa %xmm4, %xmm7 /* H: rotate t (0) */ - pslld $18, %xmm0 /* G: rotate t (1) */ - psrld $14, %xmm3 /* G: rotate t (2) */ - pslld $18, %xmm4 /* H: rotate t (1) */ - psrld $14, %xmm7 /* H: rotate t (2) */ - por %xmm3, %xmm0 /* G: rotate t (3) */ - por %xmm7, %xmm4 /* H: rotate t (3) */ - pxor 160(%rdx), %xmm0 /* G: a = a ^ t */ - pxor 240(%rdx), %xmm4 /* H: a = a ^ t */ - movdqa %xmm0, 160(%rdx) /* G: store a */ - movdqa %xmm4, 240(%rdx) /* H: store a */ - decl %ecx - jnz .xor_salsa_4way - -/* write back data */ - movdqa 0(%rdi), %xmm0 - movdqa 16(%rdi), %xmm1 - movdqa 32(%rdi), %xmm2 - movdqa 48(%rdi), %xmm3 - movdqa 64(%rdi), %xmm4 - movdqa 80(%rdi), %xmm5 - movdqa 96(%rdi), %xmm6 - movdqa 112(%rdi), %xmm7 - movdqa 128(%rdi), %xmm8 - movdqa 144(%rdi), %xmm9 - movdqa 160(%rdi), %xmm10 - movdqa 176(%rdi), %xmm11 - movdqa 192(%rdi), %xmm12 - movdqa 208(%rdi), %xmm13 - movdqa 224(%rdi), %xmm14 - movdqa 240(%rdi), %xmm15 - paddd 0(%rdx), %xmm0 - paddd 16(%rdx), %xmm1 - paddd 32(%rdx), %xmm2 - paddd 48(%rdx), %xmm3 - paddd 64(%rdx), %xmm4 - paddd 80(%rdx), %xmm5 - paddd 96(%rdx), %xmm6 - paddd 112(%rdx), %xmm7 - paddd 128(%rdx), %xmm8 - paddd 144(%rdx), %xmm9 - paddd 160(%rdx), %xmm10 - paddd 176(%rdx), %xmm11 - paddd 192(%rdx), %xmm12 - paddd 208(%rdx), %xmm13 - paddd 224(%rdx), %xmm14 - paddd 240(%rdx), %xmm15 - movdqa %xmm0, 0(%rdi) - movdqa %xmm1, 16(%rdi) - movdqa %xmm2, 32(%rdi) - movdqa %xmm3, 48(%rdi) - movdqa %xmm4, 64(%rdi) - movdqa %xmm5, 80(%rdi) - movdqa %xmm6, 96(%rdi) - movdqa %xmm7, 112(%rdi) - movdqa %xmm8, 128(%rdi) - movdqa %xmm9, 144(%rdi) - movdqa %xmm10, 160(%rdi) - movdqa %xmm11, 176(%rdi) - movdqa %xmm12, 192(%rdi) - movdqa %xmm13, 208(%rdi) - movdqa %xmm14, 224(%rdi) - movdqa %xmm15, 240(%rdi) -#ifdef WIN64 - movq %r10, %rdi - movq %r11, %rsi -#endif - ret - - -/* neoscrypt_xor_chacha_4way(mem, xormem, workmem, double_rounds) - * AMD64 (SSE2) ChaCha20 4-way with XOR */ -.globl neoscrypt_xor_chacha_4way -.globl _neoscrypt_xor_chacha_4way -neoscrypt_xor_chacha_4way: -_neoscrypt_xor_chacha_4way: -#ifdef WIN64 - movq %rdi, %r10 - movq %rsi, %r11 - movq %rcx, %rdi - movq %rdx, %rsi - movq %r8, %rdx - movq %r9, %rcx -#endif -/* XOR and copy to temporary memory */ - movdqa 0(%rdi), %xmm0 - movdqa 16(%rdi), %xmm1 - movdqa 32(%rdi), %xmm2 - movdqa 48(%rdi), %xmm3 - movdqa 64(%rdi), %xmm4 - movdqa 80(%rdi), %xmm5 - movdqa 96(%rdi), %xmm6 - movdqa 112(%rdi), %xmm7 - movdqa 128(%rdi), %xmm8 - movdqa 144(%rdi), %xmm9 - movdqa 160(%rdi), %xmm10 - movdqa 176(%rdi), %xmm11 - movdqa 192(%rdi), %xmm12 - movdqa 208(%rdi), %xmm13 - movdqa 224(%rdi), %xmm14 - movdqa 240(%rdi), %xmm15 - pxor 0(%rsi), %xmm0 - pxor 16(%rsi), %xmm1 - pxor 32(%rsi), %xmm2 - pxor 48(%rsi), %xmm3 - pxor 64(%rsi), %xmm4 - pxor 80(%rsi), %xmm5 - pxor 96(%rsi), %xmm6 - pxor 112(%rsi), %xmm7 - pxor 128(%rsi), %xmm8 - pxor 144(%rsi), %xmm9 - pxor 160(%rsi), %xmm10 - pxor 176(%rsi), %xmm11 - pxor 192(%rsi), %xmm12 - pxor 208(%rsi), %xmm13 - pxor 224(%rsi), %xmm14 - pxor 240(%rsi), %xmm15 - movdqa %xmm0, 0(%rdi) - movdqa %xmm1, 16(%rdi) - movdqa %xmm2, 32(%rdi) - movdqa %xmm3, 48(%rdi) - movdqa %xmm4, 64(%rdi) - movdqa %xmm5, 80(%rdi) - movdqa %xmm6, 96(%rdi) - movdqa %xmm7, 112(%rdi) - movdqa %xmm8, 128(%rdi) - movdqa %xmm9, 144(%rdi) - movdqa %xmm10, 160(%rdi) - movdqa %xmm11, 176(%rdi) - movdqa %xmm12, 192(%rdi) - movdqa %xmm13, 208(%rdi) - movdqa %xmm14, 224(%rdi) - movdqa %xmm15, 240(%rdi) - movdqa %xmm0, 0(%rdx) - movdqa %xmm1, 16(%rdx) - movdqa %xmm2, 32(%rdx) - movdqa %xmm3, 48(%rdx) - movdqa %xmm4, 64(%rdx) - movdqa %xmm5, 80(%rdx) - movdqa %xmm6, 96(%rdx) - movdqa %xmm7, 112(%rdx) - movdqa %xmm8, 128(%rdx) - movdqa %xmm9, 144(%rdx) - movdqa %xmm10, 160(%rdx) - movdqa %xmm11, 176(%rdx) - movdqa %xmm12, 192(%rdx) - movdqa %xmm13, 208(%rdx) - movdqa %xmm14, 224(%rdx) - movdqa %xmm15, 240(%rdx) -.xor_chacha_4way: -/* quarters A and B */ - movdqa 0(%rdx), %xmm0 /* A: load a */ - movdqa 16(%rdx), %xmm4 /* B: load a */ - movdqa 64(%rdx), %xmm1 /* A: load b */ - movdqa 80(%rdx), %xmm5 /* B: load b */ - paddd %xmm1, %xmm0 /* A: a = a + b */ - paddd %xmm5, %xmm4 /* B: a = a + b */ - movdqa 192(%rdx), %xmm3 /* A: load d */ - movdqa 208(%rdx), %xmm7 /* B: load d */ - pxor %xmm0, %xmm3 /* A: d = d ^ a */ - pxor %xmm4, %xmm7 /* B: d = d ^ a */ - movdqa 128(%rdx), %xmm2 /* A: load c */ - movdqa 144(%rdx), %xmm6 /* B: load c */ - movdqa %xmm3, %xmm14 /* A: rotate d (0) */ - movdqa %xmm7, %xmm15 /* B: rotate d (0) */ - pslld $16, %xmm3 /* A: rotate d (1) */ - psrld $16, %xmm14 /* A: rotate d (2) */ - pslld $16, %xmm7 /* B: rotate d (1) */ - psrld $16, %xmm15 /* B: rotate d (2) */ - por %xmm14, %xmm3 /* A: rotate d (3) */ - por %xmm15, %xmm7 /* B: rotate d (3) */ - paddd %xmm3, %xmm2 /* A: c = c + d */ - paddd %xmm7, %xmm6 /* B: c = c + d */ - pxor %xmm2, %xmm1 /* A: b = b ^ c */ - pxor %xmm6, %xmm5 /* B: b = b ^ c */ - movdqa %xmm1, %xmm14 /* A: rotate b (0) */ - movdqa %xmm5, %xmm15 /* B: rotate b (0) */ - pslld $12, %xmm1 /* A: rotate b (1) */ - psrld $20, %xmm14 /* A: rotate b (2) */ - pslld $12, %xmm5 /* B: rotate b (1) */ - psrld $20, %xmm15 /* B: rotate b (2) */ - por %xmm14, %xmm1 /* A: rotate b (3) */ - por %xmm15, %xmm5 /* B: rotate b (3) */ - paddd %xmm1, %xmm0 /* A: a = a + b */ - paddd %xmm5, %xmm4 /* B: a = a + b */ - movdqa %xmm0, 0(%rdx) /* A: store a */ - movdqa %xmm4, 16(%rdx) /* B: store a */ - pxor %xmm0, %xmm3 /* A: d = d ^ a */ - pxor %xmm4, %xmm7 /* B: d = d ^ a */ - movdqa %xmm3, %xmm14 /* A: rotate d (0) */ - movdqa %xmm7, %xmm15 /* B: rotate d (0) */ - pslld $8, %xmm3 /* A: rotate d (1) */ - psrld $24, %xmm14 /* A: rotate d (2) */ - pslld $8, %xmm7 /* B: rotate d (1) */ - psrld $24, %xmm15 /* B: rotate d (2) */ - por %xmm14, %xmm3 /* A: rotate d (3) */ - por %xmm15, %xmm7 /* B: rotate d (3) */ - movdqa %xmm3, 192(%rdx) /* A: store d */ - movdqa %xmm7, 208(%rdx) /* B: store d */ - paddd %xmm3, %xmm2 /* A: c = c + d */ - paddd %xmm7, %xmm6 /* B: c = c + d */ - movdqa %xmm2, 128(%rdx) /* A: store c */ - movdqa %xmm6, 144(%rdx) /* B: store c */ - pxor %xmm2, %xmm1 /* A: b = b ^ c */ - pxor %xmm6, %xmm5 /* B: b = b ^ c */ - movdqa %xmm1, %xmm14 /* A: rotate b (0) */ - movdqa %xmm5, %xmm15 /* B: rotate b (0) */ - pslld $7, %xmm1 /* A: rotate b (1) */ - psrld $25, %xmm14 /* A: rotate b (2) */ - pslld $7, %xmm5 /* B: rotate b (1) */ - psrld $25, %xmm15 /* B: rotate b (2) */ - por %xmm14, %xmm1 /* A: rotate b (3) */ - por %xmm15, %xmm5 /* B: rotate b (3) */ - movdqa %xmm1, 64(%rdx) /* A: store b */ - movdqa %xmm5, 80(%rdx) /* B: store b */ -/* quarters C and D */ - movdqa 32(%rdx), %xmm0 /* C: load a */ - movdqa 48(%rdx), %xmm4 /* D: load a */ - movdqa 96(%rdx), %xmm1 /* C: load b */ - movdqa 112(%rdx), %xmm5 /* D: load b */ - paddd %xmm1, %xmm0 /* C: a = a + b */ - paddd %xmm5, %xmm4 /* D: a = a + b */ - movdqa 224(%rdx), %xmm3 /* C: load d */ - movdqa 240(%rdx), %xmm7 /* D: load d */ - pxor %xmm0, %xmm3 /* C: d = d ^ a */ - pxor %xmm4, %xmm7 /* D: d = d ^ a */ - movdqa 160(%rdx), %xmm2 /* C: load c */ - movdqa 176(%rdx), %xmm6 /* D: load c */ - movdqa %xmm3, %xmm14 /* C: rotate d (0) */ - movdqa %xmm7, %xmm15 /* D: rotate d (0) */ - pslld $16, %xmm3 /* C: rotate d (1) */ - psrld $16, %xmm14 /* C: rotate d (2) */ - pslld $16, %xmm7 /* D: rotate d (1) */ - psrld $16, %xmm15 /* D: rotate d (2) */ - por %xmm14, %xmm3 /* C: rotate d (3) */ - por %xmm15, %xmm7 /* D: rotate d (3) */ - paddd %xmm3, %xmm2 /* C: c = c + d */ - paddd %xmm7, %xmm6 /* D: c = c + d */ - pxor %xmm2, %xmm1 /* C: b = b ^ c */ - pxor %xmm6, %xmm5 /* D: b = b ^ c */ - movdqa %xmm1, %xmm14 /* C: rotate b (0) */ - movdqa %xmm5, %xmm15 /* D: rotate b (0) */ - pslld $12, %xmm1 /* C: rotate b (1) */ - psrld $20, %xmm14 /* C: rotate b (2) */ - pslld $12, %xmm5 /* D: rotate b (1) */ - psrld $20, %xmm15 /* D: rotate b (2) */ - por %xmm14, %xmm1 /* C: rotate b (3) */ - por %xmm15, %xmm5 /* D: rotate b (3) */ - paddd %xmm1, %xmm0 /* C: a = a + b */ - paddd %xmm5, %xmm4 /* D: a = a + b */ - movdqa %xmm0, 32(%rdx) /* C: store a */ - movdqa %xmm4, 48(%rdx) /* D: store a */ - pxor %xmm0, %xmm3 /* C: d = d ^ a */ - pxor %xmm4, %xmm7 /* D: d = d ^ a */ - movdqa %xmm3, %xmm14 /* C: rotate d (0) */ - movdqa %xmm7, %xmm15 /* D: rotate d (0) */ - pslld $8, %xmm3 /* C: rotate d (1) */ - psrld $24, %xmm14 /* C: rotate d (2) */ - pslld $8, %xmm7 /* D: rotate d (1) */ - psrld $24, %xmm15 /* D: rotate d (2) */ - por %xmm14, %xmm3 /* C: rotate d (3) */ - por %xmm15, %xmm7 /* D: rotate d (3) */ - movdqa %xmm3, 224(%rdx) /* C: store d */ - movdqa %xmm7, 240(%rdx) /* D: store d */ - paddd %xmm3, %xmm2 /* C: c = c + d */ - paddd %xmm7, %xmm6 /* D: c = c + d */ - movdqa %xmm2, 160(%rdx) /* C: store c */ - movdqa %xmm6, 176(%rdx) /* D: store c */ - pxor %xmm2, %xmm1 /* C: b = b ^ c */ - pxor %xmm6, %xmm5 /* D: b = b ^ c */ - movdqa %xmm1, %xmm14 /* C: rotate b (0) */ - movdqa %xmm5, %xmm15 /* D: rotate b (0) */ - pslld $7, %xmm1 /* C: rotate b (1) */ - psrld $25, %xmm14 /* C: rotate b (2) */ - pslld $7, %xmm5 /* D: rotate b (1) */ - psrld $25, %xmm15 /* D: rotate b (2) */ - por %xmm14, %xmm1 /* C: rotate b (3) */ - por %xmm15, %xmm5 /* D: rotate b (3) */ - movdqa %xmm1, 96(%rdx) /* C: store b */ - movdqa %xmm5, 112(%rdx) /* D: store b */ -/* quarters E and F */ - movdqa 0(%rdx), %xmm0 /* E: load a */ - movdqa 16(%rdx), %xmm4 /* F: load a */ - movdqa 80(%rdx), %xmm1 /* E: load b */ - movdqa 96(%rdx), %xmm5 /* F: load b */ - paddd %xmm1, %xmm0 /* E: a = a + b */ - paddd %xmm5, %xmm4 /* F: a = a + b */ - movdqa 240(%rdx), %xmm3 /* E: load d */ - movdqa 192(%rdx), %xmm7 /* F: load d */ - pxor %xmm0, %xmm3 /* E: d = d ^ a */ - pxor %xmm4, %xmm7 /* F: d = d ^ a */ - movdqa 160(%rdx), %xmm2 /* E: load c */ - movdqa 176(%rdx), %xmm6 /* F: load c */ - movdqa %xmm3, %xmm14 /* E: rotate d (0) */ - movdqa %xmm7, %xmm15 /* F: rotate d (0) */ - pslld $16, %xmm3 /* E: rotate d (1) */ - psrld $16, %xmm14 /* E: rotate d (2) */ - pslld $16, %xmm7 /* F: rotate d (1) */ - psrld $16, %xmm15 /* F: rotate d (2) */ - por %xmm14, %xmm3 /* E: rotate d (3) */ - por %xmm15, %xmm7 /* F: rotate d (3) */ - paddd %xmm3, %xmm2 /* E: c = c + d */ - paddd %xmm7, %xmm6 /* F: c = c + d */ - pxor %xmm2, %xmm1 /* E: b = b ^ c */ - pxor %xmm6, %xmm5 /* F: b = b ^ c */ - movdqa %xmm1, %xmm14 /* E: rotate b (0) */ - movdqa %xmm5, %xmm15 /* F: rotate b (0) */ - pslld $12, %xmm1 /* E: rotate b (1) */ - psrld $20, %xmm14 /* E: rotate b (2) */ - pslld $12, %xmm5 /* F: rotate b (1) */ - psrld $20, %xmm15 /* F: rotate b (2) */ - por %xmm14, %xmm1 /* E: rotate b (3) */ - por %xmm15, %xmm5 /* F: rotate b (3) */ - paddd %xmm1, %xmm0 /* E: a = a + b */ - paddd %xmm5, %xmm4 /* F: a = a + b */ - movdqa %xmm0, 0(%rdx) /* E: store a */ - movdqa %xmm4, 16(%rdx) /* F: store a */ - pxor %xmm0, %xmm3 /* E: d = d ^ a */ - pxor %xmm4, %xmm7 /* F: d = d ^ a */ - movdqa %xmm3, %xmm14 /* E: rotate d (0) */ - movdqa %xmm7, %xmm15 /* F: rotate d (0) */ - pslld $8, %xmm3 /* E: rotate d (1) */ - psrld $24, %xmm14 /* E: rotate d (2) */ - pslld $8, %xmm7 /* F: rotate d (1) */ - psrld $24, %xmm15 /* F: rotate d (2) */ - por %xmm14, %xmm3 /* E: rotate d (3) */ - por %xmm15, %xmm7 /* F: rotate d (3) */ - movdqa %xmm3, 240(%rdx) /* E: store d */ - movdqa %xmm7, 192(%rdx) /* F: store d */ - paddd %xmm3, %xmm2 /* E: c = c + d */ - paddd %xmm7, %xmm6 /* F: c = c + d */ - movdqa %xmm2, 160(%rdx) /* E: store c */ - movdqa %xmm6, 176(%rdx) /* F: store c */ - pxor %xmm2, %xmm1 /* E: b = b ^ c */ - pxor %xmm6, %xmm5 /* F: b = b ^ c */ - movdqa %xmm1, %xmm14 /* E: rotate b (0) */ - movdqa %xmm5, %xmm15 /* F: rotate b (0) */ - pslld $7, %xmm1 /* E: rotate b (1) */ - psrld $25, %xmm14 /* E: rotate b (2) */ - pslld $7, %xmm5 /* F: rotate b (1) */ - psrld $25, %xmm15 /* F: rotate b (2) */ - por %xmm14, %xmm1 /* E: rotate b (3) */ - por %xmm15, %xmm5 /* F: rotate b (3) */ - movdqa %xmm1, 80(%rdx) /* E: store b */ - movdqa %xmm5, 96(%rdx) /* F: store b */ -/* quarter G and H */ - movdqa 32(%rdx), %xmm0 /* G: load a */ - movdqa 48(%rdx), %xmm4 /* H: load a */ - movdqa 64(%rdx), %xmm5 /* H: load b */ - movdqa 112(%rdx), %xmm1 /* G: load b */ - paddd %xmm1, %xmm0 /* G: a = a + b */ - paddd %xmm5, %xmm4 /* H: a = a + b */ - movdqa 208(%rdx), %xmm3 /* G: load d */ - movdqa 224(%rdx), %xmm7 /* H: load d */ - pxor %xmm0, %xmm3 /* G: d = d ^ a */ - pxor %xmm4, %xmm7 /* H: d = d ^ a */ - movdqa 128(%rdx), %xmm2 /* G: load c */ - movdqa 144(%rdx), %xmm6 /* H: load c */ - movdqa %xmm3, %xmm14 /* G: rotate d (0) */ - movdqa %xmm7, %xmm15 /* H: rotate d (0) */ - pslld $16, %xmm3 /* G: rotate d (1) */ - psrld $16, %xmm14 /* G: rotate d (2) */ - pslld $16, %xmm7 /* H: rotate d (1) */ - psrld $16, %xmm15 /* H: rotate d (2) */ - por %xmm14, %xmm3 /* G: rotate d (3) */ - por %xmm15, %xmm7 /* H: rotate d (3) */ - paddd %xmm3, %xmm2 /* G: c = c + d */ - paddd %xmm7, %xmm6 /* H: c = c + d */ - pxor %xmm2, %xmm1 /* G: b = b ^ c */ - pxor %xmm6, %xmm5 /* H: b = b ^ c */ - movdqa %xmm1, %xmm14 /* G: rotate b (0) */ - movdqa %xmm5, %xmm15 /* H: rotate b (0) */ - pslld $12, %xmm1 /* G: rotate b (1) */ - psrld $20, %xmm14 /* G: rotate b (2) */ - pslld $12, %xmm5 /* H: rotate b (1) */ - psrld $20, %xmm15 /* H: rotate b (2) */ - por %xmm14, %xmm1 /* G: rotate b (3) */ - por %xmm15, %xmm5 /* H: rotate b (3) */ - paddd %xmm1, %xmm0 /* G: a = a + b */ - paddd %xmm5, %xmm4 /* H: a = a + b */ - movdqa %xmm0, 32(%rdx) /* G: store a */ - movdqa %xmm4, 48(%rdx) /* H: store a */ - pxor %xmm0, %xmm3 /* G: d = d ^ a */ - pxor %xmm4, %xmm7 /* H: d = d ^ a */ - movdqa %xmm3, %xmm14 /* G: rotate d (0) */ - movdqa %xmm7, %xmm15 /* H: rotate d (0) */ - pslld $8, %xmm3 /* G: rotate d (1) */ - psrld $24, %xmm14 /* G: rotate d (2) */ - pslld $8, %xmm7 /* H: rotate d (1) */ - psrld $24, %xmm15 /* H: rotate d (2) */ - por %xmm14, %xmm3 /* G: rotate d (3) */ - por %xmm15, %xmm7 /* H: rotate d (3) */ - movdqa %xmm3, 208(%rdx) /* G: store d */ - movdqa %xmm7, 224(%rdx) /* H: store d */ - paddd %xmm3, %xmm2 /* G: c = c + d */ - paddd %xmm7, %xmm6 /* H: c = c + d */ - movdqa %xmm2, 128(%rdx) /* G: store c */ - movdqa %xmm6, 144(%rdx) /* H: store c */ - pxor %xmm2, %xmm1 /* G: b = b ^ c */ - pxor %xmm6, %xmm5 /* H: b = b ^ c */ - movdqa %xmm1, %xmm14 /* G: rotate b (0) */ - movdqa %xmm5, %xmm15 /* H: rotate b (0) */ - pslld $7, %xmm1 /* G: rotate b (1) */ - psrld $25, %xmm14 /* G: rotate b (2) */ - pslld $7, %xmm5 /* H: rotate b (1) */ - psrld $25, %xmm15 /* H: rotate b (2) */ - por %xmm14, %xmm1 /* G: rotate b (3) */ - por %xmm15, %xmm5 /* H: rotate b (3) */ - movdqa %xmm5, 64(%rdx) /* H: store b */ - movdqa %xmm1, 112(%rdx) /* G: store b */ - decl %ecx - jnz .xor_chacha_4way - -/* write back data */ - movdqa 0(%rdi), %xmm0 - movdqa 16(%rdi), %xmm1 - movdqa 32(%rdi), %xmm2 - movdqa 48(%rdi), %xmm3 - movdqa 64(%rdi), %xmm4 - movdqa 80(%rdi), %xmm5 - movdqa 96(%rdi), %xmm6 - movdqa 112(%rdi), %xmm7 - movdqa 128(%rdi), %xmm8 - movdqa 144(%rdi), %xmm9 - movdqa 160(%rdi), %xmm10 - movdqa 176(%rdi), %xmm11 - movdqa 192(%rdi), %xmm12 - movdqa 208(%rdi), %xmm13 - movdqa 224(%rdi), %xmm14 - movdqa 240(%rdi), %xmm15 - paddd 0(%rdx), %xmm0 - paddd 16(%rdx), %xmm1 - paddd 32(%rdx), %xmm2 - paddd 48(%rdx), %xmm3 - paddd 64(%rdx), %xmm4 - paddd 80(%rdx), %xmm5 - paddd 96(%rdx), %xmm6 - paddd 112(%rdx), %xmm7 - paddd 128(%rdx), %xmm8 - paddd 144(%rdx), %xmm9 - paddd 160(%rdx), %xmm10 - paddd 176(%rdx), %xmm11 - paddd 192(%rdx), %xmm12 - paddd 208(%rdx), %xmm13 - paddd 224(%rdx), %xmm14 - paddd 240(%rdx), %xmm15 - movdqa %xmm0, 0(%rdi) - movdqa %xmm1, 16(%rdi) - movdqa %xmm2, 32(%rdi) - movdqa %xmm3, 48(%rdi) - movdqa %xmm4, 64(%rdi) - movdqa %xmm5, 80(%rdi) - movdqa %xmm6, 96(%rdi) - movdqa %xmm7, 112(%rdi) - movdqa %xmm8, 128(%rdi) - movdqa %xmm9, 144(%rdi) - movdqa %xmm10, 160(%rdi) - movdqa %xmm11, 176(%rdi) - movdqa %xmm12, 192(%rdi) - movdqa %xmm13, 208(%rdi) - movdqa %xmm14, 224(%rdi) - movdqa %xmm15, 240(%rdi) -#ifdef WIN64 - movq %r10, %rdi - movq %r11, %rsi -#endif - ret - -#endif /* MINER_4WAY */ - -/* cpu_vec_exts() - * AMD64 detector of any processor vector extensions present - * output bits set in %rax: - * 0 : MMX [always true] - * 1 : Extended MMX (MMX+) [always true] - * 2 : 3DNow! - * 3 : Extended 3DNow! (3DNow!+) - * 4 : SSE [always true] - * 5 : SSE2 [always true] - * 6 : SSE3 - * 7 : SSSE3 - * 8 : SSE41 - * 9 : SSE42 - * 10 : SSE4A - * 11 : XOP - * 12 : FMA4 - * 13 : AVX - * 14 : F16C - * 15 : FMA3 - * the other bits are reserved for the future use */ -.globl cpu_vec_exts -.globl _cpu_vec_exts -cpu_vec_exts: -_cpu_vec_exts: - pushq %rbx - pushq %rbp - xorq %rbp, %rbp -/* all AMD64 compatible processors support MMX, MMX+, SSE, SSE2 */ - orl $0x00000033, %ebp -/* the CPUID extended function 0 should report the max. - * supported extended function number in %eax */ - movl $0x80000000, %eax - cpuid - cmpl $0x80000001, %eax - jb .cpu_vec_st1 - movl $0x80000001, %eax - cpuid -/* 3DNow!+ (bit 30 of %edx); implies 3DNow! */ - testl $0x80000000, %edx - jz .cpu_vec_sse4a - orl $0x0000000C, %ebp - jmp .cpu_vec_sse4a -.cpu_vec_sse4a: -/* SSE4A (bit 6 of %ecx) */ - testl $0x00000040, %ecx - jz .cpu_vec_st1 - orl $0x00000400, %ebp -/* XOP (bit 11 of %ecx) */ - testl $0x00000800, %ecx - jz .cpu_vec_st1 - orl $0x00000800, %ebp -/* FMA4 (bit 16 of %ecx) */ - testl $0x00010000, %ecx - jz .cpu_vec_st1 - orl $0x00001000, %ebp -.cpu_vec_st1: -/* the CPUID standard function 1 */ - movl $1, %eax - cpuid -/* SSE3 (bit 0 of %ecx) */ - testl $0x00000001, %ecx - jz .cpu_vec_exit - orl $0x00000040, %ebp -/* SSSE3 (bit 9 of %ecx) */ - testl $0x00000100, %ecx - jz .cpu_vec_exit - orl $0x00000080, %ebp -/* SSE4.1 (bit 19 of %ecx) */ - testl $0x00080000, %ecx - jz .cpu_vec_exit - orl $0x00000100, %ebp -/* SSE4.2 (bit 20 of %ecx) */ - testl $0x00100000, %ecx - jz .cpu_vec_exit - orl $0x00000200, %ebp -/* AVX (bit 28 of %ecx) */ - testl $0x10000000, %ecx - jz .cpu_vec_exit - orl $0x00002000, %ebp - jmp .cpu_vec_exit -/* F16C (bit 29 of %ecx) */ - testl $0x20000000, %ecx - jz .cpu_vec_exit - orl $0x00004000, %ebp - jmp .cpu_vec_exit -/* FMA3 (bit 12 of %ecx) */ - testl $0x00001000, %ecx - jz .cpu_vec_exit - orl $0x00008000, %ebp - -.cpu_vec_exit: - movq %rbp, %rax - popq %rbp - popq %rbx - ret - -#endif /* (ASM) && (__x86_64__) */ - - -#if defined(ASM) && defined(__i386__) - -/* blake2s_compress(mem) - * i386 BLAKE2s block compression */ -.globl blake2s_compress -.globl _blake2s_compress -blake2s_compress: -_blake2s_compress: - pushl %ebx - pushl %ebp - pushl %esi - pushl %edi - movl 20(%esp), %edi - leal -64(%esp), %esi - -/* initialise */ - movl 0(%edi), %eax - movl 4(%edi), %ebx - movl 8(%edi), %ecx - movl 12(%edi), %edx - movl %eax, 0(%esi) - movl %ebx, 4(%esi) - movl %ecx, 8(%esi) - movl %edx, 12(%esi) - movl 16(%edi), %eax - movl 20(%edi), %ebx - movl 24(%edi), %ecx - movl 28(%edi), %edx - movl %eax, 16(%esi) - movl %ebx, 20(%esi) - movl %ecx, 24(%esi) - movl %edx, 28(%esi) - movl $0x6A09E667, %eax - movl $0xBB67AE85, %ebx - movl $0x3C6EF372, %ecx - movl $0xA54FF53A, %edx - movl %eax, 32(%esi) - movl %ebx, 36(%esi) - movl %ecx, 40(%esi) - movl %edx, 44(%esi) - movl 32(%edi), %eax - movl 36(%edi), %ebx - movl 40(%edi), %ecx - movl 44(%edi), %edx - xorl $0x510E527F, %eax - xorl $0x9B05688C, %ebx - xorl $0x1F83D9AB, %ecx - xorl $0x5BE0CD19, %edx - movl %eax, 48(%esi) - movl 0(%esi), %eax /* A */ - movl %ebx, 52(%esi) - movl 16(%esi), %ebx /* A */ - movl %ecx, 56(%esi) - addl 48(%edi), %eax /* A */ - movl %edx, 60(%esi) -/* round 0 (A) */ - movl 48(%esi), %edx - addl %ebx, %eax - movl 32(%esi), %ecx - xorl %eax, %edx - movl 52(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 20(%esi), %ebp /* B */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* B */ - rorl $8, %edx - addl 56(%edi), %eax /* B */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* B */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* B */ - rorl $7, %ebx - movl %ebx, 16(%esi) -/* round 0 (B) */ - addl %ebp, %eax - xorl %eax, %edx - movl 60(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 24(%esi), %ebx /* C */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* C */ - rorl $8, %edx - addl 64(%edi), %eax /* C */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* C */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* C */ - rorl $7, %ebp - movl %ebp, 20(%esi) -/* round 0 (C) */ - addl %ebx, %eax - xorl %eax, %edx - movl 68(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 28(%esi), %ebp /* D */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* D */ - rorl $8, %edx - addl 72(%edi), %eax /* D */ - movl %edx, 56(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* D */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* D */ - rorl $7, %ebx - movl %ebx, 24(%esi) -/* round 0 (D) */ - addl %ebp, %eax - xorl %eax, %edx - movl 76(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 20(%esi), %ebx /* E */ - movl %eax, 12(%esi) - xorl %eax, %edx - movl 0(%esi), %eax /* E */ - rorl $8, %edx - addl 80(%edi), %eax /* E */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* E */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* E */ - rorl $7, %ebp - movl %ebp, 28(%esi) -/* round 0 (E) */ - addl %ebx, %eax - xorl %eax, %edx - movl 84(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 24(%esi), %ebp /* F */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* F */ - rorl $8, %edx - addl 88(%edi), %eax /* F */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 48(%esi), %edx /* F */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* F */ - rorl $7, %ebx - movl %ebx, 20(%esi) -/* round 0 (F) */ - addl %ebp, %eax - xorl %eax, %edx - movl 92(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 28(%esi), %ebx /* G */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* G */ - rorl $8, %edx - addl 96(%edi), %eax /* G */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* G */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 32(%esi), %ecx /* G */ - rorl $7, %ebp - movl %ebp, 24(%esi) -/* round 0 (G) */ - addl %ebx, %eax - xorl %eax, %edx - movl 100(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 16(%esi), %ebp /* H */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* H */ - rorl $8, %edx - addl 104(%edi), %eax /* H */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* H */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* H */ - rorl $7, %ebx - movl %ebx, 28(%esi) -/* round 0 (H) */ - addl %ebp, %eax - xorl %eax, %edx - movl 108(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl %eax, 12(%esi) - xorl %eax, %edx - rorl $8, %edx - movl 0(%esi), %eax /* A */ - movl %edx, 56(%esi) - addl %edx, %ecx - addl 104(%edi), %eax /* A */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 48(%esi), %edx /* A */ - rorl $7, %ebp - movl %ebp, %ebx -/* round 1 (A) */ - addl %ebx, %eax - movl 32(%esi), %ecx - xorl %eax, %edx - movl 88(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 20(%esi), %ebp /* B */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* B */ - rorl $8, %edx - addl 64(%edi), %eax /* B */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* B */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* B */ - rorl $7, %ebx - movl %ebx, 16(%esi) -/* round 1 (B) */ - addl %ebp, %eax - xorl %eax, %edx - movl 80(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 24(%esi), %ebx /* C */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* C */ - rorl $8, %edx - addl 84(%edi), %eax /* C */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* C */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* C */ - rorl $7, %ebp - movl %ebp, 20(%esi) -/* round 1 (C) */ - addl %ebx, %eax - xorl %eax, %edx - movl 108(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 28(%esi), %ebp /* D */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* D */ - rorl $8, %edx - addl 100(%edi), %eax /* D */ - movl %edx, 56(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* D */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* D */ - rorl $7, %ebx - movl %ebx, 24(%esi) -/* round 1 (D) */ - addl %ebp, %eax - xorl %eax, %edx - movl 72(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 20(%esi), %ebx /* E */ - movl %eax, 12(%esi) - xorl %eax, %edx - movl 0(%esi), %eax /* E */ - rorl $8, %edx - addl 52(%edi), %eax /* E */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* E */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* E */ - rorl $7, %ebp - movl %ebp, 28(%esi) -/* round 1 (E) */ - addl %ebx, %eax - xorl %eax, %edx - movl 96(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 24(%esi), %ebp /* F */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* F */ - rorl $8, %edx - addl 48(%edi), %eax /* F */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 48(%esi), %edx /* F */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* F */ - rorl $7, %ebx - movl %ebx, 20(%esi) -/* round 1 (F) */ - addl %ebp, %eax - xorl %eax, %edx - movl 56(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 28(%esi), %ebx /* G */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* G */ - rorl $8, %edx - addl 92(%edi), %eax /* G */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* G */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 32(%esi), %ecx /* G */ - rorl $7, %ebp - movl %ebp, 24(%esi) -/* round 1 (G) */ - addl %ebx, %eax - xorl %eax, %edx - movl 76(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 16(%esi), %ebp /* H */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* H */ - rorl $8, %edx - addl 68(%edi), %eax /* H */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* H */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* H */ - rorl $7, %ebx - movl %ebx, 28(%esi) -/* round 1 (H) */ - addl %ebp, %eax - xorl %eax, %edx - movl 60(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl %eax, 12(%esi) - xorl %eax, %edx - rorl $8, %edx - movl 0(%esi), %eax /* A */ - movl %edx, 56(%esi) - addl %edx, %ecx - addl 92(%edi), %eax /* A */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 48(%esi), %edx /* A */ - rorl $7, %ebp - movl %ebp, %ebx -/* round 2 (A) */ - addl %ebx, %eax - movl 32(%esi), %ecx - xorl %eax, %edx - movl 80(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 20(%esi), %ebp /* B */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* B */ - rorl $8, %edx - addl 96(%edi), %eax /* B */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* B */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* B */ - rorl $7, %ebx - movl %ebx, 16(%esi) -/* round 2 (B) */ - addl %ebp, %eax - xorl %eax, %edx - movl 48(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 24(%esi), %ebx /* C */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* C */ - rorl $8, %edx - addl 68(%edi), %eax /* C */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* C */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* C */ - rorl $7, %ebp - movl %ebp, 20(%esi) -/* round 2 (C) */ - addl %ebx, %eax - xorl %eax, %edx - movl 56(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 28(%esi), %ebp /* D */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* D */ - rorl $8, %edx - addl 108(%edi), %eax /* D */ - movl %edx, 56(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* D */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* D */ - rorl $7, %ebx - movl %ebx, 24(%esi) -/* round 2 (D) */ - addl %ebp, %eax - xorl %eax, %edx - movl 100(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 20(%esi), %ebx /* E */ - movl %eax, 12(%esi) - xorl %eax, %edx - movl 0(%esi), %eax /* E */ - rorl $8, %edx - addl 88(%edi), %eax /* E */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* E */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* E */ - rorl $7, %ebp - movl %ebp, 28(%esi) -/* round 2 (E) */ - addl %ebx, %eax - xorl %eax, %edx - movl 104(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 24(%esi), %ebp /* F */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* F */ - rorl $8, %edx - addl 60(%edi), %eax /* F */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 48(%esi), %edx /* F */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* F */ - rorl $7, %ebx - movl %ebx, 20(%esi) -/* round 2 (F) */ - addl %ebp, %eax - xorl %eax, %edx - movl 72(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 28(%esi), %ebx /* G */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* G */ - rorl $8, %edx - addl 76(%edi), %eax /* G */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* G */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 32(%esi), %ecx /* G */ - rorl $7, %ebp - movl %ebp, 24(%esi) -/* round 2 (G) */ - addl %ebx, %eax - xorl %eax, %edx - movl 52(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 16(%esi), %ebp /* H */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* H */ - rorl $8, %edx - addl 84(%edi), %eax /* H */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* H */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* H */ - rorl $7, %ebx - movl %ebx, 28(%esi) -/* round 2 (H) */ - addl %ebp, %eax - xorl %eax, %edx - movl 64(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl %eax, 12(%esi) - xorl %eax, %edx - rorl $8, %edx - movl 0(%esi), %eax /* A */ - movl %edx, 56(%esi) - addl %edx, %ecx - addl 76(%edi), %eax /* A */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 48(%esi), %edx /* A */ - rorl $7, %ebp - movl %ebp, %ebx -/* round 3 (A) */ - addl %ebx, %eax - movl 32(%esi), %ecx - xorl %eax, %edx - movl 84(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 20(%esi), %ebp /* B */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* B */ - rorl $8, %edx - addl 60(%edi), %eax /* B */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* B */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* B */ - rorl $7, %ebx - movl %ebx, 16(%esi) -/* round 3 (B) */ - addl %ebp, %eax - xorl %eax, %edx - movl 52(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 24(%esi), %ebx /* C */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* C */ - rorl $8, %edx - addl 100(%edi), %eax /* C */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* C */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* C */ - rorl $7, %ebp - movl %ebp, 20(%esi) -/* round 3 (C) */ - addl %ebx, %eax - xorl %eax, %edx - movl 96(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 28(%esi), %ebp /* D */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* D */ - rorl $8, %edx - addl 92(%edi), %eax /* D */ - movl %edx, 56(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* D */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* D */ - rorl $7, %ebx - movl %ebx, 24(%esi) -/* round 3 (D) */ - addl %ebp, %eax - xorl %eax, %edx - movl 104(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 20(%esi), %ebx /* E */ - movl %eax, 12(%esi) - xorl %eax, %edx - movl 0(%esi), %eax /* E */ - rorl $8, %edx - addl 56(%edi), %eax /* E */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* E */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* E */ - rorl $7, %ebp - movl %ebp, 28(%esi) -/* round 3 (E) */ - addl %ebx, %eax - xorl %eax, %edx - movl 72(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 24(%esi), %ebp /* F */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* F */ - rorl $8, %edx - addl 68(%edi), %eax /* F */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 48(%esi), %edx /* F */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* F */ - rorl $7, %ebx - movl %ebx, 20(%esi) -/* round 3 (F) */ - addl %ebp, %eax - xorl %eax, %edx - movl 88(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 28(%esi), %ebx /* G */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* G */ - rorl $8, %edx - addl 64(%edi), %eax /* G */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* G */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 32(%esi), %ecx /* G */ - rorl $7, %ebp - movl %ebp, 24(%esi) -/* round 3 (G) */ - addl %ebx, %eax - xorl %eax, %edx - movl 48(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 16(%esi), %ebp /* H */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* H */ - rorl $8, %edx - addl 108(%edi), %eax /* H */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* H */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* H */ - rorl $7, %ebx - movl %ebx, 28(%esi) -/* round 3 (H) */ - addl %ebp, %eax - xorl %eax, %edx - movl 80(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl %eax, 12(%esi) - xorl %eax, %edx - rorl $8, %edx - movl 0(%esi), %eax /* A */ - movl %edx, 56(%esi) - addl %edx, %ecx - addl 84(%edi), %eax /* A */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 48(%esi), %edx /* A */ - rorl $7, %ebp - movl %ebp, %ebx -/* round 4 (A) */ - addl %ebx, %eax - movl 32(%esi), %ecx - xorl %eax, %edx - movl 48(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 20(%esi), %ebp /* B */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* B */ - rorl $8, %edx - addl 68(%edi), %eax /* B */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* B */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* B */ - rorl $7, %ebx - movl %ebx, 16(%esi) -/* round 4 (B) */ - addl %ebp, %eax - xorl %eax, %edx - movl 76(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 24(%esi), %ebx /* C */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* C */ - rorl $8, %edx - addl 56(%edi), %eax /* C */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* C */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* C */ - rorl $7, %ebp - movl %ebp, 20(%esi) -/* round 4 (C) */ - addl %ebx, %eax - xorl %eax, %edx - movl 64(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 28(%esi), %ebp /* D */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* D */ - rorl $8, %edx - addl 88(%edi), %eax /* D */ - movl %edx, 56(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* D */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* D */ - rorl $7, %ebx - movl %ebx, 24(%esi) -/* round 4 (D) */ - addl %ebp, %eax - xorl %eax, %edx - movl 108(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 20(%esi), %ebx /* E */ - movl %eax, 12(%esi) - xorl %eax, %edx - movl 0(%esi), %eax /* E */ - rorl $8, %edx - addl 104(%edi), %eax /* E */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* E */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* E */ - rorl $7, %ebp - movl %ebp, 28(%esi) -/* round 4 (E) */ - addl %ebx, %eax - xorl %eax, %edx - movl 52(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 24(%esi), %ebp /* F */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* F */ - rorl $8, %edx - addl 92(%edi), %eax /* F */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 48(%esi), %edx /* F */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* F */ - rorl $7, %ebx - movl %ebx, 20(%esi) -/* round 4 (F) */ - addl %ebp, %eax - xorl %eax, %edx - movl 96(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 28(%esi), %ebx /* G */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* G */ - rorl $8, %edx - addl 72(%edi), %eax /* G */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* G */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 32(%esi), %ecx /* G */ - rorl $7, %ebp - movl %ebp, 24(%esi) -/* round 4 (G) */ - addl %ebx, %eax - xorl %eax, %edx - movl 80(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 16(%esi), %ebp /* H */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* H */ - rorl $8, %edx - addl 60(%edi), %eax /* H */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* H */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* H */ - rorl $7, %ebx - movl %ebx, 28(%esi) -/* round 4 (H) */ - addl %ebp, %eax - xorl %eax, %edx - movl 100(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl %eax, 12(%esi) - xorl %eax, %edx - rorl $8, %edx - movl 0(%esi), %eax /* A */ - movl %edx, 56(%esi) - addl %edx, %ecx - addl 56(%edi), %eax /* A */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 48(%esi), %edx /* A */ - rorl $7, %ebp - movl %ebp, %ebx -/* round 5 (A) */ - addl %ebx, %eax - movl 32(%esi), %ecx - xorl %eax, %edx - movl 96(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 20(%esi), %ebp /* B */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* B */ - rorl $8, %edx - addl 72(%edi), %eax /* B */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* B */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* B */ - rorl $7, %ebx - movl %ebx, 16(%esi) -/* round 5 (B) */ - addl %ebp, %eax - xorl %eax, %edx - movl 88(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 24(%esi), %ebx /* C */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* C */ - rorl $8, %edx - addl 48(%edi), %eax /* C */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* C */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* C */ - rorl $7, %ebp - movl %ebp, 20(%esi) -/* round 5 (C) */ - addl %ebx, %eax - xorl %eax, %edx - movl 92(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 28(%esi), %ebp /* D */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* D */ - rorl $8, %edx - addl 80(%edi), %eax /* D */ - movl %edx, 56(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* D */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* D */ - rorl $7, %ebx - movl %ebx, 24(%esi) -/* round 5 (D) */ - addl %ebp, %eax - xorl %eax, %edx - movl 60(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 20(%esi), %ebx /* E */ - movl %eax, 12(%esi) - xorl %eax, %edx - movl 0(%esi), %eax /* E */ - rorl $8, %edx - addl 64(%edi), %eax /* E */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* E */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* E */ - rorl $7, %ebp - movl %ebp, 28(%esi) -/* round 5 (E) */ - addl %ebx, %eax - xorl %eax, %edx - movl 100(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 24(%esi), %ebp /* F */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* F */ - rorl $8, %edx - addl 76(%edi), %eax /* F */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 48(%esi), %edx /* F */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* F */ - rorl $7, %ebx - movl %ebx, 20(%esi) -/* round 5 (F) */ - addl %ebp, %eax - xorl %eax, %edx - movl 68(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 28(%esi), %ebx /* G */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* G */ - rorl $8, %edx - addl 108(%edi), %eax /* G */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* G */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 32(%esi), %ecx /* G */ - rorl $7, %ebp - movl %ebp, 24(%esi) -/* round 5 (G) */ - addl %ebx, %eax - xorl %eax, %edx - movl 104(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 16(%esi), %ebp /* H */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* H */ - rorl $8, %edx - addl 52(%edi), %eax /* H */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* H */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* H */ - rorl $7, %ebx - movl %ebx, 28(%esi) -/* round 5 (H) */ - addl %ebp, %eax - xorl %eax, %edx - movl 84(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl %eax, 12(%esi) - xorl %eax, %edx - rorl $8, %edx - movl 0(%esi), %eax /* A */ - movl %edx, 56(%esi) - addl %edx, %ecx - addl 96(%edi), %eax /* A */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 48(%esi), %edx /* A */ - rorl $7, %ebp - movl %ebp, %ebx -/* round 6 (A) */ - addl %ebx, %eax - movl 32(%esi), %ecx - xorl %eax, %edx - movl 68(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 20(%esi), %ebp /* B */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* B */ - rorl $8, %edx - addl 52(%edi), %eax /* B */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* B */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* B */ - rorl $7, %ebx - movl %ebx, 16(%esi) -/* round 6 (B) */ - addl %ebp, %eax - xorl %eax, %edx - movl 108(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 24(%esi), %ebx /* C */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* C */ - rorl $8, %edx - addl 104(%edi), %eax /* C */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* C */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* C */ - rorl $7, %ebp - movl %ebp, 20(%esi) -/* round 6 (C) */ - addl %ebx, %eax - xorl %eax, %edx - movl 100(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 28(%esi), %ebp /* D */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* D */ - rorl $8, %edx - addl 64(%edi), %eax /* D */ - movl %edx, 56(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* D */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* D */ - rorl $7, %ebx - movl %ebx, 24(%esi) -/* round 6 (D) */ - addl %ebp, %eax - xorl %eax, %edx - movl 88(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 20(%esi), %ebx /* E */ - movl %eax, 12(%esi) - xorl %eax, %edx - movl 0(%esi), %eax /* E */ - rorl $8, %edx - addl 48(%edi), %eax /* E */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* E */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* E */ - rorl $7, %ebp - movl %ebp, 28(%esi) -/* round 6 (E) */ - addl %ebx, %eax - xorl %eax, %edx - movl 76(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 24(%esi), %ebp /* F */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* F */ - rorl $8, %edx - addl 72(%edi), %eax /* F */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 48(%esi), %edx /* F */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* F */ - rorl $7, %ebx - movl %ebx, 20(%esi) -/* round 6 (F) */ - addl %ebp, %eax - xorl %eax, %edx - movl 60(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 28(%esi), %ebx /* G */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* G */ - rorl $8, %edx - addl 84(%edi), %eax /* G */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* G */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 32(%esi), %ecx /* G */ - rorl $7, %ebp - movl %ebp, 24(%esi) -/* round 6 (G) */ - addl %ebx, %eax - xorl %eax, %edx - movl 56(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 16(%esi), %ebp /* H */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* H */ - rorl $8, %edx - addl 80(%edi), %eax /* H */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* H */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* H */ - rorl $7, %ebx - movl %ebx, 28(%esi) -/* round 6 (H) */ - addl %ebp, %eax - xorl %eax, %edx - movl 92(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl %eax, 12(%esi) - xorl %eax, %edx - rorl $8, %edx - movl 0(%esi), %eax /* A */ - movl %edx, 56(%esi) - addl %edx, %ecx - addl 100(%edi), %eax /* A */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 48(%esi), %edx /* A */ - rorl $7, %ebp - movl %ebp, %ebx -/* round 7 (A) */ - addl %ebx, %eax - movl 32(%esi), %ecx - xorl %eax, %edx - movl 92(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 20(%esi), %ebp /* B */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* B */ - rorl $8, %edx - addl 76(%edi), %eax /* B */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* B */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* B */ - rorl $7, %ebx - movl %ebx, 16(%esi) -/* round 7 (B) */ - addl %ebp, %eax - xorl %eax, %edx - movl 104(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 24(%esi), %ebx /* C */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* C */ - rorl $8, %edx - addl 96(%edi), %eax /* C */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* C */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* C */ - rorl $7, %ebp - movl %ebp, 20(%esi) -/* round 7 (C) */ - addl %ebx, %eax - xorl %eax, %edx - movl 52(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 28(%esi), %ebp /* D */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* D */ - rorl $8, %edx - addl 60(%edi), %eax /* D */ - movl %edx, 56(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* D */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* D */ - rorl $7, %ebx - movl %ebx, 24(%esi) -/* round 7 (D) */ - addl %ebp, %eax - xorl %eax, %edx - movl 84(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 20(%esi), %ebx /* E */ - movl %eax, 12(%esi) - xorl %eax, %edx - movl 0(%esi), %eax /* E */ - rorl $8, %edx - addl 68(%edi), %eax /* E */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* E */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* E */ - rorl $7, %ebp - movl %ebp, 28(%esi) -/* round 7 (E) */ - addl %ebx, %eax - xorl %eax, %edx - movl 48(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 24(%esi), %ebp /* F */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* F */ - rorl $8, %edx - addl 108(%edi), %eax /* F */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 48(%esi), %edx /* F */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* F */ - rorl $7, %ebx - movl %ebx, 20(%esi) -/* round 7 (F) */ - addl %ebp, %eax - xorl %eax, %edx - movl 64(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 28(%esi), %ebx /* G */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* G */ - rorl $8, %edx - addl 80(%edi), %eax /* G */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* G */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 32(%esi), %ecx /* G */ - rorl $7, %ebp - movl %ebp, 24(%esi) -/* round 7 (G) */ - addl %ebx, %eax - xorl %eax, %edx - movl 72(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 16(%esi), %ebp /* H */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* H */ - rorl $8, %edx - addl 56(%edi), %eax /* H */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* H */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* H */ - rorl $7, %ebx - movl %ebx, 28(%esi) -/* round 7 (H) */ - addl %ebp, %eax - xorl %eax, %edx - movl 88(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl %eax, 12(%esi) - xorl %eax, %edx - rorl $8, %edx - movl 0(%esi), %eax /* A */ - movl %edx, 56(%esi) - addl %edx, %ecx - addl 72(%edi), %eax /* A */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 48(%esi), %edx /* A */ - rorl $7, %ebp - movl %ebp, %ebx -/* round 8 (A) */ - addl %ebx, %eax - movl 32(%esi), %ecx - xorl %eax, %edx - movl 108(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 20(%esi), %ebp /* B */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* B */ - rorl $8, %edx - addl 104(%edi), %eax /* B */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* B */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* B */ - rorl $7, %ebx - movl %ebx, 16(%esi) -/* round 8 (B) */ - addl %ebp, %eax - xorl %eax, %edx - movl 84(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 24(%esi), %ebx /* C */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* C */ - rorl $8, %edx - addl 92(%edi), %eax /* C */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* C */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* C */ - rorl $7, %ebp - movl %ebp, 20(%esi) -/* round 8 (C) */ - addl %ebx, %eax - xorl %eax, %edx - movl 60(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 28(%esi), %ebp /* D */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* D */ - rorl $8, %edx - addl 48(%edi), %eax /* D */ - movl %edx, 56(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* D */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* D */ - rorl $7, %ebx - movl %ebx, 24(%esi) -/* round 8 (D) */ - addl %ebp, %eax - xorl %eax, %edx - movl 80(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 20(%esi), %ebx /* E */ - movl %eax, 12(%esi) - xorl %eax, %edx - movl 0(%esi), %eax /* E */ - rorl $8, %edx - addl 96(%edi), %eax /* E */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* E */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* E */ - rorl $7, %ebp - movl %ebp, 28(%esi) -/* round 8 (E) */ - addl %ebx, %eax - xorl %eax, %edx - movl 56(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 24(%esi), %ebp /* F */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* F */ - rorl $8, %edx - addl 100(%edi), %eax /* F */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 48(%esi), %edx /* F */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* F */ - rorl $7, %ebx - movl %ebx, 20(%esi) -/* round 8 (F) */ - addl %ebp, %eax - xorl %eax, %edx - movl 76(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 28(%esi), %ebx /* G */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* G */ - rorl $8, %edx - addl 52(%edi), %eax /* G */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* G */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 32(%esi), %ecx /* G */ - rorl $7, %ebp - movl %ebp, 24(%esi) -/* round 8 (G) */ - addl %ebx, %eax - xorl %eax, %edx - movl 64(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 16(%esi), %ebp /* H */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* H */ - rorl $8, %edx - addl 88(%edi), %eax /* H */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* H */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* H */ - rorl $7, %ebx - movl %ebx, 28(%esi) -/* round 8 (H) */ - addl %ebp, %eax - xorl %eax, %edx - movl 68(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl %eax, 12(%esi) - xorl %eax, %edx - rorl $8, %edx - movl 0(%esi), %eax /* A */ - movl %edx, 56(%esi) - addl %edx, %ecx - addl 88(%edi), %eax /* A */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 48(%esi), %edx /* A */ - rorl $7, %ebp - movl %ebp, %ebx -/* round 9 (A) */ - addl %ebx, %eax - movl 32(%esi), %ecx - xorl %eax, %edx - movl 56(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 20(%esi), %ebp /* B */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* B */ - rorl $8, %edx - addl 80(%edi), %eax /* B */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* B */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* B */ - rorl $7, %ebx - movl %ebx, 16(%esi) -/* round 9 (B) */ - addl %ebp, %eax - xorl %eax, %edx - movl 64(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 24(%esi), %ebx /* C */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* C */ - rorl $8, %edx - addl 76(%edi), %eax /* C */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* C */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* C */ - rorl $7, %ebp - movl %ebp, 20(%esi) -/* round 9 (C) */ - addl %ebx, %eax - xorl %eax, %edx - movl 72(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 28(%esi), %ebp /* D */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* D */ - rorl $8, %edx - addl 52(%edi), %eax /* D */ - movl %edx, 56(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* D */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* D */ - rorl $7, %ebx - movl %ebx, 24(%esi) -/* round 9 (D) */ - addl %ebp, %eax - xorl %eax, %edx - movl 68(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 20(%esi), %ebx /* E */ - movl %eax, 12(%esi) - xorl %eax, %edx - movl 0(%esi), %eax /* E */ - rorl $8, %edx - addl 108(%edi), %eax /* E */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 60(%esi), %edx /* E */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 40(%esi), %ecx /* E */ - rorl $7, %ebp - movl %ebp, 28(%esi) -/* round 9 (E) */ - addl %ebx, %eax - xorl %eax, %edx - movl 92(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 24(%esi), %ebp /* F */ - movl %eax, 0(%esi) - xorl %eax, %edx - movl 4(%esi), %eax /* F */ - rorl $8, %edx - addl 84(%edi), %eax /* F */ - movl %edx, 60(%esi) - addl %edx, %ecx - movl 48(%esi), %edx /* F */ - movl %ecx, 40(%esi) - xorl %ecx, %ebx - movl 44(%esi), %ecx /* F */ - rorl $7, %ebx - movl %ebx, 20(%esi) -/* round 9 (F) */ - addl %ebp, %eax - xorl %eax, %edx - movl 104(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - movl 28(%esi), %ebx /* G */ - movl %eax, 4(%esi) - xorl %eax, %edx - movl 8(%esi), %eax /* G */ - rorl $8, %edx - addl 60(%edi), %eax /* G */ - movl %edx, 48(%esi) - addl %edx, %ecx - movl 52(%esi), %edx /* G */ - movl %ecx, 44(%esi) - xorl %ecx, %ebp - movl 32(%esi), %ecx /* G */ - rorl $7, %ebp - movl %ebp, 24(%esi) -/* round 9 (G) */ - addl %ebx, %eax - xorl %eax, %edx - movl 96(%edi), %ebp - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebx - rorl $12, %ebx - addl %ebp, %eax - addl %ebx, %eax - movl 16(%esi), %ebp /* H */ - movl %eax, 8(%esi) - xorl %eax, %edx - movl 12(%esi), %eax /* H */ - rorl $8, %edx - addl 100(%edi), %eax /* H */ - movl %edx, 52(%esi) - addl %edx, %ecx - movl 56(%esi), %edx /* H */ - movl %ecx, 32(%esi) - xorl %ecx, %ebx - movl 36(%esi), %ecx /* H */ - rorl $7, %ebx - movl %ebx, 28(%esi) -/* round 9 (H) */ - addl %ebp, %eax - xorl %eax, %edx - movl 48(%edi), %ebx - rorl $16, %edx - addl %edx, %ecx - xorl %ecx, %ebp - rorl $12, %ebp - addl %ebx, %eax - addl %ebp, %eax - xorl %eax, %edx - rorl $8, %edx - movl 8(%esi), %ebx /* finalise */ - movl %edx, 56(%esi) - addl %edx, %ecx - movl 0(%esi), %edx /* finalise */ - movl %ecx, 36(%esi) - xorl %ecx, %ebp - movl 4(%esi), %ecx /* finalise */ - rorl $7, %ebp - movl %ebp, 16(%esi) -/* finalise */ - xorl 32(%esi), %edx - xorl 36(%esi), %ecx - xorl 40(%esi), %ebx - xorl 44(%esi), %eax - xorl 0(%edi), %edx - xorl 4(%edi), %ecx - xorl 8(%edi), %ebx - xorl 12(%edi), %eax - movl %edx, 0(%edi) - movl %ecx, 4(%edi) - movl %ebx, 8(%edi) - movl %eax, 12(%edi) - movl 16(%esi), %eax - movl 20(%esi), %ebx - movl 24(%esi), %ecx - movl 28(%esi), %edx - xorl 48(%esi), %eax - xorl 52(%esi), %ebx - xorl 56(%esi), %ecx - xorl 60(%esi), %edx - xorl 16(%edi), %eax - xorl 20(%edi), %ebx - xorl 24(%edi), %ecx - xorl 28(%edi), %edx - movl %eax, 16(%edi) - movl %ebx, 20(%edi) - movl %ecx, 24(%edi) - movl %edx, 28(%edi) - - popl %edi - popl %esi - popl %ebp - popl %ebx - ret - - -/* neoscrypt_copy(dst, src, len) - * i386 memcpy() */ -.globl neoscrypt_copy -.globl _neoscrypt_copy -neoscrypt_copy: -_neoscrypt_copy: - pushl %ebx - pushl %ebp - pushl %edi - pushl %esi - movl 20(%esp), %edi - movl 24(%esp), %esi - movl 28(%esp), %ecx - shrl $4, %ecx - xorl %eax, %eax - cmpl %eax, %ecx - jz .copy_tail -.copy_16b: - movl 0(%esi), %eax - movl 4(%esi), %edx - movl 8(%esi), %ebx - movl 12(%esi), %ebp - movl %eax, 0(%edi) - movl %edx, 4(%edi) - movl %ebx, 8(%edi) - movl %ebp, 12(%edi) - addl $16, %esi - addl $16, %edi - decl %ecx - jnz .copy_16b - -.copy_tail: - xorl %eax, %eax - movl 28(%esp), %ecx - andl $0xF, %ecx - cmpl %eax, %ecx - jz .copy_finish - movb %cl, %ch - andb $0x3, %cl - shrb $2, %ch - cmpb %ah, %ch - jz .copy_1b -.copy_4b: - movl 0(%esi), %edx - movl %edx, 0(%edi) - addl $4, %esi - addl $4, %edi - decb %ch - jnz .copy_4b - - cmpb %al, %cl - jz .copy_finish -.copy_1b: - movb 0(%esi), %dl - movb %dl, 0(%edi) - incl %esi - incl %edi - decb %cl - jnz .copy_1b - -.copy_finish: - popl %esi - popl %edi - popl %ebp - popl %ebx - ret - - -/* neoscrypt_erase(dst, len) - * i386 memory eraser */ -.globl neoscrypt_erase -.globl _neoscrypt_erase -neoscrypt_erase: -_neoscrypt_erase: - movl 4(%esp), %edx - movl 8(%esp), %ecx - shrl $4, %ecx - xorl %eax, %eax - cmpl %eax, %ecx - jz .erase_tail -.erase_16b: - movl %eax, 0(%edx) - movl %eax, 4(%edx) - movl %eax, 8(%edx) - movl %eax, 12(%edx) - addl $16, %edx - decl %ecx - jnz .erase_16b - -.erase_tail: - movl 8(%esp), %ecx - andl $0xF, %ecx - cmpl %eax, %ecx - jz .erase_finish - movb %cl, %ch - andb $0x3, %cl - shrb $2, %ch - cmpb %ah, %ch - jz .erase_1b -.erase_4b: - movl %eax, 0(%edx) - addl $4, %edx - decb %ch - jnz .erase_4b - - cmpb %al, %cl - jz .erase_finish -.erase_1b: - movb %al, 0(%edx) - incl %edx - decb %cl - jnz .erase_1b - -.erase_finish: - ret - - -/* neoscrypt_xor(dst, src, len) - * i386 XOR engine */ -.globl neoscrypt_xor -.globl _neoscrypt_xor -neoscrypt_xor: -_neoscrypt_xor: - pushl %ebx - pushl %ebp - pushl %edi - pushl %esi - movl 20(%esp), %edi - movl 24(%esp), %esi - movl 28(%esp), %ecx - shrl $4, %ecx - xorl %eax, %eax - cmpl %eax, %ecx - jz .xor_tail -.xor_16b: - movl 0(%edi), %eax - movl 4(%edi), %edx - movl 8(%edi), %ebx - movl 12(%edi), %ebp - xorl 0(%esi), %eax - xorl 4(%esi), %edx - xorl 8(%esi), %ebx - xorl 12(%esi), %ebp - movl %eax, 0(%edi) - movl %edx, 4(%edi) - movl %ebx, 8(%edi) - movl %ebp, 12(%edi) - addl $16, %esi - addl $16, %edi - decl %ecx - jnz .xor_16b - -.xor_tail: - xorl %eax, %eax - movl 28(%esp), %ecx - andl $0xF, %ecx - cmpl %eax, %ecx - jz .xor_finish - movb %cl, %ch - andb $0x3, %cl - shrb $2, %ch - cmpb %ah, %ch - jz .xor_1b -.xor_4b: - movl 0(%edi), %edx - xorl 0(%esi), %edx - movl %edx, 0(%edi) - addl $4, %esi - addl $4, %edi - decb %ch - jnz .xor_4b - - cmpb %al, %cl - jz .xor_finish -.xor_1b: - movb 0(%edi), %dl - xorb 0(%esi), %dl - movb %dl, 0(%edi) - incl %esi - incl %edi - decb %cl - jnz .xor_1b - -.xor_finish: - popl %esi - popl %edi - popl %ebp - popl %ebx - ret - - -/* neoscrypt_fastkdf_opt(password, salt, output, output_len) - * i386 (MMX) FastKDF optimised */ -.globl neoscrypt_fastkdf_opt -.globl _neoscrypt_fastkdf_opt -neoscrypt_fastkdf_opt: -_neoscrypt_fastkdf_opt: - pushl %ebx - pushl %ebp - pushl %esi - pushl %edi - -/* 32 bytes (call stack and local variables + 64 bytes (alignment space) + - * 320 bytes (password buffer) + 288 bytes (salt buffer) + 112 bytes (BLAKE2s - * space) = 816 bytes */ - subl $816, %esp - leal 96(%esp), %ebp - andl $0xFFFFFFC0, %ebp - movl %ebp, 28(%esp) - - movl 836(%esp), %edx - movq 0(%edx), %mm0 - movq 8(%edx), %mm1 - movq 16(%edx), %mm2 - movq 24(%edx), %mm3 - movq 32(%edx), %mm4 - movq 40(%edx), %mm5 - movq 48(%edx), %mm6 - movq 56(%edx), %mm7 - movq %mm0, 0(%ebp) - movq %mm1, 8(%ebp) - movq %mm2, 16(%ebp) - movq %mm3, 24(%ebp) - movq %mm4, 32(%ebp) - movq %mm5, 40(%ebp) - movq %mm6, 48(%ebp) - movq %mm7, 56(%ebp) - movq %mm0, 80(%ebp) - movq %mm1, 88(%ebp) - movq %mm2, 96(%ebp) - movq %mm3, 104(%ebp) - movq %mm4, 112(%ebp) - movq %mm5, 120(%ebp) - movq %mm6, 128(%ebp) - movq %mm7, 136(%ebp) - movq %mm0, 160(%ebp) - movq %mm1, 168(%ebp) - movq %mm2, 176(%ebp) - movq %mm3, 184(%ebp) - movq %mm4, 192(%ebp) - movq %mm5, 200(%ebp) - movq %mm6, 208(%ebp) - movq %mm7, 216(%ebp) - movq %mm0, 240(%ebp) - movq %mm1, 248(%ebp) - movq %mm0, 256(%ebp) - movq %mm1, 264(%ebp) - movq %mm2, 272(%ebp) - movq %mm3, 280(%ebp) - movq %mm4, 288(%ebp) - movq %mm5, 296(%ebp) - movq %mm6, 304(%ebp) - movq %mm7, 312(%ebp) - movq 64(%edx), %mm0 - movq 72(%edx), %mm1 - movq %mm0, 64(%ebp) - movq %mm1, 72(%ebp) - movq %mm0, 144(%ebp) - movq %mm1, 152(%ebp) - movq %mm0, 224(%ebp) - movq %mm1, 232(%ebp) - - movl 840(%esp), %edx - leal 320(%ebp), %ebx - movl $32, 20(%esp) - xorl %edi, %edi - testl $0x01, 848(%esp) - jnz .fastkdf_mode_one - - movl $256, 24(%esp) - movq 0(%edx), %mm0 - movq 8(%edx), %mm1 - movq 16(%edx), %mm2 - movq 24(%edx), %mm3 - movq 32(%edx), %mm4 - movq 40(%edx), %mm5 - movq 48(%edx), %mm6 - movq 56(%edx), %mm7 - movq %mm0, 0(%ebx) - movq %mm1, 8(%ebx) - movq %mm2, 16(%ebx) - movq %mm3, 24(%ebx) - movq %mm4, 32(%ebx) - movq %mm5, 40(%ebx) - movq %mm6, 48(%ebx) - movq %mm7, 56(%ebx) - movq %mm0, 80(%ebx) - movq %mm1, 88(%ebx) - movq %mm2, 96(%ebx) - movq %mm3, 104(%ebx) - movq %mm4, 112(%ebx) - movq %mm5, 120(%ebx) - movq %mm6, 128(%ebx) - movq %mm7, 136(%ebx) - movq %mm0, 160(%ebx) - movq %mm1, 168(%ebx) - movq %mm2, 176(%ebx) - movq %mm3, 184(%ebx) - movq %mm4, 192(%ebx) - movq %mm5, 200(%ebx) - movq %mm6, 208(%ebx) - movq %mm7, 216(%ebx) - movq %mm0, 240(%ebx) - movq %mm1, 248(%ebx) - movq %mm0, 256(%ebx) - movq %mm1, 264(%ebx) - movq %mm2, 272(%ebx) - movq %mm3, 280(%ebx) - movq 64(%edx), %mm0 - movq 72(%edx), %mm1 - movq %mm0, 64(%ebx) - movq %mm1, 72(%ebx) - movq %mm0, 144(%ebx) - movq %mm1, 152(%ebx) - movq %mm0, 224(%ebx) - movq %mm1, 232(%ebx) - jmp .fastkdf_loop - -.fastkdf_mode_one: - movl $32, 24(%esp) - movq 0(%edx), %mm0 - movq 8(%edx), %mm1 - movq 16(%edx), %mm2 - movq 24(%edx), %mm3 - movq 32(%edx), %mm4 - movq 40(%edx), %mm5 - movq 48(%edx), %mm6 - movq 56(%edx), %mm7 - movq %mm0, 0(%ebx) - movq %mm1, 8(%ebx) - movq %mm2, 16(%ebx) - movq %mm3, 24(%ebx) - movq %mm4, 32(%ebx) - movq %mm5, 40(%ebx) - movq %mm6, 48(%ebx) - movq %mm7, 56(%ebx) - movq %mm0, 256(%ebx) - movq %mm1, 264(%ebx) - movq %mm2, 272(%ebx) - movq %mm3, 280(%ebx) - movq 64(%edx), %mm0 - movq 72(%edx), %mm1 - movq 80(%edx), %mm2 - movq 88(%edx), %mm3 - movq 96(%edx), %mm4 - movq 104(%edx), %mm5 - movq 112(%edx), %mm6 - movq 120(%edx), %mm7 - movq %mm0, 64(%ebx) - movq %mm1, 72(%ebx) - movq %mm2, 80(%ebx) - movq %mm3, 88(%ebx) - movq %mm4, 96(%ebx) - movq %mm5, 104(%ebx) - movq %mm6, 112(%ebx) - movq %mm7, 120(%ebx) - movq 128(%edx), %mm0 - movq 136(%edx), %mm1 - movq 144(%edx), %mm2 - movq 152(%edx), %mm3 - movq 160(%edx), %mm4 - movq 168(%edx), %mm5 - movq 176(%edx), %mm6 - movq 184(%edx), %mm7 - movq %mm0, 128(%ebx) - movq %mm1, 136(%ebx) - movq %mm2, 144(%ebx) - movq %mm3, 152(%ebx) - movq %mm4, 160(%ebx) - movq %mm5, 168(%ebx) - movq %mm6, 176(%ebx) - movq %mm7, 184(%ebx) - movq 192(%edx), %mm0 - movq 200(%edx), %mm1 - movq 208(%edx), %mm2 - movq 216(%edx), %mm3 - movq 224(%edx), %mm4 - movq 232(%edx), %mm5 - movq 240(%edx), %mm6 - movq 248(%edx), %mm7 - movq %mm0, 192(%ebx) - movq %mm1, 200(%ebx) - movq %mm2, 208(%ebx) - movq %mm3, 216(%ebx) - movq %mm4, 224(%ebx) - movq %mm5, 232(%ebx) - movq %mm6, 240(%ebx) - movq %mm7, 248(%ebx) - -.fastkdf_loop: - movl 28(%esp), %edx - leal 0(%edx, %edi), %ebp - leal 320(%edx, %edi), %ebx - leal 608(%edx), %esi - xorl %ecx, %ecx - pxor %mm0, %mm0 - - movl $0x6B08C647, 0(%esi) - movl $0xBB67AE85, 4(%esi) - movl $0x3C6EF372, 8(%esi) - movl $0xA54FF53A, 12(%esi) - movl $0x510E527F, 16(%esi) - movl $0x9B05688C, 20(%esi) - movl $0x1F83D9AB, 24(%esi) - movl $0x5BE0CD19, 28(%esi) - movl $64, 32(%esi) - movl %ecx, 36(%esi) - movq %mm0, 40(%esi) - - movq 0(%ebx), %mm4 - movq 8(%ebx), %mm5 - movq 16(%ebx), %mm6 - movq 24(%ebx), %mm7 - movq %mm4, 48(%esi) - movq %mm5, 56(%esi) - movq %mm6, 64(%esi) - movq %mm7, 72(%esi) - movq %mm0, 80(%esi) - movq %mm0, 88(%esi) - movq %mm0, 96(%esi) - movq %mm0, 104(%esi) - - movl %esi, 0(%esp) - call blake2s_compress - - movq 0(%ebp), %mm0 - movq 8(%ebp), %mm1 - movq 16(%ebp), %mm2 - movq 24(%ebp), %mm3 - movq 32(%ebp), %mm4 - movq 40(%ebp), %mm5 - movq 48(%ebp), %mm6 - movq 56(%ebp), %mm7 - movq %mm0, 48(%esi) - movq %mm1, 56(%esi) - movq %mm2, 64(%esi) - movq %mm3, 72(%esi) - movq %mm4, 80(%esi) - movq %mm5, 88(%esi) - movq %mm6, 96(%esi) - movq %mm7, 104(%esi) - - movl $128, 32(%esi) - movl $0xFFFFFFFF, 40(%esi) - call blake2s_compress - - movq 0(%esi), %mm3 - movq 8(%esi), %mm5 - movq 16(%esi), %mm6 - movq 24(%esi), %mm7 - pxor %mm0, %mm0 - movq %mm3, %mm4 - paddb %mm5, %mm3 - paddb %mm6, %mm3 - paddb %mm7, %mm3 - psadbw %mm0, %mm3 - movd %mm3, %edi - andl $0xFF, %edi - movl 28(%esp), %edx - leal 320(%edx, %edi), %ebx - movq 0(%ebx), %mm0 - movq 8(%ebx), %mm1 - movq 16(%ebx), %mm2 - movq 24(%ebx), %mm3 - pxor %mm4, %mm0 - pxor %mm5, %mm1 - pxor %mm6, %mm2 - pxor %mm7, %mm3 - movq %mm0, 0(%ebx) - movq %mm1, 8(%ebx) - movq %mm2, 16(%ebx) - movq %mm3, 24(%ebx) - -/* tail update */ - movl $32, %eax - cmpl %edi, %eax - jc .fastkdf_headupd - leal 256(%ebx), %edx - movl %edx, 0(%esp) - movl %ebx, 4(%esp) - subl %edi, %eax - movl %eax, 8(%esp) - call neoscrypt_copy - jmp .fastkdf_loop_end - -/* head update */ -.fastkdf_headupd: - movl $224, %eax - cmpl %edi, %eax - jnc .fastkdf_loop_end - movl %ebx, %edx - subl %edi, %edx - movl %edx, 0(%esp) - leal 256(%edx), %edx - movl %edx, 4(%esp) - movl %edi, %edx - subl %eax, %edx - movl %edx, 8(%esp) - call neoscrypt_copy - -.fastkdf_loop_end: - decl 20(%esp) - jnz .fastkdf_loop - - movl 24(%esp), %esi - movl 28(%esp), %ebp - movl $256, %ebx - subl %edi, %ebx - cmpl %esi, %ebx - jc .fastkdf_crosscopy - - leal 320(%ebp, %edi), %ebx - movl %ebx, 0(%esp) - movl %ebp, 4(%esp) - movl %esi, 8(%esp) - call neoscrypt_xor - movl 844(%esp), %eax - movl %eax, 0(%esp) - movl %ebx, 4(%esp) - call neoscrypt_copy - jmp .fastkdf_finish - -.fastkdf_crosscopy: - leal 320(%ebp, %edi), %edi - movl %edi, 0(%esp) - movl %ebp, 4(%esp) - movl %ebx, 8(%esp) - call neoscrypt_xor - leal 320(%ebp), %eax - movl %eax, 0(%esp) - leal 0(%ebp, %ebx), %edx - movl %edx, 4(%esp) - subl %ebx, %esi - movl %esi, 8(%esp) - call neoscrypt_xor - movl 844(%esp), %eax - movl %eax, 0(%esp) - movl %edi, 4(%esp) - movl %ebx, 8(%esp) - call neoscrypt_copy - movl 844(%esp), %eax - leal 0(%eax, %ebx), %eax - movl %eax, 0(%esp) - leal 320(%ebp), %edx - movl %edx, 4(%esp) - movl %esi, 8(%esp) - call neoscrypt_copy - -.fastkdf_finish: - addl $816, %esp - popl %edi - popl %esi - popl %ebp - popl %ebx - emms - ret - - -/* neoscrypt_xor_salsa(mem, xormem, workmem, double_rounds) - * i386 (INT) Salsa20 with XOR (MMX support required) */ -neoscrypt_xor_salsa: - pushl %ebx - pushl %ebp - pushl %esi - pushl %edi -/* XOR and copy to temporary memory */ - movl 20(%esp), %ebx - movl 24(%esp), %ecx - movl 28(%esp), %ebp - movq 0(%ebx), %mm0 - movq 8(%ebx), %mm1 - movq 16(%ebx), %mm2 - movq 24(%ebx), %mm3 - movq 32(%ebx), %mm4 - movq 40(%ebx), %mm5 - movq 48(%ebx), %mm6 - movq 56(%ebx), %mm7 - pxor 0(%ecx), %mm0 - pxor 8(%ecx), %mm1 - pxor 16(%ecx), %mm2 - pxor 24(%ecx), %mm3 - pxor 32(%ecx), %mm4 - pxor 40(%ecx), %mm5 - pxor 48(%ecx), %mm6 - pxor 56(%ecx), %mm7 - movq %mm0, 0(%ebx) - movq %mm1, 8(%ebx) - movq %mm2, 16(%ebx) - movq %mm3, 24(%ebx) - movq %mm4, 32(%ebx) - movq %mm5, 40(%ebx) - movq %mm6, 48(%ebx) - movq %mm7, 56(%ebx) - movq %mm0, 0(%ebp) - movq %mm1, 8(%ebp) - movq %mm2, 16(%ebp) - movq %mm3, 24(%ebp) - movq %mm4, 32(%ebp) - movq %mm5, 40(%ebp) - movq %mm6, 48(%ebp) - movq %mm7, 56(%ebp) -/* number of double rounds */ - movl 32(%esp), %eax - movl %eax, -4(%esp) -.xor_salsa: -/* quarters A and B, initial C and D */ - movl 0(%ebp), %eax /* A: load a */ - movl 20(%ebp), %ebx /* B: load a */ - addl 48(%ebp), %eax /* A: t = a + d */ - addl 4(%ebp), %ebx /* B: t = a + d */ - roll $7, %eax /* A: rotate t */ - roll $7, %ebx /* B: rotate t */ - xorl 16(%ebp), %eax /* A: b = b ^ t */ - xorl 36(%ebp), %ebx /* B: b = b ^ t */ - movl %eax, %esi /* A: copy b */ - movl %ebx, %edi /* B: copy b */ - movl %esi, 16(%ebp) /* A: store b */ - movl %edi, 36(%ebp) /* B: store b */ - addl 0(%ebp), %eax /* A: t = b + a */ - addl 20(%ebp), %ebx /* B: t = b + a */ - roll $9, %eax /* A: rotate t */ - roll $9, %ebx /* B: rotate t */ - xorl 32(%ebp), %eax /* A: c = c ^ t */ - xorl 52(%ebp), %ebx /* B: c = c ^ t */ - movl %eax, %ecx /* A: copy c */ - movl %ebx, %edx /* B: copy c */ - movl %ecx, 32(%ebp) /* A: store c */ - movl %edx, 52(%ebp) /* B: store c */ - addl %esi, %eax /* A: t = c + b */ - addl %edi, %ebx /* B: t = c + b */ - roll $13, %eax /* A: rotate t */ - roll $13, %ebx /* B: rotate t */ - xorl 48(%ebp), %eax /* A: d = d ^ t */ - xorl 4(%ebp), %ebx /* B: d = d ^ t */ - movl %eax, 48(%ebp) /* A: store d */ - movl %ebx, 4(%ebp) /* B: store d */ - addl %eax, %ecx /* A: t = d + c */ - movl 40(%ebp), %eax /* C: load a */ - addl %ebx, %edx /* B: t = d + c */ - movl 60(%ebp), %ebx /* D: load a */ - roll $18, %ecx /* A: rotate t */ - addl 24(%ebp), %eax /* C: t = a + d */ - roll $18, %edx /* B: rotate t */ - addl 44(%ebp), %ebx /* D: t = a + d */ - xorl 0(%ebp), %ecx /* A: a = a ^ t */ - roll $7, %eax /* C: rotate t */ - xorl 20(%ebp), %edx /* B: a = a ^ t */ - roll $7, %ebx /* D: rotate t */ - movl %ecx, 0(%ebp) /* A: store a */ - movl %edx, 20(%ebp) /* B: store a */ -/* quarters C and D, initial E and F */ - xorl 56(%ebp), %eax /* C: b = b ^ t */ - xorl 12(%ebp), %ebx /* D: b = b ^ t */ - movl %eax, %esi /* C: copy b */ - movl %ebx, %edi /* D: copy b */ - movl %esi, 56(%ebp) /* C: store b */ - movl %edi, 12(%ebp) /* D: store b */ - addl 40(%ebp), %eax /* C: t = b + a */ - addl 60(%ebp), %ebx /* D: t = b + a */ - roll $9, %eax /* C: rotate t */ - roll $9, %ebx /* D: rotate t */ - xorl 8(%ebp), %eax /* C: c = c ^ t */ - xorl 28(%ebp), %ebx /* D: c = c ^ t */ - movl %eax, %ecx /* C: copy c */ - movl %ebx, %edx /* D: copy c */ - movl %ecx, 8(%ebp) /* C: store c */ - movl %edx, 28(%ebp) /* D: store c */ - addl %esi, %eax /* C: t = c + b */ - addl %edi, %ebx /* D: t = c + b */ - roll $13, %eax /* C: rotate t */ - roll $13, %ebx /* D: rotate t */ - xorl 24(%ebp), %eax /* C: d = d ^ t */ - xorl 44(%ebp), %ebx /* D: d = d ^ t */ - movl %eax, 24(%ebp) /* C: store d */ - movl %ebx, 44(%ebp) /* D: store d */ - addl %eax, %ecx /* C: t = d + c */ - movl 0(%ebp), %eax /* E: load a */ - addl %ebx, %edx /* D: t = d + c */ - movl 20(%ebp), %ebx /* F: load a */ - roll $18, %ecx /* C: rotate t */ - addl 12(%ebp), %eax /* E: t = a + d */ - roll $18, %edx /* D: rotate t */ - addl 16(%ebp), %ebx /* F: t = a + d */ - xorl 40(%ebp), %ecx /* C: a = a ^ t */ - roll $7, %eax /* E: rotate t */ - xorl 60(%ebp), %edx /* D: a = a ^ t */ - roll $7, %ebx /* F: rotate t */ - movl %ecx, 40(%ebp) /* C: store a */ - movl %edx, 60(%ebp) /* D: store a */ -/* quarters E and F, initial G and H */ - xorl 4(%ebp), %eax /* E: b = b ^ t */ - xorl 24(%ebp), %ebx /* F: b = b ^ t */ - movl %eax, %esi /* E: copy b */ - movl %ebx, %edi /* F: copy b */ - movl %esi, 4(%ebp) /* E: store b */ - movl %edi, 24(%ebp) /* F: store b */ - addl 0(%ebp), %eax /* E: t = b + a */ - addl 20(%ebp), %ebx /* F: t = b + a */ - roll $9, %eax /* E: rotate t */ - roll $9, %ebx /* F: rotate t */ - xorl 8(%ebp), %eax /* E: c = c ^ t */ - xorl 28(%ebp), %ebx /* F: c = c ^ t */ - movl %eax, %ecx /* E: copy c */ - movl %ebx, %edx /* F: copy c */ - movl %ecx, 8(%ebp) /* E: store c */ - movl %edx, 28(%ebp) /* F: store c */ - addl %esi, %eax /* E: t = c + b */ - addl %edi, %ebx /* F: t = c + b */ - roll $13, %eax /* E: rotate t */ - roll $13, %ebx /* F: rotate t */ - xorl 12(%ebp), %eax /* E: d = d ^ t */ - xorl 16(%ebp), %ebx /* F: d = d ^ t */ - movl %eax, 12(%ebp) /* E: store d */ - movl %ebx, 16(%ebp) /* F: store d */ - addl %eax, %ecx /* E: t = d + c */ - movl 40(%ebp), %eax /* G: load a */ - addl %ebx, %edx /* F: t = d + c */ - movl 60(%ebp), %ebx /* H: load a */ - roll $18, %ecx /* E: rotate t */ - addl 36(%ebp), %eax /* G: t = a + d */ - roll $18, %edx /* F: rotate t */ - addl 56(%ebp), %ebx /* H: t = a + d */ - xorl 0(%ebp), %ecx /* E: a = a ^ t */ - roll $7, %eax /* G: rotate t */ - xorl 20(%ebp), %edx /* F: a = a ^ t */ - roll $7, %ebx /* H: rotate t */ - movl %ecx, 0(%ebp) /* E: store a */ - movl %edx, 20(%ebp) /* F: store a */ -/* quarters G and H */ - xorl 44(%ebp), %eax /* G: b = b ^ t */ - xorl 48(%ebp), %ebx /* H: b = b ^ t */ - movl %eax, %esi /* G: copy b */ - movl %ebx, %edi /* H: copy b */ - movl %esi, 44(%ebp) /* G: store b */ - movl %edi, 48(%ebp) /* H: store b */ - addl 40(%ebp), %eax /* G: t = b + a */ - addl 60(%ebp), %ebx /* H: t = b + a */ - roll $9, %eax /* G: rotate t */ - roll $9, %ebx /* H: rotate t */ - xorl 32(%ebp), %eax /* G: c = c ^ t */ - xorl 52(%ebp), %ebx /* H: c = c ^ t */ - movl %eax, %ecx /* G: copy c */ - movl %ebx, %edx /* H: copy c */ - movl %ecx, 32(%ebp) /* G: store c */ - movl %edx, 52(%ebp) /* H: store c */ - addl %esi, %eax /* G: t = c + b */ - addl %edi, %ebx /* H: t = c + b */ - roll $13, %eax /* G: rotate t */ - roll $13, %ebx /* H: rotate t */ - xorl 36(%ebp), %eax /* G: d = d ^ t */ - xorl 56(%ebp), %ebx /* H: d = d ^ t */ - movl %eax, 36(%ebp) /* G: store d */ - movl %ebx, 56(%ebp) /* H: store d */ - addl %eax, %ecx /* G: t = d + c */ - addl %ebx, %edx /* H: t = d + c */ - roll $18, %ecx /* G: rotate t */ - roll $18, %edx /* H: rotate t */ - xorl 40(%ebp), %ecx /* G: a = a ^ t */ - xorl 60(%ebp), %edx /* H: a = a ^ t */ - movl %ecx, 40(%ebp) /* G: store a */ - movl %edx, 60(%ebp) /* H: store a */ - decl -4(%esp) - jnz .xor_salsa - -/* write back data */ - movl 20(%esp), %ebx - movq 0(%ebx), %mm0 - movq 8(%ebx), %mm1 - movq 16(%ebx), %mm2 - movq 24(%ebx), %mm3 - movq 32(%ebx), %mm4 - movq 40(%ebx), %mm5 - movq 48(%ebx), %mm6 - movq 56(%ebx), %mm7 - paddd 0(%ebp), %mm0 - paddd 8(%ebp), %mm1 - paddd 16(%ebp), %mm2 - paddd 24(%ebp), %mm3 - paddd 32(%ebp), %mm4 - paddd 40(%ebp), %mm5 - paddd 48(%ebp), %mm6 - paddd 56(%ebp), %mm7 - movq %mm0, 0(%ebx) - movq %mm1, 8(%ebx) - movq %mm2, 16(%ebx) - movq %mm3, 24(%ebx) - movq %mm4, 32(%ebx) - movq %mm5, 40(%ebx) - movq %mm6, 48(%ebx) - movq %mm7, 56(%ebx) - - popl %edi - popl %esi - popl %ebp - popl %ebx - ret - - -/* neoscrypt_xor_chacha(mem, xormem, tempmem, double_rounds) - * i386 (INT) ChaCha20 with XOR (MMX support required) */ -neoscrypt_xor_chacha: - pushl %ebx - pushl %ebp - pushl %esi - pushl %edi -/* XOR and copy to temporary memory */ - movl 20(%esp), %ebx - movl 24(%esp), %ecx - movl 28(%esp), %ebp - movq 0(%ebx), %mm0 - movq 8(%ebx), %mm1 - movq 16(%ebx), %mm2 - movq 24(%ebx), %mm3 - movq 32(%ebx), %mm4 - movq 40(%ebx), %mm5 - movq 48(%ebx), %mm6 - movq 56(%ebx), %mm7 - pxor 0(%ecx), %mm0 - pxor 8(%ecx), %mm1 - pxor 16(%ecx), %mm2 - pxor 24(%ecx), %mm3 - pxor 32(%ecx), %mm4 - pxor 40(%ecx), %mm5 - pxor 48(%ecx), %mm6 - pxor 56(%ecx), %mm7 - movq %mm0, 0(%ebx) - movq %mm1, 8(%ebx) - movq %mm2, 16(%ebx) - movq %mm3, 24(%ebx) - movq %mm4, 32(%ebx) - movq %mm5, 40(%ebx) - movq %mm6, 48(%ebx) - movq %mm7, 56(%ebx) - movq %mm0, 0(%ebp) - movq %mm1, 8(%ebp) - movq %mm2, 16(%ebp) - movq %mm3, 24(%ebp) - movq %mm4, 32(%ebp) - movq %mm5, 40(%ebp) - movq %mm6, 48(%ebp) - movq %mm7, 56(%ebp) -/* number of double rounds */ - movl 32(%esp), %eax - movl %eax, -4(%esp) -.xor_chacha: -/* quarters A and B, initial C */ - movl 0(%ebp), %eax /* A: load a */ - movl 16(%ebp), %ebx /* A: load b */ - addl %ebx, %eax /* A: a = a + b */ - movl 32(%ebp), %ecx /* A: load c */ - movl 48(%ebp), %edx /* A: load d */ - xorl %eax, %edx /* A: d = d ^ a */ - movl 4(%ebp), %edi /* B: load a */ - roll $16, %edx /* A: rotate d */ - movl 20(%ebp), %esi /* B: load b */ - addl %edx, %ecx /* A: c = c + d */ - xorl %ecx, %ebx /* A: b = b ^ c */ - addl %esi, %edi /* B: a = a + b */ - roll $12, %ebx /* A: rotate b */ - addl %ebx, %eax /* A: a = a + b */ - movl %eax, 0(%ebp) /* A: store a */ - xorl %eax, %edx /* A: d = d ^ a */ - movl 52(%ebp), %eax /* B: load d */ - roll $8, %edx /* A: rotate d */ - xorl %edi, %eax /* B: d = d ^ a */ - movl %edx, 48(%ebp) /* A: store d */ - addl %edx, %ecx /* A: c = c + d */ - movl 36(%ebp), %edx /* B: load c */ - movl %ecx, 32(%ebp) /* A: store c */ - xorl %ecx, %ebx /* A: b = b ^ c */ - roll $16, %eax /* B: rotate d */ - movl 40(%ebp), %ecx /* C: load c */ - roll $7, %ebx /* A: rotate b */ - addl %eax, %edx /* B: c = c + d */ - movl %ebx, 16(%ebp) /* A: store b */ - xorl %edx, %esi /* B: b = b ^ c */ - movl 24(%ebp), %ebx /* C: load b */ - roll $12, %esi /* B: rotate b */ - addl %esi, %edi /* B: a = a + b */ - movl %edi, 4(%ebp) /* B: store a */ - xorl %edi, %eax /* B: d = d ^ a */ - roll $8, %eax /* B: rotate d */ - movl %eax, 52(%ebp) /* B: store d */ - addl %eax, %edx /* B: c = c + d */ - movl 8(%ebp), %eax /* C: load a */ - movl %edx, 36(%ebp) /* B: store c */ - xorl %edx, %esi /* B: b = b ^ c */ - movl 56(%ebp), %edx /* C: load d */ - roll $7, %esi /* B: rotate b */ - addl %ebx, %eax /* C: a = a + b */ - movl %esi, 20(%ebp) /* B: store b */ -/* quarters C and D, initial E */ - xorl %eax, %edx /* C: d = d ^ a */ - movl 12(%ebp), %edi /* D: load a */ - roll $16, %edx /* C: rotate d */ - movl 28(%ebp), %esi /* D: load b */ - addl %edx, %ecx /* C: c = c + d */ - xorl %ecx, %ebx /* C: b = b ^ c */ - addl %esi, %edi /* D: a = a + b */ - roll $12, %ebx /* C: rotate b */ - addl %ebx, %eax /* C: a = a + b */ - movl %eax, 8(%ebp) /* C: store a */ - xorl %eax, %edx /* C: d = d ^ a */ - movl 60(%ebp), %eax /* D: load d */ - roll $8, %edx /* C: rotate d */ - xorl %edi, %eax /* D: d = d ^ a */ - movl %edx, 56(%ebp) /* C: store d */ - addl %edx, %ecx /* C: c = c + d */ - movl 44(%ebp), %edx /* D: load c */ - movl %ecx, 40(%ebp) /* C: store c */ - xorl %ecx, %ebx /* C: b = b ^ c */ - roll $16, %eax /* D: rotate d */ - movl 40(%ebp), %ecx /* E: load c */ - roll $7, %ebx /* C: rotate b */ - addl %eax, %edx /* D: c = c + d */ - movl %ebx, 24(%ebp) /* C: store b */ - xorl %edx, %esi /* D: b = b ^ c */ - movl 20(%ebp), %ebx /* E: load b */ - roll $12, %esi /* D: rotate b */ - addl %esi, %edi /* D: a = a + b */ - movl %edi, 12(%ebp) /* D: store a */ - xorl %edi, %eax /* D: d = d ^ a */ - roll $8, %eax /* D: rotate d */ - movl %eax, 60(%ebp) /* D: store d */ - addl %eax, %edx /* D: c = c + d */ - movl 0(%ebp), %eax /* E: load a */ - movl %edx, 44(%ebp) /* D: store c */ - xorl %edx, %esi /* D: b = b ^ c */ - movl 60(%ebp), %edx /* E: load d */ - roll $7, %esi /* D: rotate b */ - addl %ebx, %eax /* E: a = a + b */ - movl %esi, 28(%ebp) /* D: store b */ -/* quarters E and F, initial G */ - xorl %eax, %edx /* E: d = d ^ a */ - movl 4(%ebp), %edi /* F: load a */ - roll $16, %edx /* E: rotate d */ - movl 24(%ebp), %esi /* F: load b */ - addl %edx, %ecx /* E: c = c + d */ - xorl %ecx, %ebx /* E: b = b ^ c */ - addl %esi, %edi /* F: a = a + b */ - roll $12, %ebx /* E: rotate b */ - addl %ebx, %eax /* E: a = a + b */ - movl %eax, 0(%ebp) /* E: store a */ - xorl %eax, %edx /* E: d = d ^ a */ - movl 48(%ebp), %eax /* F: load d */ - roll $8, %edx /* E: rotate d */ - xorl %edi, %eax /* F: d = d ^ a */ - movl %edx, 60(%ebp) /* E: store d */ - addl %edx, %ecx /* E: c = c + d */ - movl 44(%ebp), %edx /* F: load c */ - movl %ecx, 40(%ebp) /* E: store c */ - xorl %ecx, %ebx /* E: b = b ^ c */ - roll $16, %eax /* F: rotate d */ - movl 32(%ebp), %ecx /* G: load c */ - roll $7, %ebx /* E: rotate b */ - addl %eax, %edx /* F: c = c + d */ - movl %ebx, 20(%ebp) /* E: store b */ - xorl %edx, %esi /* F: b = b ^ c */ - movl 28(%ebp), %ebx /* G: load b */ - roll $12, %esi /* F: rotate b */ - addl %esi, %edi /* F: a = a + b */ - movl %edi, 4(%ebp) /* F: store a */ - xorl %edi, %eax /* F: d = d ^ a */ - roll $8, %eax /* F: rotate d */ - movl %eax, 48(%ebp) /* F: store d */ - addl %eax, %edx /* F: c = c + d */ - movl 8(%ebp), %eax /* G: load a */ - movl %edx, 44(%ebp) /* F: store c */ - xorl %edx, %esi /* F: b = b ^ c */ - movl 52(%ebp), %edx /* G: load d */ - roll $7, %esi /* F: rotate b */ - addl %ebx, %eax /* G: a = a + b */ - movl %esi, 24(%ebp) /* F: store b */ -/* quarters G and H */ - xorl %eax, %edx /* G: d = d ^ a */ - movl 12(%ebp), %edi /* H: load a */ - roll $16, %edx /* G: rotate d */ - movl 16(%ebp), %esi /* H: load b */ - addl %edx, %ecx /* G: c = c + d */ - xorl %ecx, %ebx /* G: b = b ^ c */ - addl %esi, %edi /* H: a = a + b */ - roll $12, %ebx /* G: rotate b */ - addl %ebx, %eax /* G: a = a + b */ - movl %eax, 8(%ebp) /* G: store a */ - xorl %eax, %edx /* G: d = d ^ a */ - movl 56(%ebp), %eax /* H: load d */ - roll $8, %edx /* G: rotate d */ - xorl %edi, %eax /* H: d = d ^ a */ - movl %edx, 52(%ebp) /* G: store d */ - addl %edx, %ecx /* G: c = c + d */ - movl 36(%ebp), %edx /* H: load c */ - movl %ecx, 32(%ebp) /* G: store c */ - xorl %ecx, %ebx /* G: b = b ^ c */ - roll $16, %eax /* H: rotate d */ - roll $7, %ebx /* G: rotate b */ - addl %eax, %edx /* H: c = c + d */ - movl %ebx, 28(%ebp) /* G: store b */ - xorl %edx, %esi /* H: b = b ^ c */ - roll $12, %esi /* H: rotate b */ - addl %esi, %edi /* H: a = a + b */ - movl %edi, 12(%ebp) /* H: store a */ - xorl %edi, %eax /* H: d = d ^ a */ - roll $8, %eax /* H: rotate d */ - movl %eax, 56(%ebp) /* H: store d */ - addl %eax, %edx /* H: c = c + d */ - movl %edx, 36(%ebp) /* H: store c */ - xorl %edx, %esi /* H: b = b ^ c */ - roll $7, %esi /* H: rotate b */ - movl %esi, 16(%ebp) /* H: store b */ - decl -4(%esp) - jnz .xor_chacha - -/* write back data */ - movl 20(%esp), %ebx - movq 0(%ebx), %mm0 - movq 8(%ebx), %mm1 - movq 16(%ebx), %mm2 - movq 24(%ebx), %mm3 - movq 32(%ebx), %mm4 - movq 40(%ebx), %mm5 - movq 48(%ebx), %mm6 - movq 56(%ebx), %mm7 - paddd 0(%ebp), %mm0 - paddd 8(%ebp), %mm1 - paddd 16(%ebp), %mm2 - paddd 24(%ebp), %mm3 - paddd 32(%ebp), %mm4 - paddd 40(%ebp), %mm5 - paddd 48(%ebp), %mm6 - paddd 56(%ebp), %mm7 - movq %mm0, 0(%ebx) - movq %mm1, 8(%ebx) - movq %mm2, 16(%ebx) - movq %mm3, 24(%ebx) - movq %mm4, 32(%ebx) - movq %mm5, 40(%ebx) - movq %mm6, 48(%ebx) - movq %mm7, 56(%ebx) - - popl %edi - popl %esi - popl %ebp - popl %ebx - ret - - -/* neoscrypt_salsa_tangle_sse2(mem, count) - * i386 (SSE2) Salsa20 map switcher; - * correct map: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - * SSE2 map: 0 5 10 15 12 1 6 11 8 13 2 7 4 9 14 3 */ -neoscrypt_salsa_tangle_sse2: - pushl %ebx - movl 8(%esp), %ebx - movl 12(%esp), %ecx -.salsa_tangle_sse2: - movl 4(%ebx), %eax - movl 20(%ebx), %edx - movl %eax, 20(%ebx) - movl %edx, 4(%ebx) - movl 8(%ebx), %eax - movl 40(%ebx), %edx - movl %eax, 40(%ebx) - movl %edx, 8(%ebx) - movl 12(%ebx), %eax - movl 60(%ebx), %edx - movl %eax, 60(%ebx) - movl %edx, 12(%ebx) - movl 16(%ebx), %eax - movl 48(%ebx), %edx - movl %eax, 48(%ebx) - movl %edx, 16(%ebx) - movl 28(%ebx), %eax - movl 44(%ebx), %edx - movl %eax, 44(%ebx) - movl %edx, 28(%ebx) - movl 36(%ebx), %eax - movl 52(%ebx), %edx - movl %eax, 52(%ebx) - movl %edx, 36(%ebx) - addl $64, %ebx - decl %ecx - jnz .salsa_tangle_sse2 - - popl %ebx - ret - - -/* neoscrypt_xor_salsa_sse2(mem, xormem, double_rounds) - * i386 (SSE2) Salsa20 with XOR; - * mem and xormem must be aligned properly */ -neoscrypt_xor_salsa_sse2: - movl 4(%esp), %edx - movl 8(%esp), %eax - movl 12(%esp), %ecx - movdqa 0(%edx), %xmm0 - movdqa 16(%edx), %xmm1 - movdqa 32(%edx), %xmm2 - movdqa 48(%edx), %xmm3 - pxor 0(%eax), %xmm0 - pxor 16(%eax), %xmm1 - pxor 32(%eax), %xmm2 - pxor 48(%eax), %xmm3 - movdqa %xmm0, %xmm6 - movdqa %xmm1, %xmm7 - movdqa %xmm2, 32(%edx) - movdqa %xmm3, 48(%edx) -.xor_salsa_sse2: - movdqa %xmm1, %xmm4 - paddd %xmm0, %xmm4 - movdqa %xmm4, %xmm5 - pslld $7, %xmm4 - psrld $25, %xmm5 - pxor %xmm4, %xmm3 - movdqa %xmm0, %xmm4 - pxor %xmm5, %xmm3 - paddd %xmm3, %xmm4 - movdqa %xmm4, %xmm5 - pslld $9, %xmm4 - psrld $23, %xmm5 - pxor %xmm4, %xmm2 - movdqa %xmm3, %xmm4 - pxor %xmm5, %xmm2 - pshufd $0x93, %xmm3, %xmm3 - paddd %xmm2, %xmm4 - movdqa %xmm4, %xmm5 - pslld $13, %xmm4 - psrld $19, %xmm5 - pxor %xmm4, %xmm1 - movdqa %xmm2, %xmm4 - pxor %xmm5, %xmm1 - pshufd $0x4E, %xmm2, %xmm2 - paddd %xmm1, %xmm4 - movdqa %xmm4, %xmm5 - pslld $18, %xmm4 - psrld $14, %xmm5 - pxor %xmm4, %xmm0 - movdqa %xmm3, %xmm4 - pxor %xmm5, %xmm0 - pshufd $0x39, %xmm1, %xmm1 - paddd %xmm0, %xmm4 - movdqa %xmm4, %xmm5 - pslld $7, %xmm4 - psrld $25, %xmm5 - pxor %xmm4, %xmm1 - movdqa %xmm0, %xmm4 - pxor %xmm5, %xmm1 - paddd %xmm1, %xmm4 - movdqa %xmm4, %xmm5 - pslld $9, %xmm4 - psrld $23, %xmm5 - pxor %xmm4, %xmm2 - movdqa %xmm1, %xmm4 - pxor %xmm5, %xmm2 - pshufd $0x93, %xmm1, %xmm1 - paddd %xmm2, %xmm4 - movdqa %xmm4, %xmm5 - pslld $13, %xmm4 - psrld $19, %xmm5 - pxor %xmm4, %xmm3 - movdqa %xmm2, %xmm4 - pxor %xmm5, %xmm3 - pshufd $0x4E, %xmm2, %xmm2 - paddd %xmm3, %xmm4 - movdqa %xmm4, %xmm5 - pslld $18, %xmm4 - psrld $14, %xmm5 - pxor %xmm4, %xmm0 - pshufd $0x39, %xmm3, %xmm3 - pxor %xmm5, %xmm0 - decl %ecx - jnz .xor_salsa_sse2 - - paddd %xmm6, %xmm0 - paddd %xmm7, %xmm1 - paddd 32(%edx), %xmm2 - paddd 48(%edx), %xmm3 - movdqa %xmm0, 0(%edx) - movdqa %xmm1, 16(%edx) - movdqa %xmm2, 32(%edx) - movdqa %xmm3, 48(%edx) - - ret - - -/* neoscrypt_xor_chacha_sse2(mem, xormem, double_rounds) - * i386 (SSE2) ChaCha20 with XOR; - * mem and xormem must be aligned properly */ -neoscrypt_xor_chacha_sse2: - movl 4(%esp), %edx - movl 8(%esp), %eax - movl 12(%esp), %ecx - movdqa 0(%edx), %xmm0 - movdqa 16(%edx), %xmm1 - movdqa 32(%edx), %xmm2 - movdqa 48(%edx), %xmm3 - pxor 0(%eax), %xmm0 - pxor 16(%eax), %xmm1 - pxor 32(%eax), %xmm2 - pxor 48(%eax), %xmm3 - movdqa %xmm0, %xmm5 - movdqa %xmm1, %xmm6 - movdqa %xmm2, %xmm7 - movdqa %xmm3, 48(%edx) -.xor_chacha_sse2: - paddd %xmm1, %xmm0 - pxor %xmm0, %xmm3 - pshuflw $0xB1, %xmm3, %xmm3 - pshufhw $0xB1, %xmm3, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm4 - pslld $12, %xmm1 - psrld $20, %xmm4 - pxor %xmm4, %xmm1 - paddd %xmm1, %xmm0 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm4 - pslld $8, %xmm3 - psrld $24, %xmm4 - pxor %xmm4, %xmm3 - pshufd $0x93, %xmm0, %xmm0 - paddd %xmm3, %xmm2 - pshufd $0x4E, %xmm3, %xmm3 - pxor %xmm2, %xmm1 - pshufd $0x39, %xmm2, %xmm2 - movdqa %xmm1, %xmm4 - pslld $7, %xmm1 - psrld $25, %xmm4 - pxor %xmm4, %xmm1 - paddd %xmm1, %xmm0 - pxor %xmm0, %xmm3 - pshuflw $0xB1, %xmm3, %xmm3 - pshufhw $0xB1, %xmm3, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm4 - pslld $12, %xmm1 - psrld $20, %xmm4 - pxor %xmm4, %xmm1 - paddd %xmm1, %xmm0 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm4 - pslld $8, %xmm3 - psrld $24, %xmm4 - pxor %xmm4, %xmm3 - pshufd $0x39, %xmm0, %xmm0 - paddd %xmm3, %xmm2 - pshufd $0x4E, %xmm3, %xmm3 - pxor %xmm2, %xmm1 - pshufd $0x93, %xmm2, %xmm2 - movdqa %xmm1, %xmm4 - pslld $7, %xmm1 - psrld $25, %xmm4 - pxor %xmm4, %xmm1 - decl %ecx - jnz .xor_chacha_sse2 - - paddd %xmm5, %xmm0 - paddd %xmm6, %xmm1 - paddd %xmm7, %xmm2 - paddd 48(%edx), %xmm3 - movdqa %xmm0, 0(%edx) - movdqa %xmm1, 16(%edx) - movdqa %xmm2, 32(%edx) - movdqa %xmm3, 48(%edx) - - ret - - -/* neoscrypt(input, output, profile) - * i386 (INT, SSE2) NeoScrypt engine (MMX required for INT); - * supports NeoScrypt and Scrypt only */ -.globl neoscrypt -.globl _neoscrypt -neoscrypt: -_neoscrypt: - pushl %ebx - pushl %ebp - pushl %esi - pushl %edi - movl 20(%esp), %esi - movl 24(%esp), %edi - movl 28(%esp), %ebx - -#ifdef SHA256 -/* Scrypt mode */ - testl $0x01, %ebx - jnz .scrypt -#endif - -#ifdef WIN32 -/* attempt to allocate 33280 + 128 bytes of stack space fails miserably; - * have to use malloc() and free() instead */ - subl $64, %esp -/* allocate memory (9 pages of 4Kb each) */ - movl $0x9000, 0(%esp) - call _malloc -/* save memory address */ - movl %eax, 32(%esp) -/* align memory */ - addl $64, %eax - andl $0xFFFFFFC0, %eax -/* memory base: X, Z, V */ - leal 64(%eax), %ebp -#else -/* align stack */ - movl %esp, %eax - andl $0xFFFFFFC0, %esp - subl $0x8280, %esp -/* save unaligned stack */ - movl %eax, 32(%esp) -/* memory base: X, Z, V */ - leal 128(%esp), %ebp -#endif /* WIN32 */ - -/* FastKDF */ -#ifdef OPT - movl %esi, 0(%esp) - movl %esi, 4(%esp) - movl %ebp, 8(%esp) - xorl %eax, %eax - movl %eax, 12(%esp) -#if defined(WIN32) || defined(__APPLE__) - call _neoscrypt_fastkdf_opt -#else - call neoscrypt_fastkdf_opt -#endif /* (WIN32) || (__APPLE__) */ -#else - movl $80, %eax - movl %esi, 0(%esp) - movl %eax, 4(%esp) - movl %esi, 8(%esp) - movl %eax, 12(%esp) - movl $32, 16(%esp) - movl %ebp, 20(%esp) - movl $256, 24(%esp) -#if defined(WIN32) || defined(__APPLE__) - call _neoscrypt_fastkdf -#else - call neoscrypt_fastkdf -#endif /* (WIN32) || (__APPLE__) */ -#endif /* OPT */ - -/* SSE2 switch */ - testl $0x1000, %ebx - jnz .neoscrypt_sse2 - -/* blkcpy(Z, X) */ - leal 256(%ebp), %eax - movq 0(%ebp), %mm0 - movq 8(%ebp), %mm1 - movq 16(%ebp), %mm2 - movq 24(%ebp), %mm3 - movq 32(%ebp), %mm4 - movq 40(%ebp), %mm5 - movq 48(%ebp), %mm6 - movq 56(%ebp), %mm7 - movq %mm0, 0(%eax) - movq %mm1, 8(%eax) - movq %mm2, 16(%eax) - movq %mm3, 24(%eax) - movq %mm4, 32(%eax) - movq %mm5, 40(%eax) - movq %mm6, 48(%eax) - movq %mm7, 56(%eax) - movq 64(%ebp), %mm0 - movq 72(%ebp), %mm1 - movq 80(%ebp), %mm2 - movq 88(%ebp), %mm3 - movq 96(%ebp), %mm4 - movq 104(%ebp), %mm5 - movq 112(%ebp), %mm6 - movq 120(%ebp), %mm7 - movq %mm0, 64(%eax) - movq %mm1, 72(%eax) - movq %mm2, 80(%eax) - movq %mm3, 88(%eax) - movq %mm4, 96(%eax) - movq %mm5, 104(%eax) - movq %mm6, 112(%eax) - movq %mm7, 120(%eax) - movq 128(%ebp), %mm0 - movq 136(%ebp), %mm1 - movq 144(%ebp), %mm2 - movq 152(%ebp), %mm3 - movq 160(%ebp), %mm4 - movq 168(%ebp), %mm5 - movq 176(%ebp), %mm6 - movq 184(%ebp), %mm7 - movq %mm0, 128(%eax) - movq %mm1, 136(%eax) - movq %mm2, 144(%eax) - movq %mm3, 152(%eax) - movq %mm4, 160(%eax) - movq %mm5, 168(%eax) - movq %mm6, 176(%eax) - movq %mm7, 184(%eax) - movq 192(%ebp), %mm0 - movq 200(%ebp), %mm1 - movq 208(%ebp), %mm2 - movq 216(%ebp), %mm3 - movq 224(%ebp), %mm4 - movq 232(%ebp), %mm5 - movq 240(%ebp), %mm6 - movq 248(%ebp), %mm7 - movq %mm0, 192(%eax) - movq %mm1, 200(%eax) - movq %mm2, 208(%eax) - movq %mm3, 216(%eax) - movq %mm4, 224(%eax) - movq %mm5, 232(%eax) - movq %mm6, 240(%eax) - movq %mm7, 248(%eax) - - leal -64(%ebp), %edx - movl %edx, 8(%esp) - movl $10, 12(%esp) - - xorl %ebx, %ebx -.chacha_ns1: -/* blkcpy(V, Z) */ - leal 512(%ebp), %eax - movl %ebx, %edx - movb $8, %cl - shll %cl, %edx - leal 256(%ebp), %ecx - addl %edx, %eax - movq 0(%ecx), %mm0 - movq 8(%ecx), %mm1 - movq 16(%ecx), %mm2 - movq 24(%ecx), %mm3 - movq 32(%ecx), %mm4 - movq 40(%ecx), %mm5 - movq 48(%ecx), %mm6 - movq 56(%ecx), %mm7 - movq %mm0, 0(%eax) - movq %mm1, 8(%eax) - movq %mm2, 16(%eax) - movq %mm3, 24(%eax) - movq %mm4, 32(%eax) - movq %mm5, 40(%eax) - movq %mm6, 48(%eax) - movq %mm7, 56(%eax) - movq 64(%ecx), %mm0 - movq 72(%ecx), %mm1 - movq 80(%ecx), %mm2 - movq 88(%ecx), %mm3 - movq 96(%ecx), %mm4 - movq 104(%ecx), %mm5 - movq 112(%ecx), %mm6 - movq 120(%ecx), %mm7 - movq %mm0, 64(%eax) - movq %mm1, 72(%eax) - movq %mm2, 80(%eax) - movq %mm3, 88(%eax) - movq %mm4, 96(%eax) - movq %mm5, 104(%eax) - movq %mm6, 112(%eax) - movq %mm7, 120(%eax) - movq 128(%ecx), %mm0 - movq 136(%ecx), %mm1 - movq 144(%ecx), %mm2 - movq 152(%ecx), %mm3 - movq 160(%ecx), %mm4 - movq 168(%ecx), %mm5 - movq 176(%ecx), %mm6 - movq 184(%ecx), %mm7 - movq %mm0, 128(%eax) - movq %mm1, 136(%eax) - movq %mm2, 144(%eax) - movq %mm3, 152(%eax) - movq %mm4, 160(%eax) - movq %mm5, 168(%eax) - movq %mm6, 176(%eax) - movq %mm7, 184(%eax) - movq 192(%ecx), %mm0 - movq 200(%ecx), %mm1 - movq 208(%ecx), %mm2 - movq 216(%ecx), %mm3 - movq 224(%ecx), %mm4 - movq 232(%ecx), %mm5 - movq 240(%ecx), %mm6 - movq 248(%ecx), %mm7 - movq %mm0, 192(%eax) - movq %mm1, 200(%eax) - movq %mm2, 208(%eax) - movq %mm3, 216(%eax) - movq %mm4, 224(%eax) - movq %mm5, 232(%eax) - movq %mm6, 240(%eax) - movq %mm7, 248(%eax) -/* blkmix(Z) */ - leal 256(%ebp), %eax - movl %eax, 0(%esp) - leal 448(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_chacha - leal 320(%ebp), %eax - movl %eax, 0(%esp) - leal 256(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_chacha - leal 384(%ebp), %eax - movl %eax, 0(%esp) - leal 320(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_chacha - leal 448(%ebp), %eax - movl %eax, 0(%esp) - leal 384(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_chacha - leal 320(%ebp), %eax - leal 384(%ebp), %edx - movq 0(%eax), %mm0 - movq 8(%eax), %mm1 - movq 16(%eax), %mm2 - movq 24(%eax), %mm3 - movq 0(%edx), %mm4 - movq 8(%edx), %mm5 - movq 16(%edx), %mm6 - movq 24(%edx), %mm7 - movq %mm0, 0(%edx) - movq %mm1, 8(%edx) - movq %mm2, 16(%edx) - movq %mm3, 24(%edx) - movq %mm4, 0(%eax) - movq %mm5, 8(%eax) - movq %mm6, 16(%eax) - movq %mm7, 24(%eax) - movq 32(%eax), %mm0 - movq 40(%eax), %mm1 - movq 48(%eax), %mm2 - movq 56(%eax), %mm3 - movq 32(%edx), %mm4 - movq 40(%edx), %mm5 - movq 48(%edx), %mm6 - movq 56(%edx), %mm7 - movq %mm0, 32(%edx) - movq %mm1, 40(%edx) - movq %mm2, 48(%edx) - movq %mm3, 56(%edx) - movq %mm4, 32(%eax) - movq %mm5, 40(%eax) - movq %mm6, 48(%eax) - movq %mm7, 56(%eax) - incl %ebx - cmpl $128, %ebx - jnz .chacha_ns1 - - xorl %ebx, %ebx -.chacha_ns2: -/* integerify(Z) mod 128 */ - leal 256(%ebp), %eax - leal 512(%ebp), %ecx - movl 448(%ebp), %edx - andl $0x7F, %edx - shll $8, %edx - addl %edx, %ecx -/* blkxor(Z, V) */ - movq 0(%eax), %mm0 - movq 8(%eax), %mm1 - movq 16(%eax), %mm2 - movq 24(%eax), %mm3 - movq 32(%eax), %mm4 - movq 40(%eax), %mm5 - movq 48(%eax), %mm6 - movq 56(%eax), %mm7 - pxor 0(%ecx), %mm0 - pxor 8(%ecx), %mm1 - pxor 16(%ecx), %mm2 - pxor 24(%ecx), %mm3 - pxor 32(%ecx), %mm4 - pxor 40(%ecx), %mm5 - pxor 48(%ecx), %mm6 - pxor 56(%ecx), %mm7 - movq %mm0, 0(%eax) - movq %mm1, 8(%eax) - movq %mm2, 16(%eax) - movq %mm3, 24(%eax) - movq %mm4, 32(%eax) - movq %mm5, 40(%eax) - movq %mm6, 48(%eax) - movq %mm7, 56(%eax) - movq 64(%eax), %mm0 - movq 72(%eax), %mm1 - movq 80(%eax), %mm2 - movq 88(%eax), %mm3 - movq 96(%eax), %mm4 - movq 104(%eax), %mm5 - movq 112(%eax), %mm6 - movq 120(%eax), %mm7 - pxor 64(%ecx), %mm0 - pxor 72(%ecx), %mm1 - pxor 80(%ecx), %mm2 - pxor 88(%ecx), %mm3 - pxor 96(%ecx), %mm4 - pxor 104(%ecx), %mm5 - pxor 112(%ecx), %mm6 - pxor 120(%ecx), %mm7 - movq %mm0, 64(%eax) - movq %mm1, 72(%eax) - movq %mm2, 80(%eax) - movq %mm3, 88(%eax) - movq %mm4, 96(%eax) - movq %mm5, 104(%eax) - movq %mm6, 112(%eax) - movq %mm7, 120(%eax) - movq 128(%eax), %mm0 - movq 136(%eax), %mm1 - movq 144(%eax), %mm2 - movq 152(%eax), %mm3 - movq 160(%eax), %mm4 - movq 168(%eax), %mm5 - movq 176(%eax), %mm6 - movq 184(%eax), %mm7 - pxor 128(%ecx), %mm0 - pxor 136(%ecx), %mm1 - pxor 144(%ecx), %mm2 - pxor 152(%ecx), %mm3 - pxor 160(%ecx), %mm4 - pxor 168(%ecx), %mm5 - pxor 176(%ecx), %mm6 - pxor 184(%ecx), %mm7 - movq %mm0, 128(%eax) - movq %mm1, 136(%eax) - movq %mm2, 144(%eax) - movq %mm3, 152(%eax) - movq %mm4, 160(%eax) - movq %mm5, 168(%eax) - movq %mm6, 176(%eax) - movq %mm7, 184(%eax) - movq 192(%eax), %mm0 - movq 200(%eax), %mm1 - movq 208(%eax), %mm2 - movq 216(%eax), %mm3 - movq 224(%eax), %mm4 - movq 232(%eax), %mm5 - movq 240(%eax), %mm6 - movq 248(%eax), %mm7 - pxor 192(%ecx), %mm0 - pxor 200(%ecx), %mm1 - pxor 208(%ecx), %mm2 - pxor 216(%ecx), %mm3 - pxor 224(%ecx), %mm4 - pxor 232(%ecx), %mm5 - pxor 240(%ecx), %mm6 - pxor 248(%ecx), %mm7 - movq %mm0, 192(%eax) - movq %mm1, 200(%eax) - movq %mm2, 208(%eax) - movq %mm3, 216(%eax) - movq %mm4, 224(%eax) - movq %mm5, 232(%eax) - movq %mm6, 240(%eax) - movq %mm7, 248(%eax) -/* blkmix(Z) */ - leal 256(%ebp), %eax - movl %eax, 0(%esp) - leal 448(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_chacha - leal 320(%ebp), %eax - movl %eax, 0(%esp) - leal 256(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_chacha - leal 384(%ebp), %eax - movl %eax, 0(%esp) - leal 320(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_chacha - leal 448(%ebp), %eax - movl %eax, 0(%esp) - leal 384(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_chacha - leal 320(%ebp), %eax - leal 384(%ebp), %edx - movq 0(%eax), %mm0 - movq 8(%eax), %mm1 - movq 16(%eax), %mm2 - movq 24(%eax), %mm3 - movq 0(%edx), %mm4 - movq 8(%edx), %mm5 - movq 16(%edx), %mm6 - movq 24(%edx), %mm7 - movq %mm0, 0(%edx) - movq %mm1, 8(%edx) - movq %mm2, 16(%edx) - movq %mm3, 24(%edx) - movq %mm4, 0(%eax) - movq %mm5, 8(%eax) - movq %mm6, 16(%eax) - movq %mm7, 24(%eax) - movq 32(%eax), %mm0 - movq 40(%eax), %mm1 - movq 48(%eax), %mm2 - movq 56(%eax), %mm3 - movq 32(%edx), %mm4 - movq 40(%edx), %mm5 - movq 48(%edx), %mm6 - movq 56(%edx), %mm7 - movq %mm0, 32(%edx) - movq %mm1, 40(%edx) - movq %mm2, 48(%edx) - movq %mm3, 56(%edx) - movq %mm4, 32(%eax) - movq %mm5, 40(%eax) - movq %mm6, 48(%eax) - movq %mm7, 56(%eax) - incl %ebx - cmpl $128, %ebx - jnz .chacha_ns2 - - xorl %ebx, %ebx -.salsa_ns1: -/* blkcpy(V, X) */ - leal 512(%ebp), %eax - movl %ebx, %edx - movl $8, %ecx - shll %cl, %edx - addl %edx, %eax - movq 0(%ebp), %mm0 - movq 8(%ebp), %mm1 - movq 16(%ebp), %mm2 - movq 24(%ebp), %mm3 - movq 32(%ebp), %mm4 - movq 40(%ebp), %mm5 - movq 48(%ebp), %mm6 - movq 56(%ebp), %mm7 - movq %mm0, 0(%eax) - movq %mm1, 8(%eax) - movq %mm2, 16(%eax) - movq %mm3, 24(%eax) - movq %mm4, 32(%eax) - movq %mm5, 40(%eax) - movq %mm6, 48(%eax) - movq %mm7, 56(%eax) - movq 64(%ebp), %mm0 - movq 72(%ebp), %mm1 - movq 80(%ebp), %mm2 - movq 88(%ebp), %mm3 - movq 96(%ebp), %mm4 - movq 104(%ebp), %mm5 - movq 112(%ebp), %mm6 - movq 120(%ebp), %mm7 - movq %mm0, 64(%eax) - movq %mm1, 72(%eax) - movq %mm2, 80(%eax) - movq %mm3, 88(%eax) - movq %mm4, 96(%eax) - movq %mm5, 104(%eax) - movq %mm6, 112(%eax) - movq %mm7, 120(%eax) - movq 128(%ebp), %mm0 - movq 136(%ebp), %mm1 - movq 144(%ebp), %mm2 - movq 152(%ebp), %mm3 - movq 160(%ebp), %mm4 - movq 168(%ebp), %mm5 - movq 176(%ebp), %mm6 - movq 184(%ebp), %mm7 - movq %mm0, 128(%eax) - movq %mm1, 136(%eax) - movq %mm2, 144(%eax) - movq %mm3, 152(%eax) - movq %mm4, 160(%eax) - movq %mm5, 168(%eax) - movq %mm6, 176(%eax) - movq %mm7, 184(%eax) - movq 192(%ebp), %mm0 - movq 200(%ebp), %mm1 - movq 208(%ebp), %mm2 - movq 216(%ebp), %mm3 - movq 224(%ebp), %mm4 - movq 232(%ebp), %mm5 - movq 240(%ebp), %mm6 - movq 248(%ebp), %mm7 - movq %mm0, 192(%eax) - movq %mm1, 200(%eax) - movq %mm2, 208(%eax) - movq %mm3, 216(%eax) - movq %mm4, 224(%eax) - movq %mm5, 232(%eax) - movq %mm6, 240(%eax) - movq %mm7, 248(%eax) -/* blkmix(X) */ - movl %ebp, 0(%esp) - leal 192(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_salsa - leal 64(%ebp), %eax - movl %eax, 0(%esp) - movl %ebp, 4(%esp) - call neoscrypt_xor_salsa - leal 128(%ebp), %eax - movl %eax, 0(%esp) - leal 64(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_salsa - leal 192(%ebp), %eax - movl %eax, 0(%esp) - leal 128(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_salsa - leal 64(%ebp), %eax - leal 128(%ebp), %edx - movq 0(%eax), %mm0 - movq 8(%eax), %mm1 - movq 16(%eax), %mm2 - movq 24(%eax), %mm3 - movq 0(%edx), %mm4 - movq 8(%edx), %mm5 - movq 16(%edx), %mm6 - movq 24(%edx), %mm7 - movq %mm0, 0(%edx) - movq %mm1, 8(%edx) - movq %mm2, 16(%edx) - movq %mm3, 24(%edx) - movq %mm4, 0(%eax) - movq %mm5, 8(%eax) - movq %mm6, 16(%eax) - movq %mm7, 24(%eax) - movq 32(%eax), %mm0 - movq 40(%eax), %mm1 - movq 48(%eax), %mm2 - movq 56(%eax), %mm3 - movq 32(%edx), %mm4 - movq 40(%edx), %mm5 - movq 48(%edx), %mm6 - movq 56(%edx), %mm7 - movq %mm0, 32(%edx) - movq %mm1, 40(%edx) - movq %mm2, 48(%edx) - movq %mm3, 56(%edx) - movq %mm4, 32(%eax) - movq %mm5, 40(%eax) - movq %mm6, 48(%eax) - movq %mm7, 56(%eax) - incl %ebx - cmpl $128, %ebx - jnz .salsa_ns1 - - xorl %ebx, %ebx -.salsa_ns2: -/* integerify(X) mod 128 */ - leal 512(%ebp), %ecx - movl 192(%ebp), %edx - andl $0x7F, %edx - shll $8, %edx - addl %edx, %ecx -/* blkxor(X, V) */ - movq 0(%ebp), %mm0 - movq 8(%ebp), %mm1 - movq 16(%ebp), %mm2 - movq 24(%ebp), %mm3 - movq 32(%ebp), %mm4 - movq 40(%ebp), %mm5 - movq 48(%ebp), %mm6 - movq 56(%ebp), %mm7 - pxor 0(%ecx), %mm0 - pxor 8(%ecx), %mm1 - pxor 16(%ecx), %mm2 - pxor 24(%ecx), %mm3 - pxor 32(%ecx), %mm4 - pxor 40(%ecx), %mm5 - pxor 48(%ecx), %mm6 - pxor 56(%ecx), %mm7 - movq %mm0, 0(%ebp) - movq %mm1, 8(%ebp) - movq %mm2, 16(%ebp) - movq %mm3, 24(%ebp) - movq %mm4, 32(%ebp) - movq %mm5, 40(%ebp) - movq %mm6, 48(%ebp) - movq %mm7, 56(%ebp) - movq 64(%ebp), %mm0 - movq 72(%ebp), %mm1 - movq 80(%ebp), %mm2 - movq 88(%ebp), %mm3 - movq 96(%ebp), %mm4 - movq 104(%ebp), %mm5 - movq 112(%ebp), %mm6 - movq 120(%ebp), %mm7 - pxor 64(%ecx), %mm0 - pxor 72(%ecx), %mm1 - pxor 80(%ecx), %mm2 - pxor 88(%ecx), %mm3 - pxor 96(%ecx), %mm4 - pxor 104(%ecx), %mm5 - pxor 112(%ecx), %mm6 - pxor 120(%ecx), %mm7 - movq %mm0, 64(%ebp) - movq %mm1, 72(%ebp) - movq %mm2, 80(%ebp) - movq %mm3, 88(%ebp) - movq %mm4, 96(%ebp) - movq %mm5, 104(%ebp) - movq %mm6, 112(%ebp) - movq %mm7, 120(%ebp) - movq 128(%ebp), %mm0 - movq 136(%ebp), %mm1 - movq 144(%ebp), %mm2 - movq 152(%ebp), %mm3 - movq 160(%ebp), %mm4 - movq 168(%ebp), %mm5 - movq 176(%ebp), %mm6 - movq 184(%ebp), %mm7 - pxor 128(%ecx), %mm0 - pxor 136(%ecx), %mm1 - pxor 144(%ecx), %mm2 - pxor 152(%ecx), %mm3 - pxor 160(%ecx), %mm4 - pxor 168(%ecx), %mm5 - pxor 176(%ecx), %mm6 - pxor 184(%ecx), %mm7 - movq %mm0, 128(%ebp) - movq %mm1, 136(%ebp) - movq %mm2, 144(%ebp) - movq %mm3, 152(%ebp) - movq %mm4, 160(%ebp) - movq %mm5, 168(%ebp) - movq %mm6, 176(%ebp) - movq %mm7, 184(%ebp) - movq 192(%ebp), %mm0 - movq 200(%ebp), %mm1 - movq 208(%ebp), %mm2 - movq 216(%ebp), %mm3 - movq 224(%ebp), %mm4 - movq 232(%ebp), %mm5 - movq 240(%ebp), %mm6 - movq 248(%ebp), %mm7 - pxor 192(%ecx), %mm0 - pxor 200(%ecx), %mm1 - pxor 208(%ecx), %mm2 - pxor 216(%ecx), %mm3 - pxor 224(%ecx), %mm4 - pxor 232(%ecx), %mm5 - pxor 240(%ecx), %mm6 - pxor 248(%ecx), %mm7 - movq %mm0, 192(%ebp) - movq %mm1, 200(%ebp) - movq %mm2, 208(%ebp) - movq %mm3, 216(%ebp) - movq %mm4, 224(%ebp) - movq %mm5, 232(%ebp) - movq %mm6, 240(%ebp) - movq %mm7, 248(%ebp) -/* blkmix(X) */ - movl %ebp, 0(%esp) - leal 192(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_salsa - leal 64(%ebp), %eax - movl %eax, 0(%esp) - movl %ebp, 4(%esp) - call neoscrypt_xor_salsa - leal 128(%ebp), %eax - movl %eax, 0(%esp) - leal 64(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_salsa - leal 192(%ebp), %eax - movl %eax, 0(%esp) - leal 128(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_salsa - leal 64(%ebp), %eax - leal 128(%ebp), %edx - movq 0(%eax), %mm0 - movq 8(%eax), %mm1 - movq 16(%eax), %mm2 - movq 24(%eax), %mm3 - movq 0(%edx), %mm4 - movq 8(%edx), %mm5 - movq 16(%edx), %mm6 - movq 24(%edx), %mm7 - movq %mm0, 0(%edx) - movq %mm1, 8(%edx) - movq %mm2, 16(%edx) - movq %mm3, 24(%edx) - movq %mm4, 0(%eax) - movq %mm5, 8(%eax) - movq %mm6, 16(%eax) - movq %mm7, 24(%eax) - movq 32(%eax), %mm0 - movq 40(%eax), %mm1 - movq 48(%eax), %mm2 - movq 56(%eax), %mm3 - movq 32(%edx), %mm4 - movq 40(%edx), %mm5 - movq 48(%edx), %mm6 - movq 56(%edx), %mm7 - movq %mm0, 32(%edx) - movq %mm1, 40(%edx) - movq %mm2, 48(%edx) - movq %mm3, 56(%edx) - movq %mm4, 32(%eax) - movq %mm5, 40(%eax) - movq %mm6, 48(%eax) - movq %mm7, 56(%eax) - incl %ebx - cmpl $128, %ebx - jnz .salsa_ns2 - -/* blkxor(X, Z) */ - leal 256(%ebp), %ecx - movq 0(%ebp), %mm0 - movq 8(%ebp), %mm1 - movq 16(%ebp), %mm2 - movq 24(%ebp), %mm3 - movq 32(%ebp), %mm4 - movq 40(%ebp), %mm5 - movq 48(%ebp), %mm6 - movq 56(%ebp), %mm7 - pxor 0(%ecx), %mm0 - pxor 8(%ecx), %mm1 - pxor 16(%ecx), %mm2 - pxor 24(%ecx), %mm3 - pxor 32(%ecx), %mm4 - pxor 40(%ecx), %mm5 - pxor 48(%ecx), %mm6 - pxor 56(%ecx), %mm7 - movq %mm0, 0(%ebp) - movq %mm1, 8(%ebp) - movq %mm2, 16(%ebp) - movq %mm3, 24(%ebp) - movq %mm4, 32(%ebp) - movq %mm5, 40(%ebp) - movq %mm6, 48(%ebp) - movq %mm7, 56(%ebp) - movq 64(%ebp), %mm0 - movq 72(%ebp), %mm1 - movq 80(%ebp), %mm2 - movq 88(%ebp), %mm3 - movq 96(%ebp), %mm4 - movq 104(%ebp), %mm5 - movq 112(%ebp), %mm6 - movq 120(%ebp), %mm7 - pxor 64(%ecx), %mm0 - pxor 72(%ecx), %mm1 - pxor 80(%ecx), %mm2 - pxor 88(%ecx), %mm3 - pxor 96(%ecx), %mm4 - pxor 104(%ecx), %mm5 - pxor 112(%ecx), %mm6 - pxor 120(%ecx), %mm7 - movq %mm0, 64(%ebp) - movq %mm1, 72(%ebp) - movq %mm2, 80(%ebp) - movq %mm3, 88(%ebp) - movq %mm4, 96(%ebp) - movq %mm5, 104(%ebp) - movq %mm6, 112(%ebp) - movq %mm7, 120(%ebp) - movq 128(%ebp), %mm0 - movq 136(%ebp), %mm1 - movq 144(%ebp), %mm2 - movq 152(%ebp), %mm3 - movq 160(%ebp), %mm4 - movq 168(%ebp), %mm5 - movq 176(%ebp), %mm6 - movq 184(%ebp), %mm7 - pxor 128(%ecx), %mm0 - pxor 136(%ecx), %mm1 - pxor 144(%ecx), %mm2 - pxor 152(%ecx), %mm3 - pxor 160(%ecx), %mm4 - pxor 168(%ecx), %mm5 - pxor 176(%ecx), %mm6 - pxor 184(%ecx), %mm7 - movq %mm0, 128(%ebp) - movq %mm1, 136(%ebp) - movq %mm2, 144(%ebp) - movq %mm3, 152(%ebp) - movq %mm4, 160(%ebp) - movq %mm5, 168(%ebp) - movq %mm6, 176(%ebp) - movq %mm7, 184(%ebp) - movq 192(%ebp), %mm0 - movq 200(%ebp), %mm1 - movq 208(%ebp), %mm2 - movq 216(%ebp), %mm3 - movq 224(%ebp), %mm4 - movq 232(%ebp), %mm5 - movq 240(%ebp), %mm6 - movq 248(%ebp), %mm7 - pxor 192(%ecx), %mm0 - pxor 200(%ecx), %mm1 - pxor 208(%ecx), %mm2 - pxor 216(%ecx), %mm3 - pxor 224(%ecx), %mm4 - pxor 232(%ecx), %mm5 - pxor 240(%ecx), %mm6 - pxor 248(%ecx), %mm7 - movq %mm0, 192(%ebp) - movq %mm1, 200(%ebp) - movq %mm2, 208(%ebp) - movq %mm3, 216(%ebp) - movq %mm4, 224(%ebp) - movq %mm5, 232(%ebp) - movq %mm6, 240(%ebp) - movq %mm7, 248(%ebp) - -/* FastKDF */ -#ifdef OPT - movl %esi, 0(%esp) - movl %ebp, 4(%esp) - movl %edi, 8(%esp) - movl $1, 12(%esp) -#if defined(WIN32) || defined(__APPLE__) - call _neoscrypt_fastkdf_opt -#else - call neoscrypt_fastkdf_opt -#endif /* (WIN32) || (__APPLE__) */ -#else - movl %esi, 0(%esp) - movl $80, 4(%esp) - movl %ebp, 8(%esp) - movl $256, 12(%esp) - movl $32, 16(%esp) - movl %edi, 20(%esp) - movl $32, 24(%esp) -#if defined(WIN32) || defined(__APPLE__) - call _neoscrypt_fastkdf -#else - call neoscrypt_fastkdf -#endif /* (WIN32) || (__APPLE__) */ -#endif /* OPT */ - -#ifdef WIN32 -/* free memory */ - movl 32(%esp), %eax - movl %eax, 0(%esp) - call _free -/* restore stack */ - addl $64, %esp -#else -/* restore stack */ - movl 32(%esp), %esp -#endif - popl %edi - popl %esi - popl %ebp - popl %ebx - emms - ret - -.neoscrypt_sse2: -/* blkcpy(Z, X) */ - leal 256(%ebp), %eax - movdqa 0(%ebp), %xmm0 - movdqa 16(%ebp), %xmm1 - movdqa 32(%ebp), %xmm2 - movdqa 48(%ebp), %xmm3 - movdqa 64(%ebp), %xmm4 - movdqa 80(%ebp), %xmm5 - movdqa 96(%ebp), %xmm6 - movdqa 112(%ebp), %xmm7 - movdqa %xmm0, 0(%eax) - movdqa %xmm1, 16(%eax) - movdqa %xmm2, 32(%eax) - movdqa %xmm3, 48(%eax) - movdqa %xmm4, 64(%eax) - movdqa %xmm5, 80(%eax) - movdqa %xmm6, 96(%eax) - movdqa %xmm7, 112(%eax) - movdqa 128(%ebp), %xmm0 - movdqa 144(%ebp), %xmm1 - movdqa 160(%ebp), %xmm2 - movdqa 176(%ebp), %xmm3 - movdqa 192(%ebp), %xmm4 - movdqa 208(%ebp), %xmm5 - movdqa 224(%ebp), %xmm6 - movdqa 240(%ebp), %xmm7 - movdqa %xmm0, 128(%eax) - movdqa %xmm1, 144(%eax) - movdqa %xmm2, 160(%eax) - movdqa %xmm3, 176(%eax) - movdqa %xmm4, 192(%eax) - movdqa %xmm5, 208(%eax) - movdqa %xmm6, 224(%eax) - movdqa %xmm7, 240(%eax) - - movl $10, 8(%esp) - - xorl %ebx, %ebx -.chacha_ns1_sse2: -/* blkcpy(V, Z) */ - leal 512(%ebp), %eax - movl %ebx, %edx - movb $8, %cl - shll %cl, %edx - leal 256(%ebp), %ecx - addl %edx, %eax - movdqa 0(%ecx), %xmm0 - movdqa 16(%ecx), %xmm1 - movdqa 32(%ecx), %xmm2 - movdqa 48(%ecx), %xmm3 - movdqa 64(%ecx), %xmm4 - movdqa 80(%ecx), %xmm5 - movdqa 96(%ecx), %xmm6 - movdqa 112(%ecx), %xmm7 - movdqa %xmm0, 0(%eax) - movdqa %xmm1, 16(%eax) - movdqa %xmm2, 32(%eax) - movdqa %xmm3, 48(%eax) - movdqa %xmm4, 64(%eax) - movdqa %xmm5, 80(%eax) - movdqa %xmm6, 96(%eax) - movdqa %xmm7, 112(%eax) - movdqa 128(%ecx), %xmm0 - movdqa 144(%ecx), %xmm1 - movdqa 160(%ecx), %xmm2 - movdqa 176(%ecx), %xmm3 - movdqa 192(%ecx), %xmm4 - movdqa 208(%ecx), %xmm5 - movdqa 224(%ecx), %xmm6 - movdqa 240(%ecx), %xmm7 - movdqa %xmm0, 128(%eax) - movdqa %xmm1, 144(%eax) - movdqa %xmm2, 160(%eax) - movdqa %xmm3, 176(%eax) - movdqa %xmm4, 192(%eax) - movdqa %xmm5, 208(%eax) - movdqa %xmm6, 224(%eax) - movdqa %xmm7, 240(%eax) -/* blkmix(Z) */ - leal 256(%ebp), %eax - movl %eax, 0(%esp) - leal 448(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_chacha_sse2 - leal 320(%ebp), %eax - movl %eax, 0(%esp) - leal 256(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_chacha_sse2 - leal 384(%ebp), %eax - movl %eax, 0(%esp) - leal 320(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_chacha_sse2 - leal 448(%ebp), %eax - movl %eax, 0(%esp) - leal 384(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_chacha_sse2 - leal 320(%ebp), %eax - leal 384(%ebp), %edx - movdqa 0(%eax), %xmm0 - movdqa 16(%eax), %xmm1 - movdqa 32(%eax), %xmm2 - movdqa 48(%eax), %xmm3 - movdqa 0(%edx), %xmm4 - movdqa 16(%edx), %xmm5 - movdqa 32(%edx), %xmm6 - movdqa 48(%edx), %xmm7 - movdqa %xmm0, 0(%edx) - movdqa %xmm1, 16(%edx) - movdqa %xmm2, 32(%edx) - movdqa %xmm3, 48(%edx) - movdqa %xmm4, 0(%eax) - movdqa %xmm5, 16(%eax) - movdqa %xmm6, 32(%eax) - movdqa %xmm7, 48(%eax) - incl %ebx - cmpl $128, %ebx - jnz .chacha_ns1_sse2 - - xorl %ebx, %ebx -.chacha_ns2_sse2: -/* integerify(Z) mod 128 */ - leal 256(%ebp), %eax - leal 512(%ebp), %ecx - movl 448(%ebp), %edx - andl $0x7F, %edx - shll $8, %edx - addl %edx, %ecx -/* blkxor(Z, V) */ - movdqa 0(%eax), %xmm0 - movdqa 16(%eax), %xmm1 - movdqa 32(%eax), %xmm2 - movdqa 48(%eax), %xmm3 - movdqa 64(%eax), %xmm4 - movdqa 80(%eax), %xmm5 - movdqa 96(%eax), %xmm6 - movdqa 112(%eax), %xmm7 - pxor 0(%ecx), %xmm0 - pxor 16(%ecx), %xmm1 - pxor 32(%ecx), %xmm2 - pxor 48(%ecx), %xmm3 - pxor 64(%ecx), %xmm4 - pxor 80(%ecx), %xmm5 - pxor 96(%ecx), %xmm6 - pxor 112(%ecx), %xmm7 - movdqa %xmm0, 0(%eax) - movdqa %xmm1, 16(%eax) - movdqa %xmm2, 32(%eax) - movdqa %xmm3, 48(%eax) - movdqa %xmm4, 64(%eax) - movdqa %xmm5, 80(%eax) - movdqa %xmm6, 96(%eax) - movdqa %xmm7, 112(%eax) - movdqa 128(%eax), %xmm0 - movdqa 144(%eax), %xmm1 - movdqa 160(%eax), %xmm2 - movdqa 176(%eax), %xmm3 - movdqa 192(%eax), %xmm4 - movdqa 208(%eax), %xmm5 - movdqa 224(%eax), %xmm6 - movdqa 240(%eax), %xmm7 - pxor 128(%ecx), %xmm0 - pxor 144(%ecx), %xmm1 - pxor 160(%ecx), %xmm2 - pxor 176(%ecx), %xmm3 - pxor 192(%ecx), %xmm4 - pxor 208(%ecx), %xmm5 - pxor 224(%ecx), %xmm6 - pxor 240(%ecx), %xmm7 - movdqa %xmm0, 128(%eax) - movdqa %xmm1, 144(%eax) - movdqa %xmm2, 160(%eax) - movdqa %xmm3, 176(%eax) - movdqa %xmm4, 192(%eax) - movdqa %xmm5, 208(%eax) - movdqa %xmm6, 224(%eax) - movdqa %xmm7, 240(%eax) -/* blkmix(Z) */ - leal 256(%ebp), %eax - movl %eax, 0(%esp) - leal 448(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_chacha_sse2 - leal 320(%ebp), %eax - movl %eax, 0(%esp) - leal 256(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_chacha_sse2 - leal 384(%ebp), %eax - movl %eax, 0(%esp) - leal 320(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_chacha_sse2 - leal 448(%ebp), %eax - movl %eax, 0(%esp) - leal 384(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_chacha_sse2 - leal 320(%ebp), %eax - leal 384(%ebp), %edx - movdqa 0(%eax), %xmm0 - movdqa 16(%eax), %xmm1 - movdqa 32(%eax), %xmm2 - movdqa 48(%eax), %xmm3 - movdqa 0(%edx), %xmm4 - movdqa 16(%edx), %xmm5 - movdqa 32(%edx), %xmm6 - movdqa 48(%edx), %xmm7 - movdqa %xmm0, 0(%edx) - movdqa %xmm1, 16(%edx) - movdqa %xmm2, 32(%edx) - movdqa %xmm3, 48(%edx) - movdqa %xmm4, 0(%eax) - movdqa %xmm5, 16(%eax) - movdqa %xmm6, 32(%eax) - movdqa %xmm7, 48(%eax) - incl %ebx - cmpl $128, %ebx - jnz .chacha_ns2_sse2 - - movl %ebp, 0(%esp) - movl $4, 4(%esp) - call neoscrypt_salsa_tangle_sse2 - - xorl %ebx, %ebx -.salsa_ns1_sse2: -/* blkcpy(V, X) */ - leal 512(%ebp), %eax - movl %ebx, %edx - movl $8, %ecx - shll %cl, %edx - addl %edx, %eax - movdqa 0(%ebp), %xmm0 - movdqa 16(%ebp), %xmm1 - movdqa 32(%ebp), %xmm2 - movdqa 48(%ebp), %xmm3 - movdqa 64(%ebp), %xmm4 - movdqa 80(%ebp), %xmm5 - movdqa 96(%ebp), %xmm6 - movdqa 112(%ebp), %xmm7 - movdqa %xmm0, 0(%eax) - movdqa %xmm1, 16(%eax) - movdqa %xmm2, 32(%eax) - movdqa %xmm3, 48(%eax) - movdqa %xmm4, 64(%eax) - movdqa %xmm5, 80(%eax) - movdqa %xmm6, 96(%eax) - movdqa %xmm7, 112(%eax) - movdqa 128(%ebp), %xmm0 - movdqa 144(%ebp), %xmm1 - movdqa 160(%ebp), %xmm2 - movdqa 176(%ebp), %xmm3 - movdqa 192(%ebp), %xmm4 - movdqa 208(%ebp), %xmm5 - movdqa 224(%ebp), %xmm6 - movdqa 240(%ebp), %xmm7 - movdqa %xmm0, 128(%eax) - movdqa %xmm1, 144(%eax) - movdqa %xmm2, 160(%eax) - movdqa %xmm3, 176(%eax) - movdqa %xmm4, 192(%eax) - movdqa %xmm5, 208(%eax) - movdqa %xmm6, 224(%eax) - movdqa %xmm7, 240(%eax) -/* blkmix(X) */ - movl %ebp, 0(%esp) - leal 192(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_salsa_sse2 - leal 64(%ebp), %eax - movl %eax, 0(%esp) - movl %ebp, 4(%esp) - call neoscrypt_xor_salsa_sse2 - leal 128(%ebp), %eax - movl %eax, 0(%esp) - leal 64(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_salsa_sse2 - leal 192(%ebp), %eax - movl %eax, 0(%esp) - leal 128(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_salsa_sse2 - leal 64(%ebp), %eax - leal 128(%ebp), %edx - movdqa 0(%eax), %xmm0 - movdqa 16(%eax), %xmm1 - movdqa 32(%eax), %xmm2 - movdqa 48(%eax), %xmm3 - movdqa 0(%edx), %xmm4 - movdqa 16(%edx), %xmm5 - movdqa 32(%edx), %xmm6 - movdqa 48(%edx), %xmm7 - movdqa %xmm0, 0(%edx) - movdqa %xmm1, 16(%edx) - movdqa %xmm2, 32(%edx) - movdqa %xmm3, 48(%edx) - movdqa %xmm4, 0(%eax) - movdqa %xmm5, 16(%eax) - movdqa %xmm6, 32(%eax) - movdqa %xmm7, 48(%eax) - incl %ebx - cmpl $128, %ebx - jnz .salsa_ns1_sse2 - - xorl %ebx, %ebx -.salsa_ns2_sse2: -/* integerify(X) mod 128 */ - leal 512(%ebp), %ecx - movl 192(%ebp), %edx - andl $0x7F, %edx - shll $8, %edx - addl %edx, %ecx -/* blkxor(X, V) */ - movdqa 0(%ebp), %xmm0 - movdqa 16(%ebp), %xmm1 - movdqa 32(%ebp), %xmm2 - movdqa 48(%ebp), %xmm3 - movdqa 64(%ebp), %xmm4 - movdqa 80(%ebp), %xmm5 - movdqa 96(%ebp), %xmm6 - movdqa 112(%ebp), %xmm7 - pxor 0(%ecx), %xmm0 - pxor 16(%ecx), %xmm1 - pxor 32(%ecx), %xmm2 - pxor 48(%ecx), %xmm3 - pxor 64(%ecx), %xmm4 - pxor 80(%ecx), %xmm5 - pxor 96(%ecx), %xmm6 - pxor 112(%ecx), %xmm7 - movdqa %xmm0, 0(%ebp) - movdqa %xmm1, 16(%ebp) - movdqa %xmm2, 32(%ebp) - movdqa %xmm3, 48(%ebp) - movdqa %xmm4, 64(%ebp) - movdqa %xmm5, 80(%ebp) - movdqa %xmm6, 96(%ebp) - movdqa %xmm7, 112(%ebp) - movdqa 128(%ebp), %xmm0 - movdqa 144(%ebp), %xmm1 - movdqa 160(%ebp), %xmm2 - movdqa 176(%ebp), %xmm3 - movdqa 192(%ebp), %xmm4 - movdqa 208(%ebp), %xmm5 - movdqa 224(%ebp), %xmm6 - movdqa 240(%ebp), %xmm7 - pxor 128(%ecx), %xmm0 - pxor 144(%ecx), %xmm1 - pxor 160(%ecx), %xmm2 - pxor 176(%ecx), %xmm3 - pxor 192(%ecx), %xmm4 - pxor 208(%ecx), %xmm5 - pxor 224(%ecx), %xmm6 - pxor 240(%ecx), %xmm7 - movdqa %xmm0, 128(%ebp) - movdqa %xmm1, 144(%ebp) - movdqa %xmm2, 160(%ebp) - movdqa %xmm3, 176(%ebp) - movdqa %xmm4, 192(%ebp) - movdqa %xmm5, 208(%ebp) - movdqa %xmm6, 224(%ebp) - movdqa %xmm7, 240(%ebp) -/* blkmix(X) */ - movl %ebp, 0(%esp) - leal 192(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_salsa_sse2 - leal 64(%ebp), %eax - movl %eax, 0(%esp) - movl %ebp, 4(%esp) - call neoscrypt_xor_salsa_sse2 - leal 128(%ebp), %eax - movl %eax, 0(%esp) - leal 64(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_salsa_sse2 - leal 192(%ebp), %eax - movl %eax, 0(%esp) - leal 128(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_salsa_sse2 - leal 64(%ebp), %eax - leal 128(%ebp), %edx - movdqa 0(%eax), %xmm0 - movdqa 16(%eax), %xmm1 - movdqa 32(%eax), %xmm2 - movdqa 48(%eax), %xmm3 - movdqa 0(%edx), %xmm4 - movdqa 16(%edx), %xmm5 - movdqa 32(%edx), %xmm6 - movdqa 48(%edx), %xmm7 - movdqa %xmm0, 0(%edx) - movdqa %xmm1, 16(%edx) - movdqa %xmm2, 32(%edx) - movdqa %xmm3, 48(%edx) - movdqa %xmm4, 0(%eax) - movdqa %xmm5, 16(%eax) - movdqa %xmm6, 32(%eax) - movdqa %xmm7, 48(%eax) - incl %ebx - cmpl $128, %ebx - jnz .salsa_ns2_sse2 - - movl %ebp, 0(%esp) - movl $4, 4(%esp) - call neoscrypt_salsa_tangle_sse2 - -/* blkxor(X, Z) */ - leal 256(%ebp), %ecx - movdqa 0(%ebp), %xmm0 - movdqa 16(%ebp), %xmm1 - movdqa 32(%ebp), %xmm2 - movdqa 48(%ebp), %xmm3 - movdqa 64(%ebp), %xmm4 - movdqa 80(%ebp), %xmm5 - movdqa 96(%ebp), %xmm6 - movdqa 112(%ebp), %xmm7 - pxor 0(%ecx), %xmm0 - pxor 16(%ecx), %xmm1 - pxor 32(%ecx), %xmm2 - pxor 48(%ecx), %xmm3 - pxor 64(%ecx), %xmm4 - pxor 80(%ecx), %xmm5 - pxor 96(%ecx), %xmm6 - pxor 112(%ecx), %xmm7 - movdqa %xmm0, 0(%ebp) - movdqa %xmm1, 16(%ebp) - movdqa %xmm2, 32(%ebp) - movdqa %xmm3, 48(%ebp) - movdqa %xmm4, 64(%ebp) - movdqa %xmm5, 80(%ebp) - movdqa %xmm6, 96(%ebp) - movdqa %xmm7, 112(%ebp) - movdqa 128(%ebp), %xmm0 - movdqa 144(%ebp), %xmm1 - movdqa 160(%ebp), %xmm2 - movdqa 176(%ebp), %xmm3 - movdqa 192(%ebp), %xmm4 - movdqa 208(%ebp), %xmm5 - movdqa 224(%ebp), %xmm6 - movdqa 240(%ebp), %xmm7 - pxor 128(%ecx), %xmm0 - pxor 144(%ecx), %xmm1 - pxor 160(%ecx), %xmm2 - pxor 176(%ecx), %xmm3 - pxor 192(%ecx), %xmm4 - pxor 208(%ecx), %xmm5 - pxor 224(%ecx), %xmm6 - pxor 240(%ecx), %xmm7 - movdqa %xmm0, 128(%ebp) - movdqa %xmm1, 144(%ebp) - movdqa %xmm2, 160(%ebp) - movdqa %xmm3, 176(%ebp) - movdqa %xmm4, 192(%ebp) - movdqa %xmm5, 208(%ebp) - movdqa %xmm6, 224(%ebp) - movdqa %xmm7, 240(%ebp) - -/* FastKDF */ -#ifdef OPT - movl %esi, 0(%esp) - movl %ebp, 4(%esp) - movl %edi, 8(%esp) - movl $1, 12(%esp) -#if defined(WIN32) || defined(__APPLE__) - call _neoscrypt_fastkdf_opt -#else - call neoscrypt_fastkdf_opt -#endif /* (WIN32) || (__APPLE__) */ -#else - movl %esi, 0(%esp) - movl $80, 4(%esp) - movl %ebp, 8(%esp) - movl $256, 12(%esp) - movl $32, 16(%esp) - movl %edi, 20(%esp) - movl $32, 24(%esp) -#if defined(WIN32) || defined(__APPLE__) - call _neoscrypt_fastkdf -#else - call neoscrypt_fastkdf -#endif /* (WIN32) || (__APPLE__) */ -#endif /* OPT */ - -#ifdef WIN32 -/* free memory */ - movl 32(%esp), %eax - movl %eax, 0(%esp) - call _free -/* restore stack */ - addl $64, %esp -#else -/* restore stack */ - movl 32(%esp), %esp -#endif - popl %edi - popl %esi - popl %ebp - popl %ebx - ret - -#ifdef SHA256 - -.scrypt: -#ifdef WIN32 -/* attempt to allocate 131200 + 128 bytes of stack space fails miserably; - * have to use malloc() and free() instead */ - subl $64, %esp -/* allocate memory (33 pages of 4Kb each) */ - movl $0x21000, 0(%esp) - call _malloc -/* save memory address */ - movl %eax, 32(%esp) -/* align memory */ - addl $64, %eax - andl $0xFFFFFFC0, %eax -/* memory base: X, Z, V */ - leal 64(%eax), %ebp -#else -/* align stack */ - movl %esp, %eax - andl $0xFFFFFFC0, %esp - subl $0x20100, %esp -/* save unaligned stack */ - movl %eax, 32(%esp) -/* memory base: X, Z, V */ - leal 128(%esp), %ebp -#endif /* WIN32 */ - -/* PBKDF2-HMAC-SHA256 */ - movl $80, %eax - movl %esi, 0(%esp) - movl %eax, 4(%esp) - movl %esi, 8(%esp) - movl %eax, 12(%esp) - movl $1, 16(%esp) - movl %ebp, 20(%esp) - movl $128, 24(%esp) -#if defined(WIN32) || defined(__APPLE__) - call _neoscrypt_pbkdf2_sha256 -#else - call neoscrypt_pbkdf2_sha256 -#endif - -/* SSE2 switch */ - testl $0x1000, %ebx - jnz .scrypt_sse2 - - leal -64(%ebp), %edx - movl %edx, 8(%esp) - movl $4, 12(%esp) - - xorl %ebx, %ebx -.salsa_s1: -/* blkcpy(V, X) */ - leal 128(%ebp), %eax - movl %ebx, %edx - movl $7, %ecx - shll %cl, %edx - addl %edx, %eax - movq 0(%ebp), %mm0 - movq 8(%ebp), %mm1 - movq 16(%ebp), %mm2 - movq 24(%ebp), %mm3 - movq 32(%ebp), %mm4 - movq 40(%ebp), %mm5 - movq 48(%ebp), %mm6 - movq 56(%ebp), %mm7 - movq %mm0, 0(%eax) - movq %mm1, 8(%eax) - movq %mm2, 16(%eax) - movq %mm3, 24(%eax) - movq %mm4, 32(%eax) - movq %mm5, 40(%eax) - movq %mm6, 48(%eax) - movq %mm7, 56(%eax) - movq 64(%ebp), %mm0 - movq 72(%ebp), %mm1 - movq 80(%ebp), %mm2 - movq 88(%ebp), %mm3 - movq 96(%ebp), %mm4 - movq 104(%ebp), %mm5 - movq 112(%ebp), %mm6 - movq 120(%ebp), %mm7 - movq %mm0, 64(%eax) - movq %mm1, 72(%eax) - movq %mm2, 80(%eax) - movq %mm3, 88(%eax) - movq %mm4, 96(%eax) - movq %mm5, 104(%eax) - movq %mm6, 112(%eax) - movq %mm7, 120(%eax) -/* blkmix(X) */ - movl %ebp, 0(%esp) - leal 64(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_salsa - leal 64(%ebp), %eax - movl %eax, 0(%esp) - movl %ebp, 4(%esp) - call neoscrypt_xor_salsa - incl %ebx - cmpl $1024, %ebx - jnz .salsa_s1 - - xorl %ebx, %ebx -.salsa_s2: -/* integerify(X) mod 1024 */ - leal 128(%ebp), %eax - movl 64(%ebp), %edx - andl $0x03FF, %edx - shll $7, %edx - addl %edx, %eax -/* blkxor(X, V) */ - movq 0(%ebp), %mm0 - movq 8(%ebp), %mm1 - movq 16(%ebp), %mm2 - movq 24(%ebp), %mm3 - movq 32(%ebp), %mm4 - movq 40(%ebp), %mm5 - movq 48(%ebp), %mm6 - movq 56(%ebp), %mm7 - pxor 0(%eax), %mm0 - pxor 8(%eax), %mm1 - pxor 16(%eax), %mm2 - pxor 24(%eax), %mm3 - pxor 32(%eax), %mm4 - pxor 40(%eax), %mm5 - pxor 48(%eax), %mm6 - pxor 56(%eax), %mm7 - movq %mm0, 0(%ebp) - movq %mm1, 8(%ebp) - movq %mm2, 16(%ebp) - movq %mm3, 24(%ebp) - movq %mm4, 32(%ebp) - movq %mm5, 40(%ebp) - movq %mm6, 48(%ebp) - movq %mm7, 56(%ebp) - movq 64(%ebp), %mm0 - movq 72(%ebp), %mm1 - movq 80(%ebp), %mm2 - movq 88(%ebp), %mm3 - movq 96(%ebp), %mm4 - movq 104(%ebp), %mm5 - movq 112(%ebp), %mm6 - movq 120(%ebp), %mm7 - pxor 64(%eax), %mm0 - pxor 72(%eax), %mm1 - pxor 80(%eax), %mm2 - pxor 88(%eax), %mm3 - pxor 96(%eax), %mm4 - pxor 104(%eax), %mm5 - pxor 112(%eax), %mm6 - pxor 120(%eax), %mm7 - movq %mm0, 64(%ebp) - movq %mm1, 72(%ebp) - movq %mm2, 80(%ebp) - movq %mm3, 88(%ebp) - movq %mm4, 96(%ebp) - movq %mm5, 104(%ebp) - movq %mm6, 112(%ebp) - movq %mm7, 120(%ebp) -/* blkmix(X) */ - movl %ebp, 0(%esp) - leal 64(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_salsa - leal 64(%ebp), %eax - movl %eax, 0(%esp) - movl %ebp, 4(%esp) - call neoscrypt_xor_salsa - incl %ebx - cmpl $1024, %ebx - jnz .salsa_s2 - -/* PBKDF2-HMAC-SHA256 */ - movl %esi, 0(%esp) - movl $80, 4(%esp) - movl %ebp, 8(%esp) - movl $128, 12(%esp) - movl $1, 16(%esp) - movl %edi, 20(%esp) - movl $32, 24(%esp) -#if defined(WIN32) || defined(__APPLE__) - call _neoscrypt_pbkdf2_sha256 -#else - call neoscrypt_pbkdf2_sha256 -#endif - -#ifdef WIN32 -/* free memory */ - movl 32(%esp), %eax - movl %eax, 0(%esp) - call _free -/* restore stack */ - addl $64, %esp -#else -/* restore stack */ - movl 32(%esp), %esp -#endif - popl %edi - popl %esi - popl %ebp - popl %ebx - emms - ret - -.scrypt_sse2: - movl %ebp, 0(%esp) - movl $2, 4(%esp) - call neoscrypt_salsa_tangle_sse2 - - movl $4, 8(%esp) - - xorl %ebx, %ebx -.salsa_s1_sse2: -/* blkcpy(V, X) */ - leal 128(%ebp), %eax - movl %ebx, %edx - movl $7, %ecx - shll %cl, %edx - addl %edx, %eax - movdqa 0(%ebp), %xmm0 - movdqa 16(%ebp), %xmm1 - movdqa 32(%ebp), %xmm2 - movdqa 48(%ebp), %xmm3 - movdqa 64(%ebp), %xmm4 - movdqa 80(%ebp), %xmm5 - movdqa 96(%ebp), %xmm6 - movdqa 112(%ebp), %xmm7 - movdqa %xmm0, 0(%eax) - movdqa %xmm1, 16(%eax) - movdqa %xmm2, 32(%eax) - movdqa %xmm3, 48(%eax) - movdqa %xmm4, 64(%eax) - movdqa %xmm5, 80(%eax) - movdqa %xmm6, 96(%eax) - movdqa %xmm7, 112(%eax) -/* blkmix(X) */ - movl %ebp, 0(%esp) - leal 64(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_salsa_sse2 - leal 64(%ebp), %eax - movl %eax, 0(%esp) - movl %ebp, 4(%esp) - call neoscrypt_xor_salsa_sse2 - incl %ebx - cmpl $1024, %ebx - jnz .salsa_s1_sse2 - - xorl %ebx, %ebx -.salsa_s2_sse2: -/* integerify(X) mod 1024 */ - leal 128(%ebp), %eax - movl 64(%ebp), %edx - andl $0x03FF, %edx - shll $7, %edx - addl %edx, %eax -/* blkxor(X, V) */ - movdqa 0(%ebp), %xmm0 - movdqa 16(%ebp), %xmm1 - movdqa 32(%ebp), %xmm2 - movdqa 48(%ebp), %xmm3 - movdqa 64(%ebp), %xmm4 - movdqa 80(%ebp), %xmm5 - movdqa 96(%ebp), %xmm6 - movdqa 112(%ebp), %xmm7 - pxor 0(%eax), %xmm0 - pxor 16(%eax), %xmm1 - pxor 32(%eax), %xmm2 - pxor 48(%eax), %xmm3 - pxor 64(%eax), %xmm4 - pxor 80(%eax), %xmm5 - pxor 96(%eax), %xmm6 - pxor 112(%eax), %xmm7 - movdqa %xmm0, 0(%ebp) - movdqa %xmm1, 16(%ebp) - movdqa %xmm2, 32(%ebp) - movdqa %xmm3, 48(%ebp) - movdqa %xmm4, 64(%ebp) - movdqa %xmm5, 80(%ebp) - movdqa %xmm6, 96(%ebp) - movdqa %xmm7, 112(%ebp) -/* blkmix(X) */ - movl %ebp, 0(%esp) - leal 64(%ebp), %edx - movl %edx, 4(%esp) - call neoscrypt_xor_salsa_sse2 - leal 64(%ebp), %eax - movl %eax, 0(%esp) - movl %ebp, 4(%esp) - call neoscrypt_xor_salsa_sse2 - incl %ebx - cmpl $1024, %ebx - jnz .salsa_s2_sse2 - - movl %ebp, 0(%esp) - movl $2, 4(%esp) - call neoscrypt_salsa_tangle_sse2 - -/* PBKDF2-HMAC-SHA256 */ - movl %esi, 0(%esp) - movl $80, 4(%esp) - movl %ebp, 8(%esp) - movl $128, 12(%esp) - movl $1, 16(%esp) - movl %edi, 20(%esp) - movl $32, 24(%esp) -#if defined(WIN32) || defined(__APPLE__) - call _neoscrypt_pbkdf2_sha256 -#else - call neoscrypt_pbkdf2_sha256 -#endif - -#ifdef WIN32 -/* free memory */ - movl 32(%esp), %eax - movl %eax, 0(%esp) - call _free -/* restore stack */ - addl $64, %esp -#else -/* restore stack */ - movl 32(%esp), %esp -#endif - popl %edi - popl %esi - popl %ebp - popl %ebx - ret - -#endif /* SHA256 */ - -#ifdef MINER_4WAY - -/* blake2s_compress_4way(mem) - * i386 (SSE2) BLAKE2s 4-way block compression */ -.globl blake2s_compress_4way -.globl _blake2s_compress_4way -blake2s_compress_4way: -_blake2s_compress_4way: - pushl %ebx - pushl %ebp - pushl %esi - pushl %edi - movl 20(%esp), %edi - -/* initialise */ - leal 448(%edi), %esi - movdqa 0(%edi), %xmm0 - movdqa 16(%edi), %xmm2 - movdqa 32(%edi), %xmm3 - movdqa 48(%edi), %xmm7 - movdqa 64(%edi), %xmm6 - movdqa 80(%edi), %xmm1 - movdqa 96(%edi), %xmm4 - movdqa 112(%edi), %xmm5 -/* movdqa %xmm0, 0(%esi) */ - movdqa %xmm2, 16(%esi) - movdqa %xmm3, 32(%esi) - movdqa %xmm7, 48(%esi) -/* movdqa %xmm6, 64(%esi) */ -/* movdqa %xmm1, 80(%esi) */ -/* movdqa %xmm4, 96(%esi) */ -/* movdqa %xmm5, 112(%esi) */ - movl $0x6A09E667, %eax - movl $0xBB67AE85, %ebx - movl $0x3C6EF372, %ecx - movl $0xA54FF53A, %edx - movl %eax, 128(%esi) - movl %eax, 132(%esi) - movl %eax, 136(%esi) - movl %eax, 140(%esi) - movl %ebx, 144(%esi) - movl %ebx, 148(%esi) - movl %ebx, 152(%esi) - movl %ebx, 156(%esi) - movl %ecx, 160(%esi) - movl %ecx, 164(%esi) - movl %ecx, 168(%esi) - movl %ecx, 172(%esi) - movl %edx, 176(%esi) - movl %edx, 180(%esi) - movl %edx, 184(%esi) - movl %edx, 188(%esi) - movl $0x510E527F, %ebp - movl 128(%edi), %eax - movl 132(%edi), %ebx - movl 136(%edi), %ecx - movl 140(%edi), %edx - xorl %ebp, %eax - xorl %ebp, %ebx - xorl %ebp, %ecx - xorl %ebp, %edx - movl %eax, 192(%esi) - movl %ebx, 196(%esi) - movl %ecx, 200(%esi) - movl %edx, 204(%esi) - movl $0x9B05688C, %ebp - movl 144(%edi), %eax - movl 148(%edi), %ebx - movl 152(%edi), %ecx - movl 156(%edi), %edx - xorl %ebp, %eax - xorl %ebp, %ebx - xorl %ebp, %ecx - xorl %ebp, %edx - movl %eax, 208(%esi) - movl %ebx, 212(%esi) - movl %ecx, 216(%esi) - movl %edx, 220(%esi) - movl $0x1F83D9AB, %ebp - movl 160(%edi), %eax - movl 164(%edi), %ebx - movl 168(%edi), %ecx - movl 172(%edi), %edx - xorl %ebp, %eax - xorl %ebp, %ebx - xorl %ebp, %ecx - xorl %ebp, %edx - movl %eax, 224(%esi) - movl %ebx, 228(%esi) - movl %ecx, 232(%esi) - movl %edx, 236(%esi) - movl $0x5BE0CD19, %ebp - movl 176(%edi), %eax - movl 180(%edi), %ebx - movl 184(%edi), %ecx - movl 188(%edi), %edx - xorl %ebp, %eax - xorl %ebp, %ebx -/* movdqa 0(%esi), %xmm0 */ /* A */ - xorl %ebp, %ecx - xorl %ebp, %edx - paddd 192(%edi), %xmm0 /* A */ - movl %eax, 240(%esi) - movl %ebx, 244(%esi) - movl %ecx, 248(%esi) - movl %edx, 252(%esi) -/* round 0 (A) */ -/* movdqa 64(%esi), %xmm6 */ - movdqa 192(%esi), %xmm3 - paddd %xmm6, %xmm0 - movdqa 128(%esi), %xmm2 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 208(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm6 - movdqa %xmm6, %xmm7 - pslld $20, %xmm6 - psrld $12, %xmm7 - por %xmm7, %xmm6 - paddd %xmm6, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* B */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm2 - movdqa %xmm2, 128(%esi) - pxor %xmm2, %xmm6 - movdqa %xmm6, %xmm7 - paddd 224(%edi), %xmm0 /* B */ - pslld $25, %xmm6 - psrld $7, %xmm7 - por %xmm7, %xmm6 - movdqa %xmm6, 64(%esi) -/* round 0 (B) */ -/* movdqa 80(%esi), %xmm1 */ - movdqa 208(%esi), %xmm3 - paddd %xmm1, %xmm0 - movdqa 144(%esi), %xmm2 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 240(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 32(%esi), %xmm0 /* C */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 208(%esi) - paddd %xmm3, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 256(%edi), %xmm0 /* C */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 -/* movdqa %xmm1, 80(%esi) */ -/* round 0 (C) */ -/* movdqa 96(%esi), %xmm4 */ - movdqa 224(%esi), %xmm3 - paddd %xmm4, %xmm0 - movdqa 160(%esi), %xmm2 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 272(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 48(%esi), %xmm0 /* D */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 224(%esi) - paddd %xmm3, %xmm2 -/* movdqa %xmm2, 160(%esi) */ - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - paddd 288(%edi), %xmm0 /* D */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 -/* movdqa %xmm4, 96(%esi) */ -/* round 0 (D) */ -/* movdqa 112(%esi), %xmm5 */ - movdqa 240(%esi), %xmm3 - paddd %xmm5, %xmm0 - movdqa 176(%esi), %xmm6 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 304(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 0(%esi), %xmm0 /* E */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 -/* movdqa %xmm3, 240(%esi) */ - paddd %xmm3, %xmm6 -/* movdqa %xmm6, 176(%esi) */ - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 320(%edi), %xmm0 /* E */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 -/* movdqa %xmm5, 112(%esi) */ -/* round 0 (E) */ -/* movdqa 80(%esi), %xmm1 */ -/* movdqa 240(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 160(%esi), %xmm2 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 336(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* F */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 240(%esi) - paddd %xmm3, %xmm2 - movdqa %xmm2, 160(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 352(%edi), %xmm0 /* F */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 80(%esi) -/* round 0 (F) */ -/* movdqa 96(%esi), %xmm4 */ - movdqa 192(%esi), %xmm3 - paddd %xmm4, %xmm0 -/* movdqa 176(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - por %xmm7, %xmm3 - paddd 368(%edi), %xmm0 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 32(%esi), %xmm0 /* G */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 -/* movdqa %xmm3, 192(%esi) */ - paddd %xmm3, %xmm6 - movdqa %xmm6, 176(%esi) - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - paddd 384(%edi), %xmm0 /* G */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 96(%esi) -/* round 0 (G) */ -/* movdqa 112(%esi), %xmm5 */ - movdqa 208(%esi), %xmm4 - paddd %xmm5, %xmm0 - movdqa 128(%esi), %xmm6 - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 400(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 48(%esi), %xmm0 /* H */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 -/* movdqa %xmm4, 208(%esi) */ - paddd %xmm4, %xmm6 -/* movdqa %xmm6, 128(%esi) */ - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 416(%edi), %xmm0 /* H */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 112(%esi) -/* round 0 (H) */ - movdqa 64(%esi), %xmm1 - movdqa 224(%esi), %xmm5 - paddd %xmm1, %xmm0 - movdqa 144(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 432(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 0(%esi), %xmm0 /* A */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 -/* movdqa %xmm5, 224(%esi) */ - paddd %xmm5, %xmm2 -/* movdqa %xmm2, 144(%esi) */ - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 416(%edi), %xmm0 /* A */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 -/* movdqa %xmm1, 64(%esi) */ -/* round 1 (A) */ -/* movdqa 64(%esi), %xmm1 */ -/* movdqa 192(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 128(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 352(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* B */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - paddd 256(%edi), %xmm0 /* B */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 1 (B) */ - movdqa 80(%esi), %xmm1 -/* movdqa 208(%esi), %xmm4 */ - paddd %xmm1, %xmm0 -/* movdqa 144(%esi), %xmm2 */ - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 320(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 32(%esi), %xmm0 /* C */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 336(%edi), %xmm0 /* C */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 -/* movdqa %xmm1, 80(%esi) */ -/* round 1 (C) */ - movdqa 96(%esi), %xmm4 -/* movdqa 224(%esi), %xmm5 */ - paddd %xmm4, %xmm0 - movdqa 160(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 432(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 48(%esi), %xmm0 /* D */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 -/* movdqa %xmm2, 160(%esi) */ - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - paddd 400(%edi), %xmm0 /* D */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 -/* movdqa %xmm4, 96(%esi) */ -/* round 1 (D) */ - movdqa 112(%esi), %xmm5 - movdqa 240(%esi), %xmm3 - paddd %xmm5, %xmm0 - movdqa 176(%esi), %xmm6 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 288(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 0(%esi), %xmm0 /* E */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 -/* movdqa %xmm3, 240(%esi) */ - paddd %xmm3, %xmm6 -/* movdqa %xmm6, 176(%esi) */ - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 208(%edi), %xmm0 /* E */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 -/* movdqa %xmm5, 112(%esi) */ -/* round 1 (E) */ -/* movdqa 80(%esi), %xmm1 */ -/* movdqa 240(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 160(%esi), %xmm2 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 384(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* F */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 240(%esi) - paddd %xmm3, %xmm2 - movdqa %xmm2, 160(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 192(%edi), %xmm0 /* F */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 80(%esi) -/* round 1 (F) */ -/* movdqa 96(%esi), %xmm4 */ - movdqa 192(%esi), %xmm3 - paddd %xmm4, %xmm0 -/* movdqa 176(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - por %xmm7, %xmm3 - paddd 224(%edi), %xmm0 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 32(%esi), %xmm0 /* G */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 176(%esi) - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - paddd 368(%edi), %xmm0 /* G */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 96(%esi) -/* round 1 (G) */ -/* movdqa 112(%esi), %xmm5 */ - movdqa 208(%esi), %xmm4 - paddd %xmm5, %xmm0 - movdqa 128(%esi), %xmm6 - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 304(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 48(%esi), %xmm0 /* H */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 272(%edi), %xmm0 /* H */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 112(%esi) -/* round 1 (H) */ - movdqa 64(%esi), %xmm1 - movdqa 224(%esi), %xmm5 - paddd %xmm1, %xmm0 - movdqa 144(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 240(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 0(%esi), %xmm0 /* A */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 368(%edi), %xmm0 /* A */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 2 (A) */ -/* movdqa 64(%esi), %xmm1 */ -/* movdqa 192(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 128(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 320(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* B */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - paddd 384(%edi), %xmm0 /* B */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 2 (B) */ - movdqa 80(%esi), %xmm1 -/* movdqa 208(%esi), %xmm4 */ - paddd %xmm1, %xmm0 -/* movdqa 144(%esi), %xmm2 */ - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 192(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 32(%esi), %xmm0 /* C */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 272(%edi), %xmm0 /* C */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 -/* movdqa %xmm1, 80(%esi) */ -/* round 2 (C) */ - movdqa 96(%esi), %xmm4 -/* movdqa 224(%esi), %xmm5 */ - paddd %xmm4, %xmm0 - movdqa 160(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 224(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 48(%esi), %xmm0 /* D */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 -/* movdqa %xmm2, 160(%esi) */ - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - paddd 432(%edi), %xmm0 /* D */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 -/* movdqa %xmm4, 96(%esi) */ -/* round 2 (D) */ - movdqa 112(%esi), %xmm5 - movdqa 240(%esi), %xmm3 - paddd %xmm5, %xmm0 - movdqa 176(%esi), %xmm6 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 400(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 0(%esi), %xmm0 /* E */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 -/* movdqa %xmm3, 240(%esi) */ - paddd %xmm3, %xmm6 -/* movdqa %xmm6, 176(%esi) */ - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 352(%edi), %xmm0 /* E */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 -/* movdqa %xmm5, 112(%esi) */ -/* round 2 (E) */ -/* movdqa 80(%esi), %xmm1 */ -/* movdqa 240(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 160(%esi), %xmm2 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 416(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* F */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 240(%esi) - paddd %xmm3, %xmm2 - movdqa %xmm2, 160(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 240(%edi), %xmm0 /* F */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 80(%esi) -/* round 2 (F) */ -/* movdqa 96(%esi), %xmm4 */ - movdqa 192(%esi), %xmm3 - paddd %xmm4, %xmm0 -/* movdqa 176(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - por %xmm7, %xmm3 - paddd 288(%edi), %xmm0 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 32(%esi), %xmm0 /* G */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 176(%esi) - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - paddd 304(%edi), %xmm0 /* G */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 96(%esi) -/* round 2 (G) */ -/* movdqa 112(%esi), %xmm5 */ - movdqa 208(%esi), %xmm4 - paddd %xmm5, %xmm0 - movdqa 128(%esi), %xmm6 - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 208(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 48(%esi), %xmm0 /* H */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 336(%edi), %xmm0 /* H */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 112(%esi) -/* round 2 (H) */ - movdqa 64(%esi), %xmm1 - movdqa 224(%esi), %xmm5 - paddd %xmm1, %xmm0 - movdqa 144(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 256(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 0(%esi), %xmm0 /* A */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 304(%edi), %xmm0 /* A */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 3 (A) */ -/* movdqa 64(%esi), %xmm1 */ -/* movdqa 192(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 128(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 336(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* B */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - paddd 240(%edi), %xmm0 /* B */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 3 (B) */ - movdqa 80(%esi), %xmm1 -/* movdqa 208(%esi), %xmm4 */ - paddd %xmm1, %xmm0 -/* movdqa 144(%esi), %xmm2 */ - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 208(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 32(%esi), %xmm0 /* C */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 400(%edi), %xmm0 /* C */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 -/* movdqa %xmm1, 80(%esi) */ -/* round 3 (C) */ - movdqa 96(%esi), %xmm4 -/* movdqa 224(%esi), %xmm5 */ - paddd %xmm4, %xmm0 - movdqa 160(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 384(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 48(%esi), %xmm0 /* D */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 -/* movdqa %xmm2, 160(%esi) */ - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - paddd 368(%edi), %xmm0 /* D */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 -/* movdqa %xmm4, 96(%esi) */ -/* round 3 (D) */ - movdqa 112(%esi), %xmm5 - movdqa 240(%esi), %xmm3 - paddd %xmm5, %xmm0 - movdqa 176(%esi), %xmm6 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 416(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 0(%esi), %xmm0 /* E */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 -/* movdqa %xmm3, 240(%esi) */ - paddd %xmm3, %xmm6 -/* movdqa %xmm6, 176(%esi) */ - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 224(%edi), %xmm0 /* E */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 -/* movdqa %xmm5, 112(%esi) */ -/* round 3 (E) */ -/* movdqa 80(%esi), %xmm1 */ -/* movdqa 240(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 160(%esi), %xmm2 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 288(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* F */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 240(%esi) - paddd %xmm3, %xmm2 - movdqa %xmm2, 160(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 272(%edi), %xmm0 /* F */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 80(%esi) -/* round 3 (F) */ -/* movdqa 96(%esi), %xmm4 */ - movdqa 192(%esi), %xmm3 - paddd %xmm4, %xmm0 -/* movdqa 176(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - por %xmm7, %xmm3 - paddd 352(%edi), %xmm0 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 32(%esi), %xmm0 /* G */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 176(%esi) - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - paddd 256(%edi), %xmm0 /* G */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 96(%esi) -/* round 3 (G) */ -/* movdqa 112(%esi), %xmm5 */ - movdqa 208(%esi), %xmm4 - paddd %xmm5, %xmm0 - movdqa 128(%esi), %xmm6 - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 192(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 48(%esi), %xmm0 /* H */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 432(%edi), %xmm0 /* H */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 112(%esi) -/* round 3 (H) */ - movdqa 64(%esi), %xmm1 - movdqa 224(%esi), %xmm5 - paddd %xmm1, %xmm0 - movdqa 144(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 320(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 0(%esi), %xmm0 /* A */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 336(%edi), %xmm0 /* A */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 4 (A) */ -/* movdqa 64(%esi), %xmm1 */ -/* movdqa 192(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 128(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 192(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* B */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - paddd 272(%edi), %xmm0 /* B */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 4 (B) */ - movdqa 80(%esi), %xmm1 -/* movdqa 208(%esi), %xmm4 */ - paddd %xmm1, %xmm0 -/* movdqa 144(%esi), %xmm2 */ - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 304(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 32(%esi), %xmm0 /* C */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 224(%edi), %xmm0 /* C */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 -/* movdqa %xmm1, 80(%esi) */ -/* round 4 (C) */ - movdqa 96(%esi), %xmm4 -/* movdqa 224(%esi), %xmm5 */ - paddd %xmm4, %xmm0 - movdqa 160(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 256(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 48(%esi), %xmm0 /* D */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 -/* movdqa %xmm2, 160(%esi) */ - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - paddd 352(%edi), %xmm0 /* D */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 -/* movdqa %xmm4, 96(%esi) */ -/* round 4 (D) */ - movdqa 112(%esi), %xmm5 - movdqa 240(%esi), %xmm3 - paddd %xmm5, %xmm0 - movdqa 176(%esi), %xmm6 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 432(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 0(%esi), %xmm0 /* E */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 -/* movdqa %xmm3, 240(%esi) */ - paddd %xmm3, %xmm6 -/* movdqa %xmm6, 176(%esi) */ - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 416(%edi), %xmm0 /* E */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 -/* movdqa %xmm5, 112(%esi) */ -/* round 4 (E) */ -/* movdqa 80(%esi), %xmm1 */ -/* movdqa 240(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 160(%esi), %xmm2 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 208(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* F */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 240(%esi) - paddd %xmm3, %xmm2 - movdqa %xmm2, 160(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 368(%edi), %xmm0 /* F */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 80(%esi) -/* round 4 (F) */ -/* movdqa 96(%esi), %xmm4 */ - movdqa 192(%esi), %xmm3 - paddd %xmm4, %xmm0 -/* movdqa 176(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - por %xmm7, %xmm3 - paddd 384(%edi), %xmm0 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 32(%esi), %xmm0 /* G */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 176(%esi) - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - paddd 288(%edi), %xmm0 /* G */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 96(%esi) -/* round 4 (G) */ -/* movdqa 112(%esi), %xmm5 */ - movdqa 208(%esi), %xmm4 - paddd %xmm5, %xmm0 - movdqa 128(%esi), %xmm6 - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 320(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 48(%esi), %xmm0 /* H */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 240(%edi), %xmm0 /* H */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 112(%esi) -/* round 4 (H) */ - movdqa 64(%esi), %xmm1 - movdqa 224(%esi), %xmm5 - paddd %xmm1, %xmm0 - movdqa 144(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 400(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 0(%esi), %xmm0 /* A */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 224(%edi), %xmm0 /* A */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 5 (A) */ -/* movdqa 64(%esi), %xmm1 */ -/* movdqa 192(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 128(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 384(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* B */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - paddd 288(%edi), %xmm0 /* B */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 5 (B) */ - movdqa 80(%esi), %xmm1 -/* movdqa 208(%esi), %xmm4 */ - paddd %xmm1, %xmm0 -/* movdqa 144(%esi), %xmm2 */ - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 352(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 32(%esi), %xmm0 /* C */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 192(%edi), %xmm0 /* C */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 -/* movdqa %xmm1, 80(%esi) */ -/* round 5 (C) */ - movdqa 96(%esi), %xmm4 -/* movdqa 224(%esi), %xmm5 */ - paddd %xmm4, %xmm0 - movdqa 160(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 368(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 48(%esi), %xmm0 /* D */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 -/* movdqa %xmm2, 160(%esi) */ - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - paddd 320(%edi), %xmm0 /* D */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 -/* movdqa %xmm4, 96(%esi) */ -/* round 5 (D) */ - movdqa 112(%esi), %xmm5 - movdqa 240(%esi), %xmm3 - paddd %xmm5, %xmm0 - movdqa 176(%esi), %xmm6 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 240(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 0(%esi), %xmm0 /* E */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 -/* movdqa %xmm3, 240(%esi) */ - paddd %xmm3, %xmm6 -/* movdqa %xmm6, 176(%esi) */ - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 256(%edi), %xmm0 /* E */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 -/* movdqa %xmm5, 112(%esi) */ -/* round 5 (E) */ -/* movdqa 80(%esi), %xmm1 */ -/* movdqa 240(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 160(%esi), %xmm2 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 400(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* F */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 240(%esi) - paddd %xmm3, %xmm2 - movdqa %xmm2, 160(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 304(%edi), %xmm0 /* F */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 80(%esi) -/* round 5 (F) */ -/* movdqa 96(%esi), %xmm4 */ - movdqa 192(%esi), %xmm3 - paddd %xmm4, %xmm0 -/* movdqa 176(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - por %xmm7, %xmm3 - paddd 272(%edi), %xmm0 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 32(%esi), %xmm0 /* G */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 176(%esi) - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - paddd 432(%edi), %xmm0 /* G */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 96(%esi) -/* round 5 (G) */ -/* movdqa 112(%esi), %xmm5 */ - movdqa 208(%esi), %xmm4 - paddd %xmm5, %xmm0 - movdqa 128(%esi), %xmm6 - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 416(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 48(%esi), %xmm0 /* H */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 208(%edi), %xmm0 /* H */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 112(%esi) -/* round 5 (H) */ - movdqa 64(%esi), %xmm1 - movdqa 224(%esi), %xmm5 - paddd %xmm1, %xmm0 - movdqa 144(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 336(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 0(%esi), %xmm0 /* A */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 384(%edi), %xmm0 /* A */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 6 (A) */ -/* movdqa 64(%esi), %xmm1 */ -/* movdqa 192(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 128(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 272(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* B */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - paddd 208(%edi), %xmm0 /* B */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 6 (B) */ - movdqa 80(%esi), %xmm1 -/* movdqa 208(%esi), %xmm4 */ - paddd %xmm1, %xmm0 -/* movdqa 144(%esi), %xmm2 */ - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 432(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 32(%esi), %xmm0 /* C */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 416(%edi), %xmm0 /* C */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 -/* movdqa %xmm1, 80(%esi) */ -/* round 6 (C) */ - movdqa 96(%esi), %xmm4 -/* movdqa 224(%esi), %xmm5 */ - paddd %xmm4, %xmm0 - movdqa 160(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 400(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 48(%esi), %xmm0 /* D */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 -/* movdqa %xmm2, 160(%esi) */ - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - paddd 256(%edi), %xmm0 /* D */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 -/* movdqa %xmm4, 96(%esi) */ -/* round 6 (D) */ - movdqa 112(%esi), %xmm5 - movdqa 240(%esi), %xmm3 - paddd %xmm5, %xmm0 - movdqa 176(%esi), %xmm6 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 352(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 0(%esi), %xmm0 /* E */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 -/* movdqa %xmm3, 240(%esi) */ - paddd %xmm3, %xmm6 -/* movdqa %xmm6, 176(%esi) */ - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 192(%edi), %xmm0 /* E */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 -/* movdqa %xmm5, 112(%esi) */ -/* round 6 (E) */ -/* movdqa 80(%esi), %xmm1 */ -/* movdqa 240(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 160(%esi), %xmm2 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 304(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* F */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 240(%esi) - paddd %xmm3, %xmm2 - movdqa %xmm2, 160(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 288(%edi), %xmm0 /* F */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 80(%esi) -/* round 6 (F) */ -/* movdqa 96(%esi), %xmm4 */ - movdqa 192(%esi), %xmm3 - paddd %xmm4, %xmm0 -/* movdqa 176(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - por %xmm7, %xmm3 - paddd 240(%edi), %xmm0 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 32(%esi), %xmm0 /* G */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 176(%esi) - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - paddd 336(%edi), %xmm0 /* G */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 96(%esi) -/* round 6 (G) */ -/* movdqa 112(%esi), %xmm5 */ - movdqa 208(%esi), %xmm4 - paddd %xmm5, %xmm0 - movdqa 128(%esi), %xmm6 - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 224(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 48(%esi), %xmm0 /* H */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 320(%edi), %xmm0 /* H */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 112(%esi) -/* round 6 (H) */ - movdqa 64(%esi), %xmm1 - movdqa 224(%esi), %xmm5 - paddd %xmm1, %xmm0 - movdqa 144(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 368(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 0(%esi), %xmm0 /* A */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 400(%edi), %xmm0 /* A */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 7 (A) */ -/* movdqa 64(%esi), %xmm1 */ -/* movdqa 192(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 128(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 368(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* B */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - paddd 304(%edi), %xmm0 /* B */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 7 (B) */ - movdqa 80(%esi), %xmm1 -/* movdqa 208(%esi), %xmm4 */ - paddd %xmm1, %xmm0 -/* movdqa 144(%esi), %xmm2 */ - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 416(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 32(%esi), %xmm0 /* C */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 384(%edi), %xmm0 /* C */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 -/* movdqa %xmm1, 80(%esi) */ -/* round 7 (C) */ - movdqa 96(%esi), %xmm4 -/* movdqa 224(%esi), %xmm5 */ - paddd %xmm4, %xmm0 - movdqa 160(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 208(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 48(%esi), %xmm0 /* D */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 -/* movdqa %xmm2, 160(%esi) */ - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - paddd 240(%edi), %xmm0 /* D */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 -/* movdqa %xmm4, 96(%esi) */ -/* round 7 (D) */ - movdqa 112(%esi), %xmm5 - movdqa 240(%esi), %xmm3 - paddd %xmm5, %xmm0 - movdqa 176(%esi), %xmm6 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 336(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 0(%esi), %xmm0 /* E */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 -/* movdqa %xmm3, 240(%esi) */ - paddd %xmm3, %xmm6 -/* movdqa %xmm6, 176(%esi) */ - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 272(%edi), %xmm0 /* E */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 -/* movdqa %xmm5, 112(%esi) */ -/* round 7 (E) */ -/* movdqa 80(%esi), %xmm1 */ -/* movdqa 240(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 160(%esi), %xmm2 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 192(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* F */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 240(%esi) - paddd %xmm3, %xmm2 - movdqa %xmm2, 160(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 432(%edi), %xmm0 /* F */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 80(%esi) -/* round 7 (F) */ -/* movdqa 96(%esi), %xmm4 */ - movdqa 192(%esi), %xmm3 - paddd %xmm4, %xmm0 -/* movdqa 176(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - por %xmm7, %xmm3 - paddd 256(%edi), %xmm0 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 32(%esi), %xmm0 /* G */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 176(%esi) - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - paddd 320(%edi), %xmm0 /* G */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 96(%esi) -/* round 7 (G) */ -/* movdqa 112(%esi), %xmm5 */ - movdqa 208(%esi), %xmm4 - paddd %xmm5, %xmm0 - movdqa 128(%esi), %xmm6 - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 288(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 48(%esi), %xmm0 /* H */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 224(%edi), %xmm0 /* H */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 112(%esi) -/* round 7 (H) */ - movdqa 64(%esi), %xmm1 - movdqa 224(%esi), %xmm5 - paddd %xmm1, %xmm0 - movdqa 144(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 352(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 0(%esi), %xmm0 /* A */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 288(%edi), %xmm0 /* A */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 8 (A) */ -/* movdqa 64(%esi), %xmm1 */ -/* movdqa 192(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 128(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 432(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* B */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - paddd 416(%edi), %xmm0 /* B */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 8 (B) */ - movdqa 80(%esi), %xmm1 -/* movdqa 208(%esi), %xmm4 */ - paddd %xmm1, %xmm0 -/* movdqa 144(%esi), %xmm2 */ - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 336(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 32(%esi), %xmm0 /* C */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 368(%edi), %xmm0 /* C */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 -/* movdqa %xmm1, 80(%esi) */ -/* round 8 (C) */ - movdqa 96(%esi), %xmm4 -/* movdqa 224(%esi), %xmm5 */ - paddd %xmm4, %xmm0 - movdqa 160(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 240(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 48(%esi), %xmm0 /* D */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 -/* movdqa %xmm2, 160(%esi) */ - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - paddd 192(%edi), %xmm0 /* D */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 -/* movdqa %xmm4, 96(%esi) */ -/* round 8 (D) */ - movdqa 112(%esi), %xmm5 - movdqa 240(%esi), %xmm3 - paddd %xmm5, %xmm0 - movdqa 176(%esi), %xmm6 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 320(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 0(%esi), %xmm0 /* E */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 -/* movdqa %xmm3, 240(%esi) */ - paddd %xmm3, %xmm6 -/* movdqa %xmm6, 176(%esi) */ - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 384(%edi), %xmm0 /* E */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 -/* movdqa %xmm5, 112(%esi) */ -/* round 8 (E) */ -/* movdqa 80(%esi), %xmm1 */ -/* movdqa 240(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 160(%esi), %xmm2 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 224(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* F */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 240(%esi) - paddd %xmm3, %xmm2 - movdqa %xmm2, 160(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 400(%edi), %xmm0 /* F */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 80(%esi) -/* round 8 (F) */ -/* movdqa 96(%esi), %xmm4 */ - movdqa 192(%esi), %xmm3 - paddd %xmm4, %xmm0 -/* movdqa 176(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - por %xmm7, %xmm3 - paddd 304(%edi), %xmm0 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 32(%esi), %xmm0 /* G */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 176(%esi) - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - paddd 208(%edi), %xmm0 /* G */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 96(%esi) -/* round 8 (G) */ -/* movdqa 112(%esi), %xmm5 */ - movdqa 208(%esi), %xmm4 - paddd %xmm5, %xmm0 - movdqa 128(%esi), %xmm6 - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 256(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 48(%esi), %xmm0 /* H */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 352(%edi), %xmm0 /* H */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 112(%esi) -/* round 8 (H) */ - movdqa 64(%esi), %xmm1 - movdqa 224(%esi), %xmm5 - paddd %xmm1, %xmm0 - movdqa 144(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 272(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 0(%esi), %xmm0 /* A */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 352(%edi), %xmm0 /* A */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 9 (A) */ -/* movdqa 64(%esi), %xmm1 */ -/* movdqa 192(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 128(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 224(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* B */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 192(%esi) - paddd %xmm3, %xmm6 - movdqa %xmm6, 128(%esi) - pxor %xmm6, %xmm1 - movdqa %xmm1, %xmm7 - paddd 320(%edi), %xmm0 /* B */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 64(%esi) -/* round 9 (B) */ - movdqa 80(%esi), %xmm1 -/* movdqa 208(%esi), %xmm4 */ - paddd %xmm1, %xmm0 -/* movdqa 144(%esi), %xmm2 */ - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 256(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 32(%esi), %xmm0 /* C */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 208(%esi) - paddd %xmm4, %xmm2 - movdqa %xmm2, 144(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 304(%edi), %xmm0 /* C */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 -/* movdqa %xmm1, 80(%esi) */ -/* round 9 (C) */ - movdqa 96(%esi), %xmm4 -/* movdqa 224(%esi), %xmm5 */ - paddd %xmm4, %xmm0 - movdqa 160(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 288(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - movdqa 48(%esi), %xmm0 /* D */ - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 224(%esi) - paddd %xmm5, %xmm2 -/* movdqa %xmm2, 160(%esi) */ - pxor %xmm2, %xmm4 - movdqa %xmm4, %xmm7 - paddd 208(%edi), %xmm0 /* D */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 -/* movdqa %xmm4, 96(%esi) */ -/* round 9 (D) */ - movdqa 112(%esi), %xmm5 - movdqa 240(%esi), %xmm3 - paddd %xmm5, %xmm0 - movdqa 176(%esi), %xmm6 - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 272(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 48(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 0(%esi), %xmm0 /* E */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 -/* movdqa %xmm3, 240(%esi) */ - paddd %xmm3, %xmm6 -/* movdqa %xmm6, 176(%esi) */ - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 432(%edi), %xmm0 /* E */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 -/* movdqa %xmm5, 112(%esi) */ -/* round 9 (E) */ -/* movdqa 80(%esi), %xmm1 */ -/* movdqa 240(%esi), %xmm3 */ - paddd %xmm1, %xmm0 -/* movdqa 160(%esi), %xmm2 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - paddd 368(%edi), %xmm0 - por %xmm7, %xmm3 - paddd %xmm3, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 - movdqa %xmm0, 0(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 16(%esi), %xmm0 /* F */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 - movdqa %xmm3, 240(%esi) - paddd %xmm3, %xmm2 - movdqa %xmm2, 160(%esi) - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - paddd 336(%edi), %xmm0 /* F */ - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 - movdqa %xmm1, 80(%esi) -/* round 9 (F) */ -/* movdqa 96(%esi), %xmm4 */ - movdqa 192(%esi), %xmm3 - paddd %xmm4, %xmm0 -/* movdqa 176(%esi), %xmm6 */ - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - pslld $16, %xmm3 - psrld $16, %xmm7 - por %xmm7, %xmm3 - paddd 416(%edi), %xmm0 - paddd %xmm3, %xmm6 - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - pslld $20, %xmm4 - psrld $12, %xmm7 - por %xmm7, %xmm4 - paddd %xmm4, %xmm0 - movdqa %xmm0, 16(%esi) - pxor %xmm0, %xmm3 - movdqa %xmm3, %xmm7 - movdqa 32(%esi), %xmm0 /* G */ - pslld $24, %xmm3 - psrld $8, %xmm7 - por %xmm7, %xmm3 -/* movdqa %xmm3, 192(%esi) */ - paddd %xmm3, %xmm6 - movdqa %xmm6, 176(%esi) - pxor %xmm6, %xmm4 - movdqa %xmm4, %xmm7 - paddd 240(%edi), %xmm0 /* G */ - pslld $25, %xmm4 - psrld $7, %xmm7 - por %xmm7, %xmm4 - movdqa %xmm4, 96(%esi) -/* round 9 (G) */ -/* movdqa 112(%esi), %xmm5 */ - movdqa 208(%esi), %xmm4 - paddd %xmm5, %xmm0 - movdqa 128(%esi), %xmm6 - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - pslld $16, %xmm4 - psrld $16, %xmm7 - paddd 384(%edi), %xmm0 - por %xmm7, %xmm4 - paddd %xmm4, %xmm6 - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - pslld $20, %xmm5 - psrld $12, %xmm7 - por %xmm7, %xmm5 - paddd %xmm5, %xmm0 - movdqa %xmm0, 32(%esi) - pxor %xmm0, %xmm4 - movdqa %xmm4, %xmm7 - movdqa 48(%esi), %xmm0 /* H */ - pslld $24, %xmm4 - psrld $8, %xmm7 - por %xmm7, %xmm4 -/* movdqa %xmm4, 208(%esi) */ - paddd %xmm4, %xmm6 -/* movdqa %xmm6, 128(%esi) */ - pxor %xmm6, %xmm5 - movdqa %xmm5, %xmm7 - paddd 400(%edi), %xmm0 /* H */ - pslld $25, %xmm5 - psrld $7, %xmm7 - por %xmm7, %xmm5 - movdqa %xmm5, 112(%esi) -/* round 9 (H) */ - movdqa 64(%esi), %xmm1 - movdqa 224(%esi), %xmm5 - paddd %xmm1, %xmm0 - movdqa 144(%esi), %xmm2 - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $16, %xmm5 - psrld $16, %xmm7 - paddd 192(%edi), %xmm0 - por %xmm7, %xmm5 - paddd %xmm5, %xmm2 - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $20, %xmm1 - psrld $12, %xmm7 - por %xmm7, %xmm1 - paddd %xmm1, %xmm0 -/* movdqa %xmm0, 48(%esi) */ - pxor %xmm0, %xmm5 - movdqa %xmm5, %xmm7 - pslld $24, %xmm5 - psrld $8, %xmm7 - por %xmm7, %xmm5 -/* movdqa %xmm5, 224(%esi) */ - paddd %xmm5, %xmm2 -/* movdqa %xmm2, 144(%esi) */ - pxor %xmm2, %xmm1 - movdqa %xmm1, %xmm7 - pslld $25, %xmm1 - psrld $7, %xmm7 - por %xmm7, %xmm1 -/* movdqa %xmm1, 64(%esi) */ -/* finalise */ - movdqa 112(%esi), %xmm7 - pxor %xmm3, %xmm1 /* 64() ^ 192() */ - pxor 176(%esi), %xmm0 /* 48() ^ 176() */ - pxor 16(%esi), %xmm2 /* 16() ^ 144() */ - movdqa 32(%esi), %xmm3 - pxor 80(%esi), %xmm4 /* 80() ^ 208() */ - pxor 96(%esi), %xmm5 /* 96() ^ 224() */ - pxor 0(%esi), %xmm6 /* 0() ^ 128() */ - pxor 240(%esi), %xmm7 /* 112() ^ 240() */ - pxor 160(%esi), %xmm3 /* 32() ^ 160() */ - pxor 0(%edi), %xmm6 - pxor 16(%edi), %xmm2 - pxor 32(%edi), %xmm3 - pxor 48(%edi), %xmm0 - pxor 64(%edi), %xmm1 - pxor 80(%edi), %xmm4 - pxor 96(%edi), %xmm5 - pxor 112(%edi), %xmm7 - movdqa %xmm6, 0(%edi) - movdqa %xmm2, 16(%edi) - movdqa %xmm3, 32(%edi) - movdqa %xmm0, 48(%edi) - movdqa %xmm1, 64(%edi) - movdqa %xmm4, 80(%edi) - movdqa %xmm5, 96(%edi) - movdqa %xmm7, 112(%edi) - - popl %edi - popl %esi - popl %ebp - popl %ebx - ret - - -/* neoscrypt_blkcpy(dst, src, len) - * i386 (SSE2) block memcpy(); - * len must be a multiple of 64 bytes aligned properly */ -.globl neoscrypt_blkcpy -.globl _neoscrypt_blkcpy -neoscrypt_blkcpy: -_neoscrypt_blkcpy: - movl 4(%esp), %eax - movl 8(%esp), %edx - movl 12(%esp), %ecx - shrl $6, %ecx -.blkcpy: - movdqa 0(%edx), %xmm0 - movdqa 16(%edx), %xmm1 - movdqa 32(%edx), %xmm2 - movdqa 48(%edx), %xmm3 - movdqa %xmm0, 0(%eax) - movdqa %xmm1, 16(%eax) - movdqa %xmm2, 32(%eax) - movdqa %xmm3, 48(%eax) - addl $64, %edx - addl $64, %eax - decl %ecx - jnz .blkcpy - - ret - - -/* neoscrypt_blkswp(blkA, blkB, len) - * i386 (SSE2) block swapper; - * len must be a multiple of 64 bytes aligned properly */ -.globl neoscrypt_blkswp -.globl _neoscrypt_blkswp -neoscrypt_blkswp: -_neoscrypt_blkswp: - movl 4(%esp), %eax - movl 8(%esp), %edx - movl 12(%esp), %ecx - shrl $6, %ecx -.blkswp: - movdqa 0(%eax), %xmm0 - movdqa 16(%eax), %xmm1 - movdqa 32(%eax), %xmm2 - movdqa 48(%eax), %xmm3 - movdqa 0(%edx), %xmm4 - movdqa 16(%edx), %xmm5 - movdqa 32(%edx), %xmm6 - movdqa 48(%edx), %xmm7 - movdqa %xmm0, 0(%edx) - movdqa %xmm1, 16(%edx) - movdqa %xmm2, 32(%edx) - movdqa %xmm3, 48(%edx) - movdqa %xmm4, 0(%eax) - movdqa %xmm5, 16(%eax) - movdqa %xmm6, 32(%eax) - movdqa %xmm7, 48(%eax) - addl $64, %eax - addl $64, %edx - decl %ecx - jnz .blkswp - - ret - - -/* neoscrypt_blkxor(dst, src, len) - * i386 (SSE2) block XOR engine; - * len must be a multiple of 64 bytes aligned properly */ -.globl neoscrypt_blkxor -.globl _neoscrypt_blkxor -neoscrypt_blkxor: -_neoscrypt_blkxor: - movl 4(%esp), %eax - movl 8(%esp), %edx - movl 12(%esp), %ecx - shrl $6, %ecx -.blkxor: - movdqa 0(%eax), %xmm0 - movdqa 16(%eax), %xmm1 - movdqa 32(%eax), %xmm2 - movdqa 48(%eax), %xmm3 - pxor 0(%edx), %xmm0 - pxor 16(%edx), %xmm1 - pxor 32(%edx), %xmm2 - pxor 48(%edx), %xmm3 - movdqa %xmm0, 0(%eax) - movdqa %xmm1, 16(%eax) - movdqa %xmm2, 32(%eax) - movdqa %xmm3, 48(%eax) - addl $64, %eax - addl $64, %edx - decl %ecx - jnz .blkxor - - ret - - -/* neoscrypt_pack_4way(dst, src, len) - * i386 4-way data packer */ -.globl neoscrypt_pack_4way -.globl _neoscrypt_pack_4way -neoscrypt_pack_4way: -_neoscrypt_pack_4way: - pushl %ebx - pushl %ebp - pushl %esi - pushl %edi - movl 20(%esp), %edi - movl 24(%esp), %esi - movl 28(%esp), %edx - movl %edx, %ecx - shrl $2, %edx - leal 0(%esi, %edx, 2), %eax - shrl $4, %ecx -.pack_4way: - movl 0(%esi), %ebx - movl 0(%esi, %edx), %ebp - addl $4, %esi - movl %ebx, 0(%edi) - movl %ebp, 4(%edi) - movl 0(%eax), %ebx - movl 0(%eax, %edx), %ebp - addl $4, %eax - movl %ebx, 8(%edi) - movl %ebp, 12(%edi) - addl $16, %edi - decl %ecx - jnz .pack_4way - - popl %edi - popl %esi - popl %ebp - popl %ebx - ret - - -/* neoscrypt_unpack_4way(dst, src, len) - * i386 4-way data unpacker */ -.globl neoscrypt_unpack_4way -.globl _neoscrypt_unpack_4way -neoscrypt_unpack_4way: -_neoscrypt_unpack_4way: - pushl %ebx - pushl %ebp - pushl %esi - pushl %edi - movl 20(%esp), %edi - movl 24(%esp), %esi - movl 28(%esp), %edx - movl %edx, %ecx - shrl $2, %edx - leal 0(%edi, %edx, 2), %eax - shrl $4, %ecx -.unpack_4way: - movl 0(%esi), %ebx - movl 4(%esi), %ebp - movl %ebx, 0(%edi) - movl %ebp, 0(%edi, %edx) - addl $4, %edi - movl 8(%esi), %ebx - movl 12(%esi), %ebp - addl $16, %esi - movl %ebx, 0(%eax) - movl %ebp, 0(%eax, %edx) - addl $4, %eax - decl %ecx - jnz .unpack_4way - - popl %edi - popl %esi - popl %ebp - popl %ebx - ret - - -/* neoscrypt_xor_4way(dst, srcA, srcB, srcC, srcD, len) - * i386 4-way XOR engine */ -.globl neoscrypt_xor_4way -.globl _neoscrypt_xor_4way -neoscrypt_xor_4way: -_neoscrypt_xor_4way: - pushl %ebx - pushl %ebp - pushl %esi - pushl %edi - movl 20(%esp), %edi - movl 24(%esp), %esi - movl 28(%esp), %edx - movl 32(%esp), %ebx - movl 36(%esp), %eax - movl 40(%esp), %ecx - shrl $4, %ecx -.xor_4way: - movl 0(%esi), %ebp - addl $16, %esi - xorl %ebp, 0(%edi) - movl 4(%edx), %ebp - addl $16, %edx - xorl %ebp, 4(%edi) - movl 8(%ebx), %ebp - addl $16, %ebx - xorl %ebp, 8(%edi) - movl 12(%eax), %ebp - addl $16, %eax - xorl %ebp, 12(%edi) - addl $16, %edi - decl %ecx - jnz .xor_4way - - popl %edi - popl %esi - popl %ebp - popl %ebx - ret - - -/* neoscrypt_xor_salsa_4way(mem, xormem, workmem, rounds) - * i386 (SSE2) Salsa20 4-way with XOR */ -.globl neoscrypt_xor_salsa_4way -.globl _neoscrypt_xor_salsa_4way -neoscrypt_xor_salsa_4way: -_neoscrypt_xor_salsa_4way: -/* XOR and copy to temporary memory */ - movl 4(%esp), %eax - movl 8(%esp), %ecx - movl 12(%esp), %edx - movdqa 0(%eax), %xmm0 - movdqa 16(%eax), %xmm1 - movdqa 32(%eax), %xmm2 - movdqa 48(%eax), %xmm3 - movdqa 64(%eax), %xmm4 - movdqa 80(%eax), %xmm5 - movdqa 96(%eax), %xmm6 - movdqa 112(%eax), %xmm7 - pxor 0(%ecx), %xmm0 - pxor 16(%ecx), %xmm1 - pxor 32(%ecx), %xmm2 - pxor 48(%ecx), %xmm3 - pxor 64(%ecx), %xmm4 - pxor 80(%ecx), %xmm5 - pxor 96(%ecx), %xmm6 - pxor 112(%ecx), %xmm7 - movdqa %xmm0, 0(%eax) - movdqa %xmm1, 16(%eax) - movdqa %xmm2, 32(%eax) - movdqa %xmm3, 48(%eax) - movdqa %xmm4, 64(%eax) - movdqa %xmm5, 80(%eax) - movdqa %xmm6, 96(%eax) - movdqa %xmm7, 112(%eax) - movdqa %xmm0, 0(%edx) - movdqa %xmm1, 16(%edx) - movdqa %xmm2, 32(%edx) - movdqa %xmm3, 48(%edx) - movdqa %xmm4, 64(%edx) - movdqa %xmm5, 80(%edx) - movdqa %xmm6, 96(%edx) - movdqa %xmm7, 112(%edx) - movdqa 128(%eax), %xmm0 - movdqa 144(%eax), %xmm1 - movdqa 160(%eax), %xmm2 - movdqa 176(%eax), %xmm3 - movdqa 192(%eax), %xmm4 - movdqa 208(%eax), %xmm5 - movdqa 224(%eax), %xmm6 - movdqa 240(%eax), %xmm7 - pxor 128(%ecx), %xmm0 - pxor 144(%ecx), %xmm1 - pxor 160(%ecx), %xmm2 - pxor 176(%ecx), %xmm3 - pxor 192(%ecx), %xmm4 - pxor 208(%ecx), %xmm5 - pxor 224(%ecx), %xmm6 - pxor 240(%ecx), %xmm7 - movdqa %xmm0, 128(%eax) - movdqa %xmm1, 144(%eax) - movdqa %xmm2, 160(%eax) - movdqa %xmm3, 176(%eax) - movdqa %xmm4, 192(%eax) - movdqa %xmm5, 208(%eax) - movdqa %xmm6, 224(%eax) - movdqa %xmm7, 240(%eax) - movdqa %xmm0, 128(%edx) - movdqa %xmm1, 144(%edx) - movdqa %xmm2, 160(%edx) - movdqa %xmm3, 176(%edx) - movdqa %xmm4, 192(%edx) - movdqa %xmm5, 208(%edx) - movdqa %xmm6, 224(%edx) - movdqa %xmm7, 240(%edx) -/* number of double rounds */ - movl 16(%esp), %ecx -.xor_salsa_4way: -/* quarters A and B */ - movdqa 0(%edx), %xmm0 /* A: load a */ - movdqa 80(%edx), %xmm4 /* B: load a */ - paddd 192(%edx), %xmm0 /* A: t = a + d */ - paddd 16(%edx), %xmm4 /* B: t = a + d */ - movdqa %xmm0, %xmm3 /* A: rotate t (0) */ - movdqa %xmm4, %xmm7 /* B: rotate t (0) */ - pslld $7, %xmm0 /* A: rotate t (1) */ - psrld $25, %xmm3 /* A: rotate t (2) */ - pslld $7, %xmm4 /* B: rotate t (1) */ - psrld $25, %xmm7 /* B: rotate t (2) */ - por %xmm3, %xmm0 /* A: rotate t (3) */ - por %xmm7, %xmm4 /* B: rotate t (3) */ - pxor 64(%edx), %xmm0 /* A: b = b ^ t */ - pxor 144(%edx), %xmm4 /* B: b = b ^ t */ - movdqa %xmm0, %xmm1 /* A: copy b */ - movdqa %xmm4, %xmm5 /* B: copy b */ - movdqa %xmm1, 64(%edx) /* A: store b */ - movdqa %xmm5, 144(%edx) /* B: store b */ - paddd 0(%edx), %xmm0 /* A: t = b + a */ - paddd 80(%edx), %xmm4 /* B: t = b + a */ - movdqa %xmm0, %xmm3 /* A: rotate t (0) */ - movdqa %xmm4, %xmm7 /* B: rotate t (0) */ - pslld $9, %xmm0 /* A: rotate t (1) */ - psrld $23, %xmm3 /* A: rotate t (2) */ - pslld $9, %xmm4 /* B: rotate t (1) */ - psrld $23, %xmm7 /* B: rotate t (2) */ - por %xmm3, %xmm0 /* A: rotate t (3) */ - por %xmm7, %xmm4 /* B: rotate t (3) */ - pxor 128(%edx), %xmm0 /* A: c = c ^ t */ - pxor 208(%edx), %xmm4 /* B: c = c ^ t */ - movdqa %xmm0, %xmm2 /* A: copy c */ - movdqa %xmm4, %xmm6 /* B: copy c */ - movdqa %xmm2, 128(%edx) /* A: store c */ - movdqa %xmm6, 208(%edx) /* B: store c */ - paddd %xmm1, %xmm0 /* A: t = c + b */ - paddd %xmm5, %xmm4 /* B: t = c + b */ - movdqa %xmm0, %xmm3 /* A: rotate t (0) */ - movdqa %xmm4, %xmm7 /* B: rotate t (0) */ - pslld $13, %xmm0 /* A: rotate t (1) */ - psrld $19, %xmm3 /* A: rotate t (2) */ - pslld $13, %xmm4 /* B: rotate t (1) */ - psrld $19, %xmm7 /* B: rotate t (2) */ - por %xmm3, %xmm0 /* A: rotate t (3) */ - por %xmm7, %xmm4 /* B: rotate t (3) */ - pxor 192(%edx), %xmm0 /* A: d = d ^ t */ - pxor 16(%edx), %xmm4 /* B: d = d ^ t */ - movdqa %xmm0, 192(%edx) /* A: store d */ - movdqa %xmm4, 16(%edx) /* B: store d */ - paddd %xmm2, %xmm0 /* A: t = d + c */ - paddd %xmm6, %xmm4 /* B: t = d + c */ - movdqa %xmm0, %xmm3 /* A: rotate t (0) */ - movdqa %xmm4, %xmm7 /* B: rotate t (0) */ - pslld $18, %xmm0 /* A: rotate t (1) */ - psrld $14, %xmm3 /* A: rotate t (2) */ - pslld $18, %xmm4 /* B: rotate t (1) */ - psrld $14, %xmm7 /* B: rotate t (2) */ - por %xmm3, %xmm0 /* A: rotate t (3) */ - por %xmm7, %xmm4 /* B: rotate t (3) */ - pxor 0(%edx), %xmm0 /* A: a = a ^ t */ - pxor 80(%edx), %xmm4 /* B: a = a ^ t */ - movdqa %xmm0, 0(%edx) /* A: store a */ - movdqa %xmm4, 80(%edx) /* B: store a */ -/* quarters C and D*/ - movdqa 160(%edx), %xmm0 /* C: load a */ - movdqa 240(%edx), %xmm4 /* D: load a */ - paddd 96(%edx), %xmm0 /* C: t = a + d */ - paddd 176(%edx), %xmm4 /* D: t = a + d */ - movdqa %xmm0, %xmm3 /* C: rotate t (0) */ - movdqa %xmm4, %xmm7 /* D: rotate t (0) */ - pslld $7, %xmm0 /* C: rotate t (1) */ - psrld $25, %xmm3 /* C: rotate t (2) */ - pslld $7, %xmm4 /* D: rotate t (1) */ - psrld $25, %xmm7 /* D: rotate t (2) */ - por %xmm3, %xmm0 /* C: rotate t (3) */ - por %xmm7, %xmm4 /* D: rotate t (3) */ - pxor 224(%edx), %xmm0 /* C: b = b ^ t */ - pxor 48(%edx), %xmm4 /* D: b = b ^ t */ - movdqa %xmm0, %xmm1 /* C: copy b */ - movdqa %xmm4, %xmm5 /* D: copy b */ - movdqa %xmm1, 224(%edx) /* C: store b */ - movdqa %xmm5, 48(%edx) /* D: store b */ - paddd 160(%edx), %xmm0 /* C: t = b + a */ - paddd 240(%edx), %xmm4 /* D: t = b + a */ - movdqa %xmm0, %xmm3 /* C: rotate t (0) */ - movdqa %xmm4, %xmm7 /* D: rotate t (0) */ - pslld $9, %xmm0 /* C: rotate t (1) */ - psrld $23, %xmm3 /* C: rotate t (2) */ - pslld $9, %xmm4 /* D: rotate t (1) */ - psrld $23, %xmm7 /* D: rotate t (2) */ - por %xmm3, %xmm0 /* C: rotate t (3) */ - por %xmm7, %xmm4 /* D: rotate t (3) */ - pxor 32(%edx), %xmm0 /* C: c = c ^ t */ - pxor 112(%edx), %xmm4 /* D: c = c ^ t */ - movdqa %xmm0, %xmm2 /* C: copy c */ - movdqa %xmm4, %xmm6 /* D: copy c */ - movdqa %xmm2, 32(%edx) /* C: store c */ - movdqa %xmm6, 112(%edx) /* D: store c */ - paddd %xmm1, %xmm0 /* C: t = c + b */ - paddd %xmm5, %xmm4 /* D: t = c + b */ - movdqa %xmm0, %xmm3 /* C: rotate t (0) */ - movdqa %xmm4, %xmm7 /* D: rotate t (0) */ - pslld $13, %xmm0 /* C: rotate t (1) */ - psrld $19, %xmm3 /* C: rotate t (2) */ - pslld $13, %xmm4 /* D: rotate t (1) */ - psrld $19, %xmm7 /* D: rotate t (2) */ - por %xmm3, %xmm0 /* C: rotate t (3) */ - por %xmm7, %xmm4 /* D: rotate t (3) */ - pxor 96(%edx), %xmm0 /* C: d = d ^ t */ - pxor 176(%edx), %xmm4 /* D: d = d ^ t */ - movdqa %xmm0, 96(%edx) /* C: store d */ - movdqa %xmm4, 176(%edx) /* D: store d */ - paddd %xmm2, %xmm0 /* C: t = d + c */ - paddd %xmm6, %xmm4 /* D: t = d + c */ - movdqa %xmm0, %xmm3 /* C: rotate t (0) */ - movdqa %xmm4, %xmm7 /* D: rotate t (0) */ - pslld $18, %xmm0 /* C: rotate t (1) */ - psrld $14, %xmm3 /* C: rotate t (2) */ - pslld $18, %xmm4 /* D: rotate t (1) */ - psrld $14, %xmm7 /* D: rotate t (2) */ - por %xmm3, %xmm0 /* C: rotate t (3) */ - por %xmm7, %xmm4 /* D: rotate t (3) */ - pxor 160(%edx), %xmm0 /* C: a = a ^ t */ - pxor 240(%edx), %xmm4 /* D: a = a ^ t */ - movdqa %xmm0, 160(%edx) /* C: store a */ - movdqa %xmm4, 240(%edx) /* D: store a */ -/* quarters E and F */ - movdqa 0(%edx), %xmm0 /* E: load a */ - movdqa 80(%edx), %xmm4 /* F: load a */ - paddd 48(%edx), %xmm0 /* E: t = a + d */ - paddd 64(%edx), %xmm4 /* F: t = a + d */ - movdqa %xmm0, %xmm3 /* E: rotate t (0) */ - movdqa %xmm4, %xmm7 /* F: rotate t (0) */ - pslld $7, %xmm0 /* E: rotate t (1) */ - psrld $25, %xmm3 /* E: rotate t (2) */ - pslld $7, %xmm4 /* F: rotate t (1) */ - psrld $25, %xmm7 /* F: rotate t (2) */ - por %xmm3, %xmm0 /* E: rotate t (3) */ - por %xmm7, %xmm4 /* F: rotate t (3) */ - pxor 16(%edx), %xmm0 /* E: b = b ^ t */ - pxor 96(%edx), %xmm4 /* F: b = b ^ t */ - movdqa %xmm0, %xmm1 /* E: copy b */ - movdqa %xmm4, %xmm5 /* F: copy b */ - movdqa %xmm1, 16(%edx) /* E: store b */ - movdqa %xmm5, 96(%edx) /* F: store b */ - paddd 0(%edx), %xmm0 /* E: t = b + a */ - paddd 80(%edx), %xmm4 /* F: t = b + a */ - movdqa %xmm0, %xmm3 /* E: rotate t (0) */ - movdqa %xmm4, %xmm7 /* F: rotate t (0) */ - pslld $9, %xmm0 /* E: rotate t (1) */ - psrld $23, %xmm3 /* E: rotate t (2) */ - pslld $9, %xmm4 /* F: rotate t (1) */ - psrld $23, %xmm7 /* F: rotate t (2) */ - por %xmm3, %xmm0 /* E: rotate t (3) */ - por %xmm7, %xmm4 /* F: rotate t (3) */ - pxor 32(%edx), %xmm0 /* E: c = c ^ t */ - pxor 112(%edx), %xmm4 /* F: c = c ^ t */ - movdqa %xmm0, %xmm2 /* E: copy c */ - movdqa %xmm4, %xmm6 /* F: copy c */ - movdqa %xmm2, 32(%edx) /* E: store c */ - movdqa %xmm6, 112(%edx) /* F: store c */ - paddd %xmm1, %xmm0 /* E: t = c + b */ - paddd %xmm5, %xmm4 /* F: t = c + b */ - movdqa %xmm0, %xmm3 /* E: rotate t (0) */ - movdqa %xmm4, %xmm7 /* F: rotate t (0) */ - pslld $13, %xmm0 /* E: rotate t (1) */ - psrld $19, %xmm3 /* E: rotate t (2) */ - pslld $13, %xmm4 /* F: rotate t (1) */ - psrld $19, %xmm7 /* F: rotate t (2) */ - por %xmm3, %xmm0 /* E: rotate t (3) */ - por %xmm7, %xmm4 /* F: rotate t (3) */ - pxor 48(%edx), %xmm0 /* E: d = d ^ t */ - pxor 64(%edx), %xmm4 /* F: d = d ^ t */ - movdqa %xmm0, 48(%edx) /* E: store d */ - movdqa %xmm4, 64(%edx) /* F: store d */ - paddd %xmm2, %xmm0 /* E: t = d + c */ - paddd %xmm6, %xmm4 /* F: t = d + c */ - movdqa %xmm0, %xmm3 /* E: rotate t (0) */ - movdqa %xmm4, %xmm7 /* F: rotate t (0) */ - pslld $18, %xmm0 /* E: rotate t (1) */ - psrld $14, %xmm3 /* E: rotate t (2) */ - pslld $18, %xmm4 /* F: rotate t (1) */ - psrld $14, %xmm7 /* F: rotate t (2) */ - por %xmm3, %xmm0 /* E: rotate t (3) */ - por %xmm7, %xmm4 /* F: rotate t (3) */ - pxor 0(%edx), %xmm0 /* E: a = a ^ t */ - pxor 80(%edx), %xmm4 /* F: a = a ^ t */ - movdqa %xmm0, 0(%edx) /* E: store a */ - movdqa %xmm4, 80(%edx) /* F: store a */ -/* quarters G and H */ - movdqa 160(%edx), %xmm0 /* G: load a */ - movdqa 240(%edx), %xmm4 /* H: load a */ - paddd 144(%edx), %xmm0 /* G: t = a + d */ - paddd 224(%edx), %xmm4 /* H: t = a + d */ - movdqa %xmm0, %xmm3 /* G: rotate t (0) */ - movdqa %xmm4, %xmm7 /* H: rotate t (0) */ - pslld $7, %xmm0 /* G: rotate t (1) */ - psrld $25, %xmm3 /* G: rotate t (2) */ - pslld $7, %xmm4 /* H: rotate t (1) */ - psrld $25, %xmm7 /* H: rotate t (2) */ - por %xmm3, %xmm0 /* G: rotate t (3) */ - por %xmm7, %xmm4 /* H: rotate t (3) */ - pxor 176(%edx), %xmm0 /* G: b = b ^ t */ - pxor 192(%edx), %xmm4 /* H: b = b ^ t */ - movdqa %xmm0, %xmm1 /* G: copy b */ - movdqa %xmm4, %xmm5 /* H: copy b */ - movdqa %xmm1, 176(%edx) /* G: store b */ - movdqa %xmm5, 192(%edx) /* H: store b */ - paddd 160(%edx), %xmm0 /* G: t = b + a */ - paddd 240(%edx), %xmm4 /* H: t = b + a */ - movdqa %xmm0, %xmm3 /* G: rotate t (0) */ - movdqa %xmm4, %xmm7 /* H: rotate t (0) */ - pslld $9, %xmm0 /* G: rotate t (1) */ - psrld $23, %xmm3 /* G: rotate t (2) */ - pslld $9, %xmm4 /* H: rotate t (1) */ - psrld $23, %xmm7 /* H: rotate t (2) */ - por %xmm3, %xmm0 /* G: rotate t (3) */ - por %xmm7, %xmm4 /* H: rotate t (3) */ - pxor 128(%edx), %xmm0 /* G: c = c ^ t */ - pxor 208(%edx), %xmm4 /* H: c = c ^ t */ - movdqa %xmm0, %xmm2 /* G: copy c */ - movdqa %xmm4, %xmm6 /* H: copy c */ - movdqa %xmm2, 128(%edx) /* G: store c */ - movdqa %xmm6, 208(%edx) /* H: store c */ - paddd %xmm1, %xmm0 /* G: t = c + b */ - paddd %xmm5, %xmm4 /* H: t = c + b */ - movdqa %xmm0, %xmm3 /* G: rotate t (0) */ - movdqa %xmm4, %xmm7 /* H: rotate t (0) */ - pslld $13, %xmm0 /* G: rotate t (1) */ - psrld $19, %xmm3 /* G: rotate t (2) */ - pslld $13, %xmm4 /* H: rotate t (1) */ - psrld $19, %xmm7 /* H: rotate t (2) */ - por %xmm3, %xmm0 /* G: rotate t (3) */ - por %xmm7, %xmm4 /* H: rotate t (3) */ - pxor 144(%edx), %xmm0 /* G: d = d ^ t */ - pxor 224(%edx), %xmm4 /* H: d = d ^ t */ - movdqa %xmm0, 144(%edx) /* G: store d */ - movdqa %xmm4, 224(%edx) /* H: store d */ - paddd %xmm2, %xmm0 /* G: t = d + c */ - paddd %xmm6, %xmm4 /* H: t = d + c */ - movdqa %xmm0, %xmm3 /* G: rotate t (0) */ - movdqa %xmm4, %xmm7 /* H: rotate t (0) */ - pslld $18, %xmm0 /* G: rotate t (1) */ - psrld $14, %xmm3 /* G: rotate t (2) */ - pslld $18, %xmm4 /* H: rotate t (1) */ - psrld $14, %xmm7 /* H: rotate t (2) */ - por %xmm3, %xmm0 /* G: rotate t (3) */ - por %xmm7, %xmm4 /* H: rotate t (3) */ - pxor 160(%edx), %xmm0 /* G: a = a ^ t */ - pxor 240(%edx), %xmm4 /* H: a = a ^ t */ - movdqa %xmm0, 160(%edx) /* G: store a */ - movdqa %xmm4, 240(%edx) /* H: store a */ - decl %ecx - jnz .xor_salsa_4way - -/* write back data */ - movdqa 0(%eax), %xmm0 - movdqa 16(%eax), %xmm1 - movdqa 32(%eax), %xmm2 - movdqa 48(%eax), %xmm3 - movdqa 64(%eax), %xmm4 - movdqa 80(%eax), %xmm5 - movdqa 96(%eax), %xmm6 - movdqa 112(%eax), %xmm7 - paddd 0(%edx), %xmm0 - paddd 16(%edx), %xmm1 - paddd 32(%edx), %xmm2 - paddd 48(%edx), %xmm3 - paddd 64(%edx), %xmm4 - paddd 80(%edx), %xmm5 - paddd 96(%edx), %xmm6 - paddd 112(%edx), %xmm7 - movdqa %xmm0, 0(%eax) - movdqa %xmm1, 16(%eax) - movdqa %xmm2, 32(%eax) - movdqa %xmm3, 48(%eax) - movdqa %xmm4, 64(%eax) - movdqa %xmm5, 80(%eax) - movdqa %xmm6, 96(%eax) - movdqa %xmm7, 112(%eax) - movdqa 128(%eax), %xmm0 - movdqa 144(%eax), %xmm1 - movdqa 160(%eax), %xmm2 - movdqa 176(%eax), %xmm3 - movdqa 192(%eax), %xmm4 - movdqa 208(%eax), %xmm5 - movdqa 224(%eax), %xmm6 - movdqa 240(%eax), %xmm7 - paddd 128(%edx), %xmm0 - paddd 144(%edx), %xmm1 - paddd 160(%edx), %xmm2 - paddd 176(%edx), %xmm3 - paddd 192(%edx), %xmm4 - paddd 208(%edx), %xmm5 - paddd 224(%edx), %xmm6 - paddd 240(%edx), %xmm7 - movdqa %xmm0, 128(%eax) - movdqa %xmm1, 144(%eax) - movdqa %xmm2, 160(%eax) - movdqa %xmm3, 176(%eax) - movdqa %xmm4, 192(%eax) - movdqa %xmm5, 208(%eax) - movdqa %xmm6, 224(%eax) - movdqa %xmm7, 240(%eax) - - ret - - -/* neoscrypt_xor_chacha_4way(mem, xormem, workmem, double_rounds) - * i386 (SSE2) ChaCha20 4-way with XOR */ -.globl neoscrypt_xor_chacha_4way -.globl _neoscrypt_xor_chacha_4way -neoscrypt_xor_chacha_4way: -_neoscrypt_xor_chacha_4way: -/* XOR and copy to temporary memory */ - movl 4(%esp), %eax - movl 8(%esp), %ecx - movl 12(%esp), %edx - movdqa 0(%eax), %xmm0 - movdqa 16(%eax), %xmm1 - movdqa 32(%eax), %xmm2 - movdqa 48(%eax), %xmm3 - movdqa 64(%eax), %xmm4 - movdqa 80(%eax), %xmm5 - movdqa 96(%eax), %xmm6 - movdqa 112(%eax), %xmm7 - pxor 0(%ecx), %xmm0 - pxor 16(%ecx), %xmm1 - pxor 32(%ecx), %xmm2 - pxor 48(%ecx), %xmm3 - pxor 64(%ecx), %xmm4 - pxor 80(%ecx), %xmm5 - pxor 96(%ecx), %xmm6 - pxor 112(%ecx), %xmm7 - movdqa %xmm0, 0(%eax) - movdqa %xmm1, 16(%eax) - movdqa %xmm2, 32(%eax) - movdqa %xmm3, 48(%eax) - movdqa %xmm4, 64(%eax) - movdqa %xmm5, 80(%eax) - movdqa %xmm6, 96(%eax) - movdqa %xmm7, 112(%eax) - movdqa %xmm0, 0(%edx) - movdqa %xmm1, 16(%edx) - movdqa %xmm2, 32(%edx) - movdqa %xmm3, 48(%edx) - movdqa %xmm4, 64(%edx) - movdqa %xmm5, 80(%edx) - movdqa %xmm6, 96(%edx) - movdqa %xmm7, 112(%edx) - movdqa 128(%eax), %xmm0 - movdqa 144(%eax), %xmm1 - movdqa 160(%eax), %xmm2 - movdqa 176(%eax), %xmm3 - movdqa 192(%eax), %xmm4 - movdqa 208(%eax), %xmm5 - movdqa 224(%eax), %xmm6 - movdqa 240(%eax), %xmm7 - pxor 128(%ecx), %xmm0 - pxor 144(%ecx), %xmm1 - pxor 160(%ecx), %xmm2 - pxor 176(%ecx), %xmm3 - pxor 192(%ecx), %xmm4 - pxor 208(%ecx), %xmm5 - pxor 224(%ecx), %xmm6 - pxor 240(%ecx), %xmm7 - movdqa %xmm0, 128(%eax) - movdqa %xmm1, 144(%eax) - movdqa %xmm2, 160(%eax) - movdqa %xmm3, 176(%eax) - movdqa %xmm4, 192(%eax) - movdqa %xmm5, 208(%eax) - movdqa %xmm6, 224(%eax) - movdqa %xmm7, 240(%eax) - movdqa %xmm0, 128(%edx) - movdqa %xmm1, 144(%edx) - movdqa %xmm2, 160(%edx) - movdqa %xmm3, 176(%edx) - movdqa %xmm4, 192(%edx) - movdqa %xmm5, 208(%edx) - movdqa %xmm6, 224(%edx) - movdqa %xmm7, 240(%edx) -/* number of double rounds */ - movl 16(%esp), %ecx -.xor_chacha_4way: -/* quarters A and B, loads for C */ - movdqa 0(%edx), %xmm0 /* A: load a */ - movdqa 64(%edx), %xmm1 /* A: load b */ - paddd %xmm1, %xmm0 /* A: a = a + b */ - movdqa 192(%edx), %xmm3 /* A: load d */ - pxor %xmm0, %xmm3 /* A: d = d ^ a */ - movdqa 128(%edx), %xmm2 /* A: load c */ - movdqa %xmm3, %xmm7 /* A: rotate d (0) */ - movdqa 16(%edx), %xmm4 /* B: load a */ - pslld $16, %xmm3 /* A: rotate d (1) */ - psrld $16, %xmm7 /* A: rotate d (2) */ - movdqa 80(%edx), %xmm5 /* B: load b */ - por %xmm7, %xmm3 /* A: rotate d (3) */ - paddd %xmm3, %xmm2 /* A: c = c + d */ - paddd %xmm5, %xmm4 /* B: a = a + b */ - pxor %xmm2, %xmm1 /* A: b = b ^ c */ - movdqa %xmm1, %xmm7 /* A: rotate b (0) */ - movdqa 208(%edx), %xmm6 /* B: load d */ - pslld $12, %xmm1 /* A: rotate b (1) */ - psrld $20, %xmm7 /* A: rotate b (2) */ - pxor %xmm4, %xmm6 /* B: d = d ^ a */ - por %xmm7, %xmm1 /* A: rotate b (3) */ - movdqa %xmm6, %xmm7 /* B: rotate d (0) */ - paddd %xmm1, %xmm0 /* A: a = a + b */ - pslld $16, %xmm6 /* B: rotate d (1) */ - psrld $16, %xmm7 /* B: rotate d (2) */ - movdqa %xmm0, 0(%edx) /* A: store a */ - pxor %xmm0, %xmm3 /* A: d = d ^ a */ - por %xmm7, %xmm6 /* B: rotate d (3) */ - movdqa 144(%edx), %xmm0 /* B: load c */ - movdqa %xmm3, %xmm7 /* A: rotate d (0) */ - paddd %xmm6, %xmm0 /* B: c = c + d */ - pslld $8, %xmm3 /* A: rotate d (1) */ - psrld $24, %xmm7 /* A: rotate d (2) */ - pxor %xmm0, %xmm5 /* B: b = b ^ c */ - por %xmm7, %xmm3 /* A: rotate d (3) */ - movdqa %xmm5, %xmm7 /* B: rotate b (0) */ - movdqa %xmm3, 192(%edx) /* A: store d */ - paddd %xmm3, %xmm2 /* A: c = c + d */ - pslld $12, %xmm5 /* B: rotate b (1) */ - psrld $20, %xmm7 /* B: rotate b (2) */ - movdqa %xmm2, 128(%edx) /* A: store c */ - pxor %xmm2, %xmm1 /* A: b = b ^ c */ - por %xmm7, %xmm5 /* B: rotate b (3) */ - movdqa 224(%edx), %xmm3 /* C: load d */ - movdqa %xmm1, %xmm7 /* A: rotate b (0) */ - paddd %xmm5, %xmm4 /* B: a = a + b */ - pslld $7, %xmm1 /* A: rotate b (1) */ - psrld $25, %xmm7 /* A: rotate b (2) */ - movdqa %xmm4, 16(%edx) /* B: store a */ - pxor %xmm4, %xmm6 /* B: d = d ^ a */ - por %xmm7, %xmm1 /* A: rotate b (3) */ - movdqa 160(%edx), %xmm2 /* C: load c */ - movdqa %xmm6, %xmm7 /* B: rotate d (0) */ - movdqa %xmm1, 64(%edx) /* A: store b */ - pslld $8, %xmm6 /* B: rotate d (1) */ - psrld $24, %xmm7 /* B: rotate d (2) */ - movdqa 96(%edx), %xmm1 /* C: load b */ - por %xmm7, %xmm6 /* B: rotate d (3) */ - movdqa %xmm6, 208(%edx) /* B: store d */ - paddd %xmm6, %xmm0 /* B: c = c + d */ - movdqa %xmm0, 144(%edx) /* B: store c */ - pxor %xmm0, %xmm5 /* B: b = b ^ c */ - movdqa %xmm5, %xmm7 /* B: rotate b (0) */ - movdqa 32(%edx), %xmm0 /* C: load a */ - pslld $7, %xmm5 /* B: rotate b (1) */ - psrld $25, %xmm7 /* B: rotate b (2) */ - por %xmm7, %xmm5 /* B: rotate b (3) */ - movdqa %xmm5, 80(%edx) /* B: store b */ -/* quarters C and D, loads for E */ - paddd %xmm1, %xmm0 /* C: a = a + b */ - pxor %xmm0, %xmm3 /* C: d = d ^ a */ - movdqa %xmm3, %xmm7 /* C: rotate d (0) */ - movdqa 48(%edx), %xmm4 /* D: load a */ - pslld $16, %xmm3 /* C: rotate d (1) */ - psrld $16, %xmm7 /* C: rotate d (2) */ - movdqa 112(%edx), %xmm5 /* D: load b */ - por %xmm7, %xmm3 /* C: rotate d (3) */ - paddd %xmm3, %xmm2 /* C: c = c + d */ - paddd %xmm5, %xmm4 /* D: a = a + b */ - pxor %xmm2, %xmm1 /* C: b = b ^ c */ - movdqa %xmm1, %xmm7 /* C: rotate b (0) */ - movdqa 240(%edx), %xmm6 /* D: load d */ - pslld $12, %xmm1 /* C: rotate b (1) */ - psrld $20, %xmm7 /* C: rotate b (2) */ - pxor %xmm4, %xmm6 /* D: d = d ^ a */ - por %xmm7, %xmm1 /* C: rotate b (3) */ - movdqa %xmm6, %xmm7 /* D: rotate d (0) */ - paddd %xmm1, %xmm0 /* C: a = a + b */ - pslld $16, %xmm6 /* D: rotate d (1) */ - psrld $16, %xmm7 /* D: rotate d (2) */ - movdqa %xmm0, 32(%edx) /* C: store a */ - pxor %xmm0, %xmm3 /* C: d = d ^ a */ - por %xmm7, %xmm6 /* D: rotate d (3) */ - movdqa 176(%edx), %xmm0 /* D: load c */ - movdqa %xmm3, %xmm7 /* C: rotate d (0) */ - paddd %xmm6, %xmm0 /* D: c = c + d */ - pslld $8, %xmm3 /* C: rotate d (1) */ - psrld $24, %xmm7 /* C: rotate d (2) */ - pxor %xmm0, %xmm5 /* D: b = b ^ c */ - por %xmm7, %xmm3 /* C: rotate d (3) */ - movdqa %xmm5, %xmm7 /* D: rotate b (0) */ - movdqa %xmm3, 224(%edx) /* C: store d */ - paddd %xmm3, %xmm2 /* C: c = c + d */ - pslld $12, %xmm5 /* D: rotate b (1) */ - psrld $20, %xmm7 /* D: rotate b (2) */ - movdqa %xmm2, 160(%edx) /* C: store c */ - pxor %xmm2, %xmm1 /* C: b = b ^ c */ - por %xmm7, %xmm5 /* D: rotate b (3) */ - movdqa %xmm1, %xmm7 /* C: rotate b (0) */ - paddd %xmm5, %xmm4 /* D: a = a + b */ - pslld $7, %xmm1 /* C: rotate b (1) */ - psrld $25, %xmm7 /* C: rotate b (2) */ - movdqa %xmm4, 48(%edx) /* D: store a */ - pxor %xmm4, %xmm6 /* D: d = d ^ a */ - por %xmm7, %xmm1 /* C: rotate b (3) */ - movdqa 160(%edx), %xmm2 /* E: load c */ - movdqa %xmm6, %xmm7 /* D: rotate d (0) */ - movdqa %xmm1, 96(%edx) /* C: store b */ - pslld $8, %xmm6 /* D: rotate d (1) */ - psrld $24, %xmm7 /* D: rotate d (2) */ - movdqa 80(%edx), %xmm1 /* E: load b */ - por %xmm7, %xmm6 /* D: rotate d (3) */ - movdqa %xmm6, 240(%edx) /* D: store d */ - movdqa %xmm6, %xmm3 /* E: load d */ - paddd %xmm6, %xmm0 /* D: c = c + d */ - movdqa %xmm0, 176(%edx) /* D: store c */ - pxor %xmm0, %xmm5 /* D: b = b ^ c */ - movdqa %xmm5, %xmm7 /* D: rotate b (0) */ - movdqa 0(%edx), %xmm0 /* E: load a */ - pslld $7, %xmm5 /* D: rotate b (1) */ - psrld $25, %xmm7 /* D: rotate b (2) */ - por %xmm7, %xmm5 /* D: rotate b (3) */ - movdqa %xmm5, 112(%edx) /* D: store b */ -/* quarters E and F, loads for G */ - paddd %xmm1, %xmm0 /* E: a = a + b */ - pxor %xmm0, %xmm3 /* E: d = d ^ a */ - movdqa %xmm3, %xmm7 /* E: rotate d (0) */ - movdqa 16(%edx), %xmm4 /* F: load a */ - pslld $16, %xmm3 /* E: rotate d (1) */ - psrld $16, %xmm7 /* E: rotate d (2) */ - movdqa 96(%edx), %xmm5 /* F: load b */ - por %xmm7, %xmm3 /* E: rotate d (3) */ - paddd %xmm3, %xmm2 /* E: c = c + d */ - paddd %xmm5, %xmm4 /* F: a = a + b */ - pxor %xmm2, %xmm1 /* E: b = b ^ c */ - movdqa %xmm1, %xmm7 /* E: rotate b (0) */ - movdqa 192(%edx), %xmm6 /* F: load d */ - pslld $12, %xmm1 /* E: rotate b (1) */ - psrld $20, %xmm7 /* E: rotate b (2) */ - pxor %xmm4, %xmm6 /* F: d = d ^ a */ - por %xmm7, %xmm1 /* E: rotate b (3) */ - movdqa %xmm6, %xmm7 /* F: rotate d (0) */ - paddd %xmm1, %xmm0 /* E: a = a + b */ - pslld $16, %xmm6 /* F: rotate d (1) */ - psrld $16, %xmm7 /* F: rotate d (2) */ - movdqa %xmm0, 0(%edx) /* E: store a */ - pxor %xmm0, %xmm3 /* E: d = d ^ a */ - por %xmm7, %xmm6 /* F: rotate d (3) */ - movdqa 176(%edx), %xmm0 /* F: load c */ - movdqa %xmm3, %xmm7 /* E: rotate d (0) */ - paddd %xmm6, %xmm0 /* F: c = c + d */ - pslld $8, %xmm3 /* E: rotate d (1) */ - psrld $24, %xmm7 /* E: rotate d (2) */ - pxor %xmm0, %xmm5 /* F: b = b ^ c */ - por %xmm7, %xmm3 /* E: rotate d (3) */ - movdqa %xmm5, %xmm7 /* F: rotate b (0) */ - movdqa %xmm3, 240(%edx) /* E: store d */ - paddd %xmm3, %xmm2 /* E: c = c + d */ - pslld $12, %xmm5 /* F: rotate b (1) */ - psrld $20, %xmm7 /* F: rotate b (2) */ - movdqa %xmm2, 160(%edx) /* E: store c */ - pxor %xmm2, %xmm1 /* E: b = b ^ c */ - por %xmm7, %xmm5 /* F: rotate b (3) */ - movdqa 208(%edx), %xmm3 /* G: load d */ - movdqa %xmm1, %xmm7 /* E: rotate b (0) */ - paddd %xmm5, %xmm4 /* F: a = a + b */ - pslld $7, %xmm1 /* E: rotate b (1) */ - psrld $25, %xmm7 /* E: rotate b (2) */ - movdqa %xmm4, 16(%edx) /* F: store a */ - pxor %xmm4, %xmm6 /* F: d = d ^ a */ - por %xmm7, %xmm1 /* E: rotate b (3) */ - movdqa 128(%edx), %xmm2 /* G: load c */ - movdqa %xmm6, %xmm7 /* F: rotate d (0) */ - movdqa %xmm1, 80(%edx) /* E: store b */ - pslld $8, %xmm6 /* F: rotate d (1) */ - psrld $24, %xmm7 /* F: rotate d (2) */ - movdqa 112(%edx), %xmm1 /* G: load b */ - por %xmm7, %xmm6 /* F: rotate d (3) */ - movdqa %xmm6, 192(%edx) /* F: store d */ - paddd %xmm6, %xmm0 /* F: c = c + d */ - movdqa %xmm0, 176(%edx) /* F: store c */ - pxor %xmm0, %xmm5 /* F: b = b ^ c */ - movdqa %xmm5, %xmm7 /* F: rotate b (0) */ - movdqa 32(%edx), %xmm0 /* G: load a */ - pslld $7, %xmm5 /* F: rotate b (1) */ - psrld $25, %xmm7 /* F: rotate b (2) */ - por %xmm7, %xmm5 /* F: rotate b (3) */ - movdqa %xmm5, 96(%edx) /* F: store b */ -/* quarters G and H */ - paddd %xmm1, %xmm0 /* G: a = a + b */ - pxor %xmm0, %xmm3 /* G: d = d ^ a */ - movdqa %xmm3, %xmm7 /* G: rotate d (0) */ - movdqa 48(%edx), %xmm4 /* H: load a */ - pslld $16, %xmm3 /* G: rotate d (1) */ - psrld $16, %xmm7 /* G: rotate d (2) */ - movdqa 64(%edx), %xmm5 /* H: load b */ - por %xmm7, %xmm3 /* G: rotate d (3) */ - paddd %xmm3, %xmm2 /* G: c = c + d */ - paddd %xmm5, %xmm4 /* H: a = a + b */ - pxor %xmm2, %xmm1 /* G: b = b ^ c */ - movdqa %xmm1, %xmm7 /* G: rotate b (0) */ - movdqa 224(%edx), %xmm6 /* H: load d */ - pslld $12, %xmm1 /* G: rotate b (1) */ - psrld $20, %xmm7 /* G: rotate b (2) */ - pxor %xmm4, %xmm6 /* H: d = d ^ a */ - por %xmm7, %xmm1 /* G: rotate b (3) */ - movdqa %xmm6, %xmm7 /* H: rotate d (0) */ - paddd %xmm1, %xmm0 /* G: a = a + b */ - pslld $16, %xmm6 /* H: rotate d (1) */ - psrld $16, %xmm7 /* H: rotate d (2) */ - movdqa %xmm0, 32(%edx) /* G: store a */ - pxor %xmm0, %xmm3 /* G: d = d ^ a */ - por %xmm7, %xmm6 /* H: rotate d (3) */ - movdqa 144(%edx), %xmm0 /* H: load c */ - movdqa %xmm3, %xmm7 /* G: rotate d (0) */ - paddd %xmm6, %xmm0 /* H: c = c + d */ - pslld $8, %xmm3 /* G: rotate d (1) */ - psrld $24, %xmm7 /* G: rotate d (2) */ - pxor %xmm0, %xmm5 /* H: b = b ^ c */ - por %xmm7, %xmm3 /* G: rotate d (3) */ - movdqa %xmm5, %xmm7 /* H: rotate b (0) */ - movdqa %xmm3, 208(%edx) /* G: store d */ - paddd %xmm3, %xmm2 /* G: c = c + d */ - pslld $12, %xmm5 /* H: rotate b (1) */ - psrld $20, %xmm7 /* H: rotate b (2) */ - movdqa %xmm2, 128(%edx) /* G: store c */ - pxor %xmm2, %xmm1 /* G: b = b ^ c */ - por %xmm7, %xmm5 /* H: rotate b (3) */ - movdqa %xmm1, %xmm7 /* G: rotate b (0) */ - paddd %xmm5, %xmm4 /* H: a = a + b */ - pslld $7, %xmm1 /* G: rotate b (1) */ - psrld $25, %xmm7 /* G: rotate b (2) */ - movdqa %xmm4, 48(%edx) /* H: store a */ - pxor %xmm4, %xmm6 /* H: d = d ^ a */ - por %xmm7, %xmm1 /* G: rotate b (3) */ - movdqa %xmm6, %xmm7 /* H: rotate d (0) */ - movdqa %xmm1, 112(%edx) /* G: store b */ - pslld $8, %xmm6 /* H: rotate d (1) */ - psrld $24, %xmm7 /* H: rotate d (2) */ - por %xmm7, %xmm6 /* H: rotate d (3) */ - movdqa %xmm6, 224(%edx) /* H: store d */ - paddd %xmm6, %xmm0 /* H: c = c + d */ - movdqa %xmm0, 144(%edx) /* H: store c */ - pxor %xmm0, %xmm5 /* H: b = b ^ c */ - movdqa %xmm5, %xmm7 /* H: rotate b (0) */ - pslld $7, %xmm5 /* H: rotate b (1) */ - psrld $25, %xmm7 /* H: rotate b (2) */ - por %xmm7, %xmm5 /* H: rotate b (3) */ - movdqa %xmm5, 64(%edx) /* H: store b */ - decl %ecx - jnz .xor_chacha_4way - -/* write back data */ - movdqa 0(%eax), %xmm0 - movdqa 16(%eax), %xmm1 - movdqa 32(%eax), %xmm2 - movdqa 48(%eax), %xmm3 - movdqa 64(%eax), %xmm4 - movdqa 80(%eax), %xmm5 - movdqa 96(%eax), %xmm6 - movdqa 112(%eax), %xmm7 - paddd 0(%edx), %xmm0 - paddd 16(%edx), %xmm1 - paddd 32(%edx), %xmm2 - paddd 48(%edx), %xmm3 - paddd 64(%edx), %xmm4 - paddd 80(%edx), %xmm5 - paddd 96(%edx), %xmm6 - paddd 112(%edx), %xmm7 - movdqa %xmm0, 0(%eax) - movdqa %xmm1, 16(%eax) - movdqa %xmm2, 32(%eax) - movdqa %xmm3, 48(%eax) - movdqa %xmm4, 64(%eax) - movdqa %xmm5, 80(%eax) - movdqa %xmm6, 96(%eax) - movdqa %xmm7, 112(%eax) - movdqa 128(%eax), %xmm0 - movdqa 144(%eax), %xmm1 - movdqa 160(%eax), %xmm2 - movdqa 176(%eax), %xmm3 - movdqa 192(%eax), %xmm4 - movdqa 208(%eax), %xmm5 - movdqa 224(%eax), %xmm6 - movdqa 240(%eax), %xmm7 - paddd 128(%edx), %xmm0 - paddd 144(%edx), %xmm1 - paddd 160(%edx), %xmm2 - paddd 176(%edx), %xmm3 - paddd 192(%edx), %xmm4 - paddd 208(%edx), %xmm5 - paddd 224(%edx), %xmm6 - paddd 240(%edx), %xmm7 - movdqa %xmm0, 128(%eax) - movdqa %xmm1, 144(%eax) - movdqa %xmm2, 160(%eax) - movdqa %xmm3, 176(%eax) - movdqa %xmm4, 192(%eax) - movdqa %xmm5, 208(%eax) - movdqa %xmm6, 224(%eax) - movdqa %xmm7, 240(%eax) - - ret - -#endif /* MINER_4WAY */ - -/* cpu_vec_exts() - * i386 detector of any vector extensions present - * output bits set in %eax: - * 0 : MMX - * 1 : Extended MMX (MMX+) - * 2 : 3DNow! - * 3 : Extended 3DNow! (3DNow!+) - * 4 : SSE - * 5 : SSE2 - * 6 : SSE3 - * 7 : SSSE3 - * 8 : SSE41 - * 9 : SSE42 - * 10 : SSE4A - * 11 : XOP - * 12 : FMA4 - * 13 : AVX - * 14 : F16C - * 15 : FMA3 - * the other bits are reserved for the future use */ -.globl cpu_vec_exts -.globl _cpu_vec_exts -cpu_vec_exts: -_cpu_vec_exts: - pushl %ebx - pushl %ebp - xorl %ebp, %ebp -/* the CPUID extended function 0 should report the max. - * supported extended function number in %eax */ - movl $0x80000000, %eax - cpuid - cmpl $0x80000001, %eax - jb .cpu_vec_st1 - movl $0x80000001, %eax - cpuid -/* MMX+ (bit 22 of %edx); implies MMX */ - testl $0x00400000, %edx - jz .cpu_vec_3dnp - orl $0x00000003, %ebp -.cpu_vec_3dnp: -/* 3DNow!+ (bit 30 of %edx); implies 3DNow! */ - testl $0x80000000, %edx - jz .cpu_vec_3dn - orl $0x0000000C, %ebp - jmp .cpu_vec_sse4a -.cpu_vec_3dn: -/* 3DNow! (bit 31 of %edx); implies MMX */ - testl $0x80000000, %edx - jz .cpu_vec_sse4a - orl $0x00000005, %ebp -.cpu_vec_sse4a: -/* SSE4A (bit 6 of %ecx) */ - testl $0x00000040, %ecx - jz .cpu_vec_st1 - orl $0x00000400, %ebp -/* XOP (bit 11 of %ecx) */ - testl $0x00000800, %ecx - jz .cpu_vec_st1 - orl $0x00000800, %ebp -/* FMA4 (bit 16 of %ecx) */ - testl $0x00010000, %ecx - jz .cpu_vec_st1 - orl $0x00001000, %ebp -.cpu_vec_st1: -/* Some original Cyrix processors report nonsense in %ecx of - * the CPUID standard function 1, however they don't support even SSE */ - movl $1, %eax - cpuid -/* SSE (bit 25 of %edx); implies MMX+ and MMX */ - testl $0x02000000, %edx - jz .cpu_vec_mmx - orl $0x00000013, %ebp -/* SSE2 (bit 26 of %edx) */ - testl $0x04000000, %edx - jz .cpu_vec_exit - orl $0x00000020, %ebp -/* SSE3 (bit 0 of %ecx) */ - testl $0x00000001, %ecx - jz .cpu_vec_exit - orl $0x00000040, %ebp -/* SSSE3 (bit 9 of %ecx) */ - testl $0x00000100, %ecx - jz .cpu_vec_exit - orl $0x00000080, %ebp -/* SSE4.1 (bit 19 of %ecx) */ - testl $0x00080000, %ecx - jz .cpu_vec_exit - orl $0x00000100, %ebp -/* SSE4.2 (bit 20 of %ecx) */ - testl $0x00100000, %ecx - jz .cpu_vec_exit - orl $0x00000200, %ebp -/* AVX (bit 28 of %ecx) */ - testl $0x10000000, %ecx - jz .cpu_vec_exit - orl $0x00002000, %ebp - jmp .cpu_vec_exit -/* F16C (bit 29 of %ecx) */ - testl $0x20000000, %ecx - jz .cpu_vec_exit - orl $0x00004000, %ebp - jmp .cpu_vec_exit -/* FMA3 (bit 12 of %ecx) */ - testl $0x00001000, %ecx - jz .cpu_vec_exit - orl $0x00008000, %ebp - jmp .cpu_vec_exit - -.cpu_vec_mmx: -/* MMX (bit 23 of %edx) */ - testl $0x00800000, %edx - jz .cpu_vec_exit - orl $0x00000001, %ebp - -.cpu_vec_exit: - movl %ebp, %eax - popl %ebp - popl %ebx - ret - -#endif /* (ASM) && (__i386__) */ From f5f530d1443845713924a729fb801494f2076974 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Thu, 25 Jan 2018 21:29:54 +0100 Subject: [PATCH 032/348] Don't tie ZMQ block notify to a single address. Fixes #183 --- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 71 +++--------------- .../Bitcoin/BitcoinPayoutHandler.cs | 4 +- ...cs => BitcoinDaemonEndpointConfigExtra.cs} | 2 +- .../DaemonInterface/DaemonClient.cs | 73 +++++++++++++++++++ .../Extensions/PoolingExtensions.cs | 16 ++++ 5 files changed, 104 insertions(+), 62 deletions(-) rename src/MiningCore/Blockchain/Bitcoin/Configuration/{BitcoinPoolConfigExtra.cs => BitcoinDaemonEndpointConfigExtra.cs} (94%) create mode 100644 src/MiningCore/Extensions/PoolingExtensions.cs diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 3078b9878..457d8fd80 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -76,7 +76,6 @@ public BitcoinJobManager( protected readonly NotificationService notificationService; protected readonly IMasterClock clock; protected DaemonClient daemon; - protected BitcoinPoolConfigExtra extraPoolConfig; protected readonly IExtraNonceProvider extraNonceProvider; protected const int ExtranonceBytes = 4; protected readonly IHashAlgorithm sha256d = new Sha256D(); @@ -112,60 +111,21 @@ protected virtual void SetupJobUpdates() var sources = new List>(); var cancelTimeout = new List>(); - // block updates via ZMQ pub/sub - var zmqPublisherSocket = extraPoolConfig?.ZmqBlockNotifySocket?.Trim(); + // collect ports + var zmq = poolConfig.Daemons + .Where(x => !string.IsNullOrEmpty(x.Extra.SafeExtensionDataAs()?.ZmqBlockNotifySocket)) + .ToDictionary(x => x, x => x.Extra.SafeExtensionDataAs().ZmqBlockNotifySocket); - if (!string.IsNullOrEmpty(zmqPublisherSocket)) + if (zmq.Count > 0) { - var newJobsPubSub = Observable.Defer(()=> Observable.Create(obs => - { - var tcs = new CancellationTokenSource(); + logger.Info(() => $"[{LogCat}] Subscribing to ZMQ push-updates from {string.Join(", ", zmq.Keys.Select(x => x.Host).Distinct())}"); - Task.Factory.StartNew(() => - { - while (!tcs.IsCancellationRequested) - { - try - { - using (var subSocket = new SubscriberSocket()) - { - //subSocket.Options.ReceiveHighWatermark = 1000; - subSocket.Connect(zmqPublisherSocket); - subSocket.Subscribe(BitcoinConstants.ZmqPublisherTopicBlockHash); - - logger.Info($"Subscribed to {zmqPublisherSocket}/{BitcoinConstants.ZmqPublisherTopicBlockHash} for ZMQ pub/sub block updates"); - - while (!tcs.IsCancellationRequested) - { - subSocket.ReceiveMultipartMessage(2); - //var msg = subSocket.ReceiveMultipartMessage(2); - //var topic = msg.First().ConvertToString(Encoding.UTF8); - //var body = msg.Last().ConvertToString(Encoding.UTF8); - - obs.OnNext(true); - } - } - } - - catch (Exception ex) - { - logger.Error(ex); - } - - // do not consume all CPU cycles in case of a long lasting error condition - Thread.Sleep(1000); - } - }, tcs.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); - - return Disposable.Create(() => - { - tcs.Cancel(); - }); - })) - .Select(_ => Observable.FromAsync(() => UpdateJob(false, "ZMQ pub/sub"))) - .Concat() - .Publish() - .RefCount(); + var newJobsPubSub = daemon.ZmqSubscribe(zmq, BitcoinConstants.ZmqPublisherTopicBlockHash, 2) + .Do(x=> x.Dispose()) // we don't care about the contents + .Select(_ => Observable.FromAsync(() => UpdateJob(false, "ZMQ pub/sub"))) + .Concat() + .Publish() + .RefCount(); sources.Add(newJobsPubSub); cancelTimeout.Add(newJobsPubSub); @@ -471,13 +431,6 @@ public virtual async Task SubmitShareAsync(StratumClient worker, o protected override string LogCat => "Bitcoin Job Manager"; - public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig) - { - extraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); - - base.Configure(poolConfig, clusterConfig); - } - protected override void ConfigureDaemons() { var jsonSerializerSettings = ctx.Resolve(); diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs index bc29715a4..8c7b6e88b 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs @@ -74,7 +74,7 @@ public BitcoinPayoutHandler( protected readonly IComponentContext ctx; protected DaemonClient daemon; protected BitcoinCoinProperties coinProperties; - protected BitcoinPoolConfigExtra extraPoolConfig; + protected BitcoinDaemonEndpointConfigExtra extraPoolConfig; protected BitcoinPoolPaymentProcessingConfigExtra extraPoolPaymentProcessingConfig; protected override string LogCategory => "Bitcoin Payout Handler"; @@ -88,7 +88,7 @@ public virtual Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolC this.poolConfig = poolConfig; this.clusterConfig = clusterConfig; - extraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); + extraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); extraPoolPaymentProcessingConfig = poolConfig.PaymentProcessing.Extra.SafeExtensionDataAs(); coinProperties = BitcoinProperties.GetCoinProperties(poolConfig.Coin.Type, poolConfig.Coin.Algorithm); diff --git a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinDaemonEndpointConfigExtra.cs similarity index 94% rename from src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs rename to src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinDaemonEndpointConfigExtra.cs index 7f5acf8b6..394fee48f 100644 --- a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs +++ b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinDaemonEndpointConfigExtra.cs @@ -20,7 +20,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace MiningCore.Blockchain.Bitcoin.Configuration { - public class BitcoinPoolConfigExtra + public class BitcoinDaemonEndpointConfigExtra { public int? MinimumConfirmations { get; set; } diff --git a/src/MiningCore/DaemonInterface/DaemonClient.cs b/src/MiningCore/DaemonInterface/DaemonClient.cs index b1d1ec00d..5f3a7b902 100644 --- a/src/MiningCore/DaemonInterface/DaemonClient.cs +++ b/src/MiningCore/DaemonInterface/DaemonClient.cs @@ -28,17 +28,21 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Net.Http; using System.Net.Http.Headers; using System.Net.WebSockets; +using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using MiningCore.Blockchain.Bitcoin; using MiningCore.Buffers; using MiningCore.Configuration; using MiningCore.Extensions; using MiningCore.JsonRpc; using MiningCore.Stratum; using MiningCore.Util; +using NetMQ; +using NetMQ.Sockets; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; @@ -229,6 +233,18 @@ public IObservable> WebsocketSubscribe(Dictionary[]> ZmqSubscribe(Dictionary portMap, string topic, int numMsgSegments = 2) + { + Contract.Requires(!string.IsNullOrEmpty(topic), $"{nameof(topic)} must not be empty"); + + logger.LogInvoke(new[] { topic }); + + return Observable.Merge(portMap.Keys + .Select(endPoint => ZmqSubscribeEndpoint(endPoint, portMap[endPoint], topic, numMsgSegments))) + .Publish() + .RefCount(); + } + #endregion // API-Surface private async Task BuildRequestTask(DaemonEndpointConfig endPoint, string method, object payload, @@ -471,5 +487,62 @@ private IObservable> WebsocketSubscribeEndpoint(DaemonE }); })); } + + private IObservable[]> ZmqSubscribeEndpoint(DaemonEndpointConfig endPoint, string url, string topic, int numMsgSegments = 2) + { + return Observable.Defer(() => Observable.Create[]>(obs => + { + var tcs = new CancellationTokenSource(); + + Task.Factory.StartNew(() => + { + using (tcs) + { + while (!tcs.IsCancellationRequested) + { + try + { + using (var subSocket = new SubscriberSocket()) + { + //subSocket.Options.ReceiveHighWatermark = 1000; + subSocket.Connect(url); + subSocket.Subscribe(topic); + + logger.Debug($"Subscribed to {url}/{BitcoinConstants.ZmqPublisherTopicBlockHash}"); + + while (!tcs.IsCancellationRequested) + { + var msg = subSocket.ReceiveMultipartMessage(numMsgSegments); + + // Export all frame data as array of PooledArraySegments + var result = msg.Select(x => + { + var buf = ArrayPool.Shared.Rent(x.BufferSize); + Array.Copy(x.ToByteArray(), buf, x.BufferSize); + return new PooledArraySegment(buf, 0, x.BufferSize); + }).ToArray(); + + obs.OnNext(result); + } + } + } + + catch (Exception ex) + { + logger.Error(ex); + } + + // do not consume all CPU cycles in case of a long lasting error condition + Thread.Sleep(1000); + } + } + }, tcs.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); + + return Disposable.Create(() => + { + tcs.Cancel(); + }); + })); + } } } diff --git a/src/MiningCore/Extensions/PoolingExtensions.cs b/src/MiningCore/Extensions/PoolingExtensions.cs new file mode 100644 index 000000000..ad384063a --- /dev/null +++ b/src/MiningCore/Extensions/PoolingExtensions.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; +using MiningCore.Buffers; + +namespace MiningCore.Extensions +{ + public static class PoolingExtensions + { + public static void Dispose(this IEnumerable> col) + { + foreach(var seg in col) + seg.Dispose(); + } + } +} From 1c6112acf7e5b0211674bda41321b1e471c61568 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 26 Jan 2018 08:45:40 +0100 Subject: [PATCH 033/348] X17 hash integration --- src/MiningCore.Tests/Crypto/HashingTests.cs | 16 + .../Blockchain/Bitcoin/BitcoinProperties.cs | 1 + .../Crypto/Hashing/Algorithms/X17.cs | 45 + src/MiningCore/Native/LibMultihash.cs | 3 + .../runtimes/win-x64/native/libmultihash.dll | Bin 873472 -> 908800 bytes .../runtimes/win-x86/native/libmultihash.dll | Bin 872448 -> 907776 bytes src/Native/libmultihash/Makefile | 3 +- src/Native/libmultihash/exports.cpp | 6 + src/Native/libmultihash/libmultihash.vcxproj | 8 +- .../libmultihash/libmultihash.vcxproj.filters | 24 +- src/Native/libmultihash/sha3/hamsi_helper.c | 6 +- src/Native/libmultihash/sha3/haval_helper.c | 195 ++++ src/Native/libmultihash/sha3/sph_haval.c | 975 ++++++++++++++++++ src/Native/libmultihash/sha3/sph_haval.h | 969 +++++++++++++++++ src/Native/libmultihash/sha3/sph_sha2.c | 691 +++++++++++++ src/Native/libmultihash/sha3/sph_sha2.h | 371 +++++++ src/Native/libmultihash/sha3/sph_sha2big.c | 248 +++++ src/Native/libmultihash/x17.c | 115 +++ src/Native/libmultihash/x17.h | 16 + 19 files changed, 3684 insertions(+), 8 deletions(-) create mode 100644 src/MiningCore/Crypto/Hashing/Algorithms/X17.cs create mode 100644 src/Native/libmultihash/sha3/haval_helper.c create mode 100644 src/Native/libmultihash/sha3/sph_haval.c create mode 100644 src/Native/libmultihash/sha3/sph_haval.h create mode 100644 src/Native/libmultihash/sha3/sph_sha2.c create mode 100644 src/Native/libmultihash/sha3/sph_sha2.h create mode 100644 src/Native/libmultihash/sha3/sph_sha2big.c create mode 100644 src/Native/libmultihash/x17.c create mode 100644 src/Native/libmultihash/x17.h diff --git a/src/MiningCore.Tests/Crypto/HashingTests.cs b/src/MiningCore.Tests/Crypto/HashingTests.cs index 8cff67e90..0f6d3d634 100644 --- a/src/MiningCore.Tests/Crypto/HashingTests.cs +++ b/src/MiningCore.Tests/Crypto/HashingTests.cs @@ -186,6 +186,22 @@ public void X11_Hash_Should_Throw_On_Null_Input() Assert.Throws(() => hasher.Digest(null)); } + [Fact] + public void X17_Hash_Should_Match() + { + var hasher = new X17(); + var result = hasher.Digest(testValue).ToHexString(); + + Assert.Equal("6a9a4f558168e60241e46fe44365021c4d7e7344144ab1739d6fb0125ac4c592", result); + } + + [Fact] + public void X17_Hash_Should_Throw_On_Null_Input() + { + var hasher = new Sha256S(); + Assert.Throws(() => hasher.Digest(null)); + } + [Fact] public void Skein_Hash_Should_Match() { diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index 928cb53a7..d8cddaaf1 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -35,6 +35,7 @@ public class BitcoinProperties private static readonly IHashAlgorithm sha256D = new Sha256D(); private static readonly IHashAlgorithm sha256DReverse = new DigestReverser(sha256D); private static readonly IHashAlgorithm x11 = new X11(); + private static readonly IHashAlgorithm x17 = new X17(); private static readonly IHashAlgorithm groestl = new Groestl(); private static readonly IHashAlgorithm lyra2Rev2 = new Lyra2Rev2(); private static readonly IHashAlgorithm scrypt_1024_1 = new Scrypt(1024, 1); diff --git a/src/MiningCore/Crypto/Hashing/Algorithms/X17.cs b/src/MiningCore/Crypto/Hashing/Algorithms/X17.cs new file mode 100644 index 000000000..114179d8f --- /dev/null +++ b/src/MiningCore/Crypto/Hashing/Algorithms/X17.cs @@ -0,0 +1,45 @@ +/* +Copyright 2017 Coin Foundry (coinfoundry.org) +Authors: Oliver Weichhold (oliver@weichhold.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +using MiningCore.Contracts; +using MiningCore.Native; + +namespace MiningCore.Crypto.Hashing.Algorithms +{ + public unsafe class X17 : IHashAlgorithm + { + public byte[] Digest(byte[] data, params object[] extra) + { + Contract.RequiresNonNull(data, nameof(data)); + + var result = new byte[32]; + + fixed(byte* input = data) + { + fixed(byte* output = result) + { + LibMultihash.x17(input, output, (uint) data.Length); + } + } + + return result; + } + } +} diff --git a/src/MiningCore/Native/LibMultihash.cs b/src/MiningCore/Native/LibMultihash.cs index 5d34c0d34..2a44cf53c 100644 --- a/src/MiningCore/Native/LibMultihash.cs +++ b/src/MiningCore/Native/LibMultihash.cs @@ -37,6 +37,9 @@ public static unsafe class LibMultihash [DllImport("libmultihash", EntryPoint = "x15_export", CallingConvention = CallingConvention.Cdecl)] public static extern int x15(byte* input, byte* output, uint inputLength); + [DllImport("libmultihash", EntryPoint = "x17_export", CallingConvention = CallingConvention.Cdecl)] + public static extern int x17(byte* input, byte* output, uint inputLength); + [DllImport("libmultihash", EntryPoint = "neoscrypt_export", CallingConvention = CallingConvention.Cdecl)] public static extern int neoscrypt(byte* input, byte* output, uint inputLength, uint profile); diff --git a/src/MiningCore/runtimes/win-x64/native/libmultihash.dll b/src/MiningCore/runtimes/win-x64/native/libmultihash.dll index ae8dcd40d2e9d6fe3291511210ae0cff2aee0422..ee0b76888a624edef135a13b22427b37e4f0ba64 100644 GIT binary patch delta 152636 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 KUDtW~Mg1RKQt6uj delta 118791 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 iee-=8;+QV)D diff --git a/src/MiningCore/runtimes/win-x86/native/libmultihash.dll b/src/MiningCore/runtimes/win-x86/native/libmultihash.dll index 672c96664dd6eac7c5fe77da18d3b0059085cd02..db45fc4d2dbcdb907ef227e70bf67cc33cfc42cf 100644 GIT binary patch delta 154849 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 delta 119517 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

    M>OCMeszF&r? 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