Skip to content

Commit

Permalink
add AccountV2Migration contract
Browse files Browse the repository at this point in the history
  • Loading branch information
turbolent committed Nov 27, 2024
1 parent 8e6fbbf commit de4b564
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 71 deletions.
84 changes: 84 additions & 0 deletions fvm/accountV2Migration/AccountV2Migration.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
access(all)
contract AccountV2Migration {

access(all)
event Migrated(
addressStartIndex: UInt64,
count: UInt64
)

access(all)
resource Admin {
access(all)
fun setEnabled(_ isEnabled: Bool) {
AccountV2Migration.isEnabled = isEnabled
}

access(all)
fun setNextAddressStartIndex(_ nextAddressStartIndex: UInt64) {
AccountV2Migration.nextAddressStartIndex = nextAddressStartIndex
}

access(all)
fun setBatchSize(_ batchSize: UInt64) {
AccountV2Migration.batchSize = batchSize
}

access(all)
fun migrateNextBatch() {
AccountV2Migration.migrateNextBatch()
}
}

access(all)
let adminStoragePath: StoragePath

access(all)
var isEnabled: Bool

access(all)
var nextAddressStartIndex: UInt64

access(all)
var batchSize: UInt64

init() {
self.adminStoragePath = /storage/accountV2MigrationAdmin
self.isEnabled = false
self.nextAddressStartIndex = 1
self.batchSize = 10

self.account.storage.save(
<-create Admin(),
to: self.adminStoragePath
)
}

access(contract)
fun migrateNextBatch() {
if !self.isEnabled {
return
}

let batchSize = self.batchSize
if batchSize <= 0 {
return
}

let startIndex = self.nextAddressStartIndex

if !scheduleAccountV2Migration(
addressStartIndex: startIndex,
count: batchSize
) {
return
}

self.nextAddressStartIndex = startIndex + batchSize

emit Migrated(
addressStartIndex: startIndex,
count: batchSize
)
}
}
133 changes: 133 additions & 0 deletions fvm/accountV2Migration/contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package accountV2Migration

import (
_ "embed"

"github.com/onflow/cadence/common"
cadenceErrors "github.com/onflow/cadence/errors"
"github.com/onflow/cadence/interpreter"
"github.com/onflow/cadence/runtime"
"github.com/onflow/cadence/sema"
"github.com/onflow/cadence/stdlib"

"github.com/onflow/flow-go/fvm/errors"
"github.com/onflow/flow-go/fvm/systemcontracts"
"github.com/onflow/flow-go/model/flow"
)

//go:embed AccountV2Migration.cdc
var ContractCode []byte

const ContractName = "AccountV2Migration"

const scheduleAccountV2MigrationFunctionName = "scheduleAccountV2Migration"

// scheduleAccountV2MigrationType is the type of the `scheduleAccountV2Migration` function.
// This defines the signature as `func(addressStartIndex: UInt64, count: UInt64): Bool`
var scheduleAccountV2MigrationType = &sema.FunctionType{
Parameters: []sema.Parameter{
{
Identifier: "addressStartIndex",
TypeAnnotation: sema.UInt64TypeAnnotation,
},
{
Identifier: "count",
TypeAnnotation: sema.UInt64TypeAnnotation,
},
},
ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.BoolType),
}

func DeclareScheduleAccountV2MigrationFunction(environment runtime.Environment, chainID flow.ChainID) {

functionType := scheduleAccountV2MigrationType

functionValue := stdlib.StandardLibraryValue{
Name: scheduleAccountV2MigrationFunctionName,
Type: functionType,
Kind: common.DeclarationKindFunction,
Value: interpreter.NewUnmeteredStaticHostFunctionValue(
functionType,
func(invocation interpreter.Invocation) interpreter.Value {
inter := invocation.Interpreter

// Get interpreter storage

storage := inter.Storage()

runtimeStorage, ok := storage.(*runtime.Storage)
if !ok {
panic(cadenceErrors.NewUnexpectedError("interpreter storage is not a runtime.Storage"))
}

// Check the number of arguments

actualArgumentCount := len(invocation.Arguments)
expectedArgumentCount := len(functionType.Parameters)

if actualArgumentCount != expectedArgumentCount {
panic(errors.NewInvalidArgumentErrorf(
"incorrect number of arguments: got %d, expected %d",
actualArgumentCount,
expectedArgumentCount,
))
}

// Get addressStartIndex argument

firstArgument := invocation.Arguments[0]
addressStartIndexValue, ok := firstArgument.(interpreter.UInt64Value)
if !ok {
panic(errors.NewInvalidArgumentErrorf(
"incorrect type for argument 0: got `%s`, expected `%s`",
firstArgument.StaticType(inter),
sema.UInt64Type,
))
}
addressStartIndex := uint64(addressStartIndexValue)

// Get count argument

secondArgument := invocation.Arguments[1]
countValue, ok := secondArgument.(interpreter.UInt64Value)
if !ok {
panic(errors.NewInvalidArgumentErrorf(
"incorrect type for argument 1: got `%s`, expected `%s`",
secondArgument.StaticType(inter),
sema.UInt64Type,
))
}
count := uint64(countValue)

// Schedule the account V2 migration for addresses

addressGenerator := chainID.Chain().NewAddressGeneratorAtIndex(addressStartIndex)
for i := uint64(0); i < count; i++ {
address, err := addressGenerator.NextAddress()
if err != nil {
panic(err)
}

if !runtimeStorage.ScheduleV2Migration(common.Address(address)) {
return interpreter.FalseValue
}
}

return interpreter.TrueValue
},
),
}

sc := systemcontracts.SystemContractsForChain(chainID)

accountV2MigrationLocation := common.NewAddressLocation(
nil,
common.Address(sc.AccountV2Migration.Address),
ContractName,
)

environment.DeclareValue(
functionValue,
accountV2MigrationLocation,
)
}
16 changes: 16 additions & 0 deletions fvm/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/onflow/flow-core-contracts/lib/go/contracts"
"github.com/onflow/flow-core-contracts/lib/go/templates"

"github.com/onflow/flow-go/fvm/accountV2Migration"
"github.com/onflow/flow-go/fvm/blueprints"
"github.com/onflow/flow-go/fvm/environment"
"github.com/onflow/flow-go/fvm/errors"
Expand Down Expand Up @@ -447,6 +448,8 @@ func (b *bootstrapExecutor) Execute() error {
// set the list of nodes which are allowed to stake in this network
b.setStakingAllowlist(service, b.identities.NodeIDs())

b.deployAccountV2MigrationContract(service)

return nil
}

Expand Down Expand Up @@ -1067,6 +1070,19 @@ func (b *bootstrapExecutor) deployStakingCollection(
panicOnMetaInvokeErrf("failed to deploy FlowStakingCollection contract: %s", txError, err)
}

func (b *bootstrapExecutor) deployAccountV2MigrationContract(deployTo flow.Address) {
tx := blueprints.DeployContractTransaction(
deployTo,
accountV2Migration.ContractCode,
accountV2Migration.ContractName,
)
txError, err := b.invokeMetaTransaction(
b.ctx,
Transaction(tx, 0),
)
panicOnMetaInvokeErrf("failed to deploy AccountV2Migration contract: %s", txError, err)
}

func (b *bootstrapExecutor) setContractDeploymentRestrictions(
service flow.Address,
deployment *bool,
Expand Down
87 changes: 16 additions & 71 deletions fvm/runtime/reusable_cadence_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ package runtime
import (
"github.com/onflow/cadence"
"github.com/onflow/cadence/common"
cadenceErrors "github.com/onflow/cadence/errors"
"github.com/onflow/cadence/interpreter"
"github.com/onflow/cadence/runtime"
"github.com/onflow/cadence/sema"
"github.com/onflow/cadence/stdlib"

"github.com/onflow/flow-go/fvm/accountV2Migration"
"github.com/onflow/flow-go/fvm/errors"
"github.com/onflow/flow-go/fvm/systemcontracts"
"github.com/onflow/flow-go/model/flow"
)

Expand All @@ -23,24 +22,11 @@ type Environment interface {
}

// randomSourceFunctionType is the type of the `randomSource` function.
// This defies the signature as `func (): [UInt8]`
// This defines the signature as `func(): [UInt8]`
var randomSourceFunctionType = &sema.FunctionType{
Parameters: []sema.Parameter{},
ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.ByteArrayType),
}

// scheduleAccountV2MigrationType is the type of the `scheduleAccountV2Migration` function.
// This defies the signature as `func (address: Address): Bool`
var scheduleAccountV2MigrationType = &sema.FunctionType{
Parameters: []sema.Parameter{
{
Identifier: "address",
TypeAnnotation: sema.AddressTypeAnnotation,
},
},
ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.BoolType),
}

type ReusableCadenceRuntime struct {
runtime.Runtime
TxRuntimeEnv runtime.Environment
Expand All @@ -61,7 +47,7 @@ func NewReusableCadenceRuntime(
}

reusable.declareRandomSourceHistory()
reusable.declareScheduleAccountV2Migration(chainID)
accountV2Migration.DeclareScheduleAccountV2MigrationFunction(reusable.TxRuntimeEnv, chainID)

return reusable
}
Expand All @@ -76,16 +62,25 @@ func (reusable *ReusableCadenceRuntime) declareRandomSourceHistory() {
// it is not part of the cadence standard library, and can just be injected from here.
// It also doesnt need user documentation, since it is not (and should not)
// be called by the user. If it is called by the user it will panic.
functionType := randomSourceFunctionType

blockRandomSource := stdlib.StandardLibraryValue{
Name: "randomSourceHistory",
Type: randomSourceFunctionType,
Type: functionType,
Kind: common.DeclarationKindFunction,
Value: interpreter.NewUnmeteredStaticHostFunctionValue(
randomSourceFunctionType,
functionType,
func(invocation interpreter.Invocation) interpreter.Value {
if len(invocation.Arguments) != 0 {

actualArgumentCount := len(invocation.Arguments)
expectedArgumentCount := len(functionType.Parameters)

if actualArgumentCount != expectedArgumentCount {
panic(errors.NewInvalidArgumentErrorf(
"randomSourceHistory should be called without arguments"))
"incorrect number of arguments: got %d, expected %d",
actualArgumentCount,
expectedArgumentCount,
))
}

var err error
Expand All @@ -111,56 +106,6 @@ func (reusable *ReusableCadenceRuntime) declareRandomSourceHistory() {
reusable.TxRuntimeEnv.DeclareValue(blockRandomSource, nil)
}

func (reusable *ReusableCadenceRuntime) declareScheduleAccountV2Migration(chainID flow.ChainID) {

serviceAccount := systemcontracts.SystemContractsForChain(chainID).FlowServiceAccount

blockRandomSource := stdlib.StandardLibraryValue{
Name: "scheduleAccountV2Migration",
Type: scheduleAccountV2MigrationType,
Kind: common.DeclarationKindFunction,
Value: interpreter.NewUnmeteredStaticHostFunctionValue(
scheduleAccountV2MigrationType,
func(invocation interpreter.Invocation) interpreter.Value {
if len(invocation.Arguments) != 1 {
panic(errors.NewInvalidArgumentErrorf(
"scheduleAccountV2Migration should be called with exactly one argument of type Address",
))
}

addressValue, ok := invocation.Arguments[0].(interpreter.AddressValue)
if !ok {
panic(errors.NewInvalidArgumentErrorf(
"scheduleAccountV2Migration should be called with exactly one argument of type Address",
))
}

storage := invocation.Interpreter.Storage()

runtimeStorage, ok := storage.(*runtime.Storage)
if !ok {
panic(cadenceErrors.NewUnexpectedError("interpreter storage is not a runtime.Storage"))
}

result := runtimeStorage.ScheduleV2Migration(common.Address(addressValue))

return interpreter.AsBoolValue(result)
},
),
}

accountV2MigrationLocation := common.NewAddressLocation(
nil,
common.Address(serviceAccount.Address),
"AccountV2Migration",
)

reusable.TxRuntimeEnv.DeclareValue(
blockRandomSource,
accountV2MigrationLocation,
)
}

func (reusable *ReusableCadenceRuntime) SetFvmEnvironment(fvmEnv Environment) {
reusable.fvmEnv = fvmEnv
}
Expand Down
Loading

0 comments on commit de4b564

Please sign in to comment.