From df1348e04116ba5b6290f20bf715c415688dfc44 Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 27 Apr 2018 20:26:44 +0200 Subject: [PATCH] Dev (#328) * Handle SIGTERM for graceful shutdown * Shutdown Logging * RVN, PGN address meta data * Eth nonce parsing * Implemented support for Tcp-Proxy-Protocol. Fixes #306 * Callisto Mainnet support * Disable Ethereum wallet ownership check at startup as it seems to do more harm than good. * Changed Ethash Future DAG generation * Equihashverify update * Short-time ban for unauthorized miners to prevent potential validateaddress RPC DDos * Max inbound request length increase * Support static diff for bitcoin family of pools using d=diff as part of the password * Partition API into regular and admin-api on different ports * MessageBus for loosely coupled inter-component communication * Publish shares over MessageBus * Refactor external share receiver logic from ShareRecorder into own component --- src/MiningCore.Tests/Crypto/HashingTests.cs | 22 + src/MiningCore.Tests/MiningCore.Tests.csproj | 6 +- src/MiningCore/Api/ApiServer.cs | 101 +- .../Api/Responses/GetBlocksResponse.cs | 2 + src/MiningCore/AutofacModule.cs | 8 + .../Blockchain/Bitcoin/BitcoinConstants.cs | 2 + .../Blockchain/Bitcoin/BitcoinJob.cs | 4 +- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 240 ++-- .../Bitcoin/BitcoinPayoutHandler.cs | 2 +- .../Blockchain/Bitcoin/BitcoinPool.cs | 7 +- .../Blockchain/Bitcoin/BitcoinPoolBase.cs | 57 +- .../Blockchain/Bitcoin/BitcoinProperties.cs | 12 + .../Configuration/BitcoinPoolConfigExtra.cs | 7 + .../Blockchain/BitcoinGold/BitcoinGoldPool.cs | 4 +- src/MiningCore/Blockchain/CoinMetaData.cs | 10 +- src/MiningCore/Blockchain/Dash/DashPool.cs | 4 +- .../Blockchain/Ethereum/EthereumConstants.cs | 1 + .../Blockchain/Ethereum/EthereumJob.cs | 4 +- .../Blockchain/Ethereum/EthereumJobManager.cs | 61 +- .../Ethereum/EthereumPayoutHandler.cs | 1 + .../Blockchain/Ethereum/EthereumPool.cs | 13 +- .../Blockchain/Flo/FloJobManager.cs | 9 +- src/MiningCore/Blockchain/Flo/FloPool.cs | 4 +- src/MiningCore/Blockchain/JobManagerBase.cs | 108 +- .../Configuration/MoneroPoolConfigExtra.cs | 32 + src/MiningCore/Blockchain/Monero/MoneroJob.cs | 19 +- .../Blockchain/Monero/MoneroJobManager.cs | 182 ++- .../Blockchain/Monero/MoneroPool.cs | 12 +- .../Blockchain/Straks/StraksPool.cs | 4 +- .../Blockchain/ZCash/ZCashConstants.cs | 2 +- src/MiningCore/Blockchain/ZCash/ZCashJob.cs | 2 +- .../Blockchain/ZCash/ZCashJobManager.cs | 3 + src/MiningCore/Blockchain/ZCash/ZCashPool.cs | 4 +- .../Blockchain/ZCash/ZCashPoolBase.cs | 10 +- src/MiningCore/Configuration/ClusterConfig.cs | 26 + .../Crypto/Hashing/Equihash/EquihashSolver.cs | 2 +- .../Crypto/Hashing/Ethash/EthashFull.cs | 17 +- src/MiningCore/JsonRpc/JsonRpcResponse.cs | 8 + src/MiningCore/Messaging/Abstractions.cs | 94 ++ src/MiningCore/Messaging/MessageBus.cs | 212 ++++ src/MiningCore/Mining/Abstractions.cs | 4 +- src/MiningCore/Mining/PoolBase.cs | 37 +- src/MiningCore/Mining/ShareReceiver.cs | 250 ++++ src/MiningCore/Mining/ShareRecorder.cs | 206 +--- src/MiningCore/Mining/ShareRelay.cs | 16 +- src/MiningCore/MiningCore.csproj | 12 +- src/MiningCore/Native/LibMultihash.cs | 2 +- .../Messages/BlockNotification.cs | 18 + .../Notifications/NotificationService.cs | 29 +- src/MiningCore/Program.cs | 61 +- src/MiningCore/Stratum/StratumClient.cs | 78 +- src/MiningCore/Stratum/StratumServer.cs | 12 +- src/MiningCore/Util/ScheduledSubject.cs | 62 + .../runtimes/win-x64/native/libmultihash.dll | Bin 931840 -> 1075200 bytes .../runtimes/win-x86/native/libmultihash.dll | Bin 921088 -> 1037312 bytes src/Native/libmultihash/Makefile | 12 +- .../libmultihash/equi/arith_uint256.cpp | 260 ++++ src/Native/libmultihash/equi/arith_uint256.h | 290 +++++ src/Native/libmultihash/equi/crypto/common.h | 111 ++ .../libmultihash/equi/crypto/equihash.cpp | 824 +++++++++++++ .../libmultihash/equi/crypto/equihash.h | 283 +++++ .../libmultihash/equi/crypto/equihash.tcc | 49 + .../libmultihash/equi/crypto/hmac_sha256.cpp | 34 + .../libmultihash/equi/crypto/hmac_sha256.h | 32 + .../libmultihash/equi/crypto/hmac_sha512.cpp | 34 + .../libmultihash/equi/crypto/hmac_sha512.h | 32 + .../libmultihash/equi/crypto/ripemd160.cpp | 292 +++++ .../libmultihash/equi/crypto/ripemd160.h | 28 + src/Native/libmultihash/equi/crypto/sha1.cpp | 199 ++++ src/Native/libmultihash/equi/crypto/sha1.h | 28 + .../libmultihash/equi/crypto/sha256.cpp | 199 ++++ src/Native/libmultihash/equi/crypto/sha256.h | 33 + .../libmultihash/equi/crypto/sha512.cpp | 207 ++++ src/Native/libmultihash/equi/crypto/sha512.h | 28 + src/Native/libmultihash/equi/endian.h | 117 -- src/Native/libmultihash/equi/equi.c | 158 --- src/Native/libmultihash/equi/equi.h | 48 - .../libmultihash/equi/equihashverify.cc | 18 + src/Native/libmultihash/equi/equihashverify.h | 16 + src/Native/libmultihash/equi/random.cpp | 91 ++ src/Native/libmultihash/equi/random.h | 74 ++ src/Native/libmultihash/equi/serialize.h | 1002 ++++++++++++++++ .../libmultihash/equi/support/cleanse.cpp | 21 + .../libmultihash/equi/support/cleanse.h | 13 + src/Native/libmultihash/equi/tinyformat.h | 1049 +++++++++++++++++ src/Native/libmultihash/equi/uint256.cpp | 146 +++ src/Native/libmultihash/equi/uint256.h | 158 +++ src/Native/libmultihash/equi/util.cpp | 79 ++ src/Native/libmultihash/equi/util.h | 58 + .../libmultihash/equi/utilstrencodings.cpp | 692 +++++++++++ .../libmultihash/equi/utilstrencodings.h | 122 ++ src/Native/libmultihash/exports.cpp | 12 +- src/Native/libmultihash/libmultihash.vcxproj | 55 +- .../libmultihash/libmultihash.vcxproj.filters | 109 +- .../{equi/endian.c => portable_endian.h} | 0 95 files changed, 8234 insertions(+), 884 deletions(-) create mode 100644 src/MiningCore/Blockchain/Monero/Configuration/MoneroPoolConfigExtra.cs create mode 100644 src/MiningCore/Messaging/Abstractions.cs create mode 100644 src/MiningCore/Messaging/MessageBus.cs create mode 100644 src/MiningCore/Mining/ShareReceiver.cs create mode 100644 src/MiningCore/Notifications/Messages/BlockNotification.cs create mode 100644 src/MiningCore/Util/ScheduledSubject.cs create mode 100644 src/Native/libmultihash/equi/arith_uint256.cpp create mode 100644 src/Native/libmultihash/equi/arith_uint256.h create mode 100644 src/Native/libmultihash/equi/crypto/common.h create mode 100644 src/Native/libmultihash/equi/crypto/equihash.cpp create mode 100644 src/Native/libmultihash/equi/crypto/equihash.h create mode 100644 src/Native/libmultihash/equi/crypto/equihash.tcc create mode 100644 src/Native/libmultihash/equi/crypto/hmac_sha256.cpp create mode 100644 src/Native/libmultihash/equi/crypto/hmac_sha256.h create mode 100644 src/Native/libmultihash/equi/crypto/hmac_sha512.cpp create mode 100644 src/Native/libmultihash/equi/crypto/hmac_sha512.h create mode 100644 src/Native/libmultihash/equi/crypto/ripemd160.cpp create mode 100644 src/Native/libmultihash/equi/crypto/ripemd160.h create mode 100644 src/Native/libmultihash/equi/crypto/sha1.cpp create mode 100644 src/Native/libmultihash/equi/crypto/sha1.h create mode 100644 src/Native/libmultihash/equi/crypto/sha256.cpp create mode 100644 src/Native/libmultihash/equi/crypto/sha256.h create mode 100644 src/Native/libmultihash/equi/crypto/sha512.cpp create mode 100644 src/Native/libmultihash/equi/crypto/sha512.h delete mode 100644 src/Native/libmultihash/equi/endian.h delete mode 100644 src/Native/libmultihash/equi/equi.c delete mode 100644 src/Native/libmultihash/equi/equi.h create mode 100644 src/Native/libmultihash/equi/equihashverify.cc create mode 100644 src/Native/libmultihash/equi/equihashverify.h create mode 100644 src/Native/libmultihash/equi/random.cpp create mode 100644 src/Native/libmultihash/equi/random.h create mode 100644 src/Native/libmultihash/equi/serialize.h create mode 100644 src/Native/libmultihash/equi/support/cleanse.cpp create mode 100644 src/Native/libmultihash/equi/support/cleanse.h create mode 100644 src/Native/libmultihash/equi/tinyformat.h create mode 100644 src/Native/libmultihash/equi/uint256.cpp create mode 100644 src/Native/libmultihash/equi/uint256.h create mode 100644 src/Native/libmultihash/equi/util.cpp create mode 100644 src/Native/libmultihash/equi/util.h create mode 100644 src/Native/libmultihash/equi/utilstrencodings.cpp create mode 100644 src/Native/libmultihash/equi/utilstrencodings.h rename src/Native/libmultihash/{equi/endian.c => portable_endian.h} (100%) diff --git a/src/MiningCore.Tests/Crypto/HashingTests.cs b/src/MiningCore.Tests/Crypto/HashingTests.cs index ae1bf3ba2..c5735274d 100644 --- a/src/MiningCore.Tests/Crypto/HashingTests.cs +++ b/src/MiningCore.Tests/Crypto/HashingTests.cs @@ -333,6 +333,28 @@ public void EquihashVerifier_Should_Verify_Success() Assert.True(result); } + [Fact] + public void EquihashVerifier_Should_Not_Verify_Invalid_Solution() + { + var hasher = EquihashSolver.Instance.Value; + var header = "0400000008e9694cc2120ec1b5733cc12687b609058eec4f7046a521ad1d1e3049b400003e7420ed6f40659de0305ef9b7ec037f4380ed9848bc1c015691c90aa16ff3930000000000000000000000000000000000000000000000000000000000000000c9310d5874e0001f000000000000000000000000000000010b000000000000000000000000000040".HexToByteArray(); + var solution = "90b43863a213bfe79f00337f5a729f09710abcc07035ef8ac34372abddecf2f82715f7223f075af96f0604fc124d6151fc8fb516d24a137faec123a89aa9a433f8a25a6bcfc554c28be556f6c878f96539186fab191505f278df48bf1ad2240e5bb39f372a143de1dd1b672312e00d52a3dd83f471b0239a7e8b30d4b9153027df87c8cd0b64de76749539fea376b4f39d08cf3d5e821495e52fdfa6f8085e59fc670656121c9d7c01388c8b4b4585aa7b9ac3f7ae796f9eb1fadba1730a1860eed797feabb18832b5e8f003c0adaf0788d1016e7a8969144018ecc86140aa4553962aa739a4850b509b505e158c5f9e2d5376374652e9e6d81b19fa0351be229af136efbce681463cc53d7880c1eeca3411154474ff8a7b2bac034a2026646776a517bf63921c31fbbd6be7c3ff42aab28230bfe81d33800b892b262f3579b7a41925a59f5cc1d4f523577c19ff9f92023146fa26486595bd89a1ba459eb0b5cec0578c3a071dbec73eca054c723ab30ce8e69de32e779cd2f1030e39878ac6ea3cdca743b43aedefe1a9b4f2da861038e2759defef0b8cad11d4179f2f08881b53ccc203e558c0571e049d998a257b3279016aad0d7999b609f6331a0d0f88e286a70432ca7f50a5bb8fafbbe9230b4ccb1fa57361c163d6b9f84579d61f41585a022d07dc8e55a8de4d8f87641dae777819458a2bf1bb02c438480ff11621ca8442ec2946875cce247c8877051359e9c822670d37bb00fa806e60e8e890ce62540fda2d5b1c790ca1e005030ac6d8e63db577bb98be111ee146828f9c48ee6257d7627b93ea3dd11aac3412e63dfc7ca132a73c4f51e7650f3f8ecf57bfc18716990b492d50e0a3e5fbf6136e771b91f7283ec3326209265b9531d157f8a07a4117fc8fb29ba1363afc6f9f0608251ea595256727a5bbe28f42a42edfbfa9017680e32980d4ad381612612b2bc7ad91e82eca693ea4fc27049a99636b50a576f1e55c72202d582b150ef194c1419f53177ecf315ea6b0e2f1aa8cd8f59b165aa0d89561c537fb6141f5813b7a4968fe16afc703326113f68508d88ff8d0aee1e88a84c0ae56c72f27511290ced48e93e8c95419d14aed1a5b2e9b2c9c1070c593e5eb50bb9a80e14e9f9fe501f56b1b3140159e8213b75d48d14af472a604484cd8e7e7abb6820245ed3ab29f9947463a033c586194be45eadec8392c8614d83a1e9ca0fe5655fa14f7a9c1d1f8f2185a06193ff4a3c3e9a96b02310033ceaa25894e7c56a6147e691597098054e285d39656d3d459ec5d13243c062b6eb44e19a13bdfc0b3c96bd3d1aeb75bb6b080322aea23555993cb529243958bb1a0e5d5027e6c78155437242d1d13c1d6e442a0e3783147a08bbfc0c2529fb705ad27713df40486fd58f001977f25dfd3c202451c07010a3880bca63959ca61f10ed3871f1152166fce2b52135718a8ceb239a0664a31c62defaad70be4b920dce70549c10d9138fbbad7f291c5b73fa21c3889929b143bc1576b72f70667ac11052b686891085290d871db528b5cfdc10a6d563925227609f10d1768a0e02dc7471ad424f94f737d4e7eb0fb167f1434fc4ae2d49e152f06f0845b6db0a44f0d6f5e7410420e6bd1f430b1af956005bf72b51405a04d9a5d9906ceca52c22c855785c3c3ac4c3e9bf532d31bab321e1db66f6a9f7dc9c017f2b7d8dfeb933cf5bbae71311ae318f6d187ebc5c843be342b08a9a0ff7c4b9c4b0f4fa74b13296afe84b6481440d58332e07b3d051ed55219d28e77af6612134da4431b797c63ef55bc53831e2f421db620fee51ba0967e4ed7009ef90af2204259bbfbb54537fd35c2132fa8e7f9c84bf9938d248862c6ca1cca9f48b0b33aa1589185c4eabc1c32".HexToByteArray(); + var result = hasher.Verify(header, solution); + + Assert.False(result); + } + + [Fact] + public void EquihashVerifier_Should_Not_Verify_Fake_Solution() + { + var hasher = EquihashSolver.Instance.Value; + var header = "0400000008e9694cc2120ec1b5733cc12687b609058eec4f7046a521ad1d1e3049b400003e7420ed6f40659de0305ef9b7ec037f4380ed9848bc1c015691c90aa16ff3930000000000000000000000000000000000000000000000000000000000000000c9310d5874e0001f000000000000000000000000000000010b000000000000000000000000000040".HexToByteArray(); + var solution = Enumerable.Repeat((byte) 0, 1344).ToArray(); + var result = hasher.Verify(header, solution); + + Assert.False(result); + } + [Fact] public void EquihashVerifier_Should_Throw_On_Null_Input() { diff --git a/src/MiningCore.Tests/MiningCore.Tests.csproj b/src/MiningCore.Tests/MiningCore.Tests.csproj index 47535b0fe..110233e33 100644 --- a/src/MiningCore.Tests/MiningCore.Tests.csproj +++ b/src/MiningCore.Tests/MiningCore.Tests.csproj @@ -18,10 +18,10 @@ - + - - + + diff --git a/src/MiningCore/Api/ApiServer.cs b/src/MiningCore/Api/ApiServer.cs index e39e00063..475aac2c3 100644 --- a/src/MiningCore/Api/ApiServer.cs +++ b/src/MiningCore/Api/ApiServer.cs @@ -39,7 +39,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Blockchain; using MiningCore.Configuration; using MiningCore.Extensions; +using MiningCore.Messaging; using MiningCore.Mining; +using MiningCore.Notifications.Messages; using MiningCore.Persistence; using MiningCore.Persistence.Model; using MiningCore.Persistence.Repositories; @@ -60,7 +62,8 @@ public ApiServer( IBlockRepository blocksRepo, IPaymentRepository paymentsRepo, IStatsRepository statsRepo, - IMasterClock clock) + IMasterClock clock, + IMessageBus messageBus) { Contract.RequiresNonNull(cf, nameof(cf)); Contract.RequiresNonNull(statsRepo, nameof(statsRepo)); @@ -68,6 +71,7 @@ public ApiServer( Contract.RequiresNonNull(paymentsRepo, nameof(paymentsRepo)); Contract.RequiresNonNull(mapper, nameof(mapper)); Contract.RequiresNonNull(clock, nameof(clock)); + Contract.RequiresNonNull(messageBus, nameof(messageBus)); this.cf = cf; this.statsRepo = statsRepo; @@ -76,6 +80,8 @@ public ApiServer( this.mapper = mapper; this.clock = clock; + messageBus.Listen().Subscribe(OnBlockNotification); + requestMap = new Dictionary> { { new Regex("^/api/pools$", RegexOptions.Compiled), GetPoolInfosAsync }, @@ -88,8 +94,10 @@ public ApiServer( { 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 }, + }; - // admin api + requestMapAdmin = new Dictionary> + { { new Regex("^/api/admin/forcegc$", RegexOptions.Compiled), HandleForceGcAsync }, { new Regex("^/api/admin/stats/gc$", RegexOptions.Compiled), HandleGcStatsAsync }, }; @@ -104,6 +112,7 @@ public ApiServer( private ClusterConfig clusterConfig; private IWebHost webHost; + private IWebHost webHostAdmin; private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); private static readonly Encoding encoding = new UTF8Encoding(false); @@ -115,6 +124,7 @@ public ApiServer( }; private readonly Dictionary> requestMap; + private readonly Dictionary> requestMapAdmin; private PoolConfig GetPool(HttpContext context, Match m) { @@ -153,6 +163,12 @@ private async Task SendJsonAsync(HttpContext context, object response) } } + private void OnBlockNotification(BlockNotification notification) + { + } + + #region API + private async Task HandleRequest(HttpContext context) { var request = context.Request; @@ -560,20 +576,13 @@ private async Task HandleGcStatsAsync(HttpContext context, Match m) Program.gcStats.GcGen0 = GC.CollectionCount(0); Program.gcStats.GcGen1 = GC.CollectionCount(1); Program.gcStats.GcGen2 = GC.CollectionCount(2); - Program.gcStats.MemAllocated = FormatUtil.FormatCapacity(GC.GetTotalMemory(false)); + Program.gcStats.MemAllocated = FormatUtil.FormatCapacity(GC.GetTotalMemory(false)); await SendJsonAsync(context, Program.gcStats); } -#region API-Surface - - public void Start(ClusterConfig clusterConfig) + private void StartApi(ClusterConfig clusterConfig) { - Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); - this.clusterConfig = clusterConfig; - - logger.Info(() => $"Launching ..."); - var address = clusterConfig.Api?.ListenAddress != null ? (clusterConfig.Api.ListenAddress != "*" ? IPAddress.Parse(clusterConfig.Api.ListenAddress) : IPAddress.Any) : IPAddress.Parse("127.0.0.1"); @@ -587,10 +596,76 @@ public void Start(ClusterConfig clusterConfig) webHost.Start(); - logger.Info(() => $"Online @ {address}:{port}"); + logger.Info(() => $"API Online @ {address}:{port}"); + } + + #endregion // API + + #region Admin API + + private async Task HandleRequestAdmin(HttpContext context) + { + var request = context.Request; + + try + { + logger.Debug(() => $"Processing request {request.GetEncodedPathAndQuery()}"); + + foreach (var path in requestMapAdmin.Keys) + { + var m = path.Match(request.Path); + + if (m.Success) + { + var handler = requestMapAdmin[path]; + await handler(context, m); + return; + } + } + + context.Response.StatusCode = 404; + } + + catch (Exception ex) + { + logger.Error(ex); + throw; + } + } + + private void StartAdminApi(ClusterConfig clusterConfig) + { + var address = clusterConfig.Api?.ListenAddress != null + ? (clusterConfig.Api.ListenAddress != "*" ? IPAddress.Parse(clusterConfig.Api.ListenAddress) : IPAddress.Any) + : IPAddress.Parse("127.0.0.1"); + + var port = clusterConfig.Api?.AdminPort ?? 4001; + + webHostAdmin = new WebHostBuilder() + .Configure(app => { app.Run(HandleRequestAdmin); }) + .UseKestrel(options => { options.Listen(address, port); }) + .Build(); + + webHostAdmin.Start(); + + logger.Info(() => $"Admin API Online @ {address}:{port}"); + } + + #endregion // Admin API + + #region API-Surface + + public void Start(ClusterConfig clusterConfig) + { + Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); + this.clusterConfig = clusterConfig; + + logger.Info(() => $"Launching ..."); + StartApi(clusterConfig); + StartAdminApi(clusterConfig); } -#endregion // API-Surface + #endregion // API-Surface } } diff --git a/src/MiningCore/Api/Responses/GetBlocksResponse.cs b/src/MiningCore/Api/Responses/GetBlocksResponse.cs index e2d38a1a8..a022a1c7d 100644 --- a/src/MiningCore/Api/Responses/GetBlocksResponse.cs +++ b/src/MiningCore/Api/Responses/GetBlocksResponse.cs @@ -34,6 +34,8 @@ public class Block public decimal Reward { get; set; } public string InfoLink { get; set; } public string Hash { get; set; } + public string Miner { get; set; } + public string Source { get; set; } public DateTime Created { get; set; } } } diff --git a/src/MiningCore/AutofacModule.cs b/src/MiningCore/AutofacModule.cs index f52b21163..65bd4be1c 100644 --- a/src/MiningCore/AutofacModule.cs +++ b/src/MiningCore/AutofacModule.cs @@ -36,6 +36,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Blockchain.ZCash; using MiningCore.Blockchain.ZCash.DaemonResponses; using MiningCore.Configuration; +using MiningCore.Messaging; using MiningCore.Mining; using MiningCore.Notifications; using MiningCore.Payments; @@ -65,6 +66,10 @@ protected override void Load(ContainerBuilder builder) ContractResolver = new CamelCasePropertyNamesContractResolver() }); + builder.RegisterType() + .AsImplementedInterfaces() + .SingleInstance(); + builder.RegisterType() .AsSelf() .SingleInstance(); @@ -80,6 +85,9 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType() .SingleInstance(); + builder.RegisterType() + .SingleInstance(); + builder.RegisterType() .SingleInstance(); diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs index 06301f461..7ea5d67f2 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs @@ -160,6 +160,8 @@ public class DevDonation {CoinType.XVG, "D5xPoHLM6HPkwWSqAweECTSQirJBmRjS8i" }, {CoinType.XMR, "475YVJbPHPedudkhrcNp1wDcLMTGYusGPF5fqE7XjnragVLPdqbCHBdZg3dF4dN9hXMjjvGbykS6a77dTAQvGrpiQqHp2eH"}, {CoinType.ETN, "etnkQJwBCjmR1MfkU8D355ZWxxLMhs8miPrtKHWN4U3uUowq9ugeKccVBoEG3n13n74us5AkT8tEoTog46w4HBgn8sMuBRhm9h"}, + {CoinType.RVN, "RQPJF65UoodQ2aZUkfnXoeX6gib3GNwm9u"}, + {CoinType.PGN, "PRm3ThUGfmU157NwcKzKBqWbgA2DPuFje1"}, }; } diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs index 2fd135fef..d0f4a6ed0 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs @@ -273,7 +273,9 @@ protected virtual byte[] SerializeHeader(byte[] coinbaseHash, uint nTime, uint n // build merkle-root var merkleRoot = mt.WithFirst(coinbaseHash); + #pragma warning disable 618 var blockHeader = new BlockHeader + #pragma warning restore 618 { Version = (int) BlockTemplate.Version, Bits = new Target(Encoders.Hex.DecodeData(BlockTemplate.Bits)), @@ -381,7 +383,7 @@ protected virtual byte[] SerializeBlock(byte[] header, byte[] coinbase) // POS coins require a zero byte appended to block which the daemon replaces with the signature if (isPoS) - bs.ReadWrite((byte) 0); + bs.ReadWrite((byte)0); return stream.ToArray(); } diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 54a643232..87194320b 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -26,6 +26,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Reactive; using System.Reactive.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Autofac; using MiningCore.Blockchain.Bitcoin.Configuration; @@ -37,6 +38,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Crypto.Hashing.Special; using MiningCore.DaemonInterface; using MiningCore.Extensions; +using MiningCore.JsonRpc; using MiningCore.Notifications; using MiningCore.Stratum; using MiningCore.Time; @@ -82,6 +84,7 @@ public BitcoinJobManager( protected BitcoinPoolPaymentProcessingConfigExtra extraPoolPaymentProcessingConfig; protected readonly IHashAlgorithm sha256s = new Sha256S(); protected readonly List validJobs = new List(); + protected DateTime? lastJobRebroadcast; protected IHashAlgorithm blockHasher; protected IHashAlgorithm coinbaseHasher; protected bool hasSubmitBlockMethod; @@ -109,75 +112,105 @@ protected virtual void SetupJobUpdates() var blockSubmission = blockSubmissionSubject.Synchronize(); var pollTimerRestart = blockSubmissionSubject.Synchronize(); - var triggers = new List> + var triggers = new List> { - blockSubmission.Select(x=> (false, "Block-submission")) + blockSubmission.Select(x=> (false, "Block-submission", (string) null)) }; - // collect ports - var zmq = poolConfig.Daemons - .Where(x => !string.IsNullOrEmpty(x.Extra.SafeExtensionDataAs()?.ZmqBlockNotifySocket)) - .ToDictionary(x => x, x => + if (extraPoolConfig?.BtStream == null) + { + // collect ports + var zmq = poolConfig.Daemons + .Where(x => !string.IsNullOrEmpty(x.Extra.SafeExtensionDataAs()?.ZmqBlockNotifySocket)) + .ToDictionary(x => x, x => + { + var extra = x.Extra.SafeExtensionDataAs(); + var topic = !string.IsNullOrEmpty(extra.ZmqBlockNotifyTopic?.Trim()) ? extra.ZmqBlockNotifyTopic.Trim() : BitcoinConstants.ZmqPublisherTopicBlockHash; + + return (Socket: extra.ZmqBlockNotifySocket, Topic: topic); + }); + + if (zmq.Count > 0) { - var extra = x.Extra.SafeExtensionDataAs(); - var topic = !string.IsNullOrEmpty(extra.ZmqBlockNotifyTopic?.Trim()) ? - extra.ZmqBlockNotifyTopic.Trim() : BitcoinConstants.ZmqPublisherTopicBlockHash; + logger.Info(() => $"[{LogCat}] Subscribing to ZMQ push-updates from {string.Join(", ", zmq.Values)}"); - return (Socket: extra.ZmqBlockNotifySocket, Topic: topic); - }); + var blockNotify = daemon.ZmqSubscribe(zmq, 2) + .Select(frames => + { + // We just take the second frame's raw data and turn it into a hex string. + // If that string changes, we got an update (DistinctUntilChanged) + var result = frames[1].ToHexString(); + frames.Dispose(); + return result; + }) + .DistinctUntilChanged() + .Select(_ => (false, "ZMQ pub/sub", (string) null)) + .Publish() + .RefCount(); + + pollTimerRestart = Observable.Merge( + blockSubmission, + blockNotify.Select(_ => Unit.Default)) + .Publish() + .RefCount(); + + triggers.Add(blockNotify); + } - if (zmq.Count > 0) - { - logger.Info(() => $"[{LogCat}] Subscribing to ZMQ push-updates from {string.Join(", ", zmq.Values)}"); + if (poolConfig.BlockRefreshInterval > 0) + { + // periodically update block-template + triggers.Add(Observable.Timer(TimeSpan.FromMilliseconds(poolConfig.BlockRefreshInterval)) + .TakeUntil(pollTimerRestart) + .Select(_ => (false, "RPC polling", (string) null)) + .Repeat()); + } - var blockNotify = daemon.ZmqSubscribe(zmq, 2) - .Select(frames => - { - // We just take the second frame's raw data and turn it into a hex string. - // If that string changes, we got an update (DistinctUntilChanged) - var result = frames[1].ToHexString(); - frames.Dispose(); - return result; - }) - .DistinctUntilChanged() - .Select(_ => (false, "ZMQ pub/sub")) - .Publish() - .RefCount(); - - pollTimerRestart = Observable.Merge( - blockSubmission, - blockNotify.Select(_ => Unit.Default)) - .Publish() - .RefCount(); - - triggers.Add(blockNotify); - } + else + { + // get initial blocktemplate + triggers.Add(Observable.Interval(TimeSpan.FromMilliseconds(1000)) + .Select(_ => (false, "Initial template", (string) null)) + .TakeWhile(_ => !hasInitialBlockTemplate)); + } - if (poolConfig.BlockRefreshInterval > 0) - { - // periodically update block-template - triggers.Add(Observable.Timer(TimeSpan.FromMilliseconds(poolConfig.BlockRefreshInterval)) + // periodically update transactions for current template + triggers.Add(Observable.Timer(jobRebroadcastTimeout) .TakeUntil(pollTimerRestart) - .Select(_ => (false, "RPC polling")) + .Select(_ => (true, "Job-Refresh", (string) null)) .Repeat()); } else { + var btStream = BtStreamSubscribe(extraPoolConfig.BtStream); + + if (poolConfig.JobRebroadcastTimeout > 0) + { + var interval = TimeSpan.FromSeconds(Math.Max(1, poolConfig.JobRebroadcastTimeout - 0.1d)); + + triggers.Add(btStream + .Select(json => (!lastJobRebroadcast.HasValue || (clock.Now - lastJobRebroadcast >= interval), "BT-Stream", json)) + .Publish() + .RefCount()); + } + + else + { + triggers.Add(btStream + .Select(json => (false, "BT-Stream", json)) + .Publish() + .RefCount()); + } + // get initial blocktemplate triggers.Add(Observable.Interval(TimeSpan.FromMilliseconds(1000)) - .Select(_ => (false, "Initial template")) + .Select(_ => (false, "Initial template", (string)null)) .TakeWhile(_ => !hasInitialBlockTemplate)); } - // periodically update transactions for current template - triggers.Add(Observable.Timer(jobRebroadcastTimeout) - .TakeUntil(pollTimerRestart) - .Select(_ => (true, "Job-Refresh")) - .Repeat()); - Jobs = Observable.Merge(triggers) - .Select(x => Observable.FromAsync(() => UpdateJob(x.Force, x.Via))) + .Select(x => Observable.FromAsync(() => UpdateJob(x.Force, x.Via, x.Data))) .Concat() .Where(x=> x.IsNew || x.Force) .Do(x => @@ -200,6 +233,18 @@ protected virtual async Task> GetBlockTemplateAsy return result; } + protected virtual DaemonResponse GetBlockTemplateFromJson(string json) + { + logger.LogInvoke(LogCat); + + var result = JsonConvert.DeserializeObject(json); + + return new DaemonResponse + { + Response = result.ResultAs(), + }; + } + protected virtual async Task ShowDaemonSyncProgressAsync() { if (hasLegacyDaemon) @@ -235,27 +280,32 @@ private async Task UpdateNetworkStatsAsync() { logger.LogInvoke(LogCat); - var results = await daemon.ExecuteBatchAnyAsync( - new DaemonCmd(BitcoinCommands.GetBlockchainInfo), - new DaemonCmd(BitcoinCommands.GetMiningInfo), - new DaemonCmd(BitcoinCommands.GetNetworkInfo) - ); - - if (results.Any(x => x.Error != null)) + try { - var errors = results.Where(x => x.Error != null).ToArray(); + var results = await daemon.ExecuteBatchAnyAsync( + new DaemonCmd(BitcoinCommands.GetMiningInfo), + new DaemonCmd(BitcoinCommands.GetNetworkInfo) + ); - if (errors.Any()) - logger.Warn(() => $"[{LogCat}] Error(s) refreshing network stats: {string.Join(", ", errors.Select(y => y.Error.Message))}"); - } + 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 networkInfoResponse = results[1].Response.ToObject(); - var infoResponse = results[0].Response.ToObject(); - var miningInfoResponse = results[1].Response.ToObject(); - var networkInfoResponse = results[2].Response.ToObject(); + BlockchainStats.NetworkHashrate = miningInfoResponse.NetworkHashps; + BlockchainStats.ConnectedPeers = networkInfoResponse.Connections; + } - BlockchainStats.BlockHeight = infoResponse.Blocks; - BlockchainStats.NetworkHashrate = miningInfoResponse.NetworkHashps; - BlockchainStats.ConnectedPeers = networkInfoResponse.Connections; + catch (Exception e) + { + logger.Error(e); + } } protected virtual async Task<(bool Accepted, string CoinbaseTransaction)> SubmitBlockAsync(Share share, string blockHex) @@ -355,25 +405,30 @@ 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)) + try { - var errors = results.Where(x => x.Error != null).ToArray(); + var results = await daemon.ExecuteBatchAnyAsync( + new DaemonCmd(BitcoinCommands.GetConnectionCount) + ); - if (errors.Any()) - logger.Warn(() => $"[{LogCat}] Error(s) refreshing network stats: {string.Join(", ", errors.Select(y => y.Error.Message))}"); - } + 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(); + var connectionCountResponse = results[0].Response.ToObject(); - BlockchainStats.BlockHeight = miningInfoResponse.Blocks; - //BlockchainStats.NetworkHashrate = miningInfoResponse.NetworkHashps; - BlockchainStats.ConnectedPeers = (int) (long) connectionCountResponse; + //BlockchainStats.NetworkHashrate = miningInfoResponse.NetworkHashps; + BlockchainStats.ConnectedPeers = (int)(long)connectionCountResponse; + } + + catch (Exception e) + { + logger.Error(e); + } } #region API-Surface @@ -571,7 +626,7 @@ protected override async Task AreDaemonsConnectedAsync() return response.Error == null && response.Response?.Connections > 0; } - protected override async Task EnsureDaemonsSynchedAsync() + protected override async Task EnsureDaemonsSynchedAsync(CancellationToken ct) { var syncPendingNotificationShown = false; @@ -597,11 +652,11 @@ protected override async Task EnsureDaemonsSynchedAsync() await ShowDaemonSyncProgressAsync(); // delay retry by 5s - await Task.Delay(5000); + await Task.Delay(5000, ct); } } - protected override async Task PostStartInitAsync() + protected override async Task PostStartInitAsync(CancellationToken ct) { var commands = new[] { @@ -643,7 +698,7 @@ protected override async Task PostStartInitAsync() if (!isPoS) { // bitcoincashd returns a different address than what was passed in - if(!validateAddressResponse.Address.StartsWith("bitcoincash:")) + if (!validateAddressResponse.Address.StartsWith("bitcoincash:")) poolAddressDestination = AddressToDestination(validateAddressResponse.Address); else poolAddressDestination = AddressToDestination(poolConfig.Address); @@ -686,6 +741,12 @@ protected override async Task PostStartInitAsync() else await UpdateNetworkStatsLegacyAsync(); + // Periodically update network stats + Observable.Interval(TimeSpan.FromMinutes(10)) + .Select(via => Observable.FromAsync(()=> !hasLegacyDaemon ? UpdateNetworkStatsAsync() : UpdateNetworkStatsLegacyAsync())) + .Concat() + .Subscribe(); + SetupCrypto(); SetupJobUpdates(); } @@ -712,13 +773,18 @@ protected virtual void ConfigureRewards() } } - protected virtual async Task<(bool IsNew, bool Force)> UpdateJob(bool forceUpdate, string via = null) + protected virtual async Task<(bool IsNew, bool Force)> UpdateJob(bool forceUpdate, string via = null, string json = null) { logger.LogInvoke(LogCat); try { - var response = await GetBlockTemplateAsync(); + if (forceUpdate) + lastJobRebroadcast = clock.Now; + + var response = string.IsNullOrEmpty(json) ? + await GetBlockTemplateAsync() : + GetBlockTemplateFromJson(json); // may happen if daemon is currently not connected to peers if (response.Error != null) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs index 1b0e68670..fe6640946 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs @@ -49,7 +49,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.XVG, - CoinType.PAK, CoinType.CANN)] + CoinType.PAK, CoinType.CANN, CoinType.RVN, CoinType.PGN)] public class BitcoinPayoutHandler : PayoutHandlerBase, IPayoutHandler { diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs index 529ef16b1..e9ee59d95 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs @@ -22,6 +22,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using AutoMapper; using MiningCore.Blockchain.Bitcoin.DaemonResponses; using MiningCore.Configuration; +using MiningCore.Messaging; using MiningCore.Notifications; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; @@ -34,7 +35,8 @@ 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.PAK, CoinType.CANN)] + CoinType.MOON, CoinType.XVG, CoinType.PAK, CoinType.CANN, + CoinType.RVN, CoinType.PGN)] public class BitcoinPool : BitcoinPoolBase, BlockTemplate> { public BitcoinPool(IComponentContext ctx, @@ -43,8 +45,9 @@ public BitcoinPool(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, + IMessageBus messageBus, NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, notificationService) + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) { } } diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs index f1c3d1602..5f409cbc6 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs @@ -23,12 +23,14 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Reactive; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; +using System.Threading; using System.Threading.Tasks; using Autofac; using AutoMapper; using MiningCore.Blockchain.Bitcoin.DaemonResponses; using MiningCore.Configuration; using MiningCore.JsonRpc; +using MiningCore.Messaging; using MiningCore.Mining; using MiningCore.Notifications; using MiningCore.Persistence; @@ -50,8 +52,9 @@ public BitcoinPoolBase(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, + IMessageBus messageBus, NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, notificationService) + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) { } @@ -106,7 +109,8 @@ protected virtual async Task OnAuthorizeAsync(StratumClient client, Timestamped< var context = client.GetContextAs(); var requestParams = request.ParamsAs(); var workerValue = requestParams?.Length > 0 ? requestParams[0] : null; - //var password = requestParams?.Length > 1 ? requestParams[1] : null; + var password = requestParams?.Length > 1 ? requestParams[1] : null; + var passParts = password?.Split(PasswordControlVarsSeparator); // extract worker/miner var split = workerValue?.Split('.'); @@ -118,11 +122,39 @@ protected virtual async Task OnAuthorizeAsync(StratumClient client, Timestamped< context.MinerName = minerName; context.WorkerName = workerName; - // respond - client.Respond(context.IsAuthorized, request.Id); + if (context.IsAuthorized) + { + // respond + client.Respond(context.IsAuthorized, request.Id); + + // log association + logger.Info(() => $"[{LogCat}] [{client.ConnectionId}] = {workerValue} = {client.RemoteEndpoint.Address}"); + + // extract control vars from password + var staticDiff = GetStaticDiffFromPassparts(passParts); + if (staticDiff.HasValue && + (context.VarDiff != null && staticDiff.Value >= context.VarDiff.Config.MinDiff || + context.VarDiff == null && staticDiff.Value > context.Difficulty)) + { + context.VarDiff = null; // disable vardiff + context.SetDifficulty(staticDiff.Value); + + client.Notify(BitcoinStratumMethods.SetDifficulty, new object[] { context.Difficulty }); + } + } + + else + { + // respond + client.RespondError(StratumError.UnauthorizedWorker, "Authorization failed", request.Id, context.IsAuthorized); + + // issue short-time ban if unauthorized to prevent DDos on daemon (validateaddress RPC) + logger.Info(() => $"[{LogCat}] [{client.ConnectionId}] Banning unauthorized worker for 60 sec"); - // log association - logger.Info(() => $"[{LogCat}] [{client.ConnectionId}] = {workerValue} = {client.RemoteEndpoint.Address}"); + banManager.Ban(client.RemoteEndpoint.Address, TimeSpan.FromSeconds(60)); + + DisconnectClient(client); + } } protected virtual async Task OnSubmitAsync(StratumClient client, Timestamped tsRequest) @@ -161,7 +193,7 @@ protected virtual async Task OnSubmitAsync(StratumClient client, Timestamped $"[{LogCat}] [{client.ConnectionId}] Share accepted: D={Math.Round(share.Difficulty, 3)}"); @@ -280,19 +312,19 @@ protected virtual BitcoinJobManager CreateJobManager() new TypedParameter(typeof(IExtraNonceProvider), new BitcoinExtraNonceProvider())); } - protected override async Task SetupJobManager() + protected override async Task SetupJobManager(CancellationToken ct) { manager = CreateJobManager(); manager.Configure(poolConfig, clusterConfig); - await manager.StartAsync(); + await manager.StartAsync(ct); if (poolConfig.EnableInternalStratum == true) { disposables.Add(manager.Jobs.Subscribe(OnNewJob)); // we need work before opening the gates - await manager.Jobs.Take(1).ToTask(); + await manager.Jobs.Take(1).ToTask(ct); } } @@ -363,7 +395,10 @@ public override double HashrateFromShares(double shares, double interval) (poolConfig.Coin.Type == CoinType.XVG && poolConfig.Coin.Algorithm.ToLower() == "lyra")) result *= 4; - return result; + if ((poolConfig.Coin.Type == CoinType.XVG && poolConfig.Coin.Algorithm.ToLower() == "x17")) + result *= 2.55; + + return result; } protected override void OnVarDiffUpdate(StratumClient client, double newDiff) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index 078b92171..0d88fb885 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -37,6 +37,8 @@ public class BitcoinProperties 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 x16r = new X16R(); + private static readonly IHashAlgorithm x16s = new X16S(); 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); @@ -94,6 +96,12 @@ public class BitcoinProperties private static readonly BitcoinCoinProperties vergeGroestlCoin = new BitcoinCoinProperties(1, sha256D, groestlMyriad, vergeBlockHasher, "Groestl-Myriad"); + private static readonly BitcoinCoinProperties x16rCoin = + new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, x16r, new DigestReverser(x16r), "X16-R"); + + private static readonly BitcoinCoinProperties x16sCoin = + new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, x16s, new DigestReverser(x16s), "X16-S"); + private static readonly Dictionary coinProperties = new Dictionary { // SHA256 @@ -135,6 +143,10 @@ public class BitcoinProperties // Neoscrypt { CoinType.GBX, neoScryptCoin }, { CoinType.CRC, neoScryptCoin }, + + // X16 + { CoinType.RVN, x16rCoin }, + { CoinType.PGN, x16sCoin }, }; public static BitcoinCoinProperties GetCoinProperties(CoinType coin, string algorithm = null) diff --git a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs index 7f3577292..212e184bf 100644 --- a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs +++ b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs @@ -18,6 +18,8 @@ portions of the Software. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +using MiningCore.Configuration; + namespace MiningCore.Blockchain.Bitcoin.Configuration { public class BitcoinPoolConfigExtra @@ -32,5 +34,10 @@ public class BitcoinPoolConfigExtra /// Set to true to limit RPC commands to old Bitcoin command set /// public bool? HasLegacyDaemon { get; set; } + + /// + /// Blocktemplate stream published via ZMQ + /// + public ZmqPubSubEndpointConfig BtStream { get; set; } } } diff --git a/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldPool.cs b/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldPool.cs index 100ed6120..5c6627b8d 100644 --- a/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldPool.cs +++ b/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldPool.cs @@ -24,6 +24,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Blockchain.ZCash; using MiningCore.Blockchain.ZCash.DaemonResponses; using MiningCore.Configuration; +using MiningCore.Messaging; using MiningCore.Notifications; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; @@ -41,8 +42,9 @@ public BitcoinGoldPool(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, + IMessageBus messageBus, NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, notificationService) + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) { } diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index 6aa1df797..7fed93d36 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -59,6 +59,8 @@ public static class CoinMetaData { CoinType.PAK, new Dictionary { { string.Empty, "https://chainz.cryptoid.info/pak/block.dws?{BlockHeightPH}.htm" } }}, { CoinType.FLO, new Dictionary { { string.Empty, "https://florincoin.info/block/{0}" } }}, { CoinType.CANN, new Dictionary { { string.Empty, $"https://chainz.cryptoid.info/cann/block.dws?{BlockHeightPH}.htm" }}}, + { CoinType.RVN, new Dictionary { { string.Empty, $"http://explorer.threeeyed.info/block/{BlockHashPH}" }}}, + { CoinType.PGN, new Dictionary { { string.Empty, $"http://explorer.pigeoncoin.org/block/{BlockHashPH}" }}}, }; public static readonly Dictionary TxInfoLinks = new Dictionary @@ -96,6 +98,8 @@ public static class CoinMetaData { CoinType.PAK, "https://chainz.cryptoid.info/pak/tx.dws?{0}.htm" }, { CoinType.FLO, "https://florincoin.info/tx/{0}" }, { CoinType.CANN, "https://chainz.cryptoid.info/cann/tx.dws?{0}.htm" }, + { CoinType.RVN, "http://explorer.threeeyed.info/tx/{0}" }, + { CoinType.PGN, "http://explorer.pigeoncoin.org/tx/{0}" }, }; public static readonly Dictionary AddressInfoLinks = new Dictionary @@ -130,9 +134,11 @@ public static class CoinMetaData { CoinType.PAK, "https://chainz.cryptoid.info/pak/address.dws?{0}.htm" }, { CoinType.FLO, "https://florincoin.info/address/{0}" }, { CoinType.CANN, "https://chainz.cryptoid.info/cann/address.dws?{0}.htm" }, + { CoinType.RVN, "http://explorer.threeeyed.info/address/{0}" }, + { CoinType.PGN, "http://explorer.pigeoncoin.org/address/{0}" }, }; - private const string Ethash = "Dagger-Hashimoto"; + private const string Ethash = "Ethash"; private const string Cryptonight = "Cryptonight"; private const string CryptonightLight = "Cryptonight-Light"; @@ -171,6 +177,8 @@ public static class CoinMetaData { CoinType.PAK, BitcoinProperties.GetAlgorithm }, { CoinType.FLO, BitcoinProperties.GetAlgorithm }, { CoinType.CANN, BitcoinProperties.GetAlgorithm }, + { CoinType.RVN, BitcoinProperties.GetAlgorithm }, + { CoinType.PGN, BitcoinProperties.GetAlgorithm }, }; } } diff --git a/src/MiningCore/Blockchain/Dash/DashPool.cs b/src/MiningCore/Blockchain/Dash/DashPool.cs index 2d57d8844..875fb851f 100644 --- a/src/MiningCore/Blockchain/Dash/DashPool.cs +++ b/src/MiningCore/Blockchain/Dash/DashPool.cs @@ -22,6 +22,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using AutoMapper; using MiningCore.Blockchain.Bitcoin; using MiningCore.Configuration; +using MiningCore.Messaging; using MiningCore.Notifications; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; @@ -39,8 +40,9 @@ public DashPool(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, + IMessageBus messageBus, NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, notificationService) + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) { } } diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumConstants.cs b/src/MiningCore/Blockchain/Ethereum/EthereumConstants.cs index 3e684c727..858cfa6d5 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumConstants.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumConstants.cs @@ -79,6 +79,7 @@ public enum ParityChainType Expanse, Ellaism, CallistoTestnet, // Callisto (CLO) v3 Testnet + Callisto, // Callisto (CLO) v3 Testnet Unknown = -1, } diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs index 66307f0bd..6e3de9d8d 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs @@ -63,7 +63,9 @@ private void RegisterNonce(StratumClient worker, string nonce) // assemble full-nonce var context = worker.GetContextAs(); var fullNonceHex = context.ExtraNonce1 + nonce; - var fullNonce = ulong.Parse(fullNonceHex, NumberStyles.HexNumber); + + if (!ulong.TryParse(fullNonceHex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var fullNonce)) + throw new StratumException(StratumError.MinusOne, "bad nonce " + fullNonceHex); // get dag for block var dag = await ethash.GetDagAsync(BlockTemplate.Height, logger); diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs index 044a3345b..c86bda8e5 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs @@ -25,6 +25,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Net; using System.Reactive.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Autofac; using MiningCore.Blockchain.Bitcoin; @@ -264,31 +265,35 @@ private async Task UpdateNetworkStatsAsync() { logger.LogInvoke(LogCat); - var commands = new[] + try { - new DaemonCmd(EC.GetBlockByNumber, new[] { (object) "latest", true }), - new DaemonCmd(EC.GetPeerCount), - }; + var commands = new[] + { + new DaemonCmd(EC.GetPeerCount), + }; - var results = await daemon.ExecuteBatchAnyAsync(commands); + var results = await daemon.ExecuteBatchAnyAsync(commands); - if (results.Any(x => x.Error != null)) - { - var errors = results.Where(x => x.Error != null) - .ToArray(); + 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))})"); - } + if (errors.Any()) + logger.Warn(() => $"[{LogCat}] Error(s) refreshing network stats: {string.Join(", ", errors.Select(y => y.Error.Message))})"); + } - // extract results - var block = results[0].Response.ToObject(); - var peerCount = results[1].Response.ToObject().IntegralFromHex(); + // extract results + var peerCount = results[0].Response.ToObject().IntegralFromHex(); - BlockchainStats.BlockHeight = block.Height.HasValue ? (long)block.Height.Value : -1; - BlockchainStats.NetworkDifficulty = block.Difficulty.IntegralFromHex(); - BlockchainStats.NetworkHashrate = 0; // TODO - BlockchainStats.ConnectedPeers = peerCount; + BlockchainStats.NetworkHashrate = 0; // TODO + BlockchainStats.ConnectedPeers = peerCount; + } + + catch (Exception e) + { + logger.Error(e); + } } private async Task SubmitBlockAsync(Share share, string fullNonceHex, string headerHash, string mixHash) @@ -477,7 +482,7 @@ protected override async Task AreDaemonsConnectedAsync() return response.Error == null && response.Response.IntegralFromHex() > 0; } - protected override async Task EnsureDaemonsSynchedAsync() + protected override async Task EnsureDaemonsSynchedAsync(CancellationToken ct) { var syncPendingNotificationShown = false; @@ -503,11 +508,11 @@ protected override async Task EnsureDaemonsSynchedAsync() await ShowDaemonSyncProgressAsync(); // delay retry by 5s - await Task.Delay(5000); + await Task.Delay(5000, ct); } } - protected override async Task PostStartInitAsync() + protected override async Task PostStartInitAsync(CancellationToken ct) { var commands = new[] { @@ -540,8 +545,8 @@ protected override async Task PostStartInitAsync() var parityChain = results[4].Response.ToObject(); // ensure pool owns wallet - if (clusterConfig.PaymentProcessing?.Enabled == true && !accounts.Contains(poolConfig.Address) || coinbase != poolConfig.Address) - logger.ThrowLogPoolStartupException($"Daemon does not own pool-address '{poolConfig.Address}'", LogCat); + //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); @@ -554,6 +559,12 @@ protected override async Task PostStartInitAsync() await UpdateNetworkStatsAsync(); + // Periodically update network stats + Observable.Interval(TimeSpan.FromMinutes(10)) + .Select(via => Observable.FromAsync(UpdateNetworkStatsAsync)) + .Concat() + .Subscribe(); + if (poolConfig.EnableInternalStratum == true) { // make sure we have a current DAG @@ -572,7 +583,7 @@ protected override async Task PostStartInitAsync() } logger.Info(() => $"[{LogCat}] Waiting for first valid block template"); - await Task.Delay(TimeSpan.FromSeconds(5)); + await Task.Delay(TimeSpan.FromSeconds(5), ct); } SetupJobUpdates(); diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumPayoutHandler.cs b/src/MiningCore/Blockchain/Ethereum/EthereumPayoutHandler.cs index b24dd678c..6ad206bd8 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumPayoutHandler.cs @@ -356,6 +356,7 @@ internal static decimal GetBaseBlockReward(ParityChainType chainType, ulong heig return EthereumConstants.ByzantiumBlockReward; case ParityChainType.CallistoTestnet: + case ParityChainType.Callisto: return CallistoConstants.BaseRewardInitial * (1.0m - CallistoConstants.TreasuryPercent); default: diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs index 2bb2a1e30..a385bb4b2 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs @@ -23,6 +23,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Reactive; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; +using System.Threading; using System.Threading.Tasks; using Autofac; using AutoMapper; @@ -30,6 +31,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Configuration; using MiningCore.Extensions; using MiningCore.JsonRpc; +using MiningCore.Messaging; using MiningCore.Mining; using MiningCore.Notifications; using MiningCore.Persistence; @@ -50,8 +52,9 @@ public EthereumPool(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, + IMessageBus messageBus, NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, notificationService) + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) { } @@ -185,7 +188,7 @@ private async Task OnSubmitAsync(StratumClient client, Timestamped(); manager.Configure(poolConfig, clusterConfig); - await manager.StartAsync(); + await manager.StartAsync(ct); if (poolConfig.EnableInternalStratum == true) { disposables.Add(manager.Jobs.Subscribe(OnNewJob)); // we need work before opening the gates - await manager.Jobs.Take(1).ToTask(); + await manager.Jobs.Take(1).ToTask(ct); } } diff --git a/src/MiningCore/Blockchain/Flo/FloJobManager.cs b/src/MiningCore/Blockchain/Flo/FloJobManager.cs index 71155a353..7e7634705 100644 --- a/src/MiningCore/Blockchain/Flo/FloJobManager.cs +++ b/src/MiningCore/Blockchain/Flo/FloJobManager.cs @@ -57,13 +57,18 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi base.Configure(poolConfig, clusterConfig); } - protected override async Task<(bool IsNew, bool Force)> UpdateJob(bool forceUpdate, string via = null) + protected override async Task<(bool IsNew, bool Force)> UpdateJob(bool forceUpdate, string via = null, string json = null) { logger.LogInvoke(LogCat); try { - var response = await GetBlockTemplateAsync(); + if (forceUpdate) + lastJobRebroadcast = clock.Now; + + var response = string.IsNullOrEmpty(json) ? + await GetBlockTemplateAsync() : + GetBlockTemplateFromJson(json); // may happen if daemon is currently not connected to peers if (response.Error != null) diff --git a/src/MiningCore/Blockchain/Flo/FloPool.cs b/src/MiningCore/Blockchain/Flo/FloPool.cs index 9cb0e4f29..fe72d0662 100644 --- a/src/MiningCore/Blockchain/Flo/FloPool.cs +++ b/src/MiningCore/Blockchain/Flo/FloPool.cs @@ -25,6 +25,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Blockchain.Flo.Configuration; using MiningCore.Configuration; using MiningCore.Extensions; +using MiningCore.Messaging; using MiningCore.Notifications; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; @@ -44,8 +45,9 @@ public FloPool(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, + IMessageBus messageBus, NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, notificationService) + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) { } diff --git a/src/MiningCore/Blockchain/JobManagerBase.cs b/src/MiningCore/Blockchain/JobManagerBase.cs index 41cd484ae..07edce4d7 100644 --- a/src/MiningCore/Blockchain/JobManagerBase.cs +++ b/src/MiningCore/Blockchain/JobManagerBase.cs @@ -20,15 +20,21 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Globalization; +using System.IO; +using System.IO.Compression; using System.Reactive; +using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; +using System.Text; using System.Threading; using System.Threading.Tasks; using Autofac; using MiningCore.Configuration; using MiningCore.Extensions; using MiningCore.Util; +using NetMQ; +using NetMQ.Sockets; using NLog; using Contract = MiningCore.Contracts.Contract; @@ -53,18 +59,19 @@ protected JobManagerBase(IComponentContext ctx) protected PoolConfig poolConfig; protected bool hasInitialBlockTemplate = false; protected Subject blockSubmissionSubject = new Subject(); + protected TimeSpan btStreamReceiveTimeout = TimeSpan.FromSeconds(60 * 10); protected virtual string LogCat { get; } = "Job Manager"; protected abstract void ConfigureDaemons(); - protected virtual async Task StartDaemonAsync() + protected virtual async Task StartDaemonAsync(CancellationToken ct) { while(!await AreDaemonsHealthyAsync()) { logger.Info(() => $"[{LogCat}] Waiting for daemons to come online ..."); - await Task.Delay(TimeSpan.FromSeconds(10)); + await Task.Delay(TimeSpan.FromSeconds(10), ct); } logger.Info(() => $"[{LogCat}] All daemons online"); @@ -73,7 +80,7 @@ protected virtual async Task StartDaemonAsync() { logger.Info(() => $"[{LogCat}] Waiting for daemons to connect to peers ..."); - await Task.Delay(TimeSpan.FromSeconds(10)); + await Task.Delay(TimeSpan.FromSeconds(10), ct); } } @@ -88,10 +95,93 @@ protected string NextJobId(string format = null) return value.ToStringHex8(); } + protected IObservable BtStreamSubscribe(ZmqPubSubEndpointConfig config) + { + 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(config.Url); + subSocket.Subscribe(config.Topic); + + logger.Debug($"Subscribed to {config.Url}/{config.Topic}"); + + while(!tcs.IsCancellationRequested) + { + var msg = (NetMQMessage) null; + + if (!subSocket.TryReceiveMultipartMessage(btStreamReceiveTimeout, ref msg, 4)) + { + logger.Warn(() => $"Timeout receiving message from {config.Url}. Reconnecting ..."); + break; + } + + // extract frames + var topic = msg.Pop().ConvertToString(Encoding.UTF8); + var flags = msg.Pop().ConvertToInt32(); + var data = msg.Pop().ToByteArray(); + var timestamp = msg.Pop().ConvertToInt64(); + + // compressed + if ((flags & 1) == 1) + { + using(var stm = new MemoryStream(data)) + { + using(var stmOut = new MemoryStream()) + { + using(var ds = new DeflateStream(stm, CompressionMode.Decompress)) + { + ds.CopyTo(stmOut); + } + + data = stmOut.ToArray(); + } + } + } + + // convert + var json = Encoding.UTF8.GetString(data); + + obs.OnNext(json); + } + } + } + + 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(); + }); + })) + .Publish() + .RefCount(); + } + protected abstract Task AreDaemonsHealthyAsync(); protected abstract Task AreDaemonsConnectedAsync(); - protected abstract Task EnsureDaemonsSynchedAsync(); - protected abstract Task PostStartInitAsync(); + protected abstract Task EnsureDaemonsSynchedAsync(CancellationToken ct); + protected abstract Task PostStartInitAsync(CancellationToken ct); #region API-Surface @@ -107,15 +197,15 @@ public virtual void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig ConfigureDaemons(); } - public async Task StartAsync() + public async Task StartAsync(CancellationToken ct) { Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); logger.Info(() => $"[{LogCat}] Launching ..."); - await StartDaemonAsync(); - await EnsureDaemonsSynchedAsync(); - await PostStartInitAsync(); + await StartDaemonAsync(ct); + await EnsureDaemonsSynchedAsync(ct); + await PostStartInitAsync(ct); logger.Info(() => $"[{LogCat}] Online"); } diff --git a/src/MiningCore/Blockchain/Monero/Configuration/MoneroPoolConfigExtra.cs b/src/MiningCore/Blockchain/Monero/Configuration/MoneroPoolConfigExtra.cs new file mode 100644 index 000000000..2f69635e3 --- /dev/null +++ b/src/MiningCore/Blockchain/Monero/Configuration/MoneroPoolConfigExtra.cs @@ -0,0 +1,32 @@ +/* +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.Configuration; + +namespace MiningCore.Blockchain.Monero.Configuration +{ + public class MoneroPoolConfigExtra + { + /// + /// Blocktemplate stream published via ZMQ + /// + public ZmqPubSubEndpointConfig BtStream { get; set; } + } +} diff --git a/src/MiningCore/Blockchain/Monero/MoneroJob.cs b/src/MiningCore/Blockchain/Monero/MoneroJob.cs index 7afb65d85..f18fb3b0e 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJob.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJob.cs @@ -46,15 +46,21 @@ public MoneroJob(GetBlockTemplateResponse blockTemplate, byte[] instanceId, stri switch (poolConfig.Coin.Type) { case CoinType.AEON: - hashSlow = (buf, variant)=> LibCryptonote.CryptonightHashSlowLite(buf); + hashSlow = LibCryptonote.CryptonightHashSlowLite; break; case CoinType.XMR: - hashSlow = LibCryptonote.CryptonightHashSlow; + hashSlow = buf=> + { + // PoW variant + var variant = buf[0] >= 7 ? buf[0] - 6 : 0; + + return LibCryptonote.CryptonightHashSlow(buf, variant); + }; break; default: - hashSlow = (buf, variant) => LibCryptonote.CryptonightHashSlow(buf, 0); + hashSlow = buf => LibCryptonote.CryptonightHashSlow(buf, 0); break; } @@ -62,7 +68,7 @@ public MoneroJob(GetBlockTemplateResponse blockTemplate, byte[] instanceId, stri PrepareBlobTemplate(instanceId); } - private readonly Func> hashSlow; + private readonly Func> hashSlow; private byte[] blobTemplate; private uint extraNonce; @@ -166,11 +172,8 @@ public void PrepareWorkerJob(MoneroWorkerJob workerJob, out string blob, out str if (blobConverted == null) throw new StratumException(StratumError.MinusOne, "malformed blob"); - // PoW variant - var hashVariant = blobConverted[0] >= 7 ? blobConverted[0] - 6 : 0; - // hash it - using (var hashSeg = hashSlow(blobConverted, hashVariant)) + using (var hashSeg = hashSlow(blobConverted)) { var hash = hashSeg.ToHexString(); if (hash != workerHash) diff --git a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs index ba53f1926..606a11b70 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs @@ -19,13 +19,18 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; +using System.Buffers; using System.Collections.Generic; +using System.IO; +using System.IO.Compression; using System.Linq; using System.Net; using System.Reactive; +using System.Reactive.Disposables; using System.Reactive.Linq; using System.Security.Cryptography; using System.Text; +using System.Threading; using System.Threading.Tasks; using Autofac; using MiningCore.Blockchain.Bitcoin; @@ -33,14 +38,18 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Blockchain.Monero.DaemonRequests; using MiningCore.Blockchain.Monero.DaemonResponses; using MiningCore.Blockchain.Monero.StratumRequests; +using MiningCore.Buffers; using MiningCore.Configuration; using MiningCore.DaemonInterface; using MiningCore.Extensions; +using MiningCore.JsonRpc; using MiningCore.Native; using MiningCore.Notifications; using MiningCore.Stratum; using MiningCore.Time; using MiningCore.Util; +using NetMQ; +using NetMQ.Sockets; using Newtonsoft.Json; using NLog; using Contract = MiningCore.Contracts.Contract; @@ -78,16 +87,19 @@ public MoneroJobManager( private readonly NotificationService notificationService; private readonly IMasterClock clock; private MoneroNetworkType networkType; + private MoneroPoolConfigExtra extraPoolConfig; private UInt64 poolAddressBase58Prefix; private DaemonEndpointConfig[] walletDaemonEndpoints; - protected async Task UpdateJob(string via = null) + protected async Task UpdateJob(string via = null, string json = null) { logger.LogInvoke(LogCat); try { - var response = await GetBlockTemplateAsync(); + var response = string.IsNullOrEmpty(json) ? + await GetBlockTemplateAsync() : + GetBlockTemplateFromJson(json); // may happen if daemon is currently not connected to peers if (response.Error != null) @@ -142,6 +154,18 @@ private async Task> GetBlockTemplateAsy return await daemon.ExecuteCmdAnyAsync(MC.GetBlockTemplate, request); } + private DaemonResponse GetBlockTemplateFromJson(string json) + { + logger.LogInvoke(LogCat); + + var result = JsonConvert.DeserializeObject(json); + + return new DaemonResponse + { + Response = result.ResultAs(), + }; + } + private async Task ShowDaemonSyncProgressAsync() { var infos = await daemon.ExecuteCmdAllAsync(MC.GetInfo); @@ -163,17 +187,23 @@ private async Task UpdateNetworkStatsAsync() { logger.LogInvoke(LogCat); - var infoResponse = await daemon.ExecuteCmdAnyAsync(MC.GetInfo); + try + { + var infoResponse = await daemon.ExecuteCmdAnyAsync(MC.GetInfo); - if (infoResponse.Error != null) - logger.Warn(() => $"[{LogCat}] Error(s) refreshing network stats: {infoResponse.Error.Message} (Code {infoResponse.Error.Code})"); + if (infoResponse.Error != null) + logger.Warn(() => $"[{LogCat}] Error(s) refreshing network stats: {infoResponse.Error.Message} (Code {infoResponse.Error.Code})"); - var info = infoResponse.Response.ToObject(); + var info = infoResponse.Response.ToObject(); + + BlockchainStats.NetworkHashrate = info.Target > 0 ? (double)info.Difficulty / info.Target : 0; + BlockchainStats.ConnectedPeers = info.OutgoingConnectionsCount + info.IncomingConnectionsCount; + } - BlockchainStats.BlockHeight = (int)info.Height; - BlockchainStats.NetworkDifficulty = info.Difficulty; - BlockchainStats.NetworkHashrate = info.Target > 0 ? (double)info.Difficulty / info.Target : 0; - BlockchainStats.ConnectedPeers = info.OutgoingConnectionsCount + info.IncomingConnectionsCount; + catch (Exception e) + { + logger.Error(e); + } } private async Task SubmitBlockAsync(Share share, string blobHex, string blobHash) @@ -204,6 +234,7 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi logger = LogUtil.GetPoolScopedLogger(typeof(JobManagerBase), poolConfig); this.poolConfig = poolConfig; this.clusterConfig = clusterConfig; + extraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); // extract standard daemon endpoints daemonEndpoints = poolConfig.Daemons @@ -391,7 +422,7 @@ protected override async Task AreDaemonsConnectedAsync() (response.Response.OutgoingConnectionsCount + response.Response.IncomingConnectionsCount) > 0; } - protected override async Task EnsureDaemonsSynchedAsync() + protected override async Task EnsureDaemonsSynchedAsync(CancellationToken ct) { var syncPendingNotificationShown = false; @@ -423,11 +454,11 @@ protected override async Task EnsureDaemonsSynchedAsync() await ShowDaemonSyncProgressAsync(); // delay retry by 5s - await Task.Delay(5000); + await Task.Delay(5000, ct); } } - protected override async Task PostStartInitAsync() + protected override async Task PostStartInitAsync(CancellationToken ct) { var infoResponse = await daemon.ExecuteCmdAnyAsync(MC.GetInfo); @@ -475,6 +506,12 @@ protected override async Task PostStartInitAsync() await UpdateNetworkStatsAsync(); + // Periodically update network stats + Observable.Interval(TimeSpan.FromMinutes(1)) + .Select(via => Observable.FromAsync(UpdateNetworkStatsAsync)) + .Concat() + .Subscribe(); + SetupJobUpdates(); } @@ -503,75 +540,90 @@ protected virtual void SetupJobUpdates() var blockSubmission = blockSubmissionSubject.Synchronize(); var pollTimerRestart = blockSubmissionSubject.Synchronize(); - var triggers = new List> + var triggers = new List> { - blockSubmission.Select(x=> "Block-submission") + blockSubmission.Select(x => ("Block-submission", (string) null)) }; - // collect ports - var zmq = poolConfig.Daemons - .Where(x => !string.IsNullOrEmpty(x.Extra.SafeExtensionDataAs()?.ZmqBlockNotifySocket)) - .ToDictionary(x => x, x => - { - var extra = x.Extra.SafeExtensionDataAs(); - var topic = !string.IsNullOrEmpty(extra.ZmqBlockNotifyTopic.Trim()) ? - extra.ZmqBlockNotifyTopic.Trim() : BitcoinConstants.ZmqPublisherTopicBlockHash; - - return (Socket: extra.ZmqBlockNotifySocket, Topic: topic); - }); - - if (zmq.Count > 0) + if (extraPoolConfig?.BtStream == null) { - logger.Info(() => $"[{LogCat}] Subscribing to ZMQ push-updates from {string.Join(", ", zmq.Values)}"); - - var blockNotify = daemon.ZmqSubscribe(zmq, 2) - .Select(frames => + // collect ports + var zmq = poolConfig.Daemons + .Where(x => !string.IsNullOrEmpty(x.Extra.SafeExtensionDataAs()?.ZmqBlockNotifySocket)) + .ToDictionary(x => x, x => { - // We just take the second frame's raw data and turn it into a hex string. - // If that string changes, we got an update (DistinctUntilChanged) - var result = frames[1].ToHexString(); - frames.Dispose(); - return result; - }) - .DistinctUntilChanged() - .Select(_ => "ZMQ pub/sub") - .Publish() - .RefCount(); + var extra = x.Extra.SafeExtensionDataAs(); + var topic = !string.IsNullOrEmpty(extra.ZmqBlockNotifyTopic.Trim()) ? extra.ZmqBlockNotifyTopic.Trim() : BitcoinConstants.ZmqPublisherTopicBlockHash; - pollTimerRestart = Observable.Merge( - blockSubmission, - blockNotify.Select(_ => Unit.Default)) - .Publish() - .RefCount(); + return (Socket: extra.ZmqBlockNotifySocket, Topic: topic); + }); - triggers.Add(blockNotify); - } + if (zmq.Count > 0) + { + logger.Info(() => $"[{LogCat}] Subscribing to ZMQ push-updates from {string.Join(", ", zmq.Values)}"); + + var blockNotify = daemon.ZmqSubscribe(zmq, 2) + .Select(frames => + { + // We just take the second frame's raw data and turn it into a hex string. + // If that string changes, we got an update (DistinctUntilChanged) + var result = frames[1].ToHexString(); + frames.Dispose(); + return result; + }) + .DistinctUntilChanged() + .Select(_ => ("ZMQ pub/sub", (string) null)) + .Publish() + .RefCount(); + + pollTimerRestart = Observable.Merge( + blockSubmission, + blockNotify.Select(_ => Unit.Default)) + .Publish() + .RefCount(); + + triggers.Add(blockNotify); + } - if (poolConfig.BlockRefreshInterval > 0) - { - // periodically update block-template - triggers.Add(Observable.Timer(TimeSpan.FromMilliseconds(poolConfig.BlockRefreshInterval)) - .TakeUntil(pollTimerRestart) - .Select(_ => "RPC polling") - .Repeat()); + if (poolConfig.BlockRefreshInterval > 0) + { + // periodically update block-template + triggers.Add(Observable.Timer(TimeSpan.FromMilliseconds(poolConfig.BlockRefreshInterval)) + .TakeUntil(pollTimerRestart) + .Select(_ => ("RPC polling", (string) null)) + .Repeat()); + } + + else + { + // get initial blocktemplate + triggers.Add(Observable.Interval(TimeSpan.FromMilliseconds(1000)) + .Select(_ => ("Initial template", (string)null)) + .TakeWhile(_ => !hasInitialBlockTemplate)); + } } else { + triggers.Add(BtStreamSubscribe(extraPoolConfig.BtStream) + .Select(json => ("BT-Stream", json)) + .Publish() + .RefCount()); + // get initial blocktemplate triggers.Add(Observable.Interval(TimeSpan.FromMilliseconds(1000)) - .Select(_ => "Initial template") - .TakeWhile(_=> !hasInitialBlockTemplate)); + .Select(_ => ("Initial template", (string)null)) + .TakeWhile(_ => !hasInitialBlockTemplate)); } Blocks = Observable.Merge(triggers) - .Select(via => Observable.FromAsync(() => UpdateJob(via))) - .Concat() - .Where(isNew => isNew) - .Do(_=> hasInitialBlockTemplate = true) - .Select(_ => Unit.Default) - .Publish() - .RefCount(); + .Select(x => Observable.FromAsync(() => UpdateJob(x.Via, x.Data))) + .Concat() + .Where(isNew => isNew) + .Do(_ => hasInitialBlockTemplate = true) + .Select(_ => Unit.Default) + .Publish() + .RefCount(); } #endregion // Overrides diff --git a/src/MiningCore/Blockchain/Monero/MoneroPool.cs b/src/MiningCore/Blockchain/Monero/MoneroPool.cs index 5ee408240..7cb78e6db 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPool.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPool.cs @@ -32,6 +32,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Blockchain.Monero.StratumResponses; using MiningCore.Configuration; using MiningCore.JsonRpc; +using MiningCore.Messaging; using MiningCore.Mining; using MiningCore.Notifications; using MiningCore.Persistence; @@ -51,8 +52,9 @@ public MoneroPool(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, + IMessageBus messageBus, NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, notificationService) + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) { } @@ -245,7 +247,7 @@ private async Task OnSubmitAsync(StratumClient client, Timestamped $"[{LogCat}] [{client.ConnectionId}] Share accepted: D={Math.Round(share.Difficulty, 3)}"); @@ -306,19 +308,19 @@ private void OnNewJob() #region Overrides - protected override async Task SetupJobManager() + protected override async Task SetupJobManager(CancellationToken ct) { manager = ctx.Resolve(); manager.Configure(poolConfig, clusterConfig); - await manager.StartAsync(); + await manager.StartAsync(ct); if (poolConfig.EnableInternalStratum == true) { disposables.Add(manager.Blocks.Subscribe(_ => OnNewJob())); // we need work before opening the gates - await manager.Blocks.Take(1).ToTask(); + await manager.Blocks.Take(1).ToTask(ct); } } diff --git a/src/MiningCore/Blockchain/Straks/StraksPool.cs b/src/MiningCore/Blockchain/Straks/StraksPool.cs index 9dc98f196..b3481e193 100644 --- a/src/MiningCore/Blockchain/Straks/StraksPool.cs +++ b/src/MiningCore/Blockchain/Straks/StraksPool.cs @@ -23,6 +23,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Blockchain.Bitcoin; using MiningCore.Blockchain.Straks.DaemonResponses; using MiningCore.Configuration; +using MiningCore.Messaging; using MiningCore.Notifications; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; @@ -40,8 +41,9 @@ public StraksPool(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, + IMessageBus messageBus, NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, notificationService) + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) { } } diff --git a/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs b/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs index ba7d0c6dd..531263f01 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs @@ -266,7 +266,7 @@ public class ZCashConstants TreasuryRewardAddresses = new[] { - "zrRBQ5heytPMN5nY3ssPf3cG4jocXeD8fm1" + "zrRBQ5heytPMN5nY3ssPf3cG4jocXeD8fm1", "zrRBQ5heytPMN5nY3ssPf3cG4jocXeD8fm1", "zrRBQ5heytPMN5nY3ssPf3cG4jocXeD8fm1", "zrRBQ5heytPMN5nY3ssPf3cG4jocXeD8fm1" } } }, diff --git a/src/MiningCore/Blockchain/ZCash/ZCashJob.cs b/src/MiningCore/Blockchain/ZCash/ZCashJob.cs index 2049d9b05..bb55c9008 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashJob.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashJob.cs @@ -390,7 +390,7 @@ protected string GetTreasuryRewardAddress() var index = (int) Math.Floor((BlockTemplate.Height - coinbaseTxConfig.TreasuryRewardStartBlockHeight) / coinbaseTxConfig.TreasuryRewardAddressChangeInterval % coinbaseTxConfig.TreasuryRewardAddresses.Length); - var address = coinbaseTxConfig.FoundersRewardAddresses[index]; + var address = coinbaseTxConfig.TreasuryRewardAddresses[index]; return address; } diff --git a/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs b/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs index e091c411e..16c30b8d8 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Linq; +using System.Reactive; using System.Threading.Tasks; using Autofac; using MiningCore.Blockchain.Bitcoin; @@ -164,6 +165,8 @@ public override async Task SubmitShareAsync(StratumClient worker, object { logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{share.BlockHash}] submitted by {minerName}"); + blockSubmissionSubject.OnNext(Unit.Default); + // persist the coinbase transaction-hash to allow the payment processor // to verify later on that the pool has received the reward for the block share.TransactionConfirmationData = acceptResponse.CoinbaseTransaction; diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPool.cs b/src/MiningCore/Blockchain/ZCash/ZCashPool.cs index 651ca2b07..65466320c 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPool.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPool.cs @@ -23,6 +23,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Blockchain.ZCash.Configuration; using MiningCore.Configuration; using MiningCore.Extensions; +using MiningCore.Messaging; using MiningCore.Notifications; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; @@ -41,8 +42,9 @@ public ZCashPool(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, + IMessageBus messageBus, NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, notificationService) + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) { } diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs b/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs index 8ccfdb678..4f8b56c5d 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs @@ -23,6 +23,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Globalization; using System.Linq; using System.Reactive; +using System.Threading; using System.Threading.Tasks; using Autofac; using AutoMapper; @@ -31,6 +32,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Configuration; using MiningCore.Extensions; using MiningCore.JsonRpc; +using MiningCore.Messaging; using MiningCore.Notifications; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; @@ -51,8 +53,9 @@ public ZCashPoolBase(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, + IMessageBus messageBus, NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, notificationService) + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) { } @@ -67,10 +70,11 @@ protected override BitcoinJobManager CreateJobManager( #region Overrides of BitcoinPoolBase + /// /// - protected override async Task SetupJobManager() + protected override async Task SetupJobManager(CancellationToken ct) { - await base.SetupJobManager(); + await base.SetupJobManager(ct); if (ZCashConstants.CoinbaseTxConfig.TryGetValue(poolConfig.Coin.Type, out var coinbaseTx)) coinbaseTx.TryGetValue(manager.NetworkType, out coinbaseTxConfig); diff --git a/src/MiningCore/Configuration/ClusterConfig.cs b/src/MiningCore/Configuration/ClusterConfig.cs index 1a547d4ed..74120ea20 100644 --- a/src/MiningCore/Configuration/ClusterConfig.cs +++ b/src/MiningCore/Configuration/ClusterConfig.cs @@ -61,6 +61,8 @@ public enum CoinType FLO, // Flo PAK, // PAKcoin CANN, // CannabisCoin + RVN, // Ravencoin + PGN, // Pigeoncoin } public class CoinConfig @@ -138,11 +140,30 @@ public class DatabaseConfig : AuthenticatedNetworkEndpointConfig public string Database { get; set; } } + public class TcpProxyProtocolConfig + { + /// + /// Enable for client IP addresses to be detected when using a load balancer with TCP proxy protocol enabled, such as HAProxy. + /// + public bool Enable { get; set; } + + /// + /// Terminate connections that are not beginning with a proxy-protocol header + /// + public bool Mandatory { get; set; } + + /// + /// List of IP addresses of valid proxy addresses. If absent, localhost is used + /// + public string[] ProxyAddresses { get; set; } + } + public class PoolEndpoint { public string ListenAddress { get; set; } public string Name { get; set; } public double Difficulty { get; set; } + public TcpProxyProtocolConfig TcpProxyProtocol { get; set; } public VarDiffConfig VarDiff { get; set; } } @@ -304,6 +325,11 @@ public partial class ApiConfig public bool Enabled { get; set; } public string ListenAddress { get; set; } public int Port { get; set; } + + /// + /// Port for admin-apis + /// + public int AdminPort { get; set; } } public partial class ZmqPubSubEndpointConfig diff --git a/src/MiningCore/Crypto/Hashing/Equihash/EquihashSolver.cs b/src/MiningCore/Crypto/Hashing/Equihash/EquihashSolver.cs index 8f55ed0ee..e4b809caf 100644 --- a/src/MiningCore/Crypto/Hashing/Equihash/EquihashSolver.cs +++ b/src/MiningCore/Crypto/Hashing/Equihash/EquihashSolver.cs @@ -80,7 +80,7 @@ public bool Verify(byte[] header, byte[] solution) { fixed(byte *s = solution) { - return LibMultihash.equihash_verify(h, s); + return LibMultihash.equihash_verify(h, header.Length, s, solution.Length); } } } diff --git a/src/MiningCore/Crypto/Hashing/Ethash/EthashFull.cs b/src/MiningCore/Crypto/Hashing/Ethash/EthashFull.cs index c6ee86ecd..cb188a036 100644 --- a/src/MiningCore/Crypto/Hashing/Ethash/EthashFull.cs +++ b/src/MiningCore/Crypto/Hashing/Ethash/EthashFull.cs @@ -72,18 +72,15 @@ public async Task GetDagAsync(ulong block, ILogger logger) caches[epoch] = result; } - else + // If we used up the future cache, or need a refresh, regenerate + if (future == null || future.Epoch <= epoch) { - // If we used up the future cache, or need a refresh, regenerate - if (future == null || future.Epoch <= epoch) - { - logger.Info(() => $"Pre-generating DAG for epoch {epoch + 1}"); - future = new Dag(epoch + 1); + logger.Info(() => $"Pre-generating DAG for epoch {epoch + 1}"); + future = new Dag(epoch + 1); - #pragma warning disable 4014 - future.GenerateAsync(dagDir, logger); - #pragma warning restore 4014 - } + #pragma warning disable 4014 + future.GenerateAsync(dagDir, logger); + #pragma warning restore 4014 } result.LastUsed = DateTime.Now; diff --git a/src/MiningCore/JsonRpc/JsonRpcResponse.cs b/src/MiningCore/JsonRpc/JsonRpcResponse.cs index 22c8fba2e..9a660e1cd 100644 --- a/src/MiningCore/JsonRpc/JsonRpcResponse.cs +++ b/src/MiningCore/JsonRpc/JsonRpcResponse.cs @@ -75,5 +75,13 @@ public JsonRpcResponse(JsonRpcException ex, object id, object result) [JsonProperty(PropertyName = "id", NullValueHandling = NullValueHandling.Ignore)] public object Id { get; set; } + + public TParam ResultAs() where TParam : class + { + if (Result is JToken) + return ((JToken)Result)?.ToObject(); + + return (TParam)Result; + } } } diff --git a/src/MiningCore/Messaging/Abstractions.cs b/src/MiningCore/Messaging/Abstractions.cs new file mode 100644 index 000000000..191acc30b --- /dev/null +++ b/src/MiningCore/Messaging/Abstractions.cs @@ -0,0 +1,94 @@ +using System; +using System.Reactive.Concurrency; + +namespace MiningCore.Messaging +{ + /// + /// IMessageBus represents an object that can act as a "Message Bus", a + /// simple way for ViewModels and other objects to communicate with each + /// other in a loosely coupled way. + /// + /// Specifying which messages go where is done via a combination of the Type + /// of the message as well as an additional "Contract" parameter; this is a + /// unique string used to distinguish between messages of the same Type, and + /// is arbitrarily set by the client. + /// + public interface IMessageBus + { + /// + /// Registers a scheduler for the type, which may be specified at + /// runtime, and the contract. + /// + /// If a scheduler is already registered for the specified + /// runtime and contract, this will overrwrite the existing + /// registration. + /// The type of the message to listen to. + /// The scheduler on which to post the + /// notifications for the specified type and contract. + /// CurrentThreadScheduler by default. + /// A unique string to distinguish messages with + /// identical types (i.e. "MyCoolViewModel") - if the message type is + /// only used for one purpose, leave this as null. + void RegisterScheduler(IScheduler scheduler, string contract = null); + + /// + /// Listen provides an Observable that will fire whenever a Message is + /// provided for this object via RegisterMessageSource or SendMessage. + /// + /// The type of the message to listen to. + /// A unique string to distinguish messages with + /// identical types (i.e. "MyCoolViewModel") - if the message type is + /// only used for one purpose, leave this as null. + /// + IObservable Listen(string contract = null); + + /// + /// ListenIncludeLatest provides an Observable that will fire whenever a Message is + /// provided for this object via RegisterMessageSource or SendMessage and fire the + /// last provided Message immediately if applicable, or null. + /// + /// The type of the message to listen to. + /// A unique string to distinguish messages with + /// identical types (i.e. "MyCoolViewModel") - if the message type is + /// only used for one purpose, leave this as null. + /// An Observable representing the notifications posted to the + /// message bus. + IObservable ListenIncludeLatest(string contract = null); + + /// + /// Determines if a particular message Type is registered. + /// + /// The type of the message. + /// A unique string to distinguish messages with + /// identical types (i.e. "MyCoolViewModel") - if the message type is + /// only used for one purpose, leave this as null. + /// True if messages have been posted for this message Type. + bool IsRegistered(Type type, string contract = null); + + /// + /// Registers an Observable representing the stream of messages to send. + /// Another part of the code can then call Listen to retrieve this + /// Observable. + /// + /// The type of the message to listen to. + /// An Observable that will be subscribed to, and a + /// message sent out for each value provided. + /// A unique string to distinguish messages with + /// identical types (i.e. "MyCoolViewModel") - if the message type is + /// only used for one purpose, leave this as null. + IDisposable RegisterMessageSource(IObservable source, string contract = null); + + /// + /// Sends a single message using the specified Type and contract. + /// Consider using RegisterMessageSource instead if you will be sending + /// messages in response to other changes such as property changes + /// or events. + /// + /// The type of the message to send. + /// The actual message to send + /// A unique string to distinguish messages with + /// identical types (i.e. "MyCoolViewModel") - if the message type is + /// only used for one purpose, leave this as null. + void SendMessage(T message, string contract = null); + } +} diff --git a/src/MiningCore/Messaging/MessageBus.cs b/src/MiningCore/Messaging/MessageBus.cs new file mode 100644 index 000000000..49366c52b --- /dev/null +++ b/src/MiningCore/Messaging/MessageBus.cs @@ -0,0 +1,212 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MS-PL license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using MiningCore.Util; +using NLog; + +namespace MiningCore.Messaging +{ + /// + /// MessageBus represents an object that can act as a "Message Bus", a + /// simple way for ViewModels and other objects to communicate with each + /// other in a loosely coupled way. + /// Specifying which messages go where is done via a combination of the Type + /// of the message as well as an additional "Contract" parameter; this is a + /// unique string used to distinguish between messages of the same Type, and + /// is arbitrarily set by the client. + /// + public class MessageBus : IMessageBus + { + private readonly Dictionary, NotAWeakReference> messageBus = + new Dictionary, NotAWeakReference>(); + + private readonly IDictionary, IScheduler> schedulerMappings = + new Dictionary, IScheduler>(); + + private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); + + /// + /// Gets or sets the Current MessageBus. + /// + public static IMessageBus Current { get; set; } = new MessageBus(); + + /// + /// Registers a scheduler for the type, which may be specified at runtime, and the contract. + /// + /// + /// If a scheduler is already registered for the specified runtime and contract, this will overrwrite the existing + /// registration. + /// + /// The type of the message to listen to. + /// + /// The scheduler on which to post the + /// notifications for the specified type and contract. CurrentThreadScheduler by default. + /// + /// + /// A unique string to distinguish messages with + /// identical types (i.e. "MyCoolViewModel") - if the message type is + /// only used for one purpose, leave this as null. + /// + public void RegisterScheduler(IScheduler scheduler, string contract = null) + { + schedulerMappings[new Tuple(typeof(T), contract)] = scheduler; + } + + /// + /// Listen provides an Observable that will fire whenever a Message is + /// provided for this object via RegisterMessageSource or SendMessage. + /// + /// The type of the message to listen to. + /// + /// A unique string to distinguish messages with + /// identical types (i.e. "MyCoolViewModel") - if the message type is + /// only used for one purpose, leave this as null. + /// + /// + /// An Observable representing the notifications posted to the + /// message bus. + /// + public IObservable Listen(string contract = null) + { + logger.Debug("Listening to {0}:{1}", typeof(T), contract); + + return setupSubjectIfNecessary(contract).Skip(1); + } + + /// + /// Listen provides an Observable that will fire whenever a Message is + /// provided for this object via RegisterMessageSource or SendMessage. + /// + /// The type of the message to listen to. + /// + /// A unique string to distinguish messages with + /// identical types (i.e. "MyCoolViewModel") - if the message type is + /// only used for one purpose, leave this as null. + /// + /// + /// An Observable representing the notifications posted to the + /// message bus. + /// + public IObservable ListenIncludeLatest(string contract = null) + { + logger.Debug("Listening to {0}:{1}", typeof(T), contract); + + return setupSubjectIfNecessary(contract); + } + + /// + /// Determines if a particular message Type is registered. + /// + /// The Type of the message to listen to. + /// + /// A unique string to distinguish messages with + /// identical types (i.e. "MyCoolViewModel") - if the message type is + /// only used for one purpose, leave this as null. + /// + /// True if messages have been posted for this message Type. + public bool IsRegistered(Type type, string contract = null) + { + var ret = false; + withMessageBus(type, contract, (mb, tuple) => { ret = mb.ContainsKey(tuple) && mb[tuple].IsAlive; }); + + return ret; + } + + /// + /// Registers an Observable representing the stream of messages to send. + /// Another part of the code can then call Listen to retrieve this + /// Observable. + /// + /// The type of the message to listen to. + /// + /// An Observable that will be subscribed to, and a + /// message sent out for each value provided. + /// + /// + /// A unique string to distinguish messages with + /// identical types (i.e. "MyCoolViewModel") - if the message type is + /// only used for one purpose, leave this as null. + /// + public IDisposable RegisterMessageSource( + IObservable source, + string contract = null) + { + return source.Subscribe(setupSubjectIfNecessary(contract)); + } + + /// + /// Sends a single message using the specified Type and contract. + /// Consider using RegisterMessageSource instead if you will be sending + /// messages in response to other changes such as property changes + /// or events. + /// + /// The type of the message to send. + /// The actual message to send + /// + /// A unique string to distinguish messages with + /// identical types (i.e. "MyCoolViewModel") - if the message type is + /// only used for one purpose, leave this as null. + /// + public void SendMessage(T message, string contract = null) + { + setupSubjectIfNecessary(contract).OnNext(message); + } + + private ISubject setupSubjectIfNecessary(string contract) + { + ISubject ret = null; + + withMessageBus(typeof(T), contract, (mb, tuple) => + { + if (mb.TryGetValue(tuple, out var subjRef) && subjRef.IsAlive) + { + ret = (ISubject) subjRef.Target; + return; + } + + ret = new ScheduledSubject(getScheduler(tuple), null, new BehaviorSubject(default(T))); + mb[tuple] = new NotAWeakReference(ret); + }); + + return ret; + } + + private void withMessageBus( + Type type, string contract, + Action, NotAWeakReference>, + Tuple> block) + { + lock (messageBus) + { + var tuple = new Tuple(type, contract); + block(messageBus, tuple); + if (messageBus.ContainsKey(tuple) && !messageBus[tuple].IsAlive) messageBus.Remove(tuple); + } + } + + private IScheduler getScheduler(Tuple tuple) + { + schedulerMappings.TryGetValue(tuple, out var scheduler); + return scheduler ?? CurrentThreadScheduler.Instance; + } + } + + internal class NotAWeakReference + { + public NotAWeakReference(object target) + { + Target = target; + } + + public object Target { get; } + public bool IsAlive => true; + } +} + +// vim: tw=120 ts=4 sw=4 et : diff --git a/src/MiningCore/Mining/Abstractions.cs b/src/MiningCore/Mining/Abstractions.cs index 2264af804..21549e352 100644 --- a/src/MiningCore/Mining/Abstractions.cs +++ b/src/MiningCore/Mining/Abstractions.cs @@ -19,6 +19,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; +using System.Threading; using System.Threading.Tasks; using MiningCore.Blockchain; using MiningCore.Configuration; @@ -40,12 +41,11 @@ public ClientShare(StratumClient client, Share share) public interface IMiningPool { - IObservable Shares { get; } PoolConfig Config { get; } PoolStats PoolStats { get; } BlockchainStats NetworkStats { get; } void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig); double HashrateFromShares(double shares, double interval); - Task StartAsync(); + Task StartAsync(CancellationToken ctsToken); } } diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index 064fde872..044b50e12 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -27,6 +27,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Reactive.Linq; using System.Reactive.Subjects; using System.Text.RegularExpressions; +using System.Threading; using System.Threading.Tasks; using Autofac; using AutoMapper; @@ -34,6 +35,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Blockchain; using MiningCore.Configuration; using MiningCore.Extensions; +using MiningCore.Messaging; using MiningCore.Notifications; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; @@ -56,6 +58,7 @@ protected PoolBase(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, + IMessageBus messageBus, NotificationService notificationService) : base(ctx, clock) { Contract.RequiresNonNull(ctx, nameof(ctx)); @@ -63,16 +66,16 @@ protected PoolBase(IComponentContext ctx, Contract.RequiresNonNull(cf, nameof(cf)); Contract.RequiresNonNull(statsRepo, nameof(statsRepo)); Contract.RequiresNonNull(mapper, nameof(mapper)); + Contract.RequiresNonNull(clock, nameof(clock)); + Contract.RequiresNonNull(messageBus, nameof(messageBus)); Contract.RequiresNonNull(notificationService, nameof(notificationService)); this.serializerSettings = serializerSettings; this.cf = cf; this.statsRepo = statsRepo; this.mapper = mapper; + this.messageBus = messageBus; this.notificationService = notificationService; - - Shares = shareSubject - .Synchronize(); } protected PoolStats poolStats = new PoolStats(); @@ -81,12 +84,12 @@ protected PoolBase(IComponentContext ctx, protected readonly IConnectionFactory cf; protected readonly IStatsRepository statsRepo; protected readonly IMapper mapper; + protected readonly IMessageBus messageBus; protected readonly CompositeDisposable disposables = new CompositeDisposable(); protected BlockchainStats blockchainStats; protected PoolConfig poolConfig; protected const int VarDiffSampleCount = 32; protected static readonly TimeSpan maxShareAge = TimeSpan.FromSeconds(6); - protected readonly Subject shareSubject = new Subject(); protected static readonly Regex regexStaticDiff = new Regex(@"d=(\d*(\.\d+)?)", RegexOptions.Compiled); protected const string PasswordControlVarsSeparator = ";"; @@ -95,7 +98,7 @@ protected PoolBase(IComponentContext ctx, protected override string LogCat => "Pool"; - protected abstract Task SetupJobManager(); + protected abstract Task SetupJobManager(CancellationToken ct); protected abstract WorkerContextBase CreateClientContext(); protected double? GetStaticDiffFromPassparts(string[] parts) @@ -205,10 +208,11 @@ private void StartVarDiffIdleUpdate(StratumClient client, PoolEndpoint poolEndpo // Diff may not be changed , only be changed when avg is out of the range. // Diff must be dropped once changed. Will not affect reject rate. var interval = poolEndpoint.VarDiff.TargetTime; + var shareReceivedFromClient = messageBus.Listen().Where(x => x.Share.PoolId == poolConfig.Id && x.Client == client); Observable .Timer(TimeSpan.FromSeconds(interval)) - .TakeUntil(Shares.Where(x=> x.Client == client)) + .TakeUntil(shareReceivedFromClient) .Take(1) .Where(x=> client.IsAlive) .Subscribe(_ => UpdateVarDiff(client, true)); @@ -289,13 +293,13 @@ protected void ConsiderBan(StratumClient client, WorkerContextBase context, Pool } } - private IPEndPoint PoolEndpoint2IPEndpoint(int port, PoolEndpoint pep) + private (IPEndPoint IPEndPoint, TcpProxyProtocolConfig ProxyProtocol) PoolEndpoint2IPEndpoint(int port, PoolEndpoint pep) { var listenAddress = IPAddress.Parse("127.0.0.1"); if (!string.IsNullOrEmpty(pep.ListenAddress)) listenAddress = pep.ListenAddress != "*" ? IPAddress.Parse(pep.ListenAddress) : IPAddress.Any; - return new IPEndPoint(listenAddress, port); + return (new IPEndPoint(listenAddress, port), pep.TcpProxyProtocol); } private void OutputPoolInfo() @@ -319,7 +323,6 @@ Stratum Port(s): {(poolConfig.Ports?.Any() == true ? string.Join(", ", po #region API-Surface - public IObservable Shares { get; } public PoolConfig Config => poolConfig; public PoolStats PoolStats => poolStats; public BlockchainStats NetworkStats => blockchainStats; @@ -336,7 +339,7 @@ public virtual void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig public abstract double HashrateFromShares(double shares, double interval); - public virtual async Task StartAsync() + public virtual async Task StartAsync(CancellationToken ct) { Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); @@ -345,9 +348,9 @@ public virtual async Task StartAsync() try { SetupBanning(clusterConfig); - await SetupJobManager(); - InitStats(); - + await SetupJobManager(ct); + InitStats(); + if (poolConfig.EnableInternalStratum == true) { var ipEndpoints = poolConfig.Ports.Keys @@ -367,7 +370,13 @@ public virtual async Task StartAsync() throw; } - catch(Exception ex) + catch (TaskCanceledException) + { + // just forward these + throw; + } + + catch (Exception ex) { logger.Error(ex); throw; diff --git a/src/MiningCore/Mining/ShareReceiver.cs b/src/MiningCore/Mining/ShareReceiver.cs new file mode 100644 index 000000000..8249fb832 --- /dev/null +++ b/src/MiningCore/Mining/ShareReceiver.cs @@ -0,0 +1,250 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using MiningCore.Blockchain; +using MiningCore.Configuration; +using MiningCore.Contracts; +using MiningCore.Messaging; +using MiningCore.Time; +using MiningCore.Util; +using NetMQ; +using NetMQ.Sockets; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using NLog; +using ProtoBuf; + +namespace MiningCore.Mining +{ + /// + /// Received external shares from relay and re-publishes for consumption + /// + public class ShareReceiver + { + public ShareReceiver(IMasterClock clock, IMessageBus messageBus) + { + Contract.RequiresNonNull(clock, nameof(clock)); + Contract.RequiresNonNull(messageBus, nameof(messageBus)); + + this.clock = clock; + this.messageBus = messageBus; + } + + private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); + private readonly IMasterClock clock; + private readonly IMessageBus messageBus; + private ClusterConfig clusterConfig; + private readonly ConcurrentDictionary pools = new ConcurrentDictionary(); + + class PoolContext + { + public PoolContext(IMiningPool pool, ILogger logger) + { + Pool = pool; + Logger = logger; + } + + public readonly IMiningPool Pool; + public readonly ILogger Logger; + public DateTime? LastBlock; + public long BlockHeight; + } + + private readonly TimeSpan relayReceiveTimeout = TimeSpan.FromSeconds(60); + + private void StartListeners() + { + var stratumsByUrl = clusterConfig.Pools.Where(x => x.ExternalStratums?.Any() == true) + .SelectMany(x => x.ExternalStratums) + .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()) + .ToArray(); + + 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 receivedOnce = false; + + while (true) + { + try + { + using (var subSocket = new SubscriberSocket()) + { + subSocket.Connect(url); + + // subscribe to all topics + foreach (var topic in topics) + subSocket.Subscribe(topic); + + logger.Info($"Monitoring external stratum {url}/[{string.Join(", ", topics)}]"); + + while (true) + { + // receive + var msg = (NetMQMessage)null; + + if (!subSocket.TryReceiveMultipartMessage(relayReceiveTimeout, ref msg, 3)) + { + if (receivedOnce) + { + logger.Warn(() => $"Timeout receiving message from {url}. Reconnecting ..."); + break; + } + + // retry + continue; + } + + // extract frames + var topic = msg.Pop().ConvertToString(Encoding.UTF8); + var flags = msg.Pop().ConvertToInt32(); + var data = msg.Pop().ToByteArray(); + receivedOnce = true; + + // validate + if (!topics.Contains(topic)) + { + logger.Warn(() => $"Received non-matching topic {topic} on ZeroMQ subscriber socket"); + continue; + } + + if (data?.Length == 0) + { + logger.Warn(() => $"Received empty data from {url}/{topic}"); + continue; + } + + // deserialize + var wireFormat = (ShareRelay.WireFormat)(flags & ShareRelay.WireFormatMask); + Share share = null; + + switch (wireFormat) + { + case ShareRelay.WireFormat.Json: + using (var stream = new MemoryStream(data)) + { + using (var reader = new StreamReader(stream, Encoding.UTF8)) + { + 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); + share.BlockReward = (decimal)share.BlockRewardDouble; + } + break; + + default: + logger.Error(() => $"Unsupported wire format {wireFormat} of share received from {url}/{topic} "); + break; + } + + if (share == null) + { + logger.Error(() => $"Unable to deserialize share received from {url}/{topic}"); + continue; + } + + // store + share.PoolId = topic; + share.Created = clock.Now; + messageBus.SendMessage(new ClientShare(null, share)); + + // update poolstats from shares + if (pools.TryGetValue(topic, out var poolContext)) + { + 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 (pool.NetworkStats != null) + { + pool.NetworkStats.BlockHeight = share.BlockHeight; + pool.NetworkStats.NetworkDifficulty = share.NetworkDifficulty; + + if (poolContext.BlockHeight != share.BlockHeight) + { + pool.NetworkStats.LastNetworkBlockTime = clock.Now; + poolContext.BlockHeight = share.BlockHeight; + poolContext.LastBlock = clock.Now; + } + + else + pool.NetworkStats.LastNetworkBlockTime = poolContext.LastBlock; + } + } + + else + logger.Info(() => $"External {(!string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}] " : string.Empty)}share accepted: D={Math.Round(share.Difficulty, 3)}"); + } + } + } + + catch (ObjectDisposedException) + { + logger.Info($"Exiting monitoring thread for external stratum {url}/[{string.Join(", ", topics)}]"); + break; + } + + catch (Exception ex) + { + logger.Error(ex); + } + } + }); + + thread.Start(item); + } + + if(stratumsByUrl.Any()) + logger.Info(() => "Online"); + } + + #region API-Surface + + public void AttachPool(IMiningPool pool) + { + pools[pool.Config.Id] = new PoolContext(pool, LogUtil.GetPoolScopedLogger(typeof(ShareRecorder), pool.Config)); + } + + public void Start(ClusterConfig clusterConfig) + { + this.clusterConfig = clusterConfig; + + StartListeners(); + } + + public void Stop() + { + logger.Info(() => "Stopping .."); + + logger.Info(() => "Stopped"); + } + + #endregion // API-Surface + } +} diff --git a/src/MiningCore/Mining/ShareRecorder.cs b/src/MiningCore/Mining/ShareRecorder.cs index 8ec2866fe..ec833616c 100644 --- a/src/MiningCore/Mining/ShareRecorder.cs +++ b/src/MiningCore/Mining/ShareRecorder.cs @@ -28,25 +28,21 @@ 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; +using MiningCore.Messaging; using MiningCore.Notifications; +using MiningCore.Notifications.Messages; using MiningCore.Persistence; using MiningCore.Persistence.Model; using MiningCore.Persistence.Repositories; using MiningCore.Time; using MiningCore.Util; -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 ProtoBuf; using Contract = MiningCore.Contracts.Contract; using Share = MiningCore.Blockchain.Share; @@ -61,6 +57,7 @@ public ShareRecorder(IConnectionFactory cf, IMapper mapper, JsonSerializerSettings jsonSerializerSettings, IShareRepository shareRepo, IBlockRepository blockRepo, IMasterClock clock, + IMessageBus messageBus, NotificationService notificationService) { Contract.RequiresNonNull(cf, nameof(cf)); @@ -69,12 +66,14 @@ public ShareRecorder(IConnectionFactory cf, IMapper mapper, Contract.RequiresNonNull(blockRepo, nameof(blockRepo)); Contract.RequiresNonNull(jsonSerializerSettings, nameof(jsonSerializerSettings)); Contract.RequiresNonNull(clock, nameof(clock)); + Contract.RequiresNonNull(messageBus, nameof(messageBus)); Contract.RequiresNonNull(notificationService, nameof(notificationService)); this.cf = cf; this.mapper = mapper; this.jsonSerializerSettings = jsonSerializerSettings; this.clock = clock; + this.messageBus = messageBus; this.notificationService = notificationService; this.shareRepo = shareRepo; @@ -84,33 +83,19 @@ public ShareRecorder(IConnectionFactory cf, IMapper mapper, } private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); + private readonly IShareRepository shareRepo; private readonly IBlockRepository blockRepo; private readonly IConnectionFactory cf; private readonly JsonSerializerSettings jsonSerializerSettings; private readonly IMasterClock clock; + private readonly IMessageBus messageBus; private readonly NotificationService notificationService; private ClusterConfig clusterConfig; private readonly IMapper mapper; - private readonly ConcurrentDictionary pools = new ConcurrentDictionary(); - private BlockingCollection queue = new BlockingCollection(); - - class PoolContext - { - public PoolContext(IMiningPool pool, ILogger logger) - { - Pool = pool; - Logger = logger; - } - - public readonly IMiningPool Pool; - public readonly ILogger Logger; - public DateTime? LastBlock; - public long BlockHeight; - } + private readonly BlockingCollection queue = new BlockingCollection(); private readonly int QueueSizeWarningThreshold = 1024; private readonly TimeSpan relayReceiveTimeout = TimeSpan.FromSeconds(60); - private readonly IShareRepository shareRepo; private Policy faultPolicy; private bool hasLoggedPolicyFallbackFailure; private bool hasWarnedAboutBacklogSize; @@ -142,7 +127,7 @@ private void PersistShares(IList shares) blockEntity.Status = BlockStatus.Pending; blockRepo.Insert(con, tx, blockEntity); - notificationService.NotifyBlock(share.PoolId, share.BlockHeight); + messageBus.SendMessage(new BlockNotification(share.PoolId, share.BlockHeight)); } } }); @@ -313,178 +298,14 @@ private void NotifyAdminOnPolicyFallback() } } - private void StartExternalStratumPublisherListeners() - { - var stratumsByUrl = clusterConfig.Pools.Where(x => x.ExternalStratums?.Any() == true) - .SelectMany(x => x.ExternalStratums) - .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 - { - 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 receivedOnce = false; - - while (true) - { - try - { - using (var subSocket = new SubscriberSocket()) - { - subSocket.Connect(url); - - // subscribe to all topics - foreach (var topic in topics) - subSocket.Subscribe(topic); - - logger.Info($"Monitoring external stratum {url}/[{string.Join(", ", topics)}]"); - - while (true) - { - // receive - var msg = (NetMQMessage)null; - - if (!subSocket.TryReceiveMultipartMessage(relayReceiveTimeout, ref msg, 3)) - { - if (receivedOnce) - { - logger.Warn(() => $"Timeout receiving message from {url}. Reconnecting ..."); - break; - } - - // retry - continue; - } - - // extract frames - var topic = msg.Pop().ConvertToString(Encoding.UTF8); - var flags = msg.Pop().ConvertToInt32(); - var data = msg.Pop().ToByteArray(); - receivedOnce = true; - - // validate - if (!topics.Contains(topic)) - { - logger.Warn(() => $"Received non-matching topic {topic} on ZeroMQ subscriber socket"); - continue; - } - - if (data?.Length == 0) - { - logger.Warn(() => $"Received empty data from {url}/{topic}"); - continue; - } - - // deserialize - var wireFormat = (ShareRelay.WireFormat)(flags & ShareRelay.WireFormatMask); - Share share = null; - - switch (wireFormat) - { - case ShareRelay.WireFormat.Json: - using (var stream = new MemoryStream(data)) - { - using (var reader = new StreamReader(stream, Encoding.UTF8)) - { - 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); - share.BlockReward = (decimal) share.BlockRewardDouble; - } - break; - - default: - logger.Error(() => $"Unsupported wire format {wireFormat} of share received from {url}/{topic} "); - break; - } - - if (share == null) - { - logger.Error(() => $"Unable to deserialize share received from {url}/{topic}"); - continue; - } - - // store - share.PoolId = topic; - share.Created = clock.Now; - queue.Add(share); - - // misc - if (pools.TryGetValue(topic, out var poolContext)) - { - 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)}"); - - // update pool stats - if (pool.NetworkStats != null) - { - pool.NetworkStats.BlockHeight = share.BlockHeight; - pool.NetworkStats.NetworkDifficulty = share.NetworkDifficulty; - - if (poolContext.BlockHeight != share.BlockHeight) - { - pool.NetworkStats.LastNetworkBlockTime = clock.Now; - poolContext.BlockHeight = share.BlockHeight; - poolContext.LastBlock = clock.Now; - } - - else - pool.NetworkStats.LastNetworkBlockTime = poolContext.LastBlock; - } - } - } - } - } - - catch (Exception ex) - { - logger.Error(ex); - } - } - }); - - thread.Start(item); - } - } - #region API-Surface - public void AttachPool(IMiningPool pool) - { - pools[pool.Config.Id] = new PoolContext(pool, LogUtil.GetPoolScopedLogger(typeof(ShareRecorder), pool.Config)); - - pool.Shares.Subscribe(x => { queue.Add(x.Share); }); - } - public void Start(ClusterConfig clusterConfig) { this.clusterConfig = clusterConfig; ConfigureRecovery(); InitializeQueue(); - StartExternalStratumPublisherListeners(); logger.Info(() => "Online"); } @@ -493,11 +314,8 @@ public void Stop() { logger.Info(() => "Stopping .."); - queueSub?.Dispose(); - queueSub = null; - - queue?.Dispose(); - queue = null; + queueSub.Dispose(); + queue.Dispose(); logger.Info(() => "Stopped"); } @@ -506,6 +324,8 @@ public void Stop() private void InitializeQueue() { + messageBus.Listen().Subscribe(x => queue.Add(x.Share)); + queueSub = queue.GetConsumingEnumerable() .ToObservable(TaskPoolScheduler.Default) .Do(_ => CheckQueueBacklog()) diff --git a/src/MiningCore/Mining/ShareRelay.cs b/src/MiningCore/Mining/ShareRelay.cs index ce93cb249..6ee4a3e2b 100644 --- a/src/MiningCore/Mining/ShareRelay.cs +++ b/src/MiningCore/Mining/ShareRelay.cs @@ -5,6 +5,8 @@ using System.Reactive.Linq; using MiningCore.Blockchain; using MiningCore.Configuration; +using MiningCore.Contracts; +using MiningCore.Messaging; using NetMQ; using NetMQ.Sockets; using Newtonsoft.Json; @@ -15,11 +17,16 @@ namespace MiningCore.Mining { public class ShareRelay { - public ShareRelay(JsonSerializerSettings serializerSettings) + public ShareRelay(JsonSerializerSettings serializerSettings, IMessageBus messageBus) { + Contract.RequiresNonNull(serializerSettings, nameof(serializerSettings)); + Contract.RequiresNonNull(messageBus, nameof(messageBus)); + this.serializerSettings = serializerSettings; + this.messageBus = messageBus; } + private readonly IMessageBus messageBus; private ClusterConfig clusterConfig; private readonly BlockingCollection queue = new BlockingCollection(); private IDisposable queueSub; @@ -41,15 +48,12 @@ public enum WireFormat #region API-Surface - public void AttachPool(IMiningPool pool) - { - pool.Shares.Subscribe(x => { queue.Add(x.Share); }); - } - public void Start(ClusterConfig clusterConfig) { this.clusterConfig = clusterConfig; + messageBus.Listen().Subscribe(x => queue.Add(x.Share)); + pubSocket = new PublisherSocket(); if (!clusterConfig.ShareRelay.Connect) diff --git a/src/MiningCore/MiningCore.csproj b/src/MiningCore/MiningCore.csproj index 797efb61d..6234f7724 100644 --- a/src/MiningCore/MiningCore.csproj +++ b/src/MiningCore/MiningCore.csproj @@ -43,24 +43,24 @@ - + - + - + - + - + - + diff --git a/src/MiningCore/Native/LibMultihash.cs b/src/MiningCore/Native/LibMultihash.cs index 1f0365636..81e0d949c 100644 --- a/src/MiningCore/Native/LibMultihash.cs +++ b/src/MiningCore/Native/LibMultihash.cs @@ -110,7 +110,7 @@ public static unsafe class LibMultihash public static extern int lyra2rev2(byte* input, byte* output); [DllImport("libmultihash", EntryPoint = "equihash_verify_export", CallingConvention = CallingConvention.Cdecl)] - public static extern bool equihash_verify(byte* header, byte* solution); + public static extern bool equihash_verify(byte* header, int headerLength, byte* solution, int solutionLength); [DllImport("libmultihash", EntryPoint = "sha3_256_export", CallingConvention = CallingConvention.Cdecl)] public static extern int sha3_256(byte* input, byte* output, uint inputLength); diff --git a/src/MiningCore/Notifications/Messages/BlockNotification.cs b/src/MiningCore/Notifications/Messages/BlockNotification.cs new file mode 100644 index 000000000..b9f1d43e7 --- /dev/null +++ b/src/MiningCore/Notifications/Messages/BlockNotification.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MiningCore.Notifications.Messages +{ + public class BlockNotification + { + public BlockNotification(string poolId, long blockHeight) + { + PoolId = poolId; + BlockHeight = blockHeight; + } + + public string PoolId { get; } + public long BlockHeight { get; } + } +} diff --git a/src/MiningCore/Notifications/NotificationService.cs b/src/MiningCore/Notifications/NotificationService.cs index 3a67626b2..2d577c5df 100644 --- a/src/MiningCore/Notifications/NotificationService.cs +++ b/src/MiningCore/Notifications/NotificationService.cs @@ -14,6 +14,8 @@ using MimeKit; using MiningCore.Configuration; using MiningCore.Contracts; +using MiningCore.Messaging; +using MiningCore.Notifications.Messages; using MiningCore.Notifications.Slack; using Newtonsoft.Json; using NLog; @@ -24,9 +26,11 @@ public class NotificationService { public NotificationService( ClusterConfig clusterConfig, - JsonSerializerSettings serializerSettings) + JsonSerializerSettings serializerSettings, + IMessageBus messageBus) { Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); + Contract.RequiresNonNull(messageBus, nameof(messageBus)); this.clusterConfig = clusterConfig; this.serializerSettings = serializerSettings; @@ -45,6 +49,18 @@ public NotificationService( .Select(notification => Observable.FromAsync(() => SendNotificationAsync(notification))) .Concat() .Subscribe(); + + messageBus.Listen() + .Subscribe(x => + { + queue?.Add(new QueuedNotification + { + Category = NotificationCategory.Block, + PoolId = x.PoolId, + Subject = "Block Notification", + Msg = $"Pool {x.PoolId} found block candidate {x.BlockHeight}" + }); + }); } } @@ -81,17 +97,6 @@ struct QueuedNotification #region API-Surface - public void NotifyBlock(string poolId, long blockHeight) - { - queue?.Add(new QueuedNotification - { - Category = NotificationCategory.Block, - PoolId = poolId, - Subject = "Block Notification", - Msg = $"Pool {poolId} found block candidate {blockHeight}" - }); - } - public void NotifyPaymentSuccess(string poolId, decimal amount, int recpientsCount, string txInfo, decimal? txFee) { queue?.Add(new QueuedNotification diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index 6a228c955..d1862b460 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -21,10 +21,8 @@ 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 System.Reactive; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; @@ -41,11 +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.Ethereum; -using MiningCore.Blockchain.Ethereum.DaemonRequests; -using MiningCore.Blockchain.ZCash; using MiningCore.Configuration; using MiningCore.Crypto.Hashing.Algorithms; using MiningCore.Crypto.Hashing.Equihash; @@ -57,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; @@ -78,6 +70,7 @@ public class Program private static CommandOption shareRecoveryOption; private static ShareRecorder shareRecorder; private static ShareRelay shareRelay; + private static ShareReceiver shareReceiver; private static PayoutManager payoutManager; private static StatsRecorder statsRecorder; private static ClusterConfig clusterConfig; @@ -94,6 +87,7 @@ public static void Main(string[] args) { AppDomain.CurrentDomain.UnhandledException += OnAppDomainUnhandledException; AppDomain.CurrentDomain.ProcessExit += OnProcessExit; + Console.CancelKeyPress += OnCancelKeyPress; #if DEBUG PreloadNativeLibs(); #endif @@ -116,9 +110,8 @@ public static void Main(string[] args) if (!shareRecoveryOption.HasValue()) { - Console.CancelKeyPress += OnCancelKeyPress; - Start().Wait(cts.Token); - Shutdown(); + if(!cts.IsCancellationRequested) + Start().Wait(cts.Token); } else @@ -162,11 +155,15 @@ public static void Main(string[] args) Console.WriteLine("Cluster cannot start. Good Bye!"); } + + Shutdown(); + Process.GetCurrentProcess().CloseMainWindow(); + Process.GetCurrentProcess().Close(); } private static void LogRuntimeInfo() { - logger.Info(() => $"Running on {RuntimeInformation.FrameworkDescription} under {RuntimeInformation.OSDescription} [{RuntimeInformation.OSArchitecture} - {RuntimeInformation.ProcessArchitecture}]"); + logger.Info(() => $"{RuntimeInformation.FrameworkDescription.Trim()} on {RuntimeInformation.OSDescription.Trim()} [{RuntimeInformation.ProcessArchitecture}]"); } private static void ValidateConfig() @@ -565,6 +562,10 @@ private static async Task Start() // start share recorder shareRecorder = container.Resolve(); shareRecorder.Start(clusterConfig); + + // start share receiver (for external shares) + shareReceiver = container.Resolve(); + shareReceiver.Start(clusterConfig); } else @@ -614,15 +615,14 @@ await Task.WhenAll(clusterConfig.Pools.Where(x => x.Enabled).Select(async poolCo pool.Configure(poolConfig, clusterConfig); // pre-start attachments - shareRecorder?.AttachPool(pool); - shareRelay?.AttachPool(pool); + shareReceiver?.AttachPool(pool); statsRecorder?.AttachPool(pool); - await pool.StartAsync(); + await pool.StartAsync(cts.Token); })); // keep running - await Observable.Never().ToTask(); + await Observable.Never().ToTask(cts.Token); } private static void RecoverShares(string recoveryFilename) @@ -644,27 +644,38 @@ private static void OnAppDomainUnhandledException(object sender, UnhandledExcept private static void OnCancelKeyPress(object sender, ConsoleCancelEventArgs e) { - logger.Info(() => "SIGINT received. Exiting."); + logger?.Info(() => "SIGINT received. Exiting."); + Console.WriteLine("SIGINT received. Exiting."); + + try + { + cts?.Cancel(); + } + catch { } - cts.Cancel(); + e.Cancel = true; } private static void OnProcessExit(object sender, EventArgs e) { - logger.Info(() => "SIGTERM received. Exiting."); + logger?.Info(() => "SIGTERM received. Exiting."); + Console.WriteLine("SIGTERM received. Exiting."); - cts.Cancel(); + try + { + cts?.Cancel(); + } + catch { } } private static void Shutdown() { logger.Info(() => "Shutdown ..."); + Console.WriteLine("Shutdown..."); - shareRelay.Stop(); - shareRecorder.Stop(); - statsRecorder.Stop(); - - Process.GetCurrentProcess().Close(); + shareRelay?.Stop(); + shareRecorder?.Stop(); + statsRecorder?.Stop(); } private static void TouchNativeLibs() diff --git a/src/MiningCore/Stratum/StratumClient.cs b/src/MiningCore/Stratum/StratumClient.cs index 394374a71..e27c1f226 100644 --- a/src/MiningCore/Stratum/StratumClient.cs +++ b/src/MiningCore/Stratum/StratumClient.cs @@ -22,10 +22,13 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Buffers; using System.Collections.Concurrent; using System.IO; +using System.Linq; using System.Net; using System.Reactive.Disposables; +using System.Text; using Autofac; using MiningCore.Buffers; +using MiningCore.Configuration; using MiningCore.JsonRpc; using MiningCore.Mining; using MiningCore.Time; @@ -42,8 +45,8 @@ public class StratumClient { private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); - private const int MaxInboundRequestLength = 8192; - private const int MaxOutboundRequestLength = 0x4000; + private const int MaxInboundRequestLength = 0x8000; + private const int MaxOutboundRequestLength = 0x8000; private ConcurrentQueue> sendQueue; private Async sendQueueDrainer; @@ -51,6 +54,7 @@ public class StratumClient private IDisposable subscription; private bool isAlive = true; private WorkerContextBase context; + private bool expectingProxyProtocolHeader = false; private static readonly JsonSerializer serializer = new JsonSerializer { @@ -60,13 +64,15 @@ public class StratumClient #region API-Surface public void Init(Loop loop, Tcp tcp, IComponentContext ctx, IMasterClock clock, - IPEndPoint endpointConfig, string connectionId, + (IPEndPoint IPEndPoint, TcpProxyProtocolConfig ProxyProtocol) endpointConfig, string connectionId, Action> onNext, Action onCompleted, Action onError) { - PoolEndpoint = endpointConfig; + PoolEndpoint = endpointConfig.IPEndPoint; ConnectionId = connectionId; RemoteEndpoint = tcp.GetPeerEndPoint(); + expectingProxyProtocolHeader = endpointConfig.ProxyProtocol?.Enable == true; + // initialize send queue sendQueue = new ConcurrentQueue>(); sendQueueDrainer = loop.CreateAsync(DrainSendQueue); @@ -95,7 +101,7 @@ public void Init(Loop loop, Tcp tcp, IComponentContext ctx, IMasterClock clock, subscription = Disposable.Create(() => { disposer.Send(); }); // go - Receive(tcp, clock, onNext, onCompleted, onError); + Receive(tcp, endpointConfig.ProxyProtocol, clock, onNext, onCompleted, onError); } public string ConnectionId { get; private set; } @@ -235,7 +241,7 @@ public JsonRpcRequest DeserializeRequest(PooledArraySegment data) #endregion // API-Surface - private void Receive(Tcp tcp, IMasterClock clock, + private void Receive(Tcp tcp, TcpProxyProtocolConfig proxyProtocol, IMasterClock clock, Action> onNext, Action onCompleted, Action onError) { tcp.OnRead((handle, buffer) => @@ -248,9 +254,67 @@ private void Receive(Tcp tcp, IMasterClock clock, LastReceive = clock.Now; + var onLineReceived = !expectingProxyProtocolHeader ? + onNext : + (lineData) => + { + // are we expecting the Tcp-Proxy-Protocol header? + if (expectingProxyProtocolHeader) + { + expectingProxyProtocolHeader = false; + var peerAddress = tcp.GetPeerEndPoint().Address; + + // peek into line data + var line = Encoding.ASCII.GetString(lineData.Array, lineData.Offset, lineData.Size); + + if (line.StartsWith("PROXY ")) + { + using(lineData) + { + var proxyAddresses = proxyProtocol.ProxyAddresses?.Select(x => IPAddress.Parse(x)).ToArray(); + if (proxyAddresses == null || !proxyAddresses.Any()) + proxyAddresses = new[] { IPAddress.Loopback }; + + if (proxyAddresses.Any(x=> x.Equals(peerAddress))) + { + logger.Debug(() => $"[{ConnectionId}] Received Proxy-Protocol header: {line}"); + + // split header parts + var parts = line.Split(" "); + var remoteAddress = parts[2]; + var remotePort = parts[4]; + + // Update client + RemoteEndpoint = new IPEndPoint(IPAddress.Parse(remoteAddress), int.Parse(remotePort)); + logger.Info(() => $"[{ConnectionId}] Real-IP via Proxy-Protocol: {RemoteEndpoint.Address}"); + } + + else + { + logger.Error(() => $"[{ConnectionId}] Received spoofed Proxy-Protocol header from {peerAddress}"); + Disconnect(); + } + } + + return; + } + + else if (proxyProtocol.Mandatory) + { + logger.Error(() => $"[{ConnectionId}] Missing mandatory Proxy-Protocol header from {peerAddress}. Closing connection."); + lineData.Dispose(); + Disconnect(); + return; + } + } + + // forward + onNext(lineData); + }; + plb.Receive(buffer, buffer.Count, (src, dst, count) => src.ReadBytes(dst, count), - onNext, + onLineReceived, onError); } }, (handle, ex) => diff --git a/src/MiningCore/Stratum/StratumServer.cs b/src/MiningCore/Stratum/StratumServer.cs index cb871de9a..fde585500 100644 --- a/src/MiningCore/Stratum/StratumServer.cs +++ b/src/MiningCore/Stratum/StratumServer.cs @@ -64,7 +64,7 @@ protected StratumServer(IComponentContext ctx, IMasterClock clock) protected abstract string LogCat { get; } - public void StartListeners(string id, params IPEndPoint[] stratumPorts) + public void StartListeners(string id, params (IPEndPoint IPEndPoint, TcpProxyProtocolConfig ProxyProtocol)[] stratumPorts) { Contract.RequiresNonNull(stratumPorts, nameof(stratumPorts)); @@ -81,7 +81,7 @@ public void StartListeners(string id, params IPEndPoint[] stratumPorts) .CreateTcp() .NoDelay(true) .SimultaneousAccepts(false) - .Listen(endpoint, (con, ex) => + .Listen(endpoint.IPEndPoint, (con, ex) => { if (ex == null) OnClientConnected(con, endpoint, loop); @@ -91,7 +91,7 @@ public void StartListeners(string id, params IPEndPoint[] stratumPorts) lock (ports) { - ports[endpoint.Port] = listener; + ports[endpoint.IPEndPoint.Port] = listener; } } @@ -101,7 +101,7 @@ public void StartListeners(string id, params IPEndPoint[] stratumPorts) throw; } - logger.Info(() => $"[{LogCat}] Stratum port {endpoint.Address}:{endpoint.Port} online"); + logger.Info(() => $"[{LogCat}] Stratum port {endpoint.IPEndPoint.Address}:{endpoint.IPEndPoint.Port} online"); try { @@ -112,7 +112,7 @@ public void StartListeners(string id, params IPEndPoint[] stratumPorts) { logger.Error(ex, $"[{LogCat}] {ex}"); } - }) { Name = $"UvLoopThread {id}:{endpoint.Port}" }; + }) { Name = $"UvLoopThread {id}:{endpoint.IPEndPoint.Port}" }; thread.Start(); } @@ -137,7 +137,7 @@ public void StopListeners() } } - private void OnClientConnected(Tcp con, IPEndPoint endpointConfig, Loop loop) + private void OnClientConnected(Tcp con, (IPEndPoint IPEndPoint, TcpProxyProtocolConfig ProxyProtocol) endpointConfig, Loop loop) { try { diff --git a/src/MiningCore/Util/ScheduledSubject.cs b/src/MiningCore/Util/ScheduledSubject.cs new file mode 100644 index 000000000..ee248dc92 --- /dev/null +++ b/src/MiningCore/Util/ScheduledSubject.cs @@ -0,0 +1,62 @@ +using System; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Threading; + +namespace MiningCore.Util +{ + public class ScheduledSubject : ISubject + { + public ScheduledSubject(IScheduler scheduler, IObserver defaultObserver = null, ISubject defaultSubject = null) + { + _scheduler = scheduler; + _defaultObserver = defaultObserver; + _subject = defaultSubject ?? new Subject(); + + if (defaultObserver != null) _defaultObserverSub = _subject.ObserveOn(_scheduler).Subscribe(_defaultObserver); + } + + private readonly IObserver _defaultObserver; + private readonly IScheduler _scheduler; + private readonly ISubject _subject; + private IDisposable _defaultObserverSub = Disposable.Empty; + + private int _observerRefCount; + + public void OnCompleted() + { + _subject.OnCompleted(); + } + + public void OnError(Exception error) + { + _subject.OnError(error); + } + + public void OnNext(T value) + { + _subject.OnNext(value); + } + + public IDisposable Subscribe(IObserver observer) + { + Interlocked.Exchange(ref _defaultObserverSub, Disposable.Empty).Dispose(); + + Interlocked.Increment(ref _observerRefCount); + + return new CompositeDisposable( + _subject.ObserveOn(_scheduler).Subscribe(observer), + Disposable.Create(() => + { + if (Interlocked.Decrement(ref _observerRefCount) <= 0 && _defaultObserver != null) _defaultObserverSub = _subject.ObserveOn(_scheduler).Subscribe(_defaultObserver); + })); + } + + public void Dispose() + { + if (_subject is IDisposable) ((IDisposable) _subject).Dispose(); + } + } +} diff --git a/src/MiningCore/runtimes/win-x64/native/libmultihash.dll b/src/MiningCore/runtimes/win-x64/native/libmultihash.dll index ec9eddcc34f070055885a75474a8c5118251cc69..83d1dc302d1ae3e5c960df757a27d0b8e120ccc7 100644 GIT binary patch delta 276309 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 AcmMzZ delta 136680 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

RnY?8dg&jl5@9 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%0Abu=3>ltTOcw_kEY$2%mHC zzN0P(su^~aRH6zg?{sYpkTT1$sr7rTUKc(QV?w9YXSlFaS>3r&>gOy6@A3gMAmrIw zeEbLK)iMS#zVZY1nW5X#TfA5Ke+9SOAvL(i3;sWYyI~(cT+R-M|FX9!APES%(xOvi zKt|7WY}&Gcoz(TN-Q6UWgrF<_9U7%3AnVfXxdmzV>CrvXu!kV6LRW;Wgr+a`i~9qK z{Pxqg_zxdpr4aF0vypx8V?@%4&sK>#Su(Uh>0RgG=QpzTE~B9S@aH~e`+QcaW=$e{ zFF5#=PuQ&d-h0$iqs$nguIEm<@Sa-8UlAwgFby3<#{D(pJ{Mkp!1(%Z&30?exaEaa z4;UXJW0PtuFB9XcwB(%VEL_Tr0-34o^J*;$bxEbzP=Mi?$LD<9r>xbujhScw{ypC2 z|HXXvznF9Xi^3v}>xM_a^gIPni6}hg2d{4&ulKN?I8*NHpksFl1if zp)7(p-}i(mtSy@9!rS-Jm<q&OnWm1~8mp_u$_8t##);85dEmUDaeP#cy+CAYie;(By#KHW zw_W5Vm^m8rg2q%E(G<(08uNr;HfxY)s>cSVs&|=)6NjIzQWN46)$U-ZT$T?s&N_|b z1CWtensIflD0hr)y_l(Lp%XTgA>sv^|Jrq&EGdI&~E z>1RKx-f=Y44pk64ht%VzViI!FXkNV>vkE6h@{7wc74!TM{z4&oscsv2%yTTjFfmlB02bcu6;r1U`V9*kwHrRjOB6%TlDMPf8SYMM;KbUX(f%W0%eqqCc2jY{w zQf?Qh2AHYLZ6)fUI-9+9tb-3d$tJe$kIwZQqGHaN31=B3XWJW z#Fq3ZtZe?qBEr9qa)z(2x~JnlVi^c|Ouh*V$Jej<9qWKw*Ykkiu~Y8Oi+s-StcPLA zCX#m8+Pgx%Ba4b#6a8t?| zs=SH`+psg3Wj_9%IvVt!iPg5mpj^yTi~SIpEydb}BWI7A9AGSXjZZ$q2IvQU%FE9% zn`;?1HsPuzhk{eU?q}Ht9nOTR3mS#8L%D@lyL4dydOT%tJa&n?i2wvB+nYpAggW@y zvuvXN%fYUr|gi2f8Z!94(mM!aw=G zYJ2KHF-;*adjZZV%R(Hy;~&^ca})y~Ej+i&jC|%FY^lD@3;e<#>?J+3@u$zTE`hya z5mWqH*d5lFY--S+3c_&WJZ6iBVYlYx^O)0o_YfcUC+nuayq_2UiQOSn!+F)8tWEyr zf^10=Pq8uMJ;Yhvr-$|lc!-MYu}&bnCC1{VAjQV+Kp1|CvQT1-1i-c4#!#Xb zmD-I~m7+q6I_a;BMgvH7@^#eV&3@wSah=(2JoBy*R~kBpaiRs#EVOE}Fk7>r$$$NS zk-r(x=z(Y?;DILp%!BfxUCO}ckFo;zJUg2-ab5u?Y&=rpp9GbQ7#h%SJ%eFIF3Lkv z%9-s?%)WX>?I4B;KfILV1-?oUHtZ~p zUzv||Gp@)t@^7AJ;q7MuBDC|~5iVMCK;WSaV+SI!dtb|7@@5| zF|JQcE5;&CnrOE#yo%%#k#mRauoAYOjd}*LuuOi&$ZwRe$q84VHcF}T`hE6&7y4jQ zG{so2GS=;@yZrCT`c?xSeCZ1;vHge*Voo(QY3eOU>dJa7)p(VRKy(lEv#++R(e%`! zFEC<@yKmU~eyV|~w|({NA^{3-8*`J>QjR~ylv@tXdXBl# zL6g&|oiDOPdDs$>WJAQ?p1qbBxvrFjJ<>sCw#Q%P$2)4f)cdhC99Kb0Vz&fpGz+ZZ4U$O zU31`#`~C=yjg4<{iOIXs+jgD?tJGp2#RT&Vq<%-F|sj6M<}oQ z@xWJDXpf*Iku9?Oqn&>s%Kt!a2}B-fN(JLBK%!S+NCMofEVh&m%gxm~dYG z@%lfZAGYbqhbnAAhv#QCCX*K?0&v6bP#l}JUr<#vaS?)5(ODXs?EW}>a8+Sn>5ruF z@(Y-UTb089xWIbpA4%ac7ug8?Ki&D_i>L>uy7SjAVxDYUcfR=|n;rECrYw*+f7nv) z%?}D_t1I`+Qdtrza|D|M`+yqh)Ya3&mzXn%>l5)>hr zQ7A7L#z03@lPd#R{lrWo|Lqbh@J=YfxUX<`2Busq>nyDrZMYS$p(^pgOLQ8$9Nua2^gMV>_wbUQ4wL-~Hy8j91#K&T5nJ%_hK=*S=Ec2!0oZ4AnFGi~zYRd6cCDr`LayJ&lMDD7s-b=GOBTXmN4ps3p? zu(26l9Jtn79_z6KAvuO;2Mk_c51j~K^$L9lf}ob!DjoaFHF@j-Jos#AZ0$8UBXohv z&#y#n7az;`bf?54MN!Nd-Pe0SncjA0QH z;{97Jp>>ujJHeQ((u`{O4fiL(U9XrqsvlnQxV@R*y~QH?CTgC>+q3{3gJ);(9LFQx zFT#@b5Vn*dK!3pV^T3@q0>jaonexd{*yocQSdaYARG|sh4J!4*2SHC-Eke*HKZ77F z_#tVgd?!8*VY)7#|NFvKXdW|5bHm`CWjQpKG`n#-%#^JtB6T+BH#mjq3eEIAVY*J3 zwr&26PUBb@rTg*C!mIST?TJ zET042eP(~eM@?mA{@YYm_S9wk0JAN|inaK>#~o%>Z%{$3jg`W7T}JhuyORze2r-qa zA71e_nqODfZ#bzo5q!98cPF;iPpj2e=)(bI-9m@oyl6kKAQe z&zo!10@5BnC~aHtoVzSyx}x!ng7**R7xvtqhsHu}=}T=X#w{?;X^odM@ghpaKD?+^ z9YyVXc&Y4#tvVMuJhi!9T8lytvI#v%;y)}f?R5x4@kkT;$X4WQs>52=4%u65Tsl)U z#yE@l$}Y7>O;x{QBgR2?%M8`*_Pr-@5rr;}m;A$$y0-++VU34ndkvcXSq^J)oKU@7 zb0@=wv6+px+QI|62i0;3DR%cC794pV(%Ne45Ny&zYgTOhW`-Erh)c^H*6ne;>pj*w zcoHV1Xu$SPVzcqoFU3Z_=pJ_2R4+Ad;`i78E%kA0;l|}4M%k@&8yUV*eSX_JaEU}HykuwyFdvQ>R9QnW6f57C1 zD>M?(rTAfTLY?yX5zK)L`6>sC%|DB6H-*W@TzBO*-u6kj2MPCOi@i9wN;+Ao#KZ%3 zXK!EhE^K017&b5Gik=YZTyz5tgW=&J+{fPQKVwA)Dldb8L;0}DxqQ_*oC4|sfnvjj zZ($F8l+X%aNjA4O%#JJvH9B*l{^b}R ztT&I>`^h{{Z;mm1f#W@8dUJRE@o0Vu4E;yZ++Z+g=sS4wi3Z>!LE1uRnZX>azuJ<2 zXD}xk3@fClu)W9UMo~5qg9(Yqt6LDSAQ7i=T(|@sK zm&q{Zq4msvLK@x*&)%);k$Q8V4mXP7%ZcU%vUa&dtZH_@E&^tHEI7i;(tXJIbBf_15$Wx2(o%~yio;N!2d zp3%Ng=F~enwpKd89tDWTRo&IE?Gx;@;6AUv#*$j7G4>MKh$g^ze6f*Fd5-mrdug#z zigxYc+7Dli2fq*yqyR*n5b?t9fWk|-$I;lYjE^;%*R^;DJ)Ru9SB}k5FOtpL#c!Tq zLqf*AiN$f>nR0?L18)n>*KBfp75=ovN1rzR7Q<3>`rq}Nf?KnmI^T1MW2fUouuTYa zwb-wWPfZ8#IkD`6K3Kxg_f^Zyz$#{EYihKyoI%6AxZrnL6YRe+}x3x6QSI`i!$TC*O(}nVmnvi>FvrnFvV@< zbtZv+1mcgwW~cY@23Os8p@AyI+UTBeGnao#tj@cZ-OK-SU@jSD+ zmwvAg-)uJ9^Ls--&_*O8dCqJjzW$^*z6Zq4owvSn|MLL;6Co>bFvlxC+<5| z4pryfCtf%|-)V;J78k{(rIS~ZAAFS{>#!(8!p7Y z&wEQ!E5HT!q{W59zR^%I*TRj&>{ zU{dgOw&c9x_A^X}z!6_pT-&O2zr(})%wdLwU_Rt$_79oxfIXYN$;J2U`F20Eb?}SW z2G$fg^*$)gOYaA)Vq~udxfnm ztt5J!=x;=?5!LH7kNt=y5baKM7|}^YvxzPtxxuqD)O0~}$BSqL(L|!%iS{6B(`dewPK=R6Gl)(gnn`pz(JZ3b zM3)dPAX-Fp1<~h;mJ(e}bgfE}&FhF!M)X~xzH_DOGN95-XPjQ)O1lx`T(L4L=%bHh-MJYCRz%bFG^ztF{+8y z6ZNXo9EJ*^mYNKr1w_k;RuNT*HW2-XsOb^~kZ2F0wo9gb&0%6>5nVyFoM<)C2BHy{ zH3!p)W)odYw328&(TFRWd@9j&qTMgF(G0SPQ9!he=w_ldS4_O+Jo7zIjQgZG4D;gS z@kpCmWt(RitzLgoaEO))$|r6#x8{dtnb&3XpR=I%oH_HRHPvoOg0d@t-+%s$87iCb zfEhWXr_9OKc*21ABbxI?o(VobbyE)X=$9wtq}nlPDmInmns*sCB^H{?^!Y=6)EW=f z7=PCO3;%OPGxw<1Xc3t=(f}=53eo5(8crqZboUb3i`HxAnHr6e+FjQi&LWyNUK4P7 zux<>sV);&v)5#nE3d#z?ng8OaHfSz7a{xC*sRgRi$-k}PY@(G!(_L;4P#lI;}RWc=J5uH(v==60I`zA_gWc-1JgSN^;A zHE+v_ZX{Yk^h=_fiGEA8l4uptYN9nn6{0tYN)F8*529Wg&6j+M5kNGAXavz{qOnBV z5ltkTL^PRbDp4EJkwhmDb-F)Y#b_<(kUfl|-wERuiouS_`VS)(SD| zi8c_GbQEHuUPJ?k#u7~>noiW|K_=mBq6I`tiI(eFKQ&1<6Qi2wIii<{HW2mFYXM3o znog9PwwWW^WEwToY@!83ONq{srbzM9ASsS4tQod-b=LFvj@muZ(a2;c~)(gewRKsp${qW?}@BKqcW2!c~Mr30D&q zcRGt-4dHMNO0|R|2rGmm3D*TGH38xe8LO6qPSHhWulL==L?nXFU#Yq2N z#KX+OE`^i8R7nf%Lxx4Tp_T?e{7Ocf162Q5aCL~g9%p= z9!9vD@WX^_2&WUSB|Mt2LU;_}dSJCB97l`>5@^(#!n86V0x5nb!a8aQya?+FM-Vm; zP9*F?*x4i*38#iJO>cE2Mmkw=BaExiw9bQYHeu$JrWn5H2Mwrg6oujIbx+ zim-gm1DP0=Bp~(!h+j3~7KELRtv6w3W9vh>mgId2*Auo9mcljv{0MvH6C;2a5rhK? zClU@KoJu&Da5~`-!kL6a31<@yBV0f@oN#_AF~k(N_>~ckBwRr_if|?2mV~PbM-#3k z97DLCa4W)6gq8xWHJmSb5u*(WL=bLEIFWE1;Z(xy2&WT{C!9sNJ>fjU9SN@>oTOqD z?X|?{OakSEyAa+?xGUi*!pVed2zMi_5bjR6fp7|8Q>2yxy#y9jD}Wdekw7frRKm%G zdlR-1?n^j>a2nxk!u<#r5bjU7l(6`Kt6H_oh%t}^DhLlETuFE^;cCJ|2-gxGM!25v z!-Sx6A6bB zwh?YeID>Es;cUXa2p16UOSqJ9{%m5D6GKNj)@H&kgsTX<5w0Q32rGomgc}HZ5H>|? zF%BXepy7Neg&2t>Fq?2HVI3)L>4aSfXA*WJoK2V!E+A|sTuRu3aG8ox{)327K>{g+ zD+%jJ>8mE}Lb#T&8{vAw%&8>CXfZYu_9E;-I6`1i|AL5-NCGK@ZG?5CMP?9YgtG{n z3Fi^^AiRQb5aG3iQwUc8tNp)@w9HBpV1%m)n+ewv4kBDnIEApOl@?=N1Y(6>0AUuP zLMfK8nQ(F|>i<)SVRJeVty#!$IzTwf=>Xw8!YPDTIPJ%3_SZV?6E1h!C%m~;K83iQ zW}(VyfpCq}0%3))E>V+jaLN-lwWd5L96(r?q{+t;W`vXTofe2;b6QB&EMz#%31>OY zduZl)PIJO5oaU*T`C6wr;c_QV)67S0b~0=lqsobgXt>6S(>1IRc3Gm~2EsvvO|eLU zP4OeJB(Dt*dB0KSPJG2Eb1JVHWzO7`G1^>h*pxiZoN3H=DwKURx1ZC0wQ=bUtOocu`|W6@-rxt|a^e;cCLi3D*+d zK)9ap7Q#|jEdsjMxJ* zY{C-BtK&e?_>_{sG!k%*{hjkTWn@m}MXDhDG|5*I-sJRv#*xmsgc>sMN9F-!?gA|Q zmlP6k4vQKHKSvfK$bwdyFbGN3l9bA;6iepL$%gBv4LxBjL@2_Y2>(vFLBsjd z%fv8s*OH``Z~)EwVr;Tkeu zO;{oP9^nSUpAa^sXem6DZ~(BXe0)ueSQ7Y_uyeM;IX#n1<}1itoj$?4bH2w$=FUQ$ zK>_w4`3y35&Yon4lJeo4P|6~K9I~*P93X8;$|Lhn$$SN2=j=%)$y>;LEtx+|IGIv7 zmT-9rDIc?l;hd8TC%l;)m_j&L1K|KN&m&w#=G_U$lKHcQ^9v{dEr=mzq;zrAmWImU0IpIRW zwItt@@Mbb!u3{9`Y7$5xfhrOxBAiF&iG*v&d_7@>@RNia2y6yX&i8uk`g zRFwb{SVID_ghvuiCd>(!k^?@3ZDd|TxI9$zpmAE3%%36iwPfEHSokkxk$`iSvWx_* zWFe2t#}HmYcrD?zgr6r|PWVm2n+caY&B=W~Xa8SC0vRMwK?44SYsh@86NhRs>_J!| z^JRn^2!BA>)Jv0JNH~D-I%of1Ngf80Kr9K2cRD~GMiEXX^XY_bgtrmSAiRrk7U2rQ zd4xBs=?~@#VjLuawS@N(uA%@25iTe534~RhAMa-gZzl7dgsTaEM_3`8|2{FKhqNSm z)oFn|3?>{v=C2WsB|MREDWy;+!pUU5m~aD`rxLbl=K0b}Vr(XXWWpKbKmp-OGVew> zi_Di2uAu;j5Y8j>NrYDrew=WvDv$IJCB|A3m`u2gJnT%koXnRHZXgdM2yZ6yse~0W z4sLgiWbhmOVx|fbcxRv4jnTlL?<8Yy%!6 z+WJyrWRSpHgtG{LPB@S7hlE!U?oW6v;cR^G_vOjvt49CXQoQt-q$T-mX&%sQ30>Ew z*JqeVIVbu6GZ&c;F&=T-9BmpoPIHV$F!v{Q4{a)C?)W%qw%OgZX~hY%37=b@_h<)a zQ!GTWza77aaAs4byPoq3amV9<$AZUmlUIlwFy3d}v?&X+(&sswFkzFQTqFm!cv6~y zAWoCA@Nb?pN1D#>E|O=2&XAsva$%4Fqo!Z76oGs2BKQ}J<+$JoESyD1Qz4WiO@_@G z6j9;y<;8MvUsV>4=BffuLU20XvNSh<9&fZ3A+`T+r~YqY9G@>Q6Zy)2&6oLq#Nnv| zxp2)=xi25TR6c}Df#@e<^4hXPY8vbDF8Zj7y%wqB6HR4unhA|fbPuunEK=;J51I+I z1eSZitVfd>KUgf!b*b>8-FP0=XS976HK1aDo>lDK^LjbLFgb4ch_n&j^Y^u#>lZcf zt9Pvb7?r=sR+V zxa^~Ln@LK@nUR+hJy$JewMR@+EzF&~=)^>+@~BBlM`~$2s7AFWsTv+8JT`qw&Xj1i zf+M)d0q_zNr_Fp63-Z7wf%^_0nIE0ew>RFUhGtCgw4!>pNiaHNpeknqFM=$C*H_q$ zeNaxdoA>}PUA3DG9^$2X9G#Iay2O0R_Ml*fkW7D&r#hJN0B@w~U?zAGjSgz{s{p*j zvo2D?e31fL88C?yQgQU+#fzs;MIuO>_gjz(NdJWSIn$yC_EsINRGCiRIMw5-=Dd-b zT=fGyO|AyKnl`YTM;>3dNVP7&Gc;VAV707KwaVIjFaw&HY8TT0Ub8$@+t8}J z7U?zDM$4N1C&f3XKm#1U(ZnefrL%P0N$faRGmkrreAs=m&d~}l}muy3# z<_>>jvmE87x~D^v^_d(Lf6i6%dK%vz#dG6;MT&&I<~R^5m*yn1WFGFCjj-K;eG$g6 zEpl5y2W^pC<=0{imjIo9$w6Tsb>T*W^dJwlq?t@0K5y%Djl!L+PZ!}4VU9HEB~wy> zR0iGe4?MNPJ;9qEsA69zr46bdnmJ)FHPFp0amN<9O}=n7OSp;;H?>^hCi&!{Ze=m4 z>_tfuml&rX(qh{j>%5JU_f*epOQuJUW(oK>vUvH{*t#rvm8iOa?T`Sy;aFrr% zSS6oxev&E2UrK*Tl0L(;QFAx-0fx>jl97pI%#1YZC1Z|Bs#q;aSMl6XEg;z*%Wn^3)<@&E$yd%BPRsceU98<3z2zrZy|DZc@GA zhZ-bzROwAUBg*O5VD#Mt^FNb2v;U z1?9|@f|BN-E69@E)iCfQ-^qSmL>R?oTAH6xArB=NAkYr zF6u?cZSo+$K%4>dPg*be>()r7Y`07;ZhYr9Icl14w-%`@N|cYzTSA@9Mv0<&=_VD3 zaJ6=m{B(o<$1D-4sn&xV1EqAUPWlp0$T2Ox%`hLZUCV}9+vT>w>B0CCG@c@-U=!w_ zLa=XYs7@M>XSWa##q*q5GETRsJdZG)R81CzOmp1K9`~t~2g&#braavw$v8$QcXZF@ z6DsBC;Ob2}>1*8YQ*c}hXEV%SuapBt27XQ5Y;@^!dg)UPi)WKEiaKl@QKIem0%~$15gISg8AUao=NiY-gPJSSEr^ve2k|@mx z{LNi*)bPwc2I&@_fFHHMKZv0c-iU%b=qpb|qw-=xC#Mwx7$-HC)7*`5x;tyP92HzK z)F2HQW{{pIcbj4U!EQO!y*cCPZn;}<;$(xg3y<^@C1VrjE%#_m?65s@(0EZGDn(Vp zZXvI{cytyCQo*wZ=^UOSVL8B63OF%I^4dKa-PB_$e{P{cDkF!5TyxyaF5k6Bju9Dq zagW?5*lWK*O2m^~qlKs$<^%R>CE(e;@*rX1$X*l?>1P)y?iUxyT-Y1XBFYRfRw&M9x2%&&JfJKEz1rn7fwU<$vgGa4A zqHjW_68K#_m&ope7>NYC&F$%e%xF@X6oiH#Xe64~Bv&3@rFBD7tK?R}sXrN|S$Ld< zxf$l~RLN6<)znA%io}grtbC}Hdfq5?!v_RhPFi^QemOKaqKm7Pj3@o17M*68kJ&Fz z%&(Z`D*b}T8Q%wRv-oDtc9ovT(>%Tc@4zFS(j0#fYw>MvPp39|Xz-HIxS-L~`QRYL z0eNC@?enfu1DnbI$de2&qYI9|?HmlD;Xa9@Qh7Oz3{DymHx5)HLQYDq z`sgH=Got_H+YZWp!u&^=Kb(EaRa%KB_BSmA&9N47wD!nk5ph+SK_X<(i6@HK&pIrkg5LWh zJYW89;U659BNGMa-^NYa{kxV7|HjQMh=@o$@rc|bf89_wp#n=ODQ?n-c*J;7{471( zqyRilOv*%hi^lF#D@jiK!y%iE=O?oN2HrO`!z|EM3Y>4P_o;1>?UgAf3VR4ezElgL z-+Q`A*I`jm)uZA1bRE8EBlK4tc28QMids6lrZbT#Q8??RS(f_tag&bVX;*DhscHw> zk!Hs%S?0T@>#Rb1+Y0S%v#M3;yiJn#NIdBOy}ct*D6`!&MT|w7h@YU%9?p-+9S6DT zB{$vc&H31;j|}T*L=YiQrG;T9rVomjPe(c18OZPqHz@&68UhK*Cm)vsy;RGeBbGu( z8?H~}1>eg7CXwf2M93#p%OSk%dpScC@;l$l_mX+QfZo+8a%(jU_^}^qb6rI z91rwINja_!9J55;s4=i01|E{{OHDCYxfL=0#!X_!C=Ub8QecjU6gb^o3dEo`aJX83 zDxD!Yy3WaN^M?vS~~niZ8bR8}N1 z5K>XONo7SwEgBUWDV430kz%2ZX=;V}yubHcbn4afJo|b*&+GO4v%Fp(-k*E!xo7S@ z=iEEz&iBln82jF%cUKeMiB>nEKa;l+{#wuaU=#BlKiOmz=sWZ=cj`&8DvA5Mq?|aF z#PKIhNeuQ4m_^n_oirzD&iQd3N1Q{&&HD8|pWhy>=Aqu5v@RiIk6%JgU47%^1aMSVzs8+5;+p-dwH=cF(-x=#7|+5th5tIJKn1LA|J0} z@7EXk3~71mzS^)pp6IQb>x0p1H@d&Ks((>+7w(T%SL|m_x-`-|pE^(NFB#^CxgB!m z<#A6pvXAP!IWF7R)aFd~?fKL>^jI@q)Dx{PL%m1;Yt{Y+>$m<7d18)4t5N8`E9W-K zxL23s^KEkuiVj%I@dA0Bq zL#;#Gsqh!J>rvss?fN2oV!(OoC2}P4`^Udlolgxi)NXX_#eUJ8C4&v+M>MzZiGUtv zza4O%cVcffj2ULA#fVp@vV0c?oTr^BJKW3;7yoRB+j=)i9%ZO$NOpMK=gz;$yTf~* zJ4cL`9hEDPLkbv~s8XJ{t{`pBri7w+Ph7SmC7xP1L8Z`dQ#f@~rdT!6*0;$l^0{_6 zlcc2ccIZRDYKL>cP^&~fUpPJv`1#fP^r?or2_2Nf+u=MtwRayEW^)~yVjSy2dSrI& zB<3@h9nQ4OKEJ4~DYFb!fSj9k)9FuXqODJ9l2aMHcm9Tfd?Ew+ermwCCiiptfPLki z&LrQU?N0X)Ns>>J^|_}%;OX6|NjDm5COUny;rmCY^S4owxhdMWcZ>7PkqvVVHS8us z9r#8Ms6v+=CxodJM#Q=ua+8(AP>HK+(uhQlZ}k^Wms3i~mTHf2sF<zp=IdV6hJOy z9%0=NcvOCrn2V(Ku>MCxTG3(F2U*`u`X07F#4>5+U5uHFq+ltuix(l$gzkMxH637Y zBn>sOK8QTBAv@(FEd!O4R*BqrA3I!))}l91EBXZOL-7X_)M;o8nudzda#VBBo1ivg zI*~e*piV;PpbRt_O-J)lDO!dqPz`EC8_^c@1qz}ckU=z~KN^D0MVFu)bPc*5EkH{U zO-la>R}t$b?r}~HzqFBvNAI6RKbj;!_5Ja*0X+%IB-$m>lHR}3q)7yXyzaLif?}yd=e?F{6&n5U@Kdi==e8BmfFY`NRlkcp9&ILZ>pmVVA)bE^$6F!KT zQe1e&+&|Bmo5$lOt_zW=3PqerpKkwqgTW&76JZsL)#mW=L<14ae-dsFk{``6K zxl%;(*ox!JzKsW*6Mdf?bf)@Z4>?DSd)gsSzsQ<1YyQmnx2k7k+4bmX<-la z2|w2tjERf#%{}TIz$fkcuffpsY8<}X?sE3?tv~8ai)iV`yat{{l=3gSvf`BaKf|cM<~a7~AM4s2W=W)XRiw_%<0Oxs#g2*OFOhPkDYjks ze?zbRmt>b4(B=M4+2&uS?3^zv+FTmfWEZ7QI>Gm8wD~(<*WG+Nld()bm5(7J^s$+6tt3sHWor+;6DqPOQ8JTdkA|I zX%_mC3oqP&-1s(FL1;V!9~u&uQ2G1!akh~z+>4gugYc63d2KHXp|Nj?f!{?~yM=UN zJ?g}VhPx%~ea3^dG0|(`43voX!0#TRnec6oDs^xrQ-kp8F`BQE=D?T2#MRonpm$#l zdncJ{89vo3wL<*UN}c{J?Syy3zdWaXDcp^uuY`omm4wo_pbqj2!(LFT8*jp0n;B!{ zgK$MVFHQu!68?P)JNjONS2bZ4Y~xwKtOyOMOSrvhJM$t)7fQ&y93K)ow?gUL3a2uDcH?DmJ&kd3CSJzQT}X~V#?VPWXfK2H1!%hDhg)nf1NKiDxaW~B1NiVB zoks@r@kgjp(q+Iu;3wL@oCUc9n2e+#xifeVt;Wk;z&%H~W58F!RBmRP@NPKXbf|5T zA0FuAuue-*SCI75Cg>#)UqpIH0Nx7BXLLE#O45Z}Py;?7X~g53@F8({D?Xn;JWTbFE<9&M zm@)~qXTa~gzYkL`7F_3ssjcZ@R%Z;r`F!v_p7hYzyu|1WFGHjY7oal915ds@OjSrZ zaKxN2)qu}{XI&qrTJUao`wd~L3ttY)h_^=&d-sM0+Cziz5{#c#7N&B@B)l6H;Dvip z2|ft#SVm#^kU+c@kk4Hcrq+_~f%mkAsdmW&)3|5q#@q3D9{%)Ef)%KbbcCx^yzo>s z8t;a)2+C*SLjvzskiK+axGExD_=GE5Rp3Ko@u4wxiM4OYAQ(*^;gqpNw70NxW4*@% z?GkW*aZm{g{KoT@4yR(B1u0M zR`+gAu;S~<3&PbPyl^%ek1v8>-o}gJL&D)d3!O{I{Og8r)kq%UdC!HbcDyhbb>cnn z-nYWl0la!UTseuJyE^FX(AyhI7Z&XeSDCC3-h>M9rLcvkJCxwt;1QneAR8;5=x`|y zVJOFE!@)d&p%L$f?OP&L3qAmQIwDk;KPk35XBO5s~&9Dt;kMS0cmyI9FW5Pn4L zzYHG|_qXN)yh_NwmUQ83s1+X)`nRS8loR#uB3)R4OlAOtM*n42z(Xt83DSj+qnyvU z{#RnIcq~fgvBCp4)v^=#HaP65C{>Sl!wiPh1^D3uO-Y)h~`nPWa6Gu zeS^dYVc)^g%7f<~Q*nQ3Z6M?x)AGU&B>B1DRP&f@AUrN4G4bvk-)R(xX)-K2eBdL?lce6|HhePl0D6sYW+^2W64o3P+tse*9?o zG%CP1K*c-;$uCSE9j%I`99V}My)4vY_A}QYh(7>_%jfm)uN7Bh795PP(bhuzy`WitUAG+1?Gav%NY~HVYnm zMG;(qIw-UfHlhH&3BG|k@on$})P;|~ELsgk(gbPnRwO&I2#%bh4@~%1BzekUo$c$r zm~WA6Z~zXyT>CT_b_J&hcgGR%3MA)oF08k`P+h6hh1VhJHwADzlBNv6)28a{!5}yb zb<2T49`35VhglGlmrG$g*cje9jSB~!$3Ck=NOsP=Dq2lOshzyv!@E%gz8wB(I%fg- zr^6vvYo7{#xJLUPSnJU{lr%G1y@2E%a_vmc|7&LH1rO}8y|8w+P8Xh;$6kLy^}ePDeQNxu9ZZ1(RDNvJ2xKQQlNba{1TiG4g^!g{llV$F@Nc?* zxL~F2y<#@pMWGbb3={6*O2-B!+<;_Ho8k5M63XNbrVu*sv-<>m>3;3kL*E1X1u9_b z3hjkK+Y1*xs1HE+*h92`8!ue>5N+e*Qj9Nwf2p8DvJ<87#)p~KhA)Es9?@-|2wy_w zq_2lhKN_tH*|q^zt&CO%>8s({kMVv%x*NV?`(`-gaV9{Kp7l8Azxf1LC3>>3%^$6n z;)Mfi>Am<&xCvQTM(BKss^!~3x=W9G>Dw59Pfo~%jM6|$3PwOVgg3q9oFF8u#GqgWm_#={L>Va=Pt9P^wIvZ#* z@|ds~$+bZk_q@KY#6$1h{3{?+2ORu@E+`ddBDq=#3v6EsE7o##B){;?bsQN!4NgR| z(^+soY9ajq{Jc?@*$HD`(q+cO4anTd^}iXD^0Lk(%tula1u*p$j$kKWe1om)X*0Yq z?^Q0>cwzNx`iN^`pV#%ai7={((~dj_d<;qRRl*?ZCS6$ZhL?*D3+vxtg4G5tCiq&| z`X(#!!uy&z8vF_v^Ok)CaF*@!;8SS&F5b%FCny!)1B+T{f4s0A$xaCSY_x0Ii+OI7 zUa=N-qk1+FHop_CmgC!mt$fP?FWiEphC1Lm?`rRXDevpEKzJ*XJd0rX2XscZjetc+ z`igMjX4*fryDTC_L=Z@U@ZFDe zM{9$zTbPzWIuF}cwI~W_a4i;<^5(V=mi9)sZO&_Wy!wJ3a}HMb>y8eugSZ55Oxr zbwNwv_Afc7N$-LW?PMqL74StQ+cv_ZlFk9BT{K^pu9Xfr*t=U7lnNKvz68E)`xY4X zm3?F|fI4I+VCvWQws5ZPi{Oj4Z-g=3y1nD!jYzHmMKEiRF2^foC6Z1dOxUZBz=VsD zq?baT>uyz@@S<<@fMPt13({%Xp?EkMN%dyKuzidtcGKzLcqHk(C%p@LYFQi{yocCmrBiCp|@oq#)tr-*I6gy%KIhQoXJ4Mfvn{HLxNNK8aGk;*A9EM(w0`!%0WD>fy6s@=x>$ zdExw z!3f_*(ljlw3sLJ{%cL@4QI1krgXG9+VFEKo#hWmJ`E^o|2^*1IN`>E{l&=%iVYstD zN68?i3ocC}59z{IB<1j<18N}i@5GOWEy+A`GLuX#EKK9^k{S5v@U;{}HQ}4!Y#uYY z7M};NJK0bHd;wfCQ2V9upv&Nces%&LVy<2xc@D#6Oubu)Uk;C;LF7@V8|q0UueI8X zxq7fZG7o$SWs<1_ZWyAEvKbCMLwgsz<4i-9lcx+0;hC2O`1SDhVTP*5H^FIVFg9zvtX{g*ioV#%O@2OI}@TPS38ea?_9%ZN= z3R($w^I*+1(!1fr3_2aBbQat-Rv%awTsn@9Ngm7CYUdo5r z*@klM<@%qCNt;3&vqJb3k_%2f9DTX2cHzlakWL;K+;JtR5WW*WJk?N(@ci(E>Opca z!lYd7g)d%3&5@@O4*L_$f#-)S)EqDWWP?IDZ@NA&i{L%iu-9ZNhYLOI^*1yDJY}Zd zp+S(J%&@l2f~`oJQW!r=S9>BHhorZUhb1V9aynr2Y`v}5z?7hLRtWFR;~d48!AtWw zVth8d4atr!g>OnaHPj6I&!Jt(lLVhZCHMxo9a#rjD5rocnlzEVjn<*feVDWlU)Mp7%?aMGQ2 zZNV$cIAHSUmU&5JLH0;E;BR_^6gX%ZRZJ$~2X~Qt193|d_HzBFs^)T*k{Q~iD zCz1}^1=rj|rzU?ryx=}gQLca)@Ztx!0C`!FiHTW3r@+UnrHx zKL9pHsSCuPcH%{V{jGLWQ3oY0GUe?nRv*4=-FtL2R1=UuvZG z_y~VJ9QTrLzVWaXS*Io3@G_Tb^0&e(U!zQXt{3w;s+EG8xVFDRKfot#z#}<=GPnnI zlOBY1Z|Xwp;fKvMhonQ#TQt*t#tN_$Nxu=E_%;Vbx*KMoRD3C{L0&ftwU~yDR4HEg zPb5blfJcz*gxbVa4rP%?_$acf7e0+7zxfXBii*io3HPB2DZdr}E>~E_3g&x;+KMVk z=LgI91#K3}WySl38igwG8E`pTiLZn;s1naFnyKf}YJ3xvuXi=@si{!Du_az8-@r;D zk5Inol!F(_{CvqHly3qx1i1c-k#F7s_ChAFTj@fXc`kW`OcS@fkm=i& z7s^cSCQebIOly|>LYZYOwIck+PLGpm#pZ`rCZ;@1@|}=to*%)JHx|p2V*0J~RM$~nN>+uuB!B^jH=A4jOxs4Pjx|cQFTdmX?0n3d38l~Wp!?yQ*}#qTXjcupt`F%Sbex!`3=A6Px8C`seZRV-Jjvl^k@5X{T_dTzsO(WFZGxC z%l#GpN`I}t!Qbd_^0)Zg{AYCd1O6_5(0|ykYK$7QCaK0%lUn1hNw3MM$*jq)$*u9! z6x0;el=v>W$~`%55F{vA)Iw1TDbmuWMJ1S0+JacXsaUF;2nnFJ++y0< z+r!<^sOU4d;ij9<=AN;s3>C30*pwHcC^}T8b?f#V)~ZDr6zRzS`#bmMrKLEZ|Nq|) zB=?@z-}#+$e&_dkQuAz9&5LukWE)Z+o1G^=X1>S${@sHrqW{k%4PNyz-p?diSFIM` zLs#v=_s^xl`nPq}Kg9dcRj2TMpLyu24@Ei4s^j>6|87ggbNGJSWLWjA_#U+CsQ5lz zp?wb#?+aJmNqrhB9vhz;vKuV74F910Hc9_$myF|w7={g%z8NG*9Z4ef?;R?zCpgJ0fureK??3QSNix*@TAtdvXSd6Qs=wU_%kOd&#LZnJ&kr>bK*mN z!j**ryH~dl4K<~YA9dRjBUP%3SDJU5QQG?8j^yQ|@4nlV(BNiQKCsNUy%74rl+h+> z{;*K}l*{Em=bEW|G_e6qR3O=2X&2o)`OSh-=q3&(pH%pcCDsuqD7L-PE zHzj|K^v_YVu@@7{%rN40)_1o4Avm?Pr39p||l0@x%l;hcvB*{&~)B3K=5j7W4 z!7FAbi-B);&BLo`gKtFT*Cr7Ps$A1|uka2FEx%^WmH9`a@auigcct>>e>Lmas&ZxT z?*8l0t>bnkk12c`xJu|x8)h26u}Vvx@3Qk|MTd|O%3Tdf2?(Sa`6E)c$~%)so3=bK zZ0j>Sldqjz&?^e*GqBk;-GG`$kf_bqF4s;Z?55(~JCmolntp23a3FdpJ|?CwTf-am zF#JPIU(RdES6%hO&!dZH=a!$@?D`_o7g8w;SSk(NZ?S}iUN?Ho>KqGA!7^HESZ15L z7Nb+K#%5n98*AEL-hJH`gDDm3vS{k>O-A-i$awvvYf?WLY_Fep&bDJ-uWg5-z2Rxz zzhiHBns;7%3zc0zrSN&TMJi@JrDaMr=TqLsrm4Yl&BuyI^YUd&X{S}>OviTaJrjvwRa16k zrX@6N#zZ;TWhH#rGv8J`y{yEddQO$HlS;`cb%D9X(_<*Il{JQw4~`gY_pP*jVBpi< zK$qU$;9d)B$$V!SPu(hJquDHJI~kQxLoG1-v|6YnF^?tKX!h~bucrVyR9sfBm1j3C zFz;Lp$m;glJWbjhp#jv^yX04)J3xHeV=?%LR9Z{$X26 zjgl`4z)B5(aB{X|usse2tsU(TowEgw;92{ct5|aGrn5xL*IdGUy}_ml>CM5$-eAwT z_2XD`k&hQ?fP@Q(+)LpC2a)NPWNFzRnVa)0gbw@>ZR@fE0mXz>Pfr=EwxS2cd&+%w z{gvz!>dMH*mida6jds33z-MhSkCRx7HgnAuX;TD$vhC3i(E z$Fd_k>EXyf)0^F`NR>shMKuq0AG6Ipmbs7Z2{syRk2jt9a7e2@rip6qgy8;&yUAA2 z?BiumViH?{=em9VD#~eZSLXG04#(?|B0dwj$m=v_HkL&KJy<6`0IMu>@HtJWSB}Mi z5uPHbgFW^&lVx?DfhAuAd1D3gMvEpb%ayU11zMmOCcgyZ;|Fr1sM8R5u;M;ixf`EP zNx*1FPb82U2{6;yZy1U_un+4zt*+5FuW4&!d(uc5GoX4M zMU~h+RK>@Cjq<_0jwPTB+8q6#cEKe(qq`uwmS``06-w9(I{4ZekSik<4Tiz?yPm@s zC10@1UaI%!!-l)-mz0Y25$LYOv4O)e?CGVXZ~^!etf~SF9*-c=Ru6p?!b$u!^iJB7)7=|2w(^U*d8CiK+umaVd08RrSI&02Pgmdy_7Xy!+ML9^NiNzu~<7$6#iO&m_%4B`P|WG>|ek0%nN z_>3Ps*d56=N9!V8J{W9`blaM8%{vJaTRsuHK7U~YH5*UWP~H|t`GxD{CfcsUnIYn{ zU_cgTXRVcn$R!CQi1ApmphRk}#b?{$vH1AAzY>7!WLN-#4vSg2HS=9MLeoDf%Z#`K zR$Jjmbszi3qN3ulqHeN}Z$;JmKzRpWno!;@%3~LF z$EReVT@YLR_Hgoj69(Js8aED(tdp(0e40h-9Q@#r$T};}-y$|jWSxT-AemObSXKHl zms-kC{#ajG3A3qh+BlX0aBNY|(-x(rl<$aEjh?cofcd9C8llI+O00@qnP#uuH<{fxn*%l`xswBfwi^UA7HL|e{t#_H*Gvlt72I>p zfnhA!?Xd<@WR$1wP`XdAr4@_7!d2wFB-tx`cw#4c0PG!R2rGJcI9U*i7VJ+dwrmey zm>r!gF`k3P>^NYB^Z*o=uzStQeJ-=0RZe4!*cJ!7D&!b-?JkF$sCW^Y*nPZFZ2Zqjbu@MIW}|p2p5fc3zyl!NWFpkFBsr zz15(fdsp>2VC8?QV#aYI)&3fTQs+$we%7w!_I8fI9v)i6-`Db4D^A&cYJoxVHu@rg z_9A}#HGvML`Y6k@uv@H3piS`{SG<5%91(S={T>!hZ>cm^JPTqT>@ltxuND}C5sax# z^)%Y@JdLJqxs;#!gJ`Iwz$kSNZ}IGsRBvM>u&dI@Z(9ta=xvV#jx$XDX3RRcz|A;9 z3_g_MOGhKSWLxkX` zF87yHK!!?#J%+$!+AD`DM^?Oq$-97;jmwv$219ObW8iH1;i84?5Z{P4WTMgWod-GqO13z@M}l2x#ieBz9x=p{092x* z`@a^Ts4qSS=$q4m<*}t^&294val)!E&kfA7#YUh6_~0k?5_*A*u*RgZhchs%(Zg(>*?p_oKkhpgLKe3v4QTcv2LIi2sJ5Y>@)VLD?$#T{NUa`~`rg z&a+@}ih&(M8V`vSD=V}jWex_ipXE&fbQnYi+hAskEtOXOvwG}}1yisu6i=68Q8t*B z#TM|xfDa^GrqY4;fXV?-XiP+5o*mm!!XZt|r~rDjf71@^LnVZ~0(loxNJm}fyJhSw zDt|fOCWis&uYtWW5Bi?_($El{vpeiHD=Z<&gDuw|NK#aeCs3u zvIQWoHix0Ez+X5=@cK&Oq~)TlcGev{SV|qOp8}FUM$BhFAkA5i1Zb6kPi9W#C2i5rSx=XSEPAeF_QNB-4|G z&nNyCqDl%;QmS5HRSK=Fz^)Y9SvZ_R`!)){&VT&J2H*e9w=}pK{X`qoQSzSvtkc_* z2Y|3&Q(BAKw)}29UTHXaF~-Ob{1>4wO8&D-dEpOre8!fI922>ATCeYa76iLep&bZV zf#aGRC)(;v5Mo$LL5n9MwRmhGxIMHeSdKZIpXH=Se7oqIrD%z%q~b=R5RpV;OMX_4 zQ#@_DE6ZW3ks}e??&KhMLO{1Y`YRO2Nl{B}NVXj{E0#$1h^uZMzC@ki{47Um=Nj>R zIzP*i6Ie+NW*`AuJ%6Udba>(%?DU}$Xp)la=qwaPqn*ae$RK{f)*O_OJ153ka|Xt= z7!92cmV%K91~11}R2I#2H4J0rj+(Pq2ku}aT4Yppm!#TM1tVr& zyJ}vjS~sb44T>o;mE~GM0opKyb?-hnet@A&)CY3rBQ)+{Hr8Cwx2Kx3*V^iyMAHob zKXYI9)qyNFq5vIMU#WE__v;OPfz>OnxDSirL>dI%is$L+5Hq`n zOv}C|kV-wFsGZIN6trmX)u&=;STr;3j|RY-+G%Pik%7qU`}~gV3co#jzTcW%>9=G* zQlJWG^z;xKO@MI(pp8ykyn1|%5!z|7i)9G* zSl4O}*ypFzCs-pfSNe*T9dY<6{XaDEB{kvHn)nYzqgc^<%Q4f2iyX!o7K5^){d%1L zo!PbT`iBXK+5D_j@G6Q$yln!`q?|q#oHwAnwZcbVNflGDG*oXFfAI5|lBUbY{|a59 zZJczbM6&-3|1G7G{e67@2LG?Yvy_x%NYeg{NhUcd$&A7iFr!_IC3_+MEBOBk{vRzv zCHybKa|r);<9`~;y@j+-@c(PP%lI!1`~UDKQ(Xi88%^f^%_Kz=ke{4l88j@J5(W>k z4jVRl9W~OtR|+`xF=L*f;D$<)q&A@lK zE(@QiPIQ$mN_8qvIw^Zms!Mq?O?ygJo=nxA(v&A1+LNOpKQ%{r(vG+Kx*PEc&GnCd zgk1sbHl(mKwT=Es*%Je4U>Odiho4#KE8}(0Ysxhn4HK~<0++FFcAoYs81!Qah3-J1 z$g2dIDj;jAn}cV`7kQPSR_mtYGb}#w5GnPkPtL(NJHT79#Tdk22NU(&b<~DUeIFpz zyh&vw)ttj*RWxJXq|{A88kDCQ7Q;3yJsKx|Oed=&BV**NA%4uuN4gflQV<;TEAO@TO$Lbkj;HQAQ*z=+sui`nAT zO69uuulbO9c&CvyQ?^`k%xBAck7*0y0KXgNT{KL`wl0uBv`r95E)Z>lzzu|w>W$jT zE{5D!AR<8s7zo!U5w50_&>~9V$W(LX?Mm&?Y@}-=<1jf(dUmYJ_P$kmqBT5QfOAZz z~Y%+hXE#l*IYp901#zupZ^a1^Ep+|4-}j11fW)6u2K#tU3WWhNjF< zbuyJU2cBG%A-0FeNM$PR5M-pWI@%q!KeeKS$a650+@R8C!F&Du)SMVwBrPFA?(K%q z{r4&`!(^!11)`^_W|ZcS^C-cqqjw+gc@+}Y{>c`p;t47R#r6HMBBZ!JhJ4~NFHqCT z!orn69SrINKANrkjO}8o3bu=R+OFhJeMw#L3U7s;oFP#wBBn+!(nCe`T@d8}|Dx&h z62?VJv8XZ^0FnNm$0-*$zJWx&X2{k=lER*DpdSnLxTLN&tlZLI$*J83dA>#U9``Ne z@BAIKK-2!H)yIR?-F7T`TVC~X7(PRAg7B4A8ekC2v`Cc(*3D~>1bfx-4A>$|x?vX4 z*77dHvGRY!sePEIC^3dw*lL`d$>R=x!-fl2EO8Kd>;91Ms z(Uo}>J(#45Z8N|kUp*0>dm$xQ)dy385$C$mmHAqBBV`YnXbHVFW4x_UOOvvY_OZ(n z`f`SI>{GzR78d9ZHd~thX0h!sXRv@{$T6k7*L`HoeW58?Q z(6OPdg_)+9G7$QKZ#yu`+m-U8VeDl5sf89`nHbb2ZXzXMk9pnbidXQiLFpqX|2Tov zw=jP}ah~q6$c0VqNf4LLoGIE3t_Yfum%^5I95l!Xbu9B* z(vPs}jxufq%S<}5y(DCr!muOaFHANMDV#?w;!C7uo(7l%+|0o7b>kw_XGBK&hYyxX zlgSRKo^~I99GSruvpaBn&1cXWOonm`io(OXe$vilVbl!kM$N@VWxRWXHsxLml#-KK ziJdj2AJLV4{q<_sV2L#9<@dxEKIEL(j{Y8U#d4{@Ksk2Od*Z^~N!jA>+F)$&;n zG_rz+kQDRJ*)Uj-(wlrh*2rfuykwHP8~sx-nDo}*XT9=8te)2ht7k|wX;JeG%8jb| zQKbOdlU`GJxl&+>4xHb5DU6_5u0%CAA&abeIjq!@-WL38uRbN0uz894*Y#JhCV8Qc zUyEMC!s!V@xo+AD!-9xEXwW~wg4G$S*jKcJ*DD~g?f%HO-%yVX)+qbssqC)^!#U8D~#|Kw%E?rJHV8c* ze5uf7fP+AOO0hcKUR27RLfnUueyjvY$?|2Eii=u2pm2v}SWSw#oFhqkMij@|2!0$($mNCX#ZBO4ZyRlCydt zRm?M+4)YHuqY;gD5P}9ui?fchl>k?BaoVBuA>KU+1(Pd?{|JW*vP9B4;Rz&CB|# z@j%FP>_TVg?7Q=T!lGjKA+&nMQB36#rt-3wHx)>+)e4V~yfl`f1bXXE+iLd_!aNQb zc>*J0#9$~1^IWw(-SCX5d&D=LH^^jD7=s}NQYpWsZ+0qhJ4}aowm?BLDx*wY#I|`b z)|6~PFOQH`hvgC8^90_%vHkdSbX;X|cWWvFs9)2)y#fz;0{y8*TkQt~o1Et)gBd!q z)wWOqfVZ4$(t%BArF&q;zi^FPFwAxRO}L^Bo&+KiFg97!1O|=yj;n+=@@|K)p%*^(*DWNLP|txdM=FfLub=X7qiIRKRt@PyfzDo(gVtbUqtkk)jO;Z;bl)?35m+HMio1C6y zIx7y}!$(d8&pOwpyynDO=_Id*5yT6~=}qqNTJw<;fs=Ln0@mfgksJlqbXf8B40!H; zet+!TpNWO8E}jtlTf34MJNMtw^4T$T{V||m7H|`Q`;xMvV&3)>9Qx$_M)sCD&~>wt z9%WgUl8VvM2ll_6>MY2nx3u7AX8+JIfW{Ipn4+Y1uBYTFVcKC5jK>AoU#2}uL6$%u z+mr6;1s}M;ZhFsPUlVf}I2|{KQ_?)sI&_vRJqqs;53qK#ibu`HyeQ-t}GPH-_%Ix#e zvo876EF697rejZCtsk^(!NATnm2j~5^!r-J{XB*Elb6EJNk8y4l(Z+F_mGn=Jn)sf zyUFjXi;c>Rz;{#$w6nQpHa{Z^$Vm}aH8v^=QZu{R9IToOgXRb}e`+>L3~?Xwr||Z1 zz(_IT7=R}U z)uEEpP>>`{j-bhLTx-%D`S0}oW4S#nM}|ai=@C2;UVUbDF2wc0rv%pr64s|iW@A)9 z*OFON-$pTopsb+#&k@JMhSGtlK%b=CY!1&Ad~A5Gu+!mypKC8~rqexY=MpXcUmKtU zB=HaR1&ftd%#HvI$j{0m7v#bb`L0w298GB%l#iP~CX82Gadby)welf)%XhiJNu}Z2 ziElfbONTOEXf?388B79cYkY=%#Ja)q%%%gom*Cfv2 z=E@eiTp)Q2X7Tj%mmNS8lQYn$A*1tZq>(C{4aXtni!79_!JdeJ1(ghjp-pwA?xqzH zJQB(L*x&~!EzIvq8}T*3cNm}n#|y^rUN1BtH(GaEDCjVECJqd=dX7SGhywniDh-NO37^ss?w@^>W2ODduSSx$6t)qB=D zl*}<5&;@QTI)N<;8-#jwBUF*37>?5%Qe(t+CfX}iAJ`6xW$cVZgwxM3p*An&N921n zNMauDREsLWvF-0Ydg&pR`KA2V1C{*l7|QKY9{|0Jle=U=?PNjj`i*D1(Bwt!({Kr% z674%-h&tOPAkZ-8jALwogA2=M7$EM6~z1X=~#&@g~$kE-TU?+b;OXx@5 zHReJ?h5o4l^zCcrB#oNR|EN;-jfccE>fOyN@EeEB5K@w_;Siw43mv;p1lFi~c(QQwU%kQ4cW56N+l z4%1*&hE=jX(O6#yZCS#cYa+dNECK`Ku$iyzj=eZ9<}nYBo^lIBdq# z9wAA5ej60>cy+-|F%{0DCi4I(&NsViiR$moV0%EbGY|5OcY!2Aib#zgaT>}u z#4!-;%8sjM6HR%FcNdPB%y$8X@Vrz!JC#7AQqrzechr}(DJ8w|2WJaXCrTNZf3_}7 z@$RiVEsw6kQVa~AEjcC0KlnA$h-IEemJ&F~PKwk(=~r-4OtVdDvMoy5exnU%>vc)3 zvu*ZuvsKSQcR- zYi{D>OmZ_TX*YFe?hCd|(*iCIx%aOhLEf0&cGc4cZ_GAWsie-K>H90CBGuc**MXG? zR(IId>NY^*NrNK44LvhYBMY>ne>0+a_C{7aR1dG54`lD+`6CIpc3){3-**Te7F5~W z#sWKSd9t_7S0rL1;M+-4f|=RHpMW(SVM4*ZS-R@K?c%Ec$xGy*2?r$Gd0G(;bWSWKcJ?RC(58ipIE7kmLs%HVjaf_t4&T5($p z8UaX@FnuQgt}~~1Lv}jN3LO(00a~yp$3F=q_9%cAW0I{HUmM?uI3t9eq9BwQgP*48glc&F!03#aOE(rF zVlMTNOO5{b4_MV=E*FSqEy|4vp{4w$7NF5+iRtkOcjRSU=75&{Ry#DT$JSq`n#X8% zFhpCx4pyp8w*V|_F+_<~BGN%OhAmqz3&S1uwq_0^6;m;%z&_2QCn#BWuq!Kmm`o=Z zYU=UUY%*KSA}bDfZ2weNmgR1<&23}wCWdt!`vj_naK392PzGmL*A(F*0e)2C*nE-B z;?wZ)muCvQ4^+_^tZ)ic(1ljherOY0i+mTaowt z7FP%j<_bU$lY#^*?VO zs6L`W1_nSz3`#T2%iS@z@Q)FAae=pjd|^lU-g8FMh?+SK;0RRm{>b((y!buhqSWY*No(Fwn42lL%q~2)T(MW*Ub~Ld={Q=1fekmIQ ze}r2tAR$1ODHgIj?$J;xCB-x#!7iPfLePIdx5;Xki1$flYlWNW`4kWQ| zl!M+d@tEJGAM-n7$9#$4+7Q4&hqZRj@4^ggA+C&${A6Z8h$~scL5w3fyep-$#1kL$ z-vaL)-V3it6ruwko`Q7VGVenJsdJ7#jvQJouq^I z@k_^o{2!yUr~D-Vh18V*@5>IwH!fiNA0$}@x9%-xhyrgA6re`bo#_2@ieD2n@8`IA+FLxidJwg|h;J9ba2({}mtjR*y z70!J%a7^)VTVCK;`eDrfWRtQ+4PxZ<&f6;VJz@U=jccP zW1+~uWAG58;6L1yF&>V7595ZFJcDh)9(GJXRChnH%RJjzIS+1dNo<2t@IVp2#eEJu z&^ass`0~YgAY1KLKt3B*Em`u)0)YQbYAf- z4ONIzsmwxU6tA6>Xc|!IpU_cgY7q?Di%N;ZSIlQow^%iS=gXJ{ol^zDq!nW@;kiy2 z&~Y@))p)cYfsbG%N8>zN1`ZJ}_@UG%6Vb1S)Am+VwMaAOqoFIde%{I+2i z9c7n*{EhJyG4D>Ie;`D;arzenN~3=+=r;wQu6T(eLU?%vT-7LMWcv{xoJfq(njnHD zprDAe_c^o#STA)dx6OUdkKT4tf)y^_0H~oA01`;$ z9wowf6yy+u5p8OOQGQOecmZYH4ic+36FDvDt40_v+RzfwnIMcjMi>Q9eF)>T8evSQ zUQ1qL0r}~RQN}o7lmP%MLBv5+c-{5wgz@(abixP&Ga2hZ7~8bgNykPd?XS^5-wSZpo~Nq3k;{5i84y*Ij5WL^e`c( z!E0X`mgH-%4mKKV_xVlrmK?T!(`Q6oPB)wVS)hJS5E}YMQefyGa_cQEIR;3c8fmm+ zwLjUUTD=~Oux}RXq5R6eiCQCf^i8zm{LxyuyKgH|Pw4tYQX0SbsT%<$@oQ+JHUqGt zt4mcc=QaNub2zW5?vmgd95vAsA)8K|@Ff-jWHGliBDl!HpSxMd z1`fWy2pNmT0)!wPb{}PZ27E`uh1sGrSyKtZJ%_>#G>#n%lNKMt$B|A>*Aoh5pSN3_ zqO`CQw1iL+f|J8EAHu42vK%K4B{@r~FU*0Q4u{jjD7gCaOgaj` zgKZMh3j;_D41p;-pTVUJa5T$>cWS_-=BD{n?-M-Z708jER^3k?m)6mx zJ$$6lmzq%kx4xha=z9t*eD5of+=y~LjS)*{RAeEpfXcs((2SBD{7#Iuzb#u_DDqMv zGX7qU3q`Q!LN8@qO5s2|6R^FhYZ8`g4B@*Ht)IZR@~R8rTv0^l+0*l^vvZ&Vw$-k} zf)~CAilND~$kwqiJGf!*!?dv;wVCg&sVI z>m(WQQ4()_o0t)cT4zRzsvZTDKK?P>Ds+e&_X&uN>YG#`_+~0T?d(+tzHE6S4V>CF zzZX-Wg+1|qry%T!j}d7OA76u`oIOePFiRvyUB^^2^&e#!q%{`=`vsuk2G`&!5Sy>1 z&W+;IiPyj$fOd!L{6G#yh7kG>>h8mHSbS1SxNfzc7cW($s#FMgH}StZK%cs-stoVy zgFbG?>+AS0sVX6yKOwAC(4F?0AzzC$_9RV8`jK$mBmx3|!Db3!#Y@vajc%24ZH0eE z^eR&75MbSoBDluq%^bWdb<^?r+6p8g?h}tkIQA`8itMSaH2c&#D)jXWK7Md-vQ#-u z_0;QWK7KP&n5eFL>x1n$t7-|*RgJCqL=0KDE*o{BBf*%{@C0dr!lX=1+Gw?&RnaL} zi(0fx6=f-B4uJ|OpGaqiKQNFGHYW*&@Rfk>u7Ygx+^hQ4K0LCQ1%(j zQDBLEei|KibhH~bF7PR#y!P@lA zVEFpINoXU>ezWo_fvf?fRM$cgWBcJPSmBxykrsxhhG58~(_QSD%zfH^QN%1Mo|?>e z0e6z}WCpFjnNQNtY(LzFEzI7eCBmMhe$h6GJ(>IHb7kzBh6eM@S+z5hAt&1EevNK( zgJ(@u|5VMR6@eV}QOZ&TdQHuyKPz=aGT3>Da9^6&j99sa8QjLeB|vPj*&LL=G_U;< z&ft+=|0>%KgCZsej$V_+L*Uk6s8RL%;tfK}gurFQP!4DS>Pq?1n^GtDqz<4RyDy`CmZn z{$S1ftQsVa>R=VoRD{I~9rH+_X+p3xtuAddmGR@4awf>urmz*P3KJ6u_FSoxY5xGx zZn3=94rfnNeMR8^L>-6_6Z6i*#4$0M5wS2~Mi9$|uLheF4F8+fq3N_D7L3*}TGbYf zwqE$7)N}vUCX>qumY24$1Q4{I9(@B)FhN6@=qA1C8k*MG^s(mhiewB{i#x&iTy7nV#Bpwwi1M7RV+bFUK8Tb5f&S1|8^Lh)sx}sM`mXWPp}ku_GSgXM2S1 z#_fB1mhI7vNINkHAFsbcT^@j`L?>2s?X*rBJGZ@FL%F|;=RTg0`^c(S3Ewn`*PT?Z zPDVk~t76Ry)UgjhC5ghT<{N$d4E$TP69MQ=_PxG>>byH#^A5&Pb#kuLa@6`3Qoa|< z1N;829I@}!rwE85RW{GUL{f*GC3cqLdwn%^wi2C14-G3F8tFJR`gGLa$a*(Ak@QY9 zV*WTZVbP<5?hiqMPv;0I8tP;^QThlOP%lH!*@yxfqtI-ZxD1|6jK5nXHM*-EGoRe>BKl# z>_mj8i0(X(=+5lH?(?>}=T{x3ZZ<`z?6*aB-f|q-ce%=QikdaGz!XBJ6L?xzt=Lztr;O1g^_^WbTXtQfX_Hl&mnA^ekc zh6kX+=_uvj8CAcdZxO;pv_tJsWb8y z7n!v6gK5I5Pub>HpSt*zP3Ut!BFomN%)k1A>AGLb(`E~HHe4Cu>4{{>%+p1W-EgQ_ z)k^aLjF}Gc2f(KQozohNG>o21jbrpwDT445G3xph7#)h0-e*rm<>Kx*iyrKn(ofeQ z{}L#X5Wi7h@+HjIHux4+w_`EWO^vy@QY0%2`qDv~zSPWYQSQBoEitnr!74YxI7YME z-Pur4+uWb}hu|)c!Y@D-QgFSWxN^s8I$YO?>nRb=LHFo^21A2ltNS0K(n}EPc#{i+ zd~{V(@SI`2T__bscv_kFAS-X}Ob&J$f*)f4iU@ODbD3bR@vqa2j<&jTYHD=dzN))K zNXRnym^RGM%BlB$!RBXWv0Pyjb+V!aYvfH)n`ndV`9d>C4u)gT7v$Ijv#6H`_e2B} zMK1sELNkQEZJw`ym(Cvv7uNsSuII~mvtMe>qM4mI%yDflb|_+GI!HQ{pVJVs`5Am8 z`r%_+?T_&!B3-pneEA(lq4n0<30s**?~S#hlh`oYXc%WBWNGt!)me*sGNHz%m>7BB zpqQ(E5598Fb0R3FgH^ZdGp7aM=4UwpV&eT>6sQaPuLzX+pnssu$NwXwJ{mgDByCCH zs2(eGvI|$L5HktiV^W|^O;DiqRrCl0u)exAZO>IHF)=^bl^+v2``LkU$-I}Og+K?t z8cK-h@}2D`;rH?tKeNepUC8g^gHd3n$muKOQ(3yWXfE=ZR`UWOpS<4^g#62HfshXw ze{fXB5BvZd#LT|#TnUxYeOYKo6YjfYMt6dKt8~Ae&wm~~g?D>23O@N+2=}OjOSX}r zT#sSICHb|$u#n_Af1H#EF`WxkE-@?GM>CKk+_|+-9}T7Ti71^HM`<59K88`Oi4Ix@ z{3!GSF~n{FEYIDJ50LX1X8(v1GiOs)U(6y!7O4FvsTM?m3-u?v`l5CZW`R(fpg#F8 zlwjzN;)1$Q&{RN!GMI*_BN@vbl!ed?hZo2-2zZPtQ{)S^DKGI)e?cp@gJ_kGbD`!W zCLR6?y6)>Xh}W7!9+JV*{F0`EMjlhDbOQhSR1Bs8_MHk%DJ&P?piPC~fb2L2UxN%K zvnJRtEvy(J{NRPk+%2|8t_C=&c^X^mV7R=#2QFPo0N3y1>WH%l`E5DE(_7sqqyAkf zrWV}fJ9l6|uB#IcEtuN>cD;UT*=mW=U?sir!)p}8Q#-E6KoO*4Fwyg?N6E9pd&(hr zF}w1xL(Luqzqjh{VD%}?eb2%*CCm**>sN5t6+VT%^m7fV_n;lXzBd(YV{QvhZ~3eq z$5|%s@%Tn96hBT-$iBhl6b}i=EQlc{2Yf78@RP-*yg1fo84n{GLTeGNp_q~xEy(9@ z!>O3`*Vw73VHg2~g4yWS@GJw-gk^H{)Db;_&Jzp>xb7xIAF?Ucr+`{BI|)RG1)|Tc zOW95H)R|%mBc6ONeC1io1rBqGi1zX7=`lRNt>bya1UdM>;<^qU(YK+Dj%fYH4nj2X za%&yx^wD7N0zDdrdkMptaGo!Oqp>i?fraSdGw}xQA_2ou1eXc~f29`a2>uF}KwLIm z@Q@b8orvKwerkEN%~H&evOBh)vM$FcWRaIY)=lN)`J zzK{{p|9cvNs^eT1%i19om+?np&6o1ih+Uw@y>`<9aNa%qZe*h6go$!Mmij61Kl#%* z{&7NL3d9AgGjJXkxYwHnr^{}&*gU7x4<&NN8n+q0>ZLEJ*9lN14b)X0D{-a>@>6W4 zCR}c^%3~MO2A(bC=86lZfBA_H#)*4@{{%)90AV!{IUDE#AenBW!F~LX9*snBmr5Eq zfpz9G=1xCZ3IcGXghp*utv4#S;5T5{9&t@cLgVxp8}4oYdAP7!LX&~tLWz!6*0#e0 zG29VTagxECoLFs3-^*Tbkt}cii#9}zh)j0utiWx>1gK>YFqUr9u=`O{=Qz_xd~1fj zApMydIW2nH{d6>nK6i3LswVd&`VVn)S|Tgy;~e_U(zsb5=h- zKFY>JjwQXA9|CuEEqn8?Z#m+5>0;{&lHrfBGR!kJyl`t~1o< zPmPdQ;a6|rq?D9m{Gw6W?#(!F`N89{+R)+94Np|HUnS^NQOQn~o}fxk$1C~oxv)G9 zODT5vFI^eVvtEmP@M1rJq2m;RG7umPi14k4-a6MT9)^Pdy09Q`oCzhx8}zXQABZ65 z_u;lOanbfaafE480+~82Wz(%TIc5TOC-Dmj9r3BuNdm{8XQtRd+MXAfdrwNf>+g^tO!}&Xs>S4f`gd|m6j87kTzZdO-JO3a6pz7+IR3`Xl zHa=m)$if$F7?cKkMFYaD0k&qt@cf-@7`VF4FH&K}cn60u)WcL?vtp#{S31U3U;Q;w zS3%(vyoOrsYf}d?5Jsshkw=^?pYl;3G8o%6PD_HEFzJF_L+c;XuY#YS`j8F5?FHq> zlyHjwt1b+9Rvo2$Eh9e={TtzL#tW za$-MhfR=DI26Qe>xLSZN2!9J5N`Z8|L4KCAWi7#f86}jo!+%^9z=dLi_~oBMkT&Up z^cd|*1oDTXX+W=o#RUf^Zon&8TO-RK6LV$sFBf;o;RslS267+|r$QcvB$?V@9+oxt z+_*qIu;3cdIsAMBu3x$#{XI6RiX5?mTnf__Uyzm(Om2z^7j zpvmDA;H*WS@f$CNKn}-XnO)fRAlr`Sn8=JPXbzh@Eekq-D~2nY4dza&s0WM-+4V^`mRg`qmtBiL z5!SGQMA!I5a+=5`1TCWD_|r)FlGvh@5uZ8Wl^VGD6*p?YSx5+3eI4Xsw1$T|Hhy|o zCkwi?f=g0;{B>Z&U2U-i$@grFZO1rV#lrTmQb!XvH`KI4D_ZaAcJE)C1_xY4hAh-V zx?v+rZYb%lcUW5TEe5Ic60qwno_0)eJ0gwvpFzUWOz>|uwA1ht(MWk08W}^4B(zbV z7H^;nKX!ozI5qGT8Ym>F9ufNTm?#99YRT3IzBY}N^>`QDMgykXy~p6 zLp9Gyms?DGq6NQ*6?Bpgf4yFC5BmUugr^<9BN3$OK-`+`k=O8`-V%WYsKw+t1t(VM zQcc~arkS2o>j%xmjiN#F>ITNx{&}W7lsXJAl#i?`k<}h-pQFg-U6ZMDPdXIn<&d!3 zN%>vfp5BCm2G=6n?BHo~Z1(vxVC(y`)n+efLsYdL2_xVWAQIYb!{^&Yic?SNrW7ZY z$q*@c%Lq2-;L1;ON(Y+U@2$mcMu?8@k<+_XE7r}abm+l*=pJVCF!d(GwA@I&dGuWO zduzT#aT>-MC=N_}Oa} z*D!0YA#iE%{CR&`L#}bwTw`Ep@O%Wm%(0XaZ-Mew!S(9yU6KlnQa6U{&k|r|gGDP3)i`~0~8lv6%z6%4Stv;Z{1C7dxXy4;e zWtFhPY?8GLttR&gGrXNz(!vaFwcpT6Pxw_s+?7_wXQ2d`Da=`;f;;!jxTY{8NY+YBX4G6NXw^xRJRh!eHf8(MPVfa~qK7@5COEXphaLZU9sNTP1vi z3bY?QbOC3L3yKi`4t;=7huHZp;0#SfF-$vI7#X7p8rKtwaXr3GU8L5HNkjd#_SFsq zt*}5Uu2QO}ON@2lhy({10wEvC{NsfJqpC`Y{LSCS1f<|b{Nz~y@iRC0*av$4=U)qv z1McS91@1n+6bp(J!YTriydBzY_3p!pO7e_~=;Z@8Z!@|0llwiBa05OL)R)1ImvA~` zW&B!K%v<|HXYFzbEeD(gphW{9%EiI_g5k7yj^YSN7QKYrbIQlBgui0Es(8|-=)J@#YD9|z zPZ9hlPtpWe+iNyN?hIHPM)TkOEfP5)Zt*I&!irVD50&+KBb>1(|pX+ zmE-hzB8UcF#=u81%*8s5Sv_NW%8&jSitIx}C4)n#UhSJnQ-ztEjRmh)$*Vk^( zJFZ~G{fFE0a6w-G>+`yClMNi&$>yrxo);Bz$lUre0iQpW;$(~>POwAwI&Mdb$;X>J z#0Fu<@M^|}7|`Ds;vTZ;W;+=|)1el`F*M}n)O$Ke2s9N%9>`L?2P1)lB8JdeQ+-g< z?46z-640P~5f;aR1+;e`hk*9Po5J7-#^|^%=86$rcnKfeob_Ayeu8=^OG-QbHFN>G zboL6c0az`l{~d1OGfU|=^`6-BDqIxtJqh2@&7tk0*d~ChgZOD_8S{0~-U_pZvzm1 zTLYt_4WtT4q76jrZ{58+Id$Rd~9N9X!)g7K6G{YGSlJ)TL?d~ zJmJa7I&K`9?<`-&-#-H}R*1)OM2qrIqx_KNz4AyBgou^W%9or@EZ@7K!{f zSD`E12ot@^#EMA?{cy#|)WN`dmM@0gIt-dSNhH!AeYJEx4b!$sU!RiVz!R&ULA_pCQVw!h70x7j9* z+=5?p|NPFU3}ZsIcU>uF^mljNV5~nG`uklFCtjX(_X>ll3Km9M#{(ANg7X~pVnoJ$j*5eukNs>ldHqJ(FbVGy*2 zm8mTI?Mb7C*qQn5Dfm*VYZOmCANMTg2u598%2G==NxOlcGVXaSwD+F5hNnU)6;~VX z4Na?f#K?z-c2rzHB=Tcw9scd7e;hhpafMv`xwd}5@_oh5^3W@gO0)mGVZ z!~rGIEg*Z?dP}h{*ka+yts)0^yh8;{F%^-K4m&pXF&OnTC{3y~7Dq;=Qd&1|4y-#} z3@4AsNIMcpz_O|ZmZi9RBD8gtQ{dqBRZio{XF|tUxrhHtuufBgJ*M^F$FG-d=nhS) z9A|WOg>I^xX!u#^{>p6A&e^um|5T=#wqYXPsmvSMh`YVO!Qxge&(Xi&Uz&^GbTTyY z-tQS_?+7XPj+d)`LGa>NR1JaQ#oV$@Ku;HS1Ylx_thYx-S^*i(w7q{6S!H~fDjqO} znT^YrWxlhNSN~H()ygfmbxZccUr6@z`2QRHkJ1jYWmm^&V-GH#aTJ#VsL-nWM&4NU zhZHCXQu*vRM9YNLVf-E>2D66S>VAY#Q$(qEyRFq{+gDUMx?(Su0}lSmEAX2SC81sS zU2VK$bBNzJ{(DcpMtbCmcH~C_PvDn`@LozRf_PhxHw58jmD{!7E~_{u+Ibq+0MB|o z1$U)^JYnehfv2IBTwlr;it;qe{$;B7SqLzdjiGzLKf$==PoXW}pKzs%K-_D70{}{$ zca?I7_TIUq;&`m#7hVf}`2Dm388e6!wVvRFsCY6-WUA|(l}@@9i?=7Ggd$ZdNB#9->{ieOGu`QG!pdWZ`QO5! zhpQhlo(YG}RFAvlS5wjZ2zHpZRb;K1kD3$U+0U^I8lj-T05|FpMva?@tY#ePrq^FK55pWtE{Sg?xWx%!&Jg14eCFf?2~OnWQgACW`K_s; zzc2F(jIF9hafI=01JR)5Er!Z z<+GBc*Quy@YdROYX6FiL1hN=5nYzej!4l17Nd#g(stNd{pM`$6Q={8$J6F1YwL+ZH zE@X=~(|E@=BRar=pMf;A>*SB13?QZxI9}|{ZJ`HVn`|8Z)6lc8X&7sKZRO}4%SCgW zwQ|~(llygiQ;Z>XV>}YRkyfA3id)wtA6Y=gX4-5-E^XB!&Pfmr}-W=sv`L6-;zj<@= z#3fG|w8ll-od`O)pzU8B3C-I*dEBh0F}0 zyGV1xlK{7dkX^2A7`OoOBXb@r-Zgx$fK0lupdhD&q#*Jf4rT=g2P^-rEH*VC zQ3KfFmw+e>%HzMJckQtrMbK->F8&3c2u}&+9!5DZC%dAH98Q3EJ&0gP`!YTomMNXC zgdS{kh!wh}G3^TbQ9;D`)A-ith&9TU3;4Hmgbp<>9sTm+=uX$OX?=Y0bSS53`~qqI zF!E>?t7@|#4!HHa*40D9A9p=1OhSwW%~nuRvAnc$eA_aU^tuF;fu-L`i2V7A(C?c@ z&dj=ls-^1HfQikn?UXI;L3u&eHoKgZ5ILz&g9FX;O<#rn-IQv)CnGevd9w4~Ct^bu zyxavFYDM#9b91XTr2jX*(Vf1a^~syQ5sCV9Dhl*pJhY+mL+!h-ncVzkbOlubEPelY zyDzLli)VOsl@7I7@}E|jL&Nuu9COdN_Fx#Ohs{Xr4;1xauiBEhsfHa0+4qhzwj2=K z=(flE;3bNdOkRLTH(#EEXp4G zX6O%Zk4xJ^C*x)p);#Wz_yUtba%?L~^2fgjb-kTBXTubnpL(g6sMMc7$5Nn0f8%(Z zf>fMH1#iQfMy?Wox@>MJujMLZ?v~JfE#pUs-A)5Ng$nxFwXMZ5iq5XGN}p|bzH2q^ zZ%)-tt{BO3;sW*0$H*j}J}*jgX^&M}^e&&T3^jzumFh^K3Fb4pbW}M= z)_)CexTP347WJHBn_@n&i?5DjPb8f0jIPrv*E|9y6bX+RUkf$7MjU8G#5(>r;-rC$ zjP{|SnXTjI<+RgL4kRJXC!RHR2VPg`uUpV5D3DmH&l3Rlp+YZ^fWS|NhPJg%F@CCs z4z-Rm-G3&DxIFW9kt8W(*yWHLH+A&_dGS?A)N50R zBYt!tL)}Ed+rVZH%1S=K5ydQAkZ{7%KFNt@N3-|`pjXdzAZ8ooZIk6%S38=+BB8J6 zIMVUlfoEWsl6(*yrd3L~mev>6NNX!PgNTkdGmtk3Z#>fqhFhv2m{D*=sgG|$3gUn9 zmO~-TSKy}`@H`*C%?URtw#YTTKHqg6B42LLV{~m|bNYVW>St#Qg&%FR{)2-zz_|Is zW?UDT@0tOIb{T^%2Vj9^S%6eJSm5)w*+n=cUU#OR$mh&N{L^*&#u)Rrhi=}NZXCTm zw0_^%X>rBKsp~a?L{p4tw=VMfpTUc*jhWw{^Z`^2j`yd0FgO(6H}>XuwdV(_mKCc; z8WmK-SAuB%=r0L*OEu4cg>l((-Ns0#V3gY51xFP@ z?K9rF%d{F>ZRwis^!j`1hd|ysnv+;O>Pi$uG1SkeKFJgy}#+_*c7zJOQ6rw zSO|`aiA<3DiX`M%L7|+eCYs<|QJ3~gt&4Uq{fX`SG#*0jhvrVdT7!XF){oN)MZka> z-DrjWqDLPs#ogd7LZyeD_W!W= zF3?d`XB%+loS8|0kQpREfB*r)RYbzXiW)E#Mp20dG=fS5DIy|OR8YLo;0LKpPogM< zO)Dr`thA*T6$mP50tiu}f}&DIi>=y0um(g$caWmX=ihk$8O^tB z!F?ZG<{SC^whyk>^#$!y7xeo0PW@O`>hsG!zTT%7?n+oa-%+8(6O1}T)k#-%LVKI^<;X@n3mz%e+Td1 zG`?N>^HctPQ(vlDyw}oqGu17ML%!bPc*xgG{E&hFev{$5nA&oukJ8Z+CC#B`tZQSa z5pi|cXSiCaZV3DC9@*~So!QWQcVUiCR<^15ijna*JB!&9up#A<&eiKD_zHZvHM?jC zyD%L?)^>TAjl(_iupbT^cC{4Q6Mb)K*Jq0_C;HZDPj{?-`UanVIx#8!Hs3c}d;A-5 z=EJ^m+K_L={D*ycT8nS0H$CjT-Z$pETFm45=0bfvsv5vE-}`BC0%wKtjI0W0#qw;0 zJR2|1*2%LRgd?F=_tUUKwS3f4+_%7Yw>J9A>hBi#7WlM{`>HFdz}x9L|7aODexHl~ zYxm$wyK$o;Fcs63Ncu<1P=zr!FpbXqA1(i=<)YvFE(ud=q~asWW6VaPxaRl1TXTC; zEsyr2TK=WSO6m<+JW|nyGjeh9EzwRaF=gLD~}Et?sgOW!c{?@!i>J7?&+H z%g%O}?X=X}?rd51(WSnroknok;bz$eciG&>e7CsUZIET3ddxSi)0JGdxRJ60ANSpq zwCppF`^M*vp|Xp|nq{{qrEK6b-?R=c;jSjy-Km-CrB`Yf>bFnPvMT;?7b{S1=$qmp-U&Y ziuWi1H5--t>FPg2YhQ~krP>XoqYi3Ows4#htA^X9{>?4WTD7Vpvb2HnRHB^-(4iu# zGod2Qf=Vz_t@5JP1g&#V^dKC`1~3I z^hIW)D#u$#7iS&R0?W5`5{nhBO0~>lsjQXV?P8`#Wz~(DXg5_rO0<_%{FEg6LaEld z?~gZ8;k!=DI$wG!vc{hvKiK1#>$L1nP1>V)_&Tk?6xlDhYb-L`GoEMl7gof9h(l6K z$For0S0aN4J2zP!XFBVkB2G(Ox=@T5r!~7pIfDLn1x8gi<+Yr21eZc$VXPq=G1k!PwK*O%?PAA^&&O+L znUcNGNopn&=J2kUTC7l`@F7hZ<0Z&!RPFNCalW$-YJo*umc$rooEyTGqnb_w4b20J=1A+jgO){Du!cKnIb%1YjY~Lx^%+-xwtn&3a29O zB_jVotGk`Q*Op)aDc|e3k_vAnzp^S3w@gZq z?*w&K%J)V)@2Nz-2PXZbKxsLv;{TCYDe|3v9_h>R~lGB)i<6_5+T31mxMQiJxk;WMkzLxVgMK3&MEc&es zWW;Z#Xj!N8kYvtZKTXVCS!PpMivl-LC2GQp@$)yhYEAO&4hTmPCaQO(JB9IWh*tecx`1O6XTZ5pCldP_$^+^Oiz;gamkn%a;uir$1R!1jH;Pj!VWu% z{;0B--Kw?Eam5@(Ps(;l3{R_Ew@v@?>o7-eJ#`TbIu#L+61kUMAc9AySt6k8jfx3JoA^|vL+={W@sa;jFjPDM`V+ogC+4Ft*qXy72+UY&&hZc|&e?>;vZKf!TN)Rg@6gUVgTf6jD zn(KnI@6d81>|2-i#sycd+V8VymW@s({&jh>d6a~9j3HA>&yn-#HUa-Ab9jkG6 zL>!%A2{soOT)8}grMhgvUU8>auvBs5f_>snuV7_{aq4hmY>xUaGZxml{kYhzvS*1F_Lx2^17 z=68u=ooa$bf$r9vQV8+v-KIFqzIy3(i|>2n|8cQH4R9q4T*dd`u_h@-x^CO8S1hr6 zykgl&`P~+>T6G=Jp(%&&vBYv8E?8orTpqDdUA9;*zt<}kS)?(sEWOt&7Mb5AmhGyE zpC*0|iBR4^R%tv4DsQxpq`t*wax;Qdl8{B^WcQEauolu}y$*x%Gp4hA<2HO9+ zr6_(_%Ph-D$p}Z@Bc;l&-17snzD26`PON07%*6>LU!$N>R<$H@M2#=$m0bEjLiW#m&^Xo63vq8MHoo~i1ZJ3g;ukGryehF3lG5v{eajG=F;jb|C@<%Gw;aa!bc zlri$lxfk^LRwJ8BMz-PQ$jD2t%~sjz9N=q=f^x1Zx;yecYM>OqEZ-10OwSlccgr+F z5IG##g)e2IrjBTZX?T;*sV2O=8I_|l(Xc?vyoNF0ttf{g4fLWW*@Z|0DySvYnG!XQ zscc@!ZsZ^!k?nbSV+oQON;*TqYd|Oup55kJ#JHtehw7ONwL(P$-Lk~1xniZ8^P;O2 ztC4r`**XpZi1~aeSch8RdeKTQN5W$U=^S8e#aNkCG4THJm8LYy0GKsnzFRYJN;To7 zM3!bCDJ`#LvnC3>KuCVX)--& z4n;XCQB{AmSSwLFQna6iT^B1onul+_N|BS2>_3T;Ea!5EEG5A?OT_dZ%abYzUKg=k zN%j^p99EJFx02wLYKoQASW1EB(G-!)+KsD{6|-c*A)i?g29V1+{s;wMpqbzIK&6*ySD-2l%A-8=C+!Ta%~!qk@V2&k`0|c=_{}bQ_=;RTT)*JqiJ8^!uFzid=@bh+ z@fWRk8AnPOYj!rFFcgq1V_mZXRXTU6%(A2^gGhCmL&%9k%^0J|8f!=$Ip8a*$SYZ$ zL|;xa*0>^7vFxwzvF2r*{{){xaYL+Vm8nP+N4PX&MN-W`Y`Vgs861HuM@`NM1m{vS zDDdU@Qu>tTKi#JsKAE17PjQB-0-?- z8fBqX2ECsUE#)EXa|G<+Ra*0(pc_BUYalvxdJS?K{Hd`ro07E85 zZ&)Ku5zbwHaI4Wc!VqBj00QTGYr;gQ(r~8$W5BDKQ}>k5T`=+7+Y5Fy<$i zhjUt+DW|os^LL<@NDVD;%_uSRWgVxPB;5gbOlx;xavxe-D|Is**%jH2cZ@(UEWdG_ z&)LqDr9@ZeK{83EEXXPAW}|C};8o8Gj$?uwIU@70|H1rry+a`!`jBSLTi#l$;bCXX zTQtpuJ;jfs6AoVy#ktd2MB_=_C`={p=mVPyjqF|Brb0=ksl=rR5%bq+?Z)$5wl=|W z@HhRE`wj}YOfKrZT=4frfybJ~iNcq(p4Df(plLsWlHDFkaPFcc`Kkvf@veIC_hLPi z;C)yYCFa`4TQ6$u{x@qM34AD5YWfP0akuF!Fz=JTVx_}Z)SSdu?DzNzoV$F*{-nMF z--6no;42oRD#vmdPN}9C-j!(jip9xeQWfE{&3G0)Xg+H~O71IaH)w4Y1JQXT-Vy}o zc>fv=n@!80VV&(Z zx;d|?#^W(CdPK1RWUZONU0^sdYGWnq$krSlgY+LEf^8->9y7^+uV&4VUab1xuV`66 z!8{r~=7DpUc{DWUF&Z4*V2?2ww%{hEoRUml8Wb@8_131pAYR|9_0leVL43Vc zJ4>7Xtms>Z9f+*S6h-FES`k}ALZi<@@T=LC^^m1;Sm{`G-e^=!B>Bp<$w*y{KO{j5 z)LAk9XoRv=N^x?^^G7?F4M)mo33|;~)I}lj(&3IuN#_Wdngr6Z3U4d9Z7ag9lv0( zA*vDaOXLHzhk_zduXSzBzH}En470+qkT|Da>(rWZ=P?U1Zlw}8)oVGURm1{@$4~=o zY58^xmA7U*VdZ-erkXY6DGF+>?i1T4Zn@gFr(V0qr(N(@G4@UEoT2XSQ&*ru(IuhY zwTf)&$vY+`D#m-ytdV0IvFlB(TQ6Rs2T0NwqEh-0dG~_qIHK6-`j*zGP_c11K}S$H zj}^K0i!^C?AV-Rjf_hUw#4Yt&$B~L5&mD_AZrK82aCb?XtkCLXMV=*d@1Q+vSxgl`uYru5r(;%1OPPhDnvc0%=dQ zgLtY7sW(unX_pcnS0MYh_C4((pVsn8@$mcFdD69!&c~LHpknsmn+A2{RVitd&@ITY z779BlbjPkVuW_;GVt-LP#8^+Bx7&p`I6-nqUvIB;xycQ6t^e&y3jmWYc#(jrbpbCDEzM=@Q%*zC^vBQy|LT{UmvzJoZC8H3KUu+5=Q z`NT$;%fua3rj98yP5u8!unl@N&S|IEZqFK5Mw-C4~cK^}F`ugYi zv09b+MlStqi{!tWlMh}pU(LmjfkAw24W$*xw1N!rL>$Y(Ioa|&J43t@*E$sE^pofK z!CryT%Gxw02H}1MmClq`@nb|6QpAI_T$zR+BRW^Lk}lKIGen=gTF(hw4p|jID*hHB zqjOq6CWgF#u}ba+jI2ulC=1P!jJbz4`e*lIJanO_RU$9Gd0u?7AJl$q%~b5liB(O* z|9FQDk!MKqBKdDf+TO?odq&ZGBf881gnL3j^2=T&Iwj07s{_GAB}6K!WKtJ9NUgjN z3QHM?S7cybftv;<#SbMjO{=Io$O@iSuZ$>`|2ZGgjeHvTuKPmkR7P;|P9TD-*eaS& z83-k+V35=W6XaWTkw8kUvImyrXVXt$MV~Pk9MB^5TbP z2&D@tjgTV^z{NhTPgmv-z=2TlCxKho#3%b`+mI8Dx@+J?1HO`}6|DeLR!~C?OAmU$NmNv$0w+-^jKFan;XooNqeT25 zkssScqLVp^B7wOnly+gf=g_GV$nSpe zf;(Sz-YH#)A^{u)nr;HiBshA2+(jTfQx4J$xeSNf6;;_>b`yDAI2c3u85gZaIXORS z0Ld^Gsu4fXz#)|0>M8b(QY{t7qj8bn2!K?l?MS2vwj-Sp*AL)Shs4j1DWeKf0Oq** zz&F}mzA~zl24?&qP*ejB(2;%c5QpR%qiD9V!3@8shI|!gVZ$4b&7SdZ1-V0{709gk zkt3WHCGl8F{VzjJ_%*iD=X%RGBCS=%&VGZ0=5=7A@{P4ZhWT8o7 z42RDjsD`OCvQO0Ku=wn-*6~b|ia8lUY=BnsWj6d!{46mOn<$Fe$eG`3?WVYpV6?UZ zR1WrC{kHo@b-Oy3Br2k@nDSXr#j5Y|_9+4xt4e{MDu4Q!tT$p<25rx3Dw0_B7dAMsq-5v|7*IX!uoP@2JKLYPjG5COJGCh?+q zN63b&E^vw3lp!*H@XoAYA|9Lw&s_APB^Bf1{V1R}Ru#to2AU&UCA;2fRRwI*iH^xu z=*sZh=)A)@laYiwNhQLSC6HK2Qvd@$tm_FDI*&+0KGGEA!w<~`ME9dwD>q8D`| zF23%V*15SE3+88t-yYMt7c$2ZrJSSSh5q%3_nDwlXE$WFG# zvHb*%>5TJT3P+NRI_9W?y7DDZ{J@_}oi{)C}HlK%r_<(c+M~pC!5VC`t4~x zJ$n>!{lD_tiQ~`5KE0iDHS7e=2fCCB=9$1KFTjBLbjdu-Y`8|0l5jC>+Q-2gECFmk`mQG8jh^^K{PGno5#I*RoAAltoHajF+AZoL(GusIq|r9wl0)J0{p~2K3s)W`pNI0) zJCx7O5G(z9$L4O=+|N0wvcahr^g8DdZepz+@RQ)60rR>D=E&olDw)}TF z4Vp=vN;wIZMg)|EP=N!o6<0OWbDEPFDH&_kvzzH1207KMJdG>#s!=&TB}=uT8nRK8 z2JW|;>EK&wAY2IzF9gq8lP;%uk3}4hZzxHUXW*(rf+tZL615##aYrqnQ!3W?+{$|^>?lB%ivswem~Sbmn$#pt zhC)4@uJ_AjZz!joY8si8>iJVw?z++DF)+i&Du*i(HTsm$Jn@ zElK|Y%DsHe&n{D860S)dDXwzxxZ=ITd+~7 z!hWX{YJ5$b?8e&(sZ3-GF@9+yjb@Tx_B82l^x{6KAxD}b^_Cur2WjjbaS`6}iICo* zzjLTy!*h&l{Aw5~LyE{FgV-C=yN%;f7|CoTyw(#g=V#@p8l>A@Q(Weuz2} zE|>3e!18vqf^wv@7-?jPRqi2pYHfQ;SkF$jw%w&oxNBk3)i3yu9w(@$-2JNnxMG!J>v@O0gS263u$l;hD22F@6sZ*&q>)KTP!#nfqA8cF>n?hJ(^|*ciUSZTuvH|G^=SOU_g zJxKY{mCztL|0en*i{`XE0d4eur~+mA5a;$xy*;x>1worBRY@WR(ur%W(LWib%VC%O zlIVSTvc-0qGm=9>r4ER6&x-NU3NbY=74CN~fUxl~OeO6ok5ygCHEm7gF;iB7A1wFM zLOa#slyNz~j1Y;WcW4Yh@~&AVJ?e%vFNxwsA915%Vmxae{o2xK3-MC*VAw$=&S)wm zDW;l;Y4tcm;!-xh2@-h;zU&41R7QQ0xG9n01F2{Ix;4+c-K*DPS6ltX?7!TEy@UL% zJG@CFp8Y1Nr}~8Kb>f+J`t73GUVqMlWfKEwi`6Gd#2Hz7e$Gpi@qI3Tgxjiwf@s;*)xpcb`2U((J2OSSfnrA~h=%Y@_s>>gwp+M3_Jb^s0CRCzB=;d@jG*KJN z<(n>@2BU5_KBwxijpp=@r|6N(7bOGtcoJ|Dh1XQd>D_5=wN&6{Dg#PpS@=E&p95I>jncBrsQltVx-B zJptSmolX_pGs(bJCjlo>B!H`H4BXoZ;97L{fIF#J*Cqq^N)m7qMFKeavZ5qn{U8C{ zCNDT>3|yRYcuB_K@|Tl=+nNNNM3De)7qTW5>%S7f-P^^(+)2T0Ne1q%B;X{91aK6U zy1}WNpkH{wVR&1G*S{H+i*Y4ZwGzklosj~WBGI0M?iB3zFAQG_2`<6-c@kj!=KX&ziJE^gJ zOm%pD;D1!2(+5fv9v_I2=6_UPVhD2bXSBlze;A2gI>oi!Jm5|$*3YSK<_$ zizC8|T#osCXzi<%KyfTe$~g7`s$Q;GP$D{EVj+keOCy=7 zrA6v!-KJ^{W$#kDK)bXYoUrW@a?Ej z?>3HDvIn-VRiE2azZN&|a=m>a07@lLyQ?!l$BEzu%J6fX=+e|0Tlb-%q(91xevT8t z1+3dBuVaclUfn2{5t`G+6>~qwi6(PD$BC!lpnr}Np*Gmw@^hT%T&q*DSrRw%bDa49 z$2d`qAqjC}bs(Zo*K_$U67GK@v@`GB&ZLksU&i_zG>fIb(B+de7|towHp&aIikkce z<5<2zckQSS83OS33u}`-l4WR1af87RV6r@&tfK3mVu;yZ0-nC^JW zOWbxgHXQIAMdE(+Y+6YMU<)^`fhWgJJ+gP25q25v5;I15F_+6SD(TK2T|}fzl~TaW zyBJh|87fav41Og;Xr;&>q<7C^P$-7@oMtiz1E@5j$6E*KIc|uO+GGS0cpIX@+rGU( ziGGE}NisbknS^AyRUv~#WkDC&=ji?f@A`R+|YoQ=Qagyb04B=`tp%Mf#K+Iodk&J4=Z%JPp8+-D)J&;i2{^^r^QcEo zOsocpm?jfj=8byw2qWnk+QCn#n&~(x}W< zbWtZ{UkrY}J^2otsS=&FCnZajc6LnOl=4i;)$>a$FSd3-SMvQbQ0(X(KQf#|g=o7& zf(q>{ptS{b2a=%86gZ-N`jw^vID166llO>Na;i^Sk{*#zRr5~ufsu+CgZ0cot}f86 z&LmyPwd=60pmwm{^IV5Sq)y4lYn@spMVV-tU5Dsx-8))FsAeEaf(m-^qb%0aDy2+x z733S`g$gnX<;p_%F?b`EazX@IT7z_mL^(UISd~~iMDLKK6PA1(a=;@BiKfx~e7JX) zX|R?hng(03)d_nS2>M`}2jBlq!anJQSAndK!$ic2k$Q(AiIT7e3a2!NFwDj z(i-|?CW$T|62&$h7zq_dJpFQ^p3})WI+~B&T6!aXDAYSR2jVWj#LhpLyBnVjrop$u zy_zF#`=#FVYB}gTcjs32RvjWA0-*RY*bk@*EI3GJL$ox&#hGaMrQWtV^_h|}B!@51 z+qpOT!CsSDCdC3?-4Nq0pluXHn&d8m;hvCyLi|VV=0*|ErJ3pSWt-NAA}*U=Y#RyRq1NRkSQ)t~CTs6JG0JRqwWuIIP| zG7`O_Io}b3i|xbp4wpMESG`Xu!Nm~!2~I@r3?+1^RHCDh{G5xM3dm+%>;*@od@^vg z7wefNE^v+bKj}q19?md>88wW%AojhaDTudR;tj#cTO@%9dGi2cKaWz$VGP=mqLnU) z4KLAc4^Pgiv^Z}F-tp3=5Rbc5&$+4e8jWN4FdndvFN>#!Sg%)BJFI62R8P2DOl? zB0E&|*a*F6vavc7?Nb(*G%Q=raHWfKzP{(%O-e|ajFmvClNt93Du)BJPkod=!lo{lHQTZczY+dT(0*_>``J=PJ>-i|3Z%vTT>+R45|wxSMCWp5o#9b zs3)>Ra+0e$GDJPcMt#6#?!yRfFTrDy#;D3{cmC&MyNjBLvX+8?&%IffAlvBsO zXpuY>$#Q4KoSN$BXg$-H_Q+8^#rUjrlele+-ku+&s~n?G%zo(%{>a`sY}!rHUrNJ% zi`M0nu`pOX8gE%>d9~hA^S>hcUac2(b?))~)ZXL!b?7&*Vv7cC@m+kiKFHkSn>osbh@A=p0H=WwnjpMY!kAB-UVmD7J_@G3m6ikaja)IOsKkB(T*HZ=RE<-m} z2WcBCofvYh+Q;MGBO|o{;!S+rQBc+40vAdGE(>W&QciH>5Zrsk6u7>s4jbIJUU00N zY$)l;_d#V)9a8fYfnctkN+jkaf$|YG=_7ccmIRukS(2M?0~5eKGxk)OYpd#TG1o;U z5;y?IkBK5-1Hu4gP0C#2>y9Rso+`M`$-wnW0#2ex07t`VQgDf{JL-I$2i!@;+A|rr zGn0UmC=$RGHU=*7bw}@c!9g{kY)u$M|775P+Ix=jWkbp!65n(5;5ZL+Ck6Na?t6~l zx8zzGUUG!vAq-(?v7~)OPNF;`sDB9x7gSBf+!BX=r z6FlHfs*hI@4&-wAz;P;(z@c6I80Cq(pdmgJhSR{+7T?6p(LH~4s^G3m0B7QGHYiED zF$w0xZP3b+HI>=cxsOe7%CWeI-(*{Sc^|Z7M_B+wMel*D+@KyH=V_JX*4&a!YZVUR z8s#$BlMhb0w)i$rfVgC$;!g&&m{oYT_|lGOgFiNaR#>9>9P=gH;>$atk&8r(Tr6UV z#dAMVcAb}Mkx%h=8i7M;;t#2~2{7{ZrnmUYXM@@L?Vu8b(Vpl8{X3x&30x+FOR~jR z##&B)qPF;Ukl<`{!4}`~s8Mb49g9QP7T>+Luj}*a^u~hSSir=)2VP9UO9|pRe?(+$zP4b3_``Jl8|~<^ z>W63O_iCrj`9Tl&KdOg6#NVyQ^za+UMCY0M`TARF{_62F^Z6UZ?{*RB5WUi_WFUz=(EaJ}One^_Y%qEr5`QsozV$CmMeP~%_HK!U+cZKMURNA zL;9eu)Rc2elzmMQpu-*)~e&WC~7SdzW%kbZhwneE^f&$Z_cp{lnZ z7P}AWQLRNqb@y-dvwhmzzpEbmt$w9XuemToJpY}3oxT|dnTPe!dL0gKIjm38>v8b@ zVf21G4$k>rpQ7)?!Jod@Z_sz)Ama!w?Y=OhdfE}ayQc3!(xRjKuk}xH5Ily1I1cWl zgZ()8{Fr{9-hhJ}j_VKUhj8%SalJx6EDyShr%&jU^`jSNtU1kJuIVWkWr#m${+o3_ z4$^f0WIYWBchEsP4nEfXbM-I|Zt(k;>zO!65BQ(fvv9DM4ze%Gs2(5mkJ9uUB&|vF z-=gQ@02N&CFuPt@+^BYy#vZF#KCVnpu}Pv?C;>8pf5pfBRl#p*7>Hv($4NFE?jsSobJ{gTVv7nkrz@f&k)NW_8$r_ zvr_9LFXvpAQN7|3f4~>6K{7t89NDsCOXS({NZzo(?*m{|R=d!jNqDPwE%ZO{)4DcP zS1$5zXwEGbJ>fr_TYTUN{~o;%xu-no@2*p8^Plv8C0pCLV`D7Z5P3KUmmm70|N9O# zXlzGKu%Fdc`h`0)O@biXMa zsF7z2<=J+5wp^ZtOQ_KOtL0gaJlibK2FSCWeC7`nmSl)Y)&6U>n_sKmRP8_L)9%_@ zUHyVz53vevsPhlhHf*RqQ0L#%rT4XXFAm;Tk7WyMJUaY_V?1g;71!y$ns2Yaml&|u z|M3~y(Od-IEt`PRAK!L9i2HW2l0%XF@`kG^B~Ht;`Q^pe;pC#Q2!G}8Smr7cryG5l zoSRl&GC6BxbS5f`mF(m@oZC^2+{9>PR#8L9MRo8kO@4*4YK%TFYt@j&rRe4(zAzhF z`;L)a#SLe7R$w<%%h{^vdMcVtjf=0p@Sok?iIMM|N{I5av$73Qw9nr$*O@VT$nK00 z-D#l_6_nG7Jc^!~-8Q0tr*MmH`~2BmU3IRrb6Nz6wEh0Vo|H|NWcFP!Z7Req`@Jx^ z=VpgkLQK2(8|8GtbVvsRv0?7t?}fQO8O%Ok`ujHt)4|cKFa2k@*=&rXH<2^KT`pM% z-69(JQhU)R{9Rls;lfEuY`jnhhpRMb z@Iil9x0h>#Dw4^=D^L)kh*j-@-(|@lHyO5(&)&{BH7$qJ-5GgyrTFNeze@)xQn#`z zkZr&A59{SrbM=Uj&DCy8cf|c)`+LaDrbg~2R+aaZUv07+=o?@A+p~Q}O6SMq<`m_y zttSeV^HCo_GM!)hL;fCpU6ANLl|y_C(MC1-kpCq&&qt9NpI}E9MXW`0iz~Z zYejOv^V&N~+dJ}jAUpS4e;1d~)VpylxykR{$x(0ixBm76*lt*2v88;kR&|bAw=u4; zFpAf{_4jZ)ZD6)@&a-o#_MJZ_gN4#VX4lOR+?wLDo-vWbQQdd`Zf*_j=T-;x`4{eN ztHb`@?y+mG6UizYN$Fuzmra+q%8`>X*_r=**x%7*`OyaB2(nX*Q{Ysx0!H!WVShKb zKS`!hV!-$QE`6O&$~zc1)F4t77G=EyZKR@`=0}QdnqQQ4jl~FEm2WkN#>{qWOzEmv zDH$5O1oNBU7W3xT4i$e@WrAt>hE3MNCHQ1V%On1lZgEf{2X*{~Hq=N#)Gv>C#Q`|B zV&FZjrqNd(@rnaIuyR`8-jLP)#!-PWgTgP?%`J8fj*l0zR&_Y$?b&)E^i%+c{i@zQ7_i#-E2))@g zV~d^fn`8b9U89hC>dML+t+z6VBPEB;y_*<|q;9TuQEAc;t#>qMJ)y7cv!ubS1>?$M z80U+}VVq7$atTKb(bX0R@!#Vf#~K9$3$FF#kuI#ymQ>N%c>CpWx%DYW2N<`0LrI(+}kdTL5T6Fp{_%W=sSSpdl(LdHfxFQvO6w! znLBGi_hl^p_y1K`5 zmv?lS#sTCkb1IuhI`YXT(?No)8qAx)4vUz-FK^Dp)o4T#Ll2BuD@_SWn=!|{ z{myR1a+y1da&}6+by)dlUM7H$AUy~PHk?FAT*5(NbhpJH7YB0K@OD#INk7Z1=zMn* zbI?y64nCh;7(9>M-ApKAGjaeTIPaZt@_+*!41hXGGUGonYP@5&Vsl2LB#a9`DHU(^m zYwjb{J-ZCOp&L!CI#JkVOF zi9l+Lz)Y9+lcO>YVkkIu&oCVyJ9vJNMX)gIV^!>GTuOhj4wIwOiTK-slzoccrvsj!|-ydihC~O>~J=BRW zsAZQ#8&mdv8G(*{*z1+j3S*lPD-@LEcl73hQEz({uPM(J*An#4t-3)KLIZ$2vAH0F>ambK4p z73l89P1l{cb5wqb`_)!%+y`0(Iu^5QqGu^x$iv2#*o=w@uO!Bz74mDzQX;_3@Z|H` z%C#-DhljDJ1-hQ;k^)5+Y9_PLT+XpXASW{`PYbwLxa^N`aSN8?5#O8^=-p&goaO8@ z{1m`TagsX4sw5@$eJT|jQz>H3jLZl#^~sElS4B!`osh%BuvpSMknYyBOskJo6LDyD zvsl$5yZJ%uKwr1S=(urAdU>Zs(0mPt54@uMXj!)+Uhaw#S7|^rn7FeJgDf=;|5|`1yWkez$(E zXyci1sqdVb;U3w0=`v+E2igQWyVNYYL|OVwbj`9DdTQIENDOHk=;P*}t~(J8ck%LY z+dvN||21s`9Y-ijhb1SP%XZY(-mK0oZT7iIPGdoTEckLpIRc7bjjC7EJ!GH-vj zkNw-N?E<-O^dv?jn*$hk70zqzOs2R}S9gEU28LOZ43!dy46rU6hM>R=~X+6J6pc zD&KizmKNFV19H8@WZHbEp<;wb6AtH?J%&sXoHS`>CuYigd+Xqf_kNph}#L@j$XGKGMx3kXf5a`@ash-k18cVnxF%lQM%Mppk7OWr| zOSvyg`O{|>36FFLoIQwjfnP=F(on2cN-teC7BVnB7=2o-1}CH;zK(%jZtGv{KySa? zkTYN2G0??nJ2N{5x^f4M=xVR;602}g4wSA%vglEpkpFf>Fyx|siE#vrdxW4s4MT@* ztOK(H-FmySAGWbt!wJTRdYGgrq@{mJplvG7Fw2$Ys%VKRO zrA+i`bhY(r|f;z*(BPAq_SP7`YvQt4BYD`*% zYuU$M%%d8rNJc0WD_KsJ&?1mz)YX?mD*l;os;aek$I=XA6(TlkzSzht0<3WI1yJ*O z^jceU0-c*&0Kp=zxlq-jtGOU--frX7Yjdz+^)-!Vf234SC;%zBlxgrWHqgBrOIvL1 zf}qf+$Pw<6(~R?p=v-$i?z;;u2(QQYbcf z@lf}GXHyL0Hig2v&q5Kebq@^X7??RDB>^+cp)(F}P{`IYQ4Hu4$a3kpn@C$|;+|aZ z6x}sgI#g+Wu6K&gs${XPjOP2?z^KOEmTd*%mL6WW3>t29FEPvogNB1M62$BtRAj{4(ctPrg2-0MAjdx z*Z21Ju8lON*Xw!*E^k~(91QmD<5iNT#x^(CE~z*a`LBJv7M)a)_w@-3Z5*RRkw^CR z>Vtz#mm<$cPPNuV8++KrHLlBooVT=M(|I?hyRA+Sv~6;CqCis0Wf24wAM+`0Hbl z5>a?Y;5_$Y;5_PsB+P!PiV7s6^CItYAc27#y};^8l0d@RE2S!td!_6e-bX3E^^WkY zH?i3-X?RuPz(L>pHA9S^{R5pjNH$6joBbsu7U<|aJM+!`y%UW6Dr!4w%1LgtW+92c z_xGA=+^*PiRN|}tfu?t)I@CY!%s|^4+4q@7#p-Vk2<($hS+3&155&%O6I;5+?C44M zeLZt#ptF>w(n#JHMqN-!DffMu8p*3#vleq>5C-{sMv$<=3p_Txt3WS|43J2dA|;0y zFCuRA1+|Dv2Ve&8^7iPLRot^ntZYD2qoBh%yfh%t!L6t+&%D!;Imd-*k@wAAhsXxY zP3Y222d1d)UD2QY*apf<4kappeczx_UQ;;&Nqt~$+WIgOzXmgt-jOa3(HtsksLXe;x zf*c^O85l@+OLCc`uxEo=<-n#S8Mhh|uMG@zatDhJBXd0_&^!|8;9PF#9|>fsH=#8HMvDL9<2qRf$5v=-fnzmGaMKqv>HY9cTZJd|Y4g@MnO zXdPWt&K7R|Q<^-L?1@F=#@0x=_4iLty4$Jy4Tme!$9V z4hQyJjJ8(E$>!2Q(3M!Jbw9d^-BqIB*@3Pu`8w~dc{Wjr>6FvTsJMyU$5L|H-L zj>cg^gG@tdGdR%KUFCY48zZ{e&iSjsp4&;$&B!_KI`Ag%D3P4)?9UGl^zK1AX9>yF z1Iu$>D=A+zB+&hgVywR5VN3N`=6HFg9?P62&(vd?vnOPT;vs<<`dt^O2S`5~5?G}D z@**+o{6IIY(?#Nm^8;<$1TMl;l5p z7MK~}qr6K3t$g~jn=?f8(!i_yz-G%4fsy)5n1BD- zriLD;9~rp5KwjVkJbf=SgnGX|DfG{}Nudwl!Bh6HhguEabN}~?Zv#seEkN|%AMila z3yUY=+4xa`;^bA23S8gs$5%Z$qx$%$z_C(aSR7pyxLEt{EpgGafqB}OJH?J?16{S( zcUB*IHqhCp^?kEC^Z7uF!CI$>tGhM?9`I?IRn>nv5V)&AdvuKG-aq)JcIl|VW!h0ZMV}YUDI=(c|5XAQ@xLK)y+OEQxKW0Xt4Kxr)P@rs5B#t%H@fJdSkY0V zXuA_07IhhxLdhAH8T1bHFy{R0^McI>GE4cd5_{ZldR?yzd_7s53;f}{V0Ibvmo8Cz z@O_$;y=X(M=&(^#@5Fal0aGv$A#on+0|3Hv*5Vx+)HsP!frJ!LjaV=+*lH3n7%5-| zS(b45uM*`*qdpjN3m(#ld8FZompkCMakF%f#2H(|x7+gHVA-d?p}1Z}l4puV^GL8$ zb17z`Q{+X09d2-9iXIBBjBd|@x3YxIQiYna_OZkFs2*5K;F_JGi|bX9V9zXqV&s3r zx|l8nnonvc{tF;d@$;D@s2_mTOCU;c%u&ALh(kIU36u;}k;)lSb6`cyQp<<{W{2=G zov5AZ`%9eAMfNR{PiY3^%2q~eBuZp)%N(m3=cvy9#DYQkIK(pGC0jbx|7uE?9UIB&*6 zR7oTPFTLen;x&|=04_UXd{c@Etdz6nWQbRmC5ODu0lA3yF!3^6>X7nqgxYB?pdg_q zGgaW@>Pg;)sYuQf&CUt7&S!Tl(NbHmVB!Wp^hiBP*r=pRaLp!G6rU4p*WAV>ZaXKK z>2@cCQ?W!v$=#Z+1QPV(lzVAhAh9l+w}Jd?-i*U6a{ev@11gXXH(xqj6IdA!xkk|v zBf82#p^4r`3oTvUIE(8HmYEITD4OT(!7kEgwCm8zjkVW#dqrRSaCIqf#!l|lMD&We zZJhv zB#YL|2$0O9s6@w4Kq!aEV;IZ&bO>+->5z0U#4&jpUQm!VbG*dVi}MSVPizt}q!67a z0nbyM5s#*4T;<@=^o;NuPOCQE;yP(G)K|+b65w49rB`l|*C}Mp4$s(baIi<1#4bC8 zmjlyyJz{XMLvx#BF%9P&23i#M~aq5 zW-N<|WEr*JQ9Wl!aG+0n;FqHM{NOh2#X>Q+FnGDPH!9vO4EEA$qSeO>gI#>u zPtZ= zJ7;rdcp_Oa#Ei7~ zA8=SXH@6^=l2Safazt)+eDRlbF}-qq?qZsttJ)ra;wV>Gkvm*ZNqKSm?HS_4T1+T#5RQj_I4)lU zymeS^F`7zwIb{UC-f>^=7>HshhuE|tn zN|^EI#9Q8Y>;%6fuCgLGjt1lDHyt}MW%!gr)K!e!NN%9yK2ITi&gLvsLQSA_MeZKD z@at{IPQ1{Y8oMvI;N|gnN!6a}VEdj!-=OU2s!eVzD}LR_$eUy4ZTMG9(Pdq*eb#0W zB=b_$3R-r2uqa*^yilKiTZZ`ky5N<3(6BDpuWQZke>lMaiPHF}olLJoCSfu7g<#+I z%O6dVvc-Go=6rhi{{88x>Q!{JUx3#u|A!^k<{z+F7Y3DBr^nO2FvZ#cxTaX=BLj&w z46eU^3J^x*PTe{;cR#YwKjwWp-aHq7Z6ssbX;)Z1>W333h1}U-4gW;j7lWgE?JQ*# zP%T&FE+G{NJ!Y!FGUR0yc<9C8Ih|Mq%JAlE#)GQpB_H&Uv_O3JVlel@ug@_>O<0|x zo*BRESGK4NiMcy(utdEAxgpWvlpLBQE`KT5HuL?hrexRW(#ix2_wJX1*?pdV%@i)R z!or;v-}r$kT$P>V`!cd;bwl5!e77UJlJE5RqvGI8O1|fNH7dO5v20;`oF*G4|!)H62P8Db1{B)oi$be^!Q4&G0M1U$c#~?*3jp_)4&(^Pmx_VY0hn&Ctx$caEGeuFOqWp6SbX#Ez}O%)Eb- z-lmzE&Os(**c$>c_%F&2Pc;{3tU{#AqHSHUd+$q`26Ky#K7a*9*>e0t97{j63JpyuWG9kI zxoYuFWzrB?h%1P9Iv{SV4fbRmW_!ccCCJQHHZcvQjEJf1RG+Qv6}c;@()3cg9Ns&@ z4@~qLOjJ;ujtly@V?-UXMCVh6lWTuWoTv@9!yDGxZ47ql2tOA7`YB{siNdh7di>D8 z_!*nT$c-?L=uX=>cH$x%$GsbaeLF(!Wzj=u5=Aqmy{E)~bE%mBda!-#U)!|i;M&V% zC;K-BI}R+l*XHU49K(596i=rP54%t5R7LJ&I_+_F^BRj^?n}YrNy!NHL(0=ko zSK_t$(t)kdod!rRlS934TX5i+5B*??=AGBv3NWk%Ns1^(DH}!^w(G}F z>3>0)iJ;@MXtQEsv#hTZydr0s6Cc=u`Y2*MW+>7BfeBAu?3rz!k8y}7yT$0cnOiQUmNpzCH$)9m zoy%n~*CqEEP4ZnrI;tNy5Sr?^-@55gmjyK{rZ+L3>as2JU z-;9e>!%yQcd`W8fkNA5Rf88%lwO-rm^+(~z<``5M-U-l_4-PHb8qCsH4-ScrTZ4-` zr(NlANK3Y{LkfdWvZqeF>jA z+p%2I^>x9!Cp~$25|{MRL&;py1A9#~3&&f0(U?m*fNvFfT~d)Kd@b1PCJLaEd!(06 zZ_*qwxTN8W5?s=|q6wGO&$mX*st=wu`I_BHJW^FK(Ibug z!t_Yt_%}FqGR7V$<>;{_9_fo05E5LOnEJCgaON093AOQbRXXMXA6f0Fs7 z%J@(7OW(c`ynW2*BKRKsVg@T$Gl;AIR4VVj?2)Gvc?DhTYU0XFsrP4SJQWx z0em=KhMY2PZ*1yD(7aQ(?{iIuLRak&XGCsJeAZ=lJl{a$)_uh?o)2Sa_@jHIG8 zVWXlPYsD$oN6(pl!knC94*TR~%DA>N~8#x7wqcB$Ec zIl_SZ)f|C|-}sVOkh9lYbA+~U2Yaiao(9(8a_E%Pgt@t61@jhh6}kH<*R*e>sGvx4UNP=@@%THzE}f3wY`P7q zpJxB@7Qe<#YK|(zhwlX2mznXzav9#3@q~KmuNkszrmHGsp?ChqKiR{s!6CrVza)r? z+yTJ!BI41MDS`N(mywN7K*gF-7*AU+6O>vtr}@hu>O@2JtewH`K5gu?;>mZhz=Jne z-}+v#o%Zct#Ju-{qps+-3O%@M{8gh*v`LwUC7rdKibtQAkskluUnrIv9p5;S>;Xhy z6|ctOoXrCwHP?;1wsce6xZ0QE@EiBqh@67dEN$)yac)8C8Qha81-NkCC1M#}@ShMb znisw(NWDgzd0bpLIJK+HIXLx7-*NHE;MCLk{L8_q-)ULHt3Mx-`hZ^>_@%gYXzF2^ za~Rcmfo#nGop^DWYU~TT@bNEYV{^Y1=SEEw(bQ{vhsCSW)NWkE7tz!!wcmUr3NK6@ zObOMqFH9YvwciddCWR*aBh-6asLxZhk8@IJXUm$?nv2(lr%urK;GqA-sYBZ}AYZ5t zZ}XlM`s(1M(4dsF=;FMKQ)ikNXQzBKh>PFZqkY8Os<`O?(uIHk*o)G?fL&xq7f>4d4nUhzaq8Nr!DwgoL-XJzUda-;q2RJn(r#(z}BF@b@TA_=2OzIithqjS6^*xxvq4hKV|RQF0vS@ zR(Q^yE>m)@J{`BO)846z1SqLg{PEh<3)>#V46?jthCZ-n)5IsTU@X_B_;kA%FeC1I#h+!4iP%$O5@jl z$;YYKB!)`9MCSMvNQ(P#iW77tULrAM&6$<%n{nES8DXO+v-re}vb6YA%==H2WS*Fj zePTvd+)rGg`|%zUSo-7@^YKB6LHm%rDSp@?6qjJWf(GbZi z1fS(Hr|yI)ml(D)L=R&#$5X+lo_Ylph$l)@TX!Y|zE!^KXcZg|JQt1DPf44cGX@P5 zwGeNYrrulD7PK&-<>(*j)NJZ?vq;Sg5NY@UANObWVB#QUdT{ov^b}${Ej|JS((jQ0 zz9nU8@QP!<9?=aVkT@J`*F!zX5>#`E?^J}7 z9bJt?-r)>Se3fj>4Bm&I4Ua@>kW#j2Hl4+% zY|(FOz^~6DZqieD(ecJWpgb}SVOosn8!2*ABkEipS%+tvn}K|Yi3o8+d zzTg(jB{iRbE^P7nKw!1y+jq39^M3H$-Kg=5ehI|X7>hpO^rR1>EZc%~=tEg-E2*FJ z=;6h~QPLj#^9mm9{U4|&Lj({`HfH+ z_c;xYSyatE$gz=<7nMkQbBTXS<|g`LOB$$})y*~3-S3{vh5}Fa41lt;czF8qs+U{n z?AQS7Mr^}n6sD3GW> zjVYsnf|6|)dUf3iks#-pAWkB^mTG^u9O$hgD*A(7S!)-DjGC0+5RmI|@SZ22b(@jw zx*QBjA{&bE92>n!Fd=LtFkVX)iCZ2jSFi%TQg0XfjHD>!i^%Yg2m!YMufXSWoj*y= zs+G0bfn-PJ*8;jf{x=Xz#$O6Hbx5E(9p_NB& z7sgA3mwcLeW;O)MqbjL}syg7g1D8#edhQT4<>~q=gqLe6mf#1oSb3JSSIE-HvrsJ0 zBA@nU=*y$_;oHg{>!Q(+I%0Q4OQeV7{z^?0ESM@#Yw%^AWe=f=92?LcKFe7v8!!Z% z(j2*-B0md78Yr?ROHqgJz9w*u##5V=V?H*%P4`(46V7P1CyHc)vG87W3D4bi8gq#! zW>yU1bmrZ-kEJ^@DaEsdM`L4=+jBtc9ZfI(D4Bkh8FvUNJ$OKboL84W^Y0oB#>6=Q z0SNl?NXq@r9m3$-=-X2v@(>e5ux1m5)R!f?I(RA))nKrf8f-3^PUADW2mVqb3tzy5 z2>KAJsmkvh2=F11I^~U-LT}wmNy?#_!fIWoBxQQO&@Ez`2~sa#=suIi1D~Y<;-3Zt zbYer{F}LW9VCWYbJ1@c0H=bxx7U;Vi{W&~Y!Hc8HD4J#mh9jv)o{(hn-kW6d;cs9S z##TPfNS`H)vDU_V?mnwQOL*>X(4eGyQ4)56n@Ra-48%F`Ih0;zt1muFEfUS*iRye= zjb=-&+0tk(rP^{quJoBM+>lIT=D9=pUM|XKmN^oEWFT%HvZ?QE*R`lY*|z=&FJmLK zl{M3auF|&Upi^0nZF}skM$?D%NIkP4K35)t5xNb00-hxi@P%iWKK0D}A8Bkw(gaFM zkBgH&&^l=|e`naKOC-mJy9A=LiT5S>H1>J3FQ&DKN4=m!huM=l=ENfn0Y}vxLH11> z(2{*A{!QXwlRX%pNb%;9T={%jov}1>9th*^V%t6v6}PbpNL$aYc>9W`27F6Pf0`+m z=M$GeJ%>*3jY69X?5C5^vSK|AVu5jPNqi(_wX#={HPp4BG=^X=3B%662sS{M$y(|r zZvt8?Sbrf{Wm6@tg@h$bG?Yjz$K{pP+}V})zw9*@(R>(qIW~iMAeHZy`0@-rALFAo zP>@aZ+6-QsX>~@dxBcpDJ%hrlLH`d^(HU@<+eDjYZzQg?s(ffNe_{py&E6zYpu9vc z3-UaYzi(mWl@CBjHlY36%SNYG9*W?nL%~^RXM1BPk3FAj!#13F6(#TAq;-F?6I0}HBBOlF`3&fOE*BDQ##w7;K>@Pf19}TK;?eejhHJo!rWh@_~ zabFmCF2ly|MzlA=UL=*c+S+oh%nvaNkv=nDE}I_YJ`kPJo%1AJF~>`^vCmIkEL*dk zB6|#B=(RM9G|4ekW3NcMNT%MeGna6>P(R(+NWIP}ZnL2&*ouHwW3KmQ)!8Jr3gehO znxfIV7$s-g7@6CakM0Xt8o9yl?%-JTvtZg4Nda0GhslTC%QN_Hjd#2bij+?@jRgf6 zo$KTsO>9lgsYknErJFbBMhe`N2mmxI0j9zDGAWnrfA$1jr`=na!Y5iA_j3`<~A=(GerJJu?iQ7E?Z}@0KJMM*}ZrPA(d-k#2i) zI_e%sGl-o=EoO4H9Ua=UI7#C&1m-0n@uR^BEDFhXCbHvxYB-6v91Z?Z9UccF)N0gV zNQL~l@<=ey09ml9)q4u6&SkoX6bzo3&ogEd#TwZbC}s|7SoRj!$kUfzQDwyZgc!9D zsg^1yXNbf6|M+tbczw_(`rSA1{)mIrD11U+~-|YP{q1>@fn8ruU9F0Oe>ayKsW|alBzB z^&BFbrspAI9yrFQK?Jw4$v{M&T4&t4 z$5^b-?5kOh6626mK2mxC&Ukx_AL}zqnbA`Pk?b-$b*rZ~uFjKw-(_jiO!G&ZOIGJK z?y~%@Nporg?z6tE24_Szgj1uH6DyY;CdGJXA~4X)W&RGH2CZ?crNPz4xHap1X_eTv zbh;`LK>-^HBXhlTkS&*Wh4u`=!bSi)ufQbwnW$&C5VDz~St=>oZG57{J5em!?Yh>+ zHhj%%>FR`SO1nxgM0lzMvOzlgFEnP}&%u;jNU!d0gL29G#s#AQY?WCp?Q}RTEpbIsBCDWHwX1mCo;TV+|wqmHJI!7jjY?_5WsTweX!B!qTyLIpk z9_(lx?BK!owhk_oc=){5;ZO5mSL@)-Ja}>I;E#Cl($>KTdGPYq!HfqNs=*S75Ufxm z4`1Cnc|tF${@T{T!+7xe*1?l`@Dr_rXCZjg(+CY3h=N7BdPxL)L+j-0Ih2=L2XEoQ zue1){$AdSw4zA+CueT09%Y(NBgHe7E7kzI+<=xiFlX&p9*1>5!_@mar+y=I(q;>Et zo_c5NVA#bZlv&X_oLkQ}?Q0#pm_s?(I(RJ){;YNID?GTeb#Ms}J{}5|Le;P2;Z>pV zQ1DqET-`d@(1(b{S_dcb;M&%~BYALL>)=cTOSVmCTZd2M$s1Y)Z)!vUmanKc+vMRc z`WjAi9#`jpJ67J`JU!R^g5maa|8UG$%_ZYW7R!6BrvYm!hB~+2TyH+M>IRsdU6sz5K-^O5IVJx6 zDmwF;(SgC-MCfpff}8Sx?#BWHeFox|^IU1F?1oA#T}CHvR*7M#(EAdfGMdt@-k8Ox zYang|0FW6Lle7ISohC{{AbR%a42SZh%!dsXSsI zyxshr;C%KahL=j%;_V1Glt-Mvc-e46vT3N$UssiDk8^+(SlTRWb!v?I*2HXt+ zNoA8W086NqxCZ6srW7Q&yMwA^wnB{-hg!U24kOEg-4EL!Ey?&#$W1xiwr`iu0T}B{ zvGOk8-3$|TtkRl4WxrL0G)+=x0l$NXi7Q4DIo*Pl{Y!$Tpss1bLSYVk_${wr>N5j( z$aROBoKpkCeyte9E#&Mqjw5M(9hXGHCXC#@@HPSIf(<(`vz&{bwEt}qtw$RAib zJv96D)W-8J8jaEjdFK|rrfeG zsYx&e@u)DDdMyWKOT5>DjZZ+M5*+Te9G5MLu$Fr6u0|e)zgmsty}R0b9}6K>;j>hz zg_m0zP@D!#!>a-->sdAs>?fxo0GpO!o+9g)&r)Yanm}@3YNOpw_AF09t;W@Pw%nap z0S3MbZ^Udb|MIuN_TFIKEDhY2CP92=lbA7ZIen`^J|yRZQjJCRi$}~Yjm2~(qRewX z;F=^Cm^>_YQp#eSydoAv<1=+6-j+oo)-A|W8*kycmK+Af(=i7}c8Fw=$_B2vvt@4J z^ST>gAaR%G*ihku;SwoCKi!bZ4M@+7DIU&x#hzAo94gQ@5EH!g2^^EpWzwH zpK2ZWE3d{J7%ET=1klvn-Bu!+!DD3;X7)7oSO4SFE@F44$86rSGXckFQG48#AkR$< zRG~QDk|3R@Rz!<5q6KL=5K;q;&_ZN1jN!#$93g_^1Z;I?PrSBV(ra9ql5E7pYn(4z zyGEOi<)HHe_vF}oc5M!GECbc*><}Pb0ykjtJccGbAjN)6Da`d=aaWpNYh!(p%06?t zzb%?X7QEVB>3WT71fT|5z6@oEff1~!0LeVpXm(}jHE14gGtRZit|Vpnox*jU$f~V1 z{&gJ&q?&J0ZQ}=h$xi;zokF+v!MG}zzt!O4*X|Vlsg0=Cipp)Xg<;+BVhSYr&f;f1 zJsR;R>O@n5PEAA?;cuT#EWY(_VX7_~tJ#}jK4qF} zWyC|mLTTZ*Bx_kYe#+##8SCLu`2ly*zSR#Nm>$EryU|-ejv7ouJkKfC@A2` zI$@Bf!nCTDAJj+eK);TWCy8=wC)p4ku|J|T$kAXc&5Dsz#*eRZ^;j`d%DNRVomPyz zYr%b0?q2K;6y%u|uc=Y~3Z}@Ruf_42Wg^SOYalb8#*&(cDeemd03W@a7LO68Y-6lK z_9H&Fpy|HuX|6l6Kd3!D{td6=*BI_w8u9n-lP5??h8#edLSF6WV{2w48P4rM187{G z)w@HfR2Ur4PM)p$zOwtoQJJf}8W;nfrtnBe1qp{N(+@ zHQKw`)9`p;E5Y;ZpLh$otTglvx~v>Vpz5-cfFS5+Sj-`tUoUhe)pvz^Sae{9J^=9w zQX}?3fa6XStR9)RXqA`Vj*L@2c|hnCm%JC!D?^lTF+?0N8%Dx@Qqz5}Qulzs_eCn* z<_TSNIoBy8<_VK@r;?Qw^MtOWH&2Mh%G3}C{jy$Cma0-$4lhP@&N#y+V zx$K5^iE#(!UxR#*?MYTD=Ly#hqbZ8*16mr9_p;5v63S`=dyy=el=`eX5|lZ3I{6Ej zAw9`T>Vrb}gh5EGj#J#ARI?EbDEIW~pWpYCQF)_5h|hT)NH{d`bZ}~N0&yilMbQmK z$c1-LL#}(cQ*cbMM4NKhb-PIvlKR#QVYl(HZsD*P8&pz)pqLg4eIk=Ns|s{^JYEiz-3VK+|3%;lw3dTr9I-2)|5Y2RAqHp| z&84g1fA&iFKhGfme=MI6;V(g7{G;egEy+XpkLRGTihp7Y{QngCCm}jXE6g;=QYrT4 z%jdly$00hj73BXBnyx{v#61eQzLK{aN1oeZm!r}eE^S|_wwLHmH`$)XJf8c>&50}CNOK5DEOaO4sEb5!i2zPcR-2W%kf5klJ zGN?-}hrYu+%^$}764W1~{3_~`YSkWoT9vF5YcWh$64^u2^hc2obrK2Hg;x`*V*vDO zeY=NRv6e!$wM=bE@K#8p|9zzkW1U_{1h4x)A^0i+dv$p_Tc?q(+%?pmaTVN+MM*7C zKTD{e{a;a+NC|2slZ-{5!Wi*iaQ_ES^5=QQN%;#JCC=v!gnPsPiu+|z@A%(QucHao zf6;%gO7<-TAlOA43H4_Evz2&h-9;a4EnQmlKX=|$N$wsK!ub**`d>N44ZSm8E}dR8 zzy!=EecRkOC8#|Uzr8}|q`rComHVdqjVt#{_oJxHALx_P#mpBHg4xiaLlpjjDE!0! z33FV)^!s8Wb4QG@OdtjH3!diBGqytcgF?{XOlYFB|57?js>1)@gy47py)vzCVMQ@2_%hc`u0-#qT_(YEN9}y{+C%6zs+PHNRN}3oL zSYa$YO^%QkXQ=s#To%*6p*H#E|D;WP1n>%-fa*`xck(oUz7t$a`GZpD`-|J;`>VAH z+8@J7t40a6$>;EIyjq)VCI81iB<3$BaY)K9JRgukSf~F;1po2>gy20@ysp*>8nGXS zI)Ow+kpF!^H5PqD0D=Vn>0*NabTxvvqIa|YPh8*t(5n$VlVknoJAv4Ybowtu?_YxU zr%NYBtu>dc&Wqm02AV5zc#y%@@g$cpxYW{Uc2%i!3jqicy#8W>*Z&U$zi6z#P>=q1 zHG+Rx&nx!7kE=m3|19NKkxsur1i#RV!KLs_^nW7wcona!ad;w6atXm((OU}7M8kv$ z_0FnaL%17@Cp8(1bDQSKrE}5Gv*{XZamar?Uf z^lIYhHY)W0!+;yx0_k5l(zFo^!yqOnzy1$chh+r~S|#vCyc*IkA^wU;2kn1XmHe{^ zKv0ODrw)1k|D;2_ z8l^%HhO+mzUWlnV$rE9RNDx| zMOhv3eVu6fTTl2AB#EYf4-ie~hKQz9Jw($teMD0m ze2>Ss8Q(+jeQSTw^vGaIG%e{anq0j_({ueq)3HIK>5~M}RMJZ{eSM8+>WA;P_>RK& zV0_;d6uA>;qWT!(9YY>3F+x0HviY66^jonE=uvp2k+{Aai9w@}-V9#iR7jQ?w z%LJa9e8oL5oWkS=<{-G0cF%7CSLdQ*Zo4y}Mlr7npWKO$`N!Fu{jrYS9XnfOsg4)h z!4^z-M<`(j^Rb*@SQZcS>*cS^^)|Ne6+#OolP5CmTe}D)IunST0D>shvCrWNYPZQa zg@MxhCTh=Nowg6Qk(4>ap`vL98;1m&2!A^rG(iW9IEVsP_66mntv6l6B>cw8vJmQt z;WV)O55plN90|H6w}?cPa&n1~D8<0$1hfufpUjCoIScLOUvZwcv$!MQhy7BM&HOlf zBF{d3H#Sz&l0R0Du?O#a6YUM%f_3iTuI7ujQ*T3fxt3zfoFg5k+u_6XpZ7p~@_ocE z_np}AWRS8E;cbb zITM(AGqGd0k=)$8mJ;}ykza1e2{nhkTTXrm$j5ip*Q`fX0`6LN0yVR9=c(5L#WPDv z#DlXN2nLiT!#7B0Lo( zR4-6&DM8U1)(#K3jGcLx{JTIQ_{dv6^NvT&4zfkNK^%6T3wo(Mub=^u~+(IytY}Q?OIx9sqCWzMwigdJq~m?UKYn zGbI}M@?f;J=CWP~sUbjwyy`Nie%lkY=kUR8S`B*`WqKATXx!b2@+<-fv%hdWMDh{z zW+t%slh`--SdQu><0(j_O=AhNt+Skv$KKgL!wlH#1Q#4SU6TYSVzOfzXZLWjU_dWeI3j zNc!by7h;U*#2C{>2oU4i@6kC@^YupG39|CL#T8?yEMw7=`1C9@MHq|rBiD*$YGZW5 z3vUc525;0FSgu06XITOcvw$emuoxf&MB!~w1Q4u=wDWl;R*zqxqLtIY%MIive;=aN z(%b{7BzNCXDIG$kv3A z%Qbd7fuje4m>!E=_%IgH8N@a9+;)#n*#Ih78bTHRJA%~;zlgUGD&Yz@*LOK3lq7@3 zxUvZq_E{>ir=r#hr~AZQHvAJ}>{|3#d1@slEuLdUof4GBe2eS61^dtNAlTLZhezS*0)JsZ}=-7IfKWUD4rXLvS&a z|LuVLICj|u+)3aMzQMu0FEDfpI1)~-kkUBt;WTxWor6##(ygXerh7_a;r@cHtqhg> z>p+lvc>!7?+6i~nSN2hNTV49a z1B()DY&U4i+bfG5g7tvyqenR&Ua-!`WKY04EY|Za#V{hLIk2YX?4jy}UY?v)!8gP!|bqIH#rNqiQ97Aaq z1CFKZv=eT_0hJsE_5)Z7eI4ksO~1jS#GU7|$=P?sSVS#17XF4Z;mg~B(9MhYEXF1U z3%so3_-2?ob^)e^lzF0S=!&u20d>HNvD_oIR}lHIZ*eZ`w1db_+eV5o4r@FQgV_o< z;*ETl`2i9VjSz`BY!6a`Hk{uYQ}--b;+toHJAP`$*`;@-W;94Mv4MV>zO+g&FCB@U zv96A?O)t+dorO2k@`pu?U-9mdNZ={2izr7`>NRO@Xbr%~cMGr%U6^9k%vd7S&{PBp9c$Bt96kVzlI#g!?N`-@R8dnf@O-7ZS zCdYbGUpgYjd`z~OsM>au2FFL$9(zDAD zahe*eAhz)ElAh!RD0SUf0sW0j82D#bu)n{K@~6TBg1ptm`W+%%Hz&nY5i~v!3;Yeg z-gdr;qBYxpL%pDNjrJ^w2VXxDj9-P{z@pf|gYhdDB7}9N5d)uA9Pa4<6-bVA$DwvQA{ho9ofRK19NvAJc%IGB45Hd&P~U1-QDdxbYf@ zHdhB(+ovqGe13F&dZN}?gqD;v_2cw&Tzyj>)Ef(*1}Z7;1Y;33r89u-Mi3YVMmL#4 zV)~b+JZNxtB@pUR5Sj8|f;$@E?gBWyD^D%Vo#J`O6fxhN@=)x;A@aBcoQZ%YOTs?* zB#3lUi3x6KE2cDsjrs?0{v1M{Hf^aYbCOy#6g0yW76o~WShQ_enVL%bIplHVWxWu) z?3Sl!T=&=OmXlH!39feEA?IT!W@?!$**9KX)8)o^ch|yWt`2ML+8d{7-R)@0R*4Qr z_EgK0cljf^Cn`YE30MQ`1s!1MM7VETO!XQqePDN2y@m(tToLB_40)Fy6S^=u8)=Wz z=y~%s%3TYD{$lv+@dZM!vD~#6PQBcv*IW`hLvRyqIViK`ejJZqIQ*(d>gBSBj!!{$KXd*39QXo2ER%@oHo>6lXo;;?~98M#2{*(&&x zo}6Ux-Ynvj3GZCP)NJcCJ0^oT3Mg=}%*O7*?mrxA0$S0|*W<4eXJHQ=I?cv;Ae7A+ zF%t^Du#FT*e^lzO0zuxjNunqxMvE8Mkdyo-7e3kI$N3QD8BJdr_duuK<{eH6*^(Bi zrd*sV61yB{mSKzJcFltTsWz%FBhkv1zlf982$cLq>4hloux)a=r)uoniey>;WQ6HC zqcZnlOnx^bm;0@J+Q2LaK+w4s5f=?15PPu#HkOVNfL*%@ty9lo1%^ej5An%A?6N)z z2V=mkpBMxF;GQOfyD~7G*XB{EqMp9ID)u03h#NJzdf)+-*OlD3)j)pkK^e}86Q`rf z>t0ckmI^}?H@*@GVA>7ogw7F2X3vAZILyu9U^FI;enoL3mCpK#vU#bH$d91iyHw~J z(Ri?wZ4cdIJT2Tb9ur?;owaw?eS#zRgu) z{wiFjJHA>O^H<>-UHi4gbN(v)qSeK3Qf@EA8DQ&PF0L*VGIV1SpC^9kurYoChRuok zjS2i$^iQy{dNi^xSML?3_!fzB=45ZQoSA9G2E3$~X-CHmY|_h$W0i1yw__MnJg?Do z2F4k+sd11HhQ%E#mG@Q&efZ7@^CvJOAUGGQ-^g6H{0-&QDq(fXKi?n~8_wVIu6*;S zGg;R)V0q|koMBefE(TC8b6REp_;uyY)xzoyKfZo(cDq-}^a}Ii;(XvjwyY7l$62A6 zqBw|2uhz2+N2H3Z-L1+DSs12tk_D5l;ucERwX28etW0-l;wNsQ#M5GKL{yO3PMVcc zSx9U1`E76l-On~eDz~f?5|nG!3bT7=BkXqkJ%GQ(_*;R$mH2xEe^1{gDt})q^o!{M zeKZKG?0V(IS|Kf>X{x9s`h>@N-Zxb=EyN#&XVVk-dmeu;xGq)jxvfWLR}_YwX+!QUSI9l_smo2Z;x2Pj`cKL|pR zl9gVM2W zq$EEsOo_R-IjH?0<&nn)C8h!+Ly%ne4J_X9q|isJ*q#(pV)7@1BlOo&M7}oW%(!sa z)BTG@MR-6PvnV|rme7yF%-Z7OXM}t7T%`G45^gHK?*+l2i@j!ki(ZQ37O9ICBi^Rd zee{6x-OIv#lRua%VicOj4=2P4!aDz1r|8G|3DmV;M%>@<_d5RQeM|6<-s$h%;BP2S z(Y_)iD%&>+Ln8FIL@PgU5@zw2DX$3M>6ZA4Q(hGuTAgvNf|u?}$!kKXE_bQo`J1po zSGYv!wON=ObLY@-)r8K)&ukXbwH;%V!(s2oQ8U&=D=%&qb}DcGLzvacf`JjEYb^V0 zNI2P&Sf$@Tg~q;TAue(Q3y1B>7^P(yvmu6KVe%HrGll^_9>dz*rDVP?+|u)+q(|-| zaRtpkoLG79bz$1AUk?fwv9beaC@ULF;K1d#MXlPebEw7h3-Npk7=s0h7}#_E^e5c@*2hYj<6u6JthJ{ zmfzY|dEi}PtTNzTaQxc7;qak6yu8m?JLnl6TJmZRPX&2MEx|;iZi#ig3#i^A+?|52gq&ROzj2vEz*|&{Co989&edgO(Q%;USZ~EH#$2`?>P}oR z{1XbV1Pp}#?J*^9tI%EdxldWTRY-_94)yf8twLf#B~&J`ZZF5v0N^Xa1yTV=B$QAq z>o!gKVyloDQD}`;dKU|WliztXT;xZv7dh6dMQ&&gewBi2)e?&yRit9!yDp79nvPg0 z`Wx!(060Qu)q161n~<1HCk+Piy`Q3kKyPUd&ZXc+73dA?l~1<`wzom_a9MieqXp-+QNbIT+=B!h)Qa6t_dxGB{c^Rrr`c6u$boH zPAyW`uM5{BfZpg3)oPw>4ptWxvKsSenm?V+W=~T_e<1YkqHV_Mb;=jSY3({?@dv`- zxDR=0)I!cs)J^M@4?Ylj#H|iT-4KfEyiPgw0s7uQ^-Av#g;BZ>)+@6<6jGz@20|{0 zS+7@K{ZP0ejkce1Eh)@&&a;8IeI~3S)nVrEx)0NcdvF%jL4=^t9|?UU#^Pn? zM?#noEYco18~vog*8gUM?(H|C4qX+Om8w94Hm%ElXf(kU;>OyaU13v>0CMNpi9SQC@z;)~c9=@Vv^brcWZlyB3UU+x*G|<6`qbSwW(tSmh z$b!kp1eH7;>1bzytO9=Q4gdzl%%A47M{-npscIySGGB^mQc$1GZbr+-toJBc=Y&3K zG{MGB>fqh8GuhA}04&6W_V9z$=HUc~AB&G&w4e(rg!SC*Y>2YuoDe$;6Dll_(myr_ z(->G@)emqp%xs+lakp{IagKW=&T+Rnwc9)6Y;l`Y$4;T<=&az3$w-1aTJaN9{_sdi0NG|tZO?9v)H>5~?~2M{KWnY(H3)SqTpZ(`ETKAVFE zo@W5t_28zW{p#^4*sF{K$hi?PWEi{?YLF^Jv)K!j3zLnkMw}X%ZDs#);2VdH+U4b| zB~&IX!L!NA4bE|QW<9!urF|LZ{YQVQIfRrsHkgm_;~3i!Q#B3LvlB4!V$IoDoYihy z9f0+zn3G!J&h!`q+1V!&lGt3#e{rGLnBpy!>ALG!M3lbpXNm5K@O$X#6bWohk~1G zkqUNoe-D7kRWqK1fX%U;$ZKGeICwIp&z#Ji@|ae?&s7F_5u+3EioqeKupq*%O`q;Jur1@1 z`_Bs3>hc~_o;@pcg$2tHTpkozKW;a}(SmCEqcL+-_UgrfJu%KB9i65+q%07it<_F` z(;s>pF_tpVA$Dnzs2=7|o`_C*XV+dn(e_Xxe%Qb-YSA2$xifZo!;GFuZ2s1C8pUxE5c_tk z()e%T_I{5bgiH$Dp0I8!08nBFB_0w^+&D@p*e)atoZK>T7o3;FjSs6SvHy0^4rdmr z(7SC_c5WB?4?K_1mIjMovFZ;~EGaQKtz3>HgANuq$VgJ$`D5W0ZJU1G02RIoPb>GA z2-C$fY$3O@Hy%|El?cvw^w!N(*o#z|A5iTuCs zRq{R&l5~^rQ#_vtLySL~!hP`(cws6fp9uHsZgnfkyM#pD3%@Ht zbeAwlXWyWFyi0HxUB+Ze-oYM6^ah|UBw6gX-@kZU2(@MpCVot=P z)3r*E`$ahJoG!lSelcAeG3C~P^2P(==!hM5t#axC(Ht=Y_c8UECyt2dldCPBF;85i zy*1DV2TaRtaG^o$NUh7nCPs%#t^GL=Xx3k~aQdW_bI5)Yff7dOsj1EQ5bQ( zr?$V3&fH>6$N60ey|5~QQ7zdM&@GMh1d^6r!`^}~O;A_84SAJ;exZl%xvk30e&HJZ zd+<(E+OY2LR<_g%@y2s_qlHLUf7T$NcW*l*3{fIaL4p3ym&)K% zLRVuUf`h;ZoKk@&=&RYhca?>wP)x<+#ZR3Q+D7PqfqQuIt9623*FOT5==w6z9gQ9T z=3`!q!JjU>8*DkO6C#48b;kHXrQNL5>%|ToSHW!zT^fgu975aD^4@pK&w8;--|69q z{j{`65y4J{erYaAHGJ~Y?a|8ea$N273SPF93;mM*4ub`BgQ#sLblWm4;g+$dLP1ak zD9EI|c~Jpet{j6*@}~ha)}#zqBki=$*>Z*< z*K;6H%3?NaT`4xUwKfodn-Kec0t{PBFH$GD+eb1JGIB{IlKMN+Qokz(OWW)tGh*f> zXzaFZwg;-9T|JlUQ({vA*}An2=>VdfIxTb#IX)wlVH7yabl73=?*mW zcP}cZb_j#pZAUs4fXKGLsPr!rCikQn-twi|{+gvX;b(xxJw65NG!wAsY@5Q~dqR1< zOeoTCgPNlx>=eeeABRatM5Vps1#>-#*{wCo13NJRTk*J3zEik9jtd?5>3N)@c1g?> zN-}n}(!N6IoGi`&rjBt$1q}YUM(Nq#!l;ggI~SUuW;=~YsIrX}Jg-cv5JngYz4vLZ z>KEuodA>sE)t}Ew(=jXU&!U@wyai7yXA=gTY<_|a(>|koUm*-I?n~#jhG$Q;jqTl_ z^w=#V86U;kaF9+TkWb0pjq&Y+*-F7~LDKa(p)~Cl=JYDX#oJ&o%rOcJf!X4@SgRd~ zBPrqs9`s*>R(?(?+9OPez84c*BxAbs%8@-nrqG{i>K?1~-YfL!SBLSAB3FV%g9Q%! ziZ!~_tj9|5p{Jl)hHDas(>UQ*QdB`Y$J*Q zj}ezcyRK#f2mrl2@pO2g2=>DE>|PY7*0Tdp@FzKz132J8aR{DiW8k+Cv|;0rQci|- zww|Br#wn1^d7kdd>iRtO7BdhULtY0qD`&h72qb5{NYDO&zby_#V`nIcPRVIxORl9? zemMZ?rw$^|OQV!?pTf5P&M4*D{X$>qK}e5YBUu4{f@JM|Ol8!UfoG1*nQT7hvwW0e zWgq49R`$6+avJP7fyOO*nwmi5>yXtO&r&%Aa+>td!LeUArw%tkSt%GV?~*@<LLke>xo#lLv&Q@jK3hK;b5^pmU^^Ej*(PI4F!7QVTsFBU_Z5wbOe` zEL8IFoG)?^+8`vrF|zRzA3rE0bv~=+8AGl`KFe_{Ig-?&xcvuVq**#pi9Q75%wsPo z6Auaf+NqVizP}bqs{I#b=^?Do$upGK55fBV{b}XrL&DY`4Uph=VAl;tQ_dIXq?Rv= zz>@2_~q$f zia19S5BU0NWzZ4fdEM*Xl)XoQ$+l$Ww&cQq~pIsgsaj;jsi zrj6A6-h!$Wi@3CB%CLVEN!&wO1BAC%&%eO7u};HGZ+~}nXgUO2q{ja`&*~{j+e9EH z`ai^Y0MP&LtX%j^=$b)*Fjs3{?NX1F#GG*PJu1^JxC2c06*1j!D$_krc`&c?EY-rr zZ#*0kXDGM-N9f2V0#BM(+fBTP+)FW7IGIBuJBsf+R zY2F(KVhC(yC#B{;LP82J#!`uj-Nasl;u5YXW>t|?nKb!i>9|P?J+e)fw=xt7<3N--$fk@9)`LQq)Q%NJ&76!V7{8Kj*3{!Ip0K5T8(5khtvDV7wul$ zFExg!u%IVq&0mj2?dR?W7DND3Jr@a4@&qv+E=!tPi9=^?!vNTpLtG>vmM)-0_Z4A_ zdKDp+-oj#N$_6?z*F*HDY_CD}!&qGI|I7nfQ(DA#@|bmPYA%rAu-+KhndoJ_XI zl+|AfH;&i|v0#XZm>W~UUn;zoovEdmsm~Hon89DijWw6}Iw%ca3f)a@M_~Axhr2Se zj&rMq()So{HG8lu+*P=&fS*1g;}|r1rK9# z?ev8@WXrQ^7~}Lx_b$QCli(7yB3)NuNZbsKr( zK(djm37sIWq#PGI@>}-Q)mqkWM6vz2&`*0aw{d-8_s$gkroe-GuU*8AA}Zovz+=wt z&CpX9QV(#R$@cXK;*EJW;r;0#d|POq{aia`?+GD6@*^ZX&)(LKP(mg9Q{o@Z7%fgO z$MQX_1jZs7z(JMwH{f%X7V{i9j77;b@fazO=2ib9bc`RoS<2;@myi%g5_B}5&&kP= z_Z&IUjH;vLY3QfXo(4(lZtK{G57vkYp*=_~IVvUI-QIB=20h0c}E{&EI0PQl`ZlggpV}LY{UF42(cfcc5HIA7}FaU8sXyv=F zgn8q4lbIP7*2LKxlc}B39N3_^8KW&2<05MwzS*rv0rdE{xY2sk@I7)*P*dameNMmDLIw_mJ7Ut-VbWqxUBYdH|&|azj zMtEpQJ2W87!gcIN2uYyctfS`D{MLxu`L=*5z+1gjBHw-5sI2`~=+cdXuup}4G;p{o zfu9un91{+<;2EXlTcKwH#ccWr54H=spBZ*&mQOMmw<5(VzkMsD>9B@1x>^_%8-e~9 z)P$1Z0SrX{+F2>6#!UF%9T9_`6w!d2pWJ`3s9vZ1 z^kA!%RqhNin5wS3ddg9`a+BX+Wq&~ipM;yTNV~4XO?xT;5=*)AXw;DGujD4Abo@?; zl@?RGS7Q6aWT!TV-GHtUM0PkeH`M4r;0A2%fr?5g!B^d3Ki&EqG;H=WWHadLDc7g; zbpI1BL(}u&0CkikHo*JqI0LnmgD^n(mg)Uis1TtaZ~-%Ls%VF&5TvW0R9SK-DeJ`f zZ%1%RlFT`p)JV`;IrNddg+eCToWi<~XFJ#c_JcQkx)N(+Li=tX!4h4T(b_^WMZ+V2!P@Y{X z7*b~{}w2No>d z%h*lRV1pSr8521Xw65s^40%4eB1i6lC5K$NVw)@BRBCR(QNjm~>_4(MwaHlYF8FKU znQa2MJLsARB2gflTewP}8Lr#?4_eSnUO-7A(xR*kU(EkkCN-EipE zqX7~BY&d>r%Xp3p%;30t;BEORvipX;ucB4Z*m6{sr3319CUo-ru7TyAl892KOFh@^ z@VUc%4#L+OSAR`Ciy^?<-Zx2)!eUTjnj{eb0b67emm79?2|MqY0Qs)W6Myo-5iV=*Cxd%G_y z;MDo(ApN>&;1Cd(1_pKBhwC(#LD&%u9AtBUKMCTKJ%Q;0+!9nA=A(a<>y!8xi3KSs zK<5{em(2UHKSey*Y53IJaN4CQl$BZy2+SpEjw6SY2m9w!Om|DZa zM^2#-We;Sa*QqFH+Z-cNJ`iSB)I2aep0N2l#?Zj<1iWHaHr&j^p5kF7>%BaN74d-E zcmM@67Xr}E-r)#SPBEc})Zb+^rHpY!!<}s`BJJKWIK~(9#4#@qqk02IMYa;c91?(; z`;kUYXEw7p;eUWya+31v=Q;#kFI|!B{#`@?kggd*IfoEz0vfH$0g9jL+*TEQT=FUF zjtN60&O=ihAm}Dyu3Al!jxRoMG_~M`izFqcKmN}AcFT6nx(oz*Gzm+$!ClQ2MlLUB zgn_`Bw-Vy5g){GcFbEZu2(I`*WdCQ4%IV(XwGt(1F%8Aq1EN|u_maXCcJ8I9i=2CT z-R4@v_{bm}bzaGOVwu9JXHSBMAPDvBGxY__6?tf**Sd!#VkG+TyZ~RXklsgdOEK!< znSxA>^U$$ik%2xhMol$cZuIIE&$+u}tfh}IW1!V{sDfwodM&Ee7 zIe^`N<}i%jfI)zIyV~Wjhe?mbz09uyWG?-y`5WFK5O!k^b)BqF(BsK<_B{|AY0iN0ANgB#!X{cXr#ZtJ=ol+l}xdfV~8hN^(vP>-eD*QX*ii zgA-$Lb*?^zx`y9n%XOeRB$)fmSo~|u#WKGPo=1NMB2cJ1xuy~!taSZzBg1-VNOQOj zIs?CG0!ouToWnpl`Vi@j{sh`H%Y3)kXPK0og)Opr+T6^^CsWXB;CJoI*3FT%@b8CROmhD^aL=LhIk>37 zr|)KEXWs;FxGE^%)=$Un3!niOpPs<19$FOowq7px{}9sfroA7kem46k@nSsA^bX;H z1C2vAzijqot7QA6P`b#MG|17M^4?Y{H-!>nhz%$6)LNSdNp6ubAItT+OT2m2^196c zh@CIk{_DlfDef)aymx7b&h=nk>?!i*RbsZ3AXy!Hs0Z0T%NCenU%-&SnFmfmP(Pe{ z>?u#-gSn-W#i9Q>bsy7?$XwP(4XtJYs8vpFE?+pw!46{fQ!qfQrZ~<{@BoJv`55_Q z$qhN)f(o_^!Pr_hDkZNHw?Ct_l)P$V;azwk@iCnTR2d7W@R#Gp!by0M%U0`0rj4fU zOG|&ntvA>wg_+m8TgA{apNQK2n6%AZO&7`^Hk>qB&4V6ASIr; zQ1HE$kJw(`4*Vvm+bCs0wRyLvU?29n9rvvw@i@l|Wnvp)x_UW2S5K2x-!850f^DO| zH57o2q^r9uyo0Je!#DM3`RE!QDn7}$^(Lx#YK1i1w~7jsrGkS#edfU5e8bwpP$O4v z(;)jCZ-j5UZnfct)$P)8Ttr4{#j+oOgPix7XNPY2LjjHZZ4?cm4s?pChR(4t+Bf6ErH=KFZKIhd}eE;HK7tXwj57}KJuUiZButjSUbYe(KO!?E~mFp2UnCfL^ z@4^#LUik#xKVFW@ps>|=CH2u#Sx%*ByelbKwtVD`k}cc3 z9IR|P=;h#OS2_nm#-a?p|C)fil4aoP7FvU0WB6|s8^M2%vmy939|Q350e1yU#1~Ae z{!ub70TL;qE+BeepyJFW-WLe}@~FkLaDV_WtKm+1Ba9c%#+!P}a#*Ek6q;3vcKni4 zh#7M9n7mYxXNdVv{fODe+mDCj;{5N!auKT6C35lc{g=u`+U^JuhBX_5y8XhRgh?nZ z8@9=1HE1xq-{fhkTG*FOZJ8SakEN*P4*nk2`tQ_&kI|-{P|12#B3Jqk@AHgaiyObO zsc)6?Nu1f-}@}Cms~pzX-pbCi{sy#1mT2P8Z3W41nW%pK8Wq@F&2b5=CPXErFbFRWB@H}_4sqj-5D6|bsp{Y58+(&;Z{M1ARHmK&qovg*c+;d0x2Qo3+~Rx zW8w=z?50u*{^}?X)HRisov18RPQ5Djx}L^n*I@8}Glz+s!`#>yngbghRvY>0BUZL= z57^Ngr9@_lla$Fl#RS7qgs3JhX^Z0ODGrQ#nL>K|%gy!vy-k727@Tr4VvBVsN;XXHs+ zn20ecA`|ggl)}|-2~!eU7HC5S1!ri@V}JNwBJ*%664nJW4S}pj_BY!S`RsNkd#b64 z)GwNBHQqy$Eqc%!=6`yy0r;)V5$i;tfgW__Ce#4wM|3M5l|Z39p5EZW*o%LY(KYE$Y`Pe!BrNQt%X)}_^#YwS9o8|dnqB< z044c1T<3%Wm9@X)1Qx9x%1I#Z6(TcvB+q0MgXJcLwq?Y&ASC+~_849=)`Y|Rn9(1@ zSoDFNj^Bvr%lZwLARL2GOYWOAJtk`qTL%Z5U`Z#=H%YvTCbg)RRtU#P>LAJVz@$r$U*ZPlJq)Ot=!QlbPRDr@LHrG zq@h=Vlu3{{IJ)NA8OhdhK&rik->zD-q^f2gZubEYHB3_!deV@R=1*CdoD`Z_Ye!4uA>vYtigYL@24Pgg%Sbj=LgWu($_1#zj{~1>r3U!h`!Pd9=1<9<~B*iyX&R74AqYNx+zW1H4dW1C3lN zdi{gof~J2JTz+t6a&idXmAJmWN@4=teiKPjLlErYdbQSe$%EOC{2D@^ zCBT&c3yV3|A7hW-gDN&?;m4E?5STTzWmD7_T~iqjDJf%p;Y6nTkC(APJ@zWaB7hB` z#~6C#(8EEG0(v|~k5YQPEa6#m2#-rz)iI5F3hyrT2Dgn8UIA4kq~$h?Lb(CHv_l-L zDgAPK(3>-wt#3}xEgo%K(()pqj5GO9%+Pr?ueY*QqeB z6VRK}z#>|fh+|jo$Z?3o<{Em?n-gA|RM|?Z#WA-YX+{MrqC6E4da2EEB%f?F@Oz3} zV`)gzh8DgxK~y=Z_*H-XY}+wfU*(oG!XLM!kwMvMaxhZ(_G(py4|>hhSWD&F9*zot zM{4Tuc@h>+UDMRf`}!?t>Pa~5M)id46x@ctg}wSAWy86F5dlkeD6P7cp4_Cu>H3k5 z$G<#DTQnK|=R@B)&o@ z!{qLyq-e##Jr1G-;MEJq=pG<9o%2z14<^G@77@$0D;8?Sk^GE-kG0}3b$?SO9Xc3; zb;mYXT10jQcYzL#fETxwT~9~mg!w119HC@J!YRGq-AZAk zI7la%m48KwgCkx78s9{U=C*hN2zNU+zgS6(5*Kwjzcr-KzqkRukl4c!@5`!Xzul#5 zi4q4ycj?9TEcVt`<^QqvC2&z!>)$X799f)EK~X_bQBgoKT);ua1oXNNqBG!vRz^5k zna;T5z#T>%e@?so+S9sqyVWgQ46{WR1vM+RO;bzDTZcPTlwL(8=KVhB_ZvX#{lE8p zKW}~HcjoNNbDr~@=RD_oz7Juua>SwPK`0`O8IBgmvpB>;-!p+H57#+7XuAPZ zBpfej^ZQ}-0e-~?QaR#o)JIRst3z2rcU(nCq`r6^au@P}@L&23b`%@KSl=NB;Yaej zxVf+$-q5_`<^n~(qb>RHTtVAP>~B*eN78I25Nw1qMXY0K#I zGstLgIO{(owqx`Lc#CqUQ1tgm<`UKPQ1lhyY;cz&@N28)e)gX zZ#W8V`ueOXsHA)*oRw=fad~kB>*1tm_OLvDJI4$~EdTOMUZ}YJ%TxKxfO90D6mX{V z^nf#wrv#kIynn!%#d`#t**rpZ?plm`@YWw`Boc)9bP?xM@}&qi#~FnKZz*?rp-E52 zX7;FYbcAM)bghBf9B0u(pl2=wEP*F?I?z4nbovSCXK*^b(~NjDDfLT!FCEHnk@zpG>k zs*Zh>0Yj&pkvO8wY7wIl$I8DZJ&!Njs(QwYgbqU>l93|$PL@#XtXv$62k{q?hIS8r zyOXY(ZJ>pbfH+k**W*y^h^UK&nuw~}aRMk7#ZcT{L`_$<)pQp^&@;COYFZ33Pz(*) zYq||_EPRBhX(j+hMGvi}gwCq=s@_4d)T&zeLC9&?TK*1g5>@pQUev8Cs^xbcP}a%z z9!)#d)~5rp-G>RQ=+Y?a(hY&uU8DBRS4NHc_Zp(1)@Pl8YQJ$B-B?H|9npY06*c?{ zk@EQl)S%E;8R*~|k`jCNbYi)U5-#*2ZHp31kN@Bk?9cD z{>R8e?&ug9vo7tB#VXG?vL||$FTho>$>A9}#??`L{_Di97J1Tn@OHU#7nY-aH3;|x zUD%J>uD`a*SzTFywz8>JezGewYd-HG|J0QwYRAGOrW>2CS$|C~?#6~_%-7@xyRpHV z!Pn%y-Pl}B{-bi2C^ktmd%J9jV&?eDR{~6N;e!~qI#|QtC<8n=f%KYh7RYr`%-(4U zHghl(n)SUrDVp7?xf&|}J(?wG?!isvXz=)@kI2`e*=UX9VL7ck%hZHg<$Jrc=QXc% zle_m|Q#87V<;6W%s;2ux@+Ntz~F@Rj%lPG@g2P_!SS*Ptu6|v4|VTXB742up+7QY*(&+%L<(S2M}|Q^LM|$sUL2%dU9jPm?DH;`Nhh4kXEp1{|>BRJ&#bcRU4pvSSPPr zGnXP>+p0F2qEZ%|vS0`bq;q3m*Om?k&Ov-cTS8dy1i5z51pS3h*Y%9QYSFcO?`%h&R4-ZRJoghmlcR{bb6kR&wIX)UP;F zhZ1r5baATgMd4B|kd$!0Nj+$HLJ*W@0^BCb6HN5@I&wo4vcI%X*c%V9A#1Ue4y4#( z(^yBJVk{5Q#nKv_cdpamf_0S+Z-m3saSC?Vy53dc)exKQp?l~8=w>IQLPZ5!RnyJ9 zCu%RM5wn%b(9xDHC~yQmD{us!i^__Xi8mnHPU?-q!XjzQ7F5g=$L3r#!8`B{RzH! zvJTjU(qx>8acMGaYZ|4=KuNjL#^x+EE3mQI3(`{flVHm2r^ETSxqe3hS|X6$8Qe=zSAp%+w-h`$dohw4pO52ySFLFy$J4qK+R{Sd5pNQ@pvF;*1#kK%2p=92wf6u9)$%+FFDAAl1{e znXKD<`yu?796@C)d^BRv;M|u%kNySC+W0EwbN6zDjTS`naxW9J5``#+5UQA(`uoO61`%*-qQ z5g3F4w&a3h6AqS#gHt22p<)mmlEVoDAB{h!rNP?H!F>iCMM9!Z;_)UUns>@Fk2jtiTUMY1{SMHh z$D0Tb&|eJTzTJdAZuNL0@bP#P@Rz@WlWM`=4eoo8rr0%plEXPN&0n0cQR7IKc2j*y z4q~*RldLGnnEE$VUU5MQrCw5~-%siFdRD<0dcYKppx)bie#UV`l0U_(Jli2IcEH1(l5-)wh=@?l^rxCPbl8ubor zI_x$jnz@9UNl()NQ5=xZ=aik-jLzs_J>sU`cKk{PB;JX^X0MyVX+ZKD z=m1+~0u2^wsHlR)$*-OvdDT$yJ0idm&`m=nY%pK}^Vw-3%Z5q97n3o<9~}eLn@9X6 za|W$|)T;4#5aP666eGL7l0OVNj`+ zo)+{V=!=jP0Qh_~KwC~XR#v}M+h8OaY3ymB5TfL=5SXleVs9bYim@?YfrG)329L5dbi;G2~ zO+2(M2VBZ?-F^`lGn<02Ea+E7@y<`SU4eP4jD$X)l~2ZQ6h>`gE649azriRB3z19g zl8s~!PrCdjPtFP9K8;leirKm+>8EHr9vaxDl;FIags6L{+MyEqE3cwcZ2-seuHDGG&2#`!J&=mBGtnxBp7#J zS0(b^pI|mixnxymgm3yR@=oA7@^%#`1i^@oZj5`15djY3KDE9_-yn78jlm($PdzwjVaRMhm2Z z*T6~9#|gsGJnoTjNhdr{XH$BhC(l3(@ayRY_brG} z?AoZAWN;H3fm650;GPC&$sV!L-kycALsH^_;CvvE!3a#MJJ#$G1sEy_Y`|tJD9+Ft zDxBf?A$>q4zejXY@6Lh~enD1?KaIZ?n5d3eDZIm^6ctn$o#~3bi(jO2)kc(Gqk-I5 zD~aM=wVIMDR4SiOd5%h<-cS_?5o!gTZZbl2eL$t^or^5_UiU>f1w%9QP$W}VEPBh= zs~bHo1Yd*8ioe8fw&^LhlE&=cO}SY4bOZ`QmfX~Tc0-m&ID8?Bhj-*kq|NATk8ssF zAH=}60Ut9KTlW-rE6addgoTfSVp`ay8Y*fi_93q|9`74%4~$ef zJf$!tIz;YbQ-;!RM_REfJ;**dunrb`w%?50osvii0S7`jRz=qb+WX_T)efto&{&LL?KdwCg9q&Lol&C?o6(og|5&&Lt3ZC1R`Ia9T=55 z%3r+Zq^S`wZT{&KRoM9T^VYWQtZqX)sh~2bXLmZS4^iW#!^Kjn>xFFm3Mw{@ zkc7WuRG@E{L&msQQ|#G&GeuVdW7@lxQaL^Z6~LjeD<`%~=#3g*xz{U5sofM*#A;j2 zF(;A8tIL5j4KZ(A9az3Q5ne*39gnV+BF3X6h-dJWwmrK8@gSnnk6Fy)jiuD02rQ+P zmB*chzfLp%2%zRF-_1wQfdr@`7(xp7=*ByG!V9NSsJcP&Qq?_5?!I22Q2az~Z~?tg z4XY3(yRK;rZkp)QZi3vx<6B}Vq)+hrh-WwDEak?dO|-2BLj~cGV=^-Z+b2sR{AMXw zdYURK{YB^LBRx$KJYEs)6IV6!6zL=}=1Kud_jtUNv+JsU!`r?Akox*cx-VgRm;EI! zKjx1YbzODp9e3l!fz=G-jHG6pvm_lH175qSr96K=&1D!qDuZUvc$b({ zaldUj6g>-91Xui2LWY9=QFE|~RCD1eAy}FZDZTngwP&Wzr==R8?IGB*dM3wU?{E#5 z3niy$xMwoJqFvYYj!tUbCP&>_8)21ptpPV{KORFD)8Jw@j+fj-ke-Ni|J$O!xfhl~ zl9vbqA}s#qd6a?Rb_#IQ@MF(EnKkr)tNaRW$?a363k3D0y(DE{PQ-e~92tK5UzQm$rY-!8L%z_L zJ*wG$zbwVG-pg@CVV`<7ctuooXtM26rh~9;y#nGfa!-b@r4cd zA|li~Y!TEuFMmO*q4SW4=qTswRB;bt^at=IT}=Sh`rTBFFt+iup}r!8N-i zif`7V`zO;qsEE(yXZtZz%-#EtJ529+O%0I+_I%-r2JE+f?5@at-nQOcvQl2wpA|=- zPKL^y&I5zWfD@EZ&*lh4;re=(r<*wqUPtR$X15~PW1pE6BVeAzEtBT`a^Hh&NZd>W z6uN%6-0(CpgxUc)#+P(<=tKA@1jx4^WPNpA$+hJm8#>!;2OhwF{rc&?DY!>>^5@e% z32IHhKKYEEf_yi1^j+6S_3d+=FOb}(P+a9J>khFWdc6ug1uF6TrCOuC%cUG+ygl3& z&mVwAEXV#)R@hF zdDh3QchqAmaN5Mc3*t8X>I;=34m`CYOy2l0%Z%KG_Ih*9nt4SInpgiZi_>j_S0(?L z0gd=jEt@`gG3t40Vat=3s+rvgbHr0A#V7IOQZ$>HxSi>#`E z>9CwaY>WYb!p10U+oJf>l}I_Q7>BTQ#JQ=MrN!2Kg5f)j%~e9Z_Q(y~;i0u^ zGe7#4{6#TK?x!4r8Y)_7_LnxR;;NN)U8p!8zhCZu2M$p+`{klL*yz|FKSs0`Xakqj zhhCrIf8LaL-N8~c<#=H24%TzFI32|~m;>tM;U{5EmUbzh4y$Scd{tH+Op$!YMRP^}3CrfG!{n(YY-~@=u|%3G=S?ut*B@@9POHR+|XZu}>0kU`{%E%|Mo*8~+N1c<4YJ)PGbjPaDYMG_Tgnwt*~J zi|&>mhle&6iQXN^#t6p+BobY+CA;S^6_}%JdQ6JW5A$`fMmxAA7B~U&F0@CVz0)e^ z4r0T5{e;_4P~X)$5?#4Ejq6&NBia@@Kc9c`gna)X)=P_&@~eYbjI(?_B9cf1n~4J4 zPImnu@H;LA@H@&jq$UQH_T_ajC6KbFyY@h*gtMxyD!d1Rfp|rUd(mS>s;}Rk8UqX! zF>JWD*Fo31#~200&3&L{r4QvvgIQ0}L5l{nWunoa3}%Cp|Io57yF1JxC70JfQgV?e z4#qhRb@KQ`wqE4%eqtbxQ0g5V7!r)^fVVs55kobpKHQ#1bSQ>T;H3;5!ZwK^d20wu z5*eNw!iLTML!~agsbmNPktsyfm#gqwK`x5n(d$4s@j*NuUxfx6-a^-5cee!=_CbOa zxO0R80swSoNjGi&9dES8owkJ8lgTMmotD{8rRVQ8~LBld0?F*d#V6+Nh@JEmCwxwRlyLY)xW)MSreMV!cy7gz!MQ z9>Q0}0=XMIi-tll)=Swv665U#n}&>yEj-pJS0}N>B5gu4TN(*!rkUGGkgEJdG8^K2 zZgpGrE8z5``qIqY3Rj_Ts4{zi)}l$wvUe&o^}^GZfH2~Bi!`xRJu{rB4Q8ZIo0P-b zpeT3{Eqzu99tI*z&~W7{oC(;YlM$ zL_Y7M{Bnd_`7FCKEg*tK@P{9kHR&up`m2pM@5^9my@?QDXG^Dmm$J~YfOofAXY+rBV=+6O z!TM_kTzzyb>!rE8L5>~E#%t=Y$P31@QGZBRt)z>lbgzy@y8J8h(XlL9{`xl7UmFV) zIR9-J;$2_@{g$v8dGV)UMb4jO=clZU&?`%bnESumQ3_h?lEpCdQc7NcTC^p3(zaZt)H z!`h$_7E(V73#qSCr3f;Q0yt8aL`+^gUYpy#oC*R30ue@>N&=mLddyLYHH{d6XS@xt zeUBrSr?f@>X&6h5q%a`8Fa%^jE)N~fri2r!D5_3R$H=9_SwBr<7kS5UHc)f2i(EgP z4RGEJ)uT8W?ejM<-%w@qf6O(-h#%^!F=RW&ExQmr=G~xp2tTR zgrmd}S1&&6p0+7`agON3@S|cLEALx=i@Ajv!*H*wIRkGpEKOxaVlE#}Wy6J|B~|4N zeMYe1nsIl_vqrGVnoS?bkI{GDD*4<9tYOsi!8ft1!qIvY>+_eO_o0D;J!y-C2kS{k z4SA>WJU0@ld z3#`*LD8$+!AKY&9*Q#r|5Akeh~FSF?z`swq;J}P<1|S%{e!3OSU12 z9u`!DrupN*Nb`P|J>yuaDC7Nc>{j9EH=d2}g;4~~OT*UAhIgtBAC}QiEG>ZaT_Ud? z&(cOvDO<8Ll3Z7{fLYIJks<{2*Vd3+_$DX)=8L-gwNzsZetb5ziH!e#O+GW8#Uzsi zP1q`qu7OS)xCBE&(>oryn7%P=uhtxrqJ&`3ZvtC0k_sxD-X&2pKk2gThgQIzk!1o< zN{OiMez}7PuUW2bn zlXpx(w1a+}q6Vl9yB#R~n8oV4Mw7&)rn#>zO~Z0bI_R9m$%iS9Y)zZ8So{#`Bb<s zopa2i>%KWzqcj3A^rx!cXGzd+g} zP`W8-m!X2@I9{P7j1ook%fFeW_M+O?WycH+LmWF!EINcgeKQ**@>qm(62wWOI1bZw zan7NfbkPHI8y5joFF7oqrxuCz-@cq;63GkVEZe{!T`!=6AiP{FL}uAE38p#e%0%uH zpvfA@aWvW%?s!CAH3{h8>fD`&Zq1l+e)ViEuID1_xq*gMO=3AcNn*9>7lB9g;CaYG z9q6=0a-VG0C%PNFXjSgh9#oqx=Vh~Ak(VLjsYCHpw!9{r-O_6>Z56Ox!&{O#_k(I? zRg8a?&cfsGkS}Dj5jj5Qnp%iU7a?`F;@;FkuoPfQz)% zir^zJ&0&vgvz{?j>T}tWV73OEa|=K4h!%qUnBa=W)cq0z*6)c|h8otgSNh^;^+dP4sqrDi%$P4Mq%L`f55PYGd#h3ee|1 z+@SzsmUKDIKiPw~ZlCnZ=BX^EYoh0(sq3mUB~^9lo_BOoJm0oToHF%?Rrj zUkzlw{SnGXH)=;ALW%ylZWmBI%Po03vWRC)sF=vH0@Pawl$ zF>N=)(TtHzvswBuFJv8Yj_9{{rM(@{&7Q_^DYlD{3|o@E_3Fl>u|Tk>*d*_q4fXXP zw_H7&MGuAfKB%#-X#OI~m*ye<W-PUfotRUtK2O zIfo4jzXz%S2=pO*noE9a4tnJKaQWyQ)-U1J!52-Nq4lY+A-c)$qRGnd7Ni~9#u)R@1@yXLYi=MFGUsQ;f*K94D%N5m(f;)8w^ zD{~R9L%IfLBQsK*!*JEnwO&s@Q4A0d=b+1c$*$(#ulFYF7GJchxdockoQv%Vq%2le z+7e`!9VIMEc3D_A?a<0r`Ed*DqowNydo65KpEgD&kWajBj@JO` zo4mmag}3uCy6#ykv~X5()mzz1njJ3r-U2pJ^W}Q^?E;pXyAG^NnQYPpE5AV7eUk2k zVOSZV@94SxALvHBr<^qf=QlCa2zeCG1k;TOBhD+9PvIYIl!q3wep(oI%QFkvxGZ}4 zv0(vtA~x2XM8pcPK}Gdhs@i{VCi7BzmyFfZaf5O76f7x4eCI->(DUIdhfTzOk;tQFjM0sZ35jll}@*qg0#(tNf?3(AmRoX`4oyYhyZ zX?NzFPbDLnvJzza^?a6~HNz3SfKAaZfMfasHZblPr^57bjDrH7FF-Z@(F@4A0J-@W zU~jS*GjPg67GFXNY_Y=HM}jG=#;B3irahKai0 z8tGfCa-lH!UxMtRz*L|e0~AktFpN?+9-J#5UC3gz|9GQS_AO)syA!^lV|~WL2zzWs z9HAT9qxcth$b*Vl{~1G|!^nSLARuZSm_goY_^FT!?d`&zlS2yCllt=v?^rfGVwmB%f@7Mvn1T*P9#H)G4Mm|)m* zz##pOix43<$oDT|19K9^5a9+@*K8E=5lDZiSc0~$p)TmmuT2U}2LxLAO7dQWp6I}# zcMp-TECS1)e2<*G7+4=YQ5-m&&crXG%h5ttrYXptOlDXu{*flW7293-LTZR3NXk&h zJM+Oh4k9k`Xu+a+x|IYW<>XQ~gibE0lLWe?(Gg+hnM9JJ8x^vo12};KDqZg4nh<0k zDlLr2_m8Hv@3pluU(9aPRNO4jxP|ptG+-Txu+120zu|QlrseZvE73nN^@Vg0ZE9WF zCF!zPXR^?XRXbl5bSWAP9H{7c(Ob_i0nrf@oogVu+Q8_xIkl>HEK<`hE z-nX)zU8k>8=vI)A5;ON!);ARqjQ+0*T|dKy3kZtL@f}^R+!ai>%l&JWG-Ad{2h;Q= zAn^&evF=Vdj9h2=QxxOC%T{TlC>Bs4KKlf8N?O#;=#P#k+sdt1k6MaK*fH4ckxr_V z!3r5y|I(_+o+u9_2V*n542C`6i_WY7Rq$zpFq%NN9DxW>>3C;wzVjzqs~QQxi421? zcF}o(V+dsf02gcm@hq&aGjc1->j<@bRIYli?D&4;)p{% z;Bvo!x|-KH=81zBn43wTVdO?`4W8RQz5Mi74_vn!#PoHNEcWTM|fxA zovvIQJYPxr)t6|o{8^156+7GV@oBxw{bi*WgJ2~&gS&PTyZ@c;$cU9Cz*GW#PZr>Q zhY2bP6UG40)2IYiRJ23m!FIqIOXrc9jp*mvjD<0)wNg&ZI7|uWsy;jtMcTUi3Xt{y z4-!8JOaH|LoNBFrbejj(7o|XTf#;L1v};=t{OEX0g;d|zEO;fGYFG7hmB#q(-9`EQ zEd(kS1HB6=w`iMLsx2autm%_mp)-W#p2ia_r@X-WI7!n-D!#UpcdkYKVECgNNvlT? z${8wJCK`?vF^78`O(4DqPiZ4s8;(|xF?u@oDCm$F9)milvi%1G6v~T5n4rARc{d+= zWCnL10!WYyf|TZ-$aqsw-$D1IMR|nXjv~Dahcy0E&`wIF8Kx_(k^eGaF*?S4|5zLnIEQrHk2Ja3{y$hUzrP zyV@^o@`cAMZBqd=!Qg%hKdFHv_z}+aT;~aCs?K#qzfp@f>AVY@@-2BZ^fp-aw&%al zC>?blH&lFx)FRj2l+AI&mVe?4gC4`?zro?vjWyL8Hvb(?sSY6+oMSn&M5yZCKj*_b5Cr1A{`T$ehX`lI{l z6{zmY$e3zY-vnlWs{-Wf_yu6esUY?|nEQ&}p=nAzixgp~v_tP*jt`c;F) z9P5Q3Q}gO>Jd?5$SBt-k7um&Pn$;KjwBTcP*`OQYQfpSNB1P_8238-C&|-GZ$!&YE z5;rReSB&Grtl-MD`yrEsEXV=4B-6|_>ro1npN0woF!M(B_D*GFV&JZ}PnMguKxO$9 z_F2+J)OKEd$n_QjFW-r@LPs&7KIF_9P)!|!+;kanhmj$=5oPcPAm?DT;=rS@ha$t9 z0Xb1-8{MKMKyxuopegwILD&qa3=;T#;=0xEQoZ+f=jk>}gPIi`2Cux4DjbkTq3p#u z{0w3wF&I@NARay7xHw(h4VlI7F#&_Y;QkINO3J96?hWu2gOtZ{t_;=`<#9tTN>$Cp zJmHgZ=x_CCd4dxb$|R?mpu?p3<)v8OiGH)1M;>fu$KRuFMGbC-IN(@KC`4JT#0>og zKdIwk=dqh=#mVHeLz#vOn&VXN(Iui!Ny9SKzZ+%YG8i#Z(F`?lh>{ppWu)`*Xnk6t z7+iN32~qXF)$|Byrcxg72H~--Y4dD&>HO~YA`XfoI+VcAqq}@BVCoI0n+f>g^m{&_ zF2p(+1rw{Fc(a99!2_j7ZlwtRE!u_%d>{t2ZHIf`tBjPg)z0V>*lN2PJ0LnkY7@kH zp3nIgqA!dEw+Cn+mkNNIoaWESiKiyWA8uh?g+a_`TiD3h6E2k+6K-t>H)pzQ!E zPxxPA?_+eGZ&DEKfX&9S?gSmGEjDz`PSD}+IB+B*zR^d;cTV$f2IJXc!^)${#fAe_ zLB$5kRXC1l2NFi4t15V4D=6hRLt&r@s;L}y!SDmpc8!by7#j-z7M@=XLYei5HfJDJ z@5ZuY-Nt7rZecPUPH44|zF?#~{&}j9WgxZWH$y=ak{>~apCQA~`Xa+xWY|@&6tmzQ z+~eX=(mr=m&E1U$c1vv)@p`u1qb^30&1@T28d0rN}U`zE8hFF;-kK1U$~l|ts6 zM5W2Err2OTzIQQ_78~{-N6AK?TI-7l{TS}A@$YAhV6?ebH2&OghLl?P{R+Rx#lo+k zvDmQuho4d1pZ7Y^X-P-*{&T@W&hVot^mSbgGTiq%>Z^rWk<)dqth~Amu@L<%%0pfS zak~VG6@G;>18zUuwc0A>f0Xsua+HPds3s-ur|ADMHnmv7zuJQXfE1gK7$!5c4d)a2K@V z3-T|-0%0m{*edM@+f8SFon1$hkAuiaDe~8!(D22K+m`adJX;O(1?;3Yj)PCbZ zv>f%E^AYNDEWX&V@sJ7z>G~Q|A-vkvjGe?q&>eN2R}VtG{B?N^O&=tmfTN)Z zeu7nCw5#?sO`>}4uhJsN>v$E0WIroU%TPEbOz<+bRp2qgUJ2ftB6JN!qyB zADNgFxT*~cu3=d_O0bu(|GygK*eT6PFqVIZE-j6bGNc$F#@$U~>dBvpXw>er9zx|$V@NRuUmYqxMFai;+_r6X1D)^o9zp5{3zL9`j-U-k(8w4GZCktk6Fg zI&|I2<1~iHm97r25d?kcCcEkvdsX_JtqA*c&Py zM}8>DX~(QQuwSdP?FsZ6oal+`{8nn=cSfH|J&2Atj*h4i9pNja8n;(gkeYNb9F){m zPeio0mr{;Bl_Co4*Y+8-y7vmF9>lOZ3Y>|!Q9Cn26Ct(;Kcfd7BLM-K9L!Y{gOASj zZ{~?g;GOS)DRy=OuGz)bqBR@ADKWhbu1wIXMLG&rUS1u9iC_NjGOEXSW!8PP68Q#= z3(~as4fp-(#6Sijs!_^o%CN{=_aVU__`md9`Oma2fY<)NuLI~oT4Fy(9}RS>oc}Ot zhukIhgk|;bp%nu>q#3IP7KbSO{r|W)IF;1_lm7}v@iJDNyN`pG zFqbbJMjatD6ky$-^AWz_SJq1iJCCsMu<&6aGEzGZBK!y{cv-s$zT{Jbf3>dGAm_{? zsxAI~3Lla;uwYSs2dWf;+Z*FVCo9rIg%ML_Z%_Od6Gwwzf1nCvC$ga||BzLCPIcG; zw&%2m_JcRl7tv<{$Qk@sth!ZbJ*~HvYLT#^g0hIA%3KM;rE0{UqRd>q8Q+H65lo)+ z19hotm?J!vpUVbi{Lnt5{wVN3-uY|4%YHuyHK=Sw{Os~W3O(IT;$&0GpH)S%in{s7`|`5$Fk^q@jW(*zDe)1 z-u+PVIydc#w6$S}9Iq3e@N)Ro0x$U7sg(N=?T+`M*v9wm_wn?)Lnr^D{?7b>-A?|` zf56_NZ^4Ib1$~cv$a)V%7GXaTu5s8QJ8mUh!tZYmp#<_0GQGg{eX5FO#hktcy}Li+ zOipt#-T?#jwD#m(>`?6yS+@_EGpoYnnfur_9gsTw@;Cd~Jf{TLyjp&j_?%2mmrY2chlg+98U4r`A|?bPf}01edggRwHbs0? z(NN^!;d5*n`Gf<@Y zuZ!h9``K(QE@#M*2LSX#j*_?f&c>+~*M3^GaIT33d~+V&^%wm-O|oin98;Be;XFY?0ZLPN&f-X&lEnWUb81v1h4~ zPgnVBOrsjf`Bas!Qn4O*BUB##7>gaa8fOa9!l>%+HCu9oX#klP6`+Fp$JzL$E|^#p zN}qW1g0}`w6k7em1U|q!=YbG|o<}@jeIt$(O->vYL2XK#4CxcAcWqmmPc4*aHa~~c zD{;bQaQ^@zvS}At0bl|((Y(3x-A}-r@}GU>ho4{rLpSyH`|qsEl+QiEdUP5;))AL$ z;kWdazkLG0SFg;G?fJkDeCYw3@toysP>L${Qol zOYJ&3mzZZTHfo_IHIhc2F3-KU7#u6jpD*6OfL;xVx_+hRX8uB%Yw_V6cYK~a{b@GLNu$_)<9X;@Rm+1HFv+wc z>9$t?&P>q`{hF{+b z{S)pEBH-_omwe@E*3bA0e8oGTA)KC&FO>DSvo!6NQam$#JKNygR!V2g?T106%CuPN ztrizb!e$q%hyGtknxtJaK=Vh=1sxNUAezX{#5Silip+K2Y zQmGiX-zEnuhVJuWKBFGX&JGF;vbd-j2TdN|jDEs=U1J<~9+g?yNz#c*P$KzI3Hyr{ zS77DnrEF^Io_j%hw@bMjFjz&nB=0DcHdP=Ee#X$xZe~sc?tfxTD3o)%t@AvbzWXpjVE)jx2IG70r()X#0Ao|*w}5N({8Ub`4qK&CE(5#$TqX|bjAi)k%Tgf7GFgO%OoXO^*Lag$dHP3oews`-KHl{YSi zJu%H_8E;d>YwnpX`|riYg?DDl{a3JCI=@swSVFP0Jq0?xbcX!k3f4mte!Kkg3WjGi zXT$I)kA#wtd>TD|{}OrKN>;5|V5uC3*Or5|w*R!sFFV*sZ5kZs9KZoAn^W0y6wWIbA@-S$(fyamVMTKl{(`NL8+O4|oMmrB{*KbA8cJ@(&~^W0r>sFR)0-sEeo z{Laa~4c3|)TIK!axc5ipw3g%6!Zz65Z}}_B)XspT{;zCn?<$<)fW=9zUiShlj;J>w z?f`cW{k6=+9@QFtY?bvD?4&mB2lQYCY^G{2w91z&*p%V>F*H75`~t%Y-`tY->32lQ z+w{?wys8YGmG5@r=1l8&GM5ndJkO3*7^fmJ(0P)= z5d-7Sq(7+o#3?>eCjAlBCtUG~G3k%0K37JGRIw)g=c><-ich>re?s;7Lh(s3=})UZ zwTe%=N#EpDz1~o~GEMr6s?W2EPnJpFtonErpKO!9MfF*)_)IbBgH|ZbSgQC;H|ce% zkCl7^Q_R&!HW&iS69(p8b`Qkm|Hl>XUt42}8`bo`H~|2he`CiJBOQX`2qi@+x^q2pXnu0Zswb= z%RkeMex?z?Y2UK;1X+QuhWFfd@9aQV!+U(Y_mn_qI^jR~y8NdHIus$1A|xuj4rcy@ z`<(^8l+UG`RNDBfZ~$^~EgWE74dUg$xlK}6pJA&eGj)S8Gs78?oVrDGbJ^G+N29NQ zrpa+p`fY1&@Rktos^GHmK~Bd7-+8IDMUcBK3eSv5E|LoXf8%JGD3x9WVpWLZ1!PGA z{Sd)8B$`l!u8;NEFAC~B$q0B(1Tvmv<+Cd`#?+*7*x^zx3-XC zh`&F=@At3Vz~9D~q=9D7qzy0~c#o2L-lFPsT>Ta9WyiC#CcTh#z`fQ{N8n&9i{g`N(sR`(djw8kk!++%->7=sM3HXf7F8B2SOttOGF*Z~^vG}tj$x1h zMW%8JLXQHbn9XeQI4<&#L`c~=E!fRe!HA`DP28m>GgRD<7)a_F589MO zd|PM14~|r~u?akh0ybd~-n-!)-sBW~f|+ebEA}Qt*5DvdiXah^YDO+9wO7EqeV&ap zBO^7$CPIi#2U6_|ux`3$T=e6$zZU-8a4NnzkDnw*&PBYdiR%OxRT_1sZny_TdS^S4 z(z~I!$v6XO3t2#4B`OM)TxDn-M8iL&B2dmo`1WMmYgaazyN#p_w-$^tA>Tb9NWy!^x@s8~IVSfK)VQPQP(F7W8 zv%rdi#e@qecd(q^^)@PC1yvAi9sjNXwZp28hs{TL$`@li3v8kDKq{WO7kzpJNSAG) zi(FMl(~e)hEM4{L-i0AW3*hW5G%#oPp$i=?;U~xhyzYwAtJ~wMilsAm>?2`mXXg75 zLj)s8PbI6#5rY`kjt;>9w9p@=7{o{$DfF)*6hn-m&vM;$F&OnSxJ&#rZp@ZmI| z^`@H%W}6Zlt=|N1l!?l3+9C4k8^~vwn&5B3J3in&*QxryME)gOBgR=B29p{>Y{Fd9 zDG-9iuwQ#AG@H$~zG9hagmkQ?DG=UDDX3P{2h~ZBCn_*X!0zI`iH`WH$*k3r)Z7}U6tR!!42{|LkDf*EHz7^|=|D7GEEZ0Mb z@xMbYJd>CHJv{x@~7tV2ZWQF$lbz8GlQ>#)3rY_z>bCGtZN_D0{g=cY0u}IQL+Rv zA$%?E4{~Y}9F&=zJPS#Ey>OBD7L-?ri$o3xRiCxOE}suX5Wontd;@Jk^H1%@7SN_P2z zv0@Lr-Zu=scD?K0!Hyr@$59=JV!yh`eQLhJy#m8UIT<8pPDI*qo4MFCXJTuCZ@W^1 zm;Qqq^lyO4m|rM-7dBv#Qk;3jk&ct4cXe?_7kesVS~tDKfSP=d_>- zpQcrba3HdKa-(2GMK7uo`ze%;rk(OwBJ%SA!1?lPos4`hNn}sC33Z}Nt!qIcJ_GaIAQjNN7l{eA?+F{VhQk~ai z(Zo^=@@!dVY37p4Cwt@IU*81awQ3DmTb`A>)5vJe(WzfC4Wv3CPj0|4L4iJp z-}xk@nNyyp*T92BB7^%Z3Igu55=F{gOs0ziHONsPj0}P7IZDZp^(!i9pqqPbx#2Mo zK325F}G&vP)%T;Nz>ieDuLRzY>40txXq_1SGIf^U=2VMM;mXX_Y>n3dXGScwl$#l#R|RCNVN^rlc{2$ zLWtgd7O$HkgVfF3{TPjr6WuXd`V@f4H&W&w2y+8;V+t?p4?@z-2hAvLI2%kBpLAE{ z72rMNc4b=&>c0rIfcUne$=)GGt~evuP_doN?LI8P(UEklSQ=Yw8sGLr!8!~Iz{w4G zp8&5V&=UpUNJoL(dE9k482FNlK3v=}RV>{XjxCndRFNH0so=zDW`Eiw1DYx)q6AmL z>lgFq3La1fK+T-F;bTuxYEuxBBG?9poJn^<5U0mz{Yz4hd zhjF6C1Ur>ZW?P|!cT-A=vUP$VPAcXPCNrr05yVom)YW0xU}bgaV3~g0dUG1EfY;2- zN5iLhbz?}mPjdCKYj=JWfkPbs$YAAFsdpX)K^z6gISyPd+|Qo@#fnaQ5P^8jDhww8 zE9nGaeXyx@|mz0#Z;8!7Rfj0uFfzFb=E}EI>!WS@E6ynqYCKzpko*!_dc2z{Uo-Ej zgif_B%I7an0R4*vqO)ySK7R^k6!gm;ZOh8Hg$7cbgPaS`p0=>I_YEw3%zRNg35NoF zNJun+QyJ^=z_h!OafpQ+@V^=8wiEo>3_tA3r9IQZ`FR)EnifjamPk#PPbzG}wkEK8 zg71sv6W1W=%}WrzzF%mTFD<}MR?guQ9#L36U3j=kS)t0ETn3$IHS?_2NueECg#cmO zoIsTBq}PW9v4iruP_>Q%Ice$~FTi{|$XB1J!i_B$;!Vkc-4H2mUZTFEoWm{!=^PqQ zdYkx5RD2+vCDu%8WrS@+tb;RC|r3M-w(_VrQ zsQz0)%Gub0oZC5;N+4Uu2m*286QGa((Bk=D(?_^#FeXt}!{;thO*ZKuP_M81H zAt)&;-AXg>gbNFbpfto~(10MNY39J(#umoprury&&!%Y$9DOObUQ&-sHNlQ<7M_Ou ziI^Ey9#0QS9oj)uqMf8P4Wbg222n{&nU11TTLT=Sp3nwdP4joCnejoV+K`fRgOHJW zWK3FYH>$EkkBrH>2Hd*|%9ymk?xR{y)Og%FloXg7fvS7S^Vk)telO@UEc`i)mT0qm zCUO=W&G!a&fG?y~p#(<;GLHQ5|&`FygP;1iHnsy4f3b(dp&{@eKV-bn&e>#=- z$1BUeI+Oo5OlK@MQD8c0_ylO`1@J#2s>L@loj>nE!E|oz(0`xZuU0yY8Z`bt9AKY?F(g z!8ntY3AyNDIRBr-qJ{t0ViBIfh;Ns5NG=k0U==P8T|R`OQF;6?KafzA+#ZpHq7D)N zXOV~)QdO~OO9@0aJi~~)lg&{pegSuYNq{q8)RhOm7>BJCBqk`HNm7$OF~evZvyUEo zk1sz&#t(UI3lLppPBl4N?1^LsVec=kHhX7c&A{VYq?K^|$G_T~^y{%!{u{(r!urGh zIIDx4CU)BqR;LiHM*dGy)k?2IWcD7{Nl-^_f+9;%B;aHVTFz>nwi+Nzo?yI4`!BJi zT_+1l;sqch=@+2D=6nQpP_8Pc*<0EKb!%GHE3dr5`@j$P9Mg__w$PgFy{`B~ zlBytm!npyG6|SQ~{xG;n-MKRpBAnmRh)hW?<4{YGFo&zuGa;6d0D^qd)qGxP&u0|! z0cnVmx==|GBKvg2DD-u!&uJnBit@NMP9UJUPorXk9MQ^KM@}INY>QQ-ULX*mZr)AA zur7bo`_h0W3qJ+5m?GlYKUTy$`+@RS67B3CbQJ6CujBr<7Nv6}S0XxaF)vbP1*6;MK!fk!f z-gkIAXySa^yrQIII9&E=;WAX}!l0g75m{`iTg?DMX{c<%-Onb7ALJcTY|3d_9U}az zoIr7M{Ss^k^Sne)b~V}e87dEA2#b=c=lkfHRdH36Oo$k2gdP+y5b8{dqK9y-84lQF zen<7Cd%2EYd>d4hs$J)ZKz$o(MeFD-)NgV3kKX>>xTTd|oN3BXU*A(1Mb~OasbEqX*q9wizqFh6z7CPrJ;`&X>Z)lzO;Wrv%a8_2i z?lJ}gQVNRG4!uy1=bQ|cmy(*w@Bj`012hIMEME@{pdegX98&~{XAk}`rl{KVbfEew zLp5G-!|y+&aYJX}-J)fIXa>mmh=;q$y^0n#oaiOHx3ZC%jLq_UTUp%H;6OAyCA+WH z@0*DOumH%j0r%PYt++S=h5!tNw4`gmZs;pu1h}=f6)fu_hWs5Z&=#iDw#xk_Had>X zlqQf|;D~{0xf{RtWI(IjV_&Q6kk}$kWW4;P#0D4$A+HRW>8(&$zWMJ~nQa5;3q0lF z+hEpnXHPkQ8#@p)<{mMt!fO;j38+YjQu^H^r+R^Z@~K@e^0Lgq6TWKel#y^sK|(t- z&ff|}d~fI@@pvBN58N$Rds&QT)!p(bFU!~17RaghvA%=vct5ajl5*n1a}t35Api{U z++M7>M2c(Y=V9=;>ON-a90~1=+Wl|7*D8N~A2W5{4}FK~w-kN@?q`La7q=&G|T>vH5YSn!kA`_)fF*HQ;Oa8wY&kx&o~|kNUh--u(cJ zH(Bl#ydB)AM=^lDdO2XqY2lxsQW#(Y?gMj%YR$s)kj;P^WI+fKZ}KCbwaUf^Vas)H ztvuvGHYjW~CYnWR=AD>a_#jJ(gDt%}U9+*zL9cmp-d7+X%zTo(<3ZMQWEhS%T=7`c zB4BG~#19P7SCBQiUflkT@MukSfN%RATUbQ%r18e%t@2L~0(-B&ocs{W4n8B_`w*__ zwiAs&KgY??F~}$lJwf;|OT`=Iv`d|=T)vjDjC@PMQg{a|Bx?YBQIMVj0DQ^d2G#ia zkBD6#L>HYK>S-awP3q9CIiE*l`NCYyTDWB6!z^5@|Gri3^RNnqoAog3DQFo;k3)j& zonbV1SuS~)4eYuaiy@w=#%k0BsJChSn?>@o53BgN`yOV|g4n-$7>}A0)rUU925JFE zEDwK#_09KVeZ{?@{qb~Oa#H{!F!Lxnu47}aYfBnm@I7@#4607Cs+&Vx=O)~F2w3g{ zy>#azm@)lJKV2`JVMd;Ss-MZ@Me!n@n_#codQT`pNGgxpG8DUdGJ{_R5<<--#T*?_k@7WBX1vksOsLcQSo&I@|#M7yQ;&B0i?NRbBae?cs}GYn9LZ z4G6Pye-<1J95lth+Gw6_&J(f*9)$y3-I2~T`{F^gk$l;~R=M8`EVCe`J(uJE#DqBL z2LKVUqhZ~-1oqbc=vdHs+fjZ(`!?P+E*oabK^7~nx{uldr$72BL`yu zY=U;n$}qXOnkD9=mkEA|jynji-LVduE)j&pC_O#s(7R;I4q%_nhKS z?grWhhOk2W(Eu-*4MGgx!;VX!HJOwV$fegGFl!VfCDAhcve^l?5=v3cxwh= z=l_NaeFY4y(foa=;Du2+X}_QS{dbwwvjL&Bm-!i%mqY%^Zq{U9lkfT`8_?|~S`mAM zV-(3MJe;3zY?Yt?CmS}bAN+((csOqYn2(CFmPgiq7RWf=cr%t*ZEZL-Q zcmck6rSJ@{KK_P7A5JV2i%6{(S0ee-@R|MO$;<@%bSWEmNy{@6Ao+xxOo_o|sJEGkYV0hIjE>e)I2afCbmJ_JHPseEm6tr?2-vV77}_(p(!7 z7+&&=ID2Qgm+k8VrJb|Fmn0pmsrZsGQ`E*Kg9iVx^hVrm%pmu ziHoo`aXa7$LCQEplSll6_0B#BA0a##pKro_hgvC;K4dW)&W~XPl;3GpSg8qAY^Mmt zN{r>W6-KWDpI(08A1peWLj2KZX*ScdEGc41koW$B#dc~7lE3{2bH+qKj#GK_8-F7b zqPtYnF`lbl209rnHBY?E{*p#3XCVe=0SCOx84Zj32&6Tc4&-vj} zmT7(wPK%VzSHk+c{8iR-Om2tN_Sda^K9)=Z`hQ;$4L0%D+Y>(sr~00ZeC$;)h+vpY zUwoDI@0QWp>JKPu0xxFle2pc=lsAz)(In-ZE|eC=yH9QG2`0X*sZ~Dz8e60V=%JkZ zIx7>1C5K)IFy6mIvJl?#@?9(~ZXzz1 z2*8~`K%C4%7FE005RrM~F4imO#AVdK8JIu|&s(^s3u-3Z`Mi|egr9SebQQvY`8J?Ua~2+&ciAG-4kgA3O1(?C3o&~Nad*A|;3bLC zRq(^jRs2p&iC4n^1NY0R$l$4WAuSz+q>Zs_Z3%Txtm?$QfQH)Ej!?+tW#iV^)hQ@>tLda!Ixh65W z_J6(iUJ=db`}zF-pT}p8$J=wg*KM!Md#%0pI%lsc3*#+C_rVLRBk`v1@mzWt^*Ft? zOE23eAn?SIV%(Y@dEpJ&h7=iR2;fy_Q)4aMN9bYm5yrZyaU?RHb3`#jzYXrxDd&$U zoCSF$gw3KXx+RPZ<}p7N-N1LMc;S+c3p7a|TrY#ph_K9-UTT=sH#@dhkq08U)0rZ|`t!!`nX-vFWn}6&kj*!8t5R;82D<1Jht4okCUf9HD(n;xc z&Q~c*PAKZ?PeS^XH79VQnyoe6rJBk6P8Dr@Xcib;7Py$EUHtWFc|&Sw=?U+^iXP;( z6w_|UKr0-SJ8H3YVi8*);Hd^bjKjVPW?i?kz%3N>-=tpP@@VtrQ)z7u+$ad93Mgb&riM9*Fk;=v$}K*ABiZM!AYH7TLHv1{azrl!2S5 zG~Npy)0~^dx>&!sFiXlq+=nAmeT5?qKjeQhwm#&i<8RVSER4K zs5qt9YJnq67&ok5($PJN1^4N7*iw^D`c}W;hCt?hAWyMQ+9UD4tk^DXo4mfP=&UCU zPb?TZ6i59mIH^A(af&`JsGs;a5$8xh?j&cbH*!ul>~kdM(vP4lXwT!yq`uKcF(%S! z)%ZIjQb=qO?R^e6!mRFYdF;`YNr`Z$<2ZtFU8zZ=4yGEJ%O&EN1QUAi9RE#kiQ`u% znF!DD%6lwENG#$$0C(9s z|N4w0d)@?N`O7nosCZwVO!CdUP1>4AU`_dNT_ngk$f5Xj3YHJ`QM&# zglXJdk${>2eTcrhG3(mYo(G5Kr@=Khyw94Vz{8vXCjIFe7gu$7{-BaRg4lh01E5Ud?l zh=4NumuDOyhmQvwVT~jo_(H>|IVTn1Uz}%V~s5E?FXHcvQ*j||9?K# z=x`)`QuvHE<@{>yc#kn`t4mBDp9co0itvYEG z_rT2uo|tS*tpCI^yZV7fn3m82^S?onOujBdL8oAIFAO_V54Qz0Yo7{D0B6 z@|i|Bl0-I;s%IMEm>`@a1cP8@t>yd4Em>2Q$R_c<(X>@6Ry_iput@WdRCSD>`mkx`ccsvxW!nhlGJT~(;8?6r^?|+IAQ5u9)3h6|9JROCyMyx;YW?5q_dCY`kA6f=}1DVEWXF2 zgO5@U9Kos6?4YA}H>3u3rK66lKRfEEHO#p31f&1!sH3asgvxkYYL2_we|6Fk&HU`7 zBLUt2U)&DRULTJ-VmE;Q_@pB;pwqC+s^{RTB)j@=?h*fwCmjj@=}||{|MsXOv^hKJ zs2)bkr}K$8Xw>b0d$^HMc~H`))EDq0yici5Kw~4MR-82Y{`Nehb(F}Qy1nu|qhNW6 z)`|p+=O7qEM^e!2x?=iSF4oF-1lRxMKqD*sI8Q#%sKJ-#8OgU`8v6h41f#xqK9asZ z!X^(bf3ee}z-zep&H~R6*f!F)qIxdfYK2kt480Jn%7E zIihu@Xh{^U{2ijv=VSOggx90+LJH5X7EbA(<|AC+8{rw1;Y$bWic|Z&!q4^dKEN;T8NS4lLm z0P$X3MZ9u~r>l%77e5CTP!yZ{)5Y!=D?uDsEi5R+FLaE^a5ecj@I0?L)$bMdZ!5bj z7ddW2j)?=Q4Bt`SRzs1uM#}qRG-|iG6ex9xd*y}k6}6N#z<0z#&rEDvw6qXJ?S)A%B8vs`|;Z`K<{4t>wC5)kjPhVFyhIGxP zgENYKS$Z8$`D7HkOS-OjKlG0UUx)l|U9K!h9$Xn2!*F0#@rExm++VDaGRP~Alrr33 zT<$^{XBPWa&2SYmT=p@;c*;;U90eTmWqw7)3Fz>T1$m5gBP-JhMV^!}ff6>76Mh!4 zGlia{(DL#x=K<=ndK4}VtNhAv{6M1E9R>a@p9gt}*USg`?5~U`H6P7CG#oU4yswyS zp6FKSUGyGNv0?RT3?$<^-@{X$9@bM%ew0%X%PDbu$`u)%+AC zOs0g%NcgcAXr%3;AZdD!MG(GfF79==N))=sB~TRYXrvw{MR{Co_GJ_eMe(61U8D;L zY-vD~!axcO9fL44gyjT}OQb*URY&$;|hnH zk!u_#r^6nr;o9^Be(vN7M?6~2(8n%>ZbKY5Gs1)`3^w6PA)EluMvztT42JXxHQ|PU z)8MxZvK^8IIS085DT62{n{b^W{U9SDQIJKDRLDU{Hslth98zzJ3D*K*2XTdjLSi6V z$TEnq4*zY3{0un-DS$kMe1IrXnAVWakTev^9UKD*g~UTvK{i7&Am2A+sULkR6aikZedk zufAyFZRIQgX`vkUq+RoU%0>ogvac3dQHt ztvbq;bLe&M&p(s&43vZJ8YoKv=Rw?T13hR}HHOy0V z*A&_mzv~LMB13JWbh=STnG$(dVJ~#%0=ZE9qt6sh!$oo7_&=2E&UNFehIxdC#Dw^V zhRoEsM1%*z9T7g#KPtq3VyK229;(qqR`YflPI->R2m10SEH zekMwlE2mUKIA;^(Pk+>r+!Or+RsNx&5dr>mO^);YGAt^}e}*btGhG#`2@i@1ro8M2 ze#%R~ihEs^v}w}y1a`sxCQ83SoU(6ig(S&vmq7MI@?3Dp5%#mGe`qS1f(tG z2M9F{{hE`AleMJZEYTU~l(>xXB_qY5Z;EB zOa9GKDxw+;1f4+*$b*x>9$*mIq#fEHt;D&(V=_1xoC5X-X-xD2!@w3G6>caukP&IGrD)E)al8W|_S*i5SW z^{;*l{PGcO29kwagNr~1@N3W$q)`(HT7k1c8aeC0)?g+`b51U}7!-=&NQ9#tBtufP zG*mVQ%|NnXJFq3_4$>^=3&w+SAQ@l+=pPsurHPJKP4W*53B_C-5u=KZjf{+liqViA za+TpB;i}kZSY)M+h=R(0ntw>BbQ?iHXq3i3aE2-%B0O9ZAgA_^3W^QWgvY2tL!x6; zF%c1}(1>tY<46C9*cesBBvoKUn12Y2NV;Fh1(Mv8LP9mFKuvT&R7hkDCTa=~Luvhk zG~C1}A!3RqTooA-so~f~ag$N`Ph@4U>c`54N<>CQ1W|>#s%fG$lTbO-w@MmB(vTf; z0Z|cXpFqvDkN^!T5>BHCWr_(A!lm1qsToRg)s3!u(^{ZAq%VCJs#(%}H*l0nv8o2GVi;Fin+K zcS7KtST0zx9Fk7J@DgiuvPq$Y~p+e5-*a5s&Oq@E86pN8cI?)|L) zsTnK%qBR<7K@Qne-hH`{XqCU}W7A;Tmit&0jU@}k4cEj>kBFKAJDE;X4bz1w)VNY7 zRYs^<$?ym?`J_pxV05HE>H$3k3Pw%CV64(LKA#xS z^Mc& z%rltwas9B6=xDNc*m8(e@{fJNOzWfmdHx(tRp=f}cv6E#&xnrE&|UE3V5MtF79kfP zA|N1^X3&q}vElyH1UC7pVlbTjgQOV@5n@9zr^)kLlz%t|Yjg|^8!7?DK1Ny;8-vUs zVnV_+h>shKCN4%Uk2FPu1C*a_Pza6emP{^ z(zaE@`t^*B>ee+hG@y?{{?w`ZLDrv=r5rhA+)8NQs#%l9jT)LYFg2}TzrIqbG{L8y z{4th3NYpLyLJ8aOd@U0?$-8nKWVs zRHqcL-NsD?q!=y@LuycOjFgaQjdL_5A`kgt)TP?vD^}YdA8v;a8|5j_6V3tBotSyJ z#RZ@_LZ)drpQsuCDoEglVGi?;)>KRM_fN=(hO*1eQKg@7my+rfI}uY{b^kB>ilVzj zghl#C37X+CQ6b?$XcM#q>ftZ97IaUS!TymSL$O};z{4sw8!8`8_u~OI8KNRTh!zqF z*$+vBBq4s}yif75oK|Wf-f>7X6LBCayIF8RRI@qdRy+->mVZhRIiFMdK~#`JWRymk z;bW8jq^ZFnJzA5}dbg;%ypS#dX(3$>>Vuq}ywX>KwD2|u>wqed9x<#zdW5kBX=QH@ z)&JCY2it?0UHpaHlaGz2q2 zBQP7Z0Q11U;A2n^Q{+3)08}_2K4=CSfhy1f>;(1|;BbXQ4|ApuXaM?whF~aY0mgxS z!DLWRg}ws~z}=uBm_gy-NeZ_@K`0z7q;T*tg@ff3ZjAytA{;aW1w%MgaOibHi6{WH z2Ms}2^0&qCB!6%m`GdjaZ-?PY{-Bop!6fpxM~jgTZYA9TEk-(+NxB*>Mmm`1i1Bae zijtCtJ4#CQL`jL>7^hC?g4r0SparM`X;$pV$@4kN$XUa0DoA%wfU|?1530fIpeJ|$ z^aazvK=2zd5a={$102IL@FdMYT z^i4OYGU(rdM!dXxNH;dz@i;5!0U+J#rTf1vJkSW714x@??qE7dYk(S{A9OcRi463? zQ0R2yi37g|&Ea1QjE9a!l-35cR!D}v8C(Z02X}yUqdEls2KSV3_J^RYD492zzF;eGzV9JR^SoP7W@@-01tug;1$pZ%mw|_ z=zqFZhQdP!#(|eWdtA@}jE8;!Oa^a)>%gnv4)6?k2>c1ufyclcFb~WJw}SV>KyUCGI1W4oD$rt<~P}q+PV-Dh)!DAn2 z4bo!77yibe9duf~*dn1dsD?fnq-9%6&=a~p=m)(X=nEYqmkWk&0tQ0=7K{Xk65&td zbT;&1wEpu&#y0RsfJYP<2fZt}3VIxPC?gmrA3@{1I1~-EF;0`bi@f(3f(0#!j(1pful)+;( zn2QTKfSjwm*=_`W57iu;23mpZzzig80@^|!3vx);8gPIf2)ct=pf7j=3SL0fP;=nk#{vyrYT=m&i*7z&OD z6HuV;U>x+BU;f6-!%fOhb(f*u0e zf)SttSQGRF?}FpNWH1=q1jc{|K`roWF5Hmxmj(u7 z_0uC88YJk~vcXq|Q7O-x7&J^bwJ^A)r!3P~C^K9Qm1PiHHyAa(X;-EA+*Pb1Gv{j1 zfB00T9@7J&!wSh=`Jp{`LpTqZ8$=E9gJ>aXkdu%?i2gv#br4Sh{{=(hAhRLykX4Wjhz^niDS(ti z%m*PuNH8QBk_pL&yo0Dbkr^ZovI?>lLM@pA=0J)d?;yGbp)AVapY$Ad2$@=}P@aa27;7NKuQ~eDFGnan#edK8+6S&+ zZkQhZOKv57lP;~fzh8_98UmMI&aDq7Rst1>-p5PnQwd0C_% z5v_@G)lBloj=X&7A^5@!$x_6piXJ^YqOpq{68Kq2Wnz7nxu=VVt4~CgDCv676g8z4 zFMg>cFV-=~DYN0GKTt>RLgwy!9>4ct?x;-$N~172PRcd|r4@4z-DQBalnXb3bLKqp zk3O>Q!g)zP&Y0}nKJ)kCMtfq0tLixzk%z#?2iXkcMt{s?2&Trt@E?Y)LYFF;_&_Ir z>62ppPT^0x@)qr8&7Lz?;2b%rai#wq5rtAtzz>Q#N#wZB>3SuK;WefCumBY)gxrS| zK^{X&AZ3tp2p5hi38I8FgIGgsAr25V#1-NW@q~Cod?3D%aS%U9AS4(P3W1qqVl>OZe znpj2C4+QYjfq;l;i821N4t9e_^wg*rF6DA}gOz?1jE|NcNLiO_Ib;+N5Y3J7>2E`; zCSuo~o^B(Cc({1Ex=GuFv~5J>R9q|IhKtqmFqA1C}0p8Y6HsASim zC4UO}BX}`Q|EgljZhrcZBN4j}F5s8_I=|wmc#eP8`^|@bogdH)Tux~~^?-(diE?2} zsAKW@ND8NqR{EbSlysOwA&Xd9*z$^$;PwW?Oq{0~C2l1b8fmrSx8B$K2k0WwJi zNG7Rd%UXH}YY$xob^vL)OV3)iAWe<-pbe-7yMXRsPtY6eMeXkk2icw<*c%K6oxn(t z2jf6Bs0E$D1dxm_8H6eSSDW^NH!{daMkB#OkRG;*Kr)3APy?2ORM7vyroB*Y*Z!Z~ zw8v!q#jgE7yJ;^}+qM5^H|>RLyY~O=roB*Y*Z!Z~w1-)Lv1|X&ZrTf&q(9%a|7SPt zg=)L@m`ne`O?#o*uKho|X^-)*r^Zd;)2{tLyJ=6;>gT)m|Lmr{KzF>)cI~16gPZn3 zwO#vNC;;7X=uY>)-?T^nU%`c{?ArgcoAyGrUHgA_(_W~yYmd!!T1dNN)k7=$|M8~1 zAgy+2SyZ`ePwO^X&;2u-_85PWxF9W+*{=Qn?@fDJHSx%&ayVu08aBaMNC>wrl^-ZrWq~XQ0MacJ2S!O?$!W%Uyds{i8q` zunBq=_+M|@3-l0ShYNk&wa4cAEa=wo|6gp{+am9-mL5ZBBfqQV(3I$QcxLv;3pwRpauK{(Ap&qAx)hPVx{yh4(?vtR)NK$eLMkME$v}mr&*y1ukTL{S!&U}338LYI zz<(K$GVw>`2$cWtE-2TFiW~!>i&8DIrogL6G18@NxGFW3s`Yt0+ED#JZ)z${wMfX2 z?h9Y~%bAC8Go|8H53lUdFVm4fb@)X5!>?}T2LCcr2&?s%{`7rL7(}+0>K6@1asI9n z(P$xRmtanX)RUwFpdd5}SI>l+HUte8g2p7PP;oOLG00*f^dPW$npo*gv%m9~Hm9n! z9<`}IZ9-64sFq|b)qCvA@am@edHPV)G7kBLp{P{jD9IE*j}wh|(`Y22U{wDgM30gh zmA*OimwH=n0l8~F4=3BDqQ*#trs1WL+Um=E{=Oie=M~KfA4`IQP@l^qy!vP+EB`c# zX()Y48IGDpLD!%vl>@lCAGK`+I*F`@#tGh_;39-C657CX1_i)BTIx}G0LGwIf*|tf zr>tmFnTg9$dsNOD(HO4euB-&vIn_8Eg(rLR$3;JnBgmzqOr}X0eHp=qO(LPl=I_EH zP=E-jx2YKP`TPQuJ_3H!dMXsBa@??ZH2+nW7B62QBiZg{Gou6=)F~GAmHYkgE`WEf z5QD}EwOaK8&}6GZ5vbXxeGw1y|EB^_1N%z`CHGPF=Bk`#X^x`~kcZZnHKr*oj@6Xz zDAmIwP*~~ZXnFj98G(u<&r&qnsZiB3k$YO6jQ*}wD4dEYr=LMlsud)X=D#Rh;O_&; z+@jDP5g&VwCY~<~OD!QAr$0=$diXTtLbt7%h(%o?SB6Fl?i~WMqiKikM&VLmIJ_y7 zPs2-|{pk%?>ccN@y?8|r1(1g)KFt!4PyFy9xl%S6r09OPCWhg4&Mb*{R=A(+(Yo~ zV?VgPnLlkr2f{subH_jIrZKk%+%a&wz;`&@@m1pNsN$asw?K(0K^Y71fxFqFPZzU? zTPi91$1yh*D2BN`;axai`M2*fXkr2f&uJpheI7f*(pteHx_PI8ZtzAE2QB~C^acOKj{ zTBybD!|jeA9nrc@-_AEzq$IU7h!W%{gi8Na&A)Ac#E2lKFF5nD37u1xN)JJJQXdyMC0s(+EG`=05mSol1~aK^DL++C)w z2h%O@2y({tS;PwYM_t?#nd%!dwZAd{45n{q;omaGGe$E`l&KvmlXGUeEelsMf2ARF z;5P+0v8c6h(ZB5dBL1&e*ZRNP;DDbF&FtFd!D{}7*~q|pyc_ftDHAO=@mG50T^+Y# zF!YvbdcSPvBX9JM`DzW~YsUrrIAk|(aOKR=74rr{@2*XJxS#){X_r+gjfWuq&WR;E zf8<@qH0=DWzAf}}uVBs3{62o?($aY70yn;UW|PCb@S*0D`F>W8oLYDF@Tp&T-?o~5 zMd>Jjw%|@wR2DyOb9c*~opAwOz4=#@bbRd^hwA_8(gS+;KHn4^<2_@-b|sBQeD%I| zGdrH(546_2-?Xa^E*RrEa@t8g@|*9hHl9EQ{E`FRb-(h#Jtxe~`_f zFMrT{^utA7xZsI5qdl|v=>rc8y)@1R7uY>{#IoOb<rr=PgG-@CBH2=$CsKLUwGNU73o*s zbIm-@_wt!!{rblN0z7&e+Zh5 zZ<(uKSDC^)a{DhPZ*#fGkIySEiv14Z-YfTvx^a-1oD5r(WW3mEG%i zp%Gea{{+|g+If7~ySIZjeb?WS%QfGWw>FR8mQy3T*I9v@tp44xHkbK9P6;M~3;Li1 z<_FyqFY{yOJailv>j6D_)!d<1_>)0>T3atcpDLfNXm|Gt-#dA3Ju9!SsNn@6f9h5K zM);+!`6ty@g;4chad49UBG{s_xtLjhtP$#%M%;jDd6um z8DDG5o-PQtRILfR$?L}!tronWn4^y?jPE|a$^WqANvF`eNKn#a&!efg_&eV&D>P`N zhZb_Z^y8~r{2Q}IHe-KOqlPgX>}KBPRm-o z`9l6_qSv3MU$t}OtbLZoXbbrphUen?CiO*&o%e2ZtdQR}tiJGQSYz~w_CQ;O-W}fe zVx1+wZ9x+k4ZNA7zQZ58IJe35Gc6rC_ZY_j?H%5EW?`MiBU(FhsmsFqiFf!B6KaK4 zOz!2#rTsYm+50>E)O@{#xB9`zl&b7thr9gDZS9xG7@+{#F=>`F@ABKt4%$B4?}-Za zXtw^F>*DZK!OQEVe*dG~{(F0CH@q%tlMdckS>bw=v)#7j{@QEe%lD4l)$n>pIo0h^ zy<1%q1-_|nvbht&^_R5XnJ+HLUwU4Z>2Z|P4Hd&}^TpM*+{4>uyB+0{g<5?wu8MBG zTsQtz*5fFb_O91<=c}TzdCA7IHtM6C`iDQ){C-93AaLJb9@EkJD3?3D=%vpU@%yNO zN!)1^Kz(U>#qG=Dw?~pM4e{ZT;q>lnrd$@I{yfrE*Sz;p9OycJ<1$koE8Qs>7URRjOsg0K$R_2Kv^oy(=CO1U<+V>Z%za);-KjzX%K>m?Gl{f5k zNff%SXkK_X-1sP0vRu*Z$VIW+cE`{<-}XWc)Ap2lTok=l|MbBit^+bi4pm>fAa>Jz z?{u|4YPjF)Qjd@e;@0@3cBh86LILdZGowK6vjw zD;lW2`6WIO8ET!1QZMC*y|Y)`Kl`%QQLb=o^KtKf7wv!QS1&of9rOin)7t(nriZjF zUNd$eF7V5kCthd7mZv6a!&6XTt?OO4IlqZD&W;EQXofCGb2r$tQ^*!aj42H~ve*+P zY`s-=`LyV`O_A$588zaBV?XPk7N1@j8K&)nE>i!z&ae9^vDvY?joUo$j_@fJdjftH zPqbcDr!XLg{D7mxsrhrb8R>Ov05j-^0%=}y)_8o zBgfTUCyFB{ZcATahYPBou2`kW5*G=V-z^@28gagV9Er;mQ;vOIvw05~o%+?A#K%WO z*Ln|k{oa6DIK<=NSBJ#`SJxbT(+*v%O>-7j=B0}xe)uc1{OJ&6u<{3oAwP>}oLbbp z_(NAjczAKZsY7DA*SPv8T^_-xHm~Z~??=)6UQi#8rl^s6%=y^N17ga84Gj*?L4mXd z1#~QZD#vR>TjY)z#TICN-ljoi7}FANRf3 zZTP;@CB5trVMOcB!dCHY*sm-5_k+=Cdv5cXzeyZ4{PqOnrm$L0^|VLR_2S^tLpJFx z$*5*_ozpKxT)DsIpy%8G=t|>@0n0`Hp4X(qMHtpxiD%1BUyIRynE2Xx;DTIS*hz!w zqE+$Pkw*fkg_m2Ju_pp`g}m}te_(vi_>S={;~U1;jOC1fF}`Aa$ymntg0YnGIipa* z9M2e^GCpB^%=n1$A!9LP5#yhX4;cSoyw7-#@h;;XnS#2IdE92a#dwpkfbj<7b;fIq z`HWW?uP|O_%wxPHQ+tu|f<%GS<}#1-jOQ57GUhP;&Ul9LH^ywn(~PGWe`P$$c!Kdb z<1r$wh?VkFq0WRo9WtsJIYwPQ7S5<<hpsM*z^G>A z7T0uaMl~bHsH?@o8P$v& zqfVcNGphAV*b@Qs(A8uS7}bm%qpk)EXH+wCj5<9Q&ZuVO7vt}9#?aScywiae zvq#xe0TV8NHzk5U?qoXk=0U9OxV%5g!z1~bD!s5-##Dfqz3r{1^2)GIFNc3{72v_S z`Q4eyFJCoB)3PQtS>DcdYoqwIIdl6=y5T?cx)ZLBa=bO&o7)TDR?u6)mCr9y-=y2LEe~pHA9y z4WXMk*yqpSTQ#f(?v-OLZumK8Lq$mb{_WLh6FF^ch{}^0Ql?-1l}U z8I@I?CXeRwM_x=>JgE*9Fur5fR)JsF_{hWsBghDoSJ-vY@*S2An((_r2j~uKuf%Bi z%e8Zs#?>Vosn_km1uZ{)^!br(oI65)cW7DXd3;>Uj?qG|O zy=3;VqIvwFTG6*M7L)FC+}>?Izs^xu|$pF==(rT3ce>pj)rqcQ{Inoz}U<9k))$WZP8HgPDW_4JKxu)O8$hixkf5oavfJNJhc z)Wq4I{`Mi_*l(`wp4p!kf4M;x+F)^t!;4F5d+HMRExD(I#5uhdTwn3H4Rl4t0uv!f z3=7KZ+IAN$@VQxbqb7+T*8l9$;5{v0%^Tm{pb_UB_S$-6hck5hOGys`#TOICWhTuZ z4BdO5UFSft(TN{-SoEYWQ}4PK8z8Q^rT`2>4K-fc(&)@UpGYI8Hvli>b=TOe15j?scrQrgUEa5(kF;Jr`3Dt zptYg!5wH4xC0;H&v^udj6<~jD^DE=TqlvY)}Lj3NK$C}Xh zlwq=Sh^>z}Z`9PfKg3dj(l$>NhlzhSnjKcuhniHU9yw&F$j=^dJS&j;)UQ{<5iimH z&^f#O7t|%%Nxu1>;^vu2`TcrP7pK*0{>np)v}@fa@imPCKcUN&1_Q)PeO?-#7)2NG z%U;mcRqS%OR)eLjD1!Q08!u<^@L}iD-mNKIdm`?dgBaW4Vb9iqxtY+9FEi^U){IjO zxPM!!(Y~c#UB!dzysXdn#WNYFt^dfZow(3`*vP_jla|w`f|(q z0=?P9BS_~aQ|&5~bCIcaX1bc`PBPVwGIf2K-iPV-GPS*Aay^-D$Mo(@?%TzaF zx*5|OFulG^tx_gu!gOP%8_Coe%2XRLy$;iB%j9aw)Yb&01!)cD!LbMxW^$Ljm#Kco z^fyd@Et4ykseQ%tmrQ>lQ(Y=kSHkpXOn<`k$7X-Y3(`ljhqhQISH$!OEaD$Bb@!Nl zm+6HvwYOz*x0qhQ^czgSCR3d+Q+I{wm(2vZrk7a6i!!+jOh3=`b24=~GS$B`{Wqp( zGyRlI?XNPqlT1I(^kXu0qD*y`z&tXU$1gIuBQmuaOiyR}PfR}~Q+H6N`T*1SF@3L0 zZjVguE~fv$^c^zQ!uPU6w~cvhW%_2OZ<5KS$<%IO`g*2+CsVyvrY@D~DNO%XrgoJ~ zZY9%~%fyR6%wvmee7emt)r{Ir%s-78C!>0!tZO&0@bxU5QN51&f5-e8)oW#4yGA0u zB1>fv7}Y5(!fNKvsQ#Auuac=-$-)`cD_Hn)7M{$)8MWUq|7FZyB0T~ul|8gcEJ7lS zz^Gor!WT1tMs))7|5~PQ5esKjFJ$5IEPMeAXVlJzPUBxUj|DKQwX&`iWa{R!a7OhU z=0BVHGpc9Fx^^ZDpTWWz)p0C*It!oHP#*tkICvC98_NP>WO9t^XcivD!l$xuMs+0f zk6`|c>Tp@thOzKa7S5=i(oi1%+Q}?{Q60h(2D5}gESyn2iG^!ecpwXB)CMsBiOio- z?Jw(EKNdcLH8D3q_E3MtB8->GF{;Ng|FJT4V^}z&dNd37W#OY(IHPtX^B=+d8P$T1 z?4cdbA`D{@7}Z0Wzc=$|RC~#~b_fd}%)%Mfo-BM23m+&`&8YQ|^@cik*}*YN>w)F1 znhHAn`rT=C@poKFYkJ7x()74&r_KdGhmLwc4@IP3aGHE0ap%N3^bA9%KU{TMbNVX} zPYZfR;r8ouH=VYewGN4IOAkFrVd!~_;+ARGhJ%2-?Ol=uEl?{oQH6d|V`_r|GTrfNvWS}B7h zrS`4U@yYAjE_g3p(9Bf#&Z$sed$#iydTzy|vigHlEx*u}QHP`!R5a36IJH{#tSB^^ z_9i$pqco1+m1BSGeB;`re=t((@k^AdEA_*M(B1^swjNi5Uz#x@X8uqsir{OatHFDR z^jkLTE2#jfN^MPkz_hEqUa#&(;rHvS_4%oLL+`u_>qvTy28H^3tF4_U_j=uh^gy$; zTKq+&r|Rf3DSci;zuNqxuy=Q_8c6ZI8>{N@J-k{5wpn8!?M{!w1z`1O(z<+bNb_;%hUwS+S-serdS>P+}aFYS&W2#{*{wvk%N zPaReBLij9tu;y+zPFC{If?QM^LTK}Zdu3jtQA_a~bl@8CiKz#ke>frsC1I_VnmuFrlnG5sJJ zdEd^;#=O;qiNdk@oGC@f>Jr(Q4{C2ccDAn*>F!-~8}qxJf=0eBZ%+EvZnoxp1Fu{C z&X!9VbgpPb(pc?<$qtKk6S8tEz`*?h#j-Sx?Qa9mekDWDtZU?DF>-A1+ z!hhJ-pD?P5F1TdhKg4n#{b1Kdy;qvZrVZy> z@*VaVPM!WnGV-}2{95vZR1NMNIw}pr#8JAIywLxmcX;2WWE9%-os=rRc>Jc??=Ddb zX!qIJtN7neE%Vs4QVJJ42dns!?|Zxs`(5hei(OJx{PpvyHHtfuQ5D%{tN7gqwwvx> zC>8Kx*Af+<-1^AjI}IfLX*X3XKJ$ro#x^T_h0ke??7UkE{EG>u27k67Bhz;7p>4&F zPb`c5!wtU~;IyrK?r6n_F0pKJQ?CbfZeXwcR(z))wztS!E{*e8dqr!0|6i?USX$nL z4QMy_wr|aswT#Q1ae#LGw3qq>x8^6hG`PNACmC^G-{jVOtll5Z?+v4HuCAj_Xw7$9 za;0GU9GV!ku1+Pb`5HND>$g3n4Ce7FOTMh#x|BORqzfjg-7R^$?v3I_W74(foZ~F{ z*dcQ|=r5yDpfz*ZVad18it05wiZ;x&<6ZMD`Q|l~IuCM`hIy15-oCB9XZ@KdE!l`x z*I%%;;=4O`eY#+bG*_Ft`&se9vlA639i=`C@<_Jg?>sEnIphVk2qzBIS@8?)G--8@ zOXI$9P>~g1ac@?I;eVOez^20unuxSDyyM#N z3ua`s>|ffMbQ@u4S{r^`Z=;ia8+L*RmohB34d3l}?=kNJB_q=J;o9vh=Z}`?@^yMaZB5y(?G1I{5q{S5}{_M+L8i_}C7`;9hIGDoi zu2eJ>gN`3)aJ30-H*pWIoNp+0+uwZZ^>tmL3*3gQ8ykvK{Vh&=?{uLEL-QjViif## zPuukEO?r)Md_&Rn&k2dPyQJ{x*IG6d?Sk6${BxUh!M4|*nTdl}tm``^K~3>5T^G$n z`yaYiILG%RUAXa$nb`GYY@>d^n~~nWV1k*r^q0MLN53u4r2GGcf-Yv_i*CX42#8bJGD79&@jYsd&fb&9bkp zqzmTXGcgrQURyq`*yB#&VfSy>7gyUnHY}Vcb%EX=`|67aMy`2wGhAw+m468J#i1jV zU%iTP7AS$$1F!mG{RZi^PsEpILO=MRWqt8P)|1;IIZ_7dKc6YZK|k0(o!z$^#n1Uu zREp|j*6;P5r6wI&^o>&dcKel{7L6sFI8YR!6wlUvxI4{9>ayO&yi#l%`{2!i1Dz?o zSZtycYtHTeF1n8(C0LXvnuurW89b@={XmM4ba{e_xa^jv@mUY4iIcBX)Ds<#su$Qc zl8i9<>c)EFcB@65-w$|81y0K6>xpv~bsu)-8)=Vo(Y4#gVyoGm?aa=3Q^mg48$66=aX{Z3xYuO(IB(CrCz#fIOj^6HLprSz){D-1=GorOL1R!K(O z;f`o1{<3Ia>Gu8oDZKXG35KG=r~4UUbJ-yBD7;%?Ag&zT`M{OUQlI{OZ=->zE>EBE z{*=^bOYZXqVxz9>#s!b~gKFga$8Cjp;?K>uBi~6~)b@d(5Eq^vn2{1E)v)A2ONDs# z;_g!x-#4TDfBq9D8&}x8OR!Nd((Q|cI-=+L)55gz=5FNirl@5dam~oon?}D#8DtcT zwMD%j`_lWGw4a6)bI&wx!Y9~_V_*x^w{Epc^^whHrO(l9K1w6T^r^@8c^+ZUwt zD<1Q;#6A|whwa@a%`56BxAnz>WgD$bYSF$n_xOpRFV-6*thia@LOb#p_mtNc=jHw| z>)VM^3)FaayQY{{YUQ?Pnq)-Vp9wWZkK*a}%ZEwv`x0i0z6Agq+pH+t~q!y_C zVuGGn-}Ka*8Uv(0U;Uzj6Yp8J2oZBE=>n>_PGU9#wqE^1JouNawCJ({bY-^B=N??l`EZCrSnMc_O^e5^g$- zbS-=LRHCzNP%@Eb|xNew%h)GE%D-LV4Dr!o>ksUP$>r zej)xPWX-sJ!MIDR)Wo~XDqdxst`k%Ds*lvi!7uq&S;bA9Tm0T$YN5iH;>)a=f9wx9 zzhej$Xw|EVvaDH)USF{Jn!bJE9RA|VvIZ=&)bjPE0v7%yyvW)<$7cGqVoBGQi=|mc z(+|WSn=Eyq`RfYddDhVTaTRr1G^Px8yyl;0y*irE0f&5(Zudqg$=Y$hIMLuuC(I{FU%DEAPaG-`A!~HB$an@i=S#!tW{~GbAf7|4Vq36?W5J`L?T+ zUv4@6IC8tqOSV4* z%zfCB3XuJ_;$GICX~q2}X{1qM`>x_{R+}aV*LOQD)iC>=a3|~Bw#II|Hb{57v^N#E zv#$DWbF6n;x_6|#ttiO){cU|W$JvIIpH_HRaV;zMc7wqQ+B)Q+eqV7VYt7!?_qXVz zQBd-};!>90xi`P`KGsu8@S!3%Yf57W!v}FvmvA2{e$Ogh;4;-`m^3kIKUAE~`lbBt z!aIRdi>N9pj%95(9gwdJk}9aKs7TM65S(k=rj}G7L0eJreU?$ZWy6R6Buz9Z!>3Q9 zj2|cF2`BldqXJiNKQY58;80?R0p34}b(}-`yL7%H$}#0%Cy={G_qRJ88bN4OgrDklw^2chWQWZcE_Hi+uY?~q>(_RT`=}k7 zUiz?a(%TY#*3ls+pL{nZbEq)ZG*_>|bH4X?uDzANuFlNq8mc?m;W>YLTj-BwKMl<+ zwAs0LZNKOIhMv#Yid|M_4qdh_J9W%+-eA|$!;{tX9cB%kcoR3{{^{{jDyv&D7 zlk8tFdCpHUNx5LDT%H;Gd&Hx$Tb}dxgFR+>j$P)EH%>pk@R#R&omZWEEE>KhQ~2R< zaM!Q%p7RcYo4Ihk4Gzt(y=(3L);|2%j98V7#+L9cT|OZly3WmzMREpfOx`>w`q zN-2M>SiPqE(fJPZ0$$#^9bd}NG3Jx{c&-&P@7!wna{Y!0J#!{C~{7 zcU%_7_cpw!NKpj4v10FLdoS40prT?gpn{5`AYhGVvG<4-jSY>xCw9e_*jwy9#@J2l zz58CXH@5-4=KDPF`}_Uz-k;B5*_qjy*(qnvxz3rLoX@PA!`k!Kbb!zd|%6FR;7B4+dph?k54(Q8EIWFc=lQ|;hB||W0pQ~`n8W^JGMIIqv8aeDEjRXOUueP%s-)ACK(6Des`YP>4#Tj;qpU= zOq-*h(D!UyIO@6exO4KRANM5aS5KdRT;<2-R?D3ypLISmORrc(JbP)^bL)Do`)gBb z57GU8|1j(4E6*)%K##P`_h;%$tIh5A^PA_^jaF?=`K+IoR;%l83ojLVVLhC>BibI> zZ&KQ@CUaX>%pa0 z7mU60!s>84iGH(Ww(fJK-_}1pUt0CAwzOy0x;`~+WD9lOcjaDMGd2W$w`cg&w7_4F z^7U%JwAit8`|80H(#n@j!=yt2+V zDs(a=-+aAuFq~$*zGHY!4*VcKq z;F%I3bJJEIC|j>+{nysJV>N#qac)f7gDG`pU+n(c+V=9y?AQC}r_Jg*?&5)QuPt@s z;Ag)~o26e)ty1gOs@GP9Z$`Cza&Lw{q0Yua>~F8F6*um&9nZ$4iTw-CTJz+!6_avf zVg92d_5GhF=W18rjb)cD-@3-Yb92*f?K`$klHOR`zuP$UKzO$_|7Hh|)M@+1QXVg! zG5DK)X`zj;&L1}9jdfyVvBB+%%uUKlu_eSGqcgvn{ikAECCdG8x*=hHsx z>J1*OLnZ&J%iT9t?~vOA4woCCN8Rf1ZP(myE%t2j4h`B&P2;LJyg!?MYg;SDD?{4T zGxQGS&7ZTkcx(MxbK>RRU(Zczy`IiLZ{S;Nf0uoSzdJHp-}PZ&i=Ok|TFi%6c@Diz z)(1U1y{qT1Z>tHK z-W6=JWg+*@@;X0)?wHg$O>J{)VtgC>opqzvw(WoPnUhw(sjrzG)3} zE}+*ferMIF9G2(w`*~@n_I#6E^5{Ej#JuW*P8h>{qppk6+fq^b@;=)v^mX2 z{yzJw_tx*hp<|;0hU$Y(davDs8Cy!+BtH1rVLyKI0ytm@#4r{Jmn3y(rtXIS8 zULUMEg9g@?(&nT+9qZ>k`s)u?|B-Y1d>cGBZSABlM#+>FDyLvGa!O!$S_dYdYwo z_2pGj4JIsM7HGcamK;@_S1O_hB5drYQt zZT`86Paf~l-FY97FDMr%_vY@rU+ZwYh5OX+3?E*{A&fY6?VnRr$q}Oy5JiF1Mxuv z@>ziP*x26(TmGZ*u>R85&b&9AauCH*T0;m$W8B9VW0s*3Y@0e2o>XUWr*PbHix-vv9S$wvu7oU*D? z434U=iX-eRQ(vRRKuUMYCf`9KA8m-m_Yf-K+MNn_@+TkV7+^c6+m}bpL-1jY5PT^j z2GtW_UbCZg~?(;b8O#Xb9uxs+h#U|j$PmHRzT`*BXW68H|aW16m3}Q^1 zzRsb@F;Ts{C3aT>%SQ%Q!Y8JYJ^I)#A6eNKnc}QE|;;`zIg=03+?9I|K#>8iD0M-&bWi{B!@G`8VhtX-5sf@v%`+nZ{sj zd}I{PktVtR=R=Ww<8X2-IZ-yccO;IaPA?2Yz}?W5(58WCdeZR>@^B4>s_EAT7#8>z z2s)2^9m00^5@|D6!s_Tz@{NyP=*vnJi_zc;&-@@M!qva6@WFyQ*ltd~001j&`e^u4 zY{vOhyHjs+BChS4?l|{0KDO8A^P&UEQL}{akVB=rf$3q2k>ke$!TU0Ep+H3Z!&Uw{ zWwul2E|F1PACrSD6LA`5yo(}mB@_w@0tLj|_+Eb;j>^P$`{O_~C=f)xp)3}OAVs}s@3%mSck$|qtLj7e+s30Pyr{RU4_Qs ziW7o9mzk<{Lyfv-E|fGQ*?u31!o5l1Hj#FyyvcwPexGLKrx_l-}vKHL4OiNx#RF!&NuUu5tp1L&JS@h+40d! z#4XAamj|YhgqeN~{KMkVj}85uZ+a)?^-Vc4)31dc*u+1e5~%nz|A2#e-QS6)Nx`3g zapV01jt2R=t4?_!<_hj_riYPucjENVzf(a7$oLG2AA@uSYW|ZJZ);QK5I}=Y;4m3? zB2obPWy~z&gCuSdGK|tmKz5Ki9MAy+R_7BH_9$8ScjY3D8k; zLT!2lNxCm(nDJSX-+iQ`(w`wOlmiri`*NV881>gM&3GGs+=kYC1Hx-)RaAcG11 z0b%Kxk}y({NZiMP+!Z8gzSM#lpCiwPBAxv_LuMq9MqmKuDY#5w(+0~wOUp&-&k!QcA_&VlHM)PkyfJ1jpwPWp(vr#~BF;%m#ve)C8nw;(w#)FgBoS$qjBi^286HiKBX3N8 zBux-de7-Y5#iPxpaxvzofZ<2=mGFIjw z3P}WgY4EyIyZ*jMrK%x+Vl;JrWKi72TSlOEfuO7dk@VS@x`p&38SP)<9b^WOwJev{9ANn)@E0hMtV>4^hFReZZd8P%cgiPkzX%0vK_WN*UX`UKNTxjEGY&+O0SONRk%5@8 zl`;-C(n?6;{76~Lkd~}|_}8Yg4nX(7Bb+T`Chmdfnms|Nz<-_p zBvaCl?mlymhearW%vE=x87oBQZ&31=r)4FGSs|cIas&F@-Ma4loc0eyCPk3g9Z354 zu6uysf6`yCGu)GOq+n7AdDcBJGgdBRe&arPQRO~*QP=#vscS*r)SorIsc{~@)H;xT z0)HNZ0<-y2twBkk^`N^TCA%*b0a^>X2BLBx9Vi$y0JIK7dHPbJpfR9zpcA0iAStIW zwFUGTRM89Zpmm_zpg?b5ststWx35K=#y>@Td?^mp5;PDr4b-f8o#t0-9ld}2ySg{L!5 zL6D%G!C!->?Jiw`JB@G$)NU45u66C2B+(dz5xKpFE37sNn}#qlxYNTzNZ6dr&yvr( zQK^~39Q|NXrmzN18O!q8XhA&S%CLb`$nMMuorm{k3JY-_hRE-F3#Wo1PP(I+!a|*N zClK~er9)ej3SMxhvz>=STa%Pm+<~qzC&O!*!or*kZz8Pt=Srs^;FQmoO0D5Z)rcp} zj1~y;cfFW%Uygj&klKQ2m=?b5#@U6$6fuMr6ql=u_R4UH-AU0J!cPU2`z=euP^-uAd<5zkz?Nu;|1dv}D90lryYgiNifXSr`1bg6r z!hj+V;p9qkB&Amhb#g3{F-H#Z_`YmztjUN?hG<0jgOBuf9|&f^5qlKNi2m4uO>`|5rV zQZ{*ak~gR_MJi4brH)b*dWAV*c5RW6qMtiMvuu77ndGK3YzJPqutNB zhe#^YxGO}i%hI1AZ0nlqnXs%CC+V(bdG0RmN&QK_@}{qeEzwgO=-W*uShv^mpd$T zZ{(Wf>J8GWL5M?d{70+0TlaB>8jxC&l1I3naq=cD8JN>GG%KZFNjlQ{yItv=VToI3 zVn?K>>6w1*mwp|JD`}+(>DLro$<$q#eqD_#$(Ib>axGkM9)>G!KmXisehq0!z3$?Q zF!B}G|Kr&GJaoGw(;(*w*Pnt5T#ZFud=BE!SpV=1uXMhFE2$i*@CjEMXK0X0gr=uy zjVq~OH(W_xN$KI_-rcapTe@DX8m>t4`R9JIf4VdXDS|%7<9ofU{;6dm;!w-a+~5RC zAT_z1UJ_}-W$7hd_?MCbyj{-}!IhLwe(o#Diwut}v;T7o-gnjQE7w!_dvNVPvp=wv zQ@MX;KMHWaeuyjXbMq!4j$|@ELz*O4n$It{J3Y;j^fV9=sDnsDD!Tz!^1S;;!blep zB8@=q16un#jYIjkUf3H~5>8&wiO+J6DDs?3e{;74Sq;epK_oW)*JB42@DT~f?S=F! zxzByoE1(pJguCNUWQZr>1#e`IKa(YX_6<98f{Yogz3CNj97Hk-C1^W-tMHa&&~3q$ z>IBFM{}WV}#365|l0;r@ax3$5H<6yq%ICPcpC!*M>h^_)hh=#m#o8{mrj%v#(nnX#qAJiP;wK%}fM#h=l< z11^9v#%F9u628}P#ea|MOprUDWv)i6{yF0?-j#4U=N+&f6hxj*|Gh&Td0A)rI#rh2 z`hOnv!T6uNZfjiKPmn7Ka~GGfS|491py~zQ7 z|74tXWWA}AxjnMRFH4WV>3V-EWBlb)V zSkO0Sj?Y>^oAs`EcNcBQ{Jc#~-I_UrtQ9!5>A%O1*_OG0jL%o!;T>RrNK?BjoOuj@ zb9PM2oFHQcR_+a*-E7oUjq1&5fDNPeiF)4~PF4g3j+xHS`Ls(}V~ z0`h({!nI0~70dlUN$buqI6XsRT_fQcGYmzBB#an1vc|1zPC3%bUOb;s;2>b>=d?Ed zBce_c91=*NyU-2kg%X67gu4qRX?Lfm%@}^Wi@&o(S4AKp8F*Fq7|1PMW@2#Www2s?rMb)e*WNQ;Ll!-IMZ z3V>#q3cw`-1<=TNcSD5X|J?{rR*|kI{;%ah#o>RNgg3@>AR6%jI`ULKaNav215ukG z&69ClmByHd(aZlPSA1Cbnyr3RVrYD9mzt50@lil5Q~ECV zvr~Gk@T-vWe7Rpgp)E-e(kUUaZhU-fJVib6Y!w|DRl9qq_^{YUePa@%YYj|{qRM(U z&zLIZ>y>_$y?S{zkBN$kOKG*zk1JKhqb|HCBcnoNiO*I%mD8h6REGPMldJp+rxaT4 zSEfNnr$ooawn6W%#98kI32E;5@`b_i3n4o^5K=po#8Z27G)s(W(Yrs~-KeD=_24WQ z65TnzQ~bb`&5g<=-xaeIw5iMv6X(F9^ppz;HXY z)p7oB7UjCD>D-a*fXrlXkkGPIOmt+3Q!UCjJBlY}lR8mdJE0LmYB#7u7);&XeS0+@ zn2;FN%at%;v#{FF-yJ&5}S=|I%4pu_kdL><8ul%6)J@$I1PKYg{UPD%{Gst!cu zaa<5_tsNUTFh06l_r$nkRoOF zPkz7Dy_4XV?^D%S=4C_FK~Q*>fNP#xD}Ev<4+ zi*-swMd}2xLGIi#zfiQD?uhS>&iJ;@TZDH3)9e--KhT{ba|S4!^qRZaOs$pR@PbT@ zktsgBUZ*a0RHEbW|2g-J6$q~%)jKL4ulp}G$&j{1cbKPcp63ATXg|6zU6%fu*68|lQ+gR)fH9e5hN5AKMEq0s1A9y=Dc_O{ zC|#B1N}BRc>8l=9n`%F3YYZd^aybBxV`ef-m<>!tb|~A9OX0S1kGKMSRlWsp^E3IH z;%4!p_(&`yRgjiTo1}Bn4e5$4vr0XsnQ~6Op?YZ5wHU38UQw^6H_!*_WA&a!QM0u< z!@O*ISmmr4)-nr)g+d|2MW>h3o9UzUCHfKlfiA>UVt(b$aSyosdy3NB}o~fj8P^i$;vck zrZPuKRTe4BlvRqT2CElzU*l`T-z;ScW-YU&*~PR?e5%2`ZN4%eS@;wHL<#Ki$0ZM) zpEl{*s8V^R8xzC4XU_3A_!+`dVV`hRxF-}6S#hd3ODryxmActdVY!6-Kz=40N^Pa9 zo}iD{OBwx)F-B|iFH^OGtU*>1$vn=L`6zZOyNrFwX5)%;OStvi3GOeh0?+f`@SFHw z`9JtVLOEfSFkL8$LPm+xMSrQV)KZR;|B&yBFq#g7vQY0R5WoW!yGi8#uCV9mWds>_?BGAJaTDh_ktc z+)}O|y818Gey$eMDrw}ANk&Dpff;U2GMAaZnRm?|q(axA8{@K+@n$P>W4P7)4*n$H zMD8J1QC4WrwKrN(y|jMacxiZ<5?ZM$S}2)3_t-UaLg)eXU^*XzBE%f?fqyj8_&WQAFMG4$qIi%6xQ)RQKx1@17J zo=N9s@-lUpZd(Q(jrrj*^VK4 zoH@(XXIr7^&#^byzu4AXckVoA|BeatgzL=5^SAiF`Pcj>zNhf5a8*Zm4+1>1^vxtz%695C5KU2kkN4jUJYSB9sV z&n#(U`~7rGBUmQJRZ(i`cM^ac7h{hThy6bA>CnKn!W6UQVmvzax_Zsr(s zo_WdSWCJiMJFvajZ^1o&V1HtFvFF*V>w;<3j~j)Fv4-2p9p~{LlPu{!ji9|B}xklovQ5NN6hb5)y@BLbC7^dVjBQMtC8- z7jlcmMM-QZwilzt1aXSEP+TScExr+bz>L0@M5&R~PD+plNt31dwzOUPMLHo}l|D&1 z<$Q7(xwhO;Zi@*vQl2TVkT=VF{?Z>S^`1`danW{IwdIsRe7JG2Rzz>$M}=DUAD9n!jF1uddsg z-b(MN$LNFf+4@3#jlNU=L%*%R)N>l84c@3{G&edJgN+Hs4Di7l#xuji{K^c(T(4_( zFeA-x&9UZUbCtQxJZRoCADi#NC#!=`*1`OVwT4;KtOeFe>#%jn!ug4hvr%d2$_nVl zAo?4+E8UAuvgz-!j;*5)(5L8!^b49|a$#0;jKwrTKlW!vGbzj#W*_?TCi4)?qXb)# zjb`K6;q3S9a&{g2D|?*1$3A9raQ<9Xj^zw47)&<~YbmS5KHvfRPDIu3oL8va&6hegVLaZ=I7%40fRtsB&L&9H}10G^tEG3rM zRO~2r7e|XJ;sWu8_!nlmH`cEzk|704U8Ek;chXpCv9wCsjPAT4{UyCbcUF{Z$fn#_ zj+FoT9cP8l?kr9@vA&m}MY2VzD;8&JGYy#zOb=!P zGZm81CUo0X<}cZaWsqd3tVW&U8U zFsZ=o)EQJJ8(oC1M$2?Vx-A`p)oU`ngx*RYhNyL)&d&H^rRUf>Y$#Uwcya)(sj^}zEtSqnf-(%_W0|s5IjP*ldwQxxRYsN7##$FGQA^UM zXbZIs+ClB2_Rx9X3c9R^=~>5~W(YG44D)Ba#c%9o$SogOf37UJVgoLM>%$G@rh+SO=1xGodd$D){eSR zcVqBfWgoIO#TDSnb1K(}>&W%yk|0ve<2Lh$`K$b2{5#%PC?-@Dbny8|p|3C=@A{Ll zTevLV5nqcrBqGVnQV53Y0BMT!qqH7fcTu`0G`=D|Wi}^dn zUoEAs(Ca~dCZjIZHrJZFoi)(IDg+r{wn8xV`Z+>}Yi5Kq-!eZiE0~>-bgpA9E5wRy zJ@7!AO~Jt1!S>~+@QeA4{3ZS=|BnaRvDFzWVbLz|n=ZQ%}Z7r6UePQC=sIxA>2p9Cq1 z6YGjCu_jIyQ^lXeed0x1d@Oom*i^;5Zzc7Ta%&Cs=4M+oPY<)d`5k(9j=2>rbl$vb z{%t-rKbbkLuPxCsta?^UtAo|U>SK+zzK6W9)=IN}wN6;)t!LI-i;BxmWERK`-gIfY z5)_iUbU2ore)JG}947QUdNJ76QQE#j-@ve-n8Hj+yp4eO32}soUQ8mB$}D9zqv6jo zSD5?EGbTIh%NAx!vQ@#$OtvoDhaJF<0XLh^E@3yJD-N?K*<0*=_6m(Aznzv8R$9M-0$e0M$;isF1Qk3IZZ{tAX* zZlMT*m|Ud%4#m5N|! zR>$I0M+$?m5iP|@)1^5WqAR2gkT?!G62~p+g=D{%a?APU(sCuaksKzslRL{Xa)LZm z9wX0?=R$biAn%or$QL1mcq+M-{7O+reN>bnrLoci;z$hi#^K6j=!-j)z0N{*MY*B8 zRz4|yY5}zz*s`Y9QbW~N>R@%aIu7E=Y;~c!PTj2Tg9d%YhTi*3eXDvqa*CkoT5YX4 zR=8NLpEd#`bT&BL&sggYXvei+qlwYRh%g2kNk+1fVk|ONf}bCPU~{P1RK`4_7G3Ua>?9E?q_Znx1aMC{DneTRz$%N8VO+- zAMt_>y?2T*%Mo5q2^WPM5MSOxd?_iGhXA99&BeB2H;kHZ#bM$iaizFT+#{Y8?F&F8 zUSi1jN+qT85-TZEJ;mU2YC1x#j+Y0@ z$#ROkR9++hA|H~^$-hIQ{Q4eh4-RDGlRXaV3@M1rlWwb3H9URd|YxLBbR zZt%v;W)-xGTh%Pi;Rb`Oq1I$;ro#=cTDNc>`wPNJle1H=(T(1Y4pD)&Ai8v*Bk4r? zTiPB^PoX!^d!Q)&<_Ms9n1T$;DBuFknJ$oI1~Egy1=c`;I>`LV++{r3+-xbf0;{k= zkN{e--PqpjT}PpEL}DXfl@G1=2F;zdNLZ(r@Uz8`5Lxm6T2Pfk-OI zIJWEcMSIR%jXP_`&m+t|RdF#x1(vwvodXc^iQ`#wml%aqQ zCZRV|m6giR&b&XVTu|;RkCYEe4z(;04kDz~f~p;+wu30!OC6$)P^V$$FHu*khtw15 zdG#uU+NY|Qt>w{*Yh^VKl5KshvD3$cwc*-0ZL%ZfZqT+vzWGhNqTPUu^Fqs}`{)&* zxA3~A*L7r_E|Bg9=%e(B`Vaa7C@>rKDrQHl3PZ3SJhiCQ?ARRzFoK>=&!K;$SD>@E zLkc=UU!`wB->~09Zq3h>hVtm1wJ}Tr`g{yCgPF^$2Aa2*Il^4T+$BAqACOEr@PE?# zjoB7#6dS`1hB7i4Qs54DFMEVN#a@AW{e*qZ=ENFMf-C1t;0726eW8>L=f-i!NcQ*J7^mWRkAt+FP8N03!x2>8P^tQ*U5H4aRoEK4hFQt{S(D7sh)dx0&B8ZB{ZHnPJWX5(61% zs5u4z>|ArTxxw6P9x*Rs86gWueyb?NwW^i^h_bQO0;@=jHP{;Nu%8{)Uh9Z;%DQ6R zu%1}20l{Ub3SbcC1kPN7E{BywqZ`nnbYFU~O%I1WG7ZZK(SmnECOJ!AftvA*_F%l3 z>I~0lOf82Qbz-_R{UEAOgs{GdS;=e#zH!SDOWra$*j(&aY#>{M6(E*`uKY}06&jP#J#~^*G2Il-u&ZgJ{^(1{NSjRSfkA4P<^F#fG?rG#U ziW_AO&QKkJgQ(AwjhV(qBMnmWIVd5|4PP^_Ggexe9nB~+2B_%_bGf+=klY!lB7d9j zur%efzOsU?CZCt4Y1V9Or?t;Ii;;03Vk)&eyX^=V*=b)|0Vq$4)NkS{f;ijR0Y0eG0h;Jx&@2%%r<5}P_EyBju7TlmaW1XP)$3r-O>9tJBR&| zUC(ZF1k;xgN&~pUTosPt8gnhUC@uzFZYMh{(`Im^JKRI=1DAs@%$IZ)rN+RlWB3GT zVfukzz@OmHLm+*EUiJdWURBV9T0*$cRp^Iao+wNcb^!l659IKP@LDJc1gsjw)rP=L zIsrEsAli`lR|DYOE?yOHiBF(E6_kogiVe9x6q0{8=)Wn_JSdD?oE7V~^Z)>gUpj457eVK$i_|^9%P*_fRr`%fLBuHR2pNgMvxWiDT4k;`_oFBOg1G2mc>|g+ zCcYC;^LMI$#fOT@FlvWeX2 ziHTWRqS1-Z($VHXXimB9vO3@xFMFQ4@AC*lIEsmilpFo|+rRGyvRe?wm3Y4=O)`E$SK(P~Q z#0B*a^}U*1D++v!(PUtqO|+rf7;Ta^!&wCmXlGz*d8-xHOX^j0Mz5&{1MVE^U_$mn zeYw70KL&%z9ROyYMnTN{YMA+h%x>uEkJxDoldX*N#CVk z)1PR6rVv9WXeiT)>BvOECe)7^!Hjo0cOAO-0O0p4%nevk{Gq@T5VHoWv$ff#HrpC= zH5PMq6gv$%z-}0hjsr~k1HAe*+8x$CvhgYb#vG_Y=G1g9hhPj>|cAhgHW{p zfT8gvrfx2tcBXD!z6n@$B;S)C=&0Q(kSo{lX|Rypb>Qm{ycbw?aiOeWGlDDx3r(Dv zoCxGU6?VEc!bV5LJR@9&z3#CU3H0}_VlT13_#KqbsSrAsLHXPz{wCt% zv`|0;pv)6Zg5cbBq;I4)Ku6+W4;d|e?ZX3|GcM`JAt;#dskK=wnB* z@KpWPLXP@rsrA&BY6l3RiH>%$M%}2UslPfR=v~!*q<&CyXa%%lT6wJ+gwUE=yfzTf z+Gs6Bo2RV;V7>%0jqOY`2 zYAoMTqSZ@kEl9_uVf3`Uj4tLOlZT~-O3mTyR}Y{Q=!>+;jN`Au^y}%^`p)3tio^~G ze{k{vfHvYdaRlsFV=z(YDl3#VV8c|PD^7*R&*1BnIrobOD$o3_$i_sQw_>2WL1E)UVL=e6i@305B5_n^Jow8aAcx0e3HR zn6iBtLUbhu&-=zPEDgbeyNq26k@*mNmwg0nJ|~!OP2j_Ap}S26I=m7Z{4MSkRJL4v zMW}3bz*Y|fth>kO67oS|uk7&CjsRtd>^l={>J_MBPtj>5#Bw4dny?FYfv7u#KzFvd z4oJ*%@go|zFraf?Y5=t>5ew-UAa(?+I|u;%x%5u*l8Zu~tt&T!);?UG2>!SnlF$`L zQCF3kN>lK|QOZPs>?@T0$~on>@=_@Y*+)?8Vr}%r>R1ujT0^as*2&S4rfR9$Ce7Xf zM~L&z^5~@())hSnkb4I>pG?qa=}YwE`dOHg9_im2BaP|CVpwxe8h;uO4PpbVZmIy; zq5-pwG8Z`@{9*Hv`O?g8`8m>3U8@;vNPS@ioCH8-qa*hMn8l#>bc9|HIxk()b`+rw zbPq_pv#_*oa`d0O(9H8QMVX4we_Au)OmAS|DHzQw0i>OToJ3%>e9(9*z!1|ED`*r9 zF+>GljE*}A80HT9iY@4vVsywz-MAs>x}V{Wvmf#iQD<1*ay0C|ygkxc8h64R_?UkM z)w%$bYJ&H7gso;AtThXuOdkfEc3bcibBkX=k*+H?6FZBsz_4dRf!-)yg6{kT9Z0N! z667EPwhjgevl`vEM>-DU)f-qpia0XRH_(z3W zrMVKX3{*w}E?W<%?0|9}u*xTx4T`HZ;fB)!D47Q^lWLlRp6sag)cR^u;fAyveMvOk z8`>ky1{yL`Ukp?35hygb;0cn)C}C8A9k7wn7Pi2p#%ANFanX1SZe!>0uDEb9txRv zHrx*O(8r;+gu^|fKjfgfFkkGzbi2(wfH#m2TOOV=3IyCy>_i);z$FfFaskjeflP`* z+NtMEw@AkrzYV5@yO3|*12U_~Gkg&IU84Co$h6}eOYT+}+KBr3n)ie}Qw65BI>1AE z04p0UEQ4w40F;uPqMukotd5RrgK0Nf{2twQ5F$%i$>w0KYLPCtE|dGMP6gq4q+>d^OTyb1`WNR!xkT!H9#7Hm(8RLu}jJ?JY%+-i=F zG7t#%MBvvufq~s)9z!|theMJEg1hsU@oV{CZ0I9ToW9EkhYA&D#pXf+jPMfx84!i?3=~QVK*3jH1x&mU zv9%Z_4#(n?0t|Z}fUC=Zt#U}YVDBjJKvr#`Ne+dmIUDHbe(4y%symJzSyblWNpVoV z1WA%WQ6-d$=tB6vg+quO37TAB{i=W&z^LvN^e*JJg;aNk=BIFfYc9iR&D^!&zZKu-IO z({RiB1gxnLJn3}X{01I=MDZL0Th|?!;NO~FRwXb{-D-fo>ur5&jj>i(KSA9*2q4Lm zx{rR#Ll=Zqs|I{wnnSGU1-xK7{C$3Mj1hkV#`gM`6>kr?=T(5%B>-abY)v+TwR=Ft z9PPja`yA}@DGU$!xOQA;IGNepTyBxmheWA-$z|uu@im-2Bub^tPk>Ufk^co4(BCjV zdO)S92vAbP`qv4o-?v!(Rtf9DKTlxwdxJ?>NR;785rXxP-~r>oKR4Qd9}huwcqYDu z{l5T!M-?ozDOl(rX`J+f;~jHK`U7C2uUuFzC)WdRO}t}9VFjE84S_(6q)&4wxqt(3 zFuJyY-8{xING(=2Dks1{??5BS4_I1IE$ENoYK%J2WqeZaskScw#F8*DGywD*4frrc zTL34zop2$z4KuVSRK}_f)Z9hy0Y9-F9G75%>cUiUGo!%L>7(bVYcc*)EH zr1%RkqCX68cs!LftD3=P6SF;5$#LdnSW{O!xWXlLG+8BaoTBaYb4#lWyc|bcDb`YJ zgSE#xZe6wBlX<$^)A^iscDP)Wg$=VY-Hz@~kAiHo91aQl9a!Np-InRZ^n#&+pn}9# z<`I0Jvcu;|gTqA=V1cLDi{PzKS>o!%aY2qbG6~kC?c6V%eTw@X{hF6A1^9WYiZ6jV za#|j`4E#1s_9Ay(@I#;VaVFbeq6zCzV?b`(py6Kz3T&c78bg=g2J6pd;wZQ-C%zCw zQH2~};Zj#=IQn}D7R_zWqIuSl zd5FE@D>=~74Lix*9e8;rB%f7~d`>w=$vd)_lE*PmG;}1OC};H~au7ieh(1Wn5?*Q^ zc!-p75QKX0lxmArl=w-_SM4P#fe=oq7cm$f0}JwUbiqp6*IH+chd8aj10(zhj=c>W z`=oY3yQSTS-y;PeLDnt39=xZb^ms>NnyUW@_nXbm!h1@O0n;81rajG=4MXM6j;Znn zSobUAqmjofXtJ=g*RtV-(;n9D1alAu$V>-%+6{LcV()&BLE>xWb!?UO0WuT#iHs5g zBNBTzv2_zG7a1oxsb#oiqkSBJ*=?xo4z@lJ4lJqgl_QYF1^N%i*zLy@aNr7wu}w!T z?#m2g#=_%y8MBs2V}6CL@)G+02{aW?wjf&^P*ht7R7?V|ATR|1m4AVRe2)Dckn(f3 zIN)T$_uZE6>9BPZcw#pe?=z5>BlwE2D5Hk77Zr%hF|8D4P!Z#6+vsfU0QBAua9anw z6BXn|%|U_~0B64dT4@PMy$JA;eURy1upTg~eZw{5!nvMsMJ)lSc{uExHNZi9A$mms zSSSJsc`OVL4Il*{QqIE6SVFA;xwjdl-Wl+J+o1ib$#D4$((3>P3ei&rd*vxmYa&+N zEqZhKM}0JA!5hQNqT%>-*P=qbs1c}5LAnjSm;MTKY!kJI(_gsc__;GqsAz%!}ux?d^F`fI2cN2qp7s&td z5Fdv@d`y9zZXrB&2>N{vzMYT2iYmc1gJ)DG=reRmOtXEMfiTpKg*{{zGoM+`thWJ% zA7N_1)w2gXo=t{(%o=t(e6p%TgR2WgAQrZb1F*ifh0DwqJ{%(A5ZESG2Ycm>4EWGr^eFg5&)t|BB`2F5EFaU=xjnO?xytaurmqM^Luj zD|R;U83rh&sYa^N>Kt`DH0`%)KImRmv@qDNiRhOKDgB<74=_>!5J)1YZ__X81B|i8 z6v%h$j9=lQb_Pn;J;xcRAY5h-K&=`D!_s=VCp>V;Z;!nkEQq3Sv$-I0m4?h*gA;7H z3It&uhG2@cf(+ILu8_UCL~aoLnnrTtp}nSHUM}R8!N+wy)S+FlKput*{W%yR{{;U3 z0EWod+(!V3zPvwQ5SE|P*i29j>XM8t1+@VtH-Rr~TRsAya!Y6dZsr_G$hi9I0-?Z19+W0o2(8ri0v2l8Xo>A*)x0ZCilM7X+y< z1S(f6p#voM?m}<4)DD6_!$=4alZ6?O1Q%jPt%fME74vE@)Ugx7IY5(t3U^@Pd2vcVu`mq|!;*w~midJ85XtXmnz=`A*G4NnNA@S#5z+7PdfQ{xZ>~)Wsdd}dS27Pr7`-&&_ zuP{ihBQZ5=VjG72c@xHMrK(D+JWT1jdRD)yN5E?lYFB`_BNKcJ)NeX`+WfHvAqJdj z6f?z!&t)2%#PR@RZooE&THKrc4*p5gpk(a^w?59EhGLwZ^M;zmz<^MTtB>(PHUx}- zl$Qos{Umrb1(zgZ0UCqOYef9JX>W%mkD({P>3SM4tT|Xv7J}76W;m{wiq@})WN*yh&5^eOeS;j#^+&r&xVm+0{xgX%PQG_jrdOI5q%E4VACtC!jnMPQShO&)dM!AH+eI0JZfAJ3? z|3(Y;M;J|VioRkVF&~88q7ZzF0t_z$U#u!p4LH|`(i%wxicQ$q+&&0rEaF1H18 z(;2%HdcavQ9#$+A8sY8m{21|^JP(4$PVE`jT(*%G-$5l9 zVJtt^Mr}UwWRH!%$ie=y5SB_4di_{Po;R_m1z0`djqSMACwb$H46H2>2LjL`-@|-Z z78^~Pq64<%Y=HhhTNd-VSazo2+E1-i~m23TmVP6|y8(1PwR|J2l>l9?;+F zNyC&vS}Q%TQPCJ-%!jAv7&srkGD~8iIZL`?oi}v?m)5}W24MtkW6!VwTq&^khUnOD zxnVXpi(3m#L z5^$4S4-g|6$Xavo?8(LwIIUbfKBh=-oT1y@@@FuFu@WwI}x6NtN0H*aYY>~loCZ4Q%{RE;IK6wjOvtB zNmgz6?TwPBLO|XEpFK|?6(yk*G1zTV3){U~!V4=I?iPFCY3IG`M?RU3QTBCwMBXf?ANGcQ72qYUWY#50BbX_evp-+4BweAiWVhcOAW`L^E@07 z?MLDY>6kndqxF!3M%{&K9Eja)JY=m1h{-+BXtq8=p9XMo9+cztkh%`S_;v~);~ht# z@-T85g@F790{0MLes2h$zc3@xh;}^KhhtyZOk=LG&^TaRhsWQ0s9D7!NVf)X^}V^@ zhS2;Fc6UEm-V0+FLK`a**i~<9ytU5S4)|lAbse@mZ|XIg+zXc4n()Aw1;O|VT?+8< z*AOw9z_{KQ+ebz+;~?m*g*0&vaN{-j?0K@Kfm$(ez71nLvV-9BNp>+kf=F5xOE1UC zTpP}g#xlDA&aY?CdsVP$WEejW8>Vgp7@h^+oaHdA9RQ+n5tzn1AsMZu|H@mpouF` zRDO#mPMAJalDj5DKC@(j(~~;1abN z?D`&^6AW5K3xs1i83oiQ1uiCsv25J~Zz#kTmVQ@?Ly6`bzb@jcH%c7|XRA9pZ7hS} z{D^>wUeI42nN{Ff8EiF$7K?VKe4Gz6=cHA5;5I>#UFqI*N9;(t!gOb&xqSRO;T%}i zLx7}R-Zg{%NNaE@j(~bq2EG~n;rMlgTf?sem>dp2tes-8R2+u!Sir$8 zl?YfeA1Qs{3%?M)+M;$;`%Ci!072`Mu^~qSemeqQug8GgzBKxPsi(misVD|DSp|K_ z)=QhZ3kUZ~kon`GHf_Slea{5IwxMHDoeoWn2Sd4peW)?OURH>TbXYzQNj4XJc^hIo z#eU_DGDNKb9X7971Uo!OVoyjxX9e^j8y=~P%y$6T10YM^<}3{Eq3OOqe>#5h1%%Rq zO`Jle3P%8zzm+P=-2ioug#FeN8hU;u5RSuD;1*vWyIp%>&&wt3U9#2nkdVs*$!-sI zyBlm$r=eOG)4zpeycLY~w0;we^@F|--T4)qFsg%lq9zeO)HGaj(fR1&bXAP1;q)3v z9xv?ljj%kU!vi-PAdsHWQy+3iVD7FCPnEJFkF6FTB?HjGeWg13?hrJ7PdFcH!1Ve8 z)7uUW{|@ZD@3idL*H~Sz4HdD4o(dq=#3s#Iuz>6^_QCt70Q|ygSQhp;^|#JJ2}f<> zd?a&VlU4<_^uH5*j(^bD}_yh3if1$U{_XqzzDsu!C(aN{AVs@BFTpu zgNldHtsvW_0wTDBiWh(ntchKYgd@gb-{vSs$y&^OU^+lXxDH+6IrrA_Vl4_(P~dgG z2XDt?C*yob`jp@Y|KzIJiFpows_$U=H9z4Qh@hyfeI+e#7!jX}N8! z6!7V2IKS+Npm-JR;{%MmJ@jkFLTBZ3O`Aq^H@H48g1mkjeE%t}!qfRY6q6Sa#*1U0 z7V%9n;Y8R0n8CMz9F}0aOl=s^)$zadh%@#Bv-lkja{ln$rXKq^2ummWJ9-#9 zk2Sztc!35`91V}=A#g1@4!4pAa2KPnCRTx?Q3I)&G#6fe8u;c6d7%S6K9TccPgrqV ztqwdhhgC-2$9=i%R33Lv|Hfu-_ z6|)Ww$6KJpoq#{s1%~2sKrP!1AD^R8)6c-u=L&?|=TOoMz{9IJ9LUST&#M}|et4+p zdUY7b>cGMh0?9p$Z_T%dMiU7~;ux6A5~0sczz&>g5F_Wn>0%L|66Bp{jyDyEVQ7S|a!zQvw>>^2Uf#@bvpfK&w*AWOr#fXX#6(bOc3Iw8pWDp2M1<8^WQ8A(dfk2RK zvmqO@K_C!pQh}%#fj}TC5EY31?xpSP?uvc8sY11ZIrsekf6n>O9p}_V*pz){l2^1t zo9vr+Xn%Flc=u?$32o82EDfK8^VZ-^2|Q?OSVLp0PY0Y0Hv4QgIUHOW9_{oBhRZt3 z@hypdU258UKxG1<%*l@>CG0cht9qc}+|yxKgA}z7{31Z(!t7-Uk0O#Sq8JdxJ`o(T zE-)j4AvOQg!SjKVQlX4ZSk!u2*z;nzZygxH-Fc%kSl(nQ>r3v5{>P zV?MeR5dF*`)1%`xIGg~_Gvw$Te?o{ScX_zN>$RqxvSH*F$*Rn|HO%92T!1>xg(O`S zgASRA-Nw%Pg=~>gjze?BWuW6_OV~Uwu{Bf~?$q${HfXY3<1%kE=DEfEc}KC;qt!AX zr-#{5cFY8Jnw@1IvUALYAz8l6uGj`!lj|F@J(5!~7i!Fe;uuAB#sx_k%BZccG_q&$Q~D8Zs9+DYsrPAR6J?BV7!n9-O(k9|_0I6bGw=}}UY>2xX{ z*Ua%`TKHExG@1f(y35o`JfC}#~i@eWEHkxLFq6&9^gTQvTX^Z{d=_lZAZ-(W)TVhaF^mzL3*BedZV zML0%L4pNMxlt}45bG#&FiNQ3_1g>nnzeS+ip-6F={CKGn=j)OaR^^Pm5@yKgc8U;wOf6VkThn)mq@(!56D%(5v$~L{X>yi6X9KB(U%1K&L;D3lem(r{ zF!DQ&?qA~S?$vkxzko)5ioN;~J-1@6y+Ke#2~k3ZCqrlo%(z8hN(80KD6R2dk;VTw zX!$l|0ll~Hm)T;A|B>MGKd{CBNO1XPY*|7_&vXpcjIY5+I&5UXL^dSkKtLYkQ^{Q6 zL2W`k9cahEY*J*s!oxCu^`!y&--7)EDO=}sNW>Ka>S+wb-v{ptp#8;ZnVw%Ax!R%c z*JrC|A}0$g0q?CH`R2`tq4|^oNj58NhT<;n>k1cyH0f3b$nbl*lQXLPCCx~MJ-s-? zFM~`yBSTAMW{r$&1KmAJ=Lq;1!Z-prW_)!46<jNRbt{Yf3U&eUh$dx|S)iKpZV`}&59XT&ya=GZTw+s~K}nOhz61~zTFf>0$!%RGut!fIAW=i+ z{W?Qq5AF|ee}w1^T`CF>5gZ_VL&UC*!1WNd0dN)~UUdYk2XqDK((qLAxN%hxZ@5N= z+C)^$j2cPRF)#sCqUHVCCdCi@vf+wpQ5{;Z^9pT zWByW3nchnVAr{U-fEN0)kG2#^zeZJVxHl=0p0Q)Wf2ngJFdr_ahbETYDK-a<-jhQa w(BcPgxTz`+L)XyyQ}n$Gj^A?a%kqo(nKgbSmw@qBs;GFMhWLR$|M7kQPakQSTL1t6 delta 134363 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 M$(sjGiAoRqFUk3cLI3~& diff --git a/src/Native/libmultihash/Makefile b/src/Native/libmultihash/Makefile index 75e80e1b4..220b9a975 100644 --- a/src/Native/libmultihash/Makefile +++ b/src/Native/libmultihash/Makefile @@ -1,6 +1,6 @@ CC = gcc -CFLAGS = -Wall -c -fPIC -O2 -g -Wno-pointer-sign -Wno-unused-function -Wno-strict-aliasing -Wno-discarded-qualifiers -CXXFLAGS = -Wall -fPIC -fpermissive -O2 -g -Wno-unused-function -Wno-strict-aliasing +CFLAGS = -Wall -c -fPIC -O2 -g -Wno-pointer-sign -Wno-unused-function -Wno-strict-aliasing -Wno-discarded-qualifiers -Wno-unused-const-variable +CXXFLAGS = -Wall -fPIC -fpermissive -O2 -g -Wno-unused-function -Wno-strict-aliasing -Wno-sign-compare -std=c++11 LDFLAGS = -shared LDLIBS = -lsodium TARGET = libmultihash.so @@ -11,9 +11,13 @@ OBJECTS = bcrypt.o blake.o blake2s.o c11.o dcrypt.o fresh.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 \ sha3/sph_haval.o sha3/sph_sha2.o sha3/sph_sha2big.o sha3/sph_blake2s.o \ - shavite3.o skein.o x11.o x15.o x17.o \ + shavite3.o skein.o x11.o x15.o x17.o x16r.o x16s.o \ Lyra2.o Lyra2RE.o Sponge.o \ - equi/endian.o equi/equi.o \ + equi/util.o equi/support/cleanse.o equi/random.o \ + equi/uint256.o equi/arith_uint256.o equi/crypto/hmac_sha512.o \ + equi/crypto/sha1.o equi/crypto/sha512.o equi/crypto/sha256.o \ + equi/crypto/hmac_sha256.o equi/crypto/equihash.o equi/crypto/ripemd160.o \ + equi/equihashverify.o \ libethash/internal.o libethash/io.o libethash/io_posix.o libethash/sha3.o all: $(TARGET) diff --git a/src/Native/libmultihash/equi/arith_uint256.cpp b/src/Native/libmultihash/equi/arith_uint256.cpp new file mode 100644 index 000000000..2e6136357 --- /dev/null +++ b/src/Native/libmultihash/equi/arith_uint256.cpp @@ -0,0 +1,260 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "arith_uint256.h" + +#include "uint256.h" +#include "utilstrencodings.h" +#include "crypto/common.h" + +#include +#include + +template +base_uint::base_uint(const std::string& str) +{ + SetHex(str); +} + +template +base_uint& base_uint::operator<<=(unsigned int shift) +{ + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) { + if (i + k + 1 < WIDTH && shift != 0) + pn[i + k + 1] |= (a.pn[i] >> (32 - shift)); + if (i + k < WIDTH) + pn[i + k] |= (a.pn[i] << shift); + } + return *this; +} + +template +base_uint& base_uint::operator>>=(unsigned int shift) +{ + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) { + if (i - k - 1 >= 0 && shift != 0) + pn[i - k - 1] |= (a.pn[i] << (32 - shift)); + if (i - k >= 0) + pn[i - k] |= (a.pn[i] >> shift); + } + return *this; +} + +template +base_uint& base_uint::operator*=(uint32_t b32) +{ + uint64_t carry = 0; + for (int i = 0; i < WIDTH; i++) { + uint64_t n = carry + (uint64_t)b32 * pn[i]; + pn[i] = n & 0xffffffff; + carry = n >> 32; + } + return *this; +} + +template +base_uint& base_uint::operator*=(const base_uint& b) +{ + base_uint a = *this; + *this = 0; + for (int j = 0; j < WIDTH; j++) { + uint64_t carry = 0; + for (int i = 0; i + j < WIDTH; i++) { + uint64_t n = carry + pn[i + j] + (uint64_t)a.pn[j] * b.pn[i]; + pn[i + j] = n & 0xffffffff; + carry = n >> 32; + } + } + return *this; +} + +template +base_uint& base_uint::operator/=(const base_uint& b) +{ + base_uint div = b; // make a copy, so we can shift. + base_uint num = *this; // make a copy, so we can subtract. + *this = 0; // the quotient. + int num_bits = num.bits(); + int div_bits = div.bits(); + if (div_bits == 0) + throw uint_error("Division by zero"); + if (div_bits > num_bits) // the result is certainly 0. + return *this; + int shift = num_bits - div_bits; + div <<= shift; // shift so that div and num align. + while (shift >= 0) { + if (num >= div) { + num -= div; + pn[shift / 32] |= (1 << (shift & 31)); // set a bit of the result. + } + div >>= 1; // shift back. + shift--; + } + // num now contains the remainder of the division. + return *this; +} + +template +int base_uint::CompareTo(const base_uint& b) const +{ + for (int i = WIDTH - 1; i >= 0; i--) { + if (pn[i] < b.pn[i]) + return -1; + if (pn[i] > b.pn[i]) + return 1; + } + return 0; +} + +template +bool base_uint::EqualTo(uint64_t b) const +{ + for (int i = WIDTH - 1; i >= 2; i--) { + if (pn[i]) + return false; + } + if (pn[1] != (b >> 32)) + return false; + if (pn[0] != (b & 0xfffffffful)) + return false; + return true; +} + +template +double base_uint::getdouble() const +{ + double ret = 0.0; + double fact = 1.0; + for (int i = 0; i < WIDTH; i++) { + ret += fact * pn[i]; + fact *= 4294967296.0; + } + return ret; +} + +template +std::string base_uint::GetHex() const +{ + return ArithToUint256(*this).GetHex(); +} + +template +void base_uint::SetHex(const char* psz) +{ + *this = UintToArith256(uint256S(psz)); +} + +template +void base_uint::SetHex(const std::string& str) +{ + SetHex(str.c_str()); +} + +template +std::string base_uint::ToString() const +{ + return (GetHex()); +} + +template +unsigned int base_uint::bits() const +{ + for (int pos = WIDTH - 1; pos >= 0; pos--) { + if (pn[pos]) { + for (int bits = 31; bits > 0; bits--) { + if (pn[pos] & 1 << bits) + return 32 * pos + bits + 1; + } + return 32 * pos + 1; + } + } + return 0; +} + +// Explicit instantiations for base_uint<256> +template base_uint<256>::base_uint(const std::string&); +template base_uint<256>& base_uint<256>::operator<<=(unsigned int); +template base_uint<256>& base_uint<256>::operator>>=(unsigned int); +template base_uint<256>& base_uint<256>::operator*=(uint32_t b32); +template base_uint<256>& base_uint<256>::operator*=(const base_uint<256>& b); +template base_uint<256>& base_uint<256>::operator/=(const base_uint<256>& b); +template int base_uint<256>::CompareTo(const base_uint<256>&) const; +template bool base_uint<256>::EqualTo(uint64_t) const; +template double base_uint<256>::getdouble() const; +template std::string base_uint<256>::GetHex() const; +template std::string base_uint<256>::ToString() const; +template void base_uint<256>::SetHex(const char*); +template void base_uint<256>::SetHex(const std::string&); +template unsigned int base_uint<256>::bits() const; + +// This implementation directly uses shifts instead of going +// through an intermediate MPI representation. +arith_uint256& arith_uint256::SetCompact(uint32_t nCompact, bool* pfNegative, bool* pfOverflow) +{ + int nSize = nCompact >> 24; + uint32_t nWord = nCompact & 0x007fffff; + if (nSize <= 3) { + nWord >>= 8 * (3 - nSize); + *this = nWord; + } else { + *this = nWord; + *this <<= 8 * (nSize - 3); + } + if (pfNegative) + *pfNegative = nWord != 0 && (nCompact & 0x00800000) != 0; + if (pfOverflow) + *pfOverflow = nWord != 0 && ((nSize > 34) || + (nWord > 0xff && nSize > 33) || + (nWord > 0xffff && nSize > 32)); + return *this; +} + +uint32_t arith_uint256::GetCompact(bool fNegative) const +{ + int nSize = (bits() + 7) / 8; + uint32_t nCompact = 0; + if (nSize <= 3) { + nCompact = GetLow64() << 8 * (3 - nSize); + } else { + arith_uint256 bn = *this >> 8 * (nSize - 3); + nCompact = bn.GetLow64(); + } + // The 0x00800000 bit denotes the sign. + // Thus, if it is already set, divide the mantissa by 256 and increase the exponent. + if (nCompact & 0x00800000) { + nCompact >>= 8; + nSize++; + } + assert((nCompact & ~0x007fffff) == 0); + assert(nSize < 256); + nCompact |= nSize << 24; + nCompact |= (fNegative && (nCompact & 0x007fffff) ? 0x00800000 : 0); + return nCompact; +} + +uint256 ArithToUint256(const arith_uint256 &a) +{ + uint256 b; + for(int x=0; x +#include +#include +#include +#include +#include + +class uint256; + +class uint_error : public std::runtime_error { +public: + explicit uint_error(const std::string& str) : std::runtime_error(str) {} +}; + +/** Template base class for unsigned big integers. */ +template +class base_uint +{ +protected: + enum { WIDTH=BITS/32 }; + uint32_t pn[WIDTH]; +public: + + base_uint() + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + } + + base_uint(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + base_uint& operator=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + base_uint(uint64_t b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + explicit base_uint(const std::string& str); + + bool operator!() const + { + for (int i = 0; i < WIDTH; i++) + if (pn[i] != 0) + return false; + return true; + } + + const base_uint operator~() const + { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + return ret; + } + + const base_uint operator-() const + { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + ret++; + return ret; + } + + double getdouble() const; + + base_uint& operator=(uint64_t b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + base_uint& operator^=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] ^= b.pn[i]; + return *this; + } + + base_uint& operator&=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] &= b.pn[i]; + return *this; + } + + base_uint& operator|=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] |= b.pn[i]; + return *this; + } + + base_uint& operator^=(uint64_t b) + { + pn[0] ^= (unsigned int)b; + pn[1] ^= (unsigned int)(b >> 32); + return *this; + } + + base_uint& operator|=(uint64_t b) + { + pn[0] |= (unsigned int)b; + pn[1] |= (unsigned int)(b >> 32); + return *this; + } + + base_uint& operator<<=(unsigned int shift); + base_uint& operator>>=(unsigned int shift); + + base_uint& operator+=(const base_uint& b) + { + uint64_t carry = 0; + for (int i = 0; i < WIDTH; i++) + { + uint64_t n = carry + pn[i] + b.pn[i]; + pn[i] = n & 0xffffffff; + carry = n >> 32; + } + return *this; + } + + base_uint& operator-=(const base_uint& b) + { + *this += -b; + return *this; + } + + base_uint& operator+=(uint64_t b64) + { + base_uint b; + b = b64; + *this += b; + return *this; + } + + base_uint& operator-=(uint64_t b64) + { + base_uint b; + b = b64; + *this += -b; + return *this; + } + + base_uint& operator*=(uint32_t b32); + base_uint& operator*=(const base_uint& b); + base_uint& operator/=(const base_uint& b); + + base_uint& operator++() + { + // prefix operator + int i = 0; + while (++pn[i] == 0 && i < WIDTH-1) + i++; + return *this; + } + + const base_uint operator++(int) + { + // postfix operator + const base_uint ret = *this; + ++(*this); + return ret; + } + + base_uint& operator--() + { + // prefix operator + int i = 0; + while (--pn[i] == (uint32_t)-1 && i < WIDTH-1) + i++; + return *this; + } + + const base_uint operator--(int) + { + // postfix operator + const base_uint ret = *this; + --(*this); + return ret; + } + + int CompareTo(const base_uint& b) const; + bool EqualTo(uint64_t b) const; + + friend inline const base_uint operator+(const base_uint& a, const base_uint& b) { return base_uint(a) += b; } + friend inline const base_uint operator-(const base_uint& a, const base_uint& b) { return base_uint(a) -= b; } + friend inline const base_uint operator*(const base_uint& a, const base_uint& b) { return base_uint(a) *= b; } + friend inline const base_uint operator/(const base_uint& a, const base_uint& b) { return base_uint(a) /= b; } + friend inline const base_uint operator|(const base_uint& a, const base_uint& b) { return base_uint(a) |= b; } + friend inline const base_uint operator&(const base_uint& a, const base_uint& b) { return base_uint(a) &= b; } + friend inline const base_uint operator^(const base_uint& a, const base_uint& b) { return base_uint(a) ^= b; } + friend inline const base_uint operator>>(const base_uint& a, int shift) { return base_uint(a) >>= shift; } + friend inline const base_uint operator<<(const base_uint& a, int shift) { return base_uint(a) <<= shift; } + friend inline const base_uint operator*(const base_uint& a, uint32_t b) { return base_uint(a) *= b; } + friend inline bool operator==(const base_uint& a, const base_uint& b) { return memcmp(a.pn, b.pn, sizeof(a.pn)) == 0; } + friend inline bool operator!=(const base_uint& a, const base_uint& b) { return memcmp(a.pn, b.pn, sizeof(a.pn)) != 0; } + friend inline bool operator>(const base_uint& a, const base_uint& b) { return a.CompareTo(b) > 0; } + friend inline bool operator<(const base_uint& a, const base_uint& b) { return a.CompareTo(b) < 0; } + friend inline bool operator>=(const base_uint& a, const base_uint& b) { return a.CompareTo(b) >= 0; } + friend inline bool operator<=(const base_uint& a, const base_uint& b) { return a.CompareTo(b) <= 0; } + friend inline bool operator==(const base_uint& a, uint64_t b) { return a.EqualTo(b); } + friend inline bool operator!=(const base_uint& a, uint64_t b) { return !a.EqualTo(b); } + + std::string GetHex() const; + void SetHex(const char* psz); + void SetHex(const std::string& str); + std::string ToString() const; + + unsigned int size() const + { + return sizeof(pn); + } + + /** + * Returns the position of the highest bit set plus one, or zero if the + * value is zero. + */ + unsigned int bits() const; + + uint64_t GetLow64() const + { + assert(WIDTH >= 2); + return pn[0] | (uint64_t)pn[1] << 32; + } +}; + +/** 256-bit unsigned big integer. */ +class arith_uint256 : public base_uint<256> { +public: + arith_uint256() {} + arith_uint256(const base_uint<256>& b) : base_uint<256>(b) {} + arith_uint256(uint64_t b) : base_uint<256>(b) {} + explicit arith_uint256(const std::string& str) : base_uint<256>(str) {} + + /** + * The "compact" format is a representation of a whole + * number N using an unsigned 32bit number similar to a + * floating point format. + * The most significant 8 bits are the unsigned exponent of base 256. + * This exponent can be thought of as "number of bytes of N". + * The lower 23 bits are the mantissa. + * Bit number 24 (0x800000) represents the sign of N. + * N = (-1^sign) * mantissa * 256^(exponent-3) + * + * Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn(). + * MPI uses the most significant bit of the first byte as sign. + * Thus 0x1234560000 is compact (0x05123456) + * and 0xc0de000000 is compact (0x0600c0de) + * + * Bitcoin only uses this "compact" format for encoding difficulty + * targets, which are unsigned 256bit quantities. Thus, all the + * complexities of the sign bit and using base 256 are probably an + * implementation accident. + */ + arith_uint256& SetCompact(uint32_t nCompact, bool *pfNegative = NULL, bool *pfOverflow = NULL); + uint32_t GetCompact(bool fNegative = false) const; + + friend uint256 ArithToUint256(const arith_uint256 &); + friend arith_uint256 UintToArith256(const uint256 &); +}; + +uint256 ArithToUint256(const arith_uint256 &); +arith_uint256 UintToArith256(const uint256 &); + +#endif // BITCOIN_ARITH_UINT256_H diff --git a/src/Native/libmultihash/equi/crypto/common.h b/src/Native/libmultihash/equi/crypto/common.h new file mode 100644 index 000000000..e0a22646c --- /dev/null +++ b/src/Native/libmultihash/equi/crypto/common.h @@ -0,0 +1,111 @@ +// Copyright (c) 2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CRYPTO_COMMON_H +#define BITCOIN_CRYPTO_COMMON_H + +#include +#include + +#include "sodium.h" + +#ifndef _WIN32 +#include +#else +#include "../../portable_endian.h" +#endif + +#if defined(NDEBUG) +# error "Zcash cannot be compiled without assertions." +#endif + +uint16_t static inline ReadLE16(const unsigned char* ptr) +{ + return le16toh(*((uint16_t*)ptr)); +} + +uint32_t static inline ReadLE32(const unsigned char* ptr) +{ + return le32toh(*((uint32_t*)ptr)); +} + +uint64_t static inline ReadLE64(const unsigned char* ptr) +{ + return le64toh(*((uint64_t*)ptr)); +} + +void static inline WriteLE16(unsigned char* ptr, uint16_t x) +{ + *((uint16_t*)ptr) = htole16(x); +} + +void static inline WriteLE32(unsigned char* ptr, uint32_t x) +{ + *((uint32_t*)ptr) = htole32(x); +} + +void static inline WriteLE64(unsigned char* ptr, uint64_t x) +{ + *((uint64_t*)ptr) = htole64(x); +} + +uint32_t static inline ReadBE32(const unsigned char* ptr) +{ + return be32toh(*((uint32_t*)ptr)); +} + +uint64_t static inline ReadBE64(const unsigned char* ptr) +{ + return be64toh(*((uint64_t*)ptr)); +} + +void static inline WriteBE32(unsigned char* ptr, uint32_t x) +{ + *((uint32_t*)ptr) = htobe32(x); +} + +void static inline WriteBE64(unsigned char* ptr, uint64_t x) +{ + *((uint64_t*)ptr) = htobe64(x); +} + +int inline init_and_check_sodium() +{ + if (sodium_init() == -1) { + return -1; + } + + // What follows is a runtime test that ensures the version of libsodium + // we're linked against checks that signatures are canonical (s < L). + const unsigned char message[1] = { 0 }; + + unsigned char pk[crypto_sign_PUBLICKEYBYTES]; + unsigned char sk[crypto_sign_SECRETKEYBYTES]; + unsigned char sig[crypto_sign_BYTES]; + + crypto_sign_keypair(pk, sk); + crypto_sign_detached(sig, NULL, message, sizeof(message), sk); + + assert(crypto_sign_verify_detached(sig, message, sizeof(message), pk) == 0); + + // Copied from libsodium/crypto_sign/ed25519/ref10/open.c + static const unsigned char L[32] = + { 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, + 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 }; + + // Add L to S, which starts at sig[32]. + unsigned int s = 0; + for (size_t i = 0; i < 32; i++) { + s = sig[32 + i] + L[i] + (s >> 8); + sig[32 + i] = s & 0xff; + } + + assert(crypto_sign_verify_detached(sig, message, sizeof(message), pk) != 0); + + return 0; +} + +#endif // BITCOIN_CRYPTO_COMMON_H diff --git a/src/Native/libmultihash/equi/crypto/equihash.cpp b/src/Native/libmultihash/equi/crypto/equihash.cpp new file mode 100644 index 000000000..2db961502 --- /dev/null +++ b/src/Native/libmultihash/equi/crypto/equihash.cpp @@ -0,0 +1,824 @@ +// Copyright (c) 2016 Jack Grigg +// Copyright (c) 2016 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Implementation of the Equihash Proof-of-Work algorithm. +// +// Reference +// ========= +// Alex Biryukov and Dmitry Khovratovich +// Equihash: Asymmetric Proof-of-Work Based on the Generalized Birthday Problem +// NDSS ’16, 21-24 February 2016, San Diego, CA, USA +// https://www.internetsociety.org/sites/default/files/blogs-media/equihash-asymmetric-proof-of-work-based-generalized-birthday-problem.pdf + +#if defined(HAVE_CONFIG_H) +#include "config/bitcoin-config.h" +#endif + +#ifndef _WIN32 +#include +#else +#include "../../portable_endian.h" +#include +#endif + +#include "equihash.h" +#include +#include +#include + +//#include +#include "../util.h" + +EhSolverCancelledException solver_cancelled; + +template +int Equihash::InitialiseState(eh_HashState& base_state) +{ + uint32_t le_N = htole32(N); + uint32_t le_K = htole32(K); + unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {}; + memcpy(personalization, "ZcashPoW", 8); + memcpy(personalization+8, &le_N, 4); + memcpy(personalization+12, &le_K, 4); + return crypto_generichash_blake2b_init_salt_personal(&base_state, + NULL, 0, // No key. + (512/N)*N/8, + NULL, // No salt. + personalization); +} + +void GenerateHash(const eh_HashState& base_state, eh_index g, + unsigned char* hash, size_t hLen) +{ + eh_HashState state; + state = base_state; + eh_index lei = htole32(g); + crypto_generichash_blake2b_update(&state, (const unsigned char*) &lei, + sizeof(eh_index)); + crypto_generichash_blake2b_final(&state, hash, hLen); +} + +void ExpandArray(const unsigned char* in, size_t in_len, + unsigned char* out, size_t out_len, + size_t bit_len, size_t byte_pad) +{ + assert(bit_len >= 8); + assert(8*sizeof(uint32_t) >= 7+bit_len); + + size_t out_width { (bit_len+7)/8 + byte_pad }; + assert(out_len == 8*out_width*in_len/bit_len); + + uint32_t bit_len_mask { ((uint32_t)1 << bit_len) - 1 }; + + // The acc_bits least-significant bits of acc_value represent a bit sequence + // in big-endian order. + size_t acc_bits = 0; + uint32_t acc_value = 0; + + size_t j = 0; + for (size_t i = 0; i < in_len; i++) { + acc_value = (acc_value << 8) | in[i]; + acc_bits += 8; + + // When we have bit_len or more bits in the accumulator, write the next + // output element. + if (acc_bits >= bit_len) { + acc_bits -= bit_len; + for (size_t x = 0; x < byte_pad; x++) { + out[j+x] = 0; + } + for (size_t x = byte_pad; x < out_width; x++) { + out[j+x] = ( + // Big-endian + acc_value >> (acc_bits+(8*(out_width-x-1))) + ) & ( + // Apply bit_len_mask across byte boundaries + (bit_len_mask >> (8*(out_width-x-1))) & 0xFF + ); + } + j += out_width; + } + } +} + +void CompressArray(const unsigned char* in, size_t in_len, + unsigned char* out, size_t out_len, + size_t bit_len, size_t byte_pad) +{ + assert(bit_len >= 8); + assert(8*sizeof(uint32_t) >= 7+bit_len); + + size_t in_width { (bit_len+7)/8 + byte_pad }; + assert(out_len == bit_len*in_len/(8*in_width)); + + uint32_t bit_len_mask { ((uint32_t)1 << bit_len) - 1 }; + + // The acc_bits least-significant bits of acc_value represent a bit sequence + // in big-endian order. + size_t acc_bits = 0; + uint32_t acc_value = 0; + + size_t j = 0; + for (size_t i = 0; i < out_len; i++) { + // When we have fewer than 8 bits left in the accumulator, read the next + // input element. + if (acc_bits < 8) { + acc_value = acc_value << bit_len; + for (size_t x = byte_pad; x < in_width; x++) { + acc_value = acc_value | ( + ( + // Apply bit_len_mask across byte boundaries + in[j+x] & ((bit_len_mask >> (8*(in_width-x-1))) & 0xFF) + ) << (8*(in_width-x-1))); // Big-endian + } + j += in_width; + acc_bits += bit_len; + } + + acc_bits -= 8; + out[i] = (acc_value >> acc_bits) & 0xFF; + } +} + +// Big-endian so that lexicographic array comparison is equivalent to integer +// comparison +void EhIndexToArray(const eh_index i, unsigned char* array) +{ + BOOST_STATIC_ASSERT(sizeof(eh_index) == 4); + eh_index bei = htobe32(i); + memcpy(array, &bei, sizeof(eh_index)); +} + +// Big-endian so that lexicographic array comparison is equivalent to integer +// comparison +eh_index ArrayToEhIndex(const unsigned char* array) +{ + BOOST_STATIC_ASSERT(sizeof(eh_index) == 4); + eh_index bei; + memcpy(&bei, array, sizeof(eh_index)); + return be32toh(bei); +} + +eh_trunc TruncateIndex(const eh_index i, const unsigned int ilen) +{ + // Truncate to 8 bits + BOOST_STATIC_ASSERT(sizeof(eh_trunc) == 1); + return (i >> (ilen - 8)) & 0xff; +} + +eh_index UntruncateIndex(const eh_trunc t, const eh_index r, const unsigned int ilen) +{ + eh_index i{t}; + return (i << (ilen - 8)) | r; +} + +std::vector GetIndicesFromMinimal(std::vector minimal, + size_t cBitLen) +{ + assert(((cBitLen+1)+7)/8 <= sizeof(eh_index)); + size_t lenIndices { 8*sizeof(eh_index)*minimal.size()/(cBitLen+1) }; + size_t bytePad { sizeof(eh_index) - ((cBitLen+1)+7)/8 }; + std::vector array(lenIndices); + ExpandArray(minimal.data(), minimal.size(), + array.data(), lenIndices, cBitLen+1, bytePad); + std::vector ret; + for (int i = 0; i < lenIndices; i += sizeof(eh_index)) { + ret.push_back(ArrayToEhIndex(array.data()+i)); + } + return ret; +} + +std::vector GetMinimalFromIndices(std::vector indices, + size_t cBitLen) +{ + assert(((cBitLen+1)+7)/8 <= sizeof(eh_index)); + size_t lenIndices { indices.size()*sizeof(eh_index) }; + size_t minLen { (cBitLen+1)*lenIndices/(8*sizeof(eh_index)) }; + size_t bytePad { sizeof(eh_index) - ((cBitLen+1)+7)/8 }; + std::vector array(lenIndices); + for (int i = 0; i < indices.size(); i++) { + EhIndexToArray(indices[i], array.data()+(i*sizeof(eh_index))); + } + std::vector ret(minLen); + CompressArray(array.data(), lenIndices, + ret.data(), minLen, cBitLen+1, bytePad); + return ret; +} + +template +StepRow::StepRow(const unsigned char* hashIn, size_t hInLen, + size_t hLen, size_t cBitLen) +{ + assert(hLen <= WIDTH); + ExpandArray(hashIn, hInLen, hash, hLen, cBitLen); +} + +template template +StepRow::StepRow(const StepRow& a) +{ + BOOST_STATIC_ASSERT(W <= WIDTH); + std::copy(a.hash, a.hash+W, hash); +} + +template +FullStepRow::FullStepRow(const unsigned char* hashIn, size_t hInLen, + size_t hLen, size_t cBitLen, eh_index i) : + StepRow {hashIn, hInLen, hLen, cBitLen} +{ + EhIndexToArray(i, hash+hLen); +} + +template template +FullStepRow::FullStepRow(const FullStepRow& a, const FullStepRow& b, size_t len, size_t lenIndices, int trim) : + StepRow {a} +{ + assert(len+lenIndices <= W); + assert(len-trim+(2*lenIndices) <= WIDTH); + for (int i = trim; i < len; i++) + hash[i-trim] = a.hash[i] ^ b.hash[i]; + if (a.IndicesBefore(b, len, lenIndices)) { + std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim); + std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim+lenIndices); + } else { + std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim); + std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim+lenIndices); + } +} + +template +FullStepRow& FullStepRow::operator=(const FullStepRow& a) +{ + std::copy(a.hash, a.hash+WIDTH, hash); + return *this; +} + +template +bool StepRow::IsZero(size_t len) +{ + // This doesn't need to be constant time. + for (int i = 0; i < len; i++) { + if (hash[i] != 0) + return false; + } + return true; +} + +template +std::vector FullStepRow::GetIndices(size_t len, size_t lenIndices, + size_t cBitLen) const +{ + assert(((cBitLen+1)+7)/8 <= sizeof(eh_index)); + size_t minLen { (cBitLen+1)*lenIndices/(8*sizeof(eh_index)) }; + size_t bytePad { sizeof(eh_index) - ((cBitLen+1)+7)/8 }; + std::vector ret(minLen); + CompressArray(hash+len, lenIndices, ret.data(), minLen, cBitLen+1, bytePad); + return ret; +} + +template +bool HasCollision(StepRow& a, StepRow& b, int l) +{ + // This doesn't need to be constant time. + for (int j = 0; j < l; j++) { + if (a.hash[j] != b.hash[j]) + return false; + } + return true; +} + +template +TruncatedStepRow::TruncatedStepRow(const unsigned char* hashIn, size_t hInLen, + size_t hLen, size_t cBitLen, + eh_index i, unsigned int ilen) : + StepRow {hashIn, hInLen, hLen, cBitLen} +{ + hash[hLen] = TruncateIndex(i, ilen); +} + +template template +TruncatedStepRow::TruncatedStepRow(const TruncatedStepRow& a, const TruncatedStepRow& b, size_t len, size_t lenIndices, int trim) : + StepRow {a} +{ + assert(len+lenIndices <= W); + assert(len-trim+(2*lenIndices) <= WIDTH); + for (int i = trim; i < len; i++) + hash[i-trim] = a.hash[i] ^ b.hash[i]; + if (a.IndicesBefore(b, len, lenIndices)) { + std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim); + std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim+lenIndices); + } else { + std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim); + std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim+lenIndices); + } +} + +template +TruncatedStepRow& TruncatedStepRow::operator=(const TruncatedStepRow& a) +{ + std::copy(a.hash, a.hash+WIDTH, hash); + return *this; +} + +template +std::shared_ptr TruncatedStepRow::GetTruncatedIndices(size_t len, size_t lenIndices) const +{ + std::shared_ptr p (new eh_trunc[lenIndices], std::default_delete()); + std::copy(hash+len, hash+len+lenIndices, p.get()); + return p; +} + +#ifdef ENABLE_MINING +template +bool Equihash::BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled) +{ + eh_index init_size { 1 << (CollisionBitLength + 1) }; + + // 1) Generate first list + LogPrint("pow", "Generating first list\n"); + size_t hashLen = HashLength; + size_t lenIndices = sizeof(eh_index); + std::vector> X; + X.reserve(init_size); + unsigned char tmpHash[HashOutput]; + for (eh_index g = 0; X.size() < init_size; g++) { + GenerateHash(base_state, g, tmpHash, HashOutput); + for (eh_index i = 0; i < IndicesPerHashOutput && X.size() < init_size; i++) { + X.emplace_back(tmpHash+(i*N/8), N/8, HashLength, + CollisionBitLength, (g*IndicesPerHashOutput)+i); + } + if (cancelled(ListGeneration)) throw solver_cancelled; + } + + // 3) Repeat step 2 until 2n/(k+1) bits remain + for (int r = 1; r < K && X.size() > 0; r++) { + LogPrint("pow", "Round %d:\n", r); + // 2a) Sort the list + LogPrint("pow", "- Sorting list\n"); + std::sort(X.begin(), X.end(), CompareSR(CollisionByteLength)); + if (cancelled(ListSorting)) throw solver_cancelled; + + LogPrint("pow", "- Finding collisions\n"); + int i = 0; + int posFree = 0; + std::vector> Xc; + while (i < X.size() - 1) { + // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits + int j = 1; + while (i+j < X.size() && + HasCollision(X[i], X[i+j], CollisionByteLength)) { + j++; + } + + // 2c) Calculate tuples (X_i ^ X_j, (i, j)) + for (int l = 0; l < j - 1; l++) { + for (int m = l + 1; m < j; m++) { + if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices)) { + Xc.emplace_back(X[i+l], X[i+m], hashLen, lenIndices, CollisionByteLength); + } + } + } + + // 2d) Store tuples on the table in-place if possible + while (posFree < i+j && Xc.size() > 0) { + X[posFree++] = Xc.back(); + Xc.pop_back(); + } + + i += j; + if (cancelled(ListColliding)) throw solver_cancelled; + } + + // 2e) Handle edge case where final table entry has no collision + while (posFree < X.size() && Xc.size() > 0) { + X[posFree++] = Xc.back(); + Xc.pop_back(); + } + + if (Xc.size() > 0) { + // 2f) Add overflow to end of table + X.insert(X.end(), Xc.begin(), Xc.end()); + } else if (posFree < X.size()) { + // 2g) Remove empty space at the end + X.erase(X.begin()+posFree, X.end()); + X.shrink_to_fit(); + } + + hashLen -= CollisionByteLength; + lenIndices *= 2; + if (cancelled(RoundEnd)) throw solver_cancelled; + } + + // k+1) Find a collision on last 2n(k+1) bits + LogPrint("pow", "Final round:\n"); + if (X.size() > 1) { + LogPrint("pow", "- Sorting list\n"); + std::sort(X.begin(), X.end(), CompareSR(hashLen)); + if (cancelled(FinalSorting)) throw solver_cancelled; + LogPrint("pow", "- Finding collisions\n"); + int i = 0; + while (i < X.size() - 1) { + int j = 1; + while (i+j < X.size() && + HasCollision(X[i], X[i+j], hashLen)) { + j++; + } + + for (int l = 0; l < j - 1; l++) { + for (int m = l + 1; m < j; m++) { + FullStepRow res(X[i+l], X[i+m], hashLen, lenIndices, 0); + if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices)) { + auto soln = res.GetIndices(hashLen, 2*lenIndices, CollisionBitLength); + assert(soln.size() == equihash_solution_size(N, K)); + if (validBlock(soln)) { + return true; + } + } + } + } + + i += j; + if (cancelled(FinalColliding)) throw solver_cancelled; + } + } else + LogPrint("pow", "- List is empty\n"); + + return false; +} + +template +void CollideBranches(std::vector>& X, const size_t hlen, const size_t lenIndices, const unsigned int clen, const unsigned int ilen, const eh_trunc lt, const eh_trunc rt) +{ + int i = 0; + int posFree = 0; + std::vector> Xc; + while (i < X.size() - 1) { + // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits + int j = 1; + while (i+j < X.size() && + HasCollision(X[i], X[i+j], clen)) { + j++; + } + + // 2c) Calculate tuples (X_i ^ X_j, (i, j)) + for (int l = 0; l < j - 1; l++) { + for (int m = l + 1; m < j; m++) { + if (DistinctIndices(X[i+l], X[i+m], hlen, lenIndices)) { + if (IsValidBranch(X[i+l], hlen, ilen, lt) && IsValidBranch(X[i+m], hlen, ilen, rt)) { + Xc.emplace_back(X[i+l], X[i+m], hlen, lenIndices, clen); + } else if (IsValidBranch(X[i+m], hlen, ilen, lt) && IsValidBranch(X[i+l], hlen, ilen, rt)) { + Xc.emplace_back(X[i+m], X[i+l], hlen, lenIndices, clen); + } + } + } + } + + // 2d) Store tuples on the table in-place if possible + while (posFree < i+j && Xc.size() > 0) { + X[posFree++] = Xc.back(); + Xc.pop_back(); + } + + i += j; + } + + // 2e) Handle edge case where final table entry has no collision + while (posFree < X.size() && Xc.size() > 0) { + X[posFree++] = Xc.back(); + Xc.pop_back(); + } + + if (Xc.size() > 0) { + // 2f) Add overflow to end of table + X.insert(X.end(), Xc.begin(), Xc.end()); + } else if (posFree < X.size()) { + // 2g) Remove empty space at the end + X.erase(X.begin()+posFree, X.end()); + X.shrink_to_fit(); + } +} + +template +bool Equihash::OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled) +{ + eh_index init_size { 1 << (CollisionBitLength + 1) }; + eh_index recreate_size { UntruncateIndex(1, 0, CollisionBitLength + 1) }; + + // First run the algorithm with truncated indices + + const eh_index soln_size { 1 << K }; + std::vector> partialSolns; + int invalidCount = 0; + { + + // 1) Generate first list + LogPrint("pow", "Generating first list\n"); + size_t hashLen = HashLength; + size_t lenIndices = sizeof(eh_trunc); + std::vector> Xt; + Xt.reserve(init_size); + unsigned char tmpHash[HashOutput]; + for (eh_index g = 0; Xt.size() < init_size; g++) { + GenerateHash(base_state, g, tmpHash, HashOutput); + for (eh_index i = 0; i < IndicesPerHashOutput && Xt.size() < init_size; i++) { + Xt.emplace_back(tmpHash+(i*N/8), N/8, HashLength, CollisionBitLength, + (g*IndicesPerHashOutput)+i, CollisionBitLength + 1); + } + if (cancelled(ListGeneration)) throw solver_cancelled; + } + + // 3) Repeat step 2 until 2n/(k+1) bits remain + for (int r = 1; r < K && Xt.size() > 0; r++) { + LogPrint("pow", "Round %d:\n", r); + // 2a) Sort the list + LogPrint("pow", "- Sorting list\n"); + std::sort(Xt.begin(), Xt.end(), CompareSR(CollisionByteLength)); + if (cancelled(ListSorting)) throw solver_cancelled; + + LogPrint("pow", "- Finding collisions\n"); + int i = 0; + int posFree = 0; + std::vector> Xc; + while (i < Xt.size() - 1) { + // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits + int j = 1; + while (i+j < Xt.size() && + HasCollision(Xt[i], Xt[i+j], CollisionByteLength)) { + j++; + } + + // 2c) Calculate tuples (X_i ^ X_j, (i, j)) + bool checking_for_zero = (i == 0 && Xt[0].IsZero(hashLen)); + for (int l = 0; l < j - 1; l++) { + for (int m = l + 1; m < j; m++) { + // We truncated, so don't check for distinct indices here + TruncatedStepRow Xi {Xt[i+l], Xt[i+m], + hashLen, lenIndices, + CollisionByteLength}; + if (!(Xi.IsZero(hashLen-CollisionByteLength) && + IsProbablyDuplicate(Xi.GetTruncatedIndices(hashLen-CollisionByteLength, 2*lenIndices), + 2*lenIndices))) { + Xc.emplace_back(Xi); + } + } + } + + // 2d) Store tuples on the table in-place if possible + while (posFree < i+j && Xc.size() > 0) { + Xt[posFree++] = Xc.back(); + Xc.pop_back(); + } + + i += j; + if (cancelled(ListColliding)) throw solver_cancelled; + } + + // 2e) Handle edge case where final table entry has no collision + while (posFree < Xt.size() && Xc.size() > 0) { + Xt[posFree++] = Xc.back(); + Xc.pop_back(); + } + + if (Xc.size() > 0) { + // 2f) Add overflow to end of table + Xt.insert(Xt.end(), Xc.begin(), Xc.end()); + } else if (posFree < Xt.size()) { + // 2g) Remove empty space at the end + Xt.erase(Xt.begin()+posFree, Xt.end()); + Xt.shrink_to_fit(); + } + + hashLen -= CollisionByteLength; + lenIndices *= 2; + if (cancelled(RoundEnd)) throw solver_cancelled; + } + + // k+1) Find a collision on last 2n(k+1) bits + LogPrint("pow", "Final round:\n"); + if (Xt.size() > 1) { + LogPrint("pow", "- Sorting list\n"); + std::sort(Xt.begin(), Xt.end(), CompareSR(hashLen)); + if (cancelled(FinalSorting)) throw solver_cancelled; + LogPrint("pow", "- Finding collisions\n"); + int i = 0; + while (i < Xt.size() - 1) { + int j = 1; + while (i+j < Xt.size() && + HasCollision(Xt[i], Xt[i+j], hashLen)) { + j++; + } + + for (int l = 0; l < j - 1; l++) { + for (int m = l + 1; m < j; m++) { + TruncatedStepRow res(Xt[i+l], Xt[i+m], + hashLen, lenIndices, 0); + auto soln = res.GetTruncatedIndices(hashLen, 2*lenIndices); + if (!IsProbablyDuplicate(soln, 2*lenIndices)) { + partialSolns.push_back(soln); + } + } + } + + i += j; + if (cancelled(FinalColliding)) throw solver_cancelled; + } + } else + LogPrint("pow", "- List is empty\n"); + + } // Ensure Xt goes out of scope and is destroyed + + LogPrint("pow", "Found %d partial solutions\n", partialSolns.size()); + + // Now for each solution run the algorithm again to recreate the indices + LogPrint("pow", "Culling solutions\n"); + for (std::shared_ptr partialSoln : partialSolns) { + std::set> solns; + size_t hashLen; + size_t lenIndices; + unsigned char tmpHash[HashOutput]; + std::vector>>> X; + X.reserve(K+1); + + // 3) Repeat steps 1 and 2 for each partial index + for (eh_index i = 0; i < soln_size; i++) { + // 1) Generate first list of possibilities + std::vector> icv; + icv.reserve(recreate_size); + for (eh_index j = 0; j < recreate_size; j++) { + eh_index newIndex { UntruncateIndex(partialSoln.get()[i], j, CollisionBitLength + 1) }; + if (j == 0 || newIndex % IndicesPerHashOutput == 0) { + GenerateHash(base_state, newIndex/IndicesPerHashOutput, + tmpHash, HashOutput); + } + icv.emplace_back(tmpHash+((newIndex % IndicesPerHashOutput) * N/8), + N/8, HashLength, CollisionBitLength, newIndex); + if (cancelled(PartialGeneration)) throw solver_cancelled; + } + boost::optional>> ic = icv; + + // 2a) For each pair of lists: + hashLen = HashLength; + lenIndices = sizeof(eh_index); + size_t rti = i; + for (size_t r = 0; r <= K; r++) { + // 2b) Until we are at the top of a subtree: + if (r < X.size()) { + if (X[r]) { + // 2c) Merge the lists + ic->reserve(ic->size() + X[r]->size()); + ic->insert(ic->end(), X[r]->begin(), X[r]->end()); + std::sort(ic->begin(), ic->end(), CompareSR(hashLen)); + if (cancelled(PartialSorting)) throw solver_cancelled; + size_t lti = rti-(1<size() == 0) + goto invalidsolution; + + X[r] = boost::none; + hashLen -= CollisionByteLength; + lenIndices *= 2; + rti = lti; + } else { + X[r] = *ic; + break; + } + } else { + X.push_back(ic); + break; + } + if (cancelled(PartialSubtreeEnd)) throw solver_cancelled; + } + if (cancelled(PartialIndexEnd)) throw solver_cancelled; + } + + // We are at the top of the tree + assert(X.size() == K+1); + for (FullStepRow row : *X[K]) { + auto soln = row.GetIndices(hashLen, lenIndices, CollisionBitLength); + assert(soln.size() == equihash_solution_size(N, K)); + solns.insert(soln); + } + for (auto soln : solns) { + if (validBlock(soln)) + return true; + } + if (cancelled(PartialEnd)) throw solver_cancelled; + continue; + +invalidsolution: + invalidCount++; + } + LogPrint("pow", "- Number of invalid solutions found: %d\n", invalidCount); + + return false; +} +#endif // ENABLE_MINING + + + +template +bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector soln) +{ + + if (soln.size() != SolutionWidth) { + LogPrint("pow", "Invalid solution length: %d (expected %d)\n", + soln.size(), SolutionWidth); + return false; + } + + std::vector> X; + X.reserve(1 << K); + unsigned char tmpHash[HashOutput]; + for (eh_index i : GetIndicesFromMinimal(soln, CollisionBitLength)) { + GenerateHash(base_state, i/IndicesPerHashOutput, tmpHash, HashOutput); + X.emplace_back(tmpHash+((i % IndicesPerHashOutput) * N/8), + N/8, HashLength, CollisionBitLength, i); + } + + size_t hashLen = HashLength; + size_t lenIndices = sizeof(eh_index); + while (X.size() > 1) { + std::vector> Xc; + for (int i = 0; i < X.size(); i += 2) { + if (!HasCollision(X[i], X[i+1], CollisionByteLength)) { + LogPrint("pow", "Invalid solution: invalid collision length between StepRows\n"); + LogPrint("pow", "X[i] = %s\n", X[i].GetHex(hashLen)); + LogPrint("pow", "X[i+1] = %s\n", X[i+1].GetHex(hashLen)); + return false; + } + if (X[i+1].IndicesBefore(X[i], hashLen, lenIndices)) { + LogPrint("pow", "Invalid solution: Index tree incorrectly ordered\n"); + return false; + } + if (!DistinctIndices(X[i], X[i+1], hashLen, lenIndices)) { + LogPrint("pow", "Invalid solution: duplicate indices\n"); + return false; + } + Xc.emplace_back(X[i], X[i+1], hashLen, lenIndices, CollisionByteLength); + } + X = Xc; + hashLen -= CollisionByteLength; + lenIndices *= 2; + } + + assert(X.size() == 1); + return X[0].IsZero(hashLen); +} + +// Explicit instantiations for Equihash<96,3> +template int Equihash<96,3>::InitialiseState(eh_HashState& base_state); +#ifdef ENABLE_MINING +template bool Equihash<96,3>::BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +template bool Equihash<96,3>::OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +#endif +template bool Equihash<96,3>::IsValidSolution(const eh_HashState& base_state, std::vector soln); + +// Explicit instantiations for Equihash<200,9> +template int Equihash<200,9>::InitialiseState(eh_HashState& base_state); +#ifdef ENABLE_MINING +template bool Equihash<200,9>::BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +template bool Equihash<200,9>::OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +#endif +template bool Equihash<200,9>::IsValidSolution(const eh_HashState& base_state, std::vector soln); + +// Explicit instantiations for Equihash<96,5> +template int Equihash<96,5>::InitialiseState(eh_HashState& base_state); +#ifdef ENABLE_MINING +template bool Equihash<96,5>::BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +template bool Equihash<96,5>::OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +#endif +template bool Equihash<96,5>::IsValidSolution(const eh_HashState& base_state, std::vector soln); + +// Explicit instantiations for Equihash<48,5> +template int Equihash<48,5>::InitialiseState(eh_HashState& base_state); +#ifdef ENABLE_MINING +template bool Equihash<48,5>::BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +template bool Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +#endif +template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector soln); diff --git a/src/Native/libmultihash/equi/crypto/equihash.h b/src/Native/libmultihash/equi/crypto/equihash.h new file mode 100644 index 000000000..a48cf454b --- /dev/null +++ b/src/Native/libmultihash/equi/crypto/equihash.h @@ -0,0 +1,283 @@ +// Copyright (c) 2016 Jack Grigg +// Copyright (c) 2016 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_EQUIHASH_H +#define BITCOIN_EQUIHASH_H + +#include "sha256.h" +#include "../utilstrencodings.h" + +#include "sodium.h" + +#include +#include +#include +#include +#include +#include + +#include + +#ifdef WIN32 +#undef max +#endif + +typedef crypto_generichash_blake2b_state eh_HashState; +typedef uint32_t eh_index; +typedef uint8_t eh_trunc; + +void ExpandArray(const unsigned char* in, size_t in_len, + unsigned char* out, size_t out_len, + size_t bit_len, size_t byte_pad=0); +void CompressArray(const unsigned char* in, size_t in_len, + unsigned char* out, size_t out_len, + size_t bit_len, size_t byte_pad=0); + +eh_index ArrayToEhIndex(const unsigned char* array); +eh_trunc TruncateIndex(const eh_index i, const unsigned int ilen); + +std::vector GetIndicesFromMinimal(std::vector minimal, + size_t cBitLen); +std::vector GetMinimalFromIndices(std::vector indices, + size_t cBitLen); + +template +class StepRow +{ + template + friend class StepRow; + friend class CompareSR; + +protected: + unsigned char hash[WIDTH]; + +public: + StepRow(const unsigned char* hashIn, size_t hInLen, + size_t hLen, size_t cBitLen); + ~StepRow() { } + + template + StepRow(const StepRow& a); + + bool IsZero(size_t len); + std::string GetHex(size_t len) { return HexStr(hash, hash+len); } + + template + friend bool HasCollision(StepRow& a, StepRow& b, int l); +}; + +class CompareSR +{ +private: + size_t len; + +public: + CompareSR(size_t l) : len {l} { } + + template + inline bool operator()(const StepRow& a, const StepRow& b) { return memcmp(a.hash, b.hash, len) < 0; } +}; + +template +bool HasCollision(StepRow& a, StepRow& b, int l); + +template +class FullStepRow : public StepRow +{ + template + friend class FullStepRow; + + using StepRow::hash; + +public: + FullStepRow(const unsigned char* hashIn, size_t hInLen, + size_t hLen, size_t cBitLen, eh_index i); + ~FullStepRow() { } + + FullStepRow(const FullStepRow& a) : StepRow {a} { } + template + FullStepRow(const FullStepRow& a, const FullStepRow& b, size_t len, size_t lenIndices, int trim); + FullStepRow& operator=(const FullStepRow& a); + + inline bool IndicesBefore(const FullStepRow& a, size_t len, size_t lenIndices) const { return memcmp(hash+len, a.hash+len, lenIndices) < 0; } + std::vector GetIndices(size_t len, size_t lenIndices, + size_t cBitLen) const; + + template + friend bool DistinctIndices(const FullStepRow& a, const FullStepRow& b, + size_t len, size_t lenIndices); + template + friend bool IsValidBranch(const FullStepRow& a, const size_t len, const unsigned int ilen, const eh_trunc t); +}; + +template +class TruncatedStepRow : public StepRow +{ + template + friend class TruncatedStepRow; + + using StepRow::hash; + +public: + TruncatedStepRow(const unsigned char* hashIn, size_t hInLen, + size_t hLen, size_t cBitLen, + eh_index i, unsigned int ilen); + ~TruncatedStepRow() { } + + TruncatedStepRow(const TruncatedStepRow& a) : StepRow {a} { } + template + TruncatedStepRow(const TruncatedStepRow& a, const TruncatedStepRow& b, size_t len, size_t lenIndices, int trim); + TruncatedStepRow& operator=(const TruncatedStepRow& a); + + inline bool IndicesBefore(const TruncatedStepRow& a, size_t len, size_t lenIndices) const { return memcmp(hash+len, a.hash+len, lenIndices) < 0; } + std::shared_ptr GetTruncatedIndices(size_t len, size_t lenIndices) const; +}; + +enum EhSolverCancelCheck +{ + ListGeneration, + ListSorting, + ListColliding, + RoundEnd, + FinalSorting, + FinalColliding, + PartialGeneration, + PartialSorting, + PartialSubtreeEnd, + PartialIndexEnd, + PartialEnd +}; + +class EhSolverCancelledException : public std::exception +{ + virtual const char* what() const throw() { + return "Equihash solver was cancelled"; + } +}; + +inline constexpr const size_t max(const size_t A, const size_t B) { return A > B ? A : B; } + +inline constexpr size_t equihash_solution_size(unsigned int N, unsigned int K) { + return (1 << K)*(N/(K+1)+1)/8; +} + +template +class Equihash +{ +private: + BOOST_STATIC_ASSERT(K < N); + BOOST_STATIC_ASSERT(N % 8 == 0); + BOOST_STATIC_ASSERT((N/(K+1)) + 1 < 8*sizeof(eh_index)); + +public: + enum : size_t { IndicesPerHashOutput=512/N }; + enum : size_t { HashOutput=IndicesPerHashOutput*N/8 }; + enum : size_t { CollisionBitLength=N/(K+1) }; + enum : size_t { CollisionByteLength=(CollisionBitLength+7)/8 }; + enum : size_t { HashLength=(K+1)*CollisionByteLength }; + enum : size_t { FullWidth=2*CollisionByteLength+sizeof(eh_index)*(1 << (K-1)) }; + enum : size_t { FinalFullWidth=2*CollisionByteLength+sizeof(eh_index)*(1 << (K)) }; + enum : size_t { TruncatedWidth=max(HashLength+sizeof(eh_trunc), 2*CollisionByteLength+sizeof(eh_trunc)*(1 << (K-1))) }; + enum : size_t { FinalTruncatedWidth=max(HashLength+sizeof(eh_trunc), 2*CollisionByteLength+sizeof(eh_trunc)*(1 << (K))) }; + enum : size_t { SolutionWidth=(1 << K)*(CollisionBitLength+1)/8 }; + + Equihash() { } + + int InitialiseState(eh_HashState& base_state); +#ifdef ENABLE_MINING + bool BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); + bool OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +#endif + bool IsValidSolution(const eh_HashState& base_state, std::vector soln); +}; + +#include "equihash.tcc" + +static Equihash<96,3> Eh96_3; +static Equihash<200,9> Eh200_9; +static Equihash<96,5> Eh96_5; +static Equihash<48,5> Eh48_5; + +#define EhInitialiseState(n, k, base_state) \ + if (n == 96 && k == 3) { \ + Eh96_3.InitialiseState(base_state); \ + } else if (n == 200 && k == 9) { \ + Eh200_9.InitialiseState(base_state); \ + } else if (n == 96 && k == 5) { \ + Eh96_5.InitialiseState(base_state); \ + } else if (n == 48 && k == 5) { \ + Eh48_5.InitialiseState(base_state); \ + } else { \ + throw std::invalid_argument("Unsupported Equihash parameters"); \ + } + +#ifdef ENABLE_MINING +inline bool EhBasicSolve(unsigned int n, unsigned int k, const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled) +{ + if (n == 96 && k == 3) { + return Eh96_3.BasicSolve(base_state, validBlock, cancelled); + } else if (n == 200 && k == 9) { + return Eh200_9.BasicSolve(base_state, validBlock, cancelled); + } else if (n == 96 && k == 5) { + return Eh96_5.BasicSolve(base_state, validBlock, cancelled); + } else if (n == 48 && k == 5) { + return Eh48_5.BasicSolve(base_state, validBlock, cancelled); + } else { + throw std::invalid_argument("Unsupported Equihash parameters"); + } +} + +inline bool EhBasicSolveUncancellable(unsigned int n, unsigned int k, const eh_HashState& base_state, + const std::function)> validBlock) +{ + return EhBasicSolve(n, k, base_state, validBlock, + [](EhSolverCancelCheck pos) { return false; }); +} + +inline bool EhOptimisedSolve(unsigned int n, unsigned int k, const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled) +{ + if (n == 96 && k == 3) { + return Eh96_3.OptimisedSolve(base_state, validBlock, cancelled); + } else if (n == 200 && k == 9) { + return Eh200_9.OptimisedSolve(base_state, validBlock, cancelled); + } else if (n == 96 && k == 5) { + return Eh96_5.OptimisedSolve(base_state, validBlock, cancelled); + } else if (n == 48 && k == 5) { + return Eh48_5.OptimisedSolve(base_state, validBlock, cancelled); + } else { + throw std::invalid_argument("Unsupported Equihash parameters"); + } +} + +inline bool EhOptimisedSolveUncancellable(unsigned int n, unsigned int k, const eh_HashState& base_state, + const std::function)> validBlock) +{ + return EhOptimisedSolve(n, k, base_state, validBlock, + [](EhSolverCancelCheck pos) { return false; }); +} +#endif // ENABLE_MINING + +#define EhIsValidSolution(n, k, base_state, soln, ret) \ + if (n == 96 && k == 3) { \ + ret = Eh96_3.IsValidSolution(base_state, soln); \ + } else if (n == 200 && k == 9) { \ + ret = Eh200_9.IsValidSolution(base_state, soln); \ + } else if (n == 96 && k == 5) { \ + ret = Eh96_5.IsValidSolution(base_state, soln); \ + } else if (n == 48 && k == 5) { \ + ret = Eh48_5.IsValidSolution(base_state, soln); \ + } else { \ + throw std::invalid_argument("Unsupported Equihash parameters"); \ + } + +#endif // BITCOIN_EQUIHASH_H diff --git a/src/Native/libmultihash/equi/crypto/equihash.tcc b/src/Native/libmultihash/equi/crypto/equihash.tcc new file mode 100644 index 000000000..625749e47 --- /dev/null +++ b/src/Native/libmultihash/equi/crypto/equihash.tcc @@ -0,0 +1,49 @@ +// Copyright (c) 2016 Jack Grigg +// Copyright (c) 2016 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +// Checks if the intersection of a.indices and b.indices is empty +template +bool DistinctIndices(const FullStepRow& a, const FullStepRow& b, size_t len, size_t lenIndices) +{ + for(size_t i = 0; i < lenIndices; i += sizeof(eh_index)) { + for(size_t j = 0; j < lenIndices; j += sizeof(eh_index)) { + if (memcmp(a.hash+len+i, b.hash+len+j, sizeof(eh_index)) == 0) { + return false; + } + } + } + return true; +} + +template +bool IsProbablyDuplicate(std::shared_ptr indices, size_t lenIndices) +{ + assert(lenIndices <= MAX_INDICES); + bool checked_index[MAX_INDICES] = {false}; + int count_checked = 0; + for (int z = 0; z < lenIndices; z++) { + // Skip over indices we have already paired + if (!checked_index[z]) { + for (int y = z+1; y < lenIndices; y++) { + if (!checked_index[y] && indices.get()[z] == indices.get()[y]) { + // Pair found + checked_index[y] = true; + count_checked += 2; + break; + } + } + } + } + return count_checked == lenIndices; +} + +template +bool IsValidBranch(const FullStepRow& a, const size_t len, const unsigned int ilen, const eh_trunc t) +{ + return TruncateIndex(ArrayToEhIndex(a.hash+len), ilen) == t; +} diff --git a/src/Native/libmultihash/equi/crypto/hmac_sha256.cpp b/src/Native/libmultihash/equi/crypto/hmac_sha256.cpp new file mode 100644 index 000000000..b3f36bdda --- /dev/null +++ b/src/Native/libmultihash/equi/crypto/hmac_sha256.cpp @@ -0,0 +1,34 @@ +// Copyright (c) 2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "hmac_sha256.h" + +#include + +CHMAC_SHA256::CHMAC_SHA256(const unsigned char* key, size_t keylen) +{ + unsigned char rkey[64]; + if (keylen <= 64) { + memcpy(rkey, key, keylen); + memset(rkey + keylen, 0, 64 - keylen); + } else { + CSHA256().Write(key, keylen).Finalize(rkey); + memset(rkey + 32, 0, 32); + } + + for (int n = 0; n < 64; n++) + rkey[n] ^= 0x5c; + outer.Write(rkey, 64); + + for (int n = 0; n < 64; n++) + rkey[n] ^= 0x5c ^ 0x36; + inner.Write(rkey, 64); +} + +void CHMAC_SHA256::Finalize(unsigned char hash[OUTPUT_SIZE]) +{ + unsigned char temp[32]; + inner.Finalize(temp); + outer.Write(temp, 32).Finalize(hash); +} diff --git a/src/Native/libmultihash/equi/crypto/hmac_sha256.h b/src/Native/libmultihash/equi/crypto/hmac_sha256.h new file mode 100644 index 000000000..0a3f5c43c --- /dev/null +++ b/src/Native/libmultihash/equi/crypto/hmac_sha256.h @@ -0,0 +1,32 @@ +// Copyright (c) 2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CRYPTO_HMAC_SHA256_H +#define BITCOIN_CRYPTO_HMAC_SHA256_H + +#include "sha256.h" + +#include +#include + +/** A hasher class for HMAC-SHA-512. */ +class CHMAC_SHA256 +{ +private: + CSHA256 outer; + CSHA256 inner; + +public: + static const size_t OUTPUT_SIZE = 32; + + CHMAC_SHA256(const unsigned char* key, size_t keylen); + CHMAC_SHA256& Write(const unsigned char* data, size_t len) + { + inner.Write(data, len); + return *this; + } + void Finalize(unsigned char hash[OUTPUT_SIZE]); +}; + +#endif // BITCOIN_CRYPTO_HMAC_SHA256_H diff --git a/src/Native/libmultihash/equi/crypto/hmac_sha512.cpp b/src/Native/libmultihash/equi/crypto/hmac_sha512.cpp new file mode 100644 index 000000000..76321d055 --- /dev/null +++ b/src/Native/libmultihash/equi/crypto/hmac_sha512.cpp @@ -0,0 +1,34 @@ +// Copyright (c) 2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "hmac_sha512.h" + +#include + +CHMAC_SHA512::CHMAC_SHA512(const unsigned char* key, size_t keylen) +{ + unsigned char rkey[128]; + if (keylen <= 128) { + memcpy(rkey, key, keylen); + memset(rkey + keylen, 0, 128 - keylen); + } else { + CSHA512().Write(key, keylen).Finalize(rkey); + memset(rkey + 64, 0, 64); + } + + for (int n = 0; n < 128; n++) + rkey[n] ^= 0x5c; + outer.Write(rkey, 128); + + for (int n = 0; n < 128; n++) + rkey[n] ^= 0x5c ^ 0x36; + inner.Write(rkey, 128); +} + +void CHMAC_SHA512::Finalize(unsigned char hash[OUTPUT_SIZE]) +{ + unsigned char temp[64]; + inner.Finalize(temp); + outer.Write(temp, 64).Finalize(hash); +} diff --git a/src/Native/libmultihash/equi/crypto/hmac_sha512.h b/src/Native/libmultihash/equi/crypto/hmac_sha512.h new file mode 100644 index 000000000..848e133ce --- /dev/null +++ b/src/Native/libmultihash/equi/crypto/hmac_sha512.h @@ -0,0 +1,32 @@ +// Copyright (c) 2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CRYPTO_HMAC_SHA512_H +#define BITCOIN_CRYPTO_HMAC_SHA512_H + +#include "sha512.h" + +#include +#include + +/** A hasher class for HMAC-SHA-512. */ +class CHMAC_SHA512 +{ +private: + CSHA512 outer; + CSHA512 inner; + +public: + static const size_t OUTPUT_SIZE = 64; + + CHMAC_SHA512(const unsigned char* key, size_t keylen); + CHMAC_SHA512& Write(const unsigned char* data, size_t len) + { + inner.Write(data, len); + return *this; + } + void Finalize(unsigned char hash[OUTPUT_SIZE]); +}; + +#endif // BITCOIN_CRYPTO_HMAC_SHA512_H diff --git a/src/Native/libmultihash/equi/crypto/ripemd160.cpp b/src/Native/libmultihash/equi/crypto/ripemd160.cpp new file mode 100644 index 000000000..5fc7cf38f --- /dev/null +++ b/src/Native/libmultihash/equi/crypto/ripemd160.cpp @@ -0,0 +1,292 @@ +// Copyright (c) 2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "ripemd160.h" + +#include "common.h" + +#include + +// Internal implementation code. +namespace +{ +/// Internal RIPEMD-160 implementation. +namespace ripemd160 +{ +uint32_t inline f1(uint32_t x, uint32_t y, uint32_t z) { return x ^ y ^ z; } +uint32_t inline f2(uint32_t x, uint32_t y, uint32_t z) { return (x & y) | (~x & z); } +uint32_t inline f3(uint32_t x, uint32_t y, uint32_t z) { return (x | ~y) ^ z; } +uint32_t inline f4(uint32_t x, uint32_t y, uint32_t z) { return (x & z) | (y & ~z); } +uint32_t inline f5(uint32_t x, uint32_t y, uint32_t z) { return x ^ (y | ~z); } + +/** Initialize RIPEMD-160 state. */ +void inline Initialize(uint32_t* s) +{ + s[0] = 0x67452301ul; + s[1] = 0xEFCDAB89ul; + s[2] = 0x98BADCFEul; + s[3] = 0x10325476ul; + s[4] = 0xC3D2E1F0ul; +} + +uint32_t inline rol(uint32_t x, int i) { return (x << i) | (x >> (32 - i)); } + +void inline Round(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t f, uint32_t x, uint32_t k, int r) +{ + a = rol(a + f + x + k, r) + e; + c = rol(c, 10); +} + +void inline R11(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f1(b, c, d), x, 0, r); } +void inline R21(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f2(b, c, d), x, 0x5A827999ul, r); } +void inline R31(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f3(b, c, d), x, 0x6ED9EBA1ul, r); } +void inline R41(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f4(b, c, d), x, 0x8F1BBCDCul, r); } +void inline R51(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f5(b, c, d), x, 0xA953FD4Eul, r); } + +void inline R12(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f5(b, c, d), x, 0x50A28BE6ul, r); } +void inline R22(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f4(b, c, d), x, 0x5C4DD124ul, r); } +void inline R32(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f3(b, c, d), x, 0x6D703EF3ul, r); } +void inline R42(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f2(b, c, d), x, 0x7A6D76E9ul, r); } +void inline R52(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f1(b, c, d), x, 0, r); } + +/** Perform a RIPEMD-160 transformation, processing a 64-byte chunk. */ +void Transform(uint32_t* s, const unsigned char* chunk) +{ + uint32_t a1 = s[0], b1 = s[1], c1 = s[2], d1 = s[3], e1 = s[4]; + uint32_t a2 = a1, b2 = b1, c2 = c1, d2 = d1, e2 = e1; + uint32_t w0 = ReadLE32(chunk + 0), w1 = ReadLE32(chunk + 4), w2 = ReadLE32(chunk + 8), w3 = ReadLE32(chunk + 12); + uint32_t w4 = ReadLE32(chunk + 16), w5 = ReadLE32(chunk + 20), w6 = ReadLE32(chunk + 24), w7 = ReadLE32(chunk + 28); + uint32_t w8 = ReadLE32(chunk + 32), w9 = ReadLE32(chunk + 36), w10 = ReadLE32(chunk + 40), w11 = ReadLE32(chunk + 44); + uint32_t w12 = ReadLE32(chunk + 48), w13 = ReadLE32(chunk + 52), w14 = ReadLE32(chunk + 56), w15 = ReadLE32(chunk + 60); + + R11(a1, b1, c1, d1, e1, w0, 11); + R12(a2, b2, c2, d2, e2, w5, 8); + R11(e1, a1, b1, c1, d1, w1, 14); + R12(e2, a2, b2, c2, d2, w14, 9); + R11(d1, e1, a1, b1, c1, w2, 15); + R12(d2, e2, a2, b2, c2, w7, 9); + R11(c1, d1, e1, a1, b1, w3, 12); + R12(c2, d2, e2, a2, b2, w0, 11); + R11(b1, c1, d1, e1, a1, w4, 5); + R12(b2, c2, d2, e2, a2, w9, 13); + R11(a1, b1, c1, d1, e1, w5, 8); + R12(a2, b2, c2, d2, e2, w2, 15); + R11(e1, a1, b1, c1, d1, w6, 7); + R12(e2, a2, b2, c2, d2, w11, 15); + R11(d1, e1, a1, b1, c1, w7, 9); + R12(d2, e2, a2, b2, c2, w4, 5); + R11(c1, d1, e1, a1, b1, w8, 11); + R12(c2, d2, e2, a2, b2, w13, 7); + R11(b1, c1, d1, e1, a1, w9, 13); + R12(b2, c2, d2, e2, a2, w6, 7); + R11(a1, b1, c1, d1, e1, w10, 14); + R12(a2, b2, c2, d2, e2, w15, 8); + R11(e1, a1, b1, c1, d1, w11, 15); + R12(e2, a2, b2, c2, d2, w8, 11); + R11(d1, e1, a1, b1, c1, w12, 6); + R12(d2, e2, a2, b2, c2, w1, 14); + R11(c1, d1, e1, a1, b1, w13, 7); + R12(c2, d2, e2, a2, b2, w10, 14); + R11(b1, c1, d1, e1, a1, w14, 9); + R12(b2, c2, d2, e2, a2, w3, 12); + R11(a1, b1, c1, d1, e1, w15, 8); + R12(a2, b2, c2, d2, e2, w12, 6); + + R21(e1, a1, b1, c1, d1, w7, 7); + R22(e2, a2, b2, c2, d2, w6, 9); + R21(d1, e1, a1, b1, c1, w4, 6); + R22(d2, e2, a2, b2, c2, w11, 13); + R21(c1, d1, e1, a1, b1, w13, 8); + R22(c2, d2, e2, a2, b2, w3, 15); + R21(b1, c1, d1, e1, a1, w1, 13); + R22(b2, c2, d2, e2, a2, w7, 7); + R21(a1, b1, c1, d1, e1, w10, 11); + R22(a2, b2, c2, d2, e2, w0, 12); + R21(e1, a1, b1, c1, d1, w6, 9); + R22(e2, a2, b2, c2, d2, w13, 8); + R21(d1, e1, a1, b1, c1, w15, 7); + R22(d2, e2, a2, b2, c2, w5, 9); + R21(c1, d1, e1, a1, b1, w3, 15); + R22(c2, d2, e2, a2, b2, w10, 11); + R21(b1, c1, d1, e1, a1, w12, 7); + R22(b2, c2, d2, e2, a2, w14, 7); + R21(a1, b1, c1, d1, e1, w0, 12); + R22(a2, b2, c2, d2, e2, w15, 7); + R21(e1, a1, b1, c1, d1, w9, 15); + R22(e2, a2, b2, c2, d2, w8, 12); + R21(d1, e1, a1, b1, c1, w5, 9); + R22(d2, e2, a2, b2, c2, w12, 7); + R21(c1, d1, e1, a1, b1, w2, 11); + R22(c2, d2, e2, a2, b2, w4, 6); + R21(b1, c1, d1, e1, a1, w14, 7); + R22(b2, c2, d2, e2, a2, w9, 15); + R21(a1, b1, c1, d1, e1, w11, 13); + R22(a2, b2, c2, d2, e2, w1, 13); + R21(e1, a1, b1, c1, d1, w8, 12); + R22(e2, a2, b2, c2, d2, w2, 11); + + R31(d1, e1, a1, b1, c1, w3, 11); + R32(d2, e2, a2, b2, c2, w15, 9); + R31(c1, d1, e1, a1, b1, w10, 13); + R32(c2, d2, e2, a2, b2, w5, 7); + R31(b1, c1, d1, e1, a1, w14, 6); + R32(b2, c2, d2, e2, a2, w1, 15); + R31(a1, b1, c1, d1, e1, w4, 7); + R32(a2, b2, c2, d2, e2, w3, 11); + R31(e1, a1, b1, c1, d1, w9, 14); + R32(e2, a2, b2, c2, d2, w7, 8); + R31(d1, e1, a1, b1, c1, w15, 9); + R32(d2, e2, a2, b2, c2, w14, 6); + R31(c1, d1, e1, a1, b1, w8, 13); + R32(c2, d2, e2, a2, b2, w6, 6); + R31(b1, c1, d1, e1, a1, w1, 15); + R32(b2, c2, d2, e2, a2, w9, 14); + R31(a1, b1, c1, d1, e1, w2, 14); + R32(a2, b2, c2, d2, e2, w11, 12); + R31(e1, a1, b1, c1, d1, w7, 8); + R32(e2, a2, b2, c2, d2, w8, 13); + R31(d1, e1, a1, b1, c1, w0, 13); + R32(d2, e2, a2, b2, c2, w12, 5); + R31(c1, d1, e1, a1, b1, w6, 6); + R32(c2, d2, e2, a2, b2, w2, 14); + R31(b1, c1, d1, e1, a1, w13, 5); + R32(b2, c2, d2, e2, a2, w10, 13); + R31(a1, b1, c1, d1, e1, w11, 12); + R32(a2, b2, c2, d2, e2, w0, 13); + R31(e1, a1, b1, c1, d1, w5, 7); + R32(e2, a2, b2, c2, d2, w4, 7); + R31(d1, e1, a1, b1, c1, w12, 5); + R32(d2, e2, a2, b2, c2, w13, 5); + + R41(c1, d1, e1, a1, b1, w1, 11); + R42(c2, d2, e2, a2, b2, w8, 15); + R41(b1, c1, d1, e1, a1, w9, 12); + R42(b2, c2, d2, e2, a2, w6, 5); + R41(a1, b1, c1, d1, e1, w11, 14); + R42(a2, b2, c2, d2, e2, w4, 8); + R41(e1, a1, b1, c1, d1, w10, 15); + R42(e2, a2, b2, c2, d2, w1, 11); + R41(d1, e1, a1, b1, c1, w0, 14); + R42(d2, e2, a2, b2, c2, w3, 14); + R41(c1, d1, e1, a1, b1, w8, 15); + R42(c2, d2, e2, a2, b2, w11, 14); + R41(b1, c1, d1, e1, a1, w12, 9); + R42(b2, c2, d2, e2, a2, w15, 6); + R41(a1, b1, c1, d1, e1, w4, 8); + R42(a2, b2, c2, d2, e2, w0, 14); + R41(e1, a1, b1, c1, d1, w13, 9); + R42(e2, a2, b2, c2, d2, w5, 6); + R41(d1, e1, a1, b1, c1, w3, 14); + R42(d2, e2, a2, b2, c2, w12, 9); + R41(c1, d1, e1, a1, b1, w7, 5); + R42(c2, d2, e2, a2, b2, w2, 12); + R41(b1, c1, d1, e1, a1, w15, 6); + R42(b2, c2, d2, e2, a2, w13, 9); + R41(a1, b1, c1, d1, e1, w14, 8); + R42(a2, b2, c2, d2, e2, w9, 12); + R41(e1, a1, b1, c1, d1, w5, 6); + R42(e2, a2, b2, c2, d2, w7, 5); + R41(d1, e1, a1, b1, c1, w6, 5); + R42(d2, e2, a2, b2, c2, w10, 15); + R41(c1, d1, e1, a1, b1, w2, 12); + R42(c2, d2, e2, a2, b2, w14, 8); + + R51(b1, c1, d1, e1, a1, w4, 9); + R52(b2, c2, d2, e2, a2, w12, 8); + R51(a1, b1, c1, d1, e1, w0, 15); + R52(a2, b2, c2, d2, e2, w15, 5); + R51(e1, a1, b1, c1, d1, w5, 5); + R52(e2, a2, b2, c2, d2, w10, 12); + R51(d1, e1, a1, b1, c1, w9, 11); + R52(d2, e2, a2, b2, c2, w4, 9); + R51(c1, d1, e1, a1, b1, w7, 6); + R52(c2, d2, e2, a2, b2, w1, 12); + R51(b1, c1, d1, e1, a1, w12, 8); + R52(b2, c2, d2, e2, a2, w5, 5); + R51(a1, b1, c1, d1, e1, w2, 13); + R52(a2, b2, c2, d2, e2, w8, 14); + R51(e1, a1, b1, c1, d1, w10, 12); + R52(e2, a2, b2, c2, d2, w7, 6); + R51(d1, e1, a1, b1, c1, w14, 5); + R52(d2, e2, a2, b2, c2, w6, 8); + R51(c1, d1, e1, a1, b1, w1, 12); + R52(c2, d2, e2, a2, b2, w2, 13); + R51(b1, c1, d1, e1, a1, w3, 13); + R52(b2, c2, d2, e2, a2, w13, 6); + R51(a1, b1, c1, d1, e1, w8, 14); + R52(a2, b2, c2, d2, e2, w14, 5); + R51(e1, a1, b1, c1, d1, w11, 11); + R52(e2, a2, b2, c2, d2, w0, 15); + R51(d1, e1, a1, b1, c1, w6, 8); + R52(d2, e2, a2, b2, c2, w3, 13); + R51(c1, d1, e1, a1, b1, w15, 5); + R52(c2, d2, e2, a2, b2, w9, 11); + R51(b1, c1, d1, e1, a1, w13, 6); + R52(b2, c2, d2, e2, a2, w11, 11); + + uint32_t t = s[0]; + s[0] = s[1] + c1 + d2; + s[1] = s[2] + d1 + e2; + s[2] = s[3] + e1 + a2; + s[3] = s[4] + a1 + b2; + s[4] = t + b1 + c2; +} + +} // namespace ripemd160 + +} // namespace + +////// RIPEMD160 + +CRIPEMD160::CRIPEMD160() : bytes(0) +{ + ripemd160::Initialize(s); +} + +CRIPEMD160& CRIPEMD160::Write(const unsigned char* data, size_t len) +{ + const unsigned char* end = data + len; + size_t bufsize = bytes % 64; + if (bufsize && bufsize + len >= 64) { + // Fill the buffer, and process it. + memcpy(buf + bufsize, data, 64 - bufsize); + bytes += 64 - bufsize; + data += 64 - bufsize; + ripemd160::Transform(s, buf); + bufsize = 0; + } + while (end >= data + 64) { + // Process full chunks directly from the source. + ripemd160::Transform(s, data); + bytes += 64; + data += 64; + } + if (end > data) { + // Fill the buffer with what remains. + memcpy(buf + bufsize, data, end - data); + bytes += end - data; + } + return *this; +} + +void CRIPEMD160::Finalize(unsigned char hash[OUTPUT_SIZE]) +{ + static const unsigned char pad[64] = {0x80}; + unsigned char sizedesc[8]; + WriteLE64(sizedesc, bytes << 3); + Write(pad, 1 + ((119 - (bytes % 64)) % 64)); + Write(sizedesc, 8); + WriteLE32(hash, s[0]); + WriteLE32(hash + 4, s[1]); + WriteLE32(hash + 8, s[2]); + WriteLE32(hash + 12, s[3]); + WriteLE32(hash + 16, s[4]); +} + +CRIPEMD160& CRIPEMD160::Reset() +{ + bytes = 0; + ripemd160::Initialize(s); + return *this; +} diff --git a/src/Native/libmultihash/equi/crypto/ripemd160.h b/src/Native/libmultihash/equi/crypto/ripemd160.h new file mode 100644 index 000000000..687204fda --- /dev/null +++ b/src/Native/libmultihash/equi/crypto/ripemd160.h @@ -0,0 +1,28 @@ +// Copyright (c) 2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CRYPTO_RIPEMD160_H +#define BITCOIN_CRYPTO_RIPEMD160_H + +#include +#include + +/** A hasher class for RIPEMD-160. */ +class CRIPEMD160 +{ +private: + uint32_t s[5]; + unsigned char buf[64]; + size_t bytes; + +public: + static const size_t OUTPUT_SIZE = 20; + + CRIPEMD160(); + CRIPEMD160& Write(const unsigned char* data, size_t len); + void Finalize(unsigned char hash[OUTPUT_SIZE]); + CRIPEMD160& Reset(); +}; + +#endif // BITCOIN_CRYPTO_RIPEMD160_H diff --git a/src/Native/libmultihash/equi/crypto/sha1.cpp b/src/Native/libmultihash/equi/crypto/sha1.cpp new file mode 100644 index 000000000..d9b847daf --- /dev/null +++ b/src/Native/libmultihash/equi/crypto/sha1.cpp @@ -0,0 +1,199 @@ +// Copyright (c) 2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "sha1.h" + +#include "common.h" + +#include + +// Internal implementation code. +namespace +{ +/// Internal SHA-1 implementation. +namespace sha1 +{ +/** One round of SHA-1. */ +void inline Round(uint32_t a, uint32_t& b, uint32_t c, uint32_t d, uint32_t& e, uint32_t f, uint32_t k, uint32_t w) +{ + e += ((a << 5) | (a >> 27)) + f + k + w; + b = (b << 30) | (b >> 2); +} + +uint32_t inline f1(uint32_t b, uint32_t c, uint32_t d) { return d ^ (b & (c ^ d)); } +uint32_t inline f2(uint32_t b, uint32_t c, uint32_t d) { return b ^ c ^ d; } +uint32_t inline f3(uint32_t b, uint32_t c, uint32_t d) { return (b & c) | (d & (b | c)); } + +uint32_t inline left(uint32_t x) { return (x << 1) | (x >> 31); } + +/** Initialize SHA-1 state. */ +void inline Initialize(uint32_t* s) +{ + s[0] = 0x67452301ul; + s[1] = 0xEFCDAB89ul; + s[2] = 0x98BADCFEul; + s[3] = 0x10325476ul; + s[4] = 0xC3D2E1F0ul; +} + +const uint32_t k1 = 0x5A827999ul; +const uint32_t k2 = 0x6ED9EBA1ul; +const uint32_t k3 = 0x8F1BBCDCul; +const uint32_t k4 = 0xCA62C1D6ul; + +/** Perform a SHA-1 transformation, processing a 64-byte chunk. */ +void Transform(uint32_t* s, const unsigned char* chunk) +{ + uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4]; + uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; + + Round(a, b, c, d, e, f1(b, c, d), k1, w0 = ReadBE32(chunk + 0)); + Round(e, a, b, c, d, f1(a, b, c), k1, w1 = ReadBE32(chunk + 4)); + Round(d, e, a, b, c, f1(e, a, b), k1, w2 = ReadBE32(chunk + 8)); + Round(c, d, e, a, b, f1(d, e, a), k1, w3 = ReadBE32(chunk + 12)); + Round(b, c, d, e, a, f1(c, d, e), k1, w4 = ReadBE32(chunk + 16)); + Round(a, b, c, d, e, f1(b, c, d), k1, w5 = ReadBE32(chunk + 20)); + Round(e, a, b, c, d, f1(a, b, c), k1, w6 = ReadBE32(chunk + 24)); + Round(d, e, a, b, c, f1(e, a, b), k1, w7 = ReadBE32(chunk + 28)); + Round(c, d, e, a, b, f1(d, e, a), k1, w8 = ReadBE32(chunk + 32)); + Round(b, c, d, e, a, f1(c, d, e), k1, w9 = ReadBE32(chunk + 36)); + Round(a, b, c, d, e, f1(b, c, d), k1, w10 = ReadBE32(chunk + 40)); + Round(e, a, b, c, d, f1(a, b, c), k1, w11 = ReadBE32(chunk + 44)); + Round(d, e, a, b, c, f1(e, a, b), k1, w12 = ReadBE32(chunk + 48)); + Round(c, d, e, a, b, f1(d, e, a), k1, w13 = ReadBE32(chunk + 52)); + Round(b, c, d, e, a, f1(c, d, e), k1, w14 = ReadBE32(chunk + 56)); + Round(a, b, c, d, e, f1(b, c, d), k1, w15 = ReadBE32(chunk + 60)); + + Round(e, a, b, c, d, f1(a, b, c), k1, w0 = left(w0 ^ w13 ^ w8 ^ w2)); + Round(d, e, a, b, c, f1(e, a, b), k1, w1 = left(w1 ^ w14 ^ w9 ^ w3)); + Round(c, d, e, a, b, f1(d, e, a), k1, w2 = left(w2 ^ w15 ^ w10 ^ w4)); + Round(b, c, d, e, a, f1(c, d, e), k1, w3 = left(w3 ^ w0 ^ w11 ^ w5)); + Round(a, b, c, d, e, f2(b, c, d), k2, w4 = left(w4 ^ w1 ^ w12 ^ w6)); + Round(e, a, b, c, d, f2(a, b, c), k2, w5 = left(w5 ^ w2 ^ w13 ^ w7)); + Round(d, e, a, b, c, f2(e, a, b), k2, w6 = left(w6 ^ w3 ^ w14 ^ w8)); + Round(c, d, e, a, b, f2(d, e, a), k2, w7 = left(w7 ^ w4 ^ w15 ^ w9)); + Round(b, c, d, e, a, f2(c, d, e), k2, w8 = left(w8 ^ w5 ^ w0 ^ w10)); + Round(a, b, c, d, e, f2(b, c, d), k2, w9 = left(w9 ^ w6 ^ w1 ^ w11)); + Round(e, a, b, c, d, f2(a, b, c), k2, w10 = left(w10 ^ w7 ^ w2 ^ w12)); + Round(d, e, a, b, c, f2(e, a, b), k2, w11 = left(w11 ^ w8 ^ w3 ^ w13)); + Round(c, d, e, a, b, f2(d, e, a), k2, w12 = left(w12 ^ w9 ^ w4 ^ w14)); + Round(b, c, d, e, a, f2(c, d, e), k2, w13 = left(w13 ^ w10 ^ w5 ^ w15)); + Round(a, b, c, d, e, f2(b, c, d), k2, w14 = left(w14 ^ w11 ^ w6 ^ w0)); + Round(e, a, b, c, d, f2(a, b, c), k2, w15 = left(w15 ^ w12 ^ w7 ^ w1)); + + Round(d, e, a, b, c, f2(e, a, b), k2, w0 = left(w0 ^ w13 ^ w8 ^ w2)); + Round(c, d, e, a, b, f2(d, e, a), k2, w1 = left(w1 ^ w14 ^ w9 ^ w3)); + Round(b, c, d, e, a, f2(c, d, e), k2, w2 = left(w2 ^ w15 ^ w10 ^ w4)); + Round(a, b, c, d, e, f2(b, c, d), k2, w3 = left(w3 ^ w0 ^ w11 ^ w5)); + Round(e, a, b, c, d, f2(a, b, c), k2, w4 = left(w4 ^ w1 ^ w12 ^ w6)); + Round(d, e, a, b, c, f2(e, a, b), k2, w5 = left(w5 ^ w2 ^ w13 ^ w7)); + Round(c, d, e, a, b, f2(d, e, a), k2, w6 = left(w6 ^ w3 ^ w14 ^ w8)); + Round(b, c, d, e, a, f2(c, d, e), k2, w7 = left(w7 ^ w4 ^ w15 ^ w9)); + Round(a, b, c, d, e, f3(b, c, d), k3, w8 = left(w8 ^ w5 ^ w0 ^ w10)); + Round(e, a, b, c, d, f3(a, b, c), k3, w9 = left(w9 ^ w6 ^ w1 ^ w11)); + Round(d, e, a, b, c, f3(e, a, b), k3, w10 = left(w10 ^ w7 ^ w2 ^ w12)); + Round(c, d, e, a, b, f3(d, e, a), k3, w11 = left(w11 ^ w8 ^ w3 ^ w13)); + Round(b, c, d, e, a, f3(c, d, e), k3, w12 = left(w12 ^ w9 ^ w4 ^ w14)); + Round(a, b, c, d, e, f3(b, c, d), k3, w13 = left(w13 ^ w10 ^ w5 ^ w15)); + Round(e, a, b, c, d, f3(a, b, c), k3, w14 = left(w14 ^ w11 ^ w6 ^ w0)); + Round(d, e, a, b, c, f3(e, a, b), k3, w15 = left(w15 ^ w12 ^ w7 ^ w1)); + + Round(c, d, e, a, b, f3(d, e, a), k3, w0 = left(w0 ^ w13 ^ w8 ^ w2)); + Round(b, c, d, e, a, f3(c, d, e), k3, w1 = left(w1 ^ w14 ^ w9 ^ w3)); + Round(a, b, c, d, e, f3(b, c, d), k3, w2 = left(w2 ^ w15 ^ w10 ^ w4)); + Round(e, a, b, c, d, f3(a, b, c), k3, w3 = left(w3 ^ w0 ^ w11 ^ w5)); + Round(d, e, a, b, c, f3(e, a, b), k3, w4 = left(w4 ^ w1 ^ w12 ^ w6)); + Round(c, d, e, a, b, f3(d, e, a), k3, w5 = left(w5 ^ w2 ^ w13 ^ w7)); + Round(b, c, d, e, a, f3(c, d, e), k3, w6 = left(w6 ^ w3 ^ w14 ^ w8)); + Round(a, b, c, d, e, f3(b, c, d), k3, w7 = left(w7 ^ w4 ^ w15 ^ w9)); + Round(e, a, b, c, d, f3(a, b, c), k3, w8 = left(w8 ^ w5 ^ w0 ^ w10)); + Round(d, e, a, b, c, f3(e, a, b), k3, w9 = left(w9 ^ w6 ^ w1 ^ w11)); + Round(c, d, e, a, b, f3(d, e, a), k3, w10 = left(w10 ^ w7 ^ w2 ^ w12)); + Round(b, c, d, e, a, f3(c, d, e), k3, w11 = left(w11 ^ w8 ^ w3 ^ w13)); + Round(a, b, c, d, e, f2(b, c, d), k4, w12 = left(w12 ^ w9 ^ w4 ^ w14)); + Round(e, a, b, c, d, f2(a, b, c), k4, w13 = left(w13 ^ w10 ^ w5 ^ w15)); + Round(d, e, a, b, c, f2(e, a, b), k4, w14 = left(w14 ^ w11 ^ w6 ^ w0)); + Round(c, d, e, a, b, f2(d, e, a), k4, w15 = left(w15 ^ w12 ^ w7 ^ w1)); + + Round(b, c, d, e, a, f2(c, d, e), k4, w0 = left(w0 ^ w13 ^ w8 ^ w2)); + Round(a, b, c, d, e, f2(b, c, d), k4, w1 = left(w1 ^ w14 ^ w9 ^ w3)); + Round(e, a, b, c, d, f2(a, b, c), k4, w2 = left(w2 ^ w15 ^ w10 ^ w4)); + Round(d, e, a, b, c, f2(e, a, b), k4, w3 = left(w3 ^ w0 ^ w11 ^ w5)); + Round(c, d, e, a, b, f2(d, e, a), k4, w4 = left(w4 ^ w1 ^ w12 ^ w6)); + Round(b, c, d, e, a, f2(c, d, e), k4, w5 = left(w5 ^ w2 ^ w13 ^ w7)); + Round(a, b, c, d, e, f2(b, c, d), k4, w6 = left(w6 ^ w3 ^ w14 ^ w8)); + Round(e, a, b, c, d, f2(a, b, c), k4, w7 = left(w7 ^ w4 ^ w15 ^ w9)); + Round(d, e, a, b, c, f2(e, a, b), k4, w8 = left(w8 ^ w5 ^ w0 ^ w10)); + Round(c, d, e, a, b, f2(d, e, a), k4, w9 = left(w9 ^ w6 ^ w1 ^ w11)); + Round(b, c, d, e, a, f2(c, d, e), k4, w10 = left(w10 ^ w7 ^ w2 ^ w12)); + Round(a, b, c, d, e, f2(b, c, d), k4, w11 = left(w11 ^ w8 ^ w3 ^ w13)); + Round(e, a, b, c, d, f2(a, b, c), k4, w12 = left(w12 ^ w9 ^ w4 ^ w14)); + Round(d, e, a, b, c, f2(e, a, b), k4, left(w13 ^ w10 ^ w5 ^ w15)); + Round(c, d, e, a, b, f2(d, e, a), k4, left(w14 ^ w11 ^ w6 ^ w0)); + Round(b, c, d, e, a, f2(c, d, e), k4, left(w15 ^ w12 ^ w7 ^ w1)); + + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + s[4] += e; +} + +} // namespace sha1 + +} // namespace + +////// SHA1 + +CSHA1::CSHA1() : bytes(0) +{ + sha1::Initialize(s); +} + +CSHA1& CSHA1::Write(const unsigned char* data, size_t len) +{ + const unsigned char* end = data + len; + size_t bufsize = bytes % 64; + if (bufsize && bufsize + len >= 64) { + // Fill the buffer, and process it. + memcpy(buf + bufsize, data, 64 - bufsize); + bytes += 64 - bufsize; + data += 64 - bufsize; + sha1::Transform(s, buf); + bufsize = 0; + } + while (end >= data + 64) { + // Process full chunks directly from the source. + sha1::Transform(s, data); + bytes += 64; + data += 64; + } + if (end > data) { + // Fill the buffer with what remains. + memcpy(buf + bufsize, data, end - data); + bytes += end - data; + } + return *this; +} + +void CSHA1::Finalize(unsigned char hash[OUTPUT_SIZE]) +{ + static const unsigned char pad[64] = {0x80}; + unsigned char sizedesc[8]; + WriteBE64(sizedesc, bytes << 3); + Write(pad, 1 + ((119 - (bytes % 64)) % 64)); + Write(sizedesc, 8); + WriteBE32(hash, s[0]); + WriteBE32(hash + 4, s[1]); + WriteBE32(hash + 8, s[2]); + WriteBE32(hash + 12, s[3]); + WriteBE32(hash + 16, s[4]); +} + +CSHA1& CSHA1::Reset() +{ + bytes = 0; + sha1::Initialize(s); + return *this; +} diff --git a/src/Native/libmultihash/equi/crypto/sha1.h b/src/Native/libmultihash/equi/crypto/sha1.h new file mode 100644 index 000000000..7b2a21bc6 --- /dev/null +++ b/src/Native/libmultihash/equi/crypto/sha1.h @@ -0,0 +1,28 @@ +// Copyright (c) 2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CRYPTO_SHA1_H +#define BITCOIN_CRYPTO_SHA1_H + +#include +#include + +/** A hasher class for SHA1. */ +class CSHA1 +{ +private: + uint32_t s[5]; + unsigned char buf[64]; + size_t bytes; + +public: + static const size_t OUTPUT_SIZE = 20; + + CSHA1(); + CSHA1& Write(const unsigned char* data, size_t len); + void Finalize(unsigned char hash[OUTPUT_SIZE]); + CSHA1& Reset(); +}; + +#endif // BITCOIN_CRYPTO_SHA1_H diff --git a/src/Native/libmultihash/equi/crypto/sha256.cpp b/src/Native/libmultihash/equi/crypto/sha256.cpp new file mode 100644 index 000000000..f379cc6c2 --- /dev/null +++ b/src/Native/libmultihash/equi/crypto/sha256.cpp @@ -0,0 +1,199 @@ +// Copyright (c) 2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "sha256.h" + +#include "common.h" + +#include +#include + +// Internal implementation code. +namespace +{ +/// Internal SHA-256 implementation. +namespace sha256 +{ +uint32_t inline Ch(uint32_t x, uint32_t y, uint32_t z) { return z ^ (x & (y ^ z)); } +uint32_t inline Maj(uint32_t x, uint32_t y, uint32_t z) { return (x & y) | (z & (x | y)); } +uint32_t inline Sigma0(uint32_t x) { return (x >> 2 | x << 30) ^ (x >> 13 | x << 19) ^ (x >> 22 | x << 10); } +uint32_t inline Sigma1(uint32_t x) { return (x >> 6 | x << 26) ^ (x >> 11 | x << 21) ^ (x >> 25 | x << 7); } +uint32_t inline sigma0(uint32_t x) { return (x >> 7 | x << 25) ^ (x >> 18 | x << 14) ^ (x >> 3); } +uint32_t inline sigma1(uint32_t x) { return (x >> 17 | x << 15) ^ (x >> 19 | x << 13) ^ (x >> 10); } + +/** One round of SHA-256. */ +void inline Round(uint32_t a, uint32_t b, uint32_t c, uint32_t& d, uint32_t e, uint32_t f, uint32_t g, uint32_t& h, uint32_t k, uint32_t w) +{ + uint32_t t1 = h + Sigma1(e) + Ch(e, f, g) + k + w; + uint32_t t2 = Sigma0(a) + Maj(a, b, c); + d += t1; + h = t1 + t2; +} + +/** Initialize SHA-256 state. */ +void inline Initialize(uint32_t* s) +{ + s[0] = 0x6a09e667ul; + s[1] = 0xbb67ae85ul; + s[2] = 0x3c6ef372ul; + s[3] = 0xa54ff53aul; + s[4] = 0x510e527ful; + s[5] = 0x9b05688cul; + s[6] = 0x1f83d9abul; + s[7] = 0x5be0cd19ul; +} + +/** Perform one SHA-256 transformation, processing a 64-byte chunk. */ +void Transform(uint32_t* s, const unsigned char* chunk) +{ + uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7]; + uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; + + Round(a, b, c, d, e, f, g, h, 0x428a2f98, w0 = ReadBE32(chunk + 0)); + Round(h, a, b, c, d, e, f, g, 0x71374491, w1 = ReadBE32(chunk + 4)); + Round(g, h, a, b, c, d, e, f, 0xb5c0fbcf, w2 = ReadBE32(chunk + 8)); + Round(f, g, h, a, b, c, d, e, 0xe9b5dba5, w3 = ReadBE32(chunk + 12)); + Round(e, f, g, h, a, b, c, d, 0x3956c25b, w4 = ReadBE32(chunk + 16)); + Round(d, e, f, g, h, a, b, c, 0x59f111f1, w5 = ReadBE32(chunk + 20)); + Round(c, d, e, f, g, h, a, b, 0x923f82a4, w6 = ReadBE32(chunk + 24)); + Round(b, c, d, e, f, g, h, a, 0xab1c5ed5, w7 = ReadBE32(chunk + 28)); + Round(a, b, c, d, e, f, g, h, 0xd807aa98, w8 = ReadBE32(chunk + 32)); + Round(h, a, b, c, d, e, f, g, 0x12835b01, w9 = ReadBE32(chunk + 36)); + Round(g, h, a, b, c, d, e, f, 0x243185be, w10 = ReadBE32(chunk + 40)); + Round(f, g, h, a, b, c, d, e, 0x550c7dc3, w11 = ReadBE32(chunk + 44)); + Round(e, f, g, h, a, b, c, d, 0x72be5d74, w12 = ReadBE32(chunk + 48)); + Round(d, e, f, g, h, a, b, c, 0x80deb1fe, w13 = ReadBE32(chunk + 52)); + Round(c, d, e, f, g, h, a, b, 0x9bdc06a7, w14 = ReadBE32(chunk + 56)); + Round(b, c, d, e, f, g, h, a, 0xc19bf174, w15 = ReadBE32(chunk + 60)); + + Round(a, b, c, d, e, f, g, h, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x0fc19dc6, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x240ca1cc, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x2de92c6f, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x4a7484aa, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x5cb0a9dc, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x76f988da, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0x983e5152, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0xa831c66d, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0xb00327c8, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0xbf597fc7, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0xc6e00bf3, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xd5a79147, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0x06ca6351, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x14292967, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, d, e, f, g, h, 0x27b70a85, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0x2e1b2138, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x4d2c6dfc, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x53380d13, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x650a7354, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x766a0abb, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x81c2c92e, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x92722c85, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0xa2bfe8a1, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0xa81a664b, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0xc24b8b70, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0xc76c51a3, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0xd192e819, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xd6990624, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0xf40e3585, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x106aa070, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, d, e, f, g, h, 0x19a4c116, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0x1e376c08, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x2748774c, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x34b0bcb5, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x391c0cb3, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x4ed8aa4a, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x5b9cca4f, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x682e6ff3, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0x748f82ee, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0x78a5636f, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0x84c87814, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0x8cc70208, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0x90befffa, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xa4506ceb, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0xbef9a3f7, w14 + sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0xc67178f2, w15 + sigma1(w13) + w8 + sigma0(w0)); + + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + s[4] += e; + s[5] += f; + s[6] += g; + s[7] += h; +} + +} // namespace sha256 +} // namespace + + +////// SHA-256 + +CSHA256::CSHA256() : bytes(0) +{ + sha256::Initialize(s); +} + +CSHA256& CSHA256::Write(const unsigned char* data, size_t len) +{ + const unsigned char* end = data + len; + size_t bufsize = bytes % 64; + if (bufsize && bufsize + len >= 64) { + // Fill the buffer, and process it. + memcpy(buf + bufsize, data, 64 - bufsize); + bytes += 64 - bufsize; + data += 64 - bufsize; + sha256::Transform(s, buf); + bufsize = 0; + } + while (end >= data + 64) { + // Process full chunks directly from the source. + sha256::Transform(s, data); + bytes += 64; + data += 64; + } + if (end > data) { + // Fill the buffer with what remains. + memcpy(buf + bufsize, data, end - data); + bytes += end - data; + } + return *this; +} + +void CSHA256::Finalize(unsigned char hash[OUTPUT_SIZE]) +{ + static const unsigned char pad[64] = {0x80}; + unsigned char sizedesc[8]; + WriteBE64(sizedesc, bytes << 3); + Write(pad, 1 + ((119 - (bytes % 64)) % 64)); + Write(sizedesc, 8); + FinalizeNoPadding(hash, false); +} + +void CSHA256::FinalizeNoPadding(unsigned char hash[OUTPUT_SIZE], bool enforce_compression) +{ + if (enforce_compression && bytes != 64) { + throw std::length_error("SHA256Compress should be invoked with a 512-bit block"); + } + + WriteBE32(hash, s[0]); + WriteBE32(hash + 4, s[1]); + WriteBE32(hash + 8, s[2]); + WriteBE32(hash + 12, s[3]); + WriteBE32(hash + 16, s[4]); + WriteBE32(hash + 20, s[5]); + WriteBE32(hash + 24, s[6]); + WriteBE32(hash + 28, s[7]); +} + +CSHA256& CSHA256::Reset() +{ + bytes = 0; + sha256::Initialize(s); + return *this; +} diff --git a/src/Native/libmultihash/equi/crypto/sha256.h b/src/Native/libmultihash/equi/crypto/sha256.h new file mode 100644 index 000000000..eb75094ee --- /dev/null +++ b/src/Native/libmultihash/equi/crypto/sha256.h @@ -0,0 +1,33 @@ +// Copyright (c) 2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CRYPTO_SHA256_H +#define BITCOIN_CRYPTO_SHA256_H + +#include +#include + + +/** A hasher class for SHA-256. */ +class CSHA256 +{ +public: + static const size_t OUTPUT_SIZE = 32; + + CSHA256(); + CSHA256& Write(const unsigned char* data, size_t len); + void Finalize(unsigned char hash[OUTPUT_SIZE]); + void FinalizeNoPadding(unsigned char hash[OUTPUT_SIZE]) { + FinalizeNoPadding(hash, true); + }; + CSHA256& Reset(); + +private: + uint32_t s[8]; + unsigned char buf[64]; + size_t bytes; + void FinalizeNoPadding(unsigned char hash[OUTPUT_SIZE], bool enforce_compression); +}; + +#endif // BITCOIN_CRYPTO_SHA256_H diff --git a/src/Native/libmultihash/equi/crypto/sha512.cpp b/src/Native/libmultihash/equi/crypto/sha512.cpp new file mode 100644 index 000000000..bf8f344ae --- /dev/null +++ b/src/Native/libmultihash/equi/crypto/sha512.cpp @@ -0,0 +1,207 @@ +// Copyright (c) 2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "sha512.h" + +#include "common.h" + +#include + +// Internal implementation code. +namespace +{ +/// Internal SHA-512 implementation. +namespace sha512 +{ +uint64_t inline Ch(uint64_t x, uint64_t y, uint64_t z) { return z ^ (x & (y ^ z)); } +uint64_t inline Maj(uint64_t x, uint64_t y, uint64_t z) { return (x & y) | (z & (x | y)); } +uint64_t inline Sigma0(uint64_t x) { return (x >> 28 | x << 36) ^ (x >> 34 | x << 30) ^ (x >> 39 | x << 25); } +uint64_t inline Sigma1(uint64_t x) { return (x >> 14 | x << 50) ^ (x >> 18 | x << 46) ^ (x >> 41 | x << 23); } +uint64_t inline sigma0(uint64_t x) { return (x >> 1 | x << 63) ^ (x >> 8 | x << 56) ^ (x >> 7); } +uint64_t inline sigma1(uint64_t x) { return (x >> 19 | x << 45) ^ (x >> 61 | x << 3) ^ (x >> 6); } + +/** One round of SHA-512. */ +void inline Round(uint64_t a, uint64_t b, uint64_t c, uint64_t& d, uint64_t e, uint64_t f, uint64_t g, uint64_t& h, uint64_t k, uint64_t w) +{ + uint64_t t1 = h + Sigma1(e) + Ch(e, f, g) + k + w; + uint64_t t2 = Sigma0(a) + Maj(a, b, c); + d += t1; + h = t1 + t2; +} + +/** Initialize SHA-256 state. */ +void inline Initialize(uint64_t* s) +{ + s[0] = 0x6a09e667f3bcc908ull; + s[1] = 0xbb67ae8584caa73bull; + s[2] = 0x3c6ef372fe94f82bull; + s[3] = 0xa54ff53a5f1d36f1ull; + s[4] = 0x510e527fade682d1ull; + s[5] = 0x9b05688c2b3e6c1full; + s[6] = 0x1f83d9abfb41bd6bull; + s[7] = 0x5be0cd19137e2179ull; +} + +/** Perform one SHA-512 transformation, processing a 128-byte chunk. */ +void Transform(uint64_t* s, const unsigned char* chunk) +{ + uint64_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7]; + uint64_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; + + Round(a, b, c, d, e, f, g, h, 0x428a2f98d728ae22ull, w0 = ReadBE64(chunk + 0)); + Round(h, a, b, c, d, e, f, g, 0x7137449123ef65cdull, w1 = ReadBE64(chunk + 8)); + Round(g, h, a, b, c, d, e, f, 0xb5c0fbcfec4d3b2full, w2 = ReadBE64(chunk + 16)); + Round(f, g, h, a, b, c, d, e, 0xe9b5dba58189dbbcull, w3 = ReadBE64(chunk + 24)); + Round(e, f, g, h, a, b, c, d, 0x3956c25bf348b538ull, w4 = ReadBE64(chunk + 32)); + Round(d, e, f, g, h, a, b, c, 0x59f111f1b605d019ull, w5 = ReadBE64(chunk + 40)); + Round(c, d, e, f, g, h, a, b, 0x923f82a4af194f9bull, w6 = ReadBE64(chunk + 48)); + Round(b, c, d, e, f, g, h, a, 0xab1c5ed5da6d8118ull, w7 = ReadBE64(chunk + 56)); + Round(a, b, c, d, e, f, g, h, 0xd807aa98a3030242ull, w8 = ReadBE64(chunk + 64)); + Round(h, a, b, c, d, e, f, g, 0x12835b0145706fbeull, w9 = ReadBE64(chunk + 72)); + Round(g, h, a, b, c, d, e, f, 0x243185be4ee4b28cull, w10 = ReadBE64(chunk + 80)); + Round(f, g, h, a, b, c, d, e, 0x550c7dc3d5ffb4e2ull, w11 = ReadBE64(chunk + 88)); + Round(e, f, g, h, a, b, c, d, 0x72be5d74f27b896full, w12 = ReadBE64(chunk + 96)); + Round(d, e, f, g, h, a, b, c, 0x80deb1fe3b1696b1ull, w13 = ReadBE64(chunk + 104)); + Round(c, d, e, f, g, h, a, b, 0x9bdc06a725c71235ull, w14 = ReadBE64(chunk + 112)); + Round(b, c, d, e, f, g, h, a, 0xc19bf174cf692694ull, w15 = ReadBE64(chunk + 120)); + + Round(a, b, c, d, e, f, g, h, 0xe49b69c19ef14ad2ull, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0xefbe4786384f25e3ull, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x0fc19dc68b8cd5b5ull, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x240ca1cc77ac9c65ull, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x2de92c6f592b0275ull, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x4a7484aa6ea6e483ull, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x5cb0a9dcbd41fbd4ull, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x76f988da831153b5ull, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0x983e5152ee66dfabull, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0xa831c66d2db43210ull, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0xb00327c898fb213full, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0xbf597fc7beef0ee4ull, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0xc6e00bf33da88fc2ull, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xd5a79147930aa725ull, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0x06ca6351e003826full, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x142929670a0e6e70ull, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, d, e, f, g, h, 0x27b70a8546d22ffcull, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0x2e1b21385c26c926ull, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x4d2c6dfc5ac42aedull, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x53380d139d95b3dfull, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x650a73548baf63deull, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x766a0abb3c77b2a8ull, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x81c2c92e47edaee6ull, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x92722c851482353bull, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0xa2bfe8a14cf10364ull, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0xa81a664bbc423001ull, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0xc24b8b70d0f89791ull, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0xc76c51a30654be30ull, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0xd192e819d6ef5218ull, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xd69906245565a910ull, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0xf40e35855771202aull, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x106aa07032bbd1b8ull, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, d, e, f, g, h, 0x19a4c116b8d2d0c8ull, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0x1e376c085141ab53ull, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x2748774cdf8eeb99ull, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x34b0bcb5e19b48a8ull, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x391c0cb3c5c95a63ull, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x4ed8aa4ae3418acbull, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x5b9cca4f7763e373ull, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x682e6ff3d6b2b8a3ull, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0x748f82ee5defb2fcull, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0x78a5636f43172f60ull, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0x84c87814a1f0ab72ull, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0x8cc702081a6439ecull, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0x90befffa23631e28ull, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xa4506cebde82bde9ull, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0xbef9a3f7b2c67915ull, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0xc67178f2e372532bull, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, d, e, f, g, h, 0xca273eceea26619cull, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0xd186b8c721c0c207ull, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0xeada7dd6cde0eb1eull, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0xf57d4f7fee6ed178ull, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x06f067aa72176fbaull, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x0a637dc5a2c898a6ull, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x113f9804bef90daeull, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x1b710b35131c471bull, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0x28db77f523047d84ull, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0x32caab7b40c72493ull, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0x3c9ebe0a15c9bebcull, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0x431d67c49c100d4cull, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0x4cc5d4becb3e42b6ull, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0x597f299cfc657e2aull, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0x5fcb6fab3ad6faecull, w14 + sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x6c44198c4a475817ull, w15 + sigma1(w13) + w8 + sigma0(w0)); + + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + s[4] += e; + s[5] += f; + s[6] += g; + s[7] += h; +} + +} // namespace sha512 + +} // namespace + + +////// SHA-512 + +CSHA512::CSHA512() : bytes(0) +{ + sha512::Initialize(s); +} + +CSHA512& CSHA512::Write(const unsigned char* data, size_t len) +{ + const unsigned char* end = data + len; + size_t bufsize = bytes % 128; + if (bufsize && bufsize + len >= 128) { + // Fill the buffer, and process it. + memcpy(buf + bufsize, data, 128 - bufsize); + bytes += 128 - bufsize; + data += 128 - bufsize; + sha512::Transform(s, buf); + bufsize = 0; + } + while (end >= data + 128) { + // Process full chunks directly from the source. + sha512::Transform(s, data); + data += 128; + bytes += 128; + } + if (end > data) { + // Fill the buffer with what remains. + memcpy(buf + bufsize, data, end - data); + bytes += end - data; + } + return *this; +} + +void CSHA512::Finalize(unsigned char hash[OUTPUT_SIZE]) +{ + static const unsigned char pad[128] = {0x80}; + unsigned char sizedesc[16] = {0x00}; + WriteBE64(sizedesc + 8, bytes << 3); + Write(pad, 1 + ((239 - (bytes % 128)) % 128)); + Write(sizedesc, 16); + WriteBE64(hash, s[0]); + WriteBE64(hash + 8, s[1]); + WriteBE64(hash + 16, s[2]); + WriteBE64(hash + 24, s[3]); + WriteBE64(hash + 32, s[4]); + WriteBE64(hash + 40, s[5]); + WriteBE64(hash + 48, s[6]); + WriteBE64(hash + 56, s[7]); +} + +CSHA512& CSHA512::Reset() +{ + bytes = 0; + sha512::Initialize(s); + return *this; +} diff --git a/src/Native/libmultihash/equi/crypto/sha512.h b/src/Native/libmultihash/equi/crypto/sha512.h new file mode 100644 index 000000000..f1f17caf9 --- /dev/null +++ b/src/Native/libmultihash/equi/crypto/sha512.h @@ -0,0 +1,28 @@ +// Copyright (c) 2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CRYPTO_SHA512_H +#define BITCOIN_CRYPTO_SHA512_H + +#include +#include + +/** A hasher class for SHA-512. */ +class CSHA512 +{ +private: + uint64_t s[8]; + unsigned char buf[128]; + size_t bytes; + +public: + static const size_t OUTPUT_SIZE = 64; + + CSHA512(); + CSHA512& Write(const unsigned char* data, size_t len); + void Finalize(unsigned char hash[OUTPUT_SIZE]); + CSHA512& Reset(); +}; + +#endif // BITCOIN_CRYPTO_SHA512_H diff --git a/src/Native/libmultihash/equi/endian.h b/src/Native/libmultihash/equi/endian.h deleted file mode 100644 index f00d3b0cd..000000000 --- a/src/Native/libmultihash/equi/endian.h +++ /dev/null @@ -1,117 +0,0 @@ -// "License": Public Domain -// I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like. -// In case there are jurisdictions that don't support putting things in the public domain you can also consider it to -// be "dual licensed" under the BSD, MIT and Apache licenses, if you want to. This code is trivial anyway. Consider it -// an example on how to get the endian conversion functions on different platforms. - -#ifndef PORTABLE_ENDIAN_H__ -#define PORTABLE_ENDIAN_H__ - -#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) - -# define __WINDOWS__ - -#endif - -#if defined(__linux__) || defined(__CYGWIN__) - -# include - -#elif defined(__APPLE__) - -# include - -# define htobe16(x) OSSwapHostToBigInt16(x) -# define htole16(x) OSSwapHostToLittleInt16(x) -# define be16toh(x) OSSwapBigToHostInt16(x) -# define le16toh(x) OSSwapLittleToHostInt16(x) - -# define htobe32(x) OSSwapHostToBigInt32(x) -# define htole32(x) OSSwapHostToLittleInt32(x) -# define be32toh(x) OSSwapBigToHostInt32(x) -# define le32toh(x) OSSwapLittleToHostInt32(x) - -# define htobe64(x) OSSwapHostToBigInt64(x) -# define htole64(x) OSSwapHostToLittleInt64(x) -# define be64toh(x) OSSwapBigToHostInt64(x) -# define le64toh(x) OSSwapLittleToHostInt64(x) - -# define __BYTE_ORDER BYTE_ORDER -# define __BIG_ENDIAN BIG_ENDIAN -# define __LITTLE_ENDIAN LITTLE_ENDIAN -# define __PDP_ENDIAN PDP_ENDIAN - -#elif defined(__OpenBSD__) - -# include - -#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) - -# include - -# define be16toh(x) betoh16(x) -# define le16toh(x) letoh16(x) - -# define be32toh(x) betoh32(x) -# define le32toh(x) letoh32(x) - -# define be64toh(x) betoh64(x) -# define le64toh(x) letoh64(x) - -#elif defined(__WINDOWS__) - -# include - -# if BYTE_ORDER == LITTLE_ENDIAN - -# define htobe16(x) htons(x) -# define htole16(x) (x) -# define be16toh(x) ntohs(x) -# define le16toh(x) (x) - -# define htobe32(x) htonl(x) -# define htole32(x) (x) -# define be32toh(x) ntohl(x) -# define le32toh(x) (x) - -# define htobe64(x) htonll(x) -# define htole64(x) (x) -# define be64toh(x) ntohll(x) -# define le64toh(x) (x) - -# elif BYTE_ORDER == BIG_ENDIAN - - /* that would be xbox 360 */ -# define htobe16(x) (x) -# define htole16(x) __builtin_bswap16(x) -# define be16toh(x) (x) -# define le16toh(x) __builtin_bswap16(x) - -# define htobe32(x) (x) -# define htole32(x) __builtin_bswap32(x) -# define be32toh(x) (x) -# define le32toh(x) __builtin_bswap32(x) - -# define htobe64(x) (x) -# define htole64(x) __builtin_bswap64(x) -# define be64toh(x) (x) -# define le64toh(x) __builtin_bswap64(x) - -# else - -# error byte order not supported - -# endif - -# define __BYTE_ORDER BYTE_ORDER -# define __BIG_ENDIAN BIG_ENDIAN -# define __LITTLE_ENDIAN LITTLE_ENDIAN -# define __PDP_ENDIAN PDP_ENDIAN - -#else - -# error platform not supported - -#endif - -#endif \ No newline at end of file diff --git a/src/Native/libmultihash/equi/equi.c b/src/Native/libmultihash/equi/equi.c deleted file mode 100644 index 3174503a2..000000000 --- a/src/Native/libmultihash/equi/equi.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2016 abc at openwall dot com - * Copyright (c) 2016 Jack Grigg - * Copyright (c) 2016 The Zcash developers - * - * Distributed under the MIT software license, see the accompanying - * file COPYING or http://www.opensource.org/licenses/mit-license.php. - * - * Port to C of C++ implementation of the Equihash Proof-of-Work - * algorithm from zcashd. - */ - -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#define alloca(x) _alloca(x) -#endif - -#include - -#include "endian.h" - -static void digestInit(crypto_generichash_blake2b_state *S, const int n, const int k) { - uint32_t le_N = htole32(n); - uint32_t le_K = htole32(k); - - // No VLAs with VC ;-( -#ifdef _MSC_VER - unsigned char *personalization = (unsigned char *) alloca(sizeof(unsigned char) * crypto_generichash_blake2b_PERSONALBYTES); -#else - unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {}; -#endif - - memcpy(personalization, "ZcashPoW", 9); - memcpy(personalization + 8, &le_N, 4); - memcpy(personalization + 12, &le_K, 4); - crypto_generichash_blake2b_init_salt_personal(S, - NULL, 0, (512 / n) * n / 8, NULL, personalization); -} - -static void expandArray(const unsigned char *in, const size_t in_len, - unsigned char *out, const size_t out_len, - const size_t bit_len, const size_t byte_pad) -{ - assert(bit_len >= 8); - assert(8 * sizeof(uint32_t) >= 7 + bit_len); - - const size_t out_width = (bit_len + 7) / 8 + byte_pad; - assert(out_len == 8 * out_width * in_len / bit_len); - - const uint32_t bit_len_mask = ((uint32_t)1 << bit_len) - 1; - - // The acc_bits least-significant bits of acc_value represent a bit sequence - // in big-endian order. - size_t acc_bits = 0; - uint32_t acc_value = 0; - - size_t j = 0; - for (size_t i = 0; i < in_len; i++) { - acc_value = (acc_value << 8) | in[i]; - acc_bits += 8; - - // When we have bit_len or more bits in the accumulator, write the next - // output element. - if (acc_bits >= bit_len) { - acc_bits -= bit_len; - for (size_t x = 0; x < byte_pad; x++) { - out[j + x] = 0; - } - for (size_t x = byte_pad; x < out_width; x++) { - out[j + x] = ( - // Big-endian - acc_value >> (acc_bits + (8 * (out_width - x - 1))) - ) & ( - // Apply bit_len_mask across byte boundaries - (bit_len_mask >> (8 * (out_width - x - 1))) & 0xFF - ); - } - j += out_width; - } - } -} - -static int isZero(const uint8_t *hash, size_t len) { - // This doesn't need to be constant time. - for (int i = 0; i < len; i++) { - if (hash[i] != 0) - return 0; - } - return 1; -} - -static void generateHash(crypto_generichash_blake2b_state *S, const uint32_t g, uint8_t *hash, const size_t hashLen) { - const uint32_t le_g = htole32(g); - crypto_generichash_blake2b_state digest = *S; /* copy */ - - crypto_generichash_blake2b_update(&digest, (uint8_t *)&le_g, sizeof(le_g)); - crypto_generichash_blake2b_final(&digest, hash, hashLen); -} - -// hdr -> header including nonce (140 bytes) -// soln -> equihash solution (excluding 3 bytes with size, so 1344 bytes length) -bool verifyEH(const char *hdr, const char *soln) { - const int n = 200; - const int k = 9; - const int collisionBitLength = n / (k + 1); - const int collisionByteLength = (collisionBitLength + 7) / 8; - const int hashLength = (k + 1) * collisionByteLength; - const int indicesPerHashOutput = 512 / n; - const int hashOutput = indicesPerHashOutput * n / 8; - const int equihashSolutionSize = (1 << k) * (n / (k + 1) + 1) / 8; - const int solnr = 1 << k; - uint32_t indices[512]; - - crypto_generichash_blake2b_state state; - digestInit(&state, n, k); - crypto_generichash_blake2b_update(&state, hdr, 140); - - expandArray(soln, equihashSolutionSize, (char *)&indices, sizeof(indices), collisionBitLength + 1, 1); - - // No VLAs with VC ;-( -#ifdef _MSC_VER - uint8_t *vHash = (uint8_t*)alloca(sizeof(uint8_t) * hashLength); - memset(vHash, 0, sizeof(uint8_t) * hashLength); - - uint8_t *tmpHash = (uint8_t*)alloca(sizeof(uint8_t) * hashOutput); - memset(tmpHash, 0, sizeof(uint8_t) * hashOutput); - - uint8_t *hash = (uint8_t*)alloca(sizeof(uint8_t) * hashLength); - memset(hash, 0, sizeof(uint8_t) * hashLength); - - for (int j = 0; j < solnr; j++) { - int i = be32toh(indices[j]); - generateHash(&state, i / indicesPerHashOutput, tmpHash, hashOutput); - expandArray(tmpHash + (i % indicesPerHashOutput * n / 8), n / 8, hash, hashLength, collisionBitLength, 0); - for (int k = 0; k < hashLength; ++k) - vHash[k] ^= hash[k]; - } -#else - uint8_t vHash[hashLength]; - memset(vHash, 0 , sizeof(vHash)); - for (int j = 0; j < solnr; j++) { - uint8_t tmpHash[hashOutput]; - uint8_t hash[hashLength]; - int i = be32toh(indices[j]); - generateHash(&state, i / indicesPerHashOutput, tmpHash, hashOutput); - expandArray(tmpHash + (i % indicesPerHashOutput * n / 8), n / 8, hash, hashLength, collisionBitLength, 0); - for (int k = 0; k < hashLength; ++k) - vHash[k] ^= hash[k]; - } -#endif - - return isZero(vHash, sizeof(vHash)); -} diff --git a/src/Native/libmultihash/equi/equi.h b/src/Native/libmultihash/equi/equi.h deleted file mode 100644 index e14114675..000000000 --- a/src/Native/libmultihash/equi/equi.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2016 abc at openwall dot com - * Copyright (c) 2016 Jack Grigg - * Copyright (c) 2016 The Zcash developers - * - * Distributed under the MIT software license, see the accompanying - * file COPYING or http://www.opensource.org/licenses/mit-license.php. - * - * Port to C of C++ implementation of the Equihash Proof-of-Work - * algorithm from zcashd. - */ - -#ifndef EQUI_H -#define EQUI_H - -#ifdef __cplusplus -extern "C" { -#endif - - -#include -#include -#include -#include - -#include - -#include "endian.h" - -static void digestInit(crypto_generichash_blake2b_state *S, const int n, const int k); - -static void expandArray(const unsigned char *in, const size_t in_len, - unsigned char *out, const size_t out_len, - const size_t bit_len, const size_t byte_pad); - -static int isZero(const uint8_t *hash, size_t len); - -static void generateHash(crypto_generichash_blake2b_state *S, const uint32_t g, uint8_t *hash, const size_t hashLen); - -// hdr -> header including nonce (140 bytes) -// soln -> equihash solution (excluding 3 bytes with size, so 1344 bytes length) -bool verifyEH(const char *hdr, const char *soln); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/Native/libmultihash/equi/equihashverify.cc b/src/Native/libmultihash/equi/equihashverify.cc new file mode 100644 index 000000000..0d9423d83 --- /dev/null +++ b/src/Native/libmultihash/equi/equihashverify.cc @@ -0,0 +1,18 @@ +#include +#include "crypto/equihash.h" +#include "equihashverify.h" + +bool verifyEH(const char *hdr, const std::vector &soln){ + unsigned int n = 200; + unsigned int k = 9; + + // Hash state + crypto_generichash_blake2b_state state; + EhInitialiseState(n, k, state); + + crypto_generichash_blake2b_update(&state, (const unsigned char*)hdr, 140); + + bool isValid = Eh200_9.IsValidSolution(state, soln); + + return isValid; +} diff --git a/src/Native/libmultihash/equi/equihashverify.h b/src/Native/libmultihash/equi/equihashverify.h new file mode 100644 index 000000000..ee5b4b664 --- /dev/null +++ b/src/Native/libmultihash/equi/equihashverify.h @@ -0,0 +1,16 @@ +#ifndef EQUIHASHVERIFY_H +#define EQUIHASHVERIFY_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool verifyEH(const char*, const std::vector&); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/Native/libmultihash/equi/random.cpp b/src/Native/libmultihash/equi/random.cpp new file mode 100644 index 000000000..aa16c81dd --- /dev/null +++ b/src/Native/libmultihash/equi/random.cpp @@ -0,0 +1,91 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "random.h" + +#include "support/cleanse.h" +#include "serialize.h" // for begin_ptr(vec) +#include "util.h" // for LogPrint() +#include "utilstrencodings.h" // for GetTime() + +#include + +#ifndef WIN32 +#include +#else +#undef max +#endif + +#include "sodium.h" + +static inline int64_t GetPerformanceCounter() +{ + int64_t nCounter = 0; +#ifdef WIN32 + QueryPerformanceCounter((LARGE_INTEGER*)&nCounter); +#else + timeval t; + gettimeofday(&t, NULL); + nCounter = (int64_t)(t.tv_sec * 1000000 + t.tv_usec); +#endif + return nCounter; +} + +void GetRandBytes(unsigned char* buf, size_t num) +{ + randombytes_buf(buf, num); +} + +uint64_t GetRand(uint64_t nMax) +{ + if (nMax == 0) + return 0; + + // The range of the random source must be a multiple of the modulus + // to give every possible output value an equal possibility + uint64_t nRange = (std::numeric_limits::max() / nMax) * nMax; + uint64_t nRand = 0; + do { + GetRandBytes((unsigned char*)&nRand, sizeof(nRand)); + } while (nRand >= nRange); + return (nRand % nMax); +} + +int GetRandInt(int nMax) +{ + return GetRand(nMax); +} + +uint256 GetRandHash() +{ + uint256 hash; + GetRandBytes((unsigned char*)&hash, sizeof(hash)); + return hash; +} + +uint32_t insecure_rand_Rz = 11; +uint32_t insecure_rand_Rw = 11; +void seed_insecure_rand(bool fDeterministic) +{ + // The seed values have some unlikely fixed points which we avoid. + if (fDeterministic) { + insecure_rand_Rz = insecure_rand_Rw = 11; + } else { + uint32_t tmp; + do { + GetRandBytes((unsigned char*)&tmp, 4); + } while (tmp == 0 || tmp == 0x9068ffffU); + insecure_rand_Rz = tmp; + do { + GetRandBytes((unsigned char*)&tmp, 4); + } while (tmp == 0 || tmp == 0x464fffffU); + insecure_rand_Rw = tmp; + } +} + +int GenIdentity(int n) +{ + return n-1; +} diff --git a/src/Native/libmultihash/equi/random.h b/src/Native/libmultihash/equi/random.h new file mode 100644 index 000000000..8cec678ef --- /dev/null +++ b/src/Native/libmultihash/equi/random.h @@ -0,0 +1,74 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RANDOM_H +#define BITCOIN_RANDOM_H + +#include "uint256.h" + +#include +#include + +/** + * Functions to gather random data via the libsodium CSPRNG + */ +void GetRandBytes(unsigned char* buf, size_t num); +uint64_t GetRand(uint64_t nMax); +int GetRandInt(int nMax); +uint256 GetRandHash(); + +/** + * Identity function for MappedShuffle, so that elements retain their original order. + */ + int GenIdentity(int n); + +/** + * Rearranges the elements in the range [first,first+len) randomly, assuming + * that gen is a uniform random number generator. Follows the same algorithm as + * std::shuffle in C++11 (a Durstenfeld shuffle). + * + * The elements in the range [mapFirst,mapFirst+len) are rearranged according to + * the same permutation, enabling the permutation to be tracked by the caller. + * + * gen takes an integer n and produces a uniform random output in [0,n). + */ +template +void MappedShuffle(RandomAccessIterator first, + MapRandomAccessIterator mapFirst, + size_t len, + std::function gen) +{ + for (size_t i = len-1; i > 0; --i) { + auto r = gen(i+1); + assert(r >= 0); + assert(r <= i); + std::swap(first[i], first[r]); + std::swap(mapFirst[i], mapFirst[r]); + } +} + +/** + * Seed insecure_rand using the random pool. + * @param Deterministic Use a deterministic seed + */ +void seed_insecure_rand(bool fDeterministic = false); + +/** + * MWC RNG of George Marsaglia + * This is intended to be fast. It has a period of 2^59.3, though the + * least significant 16 bits only have a period of about 2^30.1. + * + * @return random value + */ +extern uint32_t insecure_rand_Rz; +extern uint32_t insecure_rand_Rw; +static inline uint32_t insecure_rand(void) +{ + insecure_rand_Rz = 36969 * (insecure_rand_Rz & 65535) + (insecure_rand_Rz >> 16); + insecure_rand_Rw = 18000 * (insecure_rand_Rw & 65535) + (insecure_rand_Rw >> 16); + return (insecure_rand_Rw << 16) + insecure_rand_Rz; +} + +#endif // BITCOIN_RANDOM_H diff --git a/src/Native/libmultihash/equi/serialize.h b/src/Native/libmultihash/equi/serialize.h new file mode 100644 index 000000000..8a73e46c0 --- /dev/null +++ b/src/Native/libmultihash/equi/serialize.h @@ -0,0 +1,1002 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_SERIALIZE_H +#define BITCOIN_SERIALIZE_H + +#ifndef _WIN32 +#include +#else +#include "portable_endian.h" +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +class CScript; + +static const unsigned int MAX_SIZE = 0x02000000; + +/** + * Used to bypass the rule against non-const reference to temporary + * where it makes sense with wrappers such as CFlatData or CTxDB + */ +template +inline T& REF(const T& val) +{ + return const_cast(val); +} + +/** + * Used to acquire a non-const pointer "this" to generate bodies + * of const serialization operations from a template + */ +template +inline T* NCONST_PTR(const T* val) +{ + return const_cast(val); +} + +/** + * Get begin pointer of vector (non-const version). + * @note These functions avoid the undefined case of indexing into an empty + * vector, as well as that of indexing after the end of the vector. + */ +template +inline T* begin_ptr(std::vector& v) +{ + return v.empty() ? NULL : &v[0]; +} +/** Get begin pointer of vector (const version) */ +template +inline const T* begin_ptr(const std::vector& v) +{ + return v.empty() ? NULL : &v[0]; +} +/** Get end pointer of vector (non-const version) */ +template +inline T* end_ptr(std::vector& v) +{ + return v.empty() ? NULL : (&v[0] + v.size()); +} +/** Get end pointer of vector (const version) */ +template +inline const T* end_ptr(const std::vector& v) +{ + return v.empty() ? NULL : (&v[0] + v.size()); +} + +/* + * Lowest-level serialization and conversion. + * @note Sizes of these types are verified in the tests + */ +template inline void ser_writedata8(Stream &s, uint8_t obj) +{ + s.write((char*)&obj, 1); +} +template inline void ser_writedata16(Stream &s, uint16_t obj) +{ + obj = htole16(obj); + s.write((char*)&obj, 2); +} +template inline void ser_writedata32(Stream &s, uint32_t obj) +{ + obj = htole32(obj); + s.write((char*)&obj, 4); +} +template inline void ser_writedata64(Stream &s, uint64_t obj) +{ + obj = htole64(obj); + s.write((char*)&obj, 8); +} +template inline uint8_t ser_readdata8(Stream &s) +{ + uint8_t obj; + s.read((char*)&obj, 1); + return obj; +} +template inline uint16_t ser_readdata16(Stream &s) +{ + uint16_t obj; + s.read((char*)&obj, 2); + return le16toh(obj); +} +template inline uint32_t ser_readdata32(Stream &s) +{ + uint32_t obj; + s.read((char*)&obj, 4); + return le32toh(obj); +} +template inline uint64_t ser_readdata64(Stream &s) +{ + uint64_t obj; + s.read((char*)&obj, 8); + return le64toh(obj); +} +inline uint64_t ser_double_to_uint64(double x) +{ + union { double x; uint64_t y; } tmp; + tmp.x = x; + return tmp.y; +} +inline uint32_t ser_float_to_uint32(float x) +{ + union { float x; uint32_t y; } tmp; + tmp.x = x; + return tmp.y; +} +inline double ser_uint64_to_double(uint64_t y) +{ + union { double x; uint64_t y; } tmp; + tmp.y = y; + return tmp.x; +} +inline float ser_uint32_to_float(uint32_t y) +{ + union { float x; uint32_t y; } tmp; + tmp.y = y; + return tmp.x; +} + + +///////////////////////////////////////////////////////////////// +// +// Templates for serializing to anything that looks like a stream, +// i.e. anything that supports .read(char*, size_t) and .write(char*, size_t) +// + +enum +{ + // primary actions + SER_NETWORK = (1 << 0), + SER_DISK = (1 << 1), + SER_GETHASH = (1 << 2), +}; + +#define READWRITE(obj) (::SerReadWrite(s, (obj), nType, nVersion, ser_action)) + +/** + * Implement three methods for serializable objects. These are actually wrappers over + * "SerializationOp" template, which implements the body of each class' serialization + * code. Adding "ADD_SERIALIZE_METHODS" in the body of the class causes these wrappers to be + * added as members. + */ +#define ADD_SERIALIZE_METHODS \ + size_t GetSerializeSize(int nType, int nVersion) const { \ + CSizeComputer s(nType, nVersion); \ + NCONST_PTR(this)->SerializationOp(s, CSerActionSerialize(), nType, nVersion);\ + return s.size(); \ + } \ + template \ + void Serialize(Stream& s, int nType, int nVersion) const { \ + NCONST_PTR(this)->SerializationOp(s, CSerActionSerialize(), nType, nVersion);\ + } \ + template \ + void Unserialize(Stream& s, int nType, int nVersion) { \ + SerializationOp(s, CSerActionUnserialize(), nType, nVersion); \ + } + +/* + * Basic Types + */ +inline unsigned int GetSerializeSize(char a, int, int=0) { return 1; } +inline unsigned int GetSerializeSize(int8_t a, int, int=0) { return 1; } +inline unsigned int GetSerializeSize(uint8_t a, int, int=0) { return 1; } +inline unsigned int GetSerializeSize(int16_t a, int, int=0) { return 2; } +inline unsigned int GetSerializeSize(uint16_t a, int, int=0) { return 2; } +inline unsigned int GetSerializeSize(int32_t a, int, int=0) { return 4; } +inline unsigned int GetSerializeSize(uint32_t a, int, int=0) { return 4; } +inline unsigned int GetSerializeSize(int64_t a, int, int=0) { return 8; } +inline unsigned int GetSerializeSize(uint64_t a, int, int=0) { return 8; } +inline unsigned int GetSerializeSize(float a, int, int=0) { return 4; } +inline unsigned int GetSerializeSize(double a, int, int=0) { return 8; } + +template inline void Serialize(Stream& s, char a, int, int=0) { ser_writedata8(s, a); } // TODO Get rid of bare char +template inline void Serialize(Stream& s, int8_t a, int, int=0) { ser_writedata8(s, a); } +template inline void Serialize(Stream& s, uint8_t a, int, int=0) { ser_writedata8(s, a); } +template inline void Serialize(Stream& s, int16_t a, int, int=0) { ser_writedata16(s, a); } +template inline void Serialize(Stream& s, uint16_t a, int, int=0) { ser_writedata16(s, a); } +template inline void Serialize(Stream& s, int32_t a, int, int=0) { ser_writedata32(s, a); } +template inline void Serialize(Stream& s, uint32_t a, int, int=0) { ser_writedata32(s, a); } +template inline void Serialize(Stream& s, int64_t a, int, int=0) { ser_writedata64(s, a); } +template inline void Serialize(Stream& s, uint64_t a, int, int=0) { ser_writedata64(s, a); } +template inline void Serialize(Stream& s, float a, int, int=0) { ser_writedata32(s, ser_float_to_uint32(a)); } +template inline void Serialize(Stream& s, double a, int, int=0) { ser_writedata64(s, ser_double_to_uint64(a)); } + +template inline void Unserialize(Stream& s, char& a, int, int=0) { a = ser_readdata8(s); } // TODO Get rid of bare char +template inline void Unserialize(Stream& s, int8_t& a, int, int=0) { a = ser_readdata8(s); } +template inline void Unserialize(Stream& s, uint8_t& a, int, int=0) { a = ser_readdata8(s); } +template inline void Unserialize(Stream& s, int16_t& a, int, int=0) { a = ser_readdata16(s); } +template inline void Unserialize(Stream& s, uint16_t& a, int, int=0) { a = ser_readdata16(s); } +template inline void Unserialize(Stream& s, int32_t& a, int, int=0) { a = ser_readdata32(s); } +template inline void Unserialize(Stream& s, uint32_t& a, int, int=0) { a = ser_readdata32(s); } +template inline void Unserialize(Stream& s, int64_t& a, int, int=0) { a = ser_readdata64(s); } +template inline void Unserialize(Stream& s, uint64_t& a, int, int=0) { a = ser_readdata64(s); } +template inline void Unserialize(Stream& s, float& a, int, int=0) { a = ser_uint32_to_float(ser_readdata32(s)); } +template inline void Unserialize(Stream& s, double& a, int, int=0) { a = ser_uint64_to_double(ser_readdata64(s)); } + +inline unsigned int GetSerializeSize(bool a, int, int=0) { return sizeof(char); } +template inline void Serialize(Stream& s, bool a, int, int=0) { char f=a; ser_writedata8(s, f); } +template inline void Unserialize(Stream& s, bool& a, int, int=0) { char f=ser_readdata8(s); a=f; } + + + + + + +/** + * Compact Size + * size < 253 -- 1 byte + * size <= 0xFFFF -- 3 bytes (253 + 2 bytes) + * size <= 0xFFFFFFFF -- 5 bytes (254 + 4 bytes) + * size > 0xFFFFFFFF -- 9 bytes (255 + 8 bytes) + */ +inline unsigned int GetSizeOfCompactSize(uint64_t nSize) +{ + if (nSize < 253) return 1; + else if (nSize <= 0xFFFFu) return 3; + else if (nSize <= 0xFFFFFFFFu) return 5; + else return 9; +} + +template +void WriteCompactSize(Stream& os, uint64_t nSize) +{ + if (nSize < 253) + { + ser_writedata8(os, nSize); + } + else if (nSize <= 0xFFFFu) + { + ser_writedata8(os, 253); + ser_writedata16(os, nSize); + } + else if (nSize <= 0xFFFFFFFFu) + { + ser_writedata8(os, 254); + ser_writedata32(os, nSize); + } + else + { + ser_writedata8(os, 255); + ser_writedata64(os, nSize); + } +} + +template +uint64_t ReadCompactSize(Stream& is) +{ + uint8_t chSize = ser_readdata8(is); + uint64_t nSizeRet = 0; + if (chSize < 253) + { + nSizeRet = chSize; + } + else if (chSize == 253) + { + nSizeRet = ser_readdata16(is); + if (nSizeRet < 253) + throw std::ios_base::failure("non-canonical ReadCompactSize()"); + } + else if (chSize == 254) + { + nSizeRet = ser_readdata32(is); + if (nSizeRet < 0x10000u) + throw std::ios_base::failure("non-canonical ReadCompactSize()"); + } + else + { + nSizeRet = ser_readdata64(is); + if (nSizeRet < 0x100000000ULL) + throw std::ios_base::failure("non-canonical ReadCompactSize()"); + } + if (nSizeRet > (uint64_t)MAX_SIZE) + throw std::ios_base::failure("ReadCompactSize(): size too large"); + return nSizeRet; +} + +/** + * Variable-length integers: bytes are a MSB base-128 encoding of the number. + * The high bit in each byte signifies whether another digit follows. To make + * sure the encoding is one-to-one, one is subtracted from all but the last digit. + * Thus, the byte sequence a[] with length len, where all but the last byte + * has bit 128 set, encodes the number: + * + * (a[len-1] & 0x7F) + sum(i=1..len-1, 128^i*((a[len-i-1] & 0x7F)+1)) + * + * Properties: + * * Very small (0-127: 1 byte, 128-16511: 2 bytes, 16512-2113663: 3 bytes) + * * Every integer has exactly one encoding + * * Encoding does not depend on size of original integer type + * * No redundancy: every (infinite) byte sequence corresponds to a list + * of encoded integers. + * + * 0: [0x00] 256: [0x81 0x00] + * 1: [0x01] 16383: [0xFE 0x7F] + * 127: [0x7F] 16384: [0xFF 0x00] + * 128: [0x80 0x00] 16511: [0x80 0xFF 0x7F] + * 255: [0x80 0x7F] 65535: [0x82 0xFD 0x7F] + * 2^32: [0x8E 0xFE 0xFE 0xFF 0x00] + */ + +template +inline unsigned int GetSizeOfVarInt(I n) +{ + int nRet = 0; + while(true) { + nRet++; + if (n <= 0x7F) + break; + n = (n >> 7) - 1; + } + return nRet; +} + +template +void WriteVarInt(Stream& os, I n) +{ + unsigned char tmp[(sizeof(n)*8+6)/7]; + int len=0; + while(true) { + tmp[len] = (n & 0x7F) | (len ? 0x80 : 0x00); + if (n <= 0x7F) + break; + n = (n >> 7) - 1; + len++; + } + do { + ser_writedata8(os, tmp[len]); + } while(len--); +} + +template +I ReadVarInt(Stream& is) +{ + I n = 0; + while(true) { + unsigned char chData = ser_readdata8(is); + n = (n << 7) | (chData & 0x7F); + if (chData & 0x80) + n++; + else + return n; + } +} + +#define FLATDATA(obj) REF(CFlatData((char*)&(obj), (char*)&(obj) + sizeof(obj))) +#define VARINT(obj) REF(WrapVarInt(REF(obj))) +#define LIMITED_STRING(obj,n) REF(LimitedString< n >(REF(obj))) + +/** + * Wrapper for serializing arrays and POD. + */ +class CFlatData +{ +protected: + char* pbegin; + char* pend; +public: + CFlatData(void* pbeginIn, void* pendIn) : pbegin((char*)pbeginIn), pend((char*)pendIn) { } + template + explicit CFlatData(std::vector &v) + { + pbegin = (char*)begin_ptr(v); + pend = (char*)end_ptr(v); + } + char* begin() { return pbegin; } + const char* begin() const { return pbegin; } + char* end() { return pend; } + const char* end() const { return pend; } + + unsigned int GetSerializeSize(int, int=0) const + { + return pend - pbegin; + } + + template + void Serialize(Stream& s, int, int=0) const + { + s.write(pbegin, pend - pbegin); + } + + template + void Unserialize(Stream& s, int, int=0) + { + s.read(pbegin, pend - pbegin); + } +}; + +template +class CVarInt +{ +protected: + I &n; +public: + CVarInt(I& nIn) : n(nIn) { } + + unsigned int GetSerializeSize(int, int) const { + return GetSizeOfVarInt(n); + } + + template + void Serialize(Stream &s, int, int) const { + WriteVarInt(s, n); + } + + template + void Unserialize(Stream& s, int, int) { + n = ReadVarInt(s); + } +}; + +template +class LimitedString +{ +protected: + std::string& string; +public: + LimitedString(std::string& string) : string(string) {} + + template + void Unserialize(Stream& s, int, int=0) + { + size_t size = ReadCompactSize(s); + if (size > Limit) { + throw std::ios_base::failure("String length limit exceeded"); + } + string.resize(size); + if (size != 0) + s.read((char*)&string[0], size); + } + + template + void Serialize(Stream& s, int, int=0) const + { + WriteCompactSize(s, string.size()); + if (!string.empty()) + s.write((char*)&string[0], string.size()); + } + + unsigned int GetSerializeSize(int, int=0) const + { + return GetSizeOfCompactSize(string.size()) + string.size(); + } +}; + +template +CVarInt WrapVarInt(I& n) { return CVarInt(n); } + +/** + * Forward declarations + */ + +/** + * string + */ +template unsigned int GetSerializeSize(const std::basic_string& str, int, int=0); +template void Serialize(Stream& os, const std::basic_string& str, int, int=0); +template void Unserialize(Stream& is, std::basic_string& str, int, int=0); + +/** + * vector + * vectors of unsigned char are a special case and are intended to be serialized as a single opaque blob. + */ +template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const unsigned char&); +template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const V&); +template inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion); +template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const unsigned char&); +template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const V&); +template inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion); +template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const unsigned char&); +template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const V&); +template inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion); + +/** + * others derived from vector + */ +extern inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion); +template void Serialize(Stream& os, const CScript& v, int nType, int nVersion); +template void Unserialize(Stream& is, CScript& v, int nType, int nVersion); + +/** + * optional + */ +template unsigned int GetSerializeSize(const boost::optional &item, int nType, int nVersion); +template void Serialize(Stream& os, const boost::optional& item, int nType, int nVersion); +template void Unserialize(Stream& is, boost::optional& item, int nType, int nVersion); + +/** + * array + */ +template unsigned int GetSerializeSize(const boost::array &item, int nType, int nVersion); +template void Serialize(Stream& os, const boost::array& item, int nType, int nVersion); +template void Unserialize(Stream& is, boost::array& item, int nType, int nVersion); + +/** + * pair + */ +template unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion); +template void Serialize(Stream& os, const std::pair& item, int nType, int nVersion); +template void Unserialize(Stream& is, std::pair& item, int nType, int nVersion); + +/** + * map + */ +template unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion); +template void Serialize(Stream& os, const std::map& m, int nType, int nVersion); +template void Unserialize(Stream& is, std::map& m, int nType, int nVersion); + +/** + * set + */ +template unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion); +template void Serialize(Stream& os, const std::set& m, int nType, int nVersion); +template void Unserialize(Stream& is, std::set& m, int nType, int nVersion); + +/** + * list + */ +template unsigned int GetSerializeSize(const std::list& m, int nType, int nVersion); +template void Serialize(Stream& os, const std::list& m, int nType, int nVersion); +template void Unserialize(Stream& is, std::list& m, int nType, int nVersion); + + + + + +/** + * If none of the specialized versions above matched, default to calling member function. + * "int nType" is changed to "long nType" to keep from getting an ambiguous overload error. + * The compiler will only cast int to long if none of the other templates matched. + * Thanks to Boost serialization for this idea. + */ +template +inline unsigned int GetSerializeSize(const T& a, long nType, int nVersion) +{ + return a.GetSerializeSize((int)nType, nVersion); +} + +template +inline void Serialize(Stream& os, const T& a, long nType, int nVersion) +{ + a.Serialize(os, (int)nType, nVersion); +} + +template +inline void Unserialize(Stream& is, T& a, long nType, int nVersion) +{ + a.Unserialize(is, (int)nType, nVersion); +} + + + + + +/** + * string + */ +template +unsigned int GetSerializeSize(const std::basic_string& str, int, int) +{ + return GetSizeOfCompactSize(str.size()) + str.size() * sizeof(str[0]); +} + +template +void Serialize(Stream& os, const std::basic_string& str, int, int) +{ + WriteCompactSize(os, str.size()); + if (!str.empty()) + os.write((char*)&str[0], str.size() * sizeof(str[0])); +} + +template +void Unserialize(Stream& is, std::basic_string& str, int, int) +{ + unsigned int nSize = ReadCompactSize(is); + str.resize(nSize); + if (nSize != 0) + is.read((char*)&str[0], nSize * sizeof(str[0])); +} + + + +/** + * vector + */ +template +unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const unsigned char&) +{ + return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T)); +} + +template +unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const V&) +{ + unsigned int nSize = GetSizeOfCompactSize(v.size()); + for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) + nSize += GetSerializeSize((*vi), nType, nVersion); + return nSize; +} + +template +inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion) +{ + return GetSerializeSize_impl(v, nType, nVersion, T()); +} + + +template +void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const unsigned char&) +{ + WriteCompactSize(os, v.size()); + if (!v.empty()) + os.write((char*)&v[0], v.size() * sizeof(T)); +} + +template +void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const V&) +{ + WriteCompactSize(os, v.size()); + for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) + ::Serialize(os, (*vi), nType, nVersion); +} + +template +inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion) +{ + Serialize_impl(os, v, nType, nVersion, T()); +} + + +template +void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const unsigned char&) +{ + // Limit size per read so bogus size value won't cause out of memory + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + while (i < nSize) + { + unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T))); + v.resize(i + blk); + is.read((char*)&v[i], blk * sizeof(T)); + i += blk; + } +} + +template +void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const V&) +{ + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + unsigned int nMid = 0; + while (nMid < nSize) + { + nMid += 5000000 / sizeof(T); + if (nMid > nSize) + nMid = nSize; + v.resize(nMid); + for (; i < nMid; i++) + Unserialize(is, v[i], nType, nVersion); + } +} + +template +inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion) +{ + Unserialize_impl(is, v, nType, nVersion, T()); +} + + + +/** + * others derived from vector + */ +inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion) +{ + return GetSerializeSize((const std::vector&)v, nType, nVersion); +} + +template +void Serialize(Stream& os, const CScript& v, int nType, int nVersion) +{ + Serialize(os, (const std::vector&)v, nType, nVersion); +} + +template +void Unserialize(Stream& is, CScript& v, int nType, int nVersion) +{ + Unserialize(is, (std::vector&)v, nType, nVersion); +} + + + +/** + * optional + */ +template +unsigned int GetSerializeSize(const boost::optional &item, int nType, int nVersion) +{ + if (item) { + return 1 + GetSerializeSize(*item, nType, nVersion); + } else { + return 1; + } +} + +template +void Serialize(Stream& os, const boost::optional& item, int nType, int nVersion) +{ + // If the value is there, put 0x01 and then serialize the value. + // If it's not, put 0x00. + if (item) { + unsigned char discriminant = 0x01; + Serialize(os, discriminant, nType, nVersion); + Serialize(os, *item, nType, nVersion); + } else { + unsigned char discriminant = 0x00; + Serialize(os, discriminant, nType, nVersion); + } +} + +template +void Unserialize(Stream& is, boost::optional& item, int nType, int nVersion) +{ + unsigned char discriminant = 0x00; + Unserialize(is, discriminant, nType, nVersion); + + if (discriminant == 0x00) { + item = boost::none; + } else if (discriminant == 0x01) { + T object; + Unserialize(is, object, nType, nVersion); + item = object; + } else { + throw std::ios_base::failure("non-canonical optional discriminant"); + } +} + + + +/** + * array + */ +template +unsigned int GetSerializeSize(const boost::array &item, int nType, int nVersion) +{ + unsigned int size = 0; + for (size_t i = 0; i < N; i++) { + size += GetSerializeSize(item[0], nType, nVersion); + } + return size; +} + +template +void Serialize(Stream& os, const boost::array& item, int nType, int nVersion) +{ + for (size_t i = 0; i < N; i++) { + Serialize(os, item[i], nType, nVersion); + } +} + +template +void Unserialize(Stream& is, boost::array& item, int nType, int nVersion) +{ + for (size_t i = 0; i < N; i++) { + Unserialize(is, item[i], nType, nVersion); + } +} + + +/** + * pair + */ +template +unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion) +{ + return GetSerializeSize(item.first, nType, nVersion) + GetSerializeSize(item.second, nType, nVersion); +} + +template +void Serialize(Stream& os, const std::pair& item, int nType, int nVersion) +{ + Serialize(os, item.first, nType, nVersion); + Serialize(os, item.second, nType, nVersion); +} + +template +void Unserialize(Stream& is, std::pair& item, int nType, int nVersion) +{ + Unserialize(is, item.first, nType, nVersion); + Unserialize(is, item.second, nType, nVersion); +} + + + +/** + * map + */ +template +unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion) +{ + unsigned int nSize = GetSizeOfCompactSize(m.size()); + for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) + nSize += GetSerializeSize((*mi), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const std::map& m, int nType, int nVersion) +{ + WriteCompactSize(os, m.size()); + for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) + Serialize(os, (*mi), nType, nVersion); +} + +template +void Unserialize(Stream& is, std::map& m, int nType, int nVersion) +{ + m.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::map::iterator mi = m.begin(); + for (unsigned int i = 0; i < nSize; i++) + { + std::pair item; + Unserialize(is, item, nType, nVersion); + mi = m.insert(mi, item); + } +} + + + +/** + * set + */ +template +unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion) +{ + unsigned int nSize = GetSizeOfCompactSize(m.size()); + for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) + nSize += GetSerializeSize((*it), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const std::set& m, int nType, int nVersion) +{ + WriteCompactSize(os, m.size()); + for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) + Serialize(os, (*it), nType, nVersion); +} + +template +void Unserialize(Stream& is, std::set& m, int nType, int nVersion) +{ + m.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::set::iterator it = m.begin(); + for (unsigned int i = 0; i < nSize; i++) + { + K key; + Unserialize(is, key, nType, nVersion); + it = m.insert(it, key); + } +} + + + +/** + * list + */ +template +unsigned int GetSerializeSize(const std::list& l, int nType, int nVersion) +{ + unsigned int nSize = GetSizeOfCompactSize(l.size()); + for (typename std::list::const_iterator it = l.begin(); it != l.end(); ++it) + nSize += GetSerializeSize((*it), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const std::list& l, int nType, int nVersion) +{ + WriteCompactSize(os, l.size()); + for (typename std::list::const_iterator it = l.begin(); it != l.end(); ++it) + Serialize(os, (*it), nType, nVersion); +} + +template +void Unserialize(Stream& is, std::list& l, int nType, int nVersion) +{ + l.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::list::iterator it = l.begin(); + for (unsigned int i = 0; i < nSize; i++) + { + T item; + Unserialize(is, item, nType, nVersion); + l.push_back(item); + } +} + + + +/** + * Support for ADD_SERIALIZE_METHODS and READWRITE macro + */ +struct CSerActionSerialize +{ + bool ForRead() const { return false; } +}; +struct CSerActionUnserialize +{ + bool ForRead() const { return true; } +}; + +template +inline void SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionSerialize ser_action) +{ + ::Serialize(s, obj, nType, nVersion); +} + +template +inline void SerReadWrite(Stream& s, T& obj, int nType, int nVersion, CSerActionUnserialize ser_action) +{ + ::Unserialize(s, obj, nType, nVersion); +} + + + + + + + + + +class CSizeComputer +{ +protected: + size_t nSize; + +public: + int nType; + int nVersion; + + CSizeComputer(int nTypeIn, int nVersionIn) : nSize(0), nType(nTypeIn), nVersion(nVersionIn) {} + + CSizeComputer& write(const char *psz, size_t nSize) + { + this->nSize += nSize; + return *this; + } + + template + CSizeComputer& operator<<(const T& obj) + { + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } + + size_t size() const { + return nSize; + } +}; + +#endif // BITCOIN_SERIALIZE_H diff --git a/src/Native/libmultihash/equi/support/cleanse.cpp b/src/Native/libmultihash/equi/support/cleanse.cpp new file mode 100644 index 000000000..ddc7fab68 --- /dev/null +++ b/src/Native/libmultihash/equi/support/cleanse.cpp @@ -0,0 +1,21 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "cleanse.h" + +#ifndef _WIN32 +#include +#else +#include +#endif + +void memory_cleanse(void *ptr, size_t len) +{ +#ifndef _WIN32 + OPENSSL_cleanse(ptr, len); +#else + ZeroMemory(ptr, 0, len); +#endif +} diff --git a/src/Native/libmultihash/equi/support/cleanse.h b/src/Native/libmultihash/equi/support/cleanse.h new file mode 100644 index 000000000..3e02aa8fd --- /dev/null +++ b/src/Native/libmultihash/equi/support/cleanse.h @@ -0,0 +1,13 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_SUPPORT_CLEANSE_H +#define BITCOIN_SUPPORT_CLEANSE_H + +#include + +void memory_cleanse(void *ptr, size_t len); + +#endif // BITCOIN_SUPPORT_CLEANSE_H diff --git a/src/Native/libmultihash/equi/tinyformat.h b/src/Native/libmultihash/equi/tinyformat.h new file mode 100644 index 000000000..57f767250 --- /dev/null +++ b/src/Native/libmultihash/equi/tinyformat.h @@ -0,0 +1,1049 @@ +// tinyformat.h +// Copyright (C) 2011, Chris Foster [chris42f (at) gmail (d0t) com] +// +// Boost Software License - Version 1.0 +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//------------------------------------------------------------------------------ +// Tinyformat: A minimal type safe printf replacement +// +// tinyformat.h is a type safe printf replacement library in a single C++ +// header file. Design goals include: +// +// * Type safety and extensibility for user defined types. +// * C99 printf() compatibility, to the extent possible using std::ostream +// * Simplicity and minimalism. A single header file to include and distribute +// with your projects. +// * Augment rather than replace the standard stream formatting mechanism +// * C++98 support, with optional C++11 niceties +// +// +// Main interface example usage +// ---------------------------- +// +// To print a date to std::cout: +// +// std::string weekday = "Wednesday"; +// const char* month = "July"; +// size_t day = 27; +// long hour = 14; +// int min = 44; +// +// tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min); +// +// The strange types here emphasize the type safety of the interface; it is +// possible to print a std::string using the "%s" conversion, and a +// size_t using the "%d" conversion. A similar result could be achieved +// using either of the tfm::format() functions. One prints on a user provided +// stream: +// +// tfm::format(std::cerr, "%s, %s %d, %.2d:%.2d\n", +// weekday, month, day, hour, min); +// +// The other returns a std::string: +// +// std::string date = tfm::format("%s, %s %d, %.2d:%.2d\n", +// weekday, month, day, hour, min); +// std::cout << date; +// +// These are the three primary interface functions. There is also a +// convenience function printfln() which appends a newline to the usual result +// of printf() for super simple logging. +// +// +// User defined format functions +// ----------------------------- +// +// Simulating variadic templates in C++98 is pretty painful since it requires +// writing out the same function for each desired number of arguments. To make +// this bearable tinyformat comes with a set of macros which are used +// internally to generate the API, but which may also be used in user code. +// +// The three macros TINYFORMAT_ARGTYPES(n), TINYFORMAT_VARARGS(n) and +// TINYFORMAT_PASSARGS(n) will generate a list of n argument types, +// type/name pairs and argument names respectively when called with an integer +// n between 1 and 16. We can use these to define a macro which generates the +// desired user defined function with n arguments. To generate all 16 user +// defined function bodies, use the macro TINYFORMAT_FOREACH_ARGNUM. For an +// example, see the implementation of printf() at the end of the source file. +// +// Sometimes it's useful to be able to pass a list of format arguments through +// to a non-template function. The FormatList class is provided as a way to do +// this by storing the argument list in a type-opaque way. Continuing the +// example from above, we construct a FormatList using makeFormatList(): +// +// FormatListRef formatList = tfm::makeFormatList(weekday, month, day, hour, min); +// +// The format list can now be passed into any non-template function and used +// via a call to the vformat() function: +// +// tfm::vformat(std::cout, "%s, %s %d, %.2d:%.2d\n", formatList); +// +// +// Additional API information +// -------------------------- +// +// Error handling: Define TINYFORMAT_ERROR to customize the error handling for +// format strings which are unsupported or have the wrong number of format +// specifiers (calls assert() by default). +// +// User defined types: Uses operator<< for user defined types by default. +// Overload formatValue() for more control. + + +#ifndef TINYFORMAT_H_INCLUDED +#define TINYFORMAT_H_INCLUDED + +namespace tinyformat {} +//------------------------------------------------------------------------------ +// Config section. Customize to your liking! + +// Namespace alias to encourage brevity +namespace tfm = tinyformat; + +// Error handling; calls assert() by default. +#define TINYFORMAT_ERROR(reasonString) throw std::runtime_error(reasonString) + +// Define for C++11 variadic templates which make the code shorter & more +// general. If you don't define this, C++11 support is autodetected below. +// #define TINYFORMAT_USE_VARIADIC_TEMPLATES + + +//------------------------------------------------------------------------------ +// Implementation details. +#include +#include +#include +#include +#include + +#ifndef TINYFORMAT_ERROR +# define TINYFORMAT_ERROR(reason) assert(0 && reason) +#endif + +#if !defined(TINYFORMAT_USE_VARIADIC_TEMPLATES) && !defined(TINYFORMAT_NO_VARIADIC_TEMPLATES) +# ifdef __GXX_EXPERIMENTAL_CXX0X__ +# define TINYFORMAT_USE_VARIADIC_TEMPLATES +# endif +#endif + +#if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201 +// std::showpos is broken on old libstdc++ as provided with OSX. See +// http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html +# define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND +#endif + +#ifdef __APPLE__ +// Workaround macOS linker warning: Xcode uses different default symbol +// visibilities for static libs vs executables (see issue #25) +# define TINYFORMAT_HIDDEN __attribute__((visibility("hidden"))) +#else +# define TINYFORMAT_HIDDEN +#endif + +namespace tinyformat { + +//------------------------------------------------------------------------------ +namespace detail { + +// Test whether type T1 is convertible to type T2 +template +struct is_convertible +{ + private: + // two types of different size + struct fail { char dummy[2]; }; + struct succeed { char dummy; }; + // Try to convert a T1 to a T2 by plugging into tryConvert + static fail tryConvert(...); + static succeed tryConvert(const T2&); + static const T1& makeT1(); + public: +# ifdef _MSC_VER + // Disable spurious loss of precision warnings in tryConvert(makeT1()) +# pragma warning(push) +# pragma warning(disable:4244) +# pragma warning(disable:4267) +# endif + // Standard trick: the (...) version of tryConvert will be chosen from + // the overload set only if the version taking a T2 doesn't match. + // Then we compare the sizes of the return types to check which + // function matched. Very neat, in a disgusting kind of way :) + static const bool value = + sizeof(tryConvert(makeT1())) == sizeof(succeed); +# ifdef _MSC_VER +# pragma warning(pop) +# endif +}; + + +// Detect when a type is not a wchar_t string +template struct is_wchar { typedef int tinyformat_wchar_is_not_supported; }; +template<> struct is_wchar {}; +template<> struct is_wchar {}; +template struct is_wchar {}; +template struct is_wchar {}; + + +// Format the value by casting to type fmtT. This default implementation +// should never be called. +template::value> +struct formatValueAsType +{ + static void invoke(std::ostream& /*out*/, const T& /*value*/) { assert(0); } +}; +// Specialized version for types that can actually be converted to fmtT, as +// indicated by the "convertible" template parameter. +template +struct formatValueAsType +{ + static void invoke(std::ostream& out, const T& value) + { out << static_cast(value); } +}; + +#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND +template::value> +struct formatZeroIntegerWorkaround +{ + static bool invoke(std::ostream& /**/, const T& /**/) { return false; } +}; +template +struct formatZeroIntegerWorkaround +{ + static bool invoke(std::ostream& out, const T& value) + { + if (static_cast(value) == 0 && out.flags() & std::ios::showpos) + { + out << "+0"; + return true; + } + return false; + } +}; +#endif // TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND + +// Convert an arbitrary type to integer. The version with convertible=false +// throws an error. +template::value> +struct convertToInt +{ + static int invoke(const T& /*value*/) + { + TINYFORMAT_ERROR("tinyformat: Cannot convert from argument type to " + "integer for use as variable width or precision"); + return 0; + } +}; +// Specialization for convertToInt when conversion is possible +template +struct convertToInt +{ + static int invoke(const T& value) { return static_cast(value); } +}; + +// Format at most ntrunc characters to the given stream. +template +inline void formatTruncated(std::ostream& out, const T& value, int ntrunc) +{ + std::ostringstream tmp; + tmp << value; + std::string result = tmp.str(); + out.write(result.c_str(), (std::min)(ntrunc, static_cast(result.size()))); +} +#define TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(type) \ +inline void formatTruncated(std::ostream& out, type* value, int ntrunc) \ +{ \ + std::streamsize len = 0; \ + while(len < ntrunc && value[len] != 0) \ + ++len; \ + out.write(value, len); \ +} +// Overload for const char* and char*. Could overload for signed & unsigned +// char too, but these are technically unneeded for printf compatibility. +TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(const char) +TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(char) +#undef TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR + +} // namespace detail + + +//------------------------------------------------------------------------------ +// Variable formatting functions. May be overridden for user-defined types if +// desired. + + +/// Format a value into a stream, delegating to operator<< by default. +/// +/// Users may override this for their own types. When this function is called, +/// the stream flags will have been modified according to the format string. +/// The format specification is provided in the range [fmtBegin, fmtEnd). For +/// truncating conversions, ntrunc is set to the desired maximum number of +/// characters, for example "%.7s" calls formatValue with ntrunc = 7. +/// +/// By default, formatValue() uses the usual stream insertion operator +/// operator<< to format the type T, with special cases for the %c and %p +/// conversions. +template +inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, + const char* fmtEnd, int ntrunc, const T& value) +{ +#ifndef TINYFORMAT_ALLOW_WCHAR_STRINGS + // Since we don't support printing of wchar_t using "%ls", make it fail at + // compile time in preference to printing as a void* at runtime. + typedef typename detail::is_wchar::tinyformat_wchar_is_not_supported DummyType; + (void) DummyType(); // avoid unused type warning with gcc-4.8 +#endif + // The mess here is to support the %c and %p conversions: if these + // conversions are active we try to convert the type to a char or const + // void* respectively and format that instead of the value itself. For the + // %p conversion it's important to avoid dereferencing the pointer, which + // could otherwise lead to a crash when printing a dangling (const char*). + const bool canConvertToChar = detail::is_convertible::value; + const bool canConvertToVoidPtr = detail::is_convertible::value; + if(canConvertToChar && *(fmtEnd-1) == 'c') + detail::formatValueAsType::invoke(out, value); + else if(canConvertToVoidPtr && *(fmtEnd-1) == 'p') + detail::formatValueAsType::invoke(out, value); +#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND + else if(detail::formatZeroIntegerWorkaround::invoke(out, value)) /**/; +#endif + else if(ntrunc >= 0) + { + // Take care not to overread C strings in truncating conversions like + // "%.4s" where at most 4 characters may be read. + detail::formatTruncated(out, value, ntrunc); + } + else + out << value; +} + + +// Overloaded version for char types to support printing as an integer +#define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType) \ +inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \ + const char* fmtEnd, int /**/, charType value) \ +{ \ + switch(*(fmtEnd-1)) \ + { \ + case 'u': case 'd': case 'i': case 'o': case 'X': case 'x': \ + out << static_cast(value); break; \ + default: \ + out << value; break; \ + } \ +} +// per 3.9.1: char, signed char and unsigned char are all distinct types +TINYFORMAT_DEFINE_FORMATVALUE_CHAR(char) +TINYFORMAT_DEFINE_FORMATVALUE_CHAR(signed char) +TINYFORMAT_DEFINE_FORMATVALUE_CHAR(unsigned char) +#undef TINYFORMAT_DEFINE_FORMATVALUE_CHAR + + +//------------------------------------------------------------------------------ +// Tools for emulating variadic templates in C++98. The basic idea here is +// stolen from the boost preprocessor metaprogramming library and cut down to +// be just general enough for what we need. + +#define TINYFORMAT_ARGTYPES(n) TINYFORMAT_ARGTYPES_ ## n +#define TINYFORMAT_VARARGS(n) TINYFORMAT_VARARGS_ ## n +#define TINYFORMAT_PASSARGS(n) TINYFORMAT_PASSARGS_ ## n +#define TINYFORMAT_PASSARGS_TAIL(n) TINYFORMAT_PASSARGS_TAIL_ ## n + +// To keep it as transparent as possible, the macros below have been generated +// using python via the excellent cog.py code generation script. This avoids +// the need for a bunch of complex (but more general) preprocessor tricks as +// used in boost.preprocessor. +// +// To rerun the code generation in place, use `cog.py -r tinyformat.h` +// (see http://nedbatchelder.com/code/cog). Alternatively you can just create +// extra versions by hand. + +/*[[[cog +maxParams = 16 + +def makeCommaSepLists(lineTemplate, elemTemplate, startInd=1): + for j in range(startInd,maxParams+1): + list = ', '.join([elemTemplate % {'i':i} for i in range(startInd,j+1)]) + cog.outl(lineTemplate % {'j':j, 'list':list}) + +makeCommaSepLists('#define TINYFORMAT_ARGTYPES_%(j)d %(list)s', + 'class T%(i)d') + +cog.outl() +makeCommaSepLists('#define TINYFORMAT_VARARGS_%(j)d %(list)s', + 'const T%(i)d& v%(i)d') + +cog.outl() +makeCommaSepLists('#define TINYFORMAT_PASSARGS_%(j)d %(list)s', 'v%(i)d') + +cog.outl() +cog.outl('#define TINYFORMAT_PASSARGS_TAIL_1') +makeCommaSepLists('#define TINYFORMAT_PASSARGS_TAIL_%(j)d , %(list)s', + 'v%(i)d', startInd = 2) + +cog.outl() +cog.outl('#define TINYFORMAT_FOREACH_ARGNUM(m) \\\n ' + + ' '.join(['m(%d)' % (j,) for j in range(1,maxParams+1)])) +]]]*/ +#define TINYFORMAT_ARGTYPES_1 class T1 +#define TINYFORMAT_ARGTYPES_2 class T1, class T2 +#define TINYFORMAT_ARGTYPES_3 class T1, class T2, class T3 +#define TINYFORMAT_ARGTYPES_4 class T1, class T2, class T3, class T4 +#define TINYFORMAT_ARGTYPES_5 class T1, class T2, class T3, class T4, class T5 +#define TINYFORMAT_ARGTYPES_6 class T1, class T2, class T3, class T4, class T5, class T6 +#define TINYFORMAT_ARGTYPES_7 class T1, class T2, class T3, class T4, class T5, class T6, class T7 +#define TINYFORMAT_ARGTYPES_8 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8 +#define TINYFORMAT_ARGTYPES_9 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9 +#define TINYFORMAT_ARGTYPES_10 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10 +#define TINYFORMAT_ARGTYPES_11 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11 +#define TINYFORMAT_ARGTYPES_12 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12 +#define TINYFORMAT_ARGTYPES_13 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13 +#define TINYFORMAT_ARGTYPES_14 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14 +#define TINYFORMAT_ARGTYPES_15 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 +#define TINYFORMAT_ARGTYPES_16 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16 + +#define TINYFORMAT_VARARGS_1 const T1& v1 +#define TINYFORMAT_VARARGS_2 const T1& v1, const T2& v2 +#define TINYFORMAT_VARARGS_3 const T1& v1, const T2& v2, const T3& v3 +#define TINYFORMAT_VARARGS_4 const T1& v1, const T2& v2, const T3& v3, const T4& v4 +#define TINYFORMAT_VARARGS_5 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5 +#define TINYFORMAT_VARARGS_6 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6 +#define TINYFORMAT_VARARGS_7 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7 +#define TINYFORMAT_VARARGS_8 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8 +#define TINYFORMAT_VARARGS_9 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9 +#define TINYFORMAT_VARARGS_10 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10 +#define TINYFORMAT_VARARGS_11 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11 +#define TINYFORMAT_VARARGS_12 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12 +#define TINYFORMAT_VARARGS_13 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13 +#define TINYFORMAT_VARARGS_14 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14 +#define TINYFORMAT_VARARGS_15 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14, const T15& v15 +#define TINYFORMAT_VARARGS_16 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14, const T15& v15, const T16& v16 + +#define TINYFORMAT_PASSARGS_1 v1 +#define TINYFORMAT_PASSARGS_2 v1, v2 +#define TINYFORMAT_PASSARGS_3 v1, v2, v3 +#define TINYFORMAT_PASSARGS_4 v1, v2, v3, v4 +#define TINYFORMAT_PASSARGS_5 v1, v2, v3, v4, v5 +#define TINYFORMAT_PASSARGS_6 v1, v2, v3, v4, v5, v6 +#define TINYFORMAT_PASSARGS_7 v1, v2, v3, v4, v5, v6, v7 +#define TINYFORMAT_PASSARGS_8 v1, v2, v3, v4, v5, v6, v7, v8 +#define TINYFORMAT_PASSARGS_9 v1, v2, v3, v4, v5, v6, v7, v8, v9 +#define TINYFORMAT_PASSARGS_10 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10 +#define TINYFORMAT_PASSARGS_11 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 +#define TINYFORMAT_PASSARGS_12 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 +#define TINYFORMAT_PASSARGS_13 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13 +#define TINYFORMAT_PASSARGS_14 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14 +#define TINYFORMAT_PASSARGS_15 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 +#define TINYFORMAT_PASSARGS_16 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 + +#define TINYFORMAT_PASSARGS_TAIL_1 +#define TINYFORMAT_PASSARGS_TAIL_2 , v2 +#define TINYFORMAT_PASSARGS_TAIL_3 , v2, v3 +#define TINYFORMAT_PASSARGS_TAIL_4 , v2, v3, v4 +#define TINYFORMAT_PASSARGS_TAIL_5 , v2, v3, v4, v5 +#define TINYFORMAT_PASSARGS_TAIL_6 , v2, v3, v4, v5, v6 +#define TINYFORMAT_PASSARGS_TAIL_7 , v2, v3, v4, v5, v6, v7 +#define TINYFORMAT_PASSARGS_TAIL_8 , v2, v3, v4, v5, v6, v7, v8 +#define TINYFORMAT_PASSARGS_TAIL_9 , v2, v3, v4, v5, v6, v7, v8, v9 +#define TINYFORMAT_PASSARGS_TAIL_10 , v2, v3, v4, v5, v6, v7, v8, v9, v10 +#define TINYFORMAT_PASSARGS_TAIL_11 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 +#define TINYFORMAT_PASSARGS_TAIL_12 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 +#define TINYFORMAT_PASSARGS_TAIL_13 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13 +#define TINYFORMAT_PASSARGS_TAIL_14 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14 +#define TINYFORMAT_PASSARGS_TAIL_15 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 +#define TINYFORMAT_PASSARGS_TAIL_16 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 + +#define TINYFORMAT_FOREACH_ARGNUM(m) \ + m(1) m(2) m(3) m(4) m(5) m(6) m(7) m(8) m(9) m(10) m(11) m(12) m(13) m(14) m(15) m(16) +//[[[end]]] + + + +namespace detail { + +// Type-opaque holder for an argument to format(), with associated actions on +// the type held as explicit function pointers. This allows FormatArg's for +// each argument to be allocated as a homogenous array inside FormatList +// whereas a naive implementation based on inheritance does not. +class FormatArg +{ + public: + FormatArg() {} + + template + FormatArg(const T& value) + : m_value(static_cast(&value)), + m_formatImpl(&formatImpl), + m_toIntImpl(&toIntImpl) + { } + + void format(std::ostream& out, const char* fmtBegin, + const char* fmtEnd, int ntrunc) const + { + m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value); + } + + int toInt() const + { + return m_toIntImpl(m_value); + } + + private: + template + TINYFORMAT_HIDDEN static void formatImpl(std::ostream& out, const char* fmtBegin, + const char* fmtEnd, int ntrunc, const void* value) + { + formatValue(out, fmtBegin, fmtEnd, ntrunc, *static_cast(value)); + } + + template + TINYFORMAT_HIDDEN static int toIntImpl(const void* value) + { + return convertToInt::invoke(*static_cast(value)); + } + + const void* m_value; + void (*m_formatImpl)(std::ostream& out, const char* fmtBegin, + const char* fmtEnd, int ntrunc, const void* value); + int (*m_toIntImpl)(const void* value); +}; + + +// Parse and return an integer from the string c, as atoi() +// On return, c is set to one past the end of the integer. +inline int parseIntAndAdvance(const char*& c) +{ + int i = 0; + for(;*c >= '0' && *c <= '9'; ++c) + i = 10*i + (*c - '0'); + return i; +} + +// Print literal part of format string and return next format spec +// position. +// +// Skips over any occurrences of '%%', printing a literal '%' to the +// output. The position of the first % character of the next +// nontrivial format spec is returned, or the end of string. +inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt) +{ + const char* c = fmt; + for(;; ++c) + { + switch(*c) + { + case '\0': + out.write(fmt, c - fmt); + return c; + case '%': + out.write(fmt, c - fmt); + if(*(c+1) != '%') + return c; + // for "%%", tack trailing % onto next literal section. + fmt = ++c; + break; + default: + break; + } + } +} + + +// Parse a format string and set the stream state accordingly. +// +// The format mini-language recognized here is meant to be the one from C99, +// with the form "%[flags][width][.precision][length]type". +// +// Formatting options which can't be natively represented using the ostream +// state are returned in spacePadPositive (for space padded positive numbers) +// and ntrunc (for truncating conversions). argIndex is incremented if +// necessary to pull out variable width and precision. The function returns a +// pointer to the character after the end of the current format spec. +inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositive, + int& ntrunc, const char* fmtStart, + const detail::FormatArg* formatters, + int& argIndex, int numFormatters) +{ + if(*fmtStart != '%') + { + TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string"); + return fmtStart; + } + // Reset stream state to defaults. + out.width(0); + out.precision(6); + out.fill(' '); + // Reset most flags; ignore irrelevant unitbuf & skipws. + out.unsetf(std::ios::adjustfield | std::ios::basefield | + std::ios::floatfield | std::ios::showbase | std::ios::boolalpha | + std::ios::showpoint | std::ios::showpos | std::ios::uppercase); + bool precisionSet = false; + bool widthSet = false; + int widthExtra = 0; + const char* c = fmtStart + 1; + // 1) Parse flags + for(;; ++c) + { + switch(*c) + { + case '#': + out.setf(std::ios::showpoint | std::ios::showbase); + continue; + case '0': + // overridden by left alignment ('-' flag) + if(!(out.flags() & std::ios::left)) + { + // Use internal padding so that numeric values are + // formatted correctly, eg -00010 rather than 000-10 + out.fill('0'); + out.setf(std::ios::internal, std::ios::adjustfield); + } + continue; + case '-': + out.fill(' '); + out.setf(std::ios::left, std::ios::adjustfield); + continue; + case ' ': + // overridden by show positive sign, '+' flag. + if(!(out.flags() & std::ios::showpos)) + spacePadPositive = true; + continue; + case '+': + out.setf(std::ios::showpos); + spacePadPositive = false; + widthExtra = 1; + continue; + default: + break; + } + break; + } + // 2) Parse width + if(*c >= '0' && *c <= '9') + { + widthSet = true; + out.width(parseIntAndAdvance(c)); + } + if(*c == '*') + { + widthSet = true; + int width = 0; + if(argIndex < numFormatters) + width = formatters[argIndex++].toInt(); + else + TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width"); + if(width < 0) + { + // negative widths correspond to '-' flag set + out.fill(' '); + out.setf(std::ios::left, std::ios::adjustfield); + width = -width; + } + out.width(width); + ++c; + } + // 3) Parse precision + if(*c == '.') + { + ++c; + int precision = 0; + if(*c == '*') + { + ++c; + if(argIndex < numFormatters) + precision = formatters[argIndex++].toInt(); + else + TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable precision"); + } + else + { + if(*c >= '0' && *c <= '9') + precision = parseIntAndAdvance(c); + else if(*c == '-') // negative precisions ignored, treated as zero. + parseIntAndAdvance(++c); + } + out.precision(precision); + precisionSet = true; + } + // 4) Ignore any C99 length modifier + while(*c == 'l' || *c == 'h' || *c == 'L' || + *c == 'j' || *c == 'z' || *c == 't') + ++c; + // 5) We're up to the conversion specifier character. + // Set stream flags based on conversion specifier (thanks to the + // boost::format class for forging the way here). + bool intConversion = false; + switch(*c) + { + case 'u': case 'd': case 'i': + out.setf(std::ios::dec, std::ios::basefield); + intConversion = true; + break; + case 'o': + out.setf(std::ios::oct, std::ios::basefield); + intConversion = true; + break; + case 'X': + out.setf(std::ios::uppercase); + case 'x': case 'p': + out.setf(std::ios::hex, std::ios::basefield); + intConversion = true; + break; + case 'E': + out.setf(std::ios::uppercase); + case 'e': + out.setf(std::ios::scientific, std::ios::floatfield); + out.setf(std::ios::dec, std::ios::basefield); + break; + case 'F': + out.setf(std::ios::uppercase); + case 'f': + out.setf(std::ios::fixed, std::ios::floatfield); + break; + case 'G': + out.setf(std::ios::uppercase); + case 'g': + out.setf(std::ios::dec, std::ios::basefield); + // As in boost::format, let stream decide float format. + out.flags(out.flags() & ~std::ios::floatfield); + break; + case 'a': case 'A': + TINYFORMAT_ERROR("tinyformat: the %a and %A conversion specs " + "are not supported"); + break; + case 'c': + // Handled as special case inside formatValue() + break; + case 's': + if(precisionSet) + ntrunc = static_cast(out.precision()); + // Make %s print booleans as "true" and "false" + out.setf(std::ios::boolalpha); + break; + case 'n': + // Not supported - will cause problems! + TINYFORMAT_ERROR("tinyformat: %n conversion spec not supported"); + break; + case '\0': + TINYFORMAT_ERROR("tinyformat: Conversion spec incorrectly " + "terminated by end of string"); + return c; + default: + break; + } + if(intConversion && precisionSet && !widthSet) + { + // "precision" for integers gives the minimum number of digits (to be + // padded with zeros on the left). This isn't really supported by the + // iostreams, but we can approximately simulate it with the width if + // the width isn't otherwise used. + out.width(out.precision() + widthExtra); + out.setf(std::ios::internal, std::ios::adjustfield); + out.fill('0'); + } + return c+1; +} + + +//------------------------------------------------------------------------------ +inline void formatImpl(std::ostream& out, const char* fmt, + const detail::FormatArg* formatters, + int numFormatters) +{ + // Saved stream state + std::streamsize origWidth = out.width(); + std::streamsize origPrecision = out.precision(); + std::ios::fmtflags origFlags = out.flags(); + char origFill = out.fill(); + + for (int argIndex = 0; argIndex < numFormatters; ++argIndex) + { + // Parse the format string + fmt = printFormatStringLiteral(out, fmt); + bool spacePadPositive = false; + int ntrunc = -1; + const char* fmtEnd = streamStateFromFormat(out, spacePadPositive, ntrunc, fmt, + formatters, argIndex, numFormatters); + if (argIndex >= numFormatters) + { + // Check args remain after reading any variable width/precision + TINYFORMAT_ERROR("tinyformat: Not enough format arguments"); + return; + } + const FormatArg& arg = formatters[argIndex]; + // Format the arg into the stream. + if(!spacePadPositive) + arg.format(out, fmt, fmtEnd, ntrunc); + else + { + // The following is a special case with no direct correspondence + // between stream formatting and the printf() behaviour. Simulate + // it crudely by formatting into a temporary string stream and + // munging the resulting string. + std::ostringstream tmpStream; + tmpStream.copyfmt(out); + tmpStream.setf(std::ios::showpos); + arg.format(tmpStream, fmt, fmtEnd, ntrunc); + std::string result = tmpStream.str(); // allocates... yuck. + for(size_t i = 0, iend = result.size(); i < iend; ++i) + if(result[i] == '+') result[i] = ' '; + out << result; + } + fmt = fmtEnd; + } + + // Print remaining part of format string. + fmt = printFormatStringLiteral(out, fmt); + if(*fmt != '\0') + TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string"); + + // Restore stream state + out.width(origWidth); + out.precision(origPrecision); + out.flags(origFlags); + out.fill(origFill); +} + +} // namespace detail + + +/// List of template arguments format(), held in a type-opaque way. +/// +/// A const reference to FormatList (typedef'd as FormatListRef) may be +/// conveniently used to pass arguments to non-template functions: All type +/// information has been stripped from the arguments, leaving just enough of a +/// common interface to perform formatting as required. +class FormatList +{ + public: + FormatList(detail::FormatArg* formatters, int N) + : m_formatters(formatters), m_N(N) { } + + friend void vformat(std::ostream& out, const char* fmt, + const FormatList& list); + + private: + const detail::FormatArg* m_formatters; + int m_N; +}; + +/// Reference to type-opaque format list for passing to vformat() +typedef const FormatList& FormatListRef; + + +namespace detail { + +// Format list subclass with fixed storage to avoid dynamic allocation +template +class FormatListN : public FormatList +{ + public: +#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES + template + FormatListN(const Args&... args) + : FormatList(&m_formatterStore[0], N), + m_formatterStore { FormatArg(args)... } + { static_assert(sizeof...(args) == N, "Number of args must be N"); } +#else // C++98 version + void init(int) {} +# define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \ + \ + template \ + FormatListN(TINYFORMAT_VARARGS(n)) \ + : FormatList(&m_formatterStore[0], n) \ + { assert(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \ + \ + template \ + void init(int i, TINYFORMAT_VARARGS(n)) \ + { \ + m_formatterStore[i] = FormatArg(v1); \ + init(i+1 TINYFORMAT_PASSARGS_TAIL(n)); \ + } + + TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR) +# undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR +#endif + + private: + FormatArg m_formatterStore[N]; +}; + +// Special 0-arg version - MSVC says zero-sized C array in struct is nonstandard +template<> class FormatListN<0> : public FormatList +{ + public: FormatListN() : FormatList(0, 0) {} +}; + +} // namespace detail + + +//------------------------------------------------------------------------------ +// Primary API functions + +#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES + +/// Make type-agnostic format list from list of template arguments. +/// +/// The exact return type of this function is an implementation detail and +/// shouldn't be relied upon. Instead it should be stored as a FormatListRef: +/// +/// FormatListRef formatList = makeFormatList( /*...*/ ); +template +detail::FormatListN makeFormatList(const Args&... args) +{ + return detail::FormatListN(args...); +} + +#else // C++98 version + +inline detail::FormatListN<0> makeFormatList() +{ + return detail::FormatListN<0>(); +} +#define TINYFORMAT_MAKE_MAKEFORMATLIST(n) \ +template \ +detail::FormatListN makeFormatList(TINYFORMAT_VARARGS(n)) \ +{ \ + return detail::FormatListN(TINYFORMAT_PASSARGS(n)); \ +} +TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST) +#undef TINYFORMAT_MAKE_MAKEFORMATLIST + +#endif + +/// Format list of arguments to the stream according to the given format string. +/// +/// The name vformat() is chosen for the semantic similarity to vprintf(): the +/// list of format arguments is held in a single function argument. +inline void vformat(std::ostream& out, const char* fmt, FormatListRef list) +{ + detail::formatImpl(out, fmt, list.m_formatters, list.m_N); +} + + +#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES + +/// Format list of arguments to the stream according to given format string. +template +void format(std::ostream& out, const char* fmt, const Args&... args) +{ + vformat(out, fmt, makeFormatList(args...)); +} + +/// Format list of arguments according to the given format string and return +/// the result as a string. +template +std::string format(const char* fmt, const Args&... args) +{ + std::ostringstream oss; + format(oss, fmt, args...); + return oss.str(); +} + +/// Format list of arguments to std::cout, according to the given format string +template +void printf(const char* fmt, const Args&... args) +{ + format(std::cout, fmt, args...); +} + +template +void printfln(const char* fmt, const Args&... args) +{ + format(std::cout, fmt, args...); + std::cout << '\n'; +} + +#else // C++98 version + +inline void format(std::ostream& out, const char* fmt) +{ + vformat(out, fmt, makeFormatList()); +} + +inline std::string format(const char* fmt) +{ + std::ostringstream oss; + format(oss, fmt); + return oss.str(); +} + +inline void printf(const char* fmt) +{ + format(std::cout, fmt); +} + +inline void printfln(const char* fmt) +{ + format(std::cout, fmt); + std::cout << '\n'; +} + +#define TINYFORMAT_MAKE_FORMAT_FUNCS(n) \ + \ +template \ +void format(std::ostream& out, const char* fmt, TINYFORMAT_VARARGS(n)) \ +{ \ + vformat(out, fmt, makeFormatList(TINYFORMAT_PASSARGS(n))); \ +} \ + \ +template \ +std::string format(const char* fmt, TINYFORMAT_VARARGS(n)) \ +{ \ + std::ostringstream oss; \ + format(oss, fmt, TINYFORMAT_PASSARGS(n)); \ + return oss.str(); \ +} \ + \ +template \ +void printf(const char* fmt, TINYFORMAT_VARARGS(n)) \ +{ \ + format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ +} \ + \ +template \ +void printfln(const char* fmt, TINYFORMAT_VARARGS(n)) \ +{ \ + format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ + std::cout << '\n'; \ +} + +TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS) +#undef TINYFORMAT_MAKE_FORMAT_FUNCS + +#endif + +// Added for Bitcoin Core +template +std::string format(const std::string &fmt, const Args&... args) +{ + std::ostringstream oss; + format(oss, fmt.c_str(), args...); + return oss.str(); +} + +} // namespace tinyformat + +#define strprintf tfm::format + +#endif // TINYFORMAT_H_INCLUDED diff --git a/src/Native/libmultihash/equi/uint256.cpp b/src/Native/libmultihash/equi/uint256.cpp new file mode 100644 index 000000000..25148808c --- /dev/null +++ b/src/Native/libmultihash/equi/uint256.cpp @@ -0,0 +1,146 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "uint256.h" + +#include "utilstrencodings.h" + +#include +#include + +template +base_blob::base_blob(const std::vector& vch) +{ + assert(vch.size() == sizeof(data)); + memcpy(data, &vch[0], sizeof(data)); +} + +template +std::string base_blob::GetHex() const +{ + char psz[sizeof(data) * 2 + 1]; + for (unsigned int i = 0; i < sizeof(data); i++) + sprintf(psz + i * 2, "%02x", data[sizeof(data) - i - 1]); + return std::string(psz, psz + sizeof(data) * 2); +} + +template +void base_blob::SetHex(const char* psz) +{ + memset(data, 0, sizeof(data)); + + // skip leading spaces + while (isspace(*psz)) + psz++; + + // skip 0x + if (psz[0] == '0' && tolower(psz[1]) == 'x') + psz += 2; + + // hex string to uint + const char* pbegin = psz; + while (::HexDigit(*psz) != -1) + psz++; + psz--; + unsigned char* p1 = (unsigned char*)data; + unsigned char* pend = p1 + WIDTH; + while (psz >= pbegin && p1 < pend) { + *p1 = ::HexDigit(*psz--); + if (psz >= pbegin) { + *p1 |= ((unsigned char)::HexDigit(*psz--) << 4); + p1++; + } + } +} + +template +void base_blob::SetHex(const std::string& str) +{ + SetHex(str.c_str()); +} + +template +std::string base_blob::ToString() const +{ + return (GetHex()); +} + +// Explicit instantiations for base_blob<160> +template base_blob<160>::base_blob(const std::vector&); +template std::string base_blob<160>::GetHex() const; +template std::string base_blob<160>::ToString() const; +template void base_blob<160>::SetHex(const char*); +template void base_blob<160>::SetHex(const std::string&); + +// Explicit instantiations for base_blob<256> +template base_blob<256>::base_blob(const std::vector&); +template std::string base_blob<256>::GetHex() const; +template std::string base_blob<256>::ToString() const; +template void base_blob<256>::SetHex(const char*); +template void base_blob<256>::SetHex(const std::string&); + +static void inline HashMix(uint32_t& a, uint32_t& b, uint32_t& c) +{ + // Taken from lookup3, by Bob Jenkins. + a -= c; + a ^= ((c << 4) | (c >> 28)); + c += b; + b -= a; + b ^= ((a << 6) | (a >> 26)); + a += c; + c -= b; + c ^= ((b << 8) | (b >> 24)); + b += a; + a -= c; + a ^= ((c << 16) | (c >> 16)); + c += b; + b -= a; + b ^= ((a << 19) | (a >> 13)); + a += c; + c -= b; + c ^= ((b << 4) | (b >> 28)); + b += a; +} + +static void inline HashFinal(uint32_t& a, uint32_t& b, uint32_t& c) +{ + // Taken from lookup3, by Bob Jenkins. + c ^= b; + c -= ((b << 14) | (b >> 18)); + a ^= c; + a -= ((c << 11) | (c >> 21)); + b ^= a; + b -= ((a << 25) | (a >> 7)); + c ^= b; + c -= ((b << 16) | (b >> 16)); + a ^= c; + a -= ((c << 4) | (c >> 28)); + b ^= a; + b -= ((a << 14) | (a >> 18)); + c ^= b; + c -= ((b << 24) | (b >> 8)); +} + +uint64_t uint256::GetHash(const uint256& salt) const +{ + uint32_t a, b, c; + const uint32_t *pn = (const uint32_t*)data; + const uint32_t *salt_pn = (const uint32_t*)salt.data; + a = b = c = 0xdeadbeef + WIDTH; + + a += pn[0] ^ salt_pn[0]; + b += pn[1] ^ salt_pn[1]; + c += pn[2] ^ salt_pn[2]; + HashMix(a, b, c); + a += pn[3] ^ salt_pn[3]; + b += pn[4] ^ salt_pn[4]; + c += pn[5] ^ salt_pn[5]; + HashMix(a, b, c); + a += pn[6] ^ salt_pn[6]; + b += pn[7] ^ salt_pn[7]; + HashFinal(a, b, c); + + return ((((uint64_t)b) << 32) | c); +} diff --git a/src/Native/libmultihash/equi/uint256.h b/src/Native/libmultihash/equi/uint256.h new file mode 100644 index 000000000..3729c981a --- /dev/null +++ b/src/Native/libmultihash/equi/uint256.h @@ -0,0 +1,158 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UINT256_H +#define BITCOIN_UINT256_H + +#include +#include +#include +#include +#include +#include + +/** Template base class for fixed-sized opaque blobs. */ +template +class base_blob +{ +protected: + enum { WIDTH=BITS/8 }; + alignas(uint32_t) uint8_t data[WIDTH]; +public: + base_blob() + { + memset(data, 0, sizeof(data)); + } + + explicit base_blob(const std::vector& vch); + + bool IsNull() const + { + for (int i = 0; i < WIDTH; i++) + if (data[i] != 0) + return false; + return true; + } + + void SetNull() + { + memset(data, 0, sizeof(data)); + } + + friend inline bool operator==(const base_blob& a, const base_blob& b) { return memcmp(a.data, b.data, sizeof(a.data)) == 0; } + friend inline bool operator!=(const base_blob& a, const base_blob& b) { return memcmp(a.data, b.data, sizeof(a.data)) != 0; } + friend inline bool operator<(const base_blob& a, const base_blob& b) { return memcmp(a.data, b.data, sizeof(a.data)) < 0; } + + std::string GetHex() const; + void SetHex(const char* psz); + void SetHex(const std::string& str); + std::string ToString() const; + + unsigned char* begin() + { + return &data[0]; + } + + unsigned char* end() + { + return &data[WIDTH]; + } + + const unsigned char* begin() const + { + return &data[0]; + } + + const unsigned char* end() const + { + return &data[WIDTH]; + } + + unsigned int size() const + { + return sizeof(data); + } + + unsigned int GetSerializeSize(int nType, int nVersion) const + { + return sizeof(data); + } + + template + void Serialize(Stream& s, int nType, int nVersion) const + { + s.write((char*)data, sizeof(data)); + } + + template + void Unserialize(Stream& s, int nType, int nVersion) + { + s.read((char*)data, sizeof(data)); + } +}; + +/** 160-bit opaque blob. + * @note This type is called uint160 for historical reasons only. It is an opaque + * blob of 160 bits and has no integer operations. + */ +class uint160 : public base_blob<160> { +public: + uint160() {} + uint160(const base_blob<160>& b) : base_blob<160>(b) {} + explicit uint160(const std::vector& vch) : base_blob<160>(vch) {} +}; + +/** 256-bit opaque blob. + * @note This type is called uint256 for historical reasons only. It is an + * opaque blob of 256 bits and has no integer operations. Use arith_uint256 if + * those are required. + */ +class uint256 : public base_blob<256> { +public: + uint256() {} + uint256(const base_blob<256>& b) : base_blob<256>(b) {} + explicit uint256(const std::vector& vch) : base_blob<256>(vch) {} + + /** A cheap hash function that just returns 64 bits from the result, it can be + * used when the contents are considered uniformly random. It is not appropriate + * when the value can easily be influenced from outside as e.g. a network adversary could + * provide values to trigger worst-case behavior. + * @note The result of this function is not stable between little and big endian. + */ + uint64_t GetCheapHash() const + { + uint64_t result; + memcpy((void*)&result, (void*)data, 8); + return result; + } + + /** A more secure, salted hash function. + * @note This hash is not stable between little and big endian. + */ + uint64_t GetHash(const uint256& salt) const; +}; + +/* uint256 from const char *. + * This is a separate function because the constructor uint256(const char*) can result + * in dangerously catching uint256(0). + */ +inline uint256 uint256S(const char *str) +{ + uint256 rv; + rv.SetHex(str); + return rv; +} +/* uint256 from std::string. + * This is a separate function because the constructor uint256(const std::string &str) can result + * in dangerously catching uint256(0) via std::string(const char*). + */ +inline uint256 uint256S(const std::string& str) +{ + uint256 rv; + rv.SetHex(str); + return rv; +} + +#endif // BITCOIN_UINT256_H diff --git a/src/Native/libmultihash/equi/util.cpp b/src/Native/libmultihash/equi/util.cpp new file mode 100644 index 000000000..e59783e99 --- /dev/null +++ b/src/Native/libmultihash/equi/util.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined(HAVE_CONFIG_H) +#include "config/bitcoin-config.h" +#endif + +#include "util.h" +#include "random.h" +#include "serialize.h" +#include "utilstrencodings.h" + +#include + +#ifndef WIN32 +// for posix_fallocate +#ifdef __linux__ + +#ifdef _POSIX_C_SOURCE +#undef _POSIX_C_SOURCE +#endif + +#define _POSIX_C_SOURCE 200112L + +#endif // __linux__ + +#include +#include +#include +#include + +#else + +#ifdef _MSC_VER +#pragma warning(disable:4786) +#pragma warning(disable:4804) +#pragma warning(disable:4805) +#pragma warning(disable:4717) +#endif + +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0501 + +#ifdef _WIN32_IE +#undef _WIN32_IE +#endif +#define _WIN32_IE 0x0501 + +#define WIN32_LEAN_AND_MEAN 1 +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include /* for _commit */ +#include +#endif + + +using namespace std; + +bool fDebug = false; +bool fLogTimestamps = true; +bool fLogTimeMicros = false; + + + +int LogPrintStr(const std::string &str) +{ + int ret = 0; // Returns total number of characters written + // print to console + ret = fwrite(str.data(), 1, str.size(), stdout); + fflush(stdout); + return ret; +} + diff --git a/src/Native/libmultihash/equi/util.h b/src/Native/libmultihash/equi/util.h new file mode 100644 index 000000000..6a2f92f1e --- /dev/null +++ b/src/Native/libmultihash/equi/util.h @@ -0,0 +1,58 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +/** + * Server/client environment: argument handling, config file parsing, + * logging, thread wrappers + */ +#ifndef BITCOIN_UTIL_H +#define BITCOIN_UTIL_H + +#include "tinyformat.h" +#include + + + +/** Send a string to the log output */ +int LogPrintStr(const std::string &str); + +#define LogPrintf(...) LogPrint(NULL, __VA_ARGS__) + +/** + * When we switch to C++11, this can be switched to variadic templates instead + * of this macro-based construction (see tinyformat.h). + */ +#define MAKE_ERROR_AND_LOG_FUNC(n) \ + /** Print to debug.log if -debug=category switch is given OR category is NULL. */ \ + template \ + static inline int LogPrint(const char* category, const char* format, TINYFORMAT_VARARGS(n)) \ + { \ + return LogPrintStr(tfm::format(format, TINYFORMAT_PASSARGS(n))); \ + } \ + /** Log error and return false */ \ + template \ + static inline bool error(const char* format, TINYFORMAT_VARARGS(n)) \ + { \ + LogPrintStr("ERROR: " + tfm::format(format, TINYFORMAT_PASSARGS(n)) + "\n"); \ + return false; \ + } + +TINYFORMAT_FOREACH_ARGNUM(MAKE_ERROR_AND_LOG_FUNC) + +/** + * Zero-arg versions of logging and error, these are not covered by + * TINYFORMAT_FOREACH_ARGNUM + */ +static inline int LogPrint(const char* category, const char* format) +{ + return LogPrintStr(format); +} +static inline bool error(const char* format) +{ + return LogPrintStr(std::string("ERROR: ") + format + "\n"); +} + + +#endif // BITCOIN_UTIL_H diff --git a/src/Native/libmultihash/equi/utilstrencodings.cpp b/src/Native/libmultihash/equi/utilstrencodings.cpp new file mode 100644 index 000000000..0a5fbb3d2 --- /dev/null +++ b/src/Native/libmultihash/equi/utilstrencodings.cpp @@ -0,0 +1,692 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "utilstrencodings.h" + +#include "tinyformat.h" + +#include +#include +#include +#include +#include + +using namespace std; + +string SanitizeString(const string& str) +{ + /** + * safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything + * even possibly remotely dangerous like & or > + */ + static string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_/:?@()"); + string strResult; + for (std::string::size_type i = 0; i < str.size(); i++) + { + if (safeChars.find(str[i]) != std::string::npos) + strResult.push_back(str[i]); + } + return strResult; +} + +string SanitizeFilename(const string& str) +{ + /** + * safeChars chosen to restrict filename, keeping it simple to avoid cross-platform issues. + * http://stackoverflow.com/a/2306003 + */ + static string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890"); + string strResult; + for (std::string::size_type i = 0; i < str.size(); i++) + { + if (safeChars.find(str[i]) != std::string::npos) + strResult.push_back(str[i]); + } + return strResult; +} + +std::string HexInt(uint32_t val) +{ + std::stringstream ss; + ss << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex << val; + return ss.str(); +} + +uint32_t ParseHexToUInt32(const std::string& str) { + std::istringstream converter(str); + uint32_t value; + converter >> std::hex >> value; + return value; +} + +const signed char p_util_hexdigit[256] = +{ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1, + -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, }; + +signed char HexDigit(char c) +{ + return p_util_hexdigit[(unsigned char)c]; +} + +bool IsHex(const string& str) +{ + for(std::string::const_iterator it(str.begin()); it != str.end(); ++it) + { + if (HexDigit(*it) < 0) + return false; + } + return (str.size() > 0) && (str.size()%2 == 0); +} + +vector ParseHex(const char* psz) +{ + // convert hex dump to vector + vector vch; + while (true) + { + while (isspace(*psz)) + psz++; + signed char c = HexDigit(*psz++); + if (c == (signed char)-1) + break; + unsigned char n = (c << 4); + c = HexDigit(*psz++); + if (c == (signed char)-1) + break; + n |= c; + vch.push_back(n); + } + return vch; +} + +vector ParseHex(const string& str) +{ + return ParseHex(str.c_str()); +} + +string EncodeBase64(const unsigned char* pch, size_t len) +{ + static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + string strRet=""; + strRet.reserve((len+2)/3*4); + + int mode=0, left=0; + const unsigned char *pchEnd = pch+len; + + while (pch> 2]; + left = (enc & 3) << 4; + mode = 1; + break; + + case 1: // we have two bits + strRet += pbase64[left | (enc >> 4)]; + left = (enc & 15) << 2; + mode = 2; + break; + + case 2: // we have four bits + strRet += pbase64[left | (enc >> 6)]; + strRet += pbase64[enc & 63]; + mode = 0; + break; + } + } + + if (mode) + { + strRet += pbase64[left]; + strRet += '='; + if (mode == 1) + strRet += '='; + } + + return strRet; +} + +string EncodeBase64(const string& str) +{ + return EncodeBase64((const unsigned char*)str.c_str(), str.size()); +} + +vector DecodeBase64(const char* p, bool* pfInvalid) +{ + static const int decode64_table[256] = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, + -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + if (pfInvalid) + *pfInvalid = false; + + vector vchRet; + vchRet.reserve(strlen(p)*3/4); + + int mode = 0; + int left = 0; + + while (1) + { + int dec = decode64_table[(unsigned char)*p]; + if (dec == -1) break; + p++; + switch (mode) + { + case 0: // we have no bits and get 6 + left = dec; + mode = 1; + break; + + case 1: // we have 6 bits and keep 4 + vchRet.push_back((left<<2) | (dec>>4)); + left = dec & 15; + mode = 2; + break; + + case 2: // we have 4 bits and get 6, we keep 2 + vchRet.push_back((left<<4) | (dec>>2)); + left = dec & 3; + mode = 3; + break; + + case 3: // we have 2 bits and get 6 + vchRet.push_back((left<<6) | dec); + mode = 0; + break; + } + } + + if (pfInvalid) + switch (mode) + { + case 0: // 4n base64 characters processed: ok + break; + + case 1: // 4n+1 base64 character processed: impossible + *pfInvalid = true; + break; + + case 2: // 4n+2 base64 characters processed: require '==' + if (left || p[0] != '=' || p[1] != '=' || decode64_table[(unsigned char)p[2]] != -1) + *pfInvalid = true; + break; + + case 3: // 4n+3 base64 characters processed: require '=' + if (left || p[0] != '=' || decode64_table[(unsigned char)p[1]] != -1) + *pfInvalid = true; + break; + } + + return vchRet; +} + +string DecodeBase64(const string& str) +{ + vector vchRet = DecodeBase64(str.c_str()); + return (vchRet.size() == 0) ? string() : string((const char*)&vchRet[0], vchRet.size()); +} + +string EncodeBase32(const unsigned char* pch, size_t len) +{ + static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567"; + + string strRet=""; + strRet.reserve((len+4)/5*8); + + int mode=0, left=0; + const unsigned char *pchEnd = pch+len; + + while (pch> 3]; + left = (enc & 7) << 2; + mode = 1; + break; + + case 1: // we have three bits + strRet += pbase32[left | (enc >> 6)]; + strRet += pbase32[(enc >> 1) & 31]; + left = (enc & 1) << 4; + mode = 2; + break; + + case 2: // we have one bit + strRet += pbase32[left | (enc >> 4)]; + left = (enc & 15) << 1; + mode = 3; + break; + + case 3: // we have four bits + strRet += pbase32[left | (enc >> 7)]; + strRet += pbase32[(enc >> 2) & 31]; + left = (enc & 3) << 3; + mode = 4; + break; + + case 4: // we have two bits + strRet += pbase32[left | (enc >> 5)]; + strRet += pbase32[enc & 31]; + mode = 0; + } + } + + static const int nPadding[5] = {0, 6, 4, 3, 1}; + if (mode) + { + strRet += pbase32[left]; + for (int n=0; n DecodeBase32(const char* p, bool* pfInvalid) +{ + static const int decode32_table[256] = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + if (pfInvalid) + *pfInvalid = false; + + vector vchRet; + vchRet.reserve((strlen(p))*5/8); + + int mode = 0; + int left = 0; + + while (1) + { + int dec = decode32_table[(unsigned char)*p]; + if (dec == -1) break; + p++; + switch (mode) + { + case 0: // we have no bits and get 5 + left = dec; + mode = 1; + break; + + case 1: // we have 5 bits and keep 2 + vchRet.push_back((left<<3) | (dec>>2)); + left = dec & 3; + mode = 2; + break; + + case 2: // we have 2 bits and keep 7 + left = left << 5 | dec; + mode = 3; + break; + + case 3: // we have 7 bits and keep 4 + vchRet.push_back((left<<1) | (dec>>4)); + left = dec & 15; + mode = 4; + break; + + case 4: // we have 4 bits, and keep 1 + vchRet.push_back((left<<4) | (dec>>1)); + left = dec & 1; + mode = 5; + break; + + case 5: // we have 1 bit, and keep 6 + left = left << 5 | dec; + mode = 6; + break; + + case 6: // we have 6 bits, and keep 3 + vchRet.push_back((left<<2) | (dec>>3)); + left = dec & 7; + mode = 7; + break; + + case 7: // we have 3 bits, and keep 0 + vchRet.push_back((left<<5) | dec); + mode = 0; + break; + } + } + + if (pfInvalid) + switch (mode) + { + case 0: // 8n base32 characters processed: ok + break; + + case 1: // 8n+1 base32 characters processed: impossible + case 3: // +3 + case 6: // +6 + *pfInvalid = true; + break; + + case 2: // 8n+2 base32 characters processed: require '======' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || p[4] != '=' || p[5] != '=' || decode32_table[(unsigned char)p[6]] != -1) + *pfInvalid = true; + break; + + case 4: // 8n+4 base32 characters processed: require '====' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || decode32_table[(unsigned char)p[4]] != -1) + *pfInvalid = true; + break; + + case 5: // 8n+5 base32 characters processed: require '===' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || decode32_table[(unsigned char)p[3]] != -1) + *pfInvalid = true; + break; + + case 7: // 8n+7 base32 characters processed: require '=' + if (left || p[0] != '=' || decode32_table[(unsigned char)p[1]] != -1) + *pfInvalid = true; + break; + } + + return vchRet; +} + +string DecodeBase32(const string& str) +{ + vector vchRet = DecodeBase32(str.c_str()); + return (vchRet.size() == 0) ? string() : string((const char*)&vchRet[0], vchRet.size()); +} + +static bool ParsePrechecks(const std::string& str) +{ + if (str.empty()) // No empty string allowed + return false; + if (str.size() >= 1 && (isspace(str[0]) || isspace(str[str.size()-1]))) // No padding allowed + return false; + if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed + return false; + return true; +} + +bool ParseInt32(const std::string& str, int32_t *out) +{ + if (!ParsePrechecks(str)) + return false; + char *endp = NULL; + errno = 0; // strtol will not set errno if valid + long int n = strtol(str.c_str(), &endp, 10); + if(out) *out = (int32_t)n; + // Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow + // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit + // platforms the size of these types may be different. + return endp && *endp == 0 && !errno && + n >= std::numeric_limits::min() && + n <= std::numeric_limits::max(); +} + +bool ParseInt64(const std::string& str, int64_t *out) +{ + if (!ParsePrechecks(str)) + return false; + char *endp = NULL; + errno = 0; // strtoll will not set errno if valid + long long int n = strtoll(str.c_str(), &endp, 10); + if(out) *out = (int64_t)n; + // Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow + // we still have to check that the returned value is within the range of an *int64_t*. + return endp && *endp == 0 && !errno && + n >= std::numeric_limits::min() && + n <= std::numeric_limits::max(); +} + +bool ParseDouble(const std::string& str, double *out) +{ + if (!ParsePrechecks(str)) + return false; + if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed + return false; + std::istringstream text(str); + text.imbue(std::locale::classic()); + double result; + text >> result; + if(out) *out = result; + return text.eof() && !text.fail(); +} + +std::string FormatParagraph(const std::string& in, size_t width, size_t indent) +{ + std::stringstream out; + size_t col = 0; + size_t ptr = 0; + while(ptr < in.size()) + { + // Find beginning of next word + ptr = in.find_first_not_of(' ', ptr); + if (ptr == std::string::npos) + break; + // Find end of next word + size_t endword = in.find_first_of(' ', ptr); + if (endword == std::string::npos) + endword = in.size(); + // Add newline and indentation if this wraps over the allowed width + if (col > 0) + { + if ((col + endword - ptr) > width) + { + out << '\n'; + for(size_t i=0; i (UPPER_BOUND / 10LL)) + return false; /* overflow */ + mantissa *= 10; + } + mantissa += ch - '0'; + mantissa_tzeros = 0; + } + return true; +} + +bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out) +{ + int64_t mantissa = 0; + int64_t exponent = 0; + int mantissa_tzeros = 0; + bool mantissa_sign = false; + bool exponent_sign = false; + int ptr = 0; + int end = val.size(); + int point_ofs = 0; + + if (ptr < end && val[ptr] == '-') { + mantissa_sign = true; + ++ptr; + } + if (ptr < end) + { + if (val[ptr] == '0') { + /* pass single 0 */ + ++ptr; + } else if (val[ptr] >= '1' && val[ptr] <= '9') { + while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') { + if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros)) + return false; /* overflow */ + ++ptr; + } + } else return false; /* missing expected digit */ + } else return false; /* empty string or loose '-' */ + if (ptr < end && val[ptr] == '.') + { + ++ptr; + if (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') + { + while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') { + if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros)) + return false; /* overflow */ + ++ptr; + ++point_ofs; + } + } else return false; /* missing expected digit */ + } + if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E')) + { + ++ptr; + if (ptr < end && val[ptr] == '+') + ++ptr; + else if (ptr < end && val[ptr] == '-') { + exponent_sign = true; + ++ptr; + } + if (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') { + while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') { + if (exponent > (UPPER_BOUND / 10LL)) + return false; /* overflow */ + exponent = exponent * 10 + val[ptr] - '0'; + ++ptr; + } + } else return false; /* missing expected digit */ + } + if (ptr != end) + return false; /* trailing garbage */ + + /* finalize exponent */ + if (exponent_sign) + exponent = -exponent; + exponent = exponent - point_ofs + mantissa_tzeros; + + /* finalize mantissa */ + if (mantissa_sign) + mantissa = -mantissa; + + /* convert to one 64-bit fixed-point value */ + exponent += decimals; + if (exponent < 0) + return false; /* cannot represent values smaller than 10^-decimals */ + if (exponent >= 18) + return false; /* cannot represent values larger than or equal to 10^(18-decimals) */ + + for (int i=0; i < exponent; ++i) { + if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL)) + return false; /* overflow */ + mantissa *= 10; + } + if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND) + return false; /* overflow */ + + if (amount_out) + *amount_out = mantissa; + + return true; +} + diff --git a/src/Native/libmultihash/equi/utilstrencodings.h b/src/Native/libmultihash/equi/utilstrencodings.h new file mode 100644 index 000000000..ccdc6a76b --- /dev/null +++ b/src/Native/libmultihash/equi/utilstrencodings.h @@ -0,0 +1,122 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +/** + * Utilities for converting data from/to strings. + */ +#ifndef BITCOIN_UTILSTRENCODINGS_H +#define BITCOIN_UTILSTRENCODINGS_H + +#include +#include +#include + +#define BEGIN(a) ((char*)&(a)) +#define END(a) ((char*)&((&(a))[1])) +#define UBEGIN(a) ((unsigned char*)&(a)) +#define UEND(a) ((unsigned char*)&((&(a))[1])) +#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0])) + +/** This is needed because the foreach macro can't get over the comma in pair */ +#define PAIRTYPE(t1, t2) std::pair + +std::string SanitizeFilename(const std::string& str); +std::string SanitizeString(const std::string& str); +std::string HexInt(uint32_t val); +uint32_t ParseHexToUInt32(const std::string& str); +std::vector ParseHex(const char* psz); +std::vector ParseHex(const std::string& str); +signed char HexDigit(char c); +bool IsHex(const std::string& str); +std::vector DecodeBase64(const char* p, bool* pfInvalid = NULL); +std::string DecodeBase64(const std::string& str); +std::string EncodeBase64(const unsigned char* pch, size_t len); +std::string EncodeBase64(const std::string& str); +std::vector DecodeBase32(const char* p, bool* pfInvalid = NULL); +std::string DecodeBase32(const std::string& str); +std::string EncodeBase32(const unsigned char* pch, size_t len); +std::string EncodeBase32(const std::string& str); + +std::string i64tostr(int64_t n); +std::string itostr(int n); +int64_t atoi64(const char* psz); +int64_t atoi64(const std::string& str); +int atoi(const std::string& str); + +/** + * Convert string to signed 32-bit integer with strict parse error feedback. + * @returns true if the entire string could be parsed as valid integer, + * false if not the entire string could be parsed or when overflow or underflow occurred. + */ +bool ParseInt32(const std::string& str, int32_t *out); + +/** + * Convert string to signed 64-bit integer with strict parse error feedback. + * @returns true if the entire string could be parsed as valid integer, + * false if not the entire string could be parsed or when overflow or underflow occurred. + */ +bool ParseInt64(const std::string& str, int64_t *out); + +/** + * Convert string to double with strict parse error feedback. + * @returns true if the entire string could be parsed as valid double, + * false if not the entire string could be parsed or when overflow or underflow occurred. + */ +bool ParseDouble(const std::string& str, double *out); + +template +std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) +{ + std::string rv; + static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + rv.reserve((itend-itbegin)*3); + for(T it = itbegin; it < itend; ++it) + { + unsigned char val = (unsigned char)(*it); + if(fSpaces && it != itbegin) + rv.push_back(' '); + rv.push_back(hexmap[val>>4]); + rv.push_back(hexmap[val&15]); + } + + return rv; +} + +template +inline std::string HexStr(const T& vch, bool fSpaces=false) +{ + return HexStr(vch.begin(), vch.end(), fSpaces); +} + +/** + * Format a paragraph of text to a fixed width, adding spaces for + * indentation to any added line. + */ +std::string FormatParagraph(const std::string& in, size_t width = 79, size_t indent = 0); + +/** + * Timing-attack-resistant comparison. + * Takes time proportional to length + * of first argument. + */ +template +bool TimingResistantEqual(const T& a, const T& b) +{ + if (b.size() == 0) return a.size() == 0; + size_t accumulator = a.size() ^ b.size(); + for (size_t i = 0; i < a.size(); i++) + accumulator |= a[i] ^ b[i%b.size()]; + return accumulator == 0; +} + +/** Parse number as fixed point according to JSON number syntax. + * See http://json.org/number.gif + * @returns true on success, false on error. + * @note The result must be in the range (-10^18,10^18), otherwise an overflow error will trigger. + */ +bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out); + +#endif // BITCOIN_UTILSTRENCODINGS_H diff --git a/src/Native/libmultihash/exports.cpp b/src/Native/libmultihash/exports.cpp index 7778173d6..7d57ec87b 100644 --- a/src/Native/libmultihash/exports.cpp +++ b/src/Native/libmultihash/exports.cpp @@ -44,7 +44,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "Lyra2.h" #include "x16r.h" #include "x16s.h" -#include "equi/equi.h" +#include "equi/equihashverify.h" #include "libethash/sha3.h" #include "libethash/internal.h" #include "libethash/ethash.h" @@ -199,9 +199,15 @@ extern "C" MODULE_API void x16s_export(const char* input, char* output, uint32_t x16s_hash(input, output, input_len); } -extern "C" MODULE_API bool equihash_verify_export(const char* header, const char* solution) +extern "C" MODULE_API bool equihash_verify_export(const char* header, int header_length, const char* solution, int solution_length) { - return verifyEH(header, solution); + if (header_length != 140 || solution_length != 1344) { + return false; + } + + std::vector vecSolution(solution, solution + solution_length); + + return verifyEH(header, vecSolution); } extern "C" MODULE_API void sha3_256_export(const char* input, char* output, uint32_t input_len) diff --git a/src/Native/libmultihash/libmultihash.vcxproj b/src/Native/libmultihash/libmultihash.vcxproj index d34f3348c..96cf5691a 100644 --- a/src/Native/libmultihash/libmultihash.vcxproj +++ b/src/Native/libmultihash/libmultihash.vcxproj @@ -73,21 +73,23 @@ true - $(ProjectDir)windows\include\libsodium;$(IncludePath) + $(SolutionDir)\..\..\..\..\boost_1_62_0;$(ProjectDir)windows\include\libsodium;$(IncludePath);$(ProjectDir) + $(SolutionDir)\..\..\..\..\boost_1_62_0\lib32-msvc-14.0;$(LibraryPath) true - $(ProjectDir)windows\include\libsodium;$(IncludePath) - $(ProjectDir)windows\lib\x64;$(LibraryPath) + $(SolutionDir)\..\..\..\..\boost_1_62_0;$(ProjectDir)windows\include\libsodium;$(IncludePath);$(ProjectDir) + $(SolutionDir)\..\..\..\..\boost_1_62_0\lib64-msvc-14.0;$(ProjectDir)windows\lib\x64;$(LibraryPath) false - $(ProjectDir)windows\include\libsodium;$(IncludePath) + $(SolutionDir)\..\..\..\..\boost_1_62_0;$(ProjectDir)windows\include\libsodium;$(IncludePath);$(ProjectDir) + $(SolutionDir)\..\..\..\..\boost_1_62_0\lib32-msvc-14.0;$(LibraryPath) false - $(ProjectDir)windows\include\libsodium;$(ProjectDir)windows\include\libsodium;$(IncludePath) - $(LibraryPath) + $(SolutionDir)\..\..\..\..\boost_1_62_0;$(ProjectDir)windows\include\libsodium;$(ProjectDir)windows\include\libsodium;$(IncludePath);$(ProjectDir) + $(SolutionDir)\..\..\..\..\boost_1_62_0\lib64-msvc-14.0;$(LibraryPath) @@ -98,6 +100,7 @@ SODIUM_STATIC true MultiThreadedDebug + stdcpp14 Windows @@ -114,6 +117,7 @@ SODIUM_STATIC true MultiThreadedDebug + stdcpp14 Windows @@ -132,6 +136,7 @@ SODIUM_STATIC;_CRT_SECURE_NO_WARNINGS true MultiThreaded + stdcpp14 Windows @@ -152,6 +157,7 @@ SODIUM_STATIC;_CRT_SECURE_NO_WARNINGS true MultiThreaded + stdcpp14 Windows @@ -168,8 +174,23 @@ - - + + + + + + + + + + + + + + + + + @@ -190,6 +211,7 @@ + @@ -237,8 +259,20 @@ - - + + + + + + + + + + + + + + @@ -294,6 +328,7 @@ + diff --git a/src/Native/libmultihash/libmultihash.vcxproj.filters b/src/Native/libmultihash/libmultihash.vcxproj.filters index 3cac90791..204836aec 100644 --- a/src/Native/libmultihash/libmultihash.vcxproj.filters +++ b/src/Native/libmultihash/libmultihash.vcxproj.filters @@ -146,12 +146,6 @@ Header Files - - Header Files - - - Header Files - Header Files @@ -212,6 +206,60 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -340,12 +388,6 @@ Source Files - - Source Files - - - Source Files - Source Files @@ -397,8 +439,51 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + \ No newline at end of file diff --git a/src/Native/libmultihash/equi/endian.c b/src/Native/libmultihash/portable_endian.h similarity index 100% rename from src/Native/libmultihash/equi/endian.c rename to src/Native/libmultihash/portable_endian.h