diff --git a/engine/execution/block_result.go b/engine/execution/block_result.go index e10a6aeea42..1cfbb9bc0d4 100644 --- a/engine/execution/block_result.go +++ b/engine/execution/block_result.go @@ -1,6 +1,8 @@ package execution import ( + "fmt" + "github.com/onflow/flow-go/fvm/storage/snapshot" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/executiondatasync/execution_data" @@ -31,7 +33,7 @@ func (er *BlockExecutionResult) Size() int { } func (er *BlockExecutionResult) CollectionExecutionResultAt(colIndex int) *CollectionExecutionResult { - if colIndex < 0 && colIndex > len(er.collectionExecutionResults) { + if colIndex < 0 || colIndex > len(er.collectionExecutionResults) { return nil } return &er.collectionExecutionResults[colIndex] @@ -185,6 +187,12 @@ func (ar *BlockAttestationResult) ChunkAt(index int) *flow.Chunk { execRes := ar.collectionExecutionResults[index] attestRes := ar.collectionAttestationResults[index] + if execRes.executionSnapshot == nil { + // This should never happen + // In case it does, attach additional information to the error message + panic(fmt.Sprintf("execution snapshot is nil. Block ID: %s, EndState: %s", ar.Block.ID(), attestRes.endStateCommit)) + } + return flow.NewChunk( ar.Block.ID(), index, diff --git a/fvm/environment/env.go b/fvm/environment/env.go index 58cad2a9e86..b276e3645b4 100644 --- a/fvm/environment/env.go +++ b/fvm/environment/env.go @@ -4,12 +4,9 @@ import ( "github.com/onflow/cadence" "github.com/onflow/cadence/runtime" "github.com/rs/zerolog" - otelTrace "go.opentelemetry.io/otel/trace" reusableRuntime "github.com/onflow/flow-go/fvm/runtime" - "github.com/onflow/flow-go/fvm/tracing" "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module/trace" ) // Environment implements the accounts business logic and exposes cadence @@ -17,11 +14,7 @@ import ( type Environment interface { runtime.Interface - // Tracer - StartChildSpan( - name trace.SpanName, - options ...otelTrace.SpanStartOption, - ) tracing.TracerSpan + Tracer Meter diff --git a/fvm/environment/tracer.go b/fvm/environment/tracer.go new file mode 100644 index 00000000000..f276286475e --- /dev/null +++ b/fvm/environment/tracer.go @@ -0,0 +1,16 @@ +package environment + +import ( + otelTrace "go.opentelemetry.io/otel/trace" + + "github.com/onflow/flow-go/fvm/tracing" + "github.com/onflow/flow-go/module/trace" +) + +// Tracer captures traces +type Tracer interface { + StartChildSpan( + name trace.SpanName, + options ...otelTrace.SpanStartOption, + ) tracing.TracerSpan +} diff --git a/fvm/evm/backends/wrappedEnv.go b/fvm/evm/backends/wrappedEnv.go index d22aabc191c..a996dff05da 100644 --- a/fvm/evm/backends/wrappedEnv.go +++ b/fvm/evm/backends/wrappedEnv.go @@ -5,12 +5,15 @@ import ( "github.com/onflow/cadence" "github.com/onflow/cadence/runtime" "github.com/onflow/cadence/runtime/common" + otelTrace "go.opentelemetry.io/otel/trace" "github.com/onflow/flow-go/fvm/environment" "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/fvm/meter" + "github.com/onflow/flow-go/fvm/tracing" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/trace" ) // WrappedEnvironment wraps an FVM environment @@ -144,6 +147,13 @@ func (we *WrappedEnvironment) GenerateUUID() (uint64, error) { return uuid, handleEnvironmentError(err) } +func (we *WrappedEnvironment) StartChildSpan( + name trace.SpanName, + options ...otelTrace.SpanStartOption, +) tracing.TracerSpan { + return we.env.StartChildSpan(name, options...) +} + func handleEnvironmentError(err error) error { if err == nil { return nil diff --git a/fvm/evm/emulator/emulator.go b/fvm/evm/emulator/emulator.go index 63229229019..afe9a7f7366 100644 --- a/fvm/evm/emulator/emulator.go +++ b/fvm/evm/emulator/emulator.go @@ -115,10 +115,7 @@ func (bl *BlockView) DirectCall(call *types.DirectCall) (*types.Result, error) { if err != nil { return nil, err } - txHash, err := call.Hash() - if err != nil { - return nil, err - } + txHash := call.Hash() switch call.SubType { case types.DepositCallSubType: return proc.mintTo(call, txHash) @@ -256,9 +253,21 @@ func (bl *BlockView) DryRunTransaction( // return without commiting the state txResult, err := proc.run(msg, tx.Hash(), 0, tx.Type()) if txResult.Successful() { + // As mentioned in https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md#specification + // Define "all but one 64th" of N as N - floor(N / 64). + // If a call asks for more gas than the maximum allowed amount + // (i.e. the total amount of gas remaining in the parent after subtracting + // the gas cost of the call and memory expansion), do not return an OOG error; + // instead, if a call asks for more gas than all but one 64th of the maximum + // allowed amount, call with all but one 64th of the maximum allowed amount of + // gas (this is equivalent to a version of EIP-901 plus EIP-1142). + // CREATE only provides all but one 64th of the parent gas to the child call. + txResult.GasConsumed = AddOne64th(txResult.GasConsumed) + // Adding `gethParams.SstoreSentryGasEIP2200` is needed for this condition: // https://github.com/onflow/go-ethereum/blob/master/core/vm/operations_acl.go#L29-L32 txResult.GasConsumed += gethParams.SstoreSentryGasEIP2200 + // Take into account any gas refunds, which are calculated only after // transaction execution. txResult.GasConsumed += txResult.GasRefund @@ -587,3 +596,8 @@ func (proc *procedure) run( } return &res, nil } + +func AddOne64th(n uint64) uint64 { + // NOTE: Go's integer division floors, but that is desirable here + return n + (n / 64) +} diff --git a/fvm/evm/emulator/emulator_test.go b/fvm/evm/emulator/emulator_test.go index 62dbb248b62..8f1cb1bbba1 100644 --- a/fvm/evm/emulator/emulator_test.go +++ b/fvm/evm/emulator/emulator_test.go @@ -60,9 +60,7 @@ func TestNativeTokenBridging(t *testing.T) { res, err := blk.DirectCall(call) require.NoError(t, err) require.Equal(t, defaultCtx.DirectCallBaseGasUsage, res.GasConsumed) - expectedHash, err := call.Hash() - require.NoError(t, err) - require.Equal(t, expectedHash, res.TxHash) + require.Equal(t, call.Hash(), res.TxHash) nonce += 1 }) }) @@ -94,9 +92,7 @@ func TestNativeTokenBridging(t *testing.T) { res, err := blk.DirectCall(call) require.NoError(t, err) require.Equal(t, defaultCtx.DirectCallBaseGasUsage, res.GasConsumed) - expectedHash, err := call.Hash() - require.NoError(t, err) - require.Equal(t, expectedHash, res.TxHash) + require.Equal(t, call.Hash(), res.TxHash) nonce += 1 }) }) @@ -155,9 +151,7 @@ func TestContractInteraction(t *testing.T) { require.NoError(t, err) require.NotNil(t, res.DeployedContractAddress) contractAddr = *res.DeployedContractAddress - expectedHash, err := call.Hash() - require.NoError(t, err) - require.Equal(t, expectedHash, res.TxHash) + require.Equal(t, call.Hash(), res.TxHash) nonce += 1 }) RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index 3346238a30a..971e7b99fdb 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -25,6 +25,7 @@ import ( "github.com/onflow/flow-go/fvm/crypto" envMock "github.com/onflow/flow-go/fvm/environment/mock" "github.com/onflow/flow-go/fvm/evm" + "github.com/onflow/flow-go/fvm/evm/emulator" "github.com/onflow/flow-go/fvm/evm/stdlib" "github.com/onflow/flow-go/fvm/evm/testutils" . "github.com/onflow/flow-go/fvm/evm/testutils" @@ -1534,9 +1535,10 @@ func TestDryRun(t *testing.T) { // Make sure that gas consumed from `EVM.dryRun` is bigger // than the actual gas consumption of the equivalent // `EVM.run`. + totalGas := emulator.AddOne64th(res.GasConsumed) + gethParams.SstoreSentryGasEIP2200 require.Equal( t, - res.GasConsumed+gethParams.SstoreSentryGasEIP2200, + totalGas, dryRunResult.GasConsumed, ) }) @@ -1667,9 +1669,10 @@ func TestDryRun(t *testing.T) { // Make sure that gas consumed from `EVM.dryRun` is bigger // than the actual gas consumption of the equivalent // `EVM.run`. + totalGas := emulator.AddOne64th(res.GasConsumed) + gethParams.SstoreSentryGasEIP2200 require.Equal( t, - res.GasConsumed+gethParams.SstoreSentryGasEIP2200, + totalGas, dryRunResult.GasConsumed, ) }) @@ -1798,9 +1801,10 @@ func TestDryRun(t *testing.T) { // Make sure that gas consumed from `EVM.dryRun` is bigger // than the actual gas consumption of the equivalent // `EVM.run`. + totalGas := emulator.AddOne64th(res.GasConsumed) + gethParams.SstoreSentryGasEIP2200 + gethParams.SstoreClearsScheduleRefundEIP3529 require.Equal( t, - res.GasConsumed+gethParams.SstoreSentryGasEIP2200+gethParams.SstoreClearsScheduleRefundEIP3529, + totalGas, dryRunResult.GasConsumed, ) }) diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index 2a0a048ccfe..eb1dbd7822d 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -6,6 +6,7 @@ import ( "github.com/onflow/cadence/runtime/common" gethCommon "github.com/onflow/go-ethereum/common" gethTypes "github.com/onflow/go-ethereum/core/types" + "go.opentelemetry.io/otel/attribute" "github.com/onflow/flow-go/fvm/environment" fvmErrors "github.com/onflow/flow-go/fvm/errors" @@ -13,6 +14,7 @@ import ( "github.com/onflow/flow-go/fvm/evm/handler/coa" "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/trace" ) // ContractHandler is responsible for triggering calls to emulator, metering, @@ -65,6 +67,8 @@ func NewContractHandler( // DeployCOA deploys a cadence-owned-account and returns the address func (h *ContractHandler) DeployCOA(uuid uint64) types.Address { + defer h.backend.StartChildSpan(trace.FVMEVMDeployCOA).End() + res, err := h.deployCOA(uuid) panicOnErrorOrInvalidOrFailedState(res, err) return *res.DeployedContractAddress @@ -119,6 +123,8 @@ func (h *ContractHandler) RunOrPanic(rlpEncodedTx []byte, coinbase types.Address // Run tries to run an rlpencoded evm transaction and // collects the gas fees and pay it to the coinbase address provided. func (h *ContractHandler) Run(rlpEncodedTx []byte, coinbase types.Address) *types.ResultSummary { + defer h.backend.StartChildSpan(trace.FVMEVMRun).End() + res, err := h.run(rlpEncodedTx, coinbase) panicOnError(err) return res.ResultSummary() @@ -129,6 +135,10 @@ func (h *ContractHandler) Run(rlpEncodedTx []byte, coinbase types.Address) *type // All transactions provided in the batch are included in a single block, // except for invalid transactions func (h *ContractHandler) BatchRun(rlpEncodedTxs [][]byte, coinbase types.Address) []*types.ResultSummary { + span := h.backend.StartChildSpan(trace.FVMEVMBatchRun) + span.SetAttributes(attribute.Int("tx_counts", len(rlpEncodedTxs))) + defer span.End() + res, err := h.batchRun(rlpEncodedTxs, coinbase) panicOnError(err) @@ -341,6 +351,8 @@ func (h *ContractHandler) DryRun( rlpEncodedTx []byte, from types.Address, ) *types.ResultSummary { + defer h.backend.StartChildSpan(trace.FVMEVMDryRun).End() + res, err := h.dryRun(rlpEncodedTx, from) panicOnError(err) return res.ResultSummary() @@ -667,6 +679,8 @@ func (a *Account) codeHash() ([]byte, error) { // Deposit deposits the token from the given vault into the flow evm main vault // and update the account balance with the new amount func (a *Account) Deposit(v *types.FLOWTokenVault) { + defer a.fch.backend.StartChildSpan(trace.FVMEVMDeposit).End() + res, err := a.deposit(v) panicOnErrorOrInvalidOrFailedState(res, err) } @@ -692,6 +706,8 @@ func (a *Account) deposit(v *types.FLOWTokenVault) (*types.Result, error) { // Withdraw deducts the balance from the account and // withdraw and return flow token from the Flex main vault. func (a *Account) Withdraw(b types.Balance) *types.FLOWTokenVault { + defer a.fch.backend.StartChildSpan(trace.FVMEVMWithdraw).End() + res, err := a.withdraw(b) panicOnErrorOrInvalidOrFailedState(res, err) @@ -745,6 +761,8 @@ func (a *Account) transfer(to types.Address, balance types.Balance) (*types.Resu // contained in the result summary as data and // the contract data is not controlled by the caller accounts func (a *Account) Deploy(code types.Code, gaslimit types.GasLimit, balance types.Balance) *types.ResultSummary { + defer a.fch.backend.StartChildSpan(trace.FVMEVMDeploy).End() + res, err := a.deploy(code, gaslimit, balance) panicOnError(err) return res.ResultSummary() @@ -771,6 +789,8 @@ func (a *Account) deploy(code types.Code, gaslimit types.GasLimit, balance types // given it doesn't goes beyond what Flow transaction allows. // the balance would be deducted from the OFA account and would be transferred to the target address func (a *Account) Call(to types.Address, data types.Data, gaslimit types.GasLimit, balance types.Balance) *types.ResultSummary { + defer a.fch.backend.StartChildSpan(trace.FVMEVMCall).End() + res, err := a.call(to, data, gaslimit, balance) panicOnError(err) return res.ResultSummary() diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 829ba593309..6064a54a475 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -33,6 +33,7 @@ import ( "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/fvm/systemcontracts" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/trace" ) // TODO add test for fatal errors @@ -1257,6 +1258,57 @@ func TestHandler_TransactionRun(t *testing.T) { }) }) }) + + t.Run("test - open tracing", func(t *testing.T) { + t.Parallel() + + testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { + testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { + + tx := gethTypes.NewTransaction( + uint64(1), + gethCommon.Address{1, 2}, + big.NewInt(13), + uint64(0), + big.NewInt(1000), + []byte{}, + ) + + rlpTx, err := tx.MarshalBinary() + require.NoError(t, err) + + handler := SetupHandler(t, backend, rootAddr) + + backend.ExpectedSpan(t, trace.FVMEVMDryRun) + handler.DryRun(rlpTx, types.EmptyAddress) + + backend.ExpectedSpan(t, trace.FVMEVMRun) + handler.Run(rlpTx, types.EmptyAddress) + + backend.ExpectedSpan(t, trace.FVMEVMBatchRun) + handler.BatchRun([][]byte{rlpTx}, types.EmptyAddress) + + backend.ExpectedSpan(t, trace.FVMEVMDeployCOA) + coa := handler.DeployCOA(1) + + acc := handler.AccountByAddress(coa, true) + + backend.ExpectedSpan(t, trace.FVMEVMCall) + acc.Call(types.EmptyAddress, nil, 1000, types.EmptyBalance) + + backend.ExpectedSpan(t, trace.FVMEVMDeposit) + acc.Deposit(types.NewFlowTokenVault(types.EmptyBalance)) + + backend.ExpectedSpan(t, trace.FVMEVMWithdraw) + acc.Withdraw(types.EmptyBalance) + + backend.ExpectedSpan(t, trace.FVMEVMDeploy) + acc.Deploy(nil, 1, types.EmptyBalance) + }) + }) + }) + }) } // returns true if error passes the checks diff --git a/fvm/evm/testutils/backend.go b/fvm/evm/testutils/backend.go index 472d38201f4..6d5616a488e 100644 --- a/fvm/evm/testutils/backend.go +++ b/fvm/evm/testutils/backend.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/onflow/cadence/runtime/stdlib" + otelTrace "go.opentelemetry.io/otel/trace" "github.com/onflow/atree" "github.com/onflow/cadence" @@ -19,7 +20,9 @@ import ( "github.com/onflow/flow-go/fvm/environment" "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/fvm/meter" + "github.com/onflow/flow-go/fvm/tracing" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/trace" ) var TestFlowEVMRootAddress = flow.BytesToAddress([]byte("FlowEVM")) @@ -40,6 +43,7 @@ func RunWithTestBackend(t testing.TB, f func(*TestBackend)) { TestBlockInfo: getSimpleBlockStore(), TestRandomGenerator: getSimpleRandomGenerator(), TestContractFunctionInvoker: &TestContractFunctionInvoker{}, + TestTracer: &TestTracer{}, } f(tb) } @@ -185,6 +189,7 @@ type TestBackend struct { *TestRandomGenerator *TestContractFunctionInvoker *testUUIDGenerator + *TestTracer } var _ types.Backend = &TestBackend{} @@ -497,3 +502,30 @@ func (t *testUUIDGenerator) GenerateUUID() (uint64, error) { } return t.generateUUID() } + +type TestTracer struct { + StartChildSpanFunc func(trace.SpanName, ...otelTrace.SpanStartOption) tracing.TracerSpan +} + +var _ environment.Tracer = &TestTracer{} + +func (tt *TestTracer) StartChildSpan( + name trace.SpanName, + options ...otelTrace.SpanStartOption, +) tracing.TracerSpan { + // if not set we use noop tracer + if tt.StartChildSpanFunc == nil { + return tracing.NewMockTracerSpan() + } + return tt.StartChildSpanFunc(name, options...) +} + +func (tt *TestTracer) ExpectedSpan(t *testing.T, expected trace.SpanName) { + tt.StartChildSpanFunc = func( + sn trace.SpanName, + sso ...otelTrace.SpanStartOption, + ) tracing.TracerSpan { + require.Equal(t, expected, sn) + return tracing.NewMockTracerSpan() + } +} diff --git a/fvm/evm/types/backend.go b/fvm/evm/types/backend.go index 2a281f94a6b..23fd2c5e377 100644 --- a/fvm/evm/types/backend.go +++ b/fvm/evm/types/backend.go @@ -15,4 +15,5 @@ type Backend interface { environment.RandomGenerator environment.ContractFunctionInvoker environment.UUIDGenerator + environment.Tracer } diff --git a/fvm/evm/types/call.go b/fvm/evm/types/call.go index b766b0f641d..e10cab278f3 100644 --- a/fvm/evm/types/call.go +++ b/fvm/evm/types/call.go @@ -73,10 +73,12 @@ func (dc *DirectCall) Encode() ([]byte, error) { } // Hash computes the hash of a direct call -func (dc *DirectCall) Hash() (gethCommon.Hash, error) { +func (dc *DirectCall) Hash() gethCommon.Hash { // we use geth transaction hash calculation since direct call hash is included in the // block transaction hashes, and thus observed as any other transaction - return dc.Transaction().Hash(), nil + // We construct this Legacy tx type so the external 3rd party tools + // don't have to support a new type for the purpose of hash computation + return dc.Transaction().Hash() } // Message constructs a core.Message from the direct call @@ -98,6 +100,11 @@ func (dc *DirectCall) Message() *gethCore.Message { // Transaction constructs a geth.Transaction from the direct call func (dc *DirectCall) Transaction() *gethTypes.Transaction { + // Since a direct call doesn't have a valid siganture + // and we need to somehow include the From feild for the purpose + // of hash calculation. we define the canonical format by + // using the FROM bytes to set the bytes for the R part of the tx (big endian), + // S captures the subtype of transaction and V is set to DirectCallTxType (255). return gethTypes.NewTx(&gethTypes.LegacyTx{ GasPrice: big.NewInt(0), Gas: dc.GasLimit, @@ -105,6 +112,9 @@ func (dc *DirectCall) Transaction() *gethTypes.Transaction { Value: dc.Value, Data: dc.Data, Nonce: dc.Nonce, + R: new(big.Int).SetBytes(dc.From.Bytes()), + S: new(big.Int).SetBytes([]byte{dc.SubType}), + V: new(big.Int).SetBytes([]byte{DirectCallTxType}), }) } diff --git a/fvm/evm/types/call_test.go b/fvm/evm/types/call_test.go index 54880558aa9..8c57600968f 100644 --- a/fvm/evm/types/call_test.go +++ b/fvm/evm/types/call_test.go @@ -1,9 +1,13 @@ package types import ( + "bytes" + "io" "math/big" "testing" + gethTypes "github.com/onflow/go-ethereum/core/types" + "github.com/onflow/go-ethereum/rlp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -20,20 +24,43 @@ func TestDirectCall(t *testing.T) { } t.Run("calculate hash", func(t *testing.T) { - h, err := dc.Hash() + h := dc.Hash() + assert.Equal(t, "0xed76124cc3c59f13e1113f5c380e2a67dab9bf616afc645073d2491fe3aecb62", h.Hex()) + + // the hash should stay the same after RLP encoding and decoding + var b bytes.Buffer + writer := io.Writer(&b) + err := dc.Transaction().EncodeRLP(writer) + require.NoError(t, err) + + reconstructedTx := &gethTypes.Transaction{} + err = reconstructedTx.DecodeRLP(rlp.NewStream(io.Reader(&b), 1000)) require.NoError(t, err) - assert.Equal(t, "0xe28ff08eca95608646d765e3007b3710f7f2a8ac5e297431da1962c33487e7b6", h.Hex()) + + h = reconstructedTx.Hash() + assert.Equal(t, "0xed76124cc3c59f13e1113f5c380e2a67dab9bf616afc645073d2491fe3aecb62", h.Hex()) + }) + + t.Run("same content except `from` should result in different hashes", func(t *testing.T) { + h := dc.Hash() + dc.From = Address{0x4, 0x5} + h2 := dc.Hash() + assert.NotEqual(t, h2.Hex(), h.Hex()) }) t.Run("construct transaction", func(t *testing.T) { tx := dc.Transaction() - h, err := dc.Hash() - require.NoError(t, err) + h := dc.Hash() assert.Equal(t, dc.Value, tx.Value()) assert.Equal(t, dc.To.ToCommon(), *tx.To()) assert.Equal(t, h, tx.Hash()) assert.Equal(t, dc.GasLimit, tx.Gas()) assert.Equal(t, dc.Data, tx.Data()) assert.Equal(t, uint64(0), tx.Nonce()) // no nonce exists for direct call + + v, r, s := tx.RawSignatureValues() + require.Equal(t, dc.From.Bytes(), r.Bytes()) + require.Equal(t, []byte{dc.SubType}, s.Bytes()) + require.Equal(t, []byte{DirectCallTxType}, v.Bytes()) }) } diff --git a/module/trace/constants.go b/module/trace/constants.go index 55f4d94079e..43287e45e93 100644 --- a/module/trace/constants.go +++ b/module/trace/constants.go @@ -195,5 +195,14 @@ const ( FVMEnvRemoveAccountContractCode SpanName = "fvm.env.removeAccountContractCode" FVMEnvGetSigningAccounts SpanName = "fvm.env.getSigningAccounts" + FVMEVMDeployCOA SpanName = "fvm.evm.deployCOA" + FVMEVMRun SpanName = "fvm.evm.run" + FVMEVMDryRun SpanName = "fvm.evm.dryRun" + FVMEVMBatchRun SpanName = "fvm.evm.batchRun" + FVMEVMDeposit SpanName = "fvm.evm.deposit" + FVMEVMWithdraw SpanName = "fvm.evm.withdraw" + FVMEVMDeploy SpanName = "fvm.evm.deploy" + FVMEVMCall SpanName = "fvm.evm.call" + FVMCadenceTrace SpanName = "fvm.cadence.trace" ) diff --git a/tools/move-checkpoint.sh b/tools/move-checkpoint.sh new file mode 100755 index 00000000000..0b1026206e7 --- /dev/null +++ b/tools/move-checkpoint.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +# Exit immediately if a command exits with a non-zero status +set -e + +# Check if exactly two arguments are provided +usage() { + echo "Description: move checkpoint files from one directory to another" + echo "Usage: $0 source_file destination_file [--run]" + echo "Example: $0 /var/flow/from-folder/checkpoint.000010 /var/flow/to-folder/root.checkpoint [--run]" + echo "The above command will preview the checkpoint files to be moved including its 17 subfiles to the destination folder and rename them." + echo "Preview mode is default. Use --run to actually move the files." + exit 1 +} + +# Check if at least two arguments are provided +if [ "$#" -lt 2 ] || [ "$#" -gt 3 ]; then + usage +fi + +# Assign arguments to variables +source_file_pattern=$1 +destination_file_base=$2 +run_mode=false + +# Check for run mode +if [ "$#" -eq 3 ] && [ "$3" == "--run" ]; then + run_mode=true +elif [ "$#" -eq 3 ]; then + usage +fi + +# Extract the basename from the source file pattern +source_base=$(basename "$source_file_pattern") + +# Extract the directory and base name from the destination file base +source_directory=$(dirname "$source_file_pattern") +destination_directory=$(dirname "$destination_file_base") +destination_base=$(basename "$destination_file_base") + +# Create the destination directory if it doesn't exist +mkdir -p "$destination_directory" + +# Define the expected files +expected_files=( + "$source_base" + "$source_base.001" + "$source_base.002" + "$source_base.003" + "$source_base.004" + "$source_base.005" + "$source_base.006" + "$source_base.007" + "$source_base.008" + "$source_base.009" + "$source_base.010" + "$source_base.011" + "$source_base.012" + "$source_base.013" + "$source_base.014" + "$source_base.015" + "$source_base.016" +) + +# Check if all expected files are present +missing_files=() +for expected_file in "${expected_files[@]}"; do + full_expected_file="$source_directory/$expected_file" + if [ ! -f "$full_expected_file" ]; then + missing_files+=("$full_expected_file") + fi +done + +if [ "${#missing_files[@]}" -ne 0 ]; then + echo "Error: The following expected files are missing:" + for file in "${missing_files[@]}"; do + echo " $file" + done + exit 1 +fi + +# Loop through the expected files and preview/move them to the destination directory +for file in "${expected_files[@]}"; do + full_source_file="$source_directory/$file" + if [ -f "$full_source_file" ]; then + # Get the file extension (if any) + extension="${file#$source_base}" + # Construct the destination file name + destination_file="$destination_directory/$destination_base$extension" + # Preview or move the file + if [ "$run_mode" = true ]; then + echo "Moving: $(realpath "$full_source_file") -> $(realpath "$destination_file")" + mv "$full_source_file" "$destination_file" + else + echo "Preview: $(realpath "$full_source_file") -> $(realpath "$destination_file")" + fi + fi +done + + +if [ "$run_mode" = true ]; then + echo "Checkpoint files have been moved successfully." +else + echo "Preview complete. No files have been moved. add --run flag to move the files." +fi