From ba6fb0955341a04340dd85bc8bf7f1797e6e4b9b Mon Sep 17 00:00:00 2001 From: Maksim Strebkov <257byte@gmail.com> Date: Wed, 19 Jun 2024 11:10:42 +0300 Subject: [PATCH] Add ParisC protocol --- .../Proto20/Activation/ProtoActivator.cs | 26 + .../Proto20/Commits/AutostakingCommit.cs | 7 + .../Proto20/Commits/BakerCycleCommit.cs | 7 + .../Proto20/Commits/BakingRightsCommit.cs | 7 + .../Handlers/Proto20/Commits/BigMapCommit.cs | 7 + .../Handlers/Proto20/Commits/BlockCommit.cs | 7 + .../Proto20/Commits/ContractEventCommit.cs | 7 + .../Handlers/Proto20/Commits/CycleCommit.cs | 76 ++ .../Proto20/Commits/DeactivationCommit.cs | 7 + .../Proto20/Commits/DelegatorCycleCommit.cs | 7 + .../Proto20/Commits/EndorsingRewardCommit.cs | 7 + .../Handlers/Proto20/Commits/InboxCommit.cs | 7 + .../Commits/Operations/ActivationsCommit.cs | 7 + .../Commits/Operations/BallotsCommit.cs | 7 + .../Operations/DalPublishCommitmentCommit.cs | 7 + .../Commits/Operations/DelegationsCommit.cs | 7 + .../Commits/Operations/DoubleBakingCommit.cs | 7 + .../Operations/DoubleEndorsingCommit.cs | 7 + .../Operations/DoublePreendorsingCommit.cs | 7 + .../Commits/Operations/DrainDelegateCommit.cs | 7 + .../Commits/Operations/EndorsementsCommit.cs | 7 + .../Operations/IncreasePaidStorageCommit.cs | 7 + .../Operations/NonceRevelationsCommit.cs | 7 + .../Commits/Operations/OriginationsCommit.cs | 7 + .../Operations/PreendorsementsCommit.cs | 7 + .../Commits/Operations/ProposalsCommit.cs | 7 + .../Operations/RegisterConstantsCommit.cs | 7 + .../Commits/Operations/RevealsCommit.cs | 7 + .../Operations/SetDelegateParametersCommit.cs | 7 + .../Operations/SetDepositsLimitCommit.cs | 7 + .../SmartRollupAddMessagesCommit.cs | 7 + .../Operations/SmartRollupCementCommit.cs | 7 + .../Operations/SmartRollupExecuteCommit.cs | 7 + .../Operations/SmartRollupOriginateCommit.cs | 7 + .../Operations/SmartRollupPublishCommit.cs | 7 + .../SmartRollupRecoverBondCommit.cs | 7 + .../Operations/SmartRollupRefuteCommit.cs | 7 + .../Operations/SmartRollupTimeoutCommit.cs | 7 + .../Commits/Operations/StakingCommit.cs | 7 + .../Commits/Operations/TransactionsCommit.cs | 7 + .../Operations/TransferTicketCommit.cs | 7 + .../Operations/UpdateConsensusKeyCommit.cs | 7 + .../Commits/Operations/VdfRevelationCommit.cs | 7 + .../Proto20/Commits/SlashingCommit.cs | 7 + .../Proto20/Commits/SnapshotBalanceCommit.cs | 7 + .../Proto20/Commits/SoftwareCommit.cs | 7 + .../Proto20/Commits/StakingUpdateCommit.cs | 7 + .../Handlers/Proto20/Commits/StateCommit.cs | 7 + .../Proto20/Commits/StatisticsCommit.cs | 7 + .../Handlers/Proto20/Commits/SubsidyCommit.cs | 7 + .../Handlers/Proto20/Commits/TicketsCommit.cs | 7 + .../Handlers/Proto20/Commits/TokensCommit.cs | 7 + .../Handlers/Proto20/Commits/VotingCommit.cs | 7 + .../Proto20/Diagnostics/Diagnostics.cs | 7 + .../Handlers/Proto20/Proto20Handler.cs | 621 ++++++++++ .../Protocols/Handlers/Proto20/Rpc/Rpc.cs | 9 + .../Handlers/Proto20/Validation/Validator.cs | 1028 +++++++++++++++++ Tzkt.Sync/Protocols/Helpers/Chains.cs | 2 +- Tzkt.Sync/Protocols/TezosProtocols.cs | 2 + 59 files changed, 2127 insertions(+), 1 deletion(-) create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Activation/ProtoActivator.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/AutostakingCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/BakerCycleCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/BakingRightsCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/BigMapCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/BlockCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/ContractEventCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/CycleCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/DeactivationCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/DelegatorCycleCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/EndorsingRewardCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/InboxCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/ActivationsCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/BallotsCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DalPublishCommitmentCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DelegationsCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DoubleBakingCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DoubleEndorsingCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DoublePreendorsingCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DrainDelegateCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/EndorsementsCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/IncreasePaidStorageCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/NonceRevelationsCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/OriginationsCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/PreendorsementsCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/ProposalsCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/RegisterConstantsCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/RevealsCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SetDelegateParametersCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SetDepositsLimitCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupAddMessagesCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupCementCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupExecuteCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupOriginateCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupPublishCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupRecoverBondCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupRefuteCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupTimeoutCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/StakingCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/TransactionsCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/TransferTicketCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/UpdateConsensusKeyCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/VdfRevelationCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/SlashingCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/SnapshotBalanceCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/SoftwareCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/StakingUpdateCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/StateCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/StatisticsCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/SubsidyCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/TicketsCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/TokensCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Commits/VotingCommit.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Diagnostics/Diagnostics.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Proto20Handler.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Rpc/Rpc.cs create mode 100644 Tzkt.Sync/Protocols/Handlers/Proto20/Validation/Validator.cs diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Activation/ProtoActivator.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Activation/ProtoActivator.cs new file mode 100644 index 00000000..10a351b8 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Activation/ProtoActivator.cs @@ -0,0 +1,26 @@ +using Tzkt.Data.Models; + +namespace Tzkt.Sync.Protocols.Proto20 +{ + partial class ProtoActivator : Proto19.ProtoActivator + { + public ProtoActivator(ProtocolHandler proto) : base(proto) { } + + protected override void UpgradeParameters(Protocol protocol, Protocol prev) + { + // nothing to upgrade + } + + protected override Task MigrateContext(AppState state) + { + // nothing to migrate + return Task.CompletedTask; + } + + protected override Task RevertContext(AppState state) + { + // nothing to revert + return Task.CompletedTask; + } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/AutostakingCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/AutostakingCommit.cs new file mode 100644 index 00000000..1843bf8f --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/AutostakingCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class AutostakingCommit : Proto19.AutostakingCommit + { + public AutostakingCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/BakerCycleCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/BakerCycleCommit.cs new file mode 100644 index 00000000..a05239cc --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/BakerCycleCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class BakerCycleCommit : Proto18.BakerCycleCommit + { + public BakerCycleCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/BakingRightsCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/BakingRightsCommit.cs new file mode 100644 index 00000000..06bb1c54 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/BakingRightsCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class BakingRightsCommit : Proto18.BakingRightsCommit + { + public BakingRightsCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/BigMapCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/BigMapCommit.cs new file mode 100644 index 00000000..d2e33376 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/BigMapCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class BigMapCommit : Proto1.BigMapCommit + { + public BigMapCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/BlockCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/BlockCommit.cs new file mode 100644 index 00000000..0b282c78 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/BlockCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class BlockCommit : Proto19.BlockCommit + { + public BlockCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/ContractEventCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/ContractEventCommit.cs new file mode 100644 index 00000000..333089e3 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/ContractEventCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class ContractEventCommit : Proto14.ContractEventCommit + { + public ContractEventCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/CycleCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/CycleCommit.cs new file mode 100644 index 00000000..8a6d4070 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/CycleCommit.cs @@ -0,0 +1,76 @@ +using Microsoft.EntityFrameworkCore; +using Netezos.Encoding; +using Tzkt.Data.Models; + +namespace Tzkt.Sync.Protocols.Proto20 +{ + class CycleCommit : ProtocolCommit + { + public Cycle FutureCycle { get; protected set; } + public List Snapshots { get; protected set; } + public Dictionary SelectedStakes { get; protected set; } + + public CycleCommit(ProtocolHandler protocol) : base(protocol) { } + + public async Task Apply(Block block) + { + if (!block.Events.HasFlag(BlockEvents.CycleBegin)) + return; + + var index = block.Cycle + block.Protocol.ConsensusRightsDelay; + + var contextTask = Proto.Rpc.GetCycleAsync(block.Level, index); + var issuanceTask = Proto.Rpc.GetExpectedIssuance(block.Level); + await Task.WhenAll(contextTask, issuanceTask); + + var context = await contextTask; + var issuance = await issuanceTask; + var cycleIssuance = issuance.EnumerateArray().First(x => x.RequiredInt32("cycle") == index); + + SelectedStakes = context.RequiredArray("selected_stake_distribution") + .EnumerateArray() + .ToDictionary( + x => Cache.Accounts.GetDelegate(x.RequiredString("baker")).Id, + x => x.Required("active_stake").RequiredInt64("frozen") + x.Required("active_stake").RequiredInt64("delegated")); + + Snapshots = await Db.SnapshotBalances + .AsNoTracking() + .Where(x => x.Level == block.Level - 1 && x.AccountId == x.BakerId) + .ToListAsync(); + + FutureCycle = new Cycle + { + Index = index, + FirstLevel = block.Protocol.GetCycleStart(index), + LastLevel = block.Protocol.GetCycleEnd(index), + SnapshotLevel = block.Level - 1, + TotalBakers = SelectedStakes.Count, + TotalBakingPower = SelectedStakes.Values.Sum(), + Seed = Hex.Parse(context.RequiredString("random_seed")), + BlockReward = cycleIssuance.RequiredInt64("baking_reward_fixed_portion"), + BlockBonusPerSlot = cycleIssuance.RequiredInt64("baking_reward_bonus_per_slot"), + EndorsementRewardPerSlot = cycleIssuance.RequiredInt64("attesting_reward_per_slot"), + NonceRevelationReward = cycleIssuance.RequiredInt64("seed_nonce_revelation_tip"), + VdfRevelationReward = cycleIssuance.RequiredInt64("vdf_revelation_tip") + }; + + FutureCycle.MaxBlockReward = FutureCycle.BlockReward + + FutureCycle.BlockBonusPerSlot * (block.Protocol.EndorsersPerBlock - block.Protocol.ConsensusThreshold); + + Db.Cycles.Add(FutureCycle); + } + + public async Task Revert(Block block) + { + if (!block.Events.HasFlag(BlockEvents.CycleBegin)) + return; + + block.Protocol ??= await Cache.Protocols.GetAsync(block.ProtoCode); + + await Db.Database.ExecuteSqlRawAsync($""" + DELETE FROM "Cycles" + WHERE "Index" = {block.Cycle + block.Protocol.ConsensusRightsDelay} + """); + } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/DeactivationCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/DeactivationCommit.cs new file mode 100644 index 00000000..9c0c00a9 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/DeactivationCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class DeactivationCommit : Proto2.DeactivationCommit + { + public DeactivationCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/DelegatorCycleCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/DelegatorCycleCommit.cs new file mode 100644 index 00000000..2d394101 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/DelegatorCycleCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class DelegatorCycleCommit : Proto18.DelegatorCycleCommit + { + public DelegatorCycleCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/EndorsingRewardCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/EndorsingRewardCommit.cs new file mode 100644 index 00000000..8677325c --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/EndorsingRewardCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class EndorsingRewardCommit : Proto19.EndorsingRewardCommit + { + public EndorsingRewardCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/InboxCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/InboxCommit.cs new file mode 100644 index 00000000..6a2591d5 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/InboxCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + public class InboxCommit : Proto17.InboxCommit + { + public InboxCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/ActivationsCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/ActivationsCommit.cs new file mode 100644 index 00000000..f8bd1a8d --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/ActivationsCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class ActivationsCommit : Proto12.ActivationsCommit + { + public ActivationsCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/BallotsCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/BallotsCommit.cs new file mode 100644 index 00000000..3eaacd3b --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/BallotsCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class BallotsCommit : Proto3.BallotsCommit + { + public BallotsCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DalPublishCommitmentCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DalPublishCommitmentCommit.cs new file mode 100644 index 00000000..064c571c --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DalPublishCommitmentCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class DalPublishCommitmentCommit : Proto19.DalPublishCommitmentCommit + { + public DalPublishCommitmentCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DelegationsCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DelegationsCommit.cs new file mode 100644 index 00000000..7be30cb2 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DelegationsCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class DelegationsCommit : Proto18.DelegationsCommit + { + public DelegationsCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DoubleBakingCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DoubleBakingCommit.cs new file mode 100644 index 00000000..061046a0 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DoubleBakingCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class DoubleBakingCommit : Proto19.DoubleBakingCommit + { + public DoubleBakingCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DoubleEndorsingCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DoubleEndorsingCommit.cs new file mode 100644 index 00000000..d983a780 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DoubleEndorsingCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class DoubleEndorsingCommit : Proto19.DoubleEndorsingCommit + { + public DoubleEndorsingCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DoublePreendorsingCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DoublePreendorsingCommit.cs new file mode 100644 index 00000000..4c6561ba --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DoublePreendorsingCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class DoublePreendorsingCommit : Proto19.DoublePreendorsingCommit + { + public DoublePreendorsingCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DrainDelegateCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DrainDelegateCommit.cs new file mode 100644 index 00000000..b993719f --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/DrainDelegateCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class DrainDelegateCommit : Proto15.DrainDelegateCommit + { + public DrainDelegateCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/EndorsementsCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/EndorsementsCommit.cs new file mode 100644 index 00000000..6e7fffc3 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/EndorsementsCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class EndorsementsCommit : Proto19.EndorsementsCommit + { + public EndorsementsCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/IncreasePaidStorageCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/IncreasePaidStorageCommit.cs new file mode 100644 index 00000000..a746feba --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/IncreasePaidStorageCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class IncreasePaidStorageCommit : Proto14.IncreasePaidStorageCommit + { + public IncreasePaidStorageCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/NonceRevelationsCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/NonceRevelationsCommit.cs new file mode 100644 index 00000000..07f1b264 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/NonceRevelationsCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class NonceRevelationsCommit : Proto19.NonceRevelationsCommit + { + public NonceRevelationsCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/OriginationsCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/OriginationsCommit.cs new file mode 100644 index 00000000..ef12fc30 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/OriginationsCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class OriginationsCommit : Proto14.OriginationsCommit + { + public OriginationsCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/PreendorsementsCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/PreendorsementsCommit.cs new file mode 100644 index 00000000..3e393487 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/PreendorsementsCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class PreendorsementsCommit : Proto19.PreendorsementsCommit + { + public PreendorsementsCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/ProposalsCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/ProposalsCommit.cs new file mode 100644 index 00000000..39653895 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/ProposalsCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class ProposalsCommit : Proto14.ProposalsCommit + { + public ProposalsCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/RegisterConstantsCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/RegisterConstantsCommit.cs new file mode 100644 index 00000000..67dd9453 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/RegisterConstantsCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class RegisterConstantsCommit : Proto14.RegisterConstantsCommit + { + public RegisterConstantsCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/RevealsCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/RevealsCommit.cs new file mode 100644 index 00000000..3540a195 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/RevealsCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class RevealsCommit : Proto14.RevealsCommit + { + public RevealsCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SetDelegateParametersCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SetDelegateParametersCommit.cs new file mode 100644 index 00000000..fd136a6a --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SetDelegateParametersCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class SetDelegateParametersCommit : Proto18.SetDelegateParametersCommit + { + public SetDelegateParametersCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SetDepositsLimitCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SetDepositsLimitCommit.cs new file mode 100644 index 00000000..4d59f122 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SetDepositsLimitCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class SetDepositsLimitCommit : Proto12.SetDepositsLimitCommit + { + public SetDepositsLimitCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupAddMessagesCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupAddMessagesCommit.cs new file mode 100644 index 00000000..3e9acd55 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupAddMessagesCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class SmartRollupAddMessagesCommit : Proto16.SmartRollupAddMessagesCommit + { + public SmartRollupAddMessagesCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupCementCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupCementCommit.cs new file mode 100644 index 00000000..0f9b522e --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupCementCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class SmartRollupCementCommit : Proto17.SmartRollupCementCommit + { + public SmartRollupCementCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupExecuteCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupExecuteCommit.cs new file mode 100644 index 00000000..edafda47 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupExecuteCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class SmartRollupExecuteCommit : Proto16.SmartRollupExecuteCommit + { + public SmartRollupExecuteCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupOriginateCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupOriginateCommit.cs new file mode 100644 index 00000000..28425529 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupOriginateCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class SmartRollupOriginateCommit : Proto16.SmartRollupOriginateCommit + { + public SmartRollupOriginateCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupPublishCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupPublishCommit.cs new file mode 100644 index 00000000..55a1eb53 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupPublishCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class SmartRollupPublishCommit : Proto16.SmartRollupPublishCommit + { + public SmartRollupPublishCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupRecoverBondCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupRecoverBondCommit.cs new file mode 100644 index 00000000..30d69790 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupRecoverBondCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class SmartRollupRecoverBondCommit : Proto16.SmartRollupRecoverBondCommit + { + public SmartRollupRecoverBondCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupRefuteCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupRefuteCommit.cs new file mode 100644 index 00000000..37621213 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupRefuteCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class SmartRollupRefuteCommit : Proto16.SmartRollupRefuteCommit + { + public SmartRollupRefuteCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupTimeoutCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupTimeoutCommit.cs new file mode 100644 index 00000000..ee93181a --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/SmartRollupTimeoutCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class SmartRollupTimeoutCommit : Proto16.SmartRollupTimeoutCommit + { + public SmartRollupTimeoutCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/StakingCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/StakingCommit.cs new file mode 100644 index 00000000..37aea057 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/StakingCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class StakingCommit : Proto19.StakingCommit + { + public StakingCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/TransactionsCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/TransactionsCommit.cs new file mode 100644 index 00000000..7819c126 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/TransactionsCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class TransactionsCommit : Proto14.TransactionsCommit + { + public TransactionsCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/TransferTicketCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/TransferTicketCommit.cs new file mode 100644 index 00000000..56f768aa --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/TransferTicketCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class TransferTicketCommit : Proto13.TransferTicketCommit + { + public TransferTicketCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/UpdateConsensusKeyCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/UpdateConsensusKeyCommit.cs new file mode 100644 index 00000000..19a2208e --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/UpdateConsensusKeyCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class UpdateConsensusKeyCommit : Proto15.UpdateConsensusKeyCommit + { + public UpdateConsensusKeyCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/VdfRevelationCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/VdfRevelationCommit.cs new file mode 100644 index 00000000..414be7ed --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/Operations/VdfRevelationCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class VdfRevelationCommit : Proto19.VdfRevelationCommit + { + public VdfRevelationCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/SlashingCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/SlashingCommit.cs new file mode 100644 index 00000000..6a2c761b --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/SlashingCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class SlashingCommit : Proto19.SlashingCommit + { + public SlashingCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/SnapshotBalanceCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/SnapshotBalanceCommit.cs new file mode 100644 index 00000000..a64cabec --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/SnapshotBalanceCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class SnapshotBalanceCommit : Proto19.SnapshotBalanceCommit + { + public SnapshotBalanceCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/SoftwareCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/SoftwareCommit.cs new file mode 100644 index 00000000..3adcb652 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/SoftwareCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class SoftwareCommit : Proto5.SoftwareCommit + { + public SoftwareCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/StakingUpdateCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/StakingUpdateCommit.cs new file mode 100644 index 00000000..36265b08 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/StakingUpdateCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class StakingUpdateCommit : Proto18.StakingUpdateCommit + { + public StakingUpdateCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/StateCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/StateCommit.cs new file mode 100644 index 00000000..22459971 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/StateCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class StateCommit : Proto1.StateCommit + { + public StateCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/StatisticsCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/StatisticsCommit.cs new file mode 100644 index 00000000..22417578 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/StatisticsCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class StatisticsCommit : Proto1.StatisticsCommit + { + public StatisticsCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/SubsidyCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/SubsidyCommit.cs new file mode 100644 index 00000000..4d280529 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/SubsidyCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class SubsidyCommit : Proto10.SubsidyCommit + { + public SubsidyCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/TicketsCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/TicketsCommit.cs new file mode 100644 index 00000000..7ef7807f --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/TicketsCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class TicketsCommit : Proto16.TicketsCommit + { + public TicketsCommit(ProtocolHandler protocol) : base(protocol) { } + } +} \ No newline at end of file diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/TokensCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/TokensCommit.cs new file mode 100644 index 00000000..ed0b0b34 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/TokensCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class TokensCommit : Proto5.TokensCommit + { + public TokensCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/VotingCommit.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/VotingCommit.cs new file mode 100644 index 00000000..c415bb5c --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Commits/VotingCommit.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class VotingCommit : Proto13.VotingCommit + { + public VotingCommit(ProtocolHandler protocol) : base(protocol) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Diagnostics/Diagnostics.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Diagnostics/Diagnostics.cs new file mode 100644 index 00000000..deaaa109 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Diagnostics/Diagnostics.cs @@ -0,0 +1,7 @@ +namespace Tzkt.Sync.Protocols.Proto20 +{ + class Diagnostics : Proto18.Diagnostics + { + public Diagnostics(ProtocolHandler handler) : base(handler) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Proto20Handler.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Proto20Handler.cs new file mode 100644 index 00000000..7e771313 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Proto20Handler.cs @@ -0,0 +1,621 @@ +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using App.Metrics; +using Tzkt.Data; +using Tzkt.Data.Models; +using Tzkt.Data.Models.Base; +using Tzkt.Sync.Services; +using Tzkt.Sync.Protocols.Proto20; + +namespace Tzkt.Sync.Protocols +{ + class Proto20Handler : ProtocolHandler + { + public override IDiagnostics Diagnostics { get; } + public override IValidator Validator { get; } + public override IRpc Rpc { get; } + public override string VersionName => "paris_020"; + public override int VersionNumber => 20; + + public Proto20Handler(TezosNode node, TzktContext db, CacheService cache, QuotesService quotes, IServiceProvider services, IConfiguration config, ILogger logger, IMetrics metrics) + : base(node, db, cache, quotes, services, config, logger, metrics) + { + Rpc = new Rpc(node); + Diagnostics = new Diagnostics(this); + Validator = new Validator(this); + } + + public override Task Activate(AppState state, JsonElement block) => new ProtoActivator(this).Activate(state, block); + public override Task Deactivate(AppState state) => new ProtoActivator(this).Deactivate(state); + + public override async Task Commit(JsonElement block) + { + await new StatisticsCommit(this).Apply(block); + + var blockCommit = new BlockCommit(this); + await blockCommit.Apply(block); + + var cycleCommit = new CycleCommit(this); + await cycleCommit.Apply(blockCommit.Block); + + await new SetDelegateParametersCommit(this).ActivateStakingParameters(blockCommit.Block); + + await new SoftwareCommit(this).Apply(blockCommit.Block, block); + await new DeactivationCommit(this).Apply(blockCommit.Block, block); + + #region implicit operations + foreach (var op in block + .Required("metadata") + .RequiredArray("implicit_operations_results") + .EnumerateArray() + .Where(x => x.RequiredString("kind") == "transaction")) + await new SubsidyCommit(this).Apply(blockCommit.Block, op); + #endregion + + var operations = block.RequiredArray("operations", 4); + + #region operations 0 + foreach (var operation in operations[0].EnumerateArray()) + { + foreach (var content in operation.RequiredArray("contents", 1).EnumerateArray()) + { + switch (content.RequiredString("kind")) + { + case "attestation": + case "attestation_with_dal": + await new EndorsementsCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "preattestation": + case "preattestation_with_dal": + new PreendorsementsCommit(this).Apply(blockCommit.Block, operation, content); + break; + default: + throw new NotImplementedException($"'{content.RequiredString("kind")}' is not allowed in operations[0]"); + } + } + } + #endregion + + #region operations 1 + var dictatorSeen = false; + foreach (var operation in operations[1].EnumerateArray()) + { + foreach (var content in operation.RequiredArray("contents", 1).EnumerateArray()) + { + switch (content.RequiredString("kind")) + { + case "proposals": + var proposalsCommit = new ProposalsCommit(this); + await proposalsCommit.Apply(blockCommit.Block, operation, content); + dictatorSeen = proposalsCommit.DictatorSeen; + break; + case "ballot": + await new BallotsCommit(this).Apply(blockCommit.Block, operation, content); + break; + default: + throw new NotImplementedException($"'{content.RequiredString("kind")}' is not allowed in operations[1]"); + } + } + if (dictatorSeen) break; + } + #endregion + + #region operations 2 + foreach (var operation in operations[2].EnumerateArray()) + { + foreach (var content in operation.RequiredArray("contents", 1).EnumerateArray()) + { + switch (content.RequiredString("kind")) + { + case "activate_account": + await new ActivationsCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "double_baking_evidence": + await new DoubleBakingCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "double_attestation_evidence": + await new DoubleEndorsingCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "double_preattestation_evidence": + await new DoublePreendorsingCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "seed_nonce_revelation": + await new NonceRevelationsCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "vdf_revelation": + await new VdfRevelationCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "drain_delegate": + await new DrainDelegateCommit(this).Apply(blockCommit.Block, operation, content); + break; + default: + throw new NotImplementedException($"'{content.RequiredString("kind")}' is not allowed in operations[2]"); + } + } + } + #endregion + + var bigMapCommit = new BigMapCommit(this); + var ticketsCommit = new TicketsCommit(this); + + #region operations 3 + foreach (var operation in operations[3].EnumerateArray()) + { + Manager.Init(operation); + foreach (var content in operation.RequiredArray("contents").EnumerateArray()) + { + switch (content.RequiredString("kind")) + { + case "set_deposits_limit": + await new SetDepositsLimitCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "increase_paid_storage": + await new IncreasePaidStorageCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "update_consensus_key": + await new UpdateConsensusKeyCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "reveal": + await new RevealsCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "register_global_constant": + await new RegisterConstantsCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "delegation": + await new DelegationsCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "origination": + var orig = new OriginationsCommit(this); + await orig.Apply(blockCommit.Block, operation, content); + if (orig.BigMapDiffs != null) + bigMapCommit.Append(orig.Origination, orig.Origination.Contract, orig.BigMapDiffs); + break; + case "transaction": + var src = content.RequiredString("source"); + var dst = content.RequiredString("destination"); + if (src == dst && + src.StartsWith("tz") && + content.Optional("parameters")?.RequiredString("entrypoint") is string entrypoint) + { + if (Proto18.StakingCommit.Entrypoints.Contains(entrypoint)) + { + await new StakingCommit(this).Apply(blockCommit.Block, operation, content); + break; + } + else if (Proto18.SetDelegateParametersCommit.Entrypoint == entrypoint) + { + await new SetDelegateParametersCommit(this).Apply(blockCommit.Block, operation, content); + break; + } + } + + var parent = new TransactionsCommit(this); + await parent.Apply(blockCommit.Block, operation, content); + if (parent.BigMapDiffs != null) + bigMapCommit.Append(parent.Transaction, parent.Transaction.Target as Contract, parent.BigMapDiffs); + if (parent.TicketUpdates != null) + ticketsCommit.Append(parent.Transaction, parent.Transaction, parent.TicketUpdates); + + if (content.Required("metadata").TryGetProperty("internal_operation_results", out var internalResult)) + { + foreach (var internalContent in internalResult.EnumerateArray()) + { + switch (internalContent.RequiredString("kind")) + { + case "delegation": + await new DelegationsCommit(this).ApplyInternal(blockCommit.Block, parent.Transaction, internalContent); + break; + case "origination": + var internalOrig = new OriginationsCommit(this); + await internalOrig.ApplyInternal(blockCommit.Block, parent.Transaction, internalContent); + if (internalOrig.BigMapDiffs != null) + bigMapCommit.Append(internalOrig.Origination, internalOrig.Origination.Contract, internalOrig.BigMapDiffs); + break; + case "transaction": + var internalTx = new TransactionsCommit(this); + await internalTx.ApplyInternal(blockCommit.Block, parent.Transaction, internalContent); + if (internalTx.BigMapDiffs != null) + bigMapCommit.Append(internalTx.Transaction, internalTx.Transaction.Target as Contract, internalTx.BigMapDiffs); + if (internalTx.TicketUpdates != null) + ticketsCommit.Append(parent.Transaction, internalTx.Transaction, internalTx.TicketUpdates); + break; + case "event": + await new ContractEventCommit(this).Apply(blockCommit.Block, internalContent); + break; + default: + throw new NotImplementedException($"internal '{internalContent.RequiredString("kind")}' is not implemented"); + } + } + } + break; + case "transfer_ticket": + var parent1 = new TransferTicketCommit(this); + await parent1.Apply(blockCommit.Block, operation, content); + if (parent1.TicketUpdates != null) + ticketsCommit.Append(parent1.Operation, parent1.Operation, parent1.TicketUpdates); + if (content.Required("metadata").TryGetProperty("internal_operation_results", out var internalResult1)) + { + foreach (var internalContent in internalResult1.EnumerateArray()) + { + switch (internalContent.RequiredString("kind")) + { + case "transaction": + var internalTx = new TransactionsCommit(this); + await internalTx.ApplyInternal(blockCommit.Block, parent1.Operation, internalContent); + if (internalTx.BigMapDiffs != null) + bigMapCommit.Append(internalTx.Transaction, internalTx.Transaction.Target as Contract, internalTx.BigMapDiffs); + if (internalTx.TicketUpdates != null) + ticketsCommit.Append(parent1.Operation, internalTx.Transaction, internalTx.TicketUpdates); + break; + case "event": + await new ContractEventCommit(this).Apply(blockCommit.Block, internalContent); + break; + default: + throw new NotImplementedException($"internal '{internalContent.RequiredString("kind")}' inside 'transfer_ticket' is not expected"); + } + } + } + break; + case "smart_rollup_add_messages": + await new SmartRollupAddMessagesCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "smart_rollup_cement": + await new SmartRollupCementCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "smart_rollup_execute_outbox_message": + var parent2 = new SmartRollupExecuteCommit(this); + await parent2.Apply(blockCommit.Block, operation, content); + if (parent2.TicketUpdates != null) + ticketsCommit.Append(parent2.Operation, parent2.Operation, parent2.TicketUpdates); + if (content.Required("metadata").TryGetProperty("internal_operation_results", out var internalResult2)) + { + foreach (var internalContent in internalResult2.EnumerateArray()) + { + switch (internalContent.RequiredString("kind")) + { + case "delegation": + await new DelegationsCommit(this).ApplyInternal(blockCommit.Block, parent2.Operation, internalContent); + break; + case "origination": + var internalOrig = new OriginationsCommit(this); + await internalOrig.ApplyInternal(blockCommit.Block, parent2.Operation, internalContent); + if (internalOrig.BigMapDiffs != null) + bigMapCommit.Append(internalOrig.Origination, internalOrig.Origination.Contract, internalOrig.BigMapDiffs); + break; + case "transaction": + var internalTx = new TransactionsCommit(this); + await internalTx.ApplyInternal(blockCommit.Block, parent2.Operation, internalContent); + if (internalTx.BigMapDiffs != null) + bigMapCommit.Append(internalTx.Transaction, internalTx.Transaction.Target as Contract, internalTx.BigMapDiffs); + if (internalTx.TicketUpdates != null) + ticketsCommit.Append(parent2.Operation, internalTx.Transaction, internalTx.TicketUpdates); + break; + case "event": + await new ContractEventCommit(this).Apply(blockCommit.Block, internalContent); + break; + default: + throw new NotImplementedException($"internal '{internalContent.RequiredString("kind")}' is not implemented"); + } + } + } + break; + case "smart_rollup_originate": + await new SmartRollupOriginateCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "smart_rollup_publish": + await new SmartRollupPublishCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "smart_rollup_recover_bond": + await new SmartRollupRecoverBondCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "smart_rollup_refute": + await new SmartRollupRefuteCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "smart_rollup_timeout": + await new SmartRollupTimeoutCommit(this).Apply(blockCommit.Block, operation, content); + break; + case "dal_publish_commitment": + await new DalPublishCommitmentCommit(this).Apply(blockCommit.Block, operation, content); + break; + default: + throw new NotImplementedException($"'{content.RequiredString("kind")}' is not expected in operations[3]"); + } + } + Manager.Reset(); + } + #endregion + + await blockCommit.ApplyRewards(block); + + new InboxCommit(this).Apply(blockCommit.Block); + + await bigMapCommit.Apply(); + await ticketsCommit.Apply(); + await new TokensCommit(this).Apply(blockCommit.Block, bigMapCommit.Updates); + + var brCommit = new BakingRightsCommit(this); + await brCommit.Apply(blockCommit.Block, cycleCommit.FutureCycle, cycleCommit.SelectedStakes); + + await new DelegatorCycleCommit(this).Apply(blockCommit.Block, cycleCommit.FutureCycle); + + await new BakerCycleCommit(this).Apply( + blockCommit.Block, + cycleCommit.FutureCycle, + brCommit.FutureBakingRights, + brCommit.FutureEndorsingRights, + cycleCommit.Snapshots, + cycleCommit.SelectedStakes, + brCommit.CurrentRights); + + await new EndorsingRewardCommit(this).Apply(blockCommit.Block, block); + await new StateCommit(this).Apply(blockCommit.Block, block); + } + + public override async Task AfterCommit(JsonElement rawBlock) + { + var block = await Cache.Blocks.CurrentAsync(); + await new SlashingCommit(this).Apply(block, rawBlock); + await new VotingCommit(this).Apply(block, rawBlock); + await new AutostakingCommit(this).Apply(block, rawBlock); + + Diagnostics.TrackChanges(); + await Db.SaveChangesAsync(); + + await new SnapshotBalanceCommit(this).Apply(rawBlock, block); + } + + public override async Task BeforeRevert() + { + var block = await Cache.Blocks.CurrentAsync(); + await new SnapshotBalanceCommit(this).Revert(block); + await new AutostakingCommit(this).Revert(block); + await new VotingCommit(this).Revert(block); + await new SlashingCommit(this).Revert(block); + } + + public override async Task Revert() + { + var currBlock = await Cache.Blocks.CurrentAsync(); + Db.TryAttach(currBlock); + + #region load operations + var operations = new List(40); + + if (currBlock.Operations.HasFlag(Operations.Activations)) + operations.AddRange(await Db.ActivationOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.Delegations)) + operations.AddRange(await Db.DelegationOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.Endorsements)) + operations.AddRange(await Db.EndorsementOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.Preendorsements)) + operations.AddRange(await Db.PreendorsementOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.Originations)) + operations.AddRange(await Db.OriginationOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.Reveals)) + operations.AddRange(await Db.RevealOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.SetDepositsLimits)) + operations.AddRange(await Db.SetDepositsLimitOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.SetDelegateParameters)) + operations.AddRange(await Db.SetDelegateParametersOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.RegisterConstant)) + operations.AddRange(await Db.RegisterConstantOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.IncreasePaidStorage)) + operations.AddRange(await Db.IncreasePaidStorageOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.UpdateConsensusKey)) + operations.AddRange(await Db.UpdateConsensusKeyOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.Revelations)) + operations.AddRange(await Db.NonceRevelationOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.VdfRevelation)) + operations.AddRange(await Db.VdfRevelationOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.Staking)) + operations.AddRange(await Db.StakingOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.Transactions)) + operations.AddRange(await Db.TransactionOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.TransferTicket)) + operations.AddRange(await Db.TransferTicketOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.DoubleBakings)) + operations.AddRange(await Db.DoubleBakingOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.DoubleEndorsings)) + operations.AddRange(await Db.DoubleEndorsingOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.DoublePreendorsings)) + operations.AddRange(await Db.DoublePreendorsingOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.DrainDelegate)) + operations.AddRange(await Db.DrainDelegateOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.Ballots)) + operations.AddRange(await Db.BallotOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.Proposals)) + operations.AddRange(await Db.ProposalOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.RevelationPenalty)) + await Db.Entry(currBlock).Collection(x => x.RevelationPenalties).LoadAsync(); + + if (currBlock.Operations.HasFlag(Operations.Migrations)) + await Db.Entry(currBlock).Collection(x => x.Migrations).LoadAsync(); + + if (currBlock.Operations.HasFlag(Operations.SmartRollupAddMessages)) + operations.AddRange(await Db.SmartRollupAddMessagesOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.SmartRollupCement)) + operations.AddRange(await Db.SmartRollupCementOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.SmartRollupExecute)) + operations.AddRange(await Db.SmartRollupExecuteOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.SmartRollupOriginate)) + operations.AddRange(await Db.SmartRollupOriginateOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.SmartRollupPublish)) + operations.AddRange(await Db.SmartRollupPublishOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.SmartRollupRecoverBond)) + operations.AddRange(await Db.SmartRollupRecoverBondOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.SmartRollupRefute)) + operations.AddRange(await Db.SmartRollupRefuteOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Operations.HasFlag(Operations.DalPublishCommitment)) + operations.AddRange(await Db.DalPublishCommitmentOps.Where(x => x.Level == currBlock.Level).ToListAsync()); + + if (currBlock.Events.HasFlag(BlockEvents.NewAccounts)) + { + await Db.Entry(currBlock).Collection(x => x.CreatedAccounts).LoadAsync(); + foreach (var account in currBlock.CreatedAccounts) + Cache.Accounts.Add(account); + } + #endregion + + await new StatisticsCommit(this).Revert(currBlock); + + await new EndorsingRewardCommit(this).Revert(currBlock); + + await new BakerCycleCommit(this).Revert(currBlock); + await new DelegatorCycleCommit(this).Revert(currBlock); + await new BakingRightsCommit(this).Revert(currBlock); + await new TokensCommit(this).Revert(currBlock); + await new TicketsCommit(this).Revert(currBlock); + await new BigMapCommit(this).Revert(currBlock); + await new ContractEventCommit(this).Revert(currBlock); + await new InboxCommit(this).Revert(currBlock); + await new BlockCommit(this).RevertRewards(currBlock); + + foreach (var operation in operations.OrderByDescending(x => x.Id)) + { + switch (operation) + { + case EndorsementOperation op: + await new EndorsementsCommit(this).Revert(currBlock, op); + break; + case PreendorsementOperation op: + await new PreendorsementsCommit(this).Revert(currBlock, op); + break; + case ProposalOperation op: + await new ProposalsCommit(this).Revert(currBlock, op); + break; + case BallotOperation op: + await new BallotsCommit(this).Revert(currBlock, op); + break; + case ActivationOperation op: + await new ActivationsCommit(this).Revert(currBlock, op); + break; + case DoubleBakingOperation op: + new DoubleBakingCommit(this).Revert(op); + break; + case DoubleEndorsingOperation op: + new DoubleEndorsingCommit(this).Revert(op); + break; + case DoublePreendorsingOperation op: + new DoublePreendorsingCommit(this).Revert(op); + break; + case NonceRevelationOperation op: + await new NonceRevelationsCommit(this).Revert(currBlock, op); + break; + case VdfRevelationOperation op: + await new VdfRevelationCommit(this).Revert(currBlock, op); + break; + case DrainDelegateOperation op: + await new DrainDelegateCommit(this).Revert(currBlock, op); + break; + case RevealOperation op: + await new RevealsCommit(this).Revert(currBlock, op); + break; + case IncreasePaidStorageOperation op: + await new IncreasePaidStorageCommit(this).Revert(currBlock, op); + break; + case UpdateConsensusKeyOperation op: + await new UpdateConsensusKeyCommit(this).Revert(currBlock, op); + break; + case RegisterConstantOperation registerConstant: + await new RegisterConstantsCommit(this).Revert(currBlock, registerConstant); + break; + case SetDepositsLimitOperation setDepositsLimit: + await new SetDepositsLimitCommit(this).Revert(currBlock, setDepositsLimit); + break; + case DelegationOperation op: + if (op.InitiatorId == null) + await new DelegationsCommit(this).Revert(currBlock, op); + else + await new DelegationsCommit(this).RevertInternal(currBlock, op); + break; + case OriginationOperation op: + if (op.InitiatorId == null) + await new OriginationsCommit(this).Revert(currBlock, op); + else + await new OriginationsCommit(this).RevertInternal(currBlock, op); + break; + case StakingOperation op: + await new StakingCommit(this).Revert(currBlock, op); + break; + case SetDelegateParametersOperation op: + await new SetDelegateParametersCommit(this).Revert(currBlock, op); + break; + case TransactionOperation op: + if (op.InitiatorId == null) + await new TransactionsCommit(this).Revert(currBlock, op); + else + await new TransactionsCommit(this).RevertInternal(currBlock, op); + break; + case TransferTicketOperation op: + await new TransferTicketCommit(this).Revert(currBlock, op); + break; + case SmartRollupAddMessagesOperation op: + await new SmartRollupAddMessagesCommit(this).Revert(currBlock, op); + break; + case SmartRollupCementOperation op: + await new SmartRollupCementCommit(this).Revert(currBlock, op); + break; + case SmartRollupExecuteOperation op: + await new SmartRollupExecuteCommit(this).Revert(currBlock, op); + break; + case SmartRollupOriginateOperation op: + await new SmartRollupOriginateCommit(this).Revert(currBlock, op); + break; + case SmartRollupPublishOperation op: + await new SmartRollupPublishCommit(this).Revert(currBlock, op); + break; + case SmartRollupRecoverBondOperation op: + await new SmartRollupRecoverBondCommit(this).Revert(currBlock, op); + break; + case SmartRollupRefuteOperation op: + await new SmartRollupRefuteCommit(this).Revert(currBlock, op); + break; + case DalPublishCommitmentOperation op: + await new DalPublishCommitmentCommit(this).Revert(currBlock, op); + break; + default: + throw new NotImplementedException($"'{operation.GetType()}' is not implemented"); + } + } + + await new SubsidyCommit(this).Revert(currBlock); + + await new DeactivationCommit(this).Revert(currBlock); + await new SoftwareCommit(this).Revert(currBlock); + await new SetDelegateParametersCommit(this).DeactivateStakingParameters(currBlock); + await new CycleCommit(this).Revert(currBlock); + new BlockCommit(this).Revert(currBlock); + + await new StateCommit(this).Revert(currBlock); + } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Rpc/Rpc.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Rpc/Rpc.cs new file mode 100644 index 00000000..7c1a6a3d --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Rpc/Rpc.cs @@ -0,0 +1,9 @@ +using Tzkt.Sync.Services; + +namespace Tzkt.Sync.Protocols.Proto20 +{ + class Rpc : Proto19.Rpc + { + public Rpc(TezosNode node) : base(node) { } + } +} diff --git a/Tzkt.Sync/Protocols/Handlers/Proto20/Validation/Validator.cs b/Tzkt.Sync/Protocols/Handlers/Proto20/Validation/Validator.cs new file mode 100644 index 00000000..240a3428 --- /dev/null +++ b/Tzkt.Sync/Protocols/Handlers/Proto20/Validation/Validator.cs @@ -0,0 +1,1028 @@ +using System.Text.Json; +using Tzkt.Data.Models; +using Tzkt.Sync.Services; + +namespace Tzkt.Sync.Protocols.Proto20 +{ + class Validator : IValidator + { + readonly CacheService Cache; + Protocol Protocol; + string Proposer; + string Producer; + int Level; + int Cycle; + + public Validator(ProtocolHandler protocol) => Cache = protocol.Cache; + + public virtual async Task ValidateBlock(JsonElement block) + { + Protocol = await Cache.Protocols.GetAsync(Cache.AppState.GetNextProtocol()); + + if (block.RequiredString("chain_id") != Cache.AppState.GetChainId()) + throw new ValidationException("invalid chain"); + + if (block.RequiredString("protocol") != Cache.AppState.GetNextProtocol()) + throw new ValidationException("invalid block protocol", true); + + ValidateBlockHeader(block.Required("header")); + await ValidateBlockMetadata(block.Required("metadata")); + await ValidateOperations(block.RequiredArray("operations", 4)); + } + + void ValidateBlockHeader(JsonElement header) + { + Level = header.RequiredInt32("level"); + if (Level != Cache.AppState.GetNextLevel()) + throw new ValidationException($"invalid block level", true); + + if (header.RequiredString("predecessor") != Cache.AppState.GetHead()) + throw new ValidationException($"invalid block predecessor", true); + } + + async Task ValidateBlockMetadata(JsonElement metadata) + { + #region baking + Proposer = metadata.RequiredString("proposer"); + if (!Cache.Accounts.DelegateExists(Proposer)) + throw new ValidationException($"non-existent block proposer"); + + Producer = metadata.RequiredString("baker"); + if (!Cache.Accounts.DelegateExists(Producer)) + throw new ValidationException($"non-existent block baker"); + #endregion + + #region level info + Cycle = metadata.Required("level_info").RequiredInt32("cycle"); + if (Cycle != Protocol.GetCycle(Level)) + throw new ValidationException($"invalid block cycle", true); + #endregion + + #region voting info + var periodInfo = metadata.Required("voting_period_info").Required("voting_period"); + var periodIndex = periodInfo.RequiredInt32("index"); + var periodKind = periodInfo.RequiredString("kind") switch + { + "proposal" => PeriodKind.Proposal, + "exploration" => PeriodKind.Exploration, + "cooldown" => PeriodKind.Testing, + "promotion" => PeriodKind.Promotion, + "adoption" => PeriodKind.Adoption, + _ => throw new ValidationException("invalid voting period kind") + }; + + var period = await Cache.Periods.GetAsync(Cache.AppState.Get().VotingPeriod); + if (Level > period.FirstLevel && Level < period.LastLevel) + { + if (periodIndex != period.Index) + throw new ValidationException("invalid voting period index"); + + if (!Protocol.HasDictator && periodKind != period.Kind) + throw new ValidationException("unexpected voting period"); + } + #endregion + + #region deactivation + foreach (var baker in metadata.RequiredArray("deactivated").EnumerateArray()) + if (!Cache.Accounts.DelegateExists(baker.GetString())) + throw new ValidationException($"non-existent deactivated baker {baker}"); + #endregion + + #region balance updates + var balanceUpdates = metadata.RequiredArray("balance_updates").EnumerateArray(); + if (balanceUpdates.Any(x => x.RequiredString("kind") == "contract" && x.RequiredString("origin") == "block" && !Cache.Accounts.DelegateExists(x.RequiredString("contract")))) + throw new ValidationException("non-existent delegate in block balance updates"); + + if (Cycle < Protocol.NoRewardCycles) + { + if (balanceUpdates.Any(x => x.RequiredString("kind") == "minted" && x.RequiredString("category") == "baking rewards")) + throw new ValidationException("unexpected block reward"); + + if (balanceUpdates.Any(x => x.RequiredString("kind") == "minted" && x.RequiredString("category") == "baking bonuses")) + throw new ValidationException("unexpected block bonus"); + } + else + { + if (balanceUpdates.Count(x => x.RequiredString("kind") == "minted" && x.RequiredString("category") == "baking rewards") > 4) + throw new ValidationException("invalid block reward"); + + if (balanceUpdates.Count(x => x.RequiredString("kind") == "minted" && x.RequiredString("category") == "baking bonuses") > 4) + throw new ValidationException("invalid block bonus"); + } + #endregion + + #region implicit operations + foreach (var op in metadata.RequiredArray("implicit_operations_results").EnumerateArray()) + { + var kind = op.RequiredString("kind"); + if (kind == "transaction") + { + var subsidy = op.RequiredArray("balance_updates", 2).EnumerateArray() + .Where(x => x.RequiredString("kind") == "contract"); + + if (subsidy.Count() > 1) + throw new ValidationException("invalid subsidy"); + + if (subsidy.Any(x => x.RequiredString("origin") != "subsidy")) + throw new ValidationException("invalid subsidy origin"); + + if (subsidy.Any(x => x.RequiredString("contract") != Proto10.ProtoActivator.CpmmContract)) + throw new ValidationException("invalid subsidy recepient"); + } + else if (kind == "origination" && Level == Protocol.FirstLevel) + { + var contract = op.RequiredArray("originated_contracts", 1)[0].RequiredString(); + if (!await Cache.Accounts.ExistsAsync(contract, AccountType.Contract)) + throw new ValidationException("unexpected implicit origination"); + } + else + { + throw new ValidationException("unexpected implicit operation kind"); + } + } + #endregion + } + + protected virtual async Task ValidateOperations(JsonElement operations) + { + foreach (var opg in operations.EnumerateArray()) + { + foreach (var op in opg.RequiredArray().EnumerateArray()) + { + foreach (var content in op.RequiredArray("contents").EnumerateArray()) + { + switch (content.RequiredString("kind")) + { + case "attestation": ValidateAttestation(content); break; + case "attestation_with_dal": ValidateAttestation(content); break; + case "preattestation": ValidatePreattestation(content); break; + case "preattestation_with_dal": ValidatePreattestation(content); break; + case "ballot": await ValidateBallot(content); break; + case "proposals": ValidateProposal(content); break; + case "activate_account": await ValidateActivation(content); break; + case "double_baking_evidence": ValidateDoubleBaking(content); break; + case "double_attestation_evidence": ValidateDoubleBaking(content); break; + case "double_preattestation_evidence": ValidateDoubleBaking(content); break; + case "seed_nonce_revelation": await ValidateSeedNonceRevelation(content); break; + case "vdf_revelation": ValidateVdfRevelation(content); break; + case "drain_delegate": ValidateDrainDelegate(content); break; + case "delegation": await ValidateDelegation(content); break; + case "origination": await ValidateOrigination(content); break; + case "transaction": await ValidateTransaction(content); break; + case "reveal": await ValidateReveal(content); break; + case "register_global_constant": await ValidateRegisterConstant(content); break; + case "set_deposits_limit": await ValidateSetDepositsLimit(content); break; + case "increase_paid_storage": await ValidateIncreasePaidStorage(content); break; + case "update_consensus_key": await ValidateUpdateConsensusKey(content); break; + case "tx_rollup_origination": await ValidateTxRollupOrigination(content); break; + case "tx_rollup_submit_batch": await ValidateTxRollupSubmitBatch(content); break; + case "tx_rollup_commit": await ValidateTxRollupCommit(content); break; + case "tx_rollup_finalize_commitment": await ValidateTxRollupFinalizeCommitment(content); break; + case "tx_rollup_remove_commitment": await ValidateTxRollupRemoveCommitment(content); break; + case "tx_rollup_return_bond": await ValidateTxRollupReturnBond(content); break; + case "tx_rollup_rejection": await ValidateTxRollupRejection(content); break; + case "tx_rollup_dispatch_tickets": await ValidateTxRollupDispatchTickets(content); break; + case "transfer_ticket": await ValidateTransferTicket(content); break; + case "smart_rollup_add_messages": await ValidateSmartRollupAddMessages(content); break; + case "smart_rollup_cement": await ValidateSmartRollupCement(content); break; + case "smart_rollup_execute_outbox_message": await ValidateSmartRollupExecute(content); break; + case "smart_rollup_originate": await ValidateSmartRollupOriginate(content); break; + case "smart_rollup_publish": await ValidateSmartRollupPublish(content); break; + case "smart_rollup_recover_bond": await ValidateSmartRollupRecoverBond(content); break; + case "smart_rollup_refute": await ValidateSmartRollupRefute(content); break; + case "smart_rollup_timeout": await ValidateSmartRollupTimeout(content); break; + case "dal_publish_commitment": await ValidateDalPublishCommitment(content); break; + default: + throw new ValidationException("invalid operation content kind"); + } + } + } + } + } + + protected virtual void ValidateAttestation(JsonElement content) + { + if (content.RequiredInt32("level") != Cache.AppState.GetLevel()) + throw new ValidationException("invalid attestation level"); + + if (!Cache.Accounts.DelegateExists(content.Required("metadata").RequiredString("delegate"))) + throw new ValidationException("unknown attestation delegate"); + } + + protected virtual void ValidatePreattestation(JsonElement content) + { + if (content.RequiredInt32("level") != Cache.AppState.GetLevel() + 1) + throw new ValidationException("invalid preattestation level"); + + if (!Cache.Accounts.DelegateExists(content.Required("metadata").RequiredString("delegate"))) + throw new ValidationException("unknown preattestation delegate"); + } + + protected virtual async Task ValidateBallot(JsonElement content) + { + var periodIndex = content.RequiredInt32("period"); + + if (Cache.AppState.Get().VotingPeriod != periodIndex) + throw new ValidationException("invalid ballot voting period"); + + var proposal = await Cache.Proposals.GetOrDefaultAsync(Cache.AppState.Get().VotingEpoch, content.RequiredString("proposal")); + if (proposal?.Status != ProposalStatus.Active) + throw new ValidationException("invalid ballot proposal"); + + if (!Cache.Accounts.DelegateExists(content.RequiredString("source"))) + throw new ValidationException("invalid ballot sender"); + } + + protected virtual void ValidateProposal(JsonElement content) + { + var periodIndex = content.RequiredInt32("period"); + + if (Cache.AppState.Get().VotingPeriod != periodIndex) + throw new ValidationException("invalid proposal voting period"); + + var source = content.RequiredString("source"); + if (Protocol.Dictator != source && !Cache.Accounts.DelegateExists(source)) + throw new ValidationException("invalid proposal sender"); + } + + protected virtual async Task ValidateActivation(JsonElement content) + { + var account = content.RequiredString("pkh"); + + if (await Cache.Accounts.ExistsAsync(account, AccountType.User) && + ((await Cache.Accounts.GetAsync(account)) as User).ActivationsCount > 0) + throw new ValidationException("account is already activated"); + + if (content.Required("metadata").RequiredArray("balance_updates", 2)[1].RequiredString("contract") != account) + throw new ValidationException("invalid activation balance updates"); + } + + protected virtual void ValidateDoubleBaking(JsonElement content) + { + } + + protected virtual async Task ValidateSeedNonceRevelation(JsonElement content) + { + var level = content.RequiredInt32("level"); + var proto = await Cache.Protocols.FindByLevelAsync(level); + + if ((level - proto.GetCycleStart(proto.GetCycle(level)) + 1) % proto.BlocksPerCommitment != 0) + throw new ValidationException("invalid seed nonce revelation level"); + + var balanceUpdates = content.Required("metadata").RequiredArray("balance_updates").EnumerateArray(); + + if (balanceUpdates.Count() % 2 != 0) + throw new ValidationException("invalid seed nonce revelation balance updates count"); + + if (balanceUpdates.Any(x => + x.RequiredString("kind") == "contract" && Proposer != x.RequiredString("contract") || + x.RequiredString("kind") == "freezer" && Proposer != ( + x.Required("staker").Optional("baker_own_stake")?.GetString() + ?? x.Required("staker").Optional("baker_edge")?.GetString() + ?? x.Required("staker").Required("delegate").GetString() + ))) + throw new ValidationException("invalid seed nonce revelation baker"); + } + + protected virtual void ValidateVdfRevelation(JsonElement content) + { + var balanceUpdates = content.Required("metadata").RequiredArray("balance_updates").EnumerateArray(); + + if (balanceUpdates.Count() % 2 != 0) + throw new ValidationException("invalid vdf revelation balance updates count"); + + if (balanceUpdates.Any(x => + x.RequiredString("kind") == "contract" && Proposer != x.RequiredString("contract") || + x.RequiredString("kind") == "freezer" && Proposer != ( + x.Required("staker").Optional("baker_own_stake")?.GetString() + ?? x.Required("staker").Optional("baker_edge")?.GetString() + ?? x.Required("staker").Required("delegate").GetString() + ))) + throw new ValidationException("invalid vdf revelation baker"); + } + + protected virtual void ValidateDrainDelegate(JsonElement content) + { + var drainedBaker = content.RequiredString("delegate"); + var balanceUpdates = content.Required("metadata").RequiredArray("balance_updates").EnumerateArray(); + + if (!Cache.Accounts.DelegateExists(drainedBaker)) + throw new ValidationException("unknown drained delegate"); + + if (balanceUpdates.Count() % 2 != 0) + throw new ValidationException("invalid drain balance updates count"); + + if (balanceUpdates.Where(x => x.RequiredInt64("change") < 0).Any(x => x.RequiredString("contract") != drainedBaker)) + throw new ValidationException("invalid drain balance updates"); + } + + protected virtual async Task ValidateDelegation(JsonElement content) + { + var source = content.RequiredString("source"); + var delegat = content.OptionalString("delegate"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + if (content.Required("metadata").Required("operation_result").RequiredString("status") == "applied" && delegat != null) + if (source != delegat && !Cache.Accounts.DelegateExists(delegat)) + throw new ValidationException("unknown delegate account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + } + + protected virtual void ValidateInternalDelegation(JsonElement content, string initiator) + { + //var delegat = content.OptionalString("delegate"); + + //if (content.Required("result").RequiredString("status") == "applied" && delegat != null) + // if (!Cache.Accounts.DelegateExists(delegat)) + // throw new ValidationException("unknown delegate account"); + } + + protected virtual async Task ValidateOrigination(JsonElement content) + { + var source = content.RequiredString("source"); + var delegat = content.OptionalString("delegate"); + var metadata = content.Required("metadata"); + var result = metadata.Required("operation_result"); + var applied = result.RequiredString("status") == "applied"; + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + if (applied && delegat != null) + if (!Cache.Accounts.DelegateExists(delegat)) + throw new ValidationException("unknown delegate account"); + + ValidateFeeBalanceUpdates( + metadata.OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + + if (applied && result.TryGetProperty("balance_updates", out var resultUpdates)) + ValidateTransferBalanceUpdates( + resultUpdates.EnumerateArray(), + source, + result.RequiredArray("originated_contracts", 1)[0].RequiredString(), + content.RequiredInt64("balance"), + (result.OptionalInt32("paid_storage_size_diff") ?? 0) * Protocol.ByteCost, + Protocol.OriginationSize * Protocol.ByteCost); + } + + protected virtual void ValidateInternalOrigination(JsonElement content, string initiator) + { + //var delegat = content.OptionalString("delegate"); + var result = content.Required("result"); + var applied = result.RequiredString("status") == "applied"; + + //if (applied && delegat != null) + // if (!Cache.Accounts.DelegateExists(delegat)) + // throw new ValidationException("unknown delegate account"); + + if (applied && result.TryGetProperty("balance_updates", out var resultUpdates)) + ValidateTransferBalanceUpdates( + resultUpdates.RequiredArray().EnumerateArray(), + content.RequiredString("source"), + result.RequiredArray("originated_contracts", 1)[0].RequiredString(), + content.RequiredInt64("balance"), + (result.OptionalInt32("paid_storage_size_diff") ?? 0) * Protocol.ByteCost, + Protocol.OriginationSize * Protocol.ByteCost, + initiator); + } + + protected virtual async Task ValidateTransaction(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + var metadata = content.Required("metadata"); + + ValidateFeeBalanceUpdates( + metadata.OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + + var result = metadata.Required("operation_result"); + var applied = result.RequiredString("status") == "applied"; + + if (applied) + { + var target = content.RequiredString("destination"); + + if (source == target && source.StartsWith("tz") && content.Optional("parameters")?.RequiredString("entrypoint") is string entrypoint) + { + switch (entrypoint) + { + case "stake": + case "unstake": + case "finalize_unstake": + case "set_delegate_parameters": + return; + default: + throw new ValidationException("unsupported staking operation"); + } + } + + if (result.TryGetProperty("balance_updates", out var resultUpdates)) + ValidateTransferBalanceUpdates( + resultUpdates.RequiredArray().EnumerateArray(), + source, + target, + content.RequiredInt64("amount"), + (result.OptionalInt32("paid_storage_size_diff") ?? 0) * Protocol.ByteCost, + (result.OptionalBool("allocated_destination_contract") ?? false) ? Protocol.OriginationSize * Protocol.ByteCost : 0); + } + + if (metadata.TryGetProperty("internal_operation_results", out var internalResults)) + { + foreach (var internalContent in internalResults.RequiredArray().EnumerateArray()) + { + switch (internalContent.RequiredString("kind")) + { + case "delegation": ValidateInternalDelegation(internalContent, source); break; + case "origination": ValidateInternalOrigination(internalContent, source); break; + case "transaction": ValidateInternalTransaction(internalContent, source); break; + case "event": break; + default: + throw new ValidationException("invalid internal operation kind"); + } + } + } + } + + protected virtual void ValidateInternalTransaction(JsonElement content, string initiator) + { + var result = content.Required("result"); + var applied = result.RequiredString("status") == "applied"; + + if (applied && result.TryGetProperty("balance_updates", out var resultUpdates)) + ValidateTransferBalanceUpdates( + resultUpdates.RequiredArray().EnumerateArray(), + content.RequiredString("source"), + content.RequiredString("destination"), + content.RequiredInt64("amount"), + (result.OptionalInt32("paid_storage_size_diff") ?? 0) * Protocol.ByteCost, + (result.OptionalBool("allocated_destination_contract") ?? false) ? Protocol.OriginationSize * Protocol.ByteCost : 0, + initiator); + } + + protected virtual async Task ValidateReveal(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + } + + protected virtual async Task ValidateDalPublishCommitment(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + } + + protected virtual async Task ValidateRegisterConstant(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + } + + protected virtual async Task ValidateSetDepositsLimit(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + } + + protected virtual async Task ValidateIncreasePaidStorage(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + } + + protected virtual async Task ValidateUpdateConsensusKey(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + } + + protected virtual async Task ValidateTxRollupOrigination(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + + var result = content.Required("metadata").Required("operation_result"); + var applied = result.RequiredString("status") == "applied"; + + if (applied && result.TryGetProperty("balance_updates", out var resultUpdates)) + ValidateTransferBalanceUpdates( + resultUpdates.EnumerateArray(), + source, + null, + 0, + 0, + 4_000 * Protocol.ByteCost); + } + + protected virtual async Task ValidateTxRollupSubmitBatch(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + + var result = content.Required("metadata").Required("operation_result"); + var applied = result.RequiredString("status") == "applied"; + + if (applied && result.TryGetProperty("balance_updates", out var resultUpdates)) + ValidateTransferBalanceUpdates( + resultUpdates.EnumerateArray(), + source, + null, + 0, + (result.OptionalInt32("paid_storage_size_diff") ?? 0) * Protocol.ByteCost, + 0); + } + + protected virtual async Task ValidateTxRollupCommit(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + + var result = content.Required("metadata").Required("operation_result"); + if (result.TryGetProperty("balance_updates", out var updates) && updates.Count() != 0) + { + if (updates.Count() != 2) + throw new ValidationException("unexpected number of rollup bonds balance updates"); + + if (!updates.EnumerateArray().Any(x => + x.RequiredString("kind") == "contract" && + x.RequiredString("contract") == source)) + throw new ValidationException("invalid transfer balance updates"); + + if (!updates.EnumerateArray().Any(x => + x.RequiredString("kind") == "freezer" && + x.RequiredString("category") == "bonds" && + x.RequiredString("contract") == source)) + throw new ValidationException("invalid transfer balance updates"); + + if (updates[0].RequiredInt64("change") != -updates[1].RequiredInt64("change")) + throw new ValidationException("inconsistent change of rollup bonds balance updates"); + } + } + + protected virtual async Task ValidateTxRollupFinalizeCommitment(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + + var result = content.Required("metadata").Required("operation_result"); + if (result.TryGetProperty("balance_updates", out var updates) && updates.Count() != 0) + throw new ValidationException("unexpected balance updates"); + } + + protected virtual async Task ValidateTxRollupRemoveCommitment(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + + var result = content.Required("metadata").Required("operation_result"); + if (result.TryGetProperty("balance_updates", out var updates) && updates.Count() != 0) + throw new ValidationException("unexpected balance updates"); + } + + protected virtual async Task ValidateTxRollupReturnBond(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + + var result = content.Required("metadata").Required("operation_result"); + if (result.TryGetProperty("balance_updates", out var updates) && updates.Count() != 0) + { + if (updates.Count() != 2) + throw new ValidationException("unexpected number of rollup bonds balance updates"); + + if (!updates.EnumerateArray().Any(x => + x.RequiredString("kind") == "contract" && + x.RequiredString("contract") == source)) + throw new ValidationException("invalid transfer balance updates"); + + if (!updates.EnumerateArray().Any(x => + x.RequiredString("kind") == "freezer" && + x.RequiredString("category") == "bonds" && + x.RequiredString("contract") == source)) + throw new ValidationException("invalid transfer balance updates"); + + if (updates[0].RequiredInt64("change") != -updates[1].RequiredInt64("change")) + throw new ValidationException("inconsistent change of rollup bonds balance updates"); + } + } + + protected virtual async Task ValidateTxRollupRejection(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + } + + protected virtual async Task ValidateTxRollupDispatchTickets(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + } + + protected virtual async Task ValidateTransferTicket(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + } + + protected virtual async Task ValidateSmartRollupAddMessages(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + + var result = content.Required("metadata").Required("operation_result"); + var applied = result.RequiredString("status") == "applied"; + + if (applied && result.TryGetProperty("balance_updates", out var resultUpdates)) + ValidateTransferBalanceUpdates( + resultUpdates.EnumerateArray(), + source, + null, + 0, + 0, + 0); + } + + protected virtual async Task ValidateSmartRollupCement(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + + var result = content.Required("metadata").Required("operation_result"); + if (result.RequiredString("status") == "applied" && + result.TryGetProperty("balance_updates", out var updates) && updates.Count() > 0) + throw new ValidationException("unexpected balnce updates"); + } + + protected virtual async Task ValidateSmartRollupExecute(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + var metadata = content.Required("metadata"); + + ValidateFeeBalanceUpdates( + metadata.OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + + var result = metadata.Required("operation_result"); + var applied = result.RequiredString("status") == "applied"; + + if (applied && result.TryGetProperty("balance_updates", out var resultUpdates)) + ValidateTransferBalanceUpdates( + resultUpdates.RequiredArray().EnumerateArray(), + source, + null, + 0, + (result.OptionalInt32("paid_storage_size_diff") ?? 0) * Protocol.ByteCost, + 0); + + if (metadata.TryGetProperty("internal_operation_results", out var internalResults)) + { + foreach (var internalContent in internalResults.RequiredArray().EnumerateArray()) + { + switch (internalContent.RequiredString("kind")) + { + case "delegation": ValidateInternalDelegation(internalContent, source); break; + case "origination": ValidateInternalOrigination(internalContent, source); break; + case "transaction": ValidateInternalTransaction(internalContent, source); break; + case "event": break; + default: + throw new ValidationException("invalid internal operation kind"); + } + } + } + } + + protected virtual async Task ValidateSmartRollupOriginate(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + + var result = content.Required("metadata").Required("operation_result"); + var applied = result.RequiredString("status") == "applied"; + + if (applied && result.TryGetProperty("balance_updates", out var resultUpdates)) + ValidateTransferBalanceUpdates( + resultUpdates.EnumerateArray(), + source, + null, + 0, + (result.OptionalInt32("size") ?? 0) * Protocol.ByteCost, + 0); + } + + protected virtual async Task ValidateSmartRollupPublish(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + + var result = content.Required("metadata").Required("operation_result"); + if (result.TryGetProperty("balance_updates", out var updates) && updates.Count() != 0) + { + if (updates.Count() != 2) + throw new ValidationException("unexpected number of smart rollup bonds balance updates"); + + if (!updates.EnumerateArray().Any(x => + x.RequiredString("kind") == "contract" && + x.RequiredString("contract") == source)) + throw new ValidationException("invalid smart rollup bonds balance updates"); + + if (!updates.EnumerateArray().Any(x => + x.RequiredString("kind") == "freezer" && + x.RequiredString("category") == "bonds" && + x.RequiredString("contract") == source)) + throw new ValidationException("invalid smart rollup bonds balance updates"); + + if (updates[0].RequiredInt64("change") != -updates[1].RequiredInt64("change")) + throw new ValidationException("inconsistent change of smart rollup bonds balance updates"); + } + } + + protected virtual async Task ValidateSmartRollupRecoverBond(JsonElement content) + { + var source = content.RequiredString("source"); + var staker = content.RequiredString("staker"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + + var result = content.Required("metadata").Required("operation_result"); + if (result.TryGetProperty("balance_updates", out var updates) && updates.Count() != 0) + { + if (updates.Count() != 2) + throw new ValidationException("unexpected number of smart rollup bonds balance updates"); + + if (!updates.EnumerateArray().Any(x => + x.RequiredString("kind") == "contract" && + x.RequiredString("contract") == staker)) + throw new ValidationException("invalid smart rollup bonds balance updates"); + + if (!updates.EnumerateArray().Any(x => + x.RequiredString("kind") == "freezer" && + x.RequiredString("category") == "bonds" && + x.RequiredString("contract") == staker)) + throw new ValidationException("invalid smart rollup bonds balance updates"); + + if (updates[0].RequiredInt64("change") != -updates[1].RequiredInt64("change")) + throw new ValidationException("inconsistent change of smart rollup bonds balance updates"); + } + } + + protected virtual async Task ValidateSmartRollupRefute(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + } + + protected virtual async Task ValidateSmartRollupTimeout(JsonElement content) + { + var source = content.RequiredString("source"); + + if (!await Cache.Accounts.ExistsAsync(source)) + throw new ValidationException("unknown source account"); + + ValidateFeeBalanceUpdates( + content.Required("metadata").OptionalArray("balance_updates")?.EnumerateArray() ?? Enumerable.Empty(), + source, + content.RequiredInt64("fee")); + } + + protected virtual void ValidateFeeBalanceUpdates(IEnumerable balanceUpdates, string sender, long fee) + { + if (fee != 0) + { + if (balanceUpdates.Count() != 2) + throw new ValidationException("invalid fee balance updates count"); + + var first = balanceUpdates.First(); + var last = balanceUpdates.Last(); + + if (first.RequiredString("kind") != "contract" || + first.RequiredString("contract") != sender || + first.RequiredInt64("change") != -fee) + throw new ValidationException("invalid fee contract update"); + + if (last.RequiredString("kind") != "accumulator" || + last.RequiredString("category") != "block fees" || + last.RequiredInt64("change") != fee) + throw new ValidationException("invalid fee accumulator update"); + } + else + { + if (balanceUpdates.Any()) + throw new ValidationException("invalid fee balance updates count"); + } + } + + protected virtual void ValidateTransferBalanceUpdates(IEnumerable balanceUpdates, string sender, string target, long amount, long storageFee, long allocationFee, string initiator = null) + { + if (balanceUpdates.Count() != (amount != 0 ? 2 : 0) + (storageFee != 0 ? 2 : 0) + (allocationFee != 0 ? 2 : 0)) + throw new ValidationException("invalid transfer balance updates count"); + + if (amount > 0) + { + if (!balanceUpdates.Any(x => + x.RequiredString("kind") == "contract" && + x.RequiredInt64("change") == -amount && + x.RequiredString("contract") == sender)) + throw new ValidationException("invalid transfer balance updates"); + + if (!balanceUpdates.Any(x => + x.RequiredString("kind") == "contract" && + x.RequiredInt64("change") == amount && + x.RequiredString("contract") == target)) + throw new ValidationException("invalid transfer balance updates"); + } + + if (storageFee > 0) + { + if (!balanceUpdates.Any(x => + x.RequiredString("kind") == "contract" && + x.RequiredInt64("change") == -storageFee && + x.RequiredString("contract") == (initiator ?? sender))) + throw new ValidationException("invalid storage fee balance updates"); + + if (!balanceUpdates.Any(x => + x.RequiredString("kind") == "burned" && + x.RequiredString("category") == "storage fees" && + x.RequiredInt64("change") == storageFee)) + throw new ValidationException("invalid storage fee balance updates"); + } + + if (allocationFee > 0) + { + if (!balanceUpdates.Any(x => + x.RequiredString("kind") == "contract" && + x.RequiredInt64("change") == -allocationFee && + x.RequiredString("contract") == (initiator ?? sender))) + throw new ValidationException("invalid allocation fee balance updates"); + + if (!balanceUpdates.Any(x => + x.RequiredString("kind") == "burned" && + x.RequiredString("category") == "storage fees" && + x.RequiredInt64("change") == allocationFee)) + throw new ValidationException("invalid allocation fee balance updates"); + } + } + } +} diff --git a/Tzkt.Sync/Protocols/Helpers/Chains.cs b/Tzkt.Sync/Protocols/Helpers/Chains.cs index 57c8a58a..c506f323 100644 --- a/Tzkt.Sync/Protocols/Helpers/Chains.cs +++ b/Tzkt.Sync/Protocols/Helpers/Chains.cs @@ -17,7 +17,7 @@ static class Chains "NetXgbcrNtXD2yA" => "mumbainet", "NetXyuzvDo2Ugzb" => "nairobinet", "NetXxWsskGahzQB" => "oxfordnet", - "NetXR64bNAYkP4S" => "parisnet", + "NetXXWAHLEvre9b" => "parisnet", _ => "private" }; } diff --git a/Tzkt.Sync/Protocols/TezosProtocols.cs b/Tzkt.Sync/Protocols/TezosProtocols.cs index efefde28..594164d7 100644 --- a/Tzkt.Sync/Protocols/TezosProtocols.cs +++ b/Tzkt.Sync/Protocols/TezosProtocols.cs @@ -34,6 +34,7 @@ public static void AddTezosProtocols(this IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); } public static ProtocolHandler GetProtocolHandler(this IServiceProvider services, int level, string protocol) @@ -91,6 +92,7 @@ private static ProtocolHandler GetProtocolHandler(IServiceProvider services, str "ProxfordYmVfjWnRcgjWH36fW6PArwqykTFzotUxRs6gmTcZDuH" => services.GetRequiredService(), "PtParisBQscdCm6Cfow6ndeU6wKJyA3aV1j4D3gQBQMsTQyJCrz" => services.GetRequiredService(), "PtParisBxoLz5gzMmn3d9WBQNoPSZakgnkMC2VNuQ3KXfUtUQeZ" => services.GetRequiredService(), + "PsParisCZo7KAh1Z1smVd9ZMZ1HHn5gkzbM94V3PLCpknFWhUAi" => services.GetRequiredService(), _ => null, }; }