Skip to content
Open
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
9 changes: 9 additions & 0 deletions contracts/FlowTransactionScheduler.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import "FlowToken"
import "FlowFees"
import "FlowStorageFees"
import "ViewResolver"
import "FlowEpoch"

/// FlowTransactionScheduler enables smart contracts to schedule autonomous execution in the future.
///
Expand Down Expand Up @@ -1284,6 +1285,14 @@ access(all) contract FlowTransactionScheduler {
return
}

// Skip processing if the epoch is in one of the phase transitions
// or expensive operations
// We don't skip if it is running in a test environment,
// so we check automaticRewardsEnabled() because it is only true on testnet and mainnet.
if FlowEpoch.isPhaseTransition() && FlowEpoch.automaticRewardsEnabled() {
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 want to skip this check if it is on the emulator because epochs don't run on the emulator. Is checking automatic rewards enabled an okay way to do that? I assume that it is only enabled on testnet and mainnet

return
}

self.removeExecutedTransactions(currentTimestamp)

let pendingTransactions = self.pendingQueue()
Expand Down
35 changes: 35 additions & 0 deletions contracts/epochs/FlowEpoch.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,41 @@ access(all) contract FlowEpoch {
?? 0.0
}

/// Checks if the current epoch is in a phase transition
/// so that other system contracts can skip computation-heavy operations
/// during the phase transition.
access(all) fun isPhaseTransition(): Bool {
switch self.currentEpochPhase {
case EpochPhase.STAKINGAUCTION:
if let previousEpochMetadata = self.getEpochMetadata(FlowEpoch.currentEpochCounter.saturatingSubtract(1)) {
// Paying rewards for the previous epoch
if self.currentEpochCounter > 0 && !previousEpochMetadata.rewardsPaid {
return true
}
}
let currentBlock = getCurrentBlock()
let currentEpochMetadata = self.getEpochMetadata(self.currentEpochCounter)!
// Staking auction is ending
if currentBlock.view >= currentEpochMetadata.stakingEndView {
return true
}
case EpochPhase.EPOCHSETUP:
// QC and DKG are completed and will be cleaned up
if FlowClusterQC.votingCompleted() && (FlowDKG.dkgCompleted() != nil) {
return true
}
case EpochPhase.EPOCHCOMMIT:
let currentBlock = getCurrentBlock()
let currentEpochMetadata = FlowEpoch.getEpochMetadata(FlowEpoch.currentEpochCounter)!
// Epoch is ending
if currentBlock.view >= currentEpochMetadata.endView {
return true
}
}

return false
}

init (currentEpochCounter: UInt64,
numViewsInEpoch: UInt64,
numViewsInStakingAuction: UInt64,
Expand Down
2 changes: 1 addition & 1 deletion flow.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"aliases": {
"emulator": "f8d6e0586b0a20c7",
"mainnet": "8624b52f9ddcd04a",
"testing": "0000000000000007",
"testing": "0000000000000001",
"testnet": "9eca2b38b18b5dfe"
}
},
Expand Down
12 changes: 6 additions & 6 deletions lib/go/contracts/internal/assets/assets.go

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions lib/go/templates/epoch_templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const (
getCurrentViewFilename = "epoch/scripts/get_current_view.cdc"
getFlowTotalSupplyFilename = "flowToken/scripts/get_supply.cdc"
getFlowBonusTokensFilename = "epoch/scripts/get_bonus_tokens.cdc"
isPhaseTransitionFilename = "epoch/scripts/is_phase_transition.cdc"

// test scripts
getRandomizeFilename = "epoch/scripts/get_randomize.cdc"
Expand Down Expand Up @@ -238,3 +239,9 @@ func GenerateGetBonusTokensScript(env Environment) []byte {

return []byte(ReplaceAddresses(code, env))
}

func GenerateIsPhaseTransitionScript(env Environment) []byte {
code := assets.MustAssetString(isPhaseTransitionFilename)

return []byte(ReplaceAddresses(code, env))
}
23 changes: 23 additions & 0 deletions lib/go/templates/internal/assets/assets.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 20 additions & 1 deletion lib/go/test/flow_epoch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ func TestEpochDeployment(t *testing.T) {
clusterQCs: nil,
dkgKeys: nil})

