debug

package
v0.38.0-rc.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 3, 2025 License: AGPL-3.0 Imports: 17 Imported by: 2

README

Remote Debugger

The remote debugger allows running transactions and scripts against existing network data.

It uses APIs to fetch registers and block info, for example the register value API of the execution nodes, or the execution data API of the access nodes.

This is mostly provided for debugging purpose and should not be used for production level operations.

Optionally, the debugger allows the fetched registers that are necessary for the execution to be written to and read from a cache.

Use the ExecutionNodeStorageSnapshot to fetch the registers and block info from the execution node (live/recent data).

Use the ExecutionDataStorageSnapshot to fetch the execution data from the access node (recent/historic data).

Sample Code

package debug_test

import (
	"context"
	"fmt"
	"os"
	"testing"

	"github.com/onflow/flow/protobuf/go/flow/access"
	"github.com/onflow/flow/protobuf/go/flow/execution"
	"github.com/onflow/flow/protobuf/go/flow/executiondata"
	"github.com/rs/zerolog"
	"github.com/stretchr/testify/require"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"

	"github.com/onflow/flow-go/model/flow"
	"github.com/onflow/flow-go/utils/debug"
)

func getTransaction(chain flow.Chain) *flow.TransactionBody {

	const scriptTemplate = `
	import FlowServiceAccount from 0x%s
	transaction() {
		prepare(signer: &Account) {
			log(signer.balance)
		}
	  }
	`

	script := []byte(fmt.Sprintf(scriptTemplate, chain.ServiceAddress()))
	txBody := flow.NewTransactionBody().
		SetComputeLimit(9999).
		SetScript(script).
		SetPayer(chain.ServiceAddress()).
		SetProposalKey(chain.ServiceAddress(), 0, 0)
	txBody.Authorizers = []flow.Address{chain.ServiceAddress()}

	return txBody
}

func TestDebugger_RunTransactionAgainstExecutionNodeAtBlockID(t *testing.T) {

	host := "execution-001.mainnet26.nodes.onflow.org:9000"

	conn, err := grpc.NewClient(
		host,
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	)
	require.NoError(t, err)
	defer conn.Close()

	executionClient := execution.NewExecutionAPIClient(conn)

	blockID, err := flow.HexStringToIdentifier("e68a9a1fe849d1be80e4c5e414f53e3b59a170b88785e0b22be077ae9c3bbd29")
	require.NoError(t, err)

	header, err := debug.GetExecutionAPIBlockHeader(executionClient, context.Background(), blockID)

	snapshot, err := debug.NewExecutionNodeStorageSnapshot(executionClient, nil, blockID)
	require.NoError(t, err)

	defer snapshot.Close()

	chain := flow.Mainnet.Chain()
	logger := zerolog.New(os.Stdout).With().Logger()
	debugger := debug.NewRemoteDebugger(chain, logger)

	txBody := getTransaction(chain)

	_, txErr, err := debugger.RunTransaction(txBody, snapshot, header)
	require.NoError(t, txErr)
	require.NoError(t, err)
}

