Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 199 additions & 8 deletions Sources/VIZ/API.swift

Large diffs are not rendered by default.

89 changes: 82 additions & 7 deletions Sources/VIZ/Block.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ fileprivate protocol _BlockHeader: Codable {
var previous: BlockId { get }
/// Time when block was generated.
var timestamp: Date { get }
/// Witness who produced it.
var witness: String { get }
/// Validator who produced it.
var validator: String { get }
/// Merkle root hash, ripemd160.
var transactionMerkleRoot: Data { get }
/// Block extensions.
Expand All @@ -56,19 +56,71 @@ fileprivate protocol _BlockHeader: Codable {
public struct BlockHeader: _BlockHeader {
public let previous: BlockId
public let timestamp: Date
public let witness: String
public let validator: String
public let transactionMerkleRoot: Data
public let extensions: [BlockExtension]

enum CodingKeys: String, CodingKey {
case previous, timestamp, validator, transactionMerkleRoot, extensions
case legacyWitness = "witness"
}

public init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
self.previous = try c.decode(BlockId.self, forKey: .previous)
self.timestamp = try c.decode(Date.self, forKey: .timestamp)
self.transactionMerkleRoot = try c.decode(Data.self, forKey: .transactionMerkleRoot)
self.extensions = try c.decode([BlockExtension].self, forKey: .extensions)
self.validator = try (try? c.decode(String.self, forKey: .validator))
?? c.decode(String.self, forKey: .legacyWitness)
}

public func encode(to encoder: Encoder) throws {
var c = encoder.container(keyedBy: CodingKeys.self)
try c.encode(previous, forKey: .previous)
try c.encode(timestamp, forKey: .timestamp)
try c.encode(validator, forKey: .validator)
try c.encode(transactionMerkleRoot, forKey: .transactionMerkleRoot)
try c.encode(extensions, forKey: .extensions)
}
}

/// A type representing a signed VIZ block header.
public struct SignedBlockHeader: _BlockHeader, Equatable, Sendable {
public let previous: BlockId
public let timestamp: Date
public let witness: String
public let validator: String
public let transactionMerkleRoot: Data
public let extensions: [BlockExtension]
public let witnessSignature: Signature
public let validatorSignature: Signature

enum CodingKeys: String, CodingKey {
case previous, timestamp, validator, transactionMerkleRoot, extensions, validatorSignature
case legacyWitness = "witness"
case legacyWitnessSignature = "witnessSignature"
}

public init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
self.previous = try c.decode(BlockId.self, forKey: .previous)
self.timestamp = try c.decode(Date.self, forKey: .timestamp)
self.transactionMerkleRoot = try c.decode(Data.self, forKey: .transactionMerkleRoot)
self.extensions = try c.decode([BlockExtension].self, forKey: .extensions)
self.validator = try (try? c.decode(String.self, forKey: .validator))
?? c.decode(String.self, forKey: .legacyWitness)
self.validatorSignature = try (try? c.decode(Signature.self, forKey: .validatorSignature))
?? c.decode(Signature.self, forKey: .legacyWitnessSignature)
}

public func encode(to encoder: Encoder) throws {
var c = encoder.container(keyedBy: CodingKeys.self)
try c.encode(previous, forKey: .previous)
try c.encode(timestamp, forKey: .timestamp)
try c.encode(validator, forKey: .validator)
try c.encode(transactionMerkleRoot, forKey: .transactionMerkleRoot)
try c.encode(extensions, forKey: .extensions)
try c.encode(validatorSignature, forKey: .validatorSignature)
}
}

/// A type representing a VIZ block.
Expand All @@ -91,10 +143,10 @@ public struct SignedBlock: _BlockHeader, Equatable, Sendable {
// Header proxy.
public var previous: BlockId { return self.header.previous }
public var timestamp: Date { return self.header.timestamp }
public var witness: String { return self.header.witness }
public var validator: String { return self.header.validator }
public var transactionMerkleRoot: Data { return self.header.transactionMerkleRoot }
public var extensions: [BlockExtension] { return self.header.extensions }
public var witnessSignature: Signature { return self.header.witnessSignature }
public var validatorSignature: Signature { return self.header.validatorSignature }

private enum Key: CodingKey {
case transactions
Expand Down Expand Up @@ -136,3 +188,26 @@ extension BlockExtension: Codable {
}
}
}

// MARK: - Deprecated aliases (witness → validator migration, 2026-05-19)

extension BlockHeader {
@available(*, deprecated, renamed: "validator")
public var witness: String { validator }
}

extension SignedBlockHeader {
@available(*, deprecated, renamed: "validator")
public var witness: String { validator }

@available(*, deprecated, renamed: "validatorSignature")
public var witnessSignature: Signature { validatorSignature }
}

extension SignedBlock {
@available(*, deprecated, renamed: "validator")
public var witness: String { validator }

@available(*, deprecated, renamed: "validatorSignature")
public var witnessSignature: Signature { validatorSignature }
}
11 changes: 9 additions & 2 deletions Sources/VIZ/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,15 @@ extension Request {
return "operation_history"
case "get_active_paid_subscriptions", "get_inactive_paid_subscriptions", "get_paid_subscription_options", "get_paid_subscription_status", "get_paid_subscriptions":
return "paid_subscription_api"
case "get_active_witnesses", "get_witness_by_account", "get_witness_count", "get_witness_schedule", "get_witnesses", "get_witnesses_by_counted_vote", "get_witnesses_by_vote", "lookup_witness_accounts":
return "witness_api"
case "get_active_witnesses", "get_active_validators",
"get_witness_by_account", "get_validator_by_account",
"get_witness_count", "get_validator_count",
"get_witness_schedule", "get_validator_schedule",
"get_witnesses", "get_validators",
"get_witnesses_by_counted_vote", "get_validators_by_counted_vote",
"get_witnesses_by_vote", "get_validators_by_vote",
"lookup_witness_accounts", "lookup_validator_accounts":
return "validator_api"
default:
return method
}
Expand Down
149 changes: 115 additions & 34 deletions Sources/VIZ/Operation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,9 @@ public struct Operation {
}
}

/// Registers or updates witnesses.
public struct WitnessUpdate: OperationType, Equatable {
/// Witness chain properties.
/// Registers or updates validators.
public struct ValidatorUpdate: OperationType, Equatable {
/// Validator chain properties.
public struct Properties: VIZCodable, Equatable, Sendable {
// public var accountCreationFee: Asset
// public var maximumBlockSize: UInt32
Expand Down Expand Up @@ -243,21 +243,52 @@ public struct Operation {
}
}

/// Votes for a witness.
public struct AccountWitnessVote: OperationType, Equatable {
/// Votes for a validator.
public struct AccountValidatorVote: OperationType, Equatable {
public var account: String
public var witness: String
public var validator: String
public var approve: Bool

public init(account: String, witness: String, approve: Bool) {
public init(account: String, validator: String, approve: Bool) {
self.account = account
self.witness = witness
self.validator = validator
self.approve = approve
}

@available(*, deprecated, message: "Use init(account:validator:approve:)")
public init(account: String, witness: String, approve: Bool) {
self.init(account: account, validator: witness, approve: approve)
}

@available(*, deprecated, renamed: "validator")
public var witness: String {
get { validator }
set { validator = newValue }
}

enum CodingKeys: String, CodingKey {
case account, validator, approve
case legacyWitness = "witness"
}

public init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
self.account = try c.decode(String.self, forKey: .account)
self.approve = try c.decode(Bool.self, forKey: .approve)
self.validator = try (try? c.decode(String.self, forKey: .validator))
?? c.decode(String.self, forKey: .legacyWitness)
}

public func encode(to encoder: Encoder) throws {
var c = encoder.container(keyedBy: CodingKeys.self)
try c.encode(account, forKey: .account)
try c.encode(validator, forKey: .validator)
try c.encode(approve, forKey: .approve)
}
}

/// Sets a witness voting proxy.
public struct AccountWitnessProxy: OperationType, Equatable {
/// Sets a validator voting proxy.
public struct AccountValidatorProxy: OperationType, Equatable {
public var account: String
public var proxy: String

Expand Down Expand Up @@ -908,7 +939,7 @@ public struct Operation {
public let deposited: Asset
}

public struct ShutdownWitness: OperationType, Equatable {
public struct ShutdownValidator: OperationType, Equatable {
public var isVirtual: Bool { return true }
public let owner: String
}
Expand Down Expand Up @@ -959,10 +990,41 @@ public struct Operation {
public let shares: Asset
}

public struct WitnessReward: OperationType, Equatable {
public struct ValidatorReward: OperationType, Equatable {
public var isVirtual: Bool { return true }
public let witness: String
public let validator: String
public let shares: Asset

public init(validator: String, shares: Asset) {
self.validator = validator
self.shares = shares
}

@available(*, deprecated, message: "Use init(validator:shares:)")
public init(witness: String, shares: Asset) {
self.init(validator: witness, shares: shares)
}

@available(*, deprecated, renamed: "validator")
public var witness: String { validator }

enum CodingKeys: String, CodingKey {
case validator, shares
case legacyWitness = "witness"
}

public init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
self.shares = try c.decode(Asset.self, forKey: .shares)
self.validator = try (try? c.decode(String.self, forKey: .validator))
?? c.decode(String.self, forKey: .legacyWitness)
}

public func encode(to encoder: Encoder) throws {
var c = encoder.container(keyedBy: CodingKeys.self)
try c.encode(validator, forKey: .validator)
try c.encode(shares, forKey: .shares)
}
}

/// Unknown operation, seen if the decoder encounters operation which has no type defined.
Expand All @@ -980,9 +1042,9 @@ fileprivate enum OperationId: UInt8, VIZEncodable, Decodable {
case transfer_to_vesting = 3
case withdraw_vesting = 4
case account_update = 5
case witness_update = 6
case account_witness_vote = 7
case account_witness_proxy = 8
case validator_update = 6
case account_validator_vote = 7
case account_validator_proxy = 8
case delete_content = 9
case custom = 10
case set_withdraw_vesting_route = 11
Expand All @@ -1004,7 +1066,7 @@ fileprivate enum OperationId: UInt8, VIZEncodable, Decodable {
case curation_reward = 27
case content_reward = 28
case fill_vesting_withdraw = 29
case shutdown_witness = 30
case shutdown_validator = 30
case hardfork = 31
case content_payout_update = 32
case content_benefactor_reward = 33
Expand All @@ -1016,7 +1078,7 @@ fileprivate enum OperationId: UInt8, VIZEncodable, Decodable {
case committee_approve_request = 39
case committee_payout_request = 40
case committee_pay_request = 41
case witness_reward = 42
case validator_reward = 42
case create_invite = 43
case claim_invite_balance = 44
case invite_registration = 45
Expand Down Expand Up @@ -1047,9 +1109,9 @@ fileprivate enum OperationId: UInt8, VIZEncodable, Decodable {
case "withdraw_vesting": self = .withdraw_vesting
case "account_create": self = .account_create
case "account_update": self = .account_update
case "witness_update": self = .witness_update
case "account_witness_vote": self = .account_witness_vote
case "account_witness_proxy": self = .account_witness_proxy
case "witness_update", "validator_update": self = .validator_update
case "account_witness_vote", "account_validator_vote": self = .account_validator_vote
case "account_witness_proxy", "account_validator_proxy": self = .account_validator_proxy
case "custom": self = .custom
case "set_withdraw_vesting_route": self = .set_withdraw_vesting_route
case "request_account_recovery": self = .request_account_recovery
Expand All @@ -1063,10 +1125,10 @@ fileprivate enum OperationId: UInt8, VIZEncodable, Decodable {
case "author_reward": self = .author_reward
case "curation_reward": self = .curation_reward
case "fill_vesting_withdraw": self = .fill_vesting_withdraw
case "shutdown_witness": self = .shutdown_witness
case "shutdown_witness", "shutdown_validator": self = .shutdown_validator
case "hardfork": self = .hardfork
case "return_vesting_delegation": self = .return_vesting_delegation
case "witness_reward": self = .witness_reward
case "witness_reward", "validator_reward": self = .validator_reward
case "create_invite": self = .create_invite
case "claim_invite_balance": self = .claim_invite_balance
case "invite_registration": self = .invite_registration
Expand Down Expand Up @@ -1121,9 +1183,9 @@ internal struct AnyOperation: VIZEncodable, Decodable, Sendable {
case .withdraw_vesting: op = try container.decode(Operation.WithdrawVesting.self)
case .account_create: op = try container.decode(Operation.AccountCreate.self)
case .account_update: op = try container.decode(Operation.AccountUpdate.self)
case .witness_update: op = try container.decode(Operation.WitnessUpdate.self)
case .account_witness_vote: op = try container.decode(Operation.AccountWitnessVote.self)
case .account_witness_proxy: op = try container.decode(Operation.AccountWitnessProxy.self)
case .validator_update: op = try container.decode(Operation.ValidatorUpdate.self)
case .account_validator_vote: op = try container.decode(Operation.AccountValidatorVote.self)
case .account_validator_proxy: op = try container.decode(Operation.AccountValidatorProxy.self)
// TODO: encode dispatches on Operation.Custom, decode produces Operation.CustomJson — reconcile
case .custom: op = try container.decode(Operation.CustomJson.self)
case .request_account_recovery: op = try container.decode(Operation.RequestAccountRecovery.self)
Expand All @@ -1137,10 +1199,10 @@ internal struct AnyOperation: VIZEncodable, Decodable, Sendable {
case .author_reward: op = try container.decode(Operation.AuthorReward.self)
case .curation_reward: op = try container.decode(Operation.CurationReward.self)
case .fill_vesting_withdraw: op = try container.decode(Operation.FillVestingWithdraw.self)
case .shutdown_witness: op = try container.decode(Operation.ShutdownWitness.self)
case .shutdown_validator: op = try container.decode(Operation.ShutdownValidator.self)
case .hardfork: op = try container.decode(Operation.Hardfork.self)
case .return_vesting_delegation: op = try container.decode(Operation.ReturnVestingDelegation.self)
case .witness_reward: op = try container.decode(Operation.WitnessReward.self)
case .validator_reward: op = try container.decode(Operation.ValidatorReward.self)
case .create_invite: op = Operation.Unknown()
case .claim_invite_balance: op = Operation.Unknown()
case .invite_registration: op = try container.decode(Operation.InviteRegistration.self)
Expand Down Expand Up @@ -1204,14 +1266,14 @@ internal struct AnyOperation: VIZEncodable, Decodable, Sendable {
case let op as Operation.AccountUpdate:
try container.encode(OperationId.account_update)
try container.encode(op)
case let op as Operation.WitnessUpdate:
try container.encode(OperationId.witness_update)
case let op as Operation.ValidatorUpdate:
try container.encode(OperationId.validator_update)
try container.encode(op)
case let op as Operation.AccountWitnessVote:
try container.encode(OperationId.account_witness_vote)
case let op as Operation.AccountValidatorVote:
try container.encode(OperationId.account_validator_vote)
try container.encode(op)
case let op as Operation.AccountWitnessProxy:
try container.encode(OperationId.account_witness_proxy)
case let op as Operation.AccountValidatorProxy:
try container.encode(OperationId.account_validator_proxy)
try container.encode(op)
// TODO: paired with the decode-side TODO above — pick one canonical type for the `custom` op id
case let op as Operation.Custom:
Expand Down Expand Up @@ -1295,3 +1357,22 @@ extension Operation.CommentOptions.Extension {
}
}
}

// MARK: - Deprecated aliases (witness → validator migration, 2026-05-19)

extension Operation {
@available(*, deprecated, renamed: "ValidatorUpdate")
public typealias WitnessUpdate = ValidatorUpdate

@available(*, deprecated, renamed: "AccountValidatorVote")
public typealias AccountWitnessVote = AccountValidatorVote

@available(*, deprecated, renamed: "AccountValidatorProxy")
public typealias AccountWitnessProxy = AccountValidatorProxy

@available(*, deprecated, renamed: "ShutdownValidator")
public typealias ShutdownWitness = ShutdownValidator

@available(*, deprecated, renamed: "ValidatorReward")
public typealias WitnessReward = ValidatorReward
}
Loading
Loading