// Should not be in a phase transition since the epoch is just starting
result := executeScriptAndCheck(t, b, templates.GenerateIsPhaseTransitionScript(env), nil)
assertEqual(t, cadence.NewBool(false), result)
}

func TestEpochClusters(t *testing.T) {
Expand Down Expand Up @@ -586,6 +589,10 @@ func TestEpochAdvance(t *testing.T) {
// Advance to epoch Setup and make sure that the epoch cannot be ended
advanceView(t, b, env, idTableAddress, IDTableSigner, 1, "EPOCHSETUP", false)

// Should not be in a phase transition since we already advanced to epoch setup
result := executeScriptAndCheck(t, b, templates.GenerateIsPhaseTransitionScript(env), nil)
assertEqual(t, cadence.NewBool(false), result)

verifyConfigMetadata(t, b, env,
ConfigMetadata{
currentEpochCounter: startEpochCounter,
Expand Down Expand Up @@ -638,7 +645,7 @@ func TestEpochAdvance(t *testing.T) {
})

// QC Contract Checks
result := executeScriptAndCheck(t, b, templates.GenerateGetClusterWeightScript(env), [][]byte{jsoncdc.MustEncode(cadence.UInt16(uint16(0)))})
result = executeScriptAndCheck(t, b, templates.GenerateGetClusterWeightScript(env), [][]byte{jsoncdc.MustEncode(cadence.UInt16(uint16(0)))})
assert.Equal(t, cadence.NewUInt64(100), result)

result = executeScriptAndCheck(t, b, templates.GenerateGetNodeWeightScript(env), [][]byte{jsoncdc.MustEncode(cadence.UInt16(uint16(1))), jsoncdc.MustEncode(cadence.String(ids[0]))})
Expand Down Expand Up @@ -864,6 +871,10 @@ func TestEpochQCDKG(t *testing.T) {
false,
)

// Should be in a phase transition since we haven't advanced to epoch setup and view is greater than the staking end view (50)
result := executeScriptAndCheck(t, b, templates.GenerateIsPhaseTransitionScript(env), nil)
assertEqual(t, cadence.NewBool(true), result)

// Advance to epoch Setup and make sure that the epoch cannot be ended
advanceView(t, b, env, idTableAddress, IDTableSigner, 1, "EPOCHSETUP", false)

Expand Down Expand Up @@ -1001,6 +1012,10 @@ func TestEpochQCDKG(t *testing.T) {
// Advance to epoch commit
advanceView(t, b, env, idTableAddress, IDTableSigner, 1, "EPOCHCOMMIT", false)

// Should be in a phase transition since we advanced to epoch commit and view is greater than the end view (70)
result = executeScriptAndCheck(t, b, templates.GenerateIsPhaseTransitionScript(env), nil)
assertEqual(t, cadence.NewBool(true), result)

verifyConfigMetadata(t, b, env,
ConfigMetadata{
currentEpochCounter: startEpochCounter,
Expand Down Expand Up @@ -1087,6 +1102,10 @@ func TestEpochQCDKG(t *testing.T) {
rewards: "6571204.6775",
})

// Should be in a phase transition since we just ended the epoch but haven't paid rewards
result = executeScriptAndCheck(t, b, templates.GenerateIsPhaseTransitionScript(env), nil)
assertEqual(t, cadence.NewBool(true), result)

tx = createTxWithTemplateAndAuthorizer(b, templates.GenerateEpochPayRewardsScript(env), idTableAddress)

signAndSubmit(
Expand Down
21 changes: 21 additions & 0 deletions tests/scheduled_transaction_test_helpers.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,27 @@ access(all) fun upgradeSchedulerUtilsContract() {
Test.expect(upgradeResult, Test.beSucceeded())
}

access(all) fun upgradeEpochContract() {
var epochCode = Test.readFile("../contracts/epochs/FlowEpoch.cdc")
epochCode = epochCode.replaceAll(of: "\"FungibleToken\"", with: "FungibleToken from 0x0000000000000002")
epochCode = epochCode.replaceAll(of: "\"FlowToken\"", with: "FlowToken from 0x0000000000000003")
epochCode = epochCode.replaceAll(of: "\"FlowFees\"", with: "FlowFees from 0x0000000000000004")
epochCode = epochCode.replaceAll(of: "\"FlowIDTableStaking\"", with: "FlowIDTableStaking from 0x0000000000000001")
epochCode = epochCode.replaceAll(of: "\"FlowClusterQC\"", with: "FlowClusterQC from 0x0000000000000001")
epochCode = epochCode.replaceAll(of: "\"FlowDKG\"", with: "FlowDKG from 0x0000000000000001")

var upgradeTx = Test.Transaction(
code: Test.readFile("./transactions/upgrade_contract.cdc"),
authorizers: [serviceAccount.address],
signers: [serviceAccount],
arguments: ["FlowEpoch", epochCode],
)
var upgradeResult = Test.executeTransaction(
upgradeTx,
)
Test.expect(upgradeResult, Test.beSucceeded())
}

access(all) fun getTimestamp(): UFix64 {
var timestamp = _executeScript(
"./scripts/get_timestamp.cdc",
Expand Down
3 changes: 3 additions & 0 deletions tests/transactionScheduler_events_test.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ access(all) var accountBalanceBefore: UFix64 = 0.0
access(all)
fun setup() {

// upgrade the FlowEpoch contract to the latest version
upgradeEpochContract()

var err = Test.deployContract(
name: "FlowTransactionScheduler",
path: "../contracts/FlowTransactionScheduler.cdc",
Expand Down
3 changes: 3 additions & 0 deletions tests/transactionScheduler_manager_test.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ access(all) var timeInFuture: UFix64 = 0.0
access(all)
fun setup() {

// upgrade the FlowEpoch contract to the latest version
upgradeEpochContract()

var err = Test.deployContract(
name: "FlowTransactionScheduler",
path: "../contracts/FlowTransactionScheduler.cdc",
Expand Down
3 changes: 3 additions & 0 deletions tests/transactionScheduler_misc_test.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ access(all) var accountBalanceBefore: UFix64 = 0.0
access(all)
fun setup() {

// upgrade the FlowEpoch contract to the latest version
upgradeEpochContract()

var err = Test.deployContract(
name: "FlowTransactionScheduler",
path: "../contracts/FlowTransactionScheduler.cdc",
Expand Down
3 changes: 3 additions & 0 deletions tests/transactionScheduler_schedule_test.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import "scheduled_transaction_test_helpers.cdc"
access(all)
fun setup() {

// upgrade the FlowEpoch contract to the latest version
upgradeEpochContract()

var err = Test.deployContract(
name: "FlowTransactionScheduler",
path: "../contracts/FlowTransactionScheduler.cdc",
Expand Down
3 changes: 3 additions & 0 deletions tests/transactionScheduler_test.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import "scheduled_transaction_test_helpers.cdc"
access(all)
fun setup() {

// upgrade the FlowEpoch contract to the latest version
upgradeEpochContract()

var err = Test.deployContract(
name: "FlowTransactionScheduler",
path: "../contracts/FlowTransactionScheduler.cdc",
Expand Down
5 changes: 5 additions & 0 deletions transactions/epoch/scripts/is_phase_transition.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import "FlowEpoch"

access(all) fun main(): Bool {
return FlowEpoch.isPhaseTransition()
}
Loading