func TestDebugger_RunTransactionAgainstAccessNodeAtBlockIDWithFileCache(t *testing.T) {

	host := "access.mainnet.nodes.onflow.org:9000"

	conn, err := grpc.NewClient(
		host,
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	)
	require.NoError(t, err)
	defer conn.Close()

	executionDataClient := executiondata.NewExecutionDataAPIClient(conn)

	var blockHeight uint64 = 100_000_000
	
	blockID, err := flow.HexStringToIdentifier("e68a9a1fe849d1be80e4c5e414f53e3b59a170b88785e0b22be077ae9c3bbd29")
	require.NoError(t, err)

	accessClient := access.NewAccessAPIClient(conn)
	header, err := debug.GetAccessAPIBlockHeader(
		accessClient,
		context.Background(),
		blockID,
	)
	require.NoError(t, err)

	testCacheFile := "test.cache"
	defer os.Remove(testCacheFile)

	cache := debug.NewFileRegisterCache(testCacheFile)

	snapshot, err := debug.NewExecutionDataStorageSnapshot(executionDataClient, cache, blockHeight)
	require.NoError(t, err)

	defer snapshot.Close()

	chain := flow.Mainnet.Chain()
	logger := zerolog.New(os.Stdout).With().Logger()
	debugger := debug.NewRemoteDebugger(chain, logger)

	txBody := getTransaction(chain)

	// the first run will cache the results
	_, txErr, err := debugger.RunTransaction(txBody, snapshot, header)
	require.NoError(t, txErr)
	require.NoError(t, err)

	// the second run should only use the cache.
	_, txErr, err = debugger.RunTransaction(txBody, snapshot, header)
	require.NoError(t, txErr)
	require.NoError(t, err)
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GetAccessAPIBlockHeader

func GetAccessAPIBlockHeader(
	client access.AccessAPIClient,
	ctx context.Context,
	blockID flow.Identifier,
) (
	*flow.Header,
	error,
)

func GetExecutionAPIBlockHeader

func GetExecutionAPIBlockHeader(
	client execution.ExecutionAPIClient,
	ctx context.Context,
	blockID flow.Identifier,
) (
	*flow.Header,
	error,
)

func GetHeapAllocsBytes added in v0.27.0

func GetHeapAllocsBytes() uint64

GetHeapAllocsBytes returns the value of /gc/heap/allocs:bytes.

Types

type ExecutionDataStorageSnapshot

type ExecutionDataStorageSnapshot struct {
	Client      executiondata.ExecutionDataAPIClient
	Cache       RegisterCache
	BlockHeight uint64
}

ExecutionDataStorageSnapshot provides a storage snapshot connected to an access node to read the registers (via its execution data API).

func NewExecutionDataStorageSnapshot

func NewExecutionDataStorageSnapshot(
	client executiondata.ExecutionDataAPIClient,
	cache RegisterCache,
	blockHeight uint64,
) (
	*ExecutionDataStorageSnapshot,
	error,
)

func (*ExecutionDataStorageSnapshot) Close

func (snapshot *ExecutionDataStorageSnapshot) Close() error

func (*ExecutionDataStorageSnapshot) Get

type ExecutionNodeStorageSnapshot

type ExecutionNodeStorageSnapshot struct {
	Client  execution.ExecutionAPIClient
	Cache   RegisterCache
	BlockID flow.Identifier
}

ExecutionNodeStorageSnapshot provides a storage snapshot connected to an execution node to read the registers.

func NewExecutionNodeStorageSnapshot

func NewExecutionNodeStorageSnapshot(
	client execution.ExecutionAPIClient,
	cache RegisterCache,
	blockID flow.Identifier,
) (
	*ExecutionNodeStorageSnapshot,
	error,
)

func (*ExecutionNodeStorageSnapshot) Close

func (snapshot *ExecutionNodeStorageSnapshot) Close() error

func (*ExecutionNodeStorageSnapshot) Get

type FileRegisterCache

type FileRegisterCache struct {
	// contains filtered or unexported fields
}

func NewFileRegisterCache

func NewFileRegisterCache(filePath string) *FileRegisterCache

func (*FileRegisterCache) Get

func (f *FileRegisterCache) Get(owner, key string) ([]byte, bool)

func (*FileRegisterCache) Persist

func (c *FileRegisterCache) Persist() error

func (*FileRegisterCache) Set

func (f *FileRegisterCache) Set(owner, key string, value []byte)

type InMemoryRegisterCache

type InMemoryRegisterCache struct {
	// contains filtered or unexported fields
}

func NewInMemoryRegisterCache

func NewInMemoryRegisterCache() *InMemoryRegisterCache

func (*InMemoryRegisterCache) Get

func (c *InMemoryRegisterCache) Get(owner, key string) ([]byte, bool)

func (*InMemoryRegisterCache) Persist

func (c *InMemoryRegisterCache) Persist() error

func (*InMemoryRegisterCache) Set

func (c *InMemoryRegisterCache) Set(owner, key string, value []byte)

type RegisterCache

type RegisterCache interface {
	Get(owner, key string) (value []byte, found bool)
	Set(owner, key string, value []byte)
	Persist() error
}

type RemoteDebugger added in v0.23.1

type RemoteDebugger struct {
	// contains filtered or unexported fields
}

func NewRemoteDebugger added in v0.23.1

func NewRemoteDebugger(
	chain flow.Chain,
	logger zerolog.Logger,
) *RemoteDebugger

NewRemoteDebugger creates a new remote debugger. NOTE: Make sure to use the same version of flow-go as the network you are collecting registers from, otherwise the execution might differ from the way it runs on the network

func (*RemoteDebugger) RunScript added in v0.23.1

func (d *RemoteDebugger) RunScript(
	code []byte,
	arguments [][]byte,
	snapshot StorageSnapshot,
	blockHeader *flow.Header,
) (
	value cadence.Value,
	scriptError error,
	processError error,
)

RunScript runs the script using the given storage snapshot.

func (*RemoteDebugger) RunTransaction added in v0.23.1

func (d *RemoteDebugger) RunTransaction(
	txBody *flow.TransactionBody,
	snapshot StorageSnapshot,
	blockHeader *flow.Header,
) (
	resultSnapshot *snapshot.ExecutionSnapshot,
	txErr error,
	processError error,
)

RunTransaction runs the transaction using the given storage snapshot.

type StorageSnapshot

type StorageSnapshot interface {
	snapshot.StorageSnapshot
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL