public override Task Apply() { var bakers = BootstrapedAccounts .Where(x => x.Type == AccountType.Delegate) .Select(x => x as Data.Models.Delegate); var totalRolls = bakers.Sum(x => (int)(x.StakingBalance / Block.Protocol.TokensPerRoll)); for (int cycle = 0; cycle <= Block.Protocol.PreservedCycles; cycle++) { var bakerCycles = bakers.ToDictionary(x => x.Address, x => { var rolls = (int)(x.StakingBalance / Block.Protocol.TokensPerRoll); var rollsShare = (double)rolls / totalRolls; var bakerCycle = new BakerCycle { Cycle = cycle, BakerId = x.Id, Rolls = rolls, StakingBalance = x.StakingBalance, DelegatedBalance = x.StakingBalance - x.Balance, //nothing is frozen yet DelegatorsCount = x.DelegatorsCount, ExpectedBlocks = Block.Protocol.BlocksPerCycle * rollsShare, ExpectedEndorsements = Block.Protocol.EndorsersPerBlock * Block.Protocol.BlocksPerCycle * rollsShare }; return(bakerCycle); }); #region future baking rights var skipLevel = FutureBakingRights[cycle][0].Level; //skip bootstrap block rights foreach (var br in FutureBakingRights[cycle].SkipWhile(x => cycle == 0 && x.Level == skipLevel)) { if (br.Priority > 0) { continue; } if (!bakerCycles.TryGetValue(br.Delegate, out var bakerCycle)) { throw new Exception("Unknown baking right recipient"); } bakerCycle.FutureBlocks++; bakerCycle.FutureBlockDeposits += GetBlockDeposit(Block.Protocol, cycle); //bakerCycle.FutureBlockRewards += GetBlockReward(Block.Protocol, cycle); } #endregion #region future endorsing rights skipLevel = FutureEndorsingRights[cycle][^ 1].Level; //skip shifted rights
async Task <List <JsonElement> > FetchEndorsingRights(Protocol protocol, int block, Cycle cycle, Dictionary <int, BakerCycle> bakerCycles, List <JsonElement> shiftedRights) { GC.Collect(); //var rights = (await Proto.Rpc.GetEndorsingRightsAsync(block, cycle.Index)).RequiredArray().EnumerateArray(); var rights = new List <JsonElement>(protocol.BlocksPerCycle * protocol.EndorsersPerBlock / 2); var attempts = 0; for (int level = cycle.FirstLevel; level <= cycle.LastLevel; level++) { try { rights.AddRange((await Proto.Rpc.GetLevelEndorsingRightsAsync(block, level)).RequiredArray().EnumerateArray()); attempts = 0; } catch (Exception ex) { Logger.LogError("Failed to fetch endorsing rights for level {0}: {1}", level, ex.Message); if (++attempts >= 10) { throw new Exception("Too many RPC errors when fetching endorsing rights"); } await Task.Delay(3000); level--; } } if (!rights.Any() || rights.Sum(x => x.RequiredArray("slots").Count()) != protocol.BlocksPerCycle * protocol.EndorsersPerBlock) { throw new ValidationException("Rpc returned less endorsing rights (slots) than it should be"); } #region save rights var conn = Db.Database.GetDbConnection() as NpgsqlConnection; using var writer = conn.BeginBinaryImport(@"COPY ""BakingRights"" (""Cycle"", ""Level"", ""BakerId"", ""Type"", ""Status"", ""Priority"", ""Slots"") FROM STDIN (FORMAT BINARY)"); foreach (var er in rights) { writer.StartRow(); writer.Write(protocol.GetCycle(er.RequiredInt32("level") + 1), NpgsqlTypes.NpgsqlDbType.Integer); writer.Write(er.RequiredInt32("level") + 1, NpgsqlTypes.NpgsqlDbType.Integer); writer.Write(Cache.Accounts.GetDelegate(er.RequiredString("delegate")).Id, NpgsqlTypes.NpgsqlDbType.Integer); writer.Write((byte)BakingRightType.Endorsing, NpgsqlTypes.NpgsqlDbType.Smallint); writer.Write((byte)BakingRightStatus.Future, NpgsqlTypes.NpgsqlDbType.Smallint); writer.WriteNull(); writer.Write(er.RequiredArray("slots").Count(), NpgsqlTypes.NpgsqlDbType.Integer); } writer.Complete(); #endregion foreach (var er in rights.Where(x => x.RequiredInt32("level") != cycle.LastLevel)) { var baker = Cache.Accounts.GetDelegate(er.RequiredString("delegate")); var slots = er.RequiredArray("slots").Count(); if (!bakerCycles.TryGetValue(baker.Id, out var bakerCycle)) { throw new Exception("Nonexistent baker cycle"); } bakerCycle.FutureEndorsementDeposits += GetEndorsementDeposit(protocol, cycle.Index, slots); bakerCycle.FutureEndorsementRewards += GetFutureEndorsementReward(protocol, cycle.Index, slots); bakerCycle.FutureEndorsements += slots; } foreach (var er in shiftedRights) { var baker = Cache.Accounts.GetDelegate(er.RequiredString("delegate")); var slots = er.RequiredArray("slots").Count(); if (!bakerCycles.TryGetValue(baker.Id, out var bakerCycle)) { #region shifting hack var snapshotedBaker = await Proto.Rpc.GetDelegateAsync(cycle.SnapshotLevel, baker.Address); var delegators = snapshotedBaker .RequiredArray("delegated_contracts") .EnumerateArray() .Select(x => x.RequiredString()) .Where(x => x != baker.Address); var rolls = (int)(snapshotedBaker.RequiredInt64("staking_balance") / protocol.TokensPerRoll); var rollsShare = (double)rolls / cycle.TotalRolls; bakerCycle = new BakerCycle { Cycle = cycle.Index, BakerId = baker.Id, Rolls = rolls, StakingBalance = snapshotedBaker.RequiredInt64("staking_balance"), DelegatedBalance = snapshotedBaker.RequiredInt64("delegated_balance"), DelegatorsCount = delegators.Count(), ExpectedBlocks = protocol.BlocksPerCycle * rollsShare, ExpectedEndorsements = protocol.EndorsersPerBlock * protocol.BlocksPerCycle * rollsShare }; bakerCycles.Add(baker.Id, bakerCycle); Db.BakerCycles.Add(bakerCycle); foreach (var delegatorAddress in delegators) { var snapshotedDelegator = await Proto.Rpc.GetContractAsync(cycle.SnapshotLevel, delegatorAddress); Db.DelegatorCycles.Add(new DelegatorCycle { BakerId = baker.Id, Balance = snapshotedDelegator.RequiredInt64("balance"), Cycle = cycle.Index, DelegatorId = (await Cache.Accounts.GetAsync(delegatorAddress)).Id }); } #endregion } bakerCycle.FutureEndorsementDeposits += GetEndorsementDeposit(protocol, cycle.Index, slots); bakerCycle.FutureEndorsementRewards += GetFutureEndorsementReward(protocol, cycle.Index, slots); bakerCycle.FutureEndorsements += slots; } return(rights.Where(x => x.RequiredInt32("level") == cycle.LastLevel).ToList()); }