Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Cadence 0.x] Add ProtocolStateVersionUpgrade service event #411

Closed
wants to merge 6 commits into from
Closed
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
72 changes: 69 additions & 3 deletions contracts/NodeVersionBeacon.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
/// The contract itself can be used to query the current version and the next upcoming version.
pub contract NodeVersionBeacon {

/// =========================
/// Execution State Versioning
/// =========================

/// Struct representing software version as Semantic Version
/// along with helper functions
/// For reference, see https://semver.org/
Expand Down Expand Up @@ -134,9 +138,10 @@ pub contract NodeVersionBeacon {
)
}

/// Event emitted when the version table is updated.
/// It contains the current version and all the upcoming versions
/// sorted by block height.
/// A service event emitted when the version table is updated.
/// The version is the software version which must be used for executing a height range of blocks.
/// The version pertains to Execution and Verification Nodes.
/// The table contains the current version and all the upcoming versions sorted by block height.
/// The sequence increases by one each time an event is emitted.
/// It can be used to verify no events were missed.
pub event VersionBeacon(
Expand Down Expand Up @@ -280,6 +285,13 @@ pub contract NodeVersionBeacon {

emit NodeVersionBoundaryFreezePeriodChanged(freezePeriod: newFreezePeriod)
}

/// Adds a pending protocol state version upgrade, which will be emitted
/// as a service event during the next heartbeat.
pub fun setPendingProtocolStateVersionUpgrade(newProtocolVersion: UInt64, activeView: UInt64) {
let pendingUpgrade = NodeVersionBeacon.ViewActivatedVersion(version: newProtocolVersion, activeView: activeView)
NodeVersionBeacon.storePendingProtocolStateVersionUpgrade(upgrade: pendingUpgrade)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to confirm my understanding:

The reason we don't just emit the event here is that the service events are only observed from the service transaction.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly. The idea was to be as restrictive as possible for what qualifies as a service event, because they are so security-sensitive, at the cost of convenience in this kind of case.

}
}

/// Heartbeat resource that emits the version beacon event and keeps track of upcoming versions.
Expand All @@ -291,6 +303,8 @@ pub contract NodeVersionBeacon {
pub fun heartbeat() {
self.checkFirstUpcomingBoundary()

self.emitProtocolStateVersionUpgradeEventIfNeeded()

if (!NodeVersionBeacon.emitEventOnNextHeartbeat) {
return
}
Expand Down Expand Up @@ -336,6 +350,17 @@ pub contract NodeVersionBeacon {
// If we passed a boundary re-emit the VersionBeacon event
NodeVersionBeacon.emitEventOnNextHeartbeat = true
}

/// Emit a ProtocolStateVersionUpgrade event, if a pending upgrade is in storage.
access(self) fun emitProtocolStateVersionUpgradeEventIfNeeded() {
let pendingUpgrade = NodeVersionBeacon.popPendingProtocolStateVersionUpgrade()
if pendingUpgrade == nil {
return
}
emit NodeVersionBeacon.ProtocolStateVersionUpgrade(
newProtocolVersion: pendingUpgrade!.version,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look like it from looking at the other code, but I just want to confirm that there is no way that this optional can be nil here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure it cannot be nil, because of the if statement above:

if pendingUpgrade == nil {
    return
}

activeView: pendingUpgrade!.activeView)
}
}

/// getCurrentVersionBoundaries returns the current version boundaries.
Expand Down Expand Up @@ -495,6 +520,47 @@ pub contract NodeVersionBeacon {
return self.versionBoundaryBlockList[0]
}

/// =========================
/// Protocol State Versioning
/// =========================

/// ViewActivatedVersion stores a version and a view. It indicates a version upgrade
/// so that `version` will become active at view `activeView`.
pub struct ViewActivatedVersion {
pub let version: UInt64
pub let activeView: UInt64

init(version: UInt64, activeView: UInt64) {
self.version = version
self.activeView = activeView
}
}

/// A service event which is emitted to indicate that the Protocol State version is being upgraded.
/// This acts as a signal to begin using the upgraded Protocol State version
/// after this service event is sealed, and after view `activeView` is entered.
/// Nodes running a software version which does not support `newProtocolVersion`
/// will stop processing new blocks when they reach view `activeAtView`.
pub event ProtocolStateVersionUpgrade(newProtocolVersion: UInt64, activeView: UInt64)

/// Removes the pending ProtocolStateVersionUpgrade from storage and returns it.
/// If no ProtocolStateVersionUpgrade was in storage, returns nil.
access(contract) fun popPendingProtocolStateVersionUpgrade(): ViewActivatedVersion? {
return self.account.load<ViewActivatedVersion>(from: /storage/PendingProtocolStateVersionUpgrade)
}

/// Reads the pending ProtocolStateVersionUpgrade from storage, without removing it, and returns it.
/// If no ProtocolStateVersionUpgrade was in storage, returns nil.
pub fun peekPendingProtocolStateVersionUpgrade(): ViewActivatedVersion? {
return self.account.copy<ViewActivatedVersion>(from: /storage/PendingProtocolStateVersionUpgrade)
}

/// Stores the pending ProtocolStateVersionUpgrade to storage.
/// No ProtocolStateVersionUpgrade may already be stored, otherwise the transaction will revert.
access(contract) fun storePendingProtocolStateVersionUpgrade(upgrade: ViewActivatedVersion) {
self.account.save<ViewActivatedVersion>(upgrade, to: /storage/PendingProtocolStateVersionUpgrade)
}

init(versionUpdateFreezePeriod: UInt64) {
self.AdminStoragePath = /storage/NodeVersionBeaconAdmin
self.HeartbeatStoragePath = /storage/NodeVersionBeaconHeartbeat
Expand Down
Loading
Loading