Unverified Commit 14f3b952 authored by Clint.Network's avatar Clint.Network Committed by GitHub

Merge pull request #10 from clintnetwork/lottery

Implement Lottery Feature
parents 116d4092 266dd413
......@@ -9,15 +9,21 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using NBitcoin;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PaulMiami.AspNetCore.Mvc.Recaptcha;
using QRCoder;
using RestSharp;
using Stratis.Guru.Models;
using Stratis.Guru.Modules;
using Stratis.Guru.Settings;
namespace Stratis.Guru.Controllers
{
......@@ -25,11 +31,19 @@ namespace Stratis.Guru.Controllers
{
private readonly IMemoryCache _memoryCache;
private readonly IAsk _ask;
private readonly ISettings _settings;
private readonly IParticipation _participation;
private readonly IDraws _draws;
private readonly DrawSettings _drawSettings;
public HomeController(IMemoryCache memoryCache, IAsk ask)
public HomeController(IMemoryCache memoryCache, IAsk ask, ISettings settings, IParticipation participation, IDraws draws, IOptions<DrawSettings> drawSettings)
{
_memoryCache = memoryCache;
_ask = ask;
_settings = settings;
_participation = participation;
_draws = draws;
_drawSettings = drawSettings.Value;
}
public IActionResult Index()
......@@ -69,9 +83,98 @@ namespace Stratis.Guru.Controllers
[Route("lottery")]
public IActionResult Lottery()
{
ViewBag.NextDraw = long.Parse(_memoryCache.Get("NextDraw").ToString());
ViewBag.Jackpot = _memoryCache.Get("Jackpot");
ViewBag.Players = _participation.GetPlayers(_draws.GetLastDraw());
return View();
}
[ValidateRecaptcha]
[HttpPost]
[Route("lottery/participate")]
public IActionResult Participate()
{
if (ModelState.IsValid)
{
var lastDraw = _draws.GetLastDraw();
HttpContext.Session.SetString("HaveBeginParticipation", "true");
return RedirectToAction("Participate", new{id=lastDraw});
}
ViewBag.NextDraw = long.Parse(_memoryCache.Get("NextDraw").ToString());
ViewBag.Jackpot = _memoryCache.Get("Jackpot");
ViewBag.Players = _participation.GetPlayers(_draws.GetLastDraw());
ViewBag.Participate = true;
return View("Lottery");
}
[Route("lottery/participate/{id}")]
public IActionResult Participate(string id)
{
if(HttpContext.Session.GetString("HaveBeginParticipation") == null)
{
return RedirectToAction("Lottery");
}
ViewBag.NextDraw = long.Parse(_memoryCache.Get("NextDraw").ToString());
ViewBag.Jackpot = _memoryCache.Get("Jackpot");
ViewBag.Players = _participation.GetPlayers(_draws.GetLastDraw());
ViewBag.Participate = true;
var pubkey = ExtPubKey.Parse(_drawSettings.PublicKey);
ViewBag.DepositAddress = pubkey.Derive(0).Derive(_settings.GetIterator()).PubKey.GetAddress(Network.StratisMain);
return View("Lottery");
}
[HttpPost]
[Route("lottery/check-payment")]
public IActionResult CheckPayment()
{
var pubkey = ExtPubKey.Parse(_drawSettings.PublicKey);
var depositAddress = pubkey.Derive(0).Derive(_settings.GetIterator()).PubKey.GetAddress(Network.StratisMain).ToString();
ViewBag.DepositAddress = depositAddress;
var rc = new RestClient($"https://stratis.guru/api/address/{depositAddress}");
var rq = new RestRequest(Method.GET);
var response = rc.Execute(rq);
dynamic stratisAdressRequest = JsonConvert.DeserializeObject(response.Content);
if(stratisAdressRequest.unconfirmedBalance + stratisAdressRequest.balance > 0)
{
HttpContext.Session.SetString("Deposited", depositAddress);
HttpContext.Session.SetString("DepositedAmount", ((double)(stratisAdressRequest.unconfirmedBalance + stratisAdressRequest.balance)).ToString());
return Json(true);
}
return BadRequest();
}
[Route("lottery/new-participation")]
public IActionResult NewParticipation()
{
var ticket = Guid.NewGuid().ToString();
HttpContext.Session.SetString("Ticket", ticket);
ViewBag.Ticket = ticket;
return PartialView();
}
[Route("lottery/save-participation")]
public IActionResult SaveParticipation(string nickname, string address)
{
_settings.IncrementIterator();
_participation.StoreParticipation(HttpContext.Session.GetString("Ticket"), nickname, address, double.Parse(HttpContext.Session.GetString("DepositedAmount")));
return RedirectToAction("Participated");
}
[Route("lottery/participated")]
public IActionResult Participated()
{
ViewBag.NextDraw = long.Parse(_memoryCache.Get("NextDraw").ToString());
ViewBag.Jackpot = _memoryCache.Get("Jackpot");
ViewBag.Players = _participation.GetPlayers(_draws.GetLastDraw());
ViewBag.Participated = true;
return View("Lottery");
}
[Route("about")]
public IActionResult About()
{
......
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using Stratis.Guru.Models;
using Stratis.Guru.Services;
using Stratis.Guru.Settings;
namespace Stratis.Guru
{
public class DatabaseContext
{
private readonly IMongoDatabase _database;
public DatabaseContext(IConfiguration configuration)
{
var client = new MongoClient(configuration.GetConnectionString("DefaultConnection"));
if (client != null)
{
_database = client.GetDatabase("stratis-guru");
}
}
public IMongoCollection<Draw> Draws => _database.GetCollection<Draw>("draws");
public IMongoCollection<Setting> Settings => _database.GetCollection<Setting>("lottery");
public IMongoCollection<Participation> Participations => _database.GetCollection<Participation>("participations");
}
}
\ No newline at end of file
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Stratis.Guru.Models
{
public class Draw
{
[BsonId]
public ObjectId Id { get; set; }
public long DrawDate { get; set; }
public bool Passed { get; set; }
public uint BeginIterator { get; set; }
}
}
\ No newline at end of file
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using Stratis.Guru;
using Stratis.Guru.Services;
namespace Stratis.Guru.Models
{
public class Draws : IDraws
{
private DatabaseContext _databaseContext;
private ISettings _settings;
public Draws(DatabaseContext databaseContext, ISettings settings)
{
_databaseContext = databaseContext;
_settings = settings;
}
public string GetLastDraw()
{
return _databaseContext.Draws.Find(x => true).ToList().OrderByDescending(x => x.DrawDate).FirstOrDefault().Id.ToString();
}
public async Task InitDrawAsync(long nextDrawTimestamp)
{
if(!_databaseContext.Draws.Find(x => x.DrawDate.Equals(nextDrawTimestamp)).Any())
{
await _databaseContext.Draws.InsertOneAsync(new Draw()
{
DrawDate = nextDrawTimestamp,
BeginIterator = _settings.GetIterator(),
Passed = false
});
}
}
}
}
\ No newline at end of file
using System.Threading.Tasks;
namespace Stratis.Guru.Models
{
public interface IDraws
{
Task InitDrawAsync(long nextDrawTimestamp);
string GetLastDraw();
}
}
\ No newline at end of file
using System.Collections.Generic;
namespace Stratis.Guru.Models
{
public interface IParticipation
{
void StoreParticipation(string ticket, string nickname, string address, double amount);
List<Participation> GetPlayers(string draw);
}
}
\ No newline at end of file
using System.Threading.Tasks;
namespace Stratis.Guru.Models
{
public interface ISettings
{
Task InitAsync();
uint GetIterator();
void IncrementIterator();
}
}
\ No newline at end of file
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Stratis.Guru.Models
{
public class Participation
{
[BsonId]
public ObjectId Id { get; set; }
public string Ticket { get; set; }
public string WithdrawAddress { get; set; }
public BsonDateTime CreationDate { get; set; }
public string Nickname { get; internal set; }
public string Draw { get; internal set; }
public double Amount { get; internal set; }
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using MongoDB.Driver;
namespace Stratis.Guru.Models
{
public class Participations : IParticipation
{
private DatabaseContext _databaseContext;
private IDraws _draws;
public Participations(DatabaseContext databaseContext, IDraws draws)
{
_databaseContext = databaseContext;
_draws = draws;
}
public List<Participation> GetPlayers(string draw) => _databaseContext.Participations.Find(x => x.Draw.Equals(draw)).ToList().OrderByDescending(x => x.CreationDate).ToList();
public void StoreParticipation(string ticket, string nickname, string address, double amount)
{
_databaseContext.Participations.InsertOne(new Participation
{
CreationDate = DateTime.Now,
Ticket = ticket,
Nickname = nickname,
WithdrawAddress = address,
Draw = _draws.GetLastDraw(),
Amount = amount
});
}
}
}
\ No newline at end of file
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Stratis.Guru.Models
{
public class Setting
{
[BsonId]
public ObjectId Id { get; set; }
public uint PublicKeyIterator { get; set; }
}
}
\ No newline at end of file
using System.Threading.Tasks;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
namespace Stratis.Guru.Models
{
public class Settings : ISettings
{
private DatabaseContext _databaseContext;
public Settings(DatabaseContext databaseContext)
{
_databaseContext = databaseContext;
}
public uint GetIterator() => _databaseContext.Settings.Find(x => true).FirstOrDefault().PublicKeyIterator;
public void IncrementIterator()
{
var itemId =_databaseContext.Settings.Find(x => true).FirstOrDefault().Id;
_databaseContext.Settings.FindOneAndUpdate(Builders<Setting>.Filter.Eq("_id", itemId), Builders<Setting>.Update.Inc(c => c.PublicKeyIterator, (uint)1), new FindOneAndUpdateOptions<Setting> { IsUpsert = true});
}
public async Task InitAsync()
{
if(!_databaseContext.Settings.Find(x => true).Any())
{
await _databaseContext.Settings.InsertOneAsync(new Setting
{
PublicKeyIterator = 0
});
}
}
}
}
\ No newline at end of file
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using NBitcoin;
using Newtonsoft.Json;
using RestSharp;
using Stratis.Guru.Models;
using Stratis.Guru.Settings;
namespace Stratis.Guru.Services
{
public class LotteryService : IHostedService, IDisposable
{
private readonly IMemoryCache _memoryCache;
private readonly ISettings _settings;
private readonly IDraws _draws;
private readonly DrawSettings _drawSettings;
private readonly System.Timers.Timer _updateTimer;
private DateTime _nextDraw;
public LotteryService(IMemoryCache memoryCache, ISettings settings, IDraws draws, IOptions<DrawSettings> drawSettings)
{
_memoryCache = memoryCache;
_settings = settings;
_draws = draws;
_drawSettings = drawSettings.Value;
_updateTimer = new System.Timers.Timer();
}
public async Task StartAsync(CancellationToken cancellationToken)
{
JackpotCounter();
await InitLotteryAsync();
await CalculateNextDrawAsync();
_updateTimer.Interval = TimeSpan.FromMinutes(5).TotalMilliseconds;
_updateTimer.Enabled = true;
_updateTimer.Elapsed += async (sender, args) =>
{
JackpotCounter();
await CalculateNextDrawAsync();
};
_updateTimer.Start();
}
private void JackpotCounter()
{
Task.Run(() =>
{
var totalJackpot = 0.0;
var pubkey = ExtPubKey.Parse(_drawSettings.PublicKey);
for(int i=0; i<=_settings.GetIterator(); i++)
{
var depositAddress = pubkey.Derive(0).Derive((uint)i).PubKey.GetAddress(Network.StratisMain).ToString();
var rc = new RestClient($"https://stratis.guru/api/address/{depositAddress}");
var rq = new RestRequest(Method.GET);
var response = rc.Execute(rq);
dynamic stratisAdressRequest = JsonConvert.DeserializeObject(response.Content);
totalJackpot += (double)stratisAdressRequest.balance;
}
_memoryCache.Set("Jackpot", totalJackpot);
});
}
private async Task InitLotteryAsync() => await _settings.InitAsync();
private async Task CalculateNextDrawAsync()
{
DateTime today = DateTime.UtcNow.Date;
int daysUntilFriday = ((int)DayOfWeek.Friday - (int)today.DayOfWeek + 7) % 7;
_nextDraw = today.AddDays(daysUntilFriday);
var nextDrawTimestamp = ((DateTimeOffset)_nextDraw).ToUnixTimeSeconds();
await _draws.InitDrawAsync(nextDrawTimestamp);
_memoryCache.Set("NextDraw", nextDrawTimestamp);
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public void Dispose()
{
_updateTimer?.Stop();
_updateTimer?.Dispose();
}
}
}
\ No newline at end of file
namespace Stratis.Guru.Settings
{
public class DrawSettings
{
public string PublicKey { get; set; }
}
}
\ No newline at end of file
......@@ -10,9 +10,11 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using PaulMiami.AspNetCore.Mvc.Recaptcha;
using Stratis.Guru.Hubs;
using Stratis.Guru.Models;
using Stratis.Guru.Modules;
......@@ -41,21 +43,39 @@ namespace Stratis.Guru
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = None;
});
services.AddSession(options =>
{
options.Cookie.IsEssential = true;
});
services.Configure<NakoApiSettings>(Configuration.GetSection("NakoApi"));
services.Configure<FixerApiSettings>(Configuration.GetSection("FixerApi"));
services.Configure<DrawSettings>(Configuration.GetSection("Draw"));
services.AddMemoryCache();
services.AddTransient<UpdateHub>();
services.AddSingleton<IAsk, Ask>();
services.AddTransient<DatabaseContext>();
services.AddSingleton<ISettings, Models.Settings>();
services.AddSingleton<IDraws, Draws>();
services.AddSingleton<IParticipation, Participations>();
services.AddHostedService<UpdateInfosService>();
services.AddHostedService<FixerService>();
services.AddHostedService<LotteryService>();
services.AddHostedService<VanityService>();
services.AddLocalization();
services.AddMvc();
services.AddRecaptcha(new RecaptchaOptions
{
SiteKey = "6LfmOIQUAAAAAIEsH2nG6kEiL-bpLhvm0ibhHnol", //Configuration["Recaptcha:SiteKey"],
SecretKey = "6LfmOIQUAAAAAO06PpD8MmndjrjfBr7x-fgnDt2G" //Configuration["Recaptcha:SecretKey"]
});
services.AddSignalR();
}
......@@ -70,9 +90,10 @@ namespace Stratis.Guru
{
app.UseExceptionHandler("/Home/Error");
//app.UseHsts();
app.UseHttpsRedirection();
}
//app.UseHttpsRedirection();
app.UseSession();
app.UseStaticFiles();
app.UseCookiePolicy();
......
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
<DotNetCliToolReference Include="BundlerMinifier.Core" Version="2.6.362" />
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="1.0.113" />
<PackageReference Include="Microsoft.AspNetCore.App"/>
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.1.2" PrivateAssets="All"/>
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.0.4"/>
<PackageReference Include="NStratis" Version="4.0.0.60"/>
<PackageReference Include="QRCoder" Version="1.3.3"/>
<PackageReference Include="RestSharp" Version="106.5.4"/>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="mongocsharpdriver" Version="2.7.2" />
<PackageReference Include="NStratis" Version="4.0.0.60" />
<PackageReference Include="PaulMiami.AspNetCore.Mvc.Recaptcha" Version="1.2.1" />
<PackageReference Include="QRCoder" Version="1.3.3" />
<PackageReference Include="RestSharp" Version="106.5.4" />
<PackageReference Include="Sentry.AspNetCore" Version="1.1.0" />
</ItemGroup>
......
This diff is collapsed.
<h2 class="text-dark mb-1">Thank You</h2>
<p>We have received your $STRAT.</p>
<div class="form-group mt-3 required text-left">
<label for="withdrawAddress">Withdraw Address <span class="text-danger">*</span></label>
<input type="text" name="address" class="form-control text-dark form-control-lg" required id="withdrawAddress" placeholder="Enter a Stratis Address">
</div>
<div class="form-group required text-left">
<label for="nickname">Your Nickname <span class="text-danger">*</span></label>
<input type="text" name="nickname" class="form-control text-dark form-control-lg" required id="nickname" placeholder="Enter a Nickname">
</div>
<div class="form-group mb-0 required text-left">
<label for="ticket" class="required">Your Ticket Number</label>
<input type="text" class="form-control text-dark form-control-lg" id="ticket" value="@ViewBag.Ticket">
<small class="form-text text-muted">Please keep this ticket private.</small>
</div>
\ No newline at end of file
......@@ -41,10 +41,10 @@
</a>
<ul class="nav">
<li class="@(Context.Request.Path.Equals(Url.Action("Index", "Home")) ? "active":"")"><a asp-action="Index" asp-controller="Home"><i class="fa fa-home"></i> HOME</a></li>
@*<li class="@(Context.Request.Path.Equals(Url.Action("Lottery", "Home")) ? "active":"")"><a asp-action="Lottery" asp-controller="Home"><i class="fa fa-trophy"></i> LOTTERY</a></li>*@
<li class="@(Context.Request.Path.Equals(Url.Action("Lottery", "Home")) ? "active":"")"><a asp-action="Lottery" asp-controller="Home"><i class="fa fa-trophy"></i> LOTTERY</a></li>
<li class="@(Context.Request.Path.Equals(Url.Action("Index", "BlockExplorer")) ? "active":"")"><a asp-action="Index" asp-controller="BlockExplorer"><i class="fa fa-cube"></i> BLOCK EXPLORER</a></li>
<li class="@(Context.Request.Path.Equals(Url.Action("Vanity", "Home")) ? "active":"")"><a asp-action="Vanity" asp-controller="Home"><i class="fa fa-at"></i> VANITY</a></li>
<li class="@(Context.Request.Path.Equals(Url.Action("Generator", "Home")) ? "active":"")"><a asp-action="Generator" asp-controller="Home"><i class="fa fa-qrcode"></i> ADDRESS GENERATOR</a></li>
<li class="@(Context.Request.Path.Equals(Url.Action("Generator", "Home")) ? "active":"")"><a asp-action="Generator" asp-controller="Home"><i class="fa fa-qrcode"></i> GENERATOR</a></li>
<li class="@(Context.Request.Path.Equals(Url.Action("Documentation", "Home")) ? "active":"")"><a asp-action="Documentation" asp-controller="Home"><i class="fa fa-book"></i> API</a></li>
<li class="@(Context.Request.Path.Equals(Url.Action("About", "Home")) ? "active":"")"><a asp-action="About" asp-controller="Home"><i class="fa fa-info-circle"></i> ABOUT</a></li>
</ul>
......
......@@ -2,3 +2,4 @@
@using Stratis.Guru.Models
@using System.Globalization
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, PaulMiami.AspNetCore.Mvc.Recaptcha
\ No newline at end of file
......@@ -6,10 +6,6 @@
"library": "bootstrap@4.1.3",
"destination": "wwwroot\\lib\\bootstrap"
},
{
"library": "jquery-countdown@2.2.0",
"destination": "wwwroot\\lib\\jquery-countdown"
},
{
"library": "nprogress@0.2.0",
"destination": "wwwroot\\lib\\nprogress"
......@@ -17,6 +13,18 @@
{
"library": "@aspnet/signalr@1.1.0",
"destination": "wwwroot\\lib\\@aspnet/signalr"
},
{
"library": "jquery-countdown@2.2.0",
"destination": "wwwroot\\lib\\jquery-countdown"
},
{
"library": "jquery-ajax-unobtrusive@3.2.6",
"destination": "wwwroot\\lib\\jquery-ajax-unobtrusive"
},
{
"library": "moment@2.23.0",
"destination": "wwwroot\\lib\\moment"
}
]
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment