Skip to content

Commit

Permalink
Merge branch 'v0.37' into janez/migration-mainnet-add-keys-to-core-co…
Browse files Browse the repository at this point in the history
…ntracts-2
  • Loading branch information
janezpodhostnik authored Aug 28, 2024
2 parents 13da983 + 1afc5a4 commit cbf3496
Show file tree
Hide file tree
Showing 44 changed files with 3,278 additions and 2,260 deletions.
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,14 @@ docker-native-build-access-corrupt:
-t "$(CONTAINER_REGISTRY)/access-corrupted:$(IMAGE_TAG)" .
./insecure/cmd/mods_restore.sh

# build a binary to run on bare metal without using docker.
# binary is written to file ./bin/app
.PHONY: docker-native-build-access-binary
docker-native-build-access-binary: docker-native-build-access
docker create --name extract "$(CONTAINER_REGISTRY)/access:latest"
docker cp extract:/bin/app ./flow_access_node
docker rm extract

.PHONY: docker-native-build-observer
docker-native-build-observer:
docker build -f cmd/Dockerfile --build-arg TARGET=./cmd/observer --build-arg COMMIT=$(COMMIT) --build-arg VERSION=$(IMAGE_TAG) --build-arg GOARCH=$(GOARCH) --build-arg CGO_FLAG=$(CRYPTO_FLAG) --target production \
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,17 @@ Build a Docker image for a particular node role (replace `$ROLE` with `collectio
make docker-native-build-$ROLE
```

#### Building a binary for the access node

Build the binary for an access node that can be run directly on the machine without using Docker.

```bash
make docker-native-build-access-binary
```
_this builds a binary for Linux/x86_64 machine_.

The make command will generate a binary called `flow_access_node`

### Importing the module

When importing the `github.com/onflow/flow-go` module in your Go project, testing or building your project may require setting extra Go flags because the module requires [cgo](https://pkg.go.dev/cmd/cgo). In particular, `CGO_ENABLED` must be set to `1` if `cgo` isn't enabled by default. This constraint comes from the underlying cryptography library. Refer to the [crypto repository build](https://github.com/onflow/crypto?tab=readme-ov-file#build) for more details.
Expand Down
46 changes: 36 additions & 10 deletions cmd/bootstrap/cmd/pull.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
package cmd

import (
"bytes"
"context"
"fmt"
"path/filepath"
"strings"
"sync"
"time"

"github.com/spf13/cobra"
"golang.org/x/sync/semaphore"

"github.com/onflow/flow-go/cmd"
"github.com/onflow/flow-go/cmd/bootstrap/gcs"
"github.com/onflow/flow-go/cmd/bootstrap/utils"
)

var (
flagNetwork string
flagBucketName string
flagNetwork string
flagBucketName string
flagConcurrency int64
)

// pullCmd represents a command to pull parnter node details from the google
Expand All @@ -37,6 +42,7 @@ func addPullCmdFlags() {
cmd.MarkFlagRequired(pullCmd, "network")

pullCmd.Flags().StringVar(&flagBucketName, "bucket", "flow-genesis-bootstrap", "google bucket name")
pullCmd.Flags().Int64Var(&flagConcurrency, "concurrency", 2, "concurrency limit")
}

// pull partner node info from google bucket
Expand All @@ -62,15 +68,35 @@ func pull(cmd *cobra.Command, args []string) {
}
log.Info().Msgf("found %d files in google bucket", len(files))

sem := semaphore.NewWeighted(flagConcurrency)
wait := sync.WaitGroup{}
for _, file := range files {
if strings.Contains(file, "node-info.pub") {
fullOutpath := filepath.Join(flagOutdir, file)
log.Printf("downloading %s", file)

err = bucket.DownloadFile(ctx, client, fullOutpath, file)
if err != nil {
log.Error().Msgf("error trying download google bucket file: %v", err)
wait.Add(1)
go func(file gcs.GCSFile) {
_ = sem.Acquire(ctx, 1)
defer func() {
sem.Release(1)
wait.Done()
}()

if strings.Contains(file.Name, "node-info.pub") {
fullOutpath := filepath.Join(flagOutdir, file.Name)

fmd5 := utils.CalcMd5(fullOutpath)
// only skip files that have an MD5 hash
if file.MD5 != nil && bytes.Equal(fmd5, file.MD5) {
log.Printf("skipping %s", file)
return
}

log.Printf("downloading %s", file)
err = bucket.DownloadFile(ctx, client, fullOutpath, file.Name)
if err != nil {
log.Error().Msgf("error trying download google bucket file: %v", err)
}
}
}
}(file)
}

wait.Wait()
}
14 changes: 11 additions & 3 deletions cmd/bootstrap/gcs/gcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,19 @@ func (g *googleBucket) NewClient(ctx context.Context) (*storage.Client, error) {
return client, nil
}

type GCSFile struct {
Name string
MD5 []byte
}

// GetFiles returns a list of file names within the Google bucket
func (g *googleBucket) GetFiles(ctx context.Context, client *storage.Client, prefix, delimiter string) ([]string, error) {
func (g *googleBucket) GetFiles(ctx context.Context, client *storage.Client, prefix, delimiter string) ([]GCSFile, error) {
it := client.Bucket(g.Name).Objects(ctx, &storage.Query{
Prefix: prefix,
Delimiter: delimiter,
})

var files []string
var files []GCSFile
for {
attrs, err := it.Next()
if err == iterator.Done {
Expand All @@ -50,7 +55,10 @@ func (g *googleBucket) GetFiles(ctx context.Context, client *storage.Client, pre
return nil, err
}

files = append(files, attrs.Name)
files = append(files, GCSFile{
Name: attrs.Name,
MD5: attrs.MD5,
})
}

return files, nil
Expand Down
1 change: 1 addition & 0 deletions cmd/bootstrap/transit/cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ var (
flagAccessAddress string
flagNodeRole string
flagTimeout time.Duration
flagConcurrency int64

flagWrapID string // wrap ID
flagVoteFile string
Expand Down
38 changes: 30 additions & 8 deletions cmd/bootstrap/transit/cmd/pull.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package cmd

import (
"bytes"
"context"
"fmt"
"io/fs"
"path/filepath"
"strings"
"sync"
"time"

"github.com/spf13/cobra"
"golang.org/x/sync/semaphore"

"github.com/onflow/flow-go/cmd/bootstrap/gcs"
"github.com/onflow/flow-go/cmd/bootstrap/utils"
model "github.com/onflow/flow-go/model/bootstrap"
"github.com/onflow/flow-go/model/flow"
)
Expand All @@ -32,6 +36,7 @@ func addPullCmdFlags() {
pullCmd.Flags().StringVarP(&flagToken, "token", "t", "", "token provided by the Flow team to access the Transit server")
pullCmd.Flags().StringVarP(&flagNodeRole, "role", "r", "", `node role (can be "collection", "consensus", "execution", "verification" or "access")`)
pullCmd.Flags().DurationVar(&flagTimeout, "timeout", time.Second*300, `timeout for pull`)
pullCmd.Flags().Int64Var(&flagConcurrency, "concurrency", 2, `concurrency limit for pull`)

_ = pullCmd.MarkFlagRequired("token")
_ = pullCmd.MarkFlagRequired("role")
Expand Down Expand Up @@ -78,17 +83,34 @@ func pull(cmd *cobra.Command, args []string) {
}
log.Info().Msgf("found %d files in Google Bucket", len(files))

// download found files
sem := semaphore.NewWeighted(flagConcurrency)
wait := sync.WaitGroup{}
for _, file := range files {
fullOutpath := filepath.Join(flagBootDir, "public-root-information", filepath.Base(file))

log.Info().Str("source", file).Str("dest", fullOutpath).Msgf("downloading file from transit servers")
err = bucket.DownloadFile(ctx, client, fullOutpath, file)
if err != nil {
log.Fatal().Err(err).Msgf("could not download google bucket file")
}
wait.Add(1)
go func(file gcs.GCSFile) {
_ = sem.Acquire(ctx, 1)
defer func() {
sem.Release(1)
wait.Done()
}()

fullOutpath := filepath.Join(flagBootDir, "public-root-information", filepath.Base(file.Name))
fmd5 := utils.CalcMd5(fullOutpath)
// only skip files that have an MD5 hash
if file.MD5 != nil && bytes.Equal(fmd5, file.MD5) {
log.Info().Str("source", file.Name).Str("dest", fullOutpath).Msgf("skipping existing file from transit servers")
return
}
log.Info().Str("source", file.Name).Str("dest", fullOutpath).Msgf("downloading file from transit servers")
err = bucket.DownloadFile(ctx, client, fullOutpath, file.Name)
if err != nil {
log.Fatal().Err(err).Msgf("could not download google bucket file")
}
}(file)
}

wait.Wait()

// download any extra files specific to node role
extraFiles := getAdditionalFilesToDownload(role, nodeID)
for _, file := range extraFiles {
Expand Down
25 changes: 25 additions & 0 deletions cmd/bootstrap/utils/md5.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package utils

// The google storage API only provides md5 and crc32 hence overriding the linter flag for md5
// #nosec
import (
"crypto/md5"
"io"
"os"
)

func CalcMd5(outpath string) []byte {
f, err := os.Open(outpath)
if err != nil {
return nil
}
defer f.Close()

// #nosec
h := md5.New()
if _, err := io.Copy(h, f); err != nil {
return nil
}

return h.Sum(nil)
}
35 changes: 32 additions & 3 deletions cmd/util/cmd/check-storage/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package check_storage

import (
"context"
"slices"

"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
Expand All @@ -14,7 +15,9 @@ import (
"github.com/onflow/flow-go/cmd/util/ledger/reporters"
"github.com/onflow/flow-go/cmd/util/ledger/util"
"github.com/onflow/flow-go/cmd/util/ledger/util/registers"
"github.com/onflow/flow-go/fvm/systemcontracts"
"github.com/onflow/flow-go/ledger"
"github.com/onflow/flow-go/model/flow"
moduleUtil "github.com/onflow/flow-go/module/util"
)

Expand All @@ -23,6 +26,7 @@ var (
flagState string
flagStateCommitment string
flagOutputDirectory string
flagChain string
flagNWorker int
)

Expand Down Expand Up @@ -74,10 +78,22 @@ func init() {
10,
"number of workers to use",
)

Cmd.Flags().StringVar(
&flagChain,
"chain",
"",
"Chain name",
)
_ = Cmd.MarkFlagRequired("chain")
}

func run(*cobra.Command, []string) {

chainID := flow.ChainID(flagChain)
// Validate chain ID
_ = chainID.Chain()

if flagPayloads == "" && flagState == "" {
log.Fatal().Msg("Either --payloads or --state must be provided")
} else if flagPayloads != "" && flagState != "" {
Expand All @@ -87,6 +103,14 @@ func run(*cobra.Command, []string) {
log.Fatal().Msg("--state-commitment must be provided when --state is provided")
}

// For now, skip EVM storage account since a different decoder is needed for decoding EVM registers.

systemContracts := systemcontracts.SystemContractsForChain(chainID)

acctsToSkip := []string{
flow.AddressToRegisterOwner(systemContracts.EVMStorage.Address),
}

// Create report in JSONL format
rw := reporters.NewReportFileWriterFactoryWithFormat(flagOutputDirectory, log.Logger, reporters.ReportFormatJSONL).
ReportWriter(ReporterName)
Expand Down Expand Up @@ -138,7 +162,7 @@ func run(*cobra.Command, []string) {
len(payloads),
)

failedAccountAddresses, err := checkStorageHealth(registersByAccount, flagNWorker, rw)
failedAccountAddresses, err := checkStorageHealth(registersByAccount, flagNWorker, rw, acctsToSkip)
if err != nil {
log.Fatal().Err(err).Msg("failed to check storage health")
}
Expand All @@ -165,6 +189,7 @@ func checkStorageHealth(
registersByAccount *registers.ByAccount,
nWorkers int,
rw reporters.ReportWriter,
acctsToSkip []string,
) (failedAccountAddresses []string, err error) {

accountCount := registersByAccount.AccountCount()
Expand All @@ -185,6 +210,12 @@ func checkStorageHealth(
// Skip goroutine to avoid overhead
err = registersByAccount.ForEachAccount(
func(accountRegisters *registers.AccountRegisters) error {
defer logAccount(1)

if slices.Contains(acctsToSkip, accountRegisters.Owner()) {
return nil
}

accountStorageIssues := checkAccountStorageHealth(accountRegisters, nWorkers)

if len(accountStorageIssues) > 0 {
Expand All @@ -195,8 +226,6 @@ func checkStorageHealth(
}
}

logAccount(1)

return nil
})

Expand Down
16 changes: 3 additions & 13 deletions cmd/util/ledger/migrations/account_storage_migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,25 +52,15 @@ func NewAccountStorageMigration(
log.Err(err).Msg("storage health check failed")
}

// Finalize the transaction
result, err := migrationRuntime.TransactionState.FinalizeMainTransaction()
if err != nil {
return fmt.Errorf("failed to finalize main transaction: %w", err)
}
// Commit/finalize the transaction

// Merge the changes into the registers
expectedAddresses := map[flow.Address]struct{}{
flow.Address(address): {},
}

err = registers.ApplyChanges(
registersByAccount,
result.WriteSet,
expectedAddresses,
log,
)
err = migrationRuntime.Commit(expectedAddresses, log)
if err != nil {
return fmt.Errorf("failed to apply register changes: %w", err)
return fmt.Errorf("failed to commit: %w", err)
}

return nil
Expand Down
Loading

0 comments on commit cbf3496

Please sign in to comment.