Skip to content

Commit

Permalink
Add system contracts migration
Browse files Browse the repository at this point in the history
  • Loading branch information
SupunS committed Feb 2, 2024
1 parent 374fd54 commit 7590ef8
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 214 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (
github.com/onflow/cadence v1.0.0-preview.2.0.20240201184233-a301a4152f6b
github.com/onflow/crypto v0.25.0
github.com/onflow/flow-core-contracts/lib/go/templates v0.15.1-0.20240125214229-b7a95136dd0d
github.com/onflow/flow-go v0.33.2-0.20240126211806-97279f96695f
github.com/onflow/flow-go v0.33.2-0.20240202003043-efb29d915946
github.com/onflow/flow-go-sdk v1.0.0-M1
github.com/onflow/flow-nft/lib/go/contracts v1.1.1-0.20240125205553-d2b571fb3fad
github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20231213135419-ae911cc351a2
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1987,8 +1987,8 @@ github.com/onflow/flow-core-contracts/lib/go/templates v0.15.1-0.20240125214229-
github.com/onflow/flow-core-contracts/lib/go/templates v0.15.1-0.20240125214229-b7a95136dd0d/go.mod h1:MZ2j5YVTQiSE0B99zuaYhxvGG5GcvimWpQK1Fw/1QBg=
github.com/onflow/flow-ft/lib/go/contracts v0.7.1-0.20240125205519-2e80d9b4bd01 h1:8iKk5RuFvhe7NQyAO3c+xiVvv38RB/yopHdWxp4AbL8=
github.com/onflow/flow-ft/lib/go/contracts v0.7.1-0.20240125205519-2e80d9b4bd01/go.mod h1:PwsL8fC81cjnUnTfmyL/HOIyHnyaw/JA474Wfj2tl6A=
github.com/onflow/flow-go v0.33.2-0.20240126211806-97279f96695f h1:F1y95CpteZn0i4v0FDGjKiqI13Xlir3hX4x0C1xMRoc=
github.com/onflow/flow-go v0.33.2-0.20240126211806-97279f96695f/go.mod h1:9q+c+fuTpc/emueM/2bI/Ih2jw3V+9WS3Eu+pWBuLW0=
github.com/onflow/flow-go v0.33.2-0.20240202003043-efb29d915946 h1:YhEktpKe9wHOZDyzIMqVTuROWEGU9QFVmJ+s5u3M8Os=
github.com/onflow/flow-go v0.33.2-0.20240202003043-efb29d915946/go.mod h1:9q+c+fuTpc/emueM/2bI/Ih2jw3V+9WS3Eu+pWBuLW0=
github.com/onflow/flow-go-sdk v1.0.0-M1 h1:mke/ebYwNRRWPZqcwCV56Alx0A8psew43ZbSEUQ4TL8=
github.com/onflow/flow-go-sdk v1.0.0-M1/go.mod h1:TDW0MNuCs4SvqYRUzkbRnRmHQL1h4X8wURsCw9P9beo=
github.com/onflow/flow-nft/lib/go/contracts v1.1.1-0.20240125205553-d2b571fb3fad h1:I6LD9BOsilGbiqhGjP86FIIXJe0YdUz75d/oWdHFzDI=
Expand Down
206 changes: 4 additions & 202 deletions storage/migration/cadence_values_migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,27 @@ package migration

import (
"context"
"database/sql"
"encoding/binary"
"encoding/hex"
"os"
"strconv"
"strings"

"github.com/rs/zerolog"

"github.com/onflow/flow-emulator/storage"
"github.com/onflow/flow-emulator/storage/sqlite"

"github.com/onflow/cadence/runtime/common"
"github.com/onflow/cadence/runtime/interpreter"

"github.com/onflow/flow-go/cmd/util/ledger/migrations"
"github.com/onflow/flow-go/cmd/util/ledger/reporters"
"github.com/onflow/flow-go/cmd/util/ledger/util"
"github.com/onflow/flow-go/ledger"
"github.com/onflow/flow-go/ledger/common/convert"
"github.com/onflow/flow-go/model/flow"
)

func MigrateCadenceValues(store *sqlite.Store) error {
payloads, payloadInfo, accounts, err := payloadsAndAccountsFromSnapshot(store.DB())
payloads, payloadInfo, accounts, err := util.PayloadsAndAccountsFromEmulatorSnapshot(store.DB())
if err != nil {
return err
}

rwf := &ReportWriterFactory{}
capabilityIDs := map[interpreter.AddressPath]interpreter.UInt64Value{}
logger := newConsoleLogger()
logger := NewConsoleLogger()

payloads, err = migrateLinkValues(rwf, logger, capabilityIDs, accounts, payloads)
if err != nil {
Expand All @@ -62,7 +52,7 @@ func MigrateCadenceValues(store *sqlite.Store) error {
return err
}

return writePayloadsToSnapshot(store, payloads, payloadInfo)
return WritePayloadsToSnapshot(store, payloads, payloadInfo)
}

func migrateLinkValues(
Expand Down Expand Up @@ -124,191 +114,3 @@ func migrateCadenceValues(

return payloads, nil
}

func newConsoleLogger() zerolog.Logger {
writer := zerolog.ConsoleWriter{
Out: os.Stdout,
}

return zerolog.New(writer).
With().
Timestamp().
Logger().
Level(zerolog.InfoLevel)
}

func writePayloadsToSnapshot(
store *sqlite.Store,
payloads []*ledger.Payload,
payloadInfoSet map[flow.RegisterID]payloadMetaInfo,
) error {

const storeName = storage.LedgerStoreName

for _, payload := range payloads {
key, err := payload.Key()
if err != nil {
return err
}

registerId, err := convert.LedgerKeyToRegisterID(key)
if err != nil {
return err
}

registerIdBytes := []byte(registerId.String())

value := payload.Value()

payloadInfo, ok := payloadInfoSet[registerId]
if ok {
// Insert the values back with the existing height and version.
err = store.SetBytesWithVersionAndHeight(
nil,
storeName,
registerIdBytes,
value,
payloadInfo.version,
payloadInfo.height,
)
} else {
// If this is a new payload, use the current block height, and default version.
err = store.SetBytes(
nil,
storeName,
registerIdBytes,
value,
)
}

if err != nil {
return err
}
}

return nil
}

func payloadsAndAccountsFromSnapshot(db *sql.DB) (
[]*ledger.Payload,
map[flow.RegisterID]payloadMetaInfo,
[]common.Address,
error,
) {
rows, err := db.Query("SELECT key, value, version, height FROM ledger")
if err != nil {
return nil, nil, nil, err
}

var payloads []*ledger.Payload
var accounts []common.Address
accountsSet := make(map[common.Address]struct{})

payloadSet := make(map[flow.RegisterID]payloadMetaInfo)

for rows.Next() {
var hexKey, hexValue string
var height, version uint64

err := rows.Scan(&hexKey, &hexValue, &height, &version)
if err != nil {
return nil, nil, nil, err
}

key, err := hex.DecodeString(hexKey)
if err != nil {
return nil, nil, nil, err
}

value, err := hex.DecodeString(hexValue)
if err != nil {
return nil, nil, nil, err
}

registerId, address := registerIDKeyFromString(string(key))

if _, contains := accountsSet[address]; !contains {
accountsSet[address] = struct{}{}
accounts = append(accounts, address)
}

ledgerKey := convert.RegisterIDToLedgerKey(registerId)

payload := ledger.NewPayload(
ledgerKey,
value,
)

payloads = append(payloads, payload)
payloadSet[registerId] = payloadMetaInfo{
height: height,
version: version,
}
}

return payloads, payloadSet, accounts, nil
}

// registerIDKeyFromString is the inverse of `flow.RegisterID.String()` method.
func registerIDKeyFromString(s string) (flow.RegisterID, common.Address) {
parts := strings.SplitN(s, "/", 2)

owner := parts[0]
key := parts[1]

address, err := common.HexToAddress(owner)
if err != nil {
panic(err)
}

var decodedKey string

switch key[0] {
case '$':
b := make([]byte, 9)
b[0] = '$'

int64Value, err := strconv.ParseInt(key[1:], 10, 64)
if err != nil {
panic(err)
}

binary.BigEndian.PutUint64(b[1:], uint64(int64Value))

decodedKey = string(b)
case '#':
decoded, err := hex.DecodeString(key[1:])
if err != nil {
panic(err)
}
decodedKey = string(decoded)
default:
panic("Invalid register key")
}

return flow.RegisterID{
Owner: string(address.Bytes()),
Key: decodedKey,
},
address
}

type payloadMetaInfo struct {
height, version uint64
}

type ReportWriterFactory struct{}

func (_m *ReportWriterFactory) ReportWriter(_ string) reporters.ReportWriter {
return &NOOPWriter{}
}

type NOOPWriter struct{}

var _ reporters.ReportWriter = &NOOPWriter{}

func (r *NOOPWriter) Write(_ any) {
// NO-OP
}

func (r *NOOPWriter) Close() {}
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,8 @@ func TestStateMigration(t *testing.T) {

tempEmulatorStatePath := tempEmulatorState.Name()

defer func() {
err := tempEmulatorState.Close()
require.NoError(t, err)
}()

defer func() {
err := os.Remove(tempEmulatorStatePath)
require.NoError(t, err)
}()
defer tempEmulatorState.Close()
defer os.Remove(tempEmulatorStatePath)

content, err := os.ReadFile(emulatorStateFile)
require.NoError(t, err)
Expand All @@ -58,6 +51,11 @@ func TestStateMigration(t *testing.T) {
store, err := sqlite.New(tempEmulatorStatePath)
require.NoError(t, err)

// First migrate the system contracts
err = MigrateSystemContracts(store)
require.NoError(t, err)

// Then migrate the values.
err = MigrateCadenceValues(store)
require.NoError(t, err)
}
82 changes: 82 additions & 0 deletions storage/migration/system_contracts_migration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Flow Emulator
*
* Copyright 2024 Dapper Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package migration

import (
"context"
"github.com/rs/zerolog"

"github.com/onflow/flow-emulator/storage/sqlite"

"github.com/onflow/cadence/runtime/common"

"github.com/onflow/flow-go/cmd/util/ledger/migrations"
"github.com/onflow/flow-go/cmd/util/ledger/util"
"github.com/onflow/flow-go/ledger"
"github.com/onflow/flow-go/model/flow"
)

func MigrateSystemContracts(store *sqlite.Store) error {
payloads, payloadInfo, accounts, err := util.PayloadsAndAccountsFromEmulatorSnapshot(store.DB())
if err != nil {
return err
}

logger := NewConsoleLogger()

payloads, err = migrateSystemContracts(logger, accounts, payloads)
if err != nil {
return err
}

return WritePayloadsToSnapshot(store, payloads, payloadInfo)
}

func migrateSystemContracts(
logger zerolog.Logger,
accounts []common.Address,
payloads []*ledger.Payload,
) ([]*ledger.Payload, error) {

migration := migrations.ChangeContractCodeMigration{}

systemContractChanges := migrations.SystemContractChanges(flow.Emulator)

for _, contractChange := range systemContractChanges {
migration.RegisterContractChange(
contractChange.Address,
contractChange.ContractName,
contractChange.NewContractCode,
)
}

err := migration.InitMigration(logger, nil, 0)
if err != nil {
return nil, err
}

for _, account := range accounts {
ctx := context.Background()
payloads, err = migration.MigrateAccount(ctx, account, payloads)
if err != nil {
return nil, err
}
}
return payloads, nil
}
Loading

0 comments on commit 7590ef8

Please sign in to comment.