Skip to content

Commit

Permalink
Merge pull request #70 from Concordium/pv7-conformance
Browse files Browse the repository at this point in the history
Pv7 conformance
  • Loading branch information
soerenbf authored Oct 7, 2024
2 parents 547a9b5 + 3967e84 commit a42fae6
Show file tree
Hide file tree
Showing 9 changed files with 1,024 additions and 2,315 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Return type of `NodeClient.send` to `SubmittedTransaction` to provide ergonomics for working with transaction submitted to chain.
- Representation of raw data is changed from hex strings to `Data` across all functions and data structures
- Updated a number of types to conform to the changes introduced to the GRPC API in concordium node version 7

## [0.1.1] - 2024-04-29

Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ Features:
- Support for decrypting and using accounts created using the legacy wallet.
- Utilities for working with amounts in CCD and fungible CIS-2 tokens of arbitrary size.

### Dependencies
- swift-protobuf + grpc-swift: `brew install swift-protobuf grpc-swift`

### Cryptographic functions

Concordium specific cryptographic functions that are implemented in Rust are exposed as a separate Swift library
Expand Down
102 changes: 99 additions & 3 deletions Sources/Concordium/Domain/Account.swift
Original file line number Diff line number Diff line change
Expand Up @@ -847,12 +847,72 @@ public enum AccountStakingInfo: FromGRPC {
}
}

/// Describes a cooldown associated with removal of stake from a baker/delegator account
public struct Cooldown {
/// The time at which the cooldown will end
public let timestamp: Date
/// The amount that is in cooldown and set to be released at the end of the cooldown period
public let amount: CCD
/// The status of the cooldown
public let status: Status

/**
* The status of a cooldown. 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.
*/
public enum Status {
/**
* The amount is in cooldown and will expire at the specified time, becoming available
* at the subsequent pay day.
*/
case cooldown
/**
* 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.
*/
case preCooldown
/**
* 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.
*/
case prePreCooldown
}
}

extension Cooldown.Status: FromGRPC {
static func fromGRPC(_ g: Concordium_V2_Cooldown.CooldownStatus) throws -> Self {
switch g {
case .cooldown:
return .cooldown
case .preCooldown:
return .preCooldown
case .prePreCooldown:
return .prePreCooldown
case .UNRECOGNIZED:
throw GRPCError.valueOutOfBounds
}
}
}

extension Cooldown: FromGRPC {
static func fromGRPC(_ g: Concordium_V2_Cooldown) throws -> Self {
try Self(timestamp: .fromGRPC(g.endTime), amount: .fromGRPC(g.amount), status: .fromGRPC(g.status))
}
}

/// Information about the account at a particular point in time on chain.
public struct AccountInfo: FromGRPC {
/// Next sequence number to be used for transactions signed from this account.
public var sequenceNumber: SequenceNumber
/// Current (unencrypted) balance of the account.
public var amount: MicroCCDAmount
public var amount: CCD
/// Release schedule for any locked up amount. This could be an empty release schedule.
public var releaseSchedule: ReleaseSchedule
/// Map of all currently active credentials on the account.
Expand All @@ -879,20 +939,56 @@ public struct AccountInfo: FromGRPC {
/// Canonical address of the account.
/// This is derived from the first credential that created the account.
public var address: AccountAddress
/**
* 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 will be empty in
* earlier protocol versions.
*/
public var cooldowns: [Cooldown]
/**
* 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).
* This was introduced with node version 7.0
*/
public var availableBalance: CCD

static func fromGRPC(_ grpc: Concordium_V2_AccountInfo) throws -> Self {
let credentials = try grpc.creds.reduce(into: [:]) { r, v in r[UInt8(v.key)] = try Versioned(version: 0, value: AccountCredentialDeploymentValues.fromGRPC(v.value)) }
let releaseSchedule = try ReleaseSchedule.fromGRPC(grpc.schedule)
var availableBalance: CCD
if grpc.hasAvailableBalance {
/// present for node version >=7
availableBalance = try .fromGRPC(grpc.availableBalance)
} else {
/// Node version <7
var staked: MicroCCDAmount = 0
if let stakingInfo = grpc.stake.stakingInfo {
switch stakingInfo {
case let .baker(data):
staked = data.stakedAmount.value
case let .delegator(data):
staked = data.stakedAmount.value
}
}
availableBalance = CCD(microCCD: grpc.amount.value - max(releaseSchedule.total, staked))
}

return try self.init(
sequenceNumber: grpc.sequenceNumber.value,
amount: grpc.amount.value,
amount: .fromGRPC(grpc.amount),
releaseSchedule: .fromGRPC(grpc.schedule),
credentials: credentials,
threshold: SignatureThreshold(exactly: grpc.threshold.value) ?! GRPCError.valueOutOfBounds,
encryptedAmount: .fromGRPC(grpc.encryptedBalance),
encryptionKey: grpc.encryptionKey.value,
index: grpc.index.value,
stake: grpc.hasStake ? .fromGRPC(grpc.stake) : nil,
address: AccountAddress(grpc.address.value)
address: AccountAddress(grpc.address.value),
cooldowns: grpc.cooldowns.map { try Cooldown.fromGRPC($0) },
availableBalance: availableBalance
)
}
}
Expand Down
6 changes: 6 additions & 0 deletions Sources/Concordium/Domain/Events.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ public enum DelegationEvent {
case delegationStakeDecreased(delegatorId: AccountIndex, newStake: CCD)
case delegationSetRestakeEarnings(delegatorId: AccountIndex, restakeEarnings: Bool)
case delegationSetDelegationTarget(delegatorId: AccountIndex, delegationTarget: DelegationTarget)
case bakerRemoved(bakerId: AccountIndex)
}

extension DelegationEvent: FromGRPC {
Expand All @@ -134,6 +135,8 @@ extension DelegationEvent: FromGRPC {
return try .delegationStakeDecreased(delegatorId: data.delegatorID.id.value, newStake: .fromGRPC(data.newStake))
case let .delegationStakeIncreased(data):
return try .delegationStakeIncreased(delegatorId: data.delegatorID.id.value, newStake: .fromGRPC(data.newStake))
case let .bakerRemoved(data):
return .bakerRemoved(bakerId: data.bakerID.value)
}
}
}
Expand Down Expand Up @@ -173,6 +176,7 @@ public enum BakerEvent {
case bakerSetTransactionFeeCommission(bakerId: AccountIndex, commission: AmountFraction)
case bakerSetBakingRewardCommission(bakerId: AccountIndex, commission: AmountFraction)
case bakerSetFinalizationRewardCommission(bakerId: AccountIndex, commission: AmountFraction)
case delegatorRemoved(delegatorId: AccountIndex)
}

