Skip to content

Commit

Permalink
Merge pull request #102 from Concordium/node7-support
Browse files Browse the repository at this point in the history
Support Concordium Node v7 API changes
  • Loading branch information
limemloh authored Sep 23, 2024
2 parents 2f5a904 + 34fe6f3 commit f1213d1
Show file tree
Hide file tree
Showing 16 changed files with 243 additions and 25 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build-test-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
types: [ opened, reopened, synchronize, ready_for_review, edited ]
workflow_dispatch:

env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
workflow_dispatch:

env:
RUST_VERSION: 1.68
RUST_VERSION: 1.73

jobs:
build-native-ubuntu:
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
## Unreleased changes

## 5.0.0

- Added
- More documentation comments on various types.
- `AccountInfo` now includes the `Schedule` field, which are the locked CCD amounts scheduled for release.
- Method `GetStakedAmount` to interface `IAccountStakingInfo` , which provides the staked amount.

- Support Concordium Node version 7 API changes:
- `AccountInfo` now have fields `Cooldowns` and `AvailableBalance`, which are respectively describing the stake currently in cooldown and the (unencrypted) balance of an account, which can be transferred.
- New baker event `BakerEventDelegationRemoved` signaling a delegator is removed, right before becoming a baker/validator.
- New delegation event `DelegationEventBakerRemoved` signaling a baker/validator is removed, right before becoming a delegator.
- Some fields of type `BakerPoolStatus` are now nullable, more specifically `BakerEquityCapital`, `DelegatedCapital`, `DelegatedCapitalCap` and `PoolInfo`.
Querying blocks prior to Protocol Version 7, these fields will always be present.
From protocol version 7, pool removal has immediate effect, however, the pool may still be present for the current (and possibly next) reward period.


## 4.4.0
- Added
- New transaction `InitContract`
Expand Down
2 changes: 1 addition & 1 deletion concordium-base
3 changes: 2 additions & 1 deletion examples/GetPoolInfo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ private static async Task Run(GetPoolInfoOptions options)

Console.WriteLine($"BlockHash: {response.BlockHash}");

