Skip to content

Commit

Permalink
Merge pull request #39 from onflow/update-stable-cadence
Browse files Browse the repository at this point in the history
Update stable cadence branch
  • Loading branch information
sisyphusSmiling authored Jul 29, 2024
2 parents c99c1a9 + 7838613 commit 3e614c1
Show file tree
Hide file tree
Showing 14 changed files with 495 additions and 44 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
restore-keys: |
${{ runner.os }}-go-
- name: Install Flow CLI
run: sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/feature/stable-cadence/install.sh)"
run: sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh)"
- name: Flow CLI Version
run: flow-c1 version
- name: Update PATH
Expand Down
158 changes: 139 additions & 19 deletions contracts/DependencyAudit.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ access(all) contract DependencyAudit {

access(all) event PanicOnUnstagedDependenciesChanged(shouldPanic: Bool)

access(all) event BlockBoundariesChanged(start: UInt64?, end: UInt64?)

// checkDependencies is called from the FlowServiceAccount contract
access(account) fun checkDependencies(_ dependenciesAddresses: [Address], _ dependenciesNames: [String], _ authorizers: [Address]) {
var unstagedDependencies: [Dependency] = []
Expand All @@ -41,27 +43,128 @@ access(all) contract DependencyAudit {
}

if unstagedDependencies.length > 0 {
if DependencyAudit.panicOnUnstaged {
// If `panicOnUnstaged` is set to true, the transaction will panic if there are any unstaged dependencies
// the panic message will include the unstaged dependencies
var unstagedDependenciesString = ""
var numUnstagedDependencies = unstagedDependencies.length
var j = 0
while j < numUnstagedDependencies {
if j > 0 {
unstagedDependenciesString = unstagedDependenciesString.concat(", ")
}
unstagedDependenciesString = unstagedDependenciesString.concat(unstagedDependencies[j].toString())

j = j + 1
}

// the transactions will fail with a message that looks like this: `error: panic: Found unstaged dependencies: A.2ceae959ed1a7e7a.MigrationContractStaging, A.2ceae959ed1a7e7a.DependencyAudit`
panic("This transaction is using dependencies not staged for Crescendo upgrade coming soon! Learn more: https://bit.ly/FLOWCRESCENDO. Dependencies not staged: ".concat(unstagedDependenciesString))
} else {
emit UnstagedDependencies(dependencies: unstagedDependencies)
self.maybePanicOnUnstagedDependencies(unstagedDependencies)

emit UnstagedDependencies(dependencies: unstagedDependencies)
}
}

access(self) fun maybePanicOnUnstagedDependencies(_ unstagedDependencies: [Dependency]) {
// If `panicOnUnstaged` is set to false, the function will return without panicking
// Then check if we should panic randomly
if !DependencyAudit.panicOnUnstaged || !self.shouldPanicRandomly() {
return
}

var unstagedDependenciesString = ""
var numUnstagedDependencies = unstagedDependencies.length
var j = 0
while j < numUnstagedDependencies {
if j > 0 {
unstagedDependenciesString = unstagedDependenciesString.concat(", ")
}
unstagedDependenciesString = unstagedDependenciesString.concat(unstagedDependencies[j].toString())

j = j + 1
}

// the transactions will fail with a message that looks like this: `error: panic: Found unstaged dependencies: A.2ceae959ed1a7e7a.MigrationContractStaging, A.2ceae959ed1a7e7a.DependencyAudit`
panic("This transaction is using dependencies not staged for Crescendo upgrade coming soon! Learn more: https://bit.ly/FLOWCRESCENDO. Dependencies not staged: ".concat(unstagedDependenciesString))
}

// shouldPanicRandomly is used to randomly panic on unstaged dependencies
// The probability of panicking is based on the current block height and the start and end block heights
// If the start block height is greater than or equal to the end block height, the function will return true
// The function will always return true if the current block is more than the end block height
// The function will always return false if the current block is less than the start block height
// The function will return true if a random number between the start and end block heights is less than the current block height
// This means the probability of panicking increases linearly as the current block height approaches the end block height
access(self) fun shouldPanicRandomly(): Bool {
// get start block height or true
// get end block height or true
// get current block height
// get random number between start and end
// if random number is less than current block return true
// else return false
let maybeBoundaries = self.getBoundaries()
if maybeBoundaries == nil {
// if settings are invalid use default behaviour: panic true
return true
}
let boundaries = maybeBoundaries!

let startBlock: UInt64 = boundaries.start
let endBlock: UInt64 = boundaries.end
let currentBlock: UInt64 = getCurrentBlock().height

if startBlock >= endBlock {
// this should never happen becuse we validate the boundaries when setting them
// if settings are invalid use default behaviour: panic true
return true
}

let dif = endBlock - startBlock
var rnd = revertibleRandom<UInt64>() % dif
rnd = rnd + startBlock

// fail if the random number is less than the current block
return rnd < currentBlock
}

access(all) struct Boundaries {
access(all) let start: UInt64
access(all) let end: UInt64

init(start: UInt64, end: UInt64) {
self.start = start
self.end = end
}
}

access(all) fun getBoundaries(): Boundaries? {
return self.account.storage.copy<Boundaries>(from: /storage/flowDependencyAuditBoundaries)
}

access(all) fun getCurrentFailureProbability(): UFix64 {
if !DependencyAudit.panicOnUnstaged {
return 0.0 as UFix64
}

let maybeBoundaries = self.getBoundaries()
if maybeBoundaries == nil {
return 1.0 as UFix64
}

let boundaries = maybeBoundaries!

let startBlock: UInt64 = boundaries.start
let endBlock: UInt64 = boundaries.end
let currentBlock: UInt64 = getCurrentBlock().height

if startBlock >= endBlock {
return 1.0 as UFix64
}
if currentBlock >= endBlock {
return 1.0 as UFix64
}
if currentBlock < startBlock {
return 0.0 as UFix64
}

let dif = endBlock - startBlock
let currentDif = currentBlock - startBlock

return UFix64(currentDif) / UFix64(dif)
}

access(self) fun setBoundaries(boundaries: Boundaries) {
self.account.storage.load<Boundaries>(from: /storage/flowDependencyAuditBoundaries)
self.account.storage.save(boundaries, to: /storage/flowDependencyAuditBoundaries)
}

access(self) fun unsetBoundaries() {
self.account.storage.load<Boundaries>(from: /storage/flowDependencyAuditBoundaries)
}

// The Administrator resorce can be used to add or remove addresses from the excludedAddresses dictionary
Expand All @@ -87,6 +190,23 @@ access(all) contract DependencyAudit {
emit PanicOnUnstagedDependenciesChanged(shouldPanic: shouldPanic)
}

// setStartEndBlock sets the start and end block heights for the `shouldPanicRandomly` function
access(all) fun setStartEndBlock(start: UInt64, end: UInt64) {
pre {
start < end: "Start block height must be less than end block height"
}

let boundaries = Boundaries(start: start, end: end)
DependencyAudit.setBoundaries(boundaries: boundaries)
emit BlockBoundariesChanged(start: start, end: end)
}

// unsetStartEndBlock unsets the start and end block heights for the `shouldPanicRandomly` function
access(all) fun unsetStartEndBlock() {
DependencyAudit.unsetBoundaries()
emit BlockBoundariesChanged(start: nil, end: nil)
}

// testCheckDependencies is used for testing purposes
// It will call the `checkDependencies` function with the provided dependencies
// `checkDependencies` is otherwise not callable from the outside
Expand Down
25 changes: 21 additions & 4 deletions contracts/MigrationContractStaging.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ access(all) contract MigrationContractStaging {
access(all) event StagingStatusUpdated(
capsuleUUID: UInt64,
address: Address,
code: String,
codeHash: [UInt8],
contractIdentifier: String,
action: String
)
Expand Down Expand Up @@ -112,7 +112,7 @@ access(all) contract MigrationContractStaging {
emit StagingStatusUpdated(
capsuleUUID: capsuleUUID,
address: address,
code: "",
codeHash: [],
contractIdentifier: name,
action: "unstage"
)
Expand Down Expand Up @@ -156,6 +156,15 @@ access(all) contract MigrationContractStaging {
return self.getStagedContractUpdate(address: address, name: name)?.code
}

/// Returns the staged contract code hash for the given address and name or nil if it's not staged
///
access(all) view fun getStagedContractCodeHash(address: Address, name: String): [UInt8]? {
if let update = self.getStagedContractUpdate(address: address, name: name) {
return self.getCodeHash(update.code)
}
return nil
}

/// Returns the ContractUpdate struct for the given contract if it's been staged.
///
access(all) view fun getStagedContractUpdate(address: Address, name: String): ContractUpdate? {
Expand Down Expand Up @@ -207,6 +216,14 @@ access(all) contract MigrationContractStaging {
?? panic("Could not derive Capsule StoragePath for given address")
}

/* --- Util --- */

/// Returns the hash of the given code, hashing with SHA3-256
///
access(all) view fun getCodeHash(_ code: String): [UInt8] {
return HashAlgorithm.SHA3_256.hash(code.utf8)
}

/* ------------------------------------------------------------------------------------------------------------ */
/* ------------------------------------------------ Constructs ------------------------------------------------ */
/* ------------------------------------------------------------------------------------------------------------ */
Expand Down Expand Up @@ -352,7 +369,7 @@ access(all) contract MigrationContractStaging {
emit StagingStatusUpdated(
capsuleUUID: self.uuid,
address: self.update.address,
code: code,
codeHash: MigrationContractStaging.getCodeHash(code),
contractIdentifier: self.update.name,
action: "replace"
)
Expand Down Expand Up @@ -406,7 +423,7 @@ access(all) contract MigrationContractStaging {
emit StagingStatusUpdated(
capsuleUUID: capsule.uuid,
address: host.address(),
code: code,
codeHash: MigrationContractStaging.getCodeHash(code),
contractIdentifier: name,
action: "stage"
)
Expand Down
12 changes: 6 additions & 6 deletions lib/go/contracts/internal/assets/assets.go

Large diffs are not rendered by default.

Loading

0 comments on commit 3e614c1

Please sign in to comment.