diff --git a/CHANGELOG.md b/CHANGELOG.md index be895d7c..c4183a4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ ## Unreleased changes +## 6.0.0 + +- Support for Concordium Node version 8 API changes: + - Expand enum `ProtocolVersion` with `P8` variant. + - Introduce `IsSuspended` field on `AccountBaker` and `BakerPoolStatus`. + - Introduce new baker events `BakerSuspended` and `BakerResumed` for when a validator choose to suspend. + - Introduce `ChainParametersV3` with `ValidatorScoreParameters`. + - Introduce `Parameter` field for `ContractInitializedEvent`. + - Introduce `EffectValidatorScoreParameters` for pending updates to `ValidatorScoreParameters`. + - Introduce `ValidatorScoreParametersUpdate` and `UpdateType.ValidatorScoreParametersUpdate` for update payloads. + - Introduce `ValidatorPrimedForSuspension` and `ValidatorSuspended` as `ISpecialEvent`. + ## 5.0.0 - Added diff --git a/concordium-base b/concordium-base index 31a168d0..67227da0 160000 --- a/concordium-base +++ b/concordium-base @@ -1 +1 @@ -Subproject commit 31a168d0af2c568e8e6dd7931a404601b2cee090 +Subproject commit 67227da0e592f8be107f313d3b6e6bc2ee706bc0 diff --git a/src/Concordium.Sdk.csproj b/src/Concordium.Sdk.csproj index 3c933262..0ee51040 100644 --- a/src/Concordium.Sdk.csproj +++ b/src/Concordium.Sdk.csproj @@ -15,7 +15,7 @@ concordium;concordium-net-sdk;blockchain;sdk; Concordium ConcordiumNetSdk - 5.0.0 + 6.0.0 icon.png README.md MPL-2.0 diff --git a/src/Types/AccountStakingInfo.cs b/src/Types/AccountStakingInfo.cs index 97d8db72..f72de6c5 100644 --- a/src/Types/AccountStakingInfo.cs +++ b/src/Types/AccountStakingInfo.cs @@ -43,7 +43,8 @@ public sealed record AccountBaker( CcdAmount StakedAmount, BakerInfo BakerInfo, AccountBakerPendingChange? PendingChange, - BakerPoolInfo? BakerPoolInfo + BakerPoolInfo? BakerPoolInfo, + bool IsSuspended ) : IAccountStakingInfo { internal static AccountBaker From(Grpc.V2.AccountStakingInfo.Types.Baker stakeBaker) => @@ -52,7 +53,8 @@ internal static AccountBaker From(Grpc.V2.AccountStakingInfo.Types.Baker stakeBa PendingChange: AccountBakerPendingChange.From(stakeBaker.PendingChange), RestakeEarnings: stakeBaker.RestakeEarnings, StakedAmount: CcdAmount.From(stakeBaker.StakedAmount), - BakerPoolInfo: BakerPoolInfo.From(stakeBaker.PoolInfo) + BakerPoolInfo: BakerPoolInfo.From(stakeBaker.PoolInfo), + IsSuspended: stakeBaker.IsSuspended ); /// diff --git a/src/Types/BakerEvent.cs b/src/Types/BakerEvent.cs index 94649254..338cb8a0 100644 --- a/src/Types/BakerEvent.cs +++ b/src/Types/BakerEvent.cs @@ -50,6 +50,8 @@ internal static IBakerEvent From(BakerEvent bakerEvent) => AmountFraction.From(bakerEvent.BakerSetFinalizationRewardCommission.FinalizationRewardCommission) ), BakerEvent.EventOneofCase.DelegationRemoved => new BakerEventDelegationRemoved(DelegatorId.From(bakerEvent.DelegationRemoved.DelegatorId)), + BakerEvent.EventOneofCase.BakerSuspended => new BakerEventSuspended(BakerId.From(bakerEvent.BakerSuspended.BakerId)), + BakerEvent.EventOneofCase.BakerResumed => new BakerEventResumed(BakerId.From(bakerEvent.BakerResumed.BakerId)), BakerEvent.EventOneofCase.None => throw new MissingEnumException(bakerEvent.EventCase), _ => throw new MissingEnumException(bakerEvent.EventCase) @@ -142,3 +144,15 @@ public sealed record BakerSetFinalizationRewardCommissionEvent(BakerId BakerId, /// /// Delegator's id public sealed record BakerEventDelegationRemoved(DelegatorId DelegatorId) : IBakerEvent; + +/// +/// A baker has been suspended. +/// +/// Suspended baker's id +public sealed record BakerEventSuspended(BakerId BakerId) : IBakerEvent; + +/// +/// A baker has been resumed. +/// +/// The resumed baker's id +public sealed record BakerEventResumed(BakerId BakerId) : IBakerEvent; diff --git a/src/Types/BakerPoolStatus.cs b/src/Types/BakerPoolStatus.cs index a9a9c585..53dd729e 100644 --- a/src/Types/BakerPoolStatus.cs +++ b/src/Types/BakerPoolStatus.cs @@ -31,6 +31,10 @@ namespace Concordium.Sdk.Types; /// /// Any pending change to the baker's stake. /// Total capital staked across all pools. +/// +/// A flag indicating whether the pool owner is suspended. +/// Also `False` if the protocol version does not support validator suspension or the pool is removed. +/// public sealed record BakerPoolStatus( BakerId BakerId, AccountAddress BakerAddress, @@ -40,7 +44,9 @@ public sealed record BakerPoolStatus( BakerPoolInfo? PoolInfo, CurrentPaydayBakerPoolStatus? CurrentPaydayStatus, CcdAmount AllPoolTotalCapital, - BakerPoolPendingChange? BakerStakePendingChange) + BakerPoolPendingChange? BakerStakePendingChange, + bool IsSuspended +) { internal static BakerPoolStatus From(Grpc.V2.PoolInfoResponse poolInfoResponse) => new( @@ -52,6 +58,7 @@ internal static BakerPoolStatus From(Grpc.V2.PoolInfoResponse poolInfoResponse) poolInfoResponse.PoolInfo != null ? BakerPoolInfo.From(poolInfoResponse.PoolInfo) : null, CurrentPaydayBakerPoolStatus.From(poolInfoResponse.CurrentPaydayInfo), CcdAmount.From(poolInfoResponse.AllPoolTotalCapital), - BakerPoolPendingChange.From(poolInfoResponse.EquityPendingChange) - ); + BakerPoolPendingChange.From(poolInfoResponse.EquityPendingChange), + poolInfoResponse.IsSuspended + ); } diff --git a/src/Types/ChainParameters.cs b/src/Types/ChainParameters.cs index 58a9d3f2..6b7a351c 100644 --- a/src/Types/ChainParameters.cs +++ b/src/Types/ChainParameters.cs @@ -21,6 +21,8 @@ internal static IChainParameters From(ChainParameters chainParameters) => ChainParametersV1.From(chainParameters.V1), ChainParameters.ParametersOneofCase.V2 => ChainParametersV2.From(chainParameters.V2), + ChainParameters.ParametersOneofCase.V3 => + ChainParametersV3.From(chainParameters.V3), ChainParameters.ParametersOneofCase.None => throw new MissingEnumException(chainParameters.ParametersCase), _ => throw new MissingEnumException(chainParameters.ParametersCase) @@ -29,7 +31,74 @@ internal static IChainParameters From(ChainParameters chainParameters) => /// /// Values of chain parameters that can be updated via chain updates. -/// This applies to protocol version 6 and up. +/// This applies to protocol version 8 and up. +/// +/// Consensus protocol version 2 timeout parameters. +/// Minimum time interval between blocks. +/// Maximum energy allowed per block. +/// Euro per energy exchange rate. +/// Micro ccd per euro exchange rate. +/// Parameters related to cooldowns when staking. +/// Parameters related mint rate and reward period. +/// The limit for the number of account creations in a block. +/// Parameters related to the distribution of newly minted CCD. +/// Parameters related to the distribution of transaction fees. +/// Parameters related to the distribution from the GAS account. +/// Address of the foundation account. +/// Parameters for baker pools. +/// The finalization committee parameters. +/// Validator score parameters. +/// Root Keys +/// Level 1 Keys +/// Level 2 Keys +public sealed record ChainParametersV3( + TimeoutParameters TimeoutParameters, + TimeSpan MinBlockTime, + EnergyAmount BlockEnergyLimit, + ExchangeRate EuroPerEnergy, + ExchangeRate MicroCcdPerEuro, + CooldownParameters CooldownParameters, + TimeParameters TimeParameters, + CredentialsPerBlockLimit AccountCreationLimit, + MintDistributionCpv1 MintDistribution, + TransactionFeeDistribution TransactionFeeDistribution, + GasRewardsCpv2 GasRewards, + AccountAddress FoundationAccount, + PoolParameters PoolParameters, + FinalizationCommitteeParameters FinalizationCommitteeParameters, + ValidatorScoreParameters ValidatorScoreParameters, + HigherLevelKeys RootKeys, + HigherLevelKeys Level1Keys, + AuthorizationsV1 Level2Keys +) : IChainParameters +{ + internal static ChainParametersV3 From(Grpc.V2.ChainParametersV3 chainParams) => + new( + TimeoutParameters.From(chainParams.ConsensusParameters.TimeoutParameters), + TimeSpan.FromMilliseconds(chainParams.ConsensusParameters.MinBlockTime.Value), + EnergyAmount.From(chainParams.ConsensusParameters.BlockEnergyLimit), + ExchangeRate.From(chainParams.EuroPerEnergy), + ExchangeRate.From(chainParams.MicroCcdPerEuro), + CooldownParameters.From(chainParams.CooldownParameters), + TimeParameters.From(chainParams.TimeParameters), + CredentialsPerBlockLimit.From(chainParams.AccountCreationLimit), + MintDistributionCpv1.From(chainParams.MintDistribution), + TransactionFeeDistribution.From(chainParams.TransactionFeeDistribution), + GasRewardsCpv2.From(chainParams.GasRewards), + AccountAddress.From(chainParams.FoundationAccount), + PoolParameters.From(chainParams.PoolParameters), + FinalizationCommitteeParameters.From(chainParams.FinalizationCommitteeParameters), + ValidatorScoreParameters.From(chainParams.ValidatorScoreParameters), + Types.RootKeys.From(chainParams.RootKeys), + Types.Level1Keys.From(chainParams.Level1Keys), + AuthorizationsV1.From(chainParams.Level2Keys) + ); +} + + +/// +/// Values of chain parameters that can be updated via chain updates. +/// This applies to protocol version 6 and 7. /// /// Consensus protocol version 2 timeout parameters. /// Minimum time interval between blocks. diff --git a/src/Types/ContractInitializedEvent.cs b/src/Types/ContractInitializedEvent.cs index f40dc62d..87d7108e 100644 --- a/src/Types/ContractInitializedEvent.cs +++ b/src/Types/ContractInitializedEvent.cs @@ -11,13 +11,15 @@ namespace Concordium.Sdk.Types; /// Represent the CCD amount the contract instance was initialized with. /// The name of the contract. /// A list of events generated by a smart contract. +/// The parameter passed to the initializer (introduced by Concordium Node v8 and up). public sealed record ContractInitializedEvent( ContractVersion ContractVersion, ModuleReference ModuleReference, ContractAddress ContractAddress, CcdAmount Amount, ContractName InitName, - IList Events + IList Events, + Parameter? Parameter ) { /// @@ -36,8 +38,9 @@ internal static ContractInitializedEvent From(Grpc.V2.ContractInitializedEvent i var events = initializedEvent.Events .Select(e => new ContractEvent(e.Value.ToByteArray())) .ToList(); + var parameter = initializedEvent.Parameter == null ? null : Parameter.From(initializedEvent.Parameter); return new ContractInitializedEvent(contractVersion, moduleReference, contractAddress, amount, - initName, events); + initName, events, parameter); } /// diff --git a/src/Types/PendingUpdate.cs b/src/Types/PendingUpdate.cs index 8af2a1c3..edc19b67 100644 --- a/src/Types/PendingUpdate.cs +++ b/src/Types/PendingUpdate.cs @@ -47,6 +47,8 @@ internal static PendingUpdate From(Grpc.V2.PendingUpdate pendingUpdate) => GrpcEffect.MinBlockTime => new EffectMinBlockTime(TimeSpan.FromMilliseconds(pendingUpdate.MinBlockTime.Value)), GrpcEffect.BlockEnergyLimit => new EffectBlockEnergyLimit(EnergyAmount.From(pendingUpdate.BlockEnergyLimit)), GrpcEffect.FinalizationCommitteeParameters => new EffectFinalizationCommitteeParameters(FinalizationCommitteeParameters.From(pendingUpdate.FinalizationCommitteeParameters)), + GrpcEffect.ValidatorScoreParameters => + new EffectValidatorScoreParameters(ValidatorScoreParameters.From(pendingUpdate.ValidatorScoreParameters)), GrpcEffect.None => throw new NotImplementedException(), _ => throw new MissingEnumException(pendingUpdate.EffectCase), } @@ -104,3 +106,5 @@ public sealed record EffectMinBlockTime(TimeSpan MinBlockTime) : IEffect; public sealed record EffectBlockEnergyLimit(EnergyAmount BlockEnergyLimit) : IEffect; /// Updates to the finalization committee for for chain parameters version 2. public sealed record EffectFinalizationCommitteeParameters(FinalizationCommitteeParameters FinalizationCommitteeParameters) : IEffect; +/// Updates to the validator score for chain parameters version 3. +public sealed record EffectValidatorScoreParameters(ValidatorScoreParameters ValidatorScoreParameters) : IEffect; diff --git a/src/Types/ProtocolVersion.cs b/src/Types/ProtocolVersion.cs index ca768737..61320311 100644 --- a/src/Types/ProtocolVersion.cs +++ b/src/Types/ProtocolVersion.cs @@ -40,6 +40,13 @@ public enum ProtocolVersion /// implements tokenomics changes. /// P7, + /// + /// Protocol `P8` supports suspending inactive validators automatically + /// when they consistently fail to participate in the consensus protocol. + /// Suspended validators can be reactivated manually by a transaction + /// from the validator's account. + /// + P8, } @@ -66,6 +73,7 @@ internal static ProtocolVersion Into(this Grpc.V2.ProtocolVersion protocolVersio Grpc.V2.ProtocolVersion._5 => ProtocolVersion.P5, Grpc.V2.ProtocolVersion._6 => ProtocolVersion.P6, Grpc.V2.ProtocolVersion._7 => ProtocolVersion.P7, + Grpc.V2.ProtocolVersion._8 => ProtocolVersion.P8, _ => throw new MissingEnumException(protocolVersion) }; } diff --git a/src/Types/SpecialEvent.cs b/src/Types/SpecialEvent.cs index b390a166..d2a66c01 100644 --- a/src/Types/SpecialEvent.cs +++ b/src/Types/SpecialEvent.cs @@ -32,6 +32,10 @@ internal static ISpecialEvent From(BlockSpecialEvent specialEvent) => BlockAccrueReward.From(specialEvent.BlockAccrueReward), BlockSpecialEvent.EventOneofCase.PaydayPoolReward => PaydayPoolReward.From(specialEvent.PaydayPoolReward), + BlockSpecialEvent.EventOneofCase.ValidatorPrimedForSuspension => + ValidatorPrimedForSuspension.From(specialEvent.ValidatorPrimedForSuspension), + BlockSpecialEvent.EventOneofCase.ValidatorSuspended => + ValidatorSuspended.From(specialEvent.ValidatorSuspended), BlockSpecialEvent.EventOneofCase.None => throw new MissingEnumException(specialEvent.EventCase), _ => throw new MissingEnumException(specialEvent.EventCase) }; @@ -233,3 +237,37 @@ internal static PaydayPoolReward From(BlockSpecialEvent.Types.PaydayPoolReward p ); } +/// +/// A validator that is primed for suspension at the next snapshot epoch due to too +/// many missed rounds. +/// +/// The id of the primed validator. +/// The account of the primed validator. +public sealed record ValidatorPrimedForSuspension( + BakerId BakerId, + AccountAddress Account +) : ISpecialEvent +{ + internal static ValidatorPrimedForSuspension From(BlockSpecialEvent.Types.ValidatorPrimedForSuspension primed) => + new( + BakerId: BakerId.From(primed.BakerId), + Account: AccountAddress.From(primed.Account) + ); +} + +/// +/// The id of a validator that got suspended due to too many missed rounds. +/// +/// The id of the suspended validator. +/// The account of the suspended validator. +public sealed record ValidatorSuspended( + BakerId BakerId, + AccountAddress Account +) : ISpecialEvent +{ + internal static ValidatorSuspended From(BlockSpecialEvent.Types.ValidatorSuspended suspended) => + new( + BakerId: BakerId.From(suspended.BakerId), + Account: AccountAddress.From(suspended.Account) + ); +} diff --git a/src/Types/UpdatePayload.cs b/src/Types/UpdatePayload.cs index 623ef3d3..9ddee24a 100644 --- a/src/Types/UpdatePayload.cs +++ b/src/Types/UpdatePayload.cs @@ -62,6 +62,8 @@ public enum UpdateType BlockEnergyLimitUpdate, /// Update of finalization committee parameters. FinalizationCommitteeParametersUpdate, + /// Update of validator score parameters. + ValidatorScoreParametersUpdate, } /// @@ -99,6 +101,7 @@ public static UpdateType From(IUpdatePayload payload) => TimeoutParametersUpdate => UpdateType.TimeoutParametersUpdate, TimeParametersCpv1Update => UpdateType.TimeParametersCpv1Update, TransactionFeeDistributionUpdate => UpdateType.TransactionFeeDistributionUpdate, + ValidatorScoreParametersUpdate => UpdateType.ValidatorScoreParametersUpdate, _ => throw new MissingTypeException(payload) }; @@ -149,6 +152,8 @@ internal static IUpdatePayload From(UpdatePayload payload) => BlockEnergyLimitUpdate.From(payload.BlockEnergyLimitUpdate), UpdatePayload.PayloadOneofCase.FinalizationCommitteeParametersUpdate => FinalizationCommitteeParametersUpdate.From(payload.FinalizationCommitteeParametersUpdate), + UpdatePayload.PayloadOneofCase.ValidatorScoreParametersUpdate => + ValidatorScoreParametersUpdate.From(payload.ValidatorScoreParametersUpdate), UpdatePayload.PayloadOneofCase.None => throw new MissingEnumException(payload.PayloadCase), _ => throw new MissingEnumException(payload.PayloadCase) @@ -370,3 +375,11 @@ public sealed record FinalizationCommitteeParametersUpdate internal static FinalizationCommitteeParametersUpdate From(Grpc.V2.FinalizationCommitteeParameters parameters) => new(FinalizationCommitteeParameters.From(parameters)); } +/// +/// Finalization committee parameters (chain parameters version 2). +/// +public sealed record ValidatorScoreParametersUpdate + (ValidatorScoreParameters ValidatorScoreParameters) : IUpdatePayload +{ + internal static ValidatorScoreParametersUpdate From(Grpc.V2.ValidatorScoreParameters parameters) => new(ValidatorScoreParameters.From(parameters)); +} diff --git a/src/Types/ValidatorScoreParameters.cs b/src/Types/ValidatorScoreParameters.cs new file mode 100644 index 00000000..66af924f --- /dev/null +++ b/src/Types/ValidatorScoreParameters.cs @@ -0,0 +1,13 @@ +namespace Concordium.Sdk.Types; + +/// +/// Validator score parameters (introduced in Concordium Protocol 8). +/// +/// The maximal number of missed rounds before a validator gets suspended. +public sealed record ValidatorScoreParameters( + ulong MaximumMissedRounds +) +{ + internal static ValidatorScoreParameters From(Grpc.V2.ValidatorScoreParameters param) => + new(param.MaximumMissedRounds); +} diff --git a/tests/UnitTests/Types/ContractInitializedEventTests.cs b/tests/UnitTests/Types/ContractInitializedEventTests.cs index 5bf3b6fd..0acb28fb 100644 --- a/tests/UnitTests/Types/ContractInitializedEventTests.cs +++ b/tests/UnitTests/Types/ContractInitializedEventTests.cs @@ -27,7 +27,9 @@ public void WhenGetDeserializedEventsFromContractInitializedEvent_ThenReturnPars new ContractAddress(1, 0), CcdAmount.Zero, result.ContractName!, - new List { new(Convert.FromHexString(eventMessage)) }); + new List { new(Convert.FromHexString(eventMessage)) }, + Parameter.Empty() + ); // Act var events = contractInitializedEvent.GetDeserializedEvents(versionedModuleSchema);