Console.WriteLine($"Baker {bakerId} has baker equity capital {response.Response.BakerEquityCapital.GetFormattedCcd()}");
var capital = response.Response.BakerEquityCapital ?? CcdAmount.Zero;
Console.WriteLine($"Baker {bakerId} has baker equity capital {capital.GetFormattedCcd()}");
}
}
2 changes: 1 addition & 1 deletion src/Concordium.Sdk.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<PackageTags>concordium;concordium-net-sdk;blockchain;sdk;</PackageTags>
<Company>Concordium</Company>
<PackageId>ConcordiumNetSdk</PackageId>
<Version>4.4.0</Version>
<Version>5.0.0</Version>
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseExpression>MPL-2.0</PackageLicenseExpression>
Expand Down
53 changes: 48 additions & 5 deletions src/Types/AccountInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,62 @@ namespace Concordium.Sdk.Types;
/// Not null if and only if the account is a baker or delegator. In that case
/// it is the information about the baker or delegator.
/// </param>
/// <param name="Schedule">
/// Release schedule for any locked up amount. This could be an empty
/// release schedule.
/// </param>
/// <param name="Cooldowns">
/// The stake on the account that is in cooldown.
/// There can be multiple amounts in cooldown that expire at different times.
/// This was introduced in protocol version 7, and so is not present in
/// earlier protocol versions.
/// </param>
/// <param name="AvailableBalance">
/// The available (unencrypted) balance of the account (i.e. that can be transferred
/// or used to pay for transactions). This is the balance minus the locked amount.
/// The locked amount is the maximum of the amount in the release schedule and
/// the total amount that is actively staked or in cooldown (inactive stake).
/// </param>
public sealed record AccountInfo(
AccountSequenceNumber AccountNonce,
CcdAmount AccountAmount,
AccountIndex AccountIndex,
AccountAddress AccountAddress,
IAccountStakingInfo? AccountStakingInfo)
IAccountStakingInfo? AccountStakingInfo,
ReleaseSchedule Schedule,
IList<Cooldown> Cooldowns,
CcdAmount AvailableBalance
)
{
internal static AccountInfo From(Grpc.V2.AccountInfo accountInfo) =>
new(
internal static AccountInfo From(Grpc.V2.AccountInfo accountInfo)
{
var accountAmount = CcdAmount.From(accountInfo.Amount);
var schedule = ReleaseSchedule.From(accountInfo.Schedule);
var stakingInfo = Types.AccountStakingInfo.From(accountInfo.Stake);

// `accountInfo.availableBalance` was introduce in the Concordium Node API v7,
// so to remain backwards compatible with node versions prior to this, we
// compute the available balance if this is not present.
CcdAmount availableBalance;
if (accountInfo.AvailableBalance == null)
{
var staked = stakingInfo?.GetStakedAmount() ?? CcdAmount.Zero;
availableBalance = accountAmount - CcdAmount.Max(staked, schedule.Total);
}
else
{
availableBalance = CcdAmount.From(accountInfo.AvailableBalance);
}

return new(
AccountNonce: AccountSequenceNumber.From(accountInfo.SequenceNumber),
AccountAmount: CcdAmount.From(accountInfo.Amount),
AccountAmount: accountAmount,
AccountIndex: new AccountIndex(accountInfo.Index.Value),
AccountAddress: AccountAddress.From(accountInfo.Address),
AccountStakingInfo: Types.AccountStakingInfo.From(accountInfo.Stake)
AccountStakingInfo: stakingInfo,
Schedule: schedule,
Cooldowns: accountInfo.Cooldowns.Select(Cooldown.From).ToList(),
AvailableBalance: availableBalance
);
}
}
17 changes: 16 additions & 1 deletion src/Types/AccountStakingInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ namespace Concordium.Sdk.Types;
/// <summary>
/// Information related to staking for a specific account.
/// </summary>
public interface IAccountStakingInfo { }
public interface IAccountStakingInfo
{
/// <summary>
/// Get the amount of CCD being staked.
/// </summary>
public CcdAmount GetStakedAmount();
}

internal static class AccountStakingInfo
{
Expand Down Expand Up @@ -48,6 +54,11 @@ internal static AccountBaker From(Grpc.V2.AccountStakingInfo.Types.Baker stakeBa
StakedAmount: CcdAmount.From(stakeBaker.StakedAmount),
BakerPoolInfo: BakerPoolInfo.From(stakeBaker.PoolInfo)
);

/// <summary>
/// Get the amount of CCD being staked.
/// </summary>
public CcdAmount GetStakedAmount() => this.StakedAmount;
}

/// <summary>
Expand All @@ -67,4 +78,8 @@ internal static AccountDelegation From(Grpc.V2.AccountStakingInfo.Types.Delegato
DelegationTarget: DelegationTarget.From(stakeDelegator.Target),
PendingChange: AccountDelegationPendingChangeFactory.From(stakeDelegator.PendingChange)
);
/// <summary>
/// Get the amount of CCD being staked.
/// </summary>
public CcdAmount GetStakedAmount() => this.StakedAmount;
}
8 changes: 5 additions & 3 deletions src/Types/AccountTransactionEffects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal static IAccountTransactionEffects From(Grpc.V2.AccountTransactionEffect
new AccountTransfer(
CcdAmount.From(effects.AccountTransfer.Amount),
AccountAddress.From(effects.AccountTransfer.Receiver),
OnChainData.From(effects.AccountTransfer.Memo)),
effects.AccountTransfer.Memo == null ? null : OnChainData.From(effects.AccountTransfer.Memo)),
Grpc.V2.AccountTransactionEffects.EffectOneofCase.BakerAdded =>
new BakerAdded(BakerKeysEvent.From(
effects.BakerAdded.KeysEvent),
Expand All @@ -45,7 +45,7 @@ internal static IAccountTransactionEffects From(Grpc.V2.AccountTransactionEffect
new EncryptedAmountTransferred(
EncryptedAmountRemovedEvent.From(effects.EncryptedAmountTransferred.Removed),
NewEncryptedAmountEvent.From(effects.EncryptedAmountTransferred.Added),
OnChainData.From(effects.EncryptedAmountTransferred.Memo)
effects.EncryptedAmountTransferred.Memo == null ? null : OnChainData.From(effects.EncryptedAmountTransferred.Memo)
),
Grpc.V2.AccountTransactionEffects.EffectOneofCase.TransferredToEncrypted =>
new TransferredToEncrypted(
Expand All @@ -57,7 +57,7 @@ internal static IAccountTransactionEffects From(Grpc.V2.AccountTransactionEffect
new TransferredWithSchedule(
AccountAddress.From(effects.TransferredWithSchedule.Receiver),
effects.TransferredWithSchedule.Amount.Select(a => (a.Timestamp.ToDateTimeOffset(), a.Amount.ToCcd())).ToList(),
OnChainData.From(effects.TransferredWithSchedule.Memo)
effects.TransferredWithSchedule.Memo == null ? null : OnChainData.From(effects.TransferredWithSchedule.Memo)
),
Grpc.V2.AccountTransactionEffects.EffectOneofCase.CredentialKeysUpdated =>
CredentialKeysUpdated.From(effects.CredentialKeysUpdated),
Expand Down Expand Up @@ -377,6 +377,8 @@ public IEnumerable<BakerId> GetBakerIds()
case BakerStakeIncreasedEvent bakerStakeIncreasedEvent:
yield return bakerStakeIncreasedEvent.BakerId;
break;
case BakerEventDelegationRemoved delegatorId:
break;
default:
throw new MissingTypeException<IBakerEvent>(bakerEvent);
}
Expand Down
8 changes: 7 additions & 1 deletion src/Types/BakerEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ internal static IBakerEvent From(BakerEvent bakerEvent) =>
BakerId.From(bakerEvent.BakerSetFinalizationRewardCommission.BakerId),
AmountFraction.From(bakerEvent.BakerSetFinalizationRewardCommission.FinalizationRewardCommission)
),
BakerEvent.EventOneofCase.DelegationRemoved => new BakerEventDelegationRemoved(DelegatorId.From(bakerEvent.DelegationRemoved.DelegatorId)),
BakerEvent.EventOneofCase.None =>
throw new MissingEnumException<BakerEvent.EventOneofCase>(bakerEvent.EventCase),
BakerEvent.EventOneofCase.DelegationRemoved => throw new NotImplementedException(),
_ => throw new MissingEnumException<BakerEvent.EventOneofCase>(bakerEvent.EventCase)
};
}
Expand Down Expand Up @@ -136,3 +136,9 @@ public sealed record BakerSetBakingRewardCommissionEvent(BakerId BakerId, Amount
/// <param name="BakerId">Baker's id</param>
/// <param name="FinalizationRewardCommission">The finalization reward commission</param>
public sealed record BakerSetFinalizationRewardCommissionEvent(BakerId BakerId, AmountFraction FinalizationRewardCommission) : IBakerEvent;

/// <summary>
/// An existing delegator was removed.
/// </summary>
/// <param name="DelegatorId">Delegator's id</param>
public sealed record BakerEventDelegationRemoved(DelegatorId DelegatorId) : IBakerEvent;
26 changes: 16 additions & 10 deletions src/Types/BakerPoolStatus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,23 @@ namespace Concordium.Sdk.Types;
/// by transactions (and rewards). This is in contrast to "epoch baker" which is
/// the state of the baker that is currently eligible for baking.
/// </summary>
/// <remarks>
/// From protocol version 7, pool removal has immediate effect, however, the
/// pool may still be present for the current (and possibly next) reward period.
/// </remarks>
/// <param name="BakerId">The 'BakerId' of the pool owner.</param>
/// <param name="BakerAddress">The account address of the pool owner.</param>
/// <param name="BakerEquityCapital">The equity capital provided by the pool owner.</param>
/// <param name="DelegatedCapital">The capital delegated to the pool by other accounts.</param>
/// <param name="BakerEquityCapital">The equity capital provided by the pool owner. Absent if the pool is removed.</param>
/// <param name="DelegatedCapital">The capital delegated to the pool by other accounts. Absent if the pool is removed.</param>
/// <param name="DelegatedCapitalCap">
/// The maximum amount that may be delegated to the pool, accounting for
/// leverage and stake limits.
/// Absent if the pool is removed.
/// </param>
/// <param name="PoolInfo">
/// The pool info associated with the pool: open status, metadata URL
/// and commission rates.
/// Absent if the pool is removed.
/// </param>
/// <param name="CurrentPaydayStatus">
/// Status of the pool in the current reward period. This will be null
Expand All @@ -28,10 +34,10 @@ namespace Concordium.Sdk.Types;
public sealed record BakerPoolStatus(
BakerId BakerId,
AccountAddress BakerAddress,
CcdAmount BakerEquityCapital,
CcdAmount DelegatedCapital,
CcdAmount DelegatedCapitalCap,
BakerPoolInfo PoolInfo,
CcdAmount? BakerEquityCapital,
CcdAmount? DelegatedCapital,
CcdAmount? DelegatedCapitalCap,
BakerPoolInfo? PoolInfo,
CurrentPaydayBakerPoolStatus? CurrentPaydayStatus,
CcdAmount AllPoolTotalCapital,
BakerPoolPendingChange? BakerStakePendingChange)
Expand All @@ -40,10 +46,10 @@ internal static BakerPoolStatus From(Grpc.V2.PoolInfoResponse poolInfoResponse)
new(
BakerId.From(poolInfoResponse.Baker),
AccountAddress.From(poolInfoResponse.Address),
CcdAmount.From(poolInfoResponse.EquityCapital),
CcdAmount.From(poolInfoResponse.DelegatedCapital),
CcdAmount.From(poolInfoResponse.DelegatedCapitalCap),
BakerPoolInfo.From(poolInfoResponse.PoolInfo)!,
poolInfoResponse.EquityCapital != null ? CcdAmount.From(poolInfoResponse.EquityCapital) : null,
poolInfoResponse.DelegatedCapital != null ? CcdAmount.From(poolInfoResponse.DelegatedCapital) : null,
poolInfoResponse.DelegatedCapitalCap != null ? CcdAmount.From(poolInfoResponse.DelegatedCapitalCap) : null,
poolInfoResponse.PoolInfo != null ? BakerPoolInfo.From(poolInfoResponse.PoolInfo) : null,
CurrentPaydayBakerPoolStatus.From(poolInfoResponse.CurrentPaydayInfo),
CcdAmount.From(poolInfoResponse.AllPoolTotalCapital),
BakerPoolPendingChange.From(poolInfoResponse.EquityPendingChange)
Expand Down
22 changes: 22 additions & 0 deletions src/Types/Cooldown.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Concordium.Sdk.Helpers;

namespace Concordium.Sdk.Types;

/// <summary>
/// The stake on the account that is in cooldown.
/// </summary>
/// <param name="EndTime">The time when the cooldown period ends.</param>
/// <param name="Amount">The amount that is in cooldown and set to be released at the end of the cooldown period.</param>
/// <param name="Status">The status of the cooldown.</param>
public sealed record Cooldown(
DateTimeOffset EndTime,
CcdAmount Amount,
CooldownStatus Status
)
{
internal static Cooldown From(Grpc.V2.Cooldown cooldown) => new(
cooldown.EndTime.ToDateTimeOffset(),
CcdAmount.From(cooldown.Amount),
cooldown.Status.Into()
);
}
48 changes: 48 additions & 0 deletions src/Types/CooldownStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Concordium.Sdk.Exceptions;

namespace Concordium.Sdk.Types;

/// <summary>
/// The status of a cooldown.
/// </summary>
/// <remarks>
/// When stake is removed from a baker or delegator
/// (from protocol version 7) it first enters the pre-pre-cooldown state.
/// The next time the stake snaphot is taken (at the epoch transition before
/// a payday) it enters the pre-cooldown state. At the subsequent payday, it
/// enters the cooldown state. At the payday after the end of the cooldown
/// period, the stake is finally released.
/// </remarks>
public enum CooldownStatus
{
/// <summary>
/// The amount is in cooldown and will expire at the specified time, becoming available
/// at the subsequent pay day.
/// </summary>
Cooldown,
/// <summary>
/// The amount will enter cooldown at the next pay day. The specified end time is
/// projected to be the end of the cooldown period, but the actual end time will be
/// determined at the payday, and may be different if the global cooldown period
/// changes.
/// </summary>
PreCooldown,
/// <summary>
/// The amount will enter pre-cooldown at the next snapshot epoch (i.e. the epoch
/// transition before a pay day transition). As with pre-cooldown, the specified
/// end time is projected, but the actual end time will be determined later.
/// </summary>
PrePreCooldown
}

internal static class CooldownStatusFactory
{
internal static CooldownStatus Into(this Grpc.V2.Cooldown.Types.CooldownStatus status) =>
status switch
{
Grpc.V2.Cooldown.Types.CooldownStatus.Cooldown => CooldownStatus.Cooldown,
Grpc.V2.Cooldown.Types.CooldownStatus.PreCooldown => CooldownStatus.PreCooldown,
Grpc.V2.Cooldown.Types.CooldownStatus.PrePreCooldown => CooldownStatus.PrePreCooldown,
_ => throw new MissingEnumException<Grpc.V2.Cooldown.Types.CooldownStatus>(status)
};
}
14 changes: 13 additions & 1 deletion src/Types/DelegationEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal static IDelegationEvent From(DelegationEvent delegationEvent) =>
DelegationAdded.From(delegationEvent.DelegationAdded),
DelegationEvent.EventOneofCase.DelegationRemoved =>
DelegationRemoved.From(delegationEvent.DelegationRemoved),
DelegationEvent.EventOneofCase.BakerRemoved => throw new NotImplementedException(),
DelegationEvent.EventOneofCase.BakerRemoved => DelegationEventBakerRemoved.From(delegationEvent.BakerRemoved),
DelegationEvent.EventOneofCase.None =>
throw new MissingEnumException<DelegationEvent.EventOneofCase>(delegationEvent.EventCase),
_ => throw new MissingEnumException<DelegationEvent.EventOneofCase>(delegationEvent.EventCase)
Expand Down Expand Up @@ -114,3 +114,15 @@ internal static DelegationRemoved From(Grpc.V2.DelegatorId id) =>
DelegatorId.From(id)
);
}

/// <summary>
/// A baker was removed.
/// </summary>
/// <param name="BakerId">Baker's id</param>
public sealed record DelegationEventBakerRemoved(BakerId BakerId) : IDelegationEvent
{
internal static DelegationEventBakerRemoved From(DelegationEvent.Types.BakerRemoved removed) =>
new(
BakerId.From(removed.BakerId)
);
}
22 changes: 22 additions & 0 deletions src/Types/Release.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Concordium.Sdk.Helpers;

namespace Concordium.Sdk.Types;

/// <summary>
/// An individual release of a locked balance.
/// </summary>
/// <param name="Timestamp">Effective time of the release.</param>
/// <param name="Amount">Amount to be released.</param>
/// <param name="Transactions">List of transaction hashes that contribute a balance to this release.</param>
public sealed record Release(
DateTimeOffset Timestamp,
CcdAmount Amount,
IList<TransactionHash> Transactions
)
{
internal static Release From(Grpc.V2.Release release) => new(
release.Timestamp.ToDateTimeOffset(),
CcdAmount.From(release.Amount),
release.Transactions.Select(TransactionHash.From).ToList()
);
}
Loading

0 comments on commit f1213d1

Please sign in to comment.