extension BakerEvent: FromGRPC {
Expand Down Expand Up @@ -203,6 +207,8 @@ extension BakerEvent: FromGRPC {
return try .bakerSetOpenStatus(bakerId: data.bakerID.value, openStatus: .fromGRPC(data.openStatus))
case let .bakerSetMetadataURL(data):
return .bakerSetMetadataUrl(bakerId: data.bakerID.value, metadataUrl: data.url)
case let .delegationRemoved(data):
return .delegatorRemoved(delegatorId: data.delegatorID.id.value)
}
}
}
Expand Down
20 changes: 12 additions & 8 deletions Sources/Concordium/Domain/Queries.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1343,15 +1343,15 @@ public struct BakerPoolStatus {
/// The account address of the pool owner.
public let bakerAddress: AccountAddress
/// The equity capital provided by the pool owner.
public let bakerEquityCapital: CCD
public let bakerEquityCapital: CCD?
/// The capital delegated to the pool by other accounts.
public let delegatedCapital: CCD
public let delegatedCapital: CCD?
/// The maximum amount that may be delegated to the pool, accounting for
/// leverage and stake limits.
public let delegatedCapitalCap: CCD
public let delegatedCapitalCap: CCD?
/// The pool info associated with the pool: open status, metadata URL
/// and commission rates.
public let poolInfo: BakerPoolInfo
public let poolInfo: BakerPoolInfo?
/// Any pending change to the baker's stake.
public let bakerStakePendingChange: PoolPendingChange
/// Status of the pool in the current reward period. This will be [`None`]
Expand All @@ -1366,14 +1366,18 @@ extension BakerPoolStatus: FromGRPC {
typealias GRPC = Concordium_V2_PoolInfoResponse

static func fromGRPC(_ g: GRPC) throws -> BakerPoolStatus {
let bakerEquityCapital = g.hasEquityCapital ? try CCD.fromGRPC(g.equityCapital) : nil
let delegatedCapital = g.hasDelegatedCapital ? try CCD.fromGRPC(g.delegatedCapital) : nil
let delegatedCapitalCap = g.hasDelegatedCapitalCap ? try CCD.fromGRPC(g.delegatedCapitalCap) : nil
let poolInfo = g.hasPoolInfo ? try BakerPoolInfo.fromGRPC(g.poolInfo) : nil
let currentPaydayStatus = g.hasCurrentPaydayInfo ? try CurrentPaydayBakerPoolStatus.fromGRPC(g.currentPaydayInfo) : nil
return try Self(
bakerId: g.baker.value,
bakerAddress: .fromGRPC(g.address),
bakerEquityCapital: .fromGRPC(g.equityCapital),
delegatedCapital: .fromGRPC(g.delegatedCapital),
delegatedCapitalCap: .fromGRPC(g.delegatedCapitalCap),
poolInfo: .fromGRPC(g.poolInfo),
bakerEquityCapital: bakerEquityCapital,
delegatedCapital: delegatedCapital,
delegatedCapitalCap: delegatedCapitalCap,
poolInfo: poolInfo,
bakerStakePendingChange: .fromGRPC(g.equityPendingChange),
currentPaydayStatus: currentPaydayStatus,
allPoolTotalCapital: .fromGRPC(g.allPoolTotalCapital)
Expand Down
19 changes: 7 additions & 12 deletions Sources/Concordium/Generated/GRPC/v2/concordium/health.pb.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: v2/concordium/health.proto
//
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/

import Foundation
import SwiftProtobuf

// If the compiler emits an error on this type, it is because this file
Expand All @@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP

/// Parameters to the node health query. The default message gives a good
/// default.
struct Concordium_Health_NodeHealthRequest {
struct Concordium_Health_NodeHealthRequest: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
Expand All @@ -34,7 +34,7 @@ struct Concordium_Health_NodeHealthRequest {

/// Response to the health check. A return code of "OK" is used for success, and
/// errors are handled via RPC status codes
struct Concordium_Health_NodeHealthResponse {
struct Concordium_Health_NodeHealthResponse: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
Expand All @@ -44,11 +44,6 @@ struct Concordium_Health_NodeHealthResponse {
init() {}
}

#if swift(>=5.5) && canImport(_Concurrency)
extension Concordium_Health_NodeHealthRequest: @unchecked Sendable {}
extension Concordium_Health_NodeHealthResponse: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)

// MARK: - Code below here is support for the SwiftProtobuf runtime.

fileprivate let _protobuf_package = "concordium.health"
Expand All @@ -58,8 +53,8 @@ extension Concordium_Health_NodeHealthRequest: SwiftProtobuf.Message, SwiftProto
static let _protobuf_nameMap = SwiftProtobuf._NameMap()

mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let _ = try decoder.nextFieldNumber() {
}
// Load everything into unknown fields
while try decoder.nextFieldNumber() != nil {}
}

func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
Expand All @@ -77,8 +72,8 @@ extension Concordium_Health_NodeHealthResponse: SwiftProtobuf.Message, SwiftProt
static let _protobuf_nameMap = SwiftProtobuf._NameMap()

mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let _ = try decoder.nextFieldNumber() {
}
// Load everything into unknown fields
while try decoder.nextFieldNumber() != nil {}
}

func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
Expand Down
14 changes: 2 additions & 12 deletions Sources/Concordium/Generated/GRPC/v2/concordium/service.pb.swift
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
// DO NOT EDIT.
// swift-format-ignore-file
// swiftlint:disable all
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: v2/concordium/service.proto
//
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/

import Foundation
import SwiftProtobuf

// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
// This file contained no messages, enums, or extensions.
Loading

0 comments on commit a42fae6

Please sign in to comment.