Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Execute FLIX scripts/transactions #1125

Merged
merged 32 commits into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8040de5
Get flix by name
chasefleming Jul 6, 2023
207cbe7
Use flixkit library to execute flix scripts
chasefleming Jul 13, 2023
d77b75e
Remove print
chasefleming Jul 13, 2023
1f2abb5
Update flixkit to new syntax
chasefleming Jul 17, 2023
b42895e
Break out flix fetching and command parsing
chasefleming Jul 17, 2023
97e556e
Add context
chasefleming Jul 18, 2023
42e29e0
Rename for clarity
chasefleming Jul 18, 2023
70c208d
Send flix transactions
chasefleming Jul 18, 2023
0c76357
Remove print
chasefleming Jul 18, 2023
9806fcd
Move to flix commands
chasefleming Jul 31, 2023
4fe6c41
Change flix syntax and use cobra
chasefleming Aug 1, 2023
906845d
Fix tests
chasefleming Aug 1, 2023
d4182a2
Consolidate with flags approach
chasefleming Aug 2, 2023
1ba0874
Consolidate commands and detect type
chasefleming Aug 2, 2023
68d764c
Use logger
chasefleming Aug 2, 2023
3f6c803
Change name
chasefleming Aug 2, 2023
0535db3
Update internal/flix/flix.go
chasefleming Aug 7, 2023
3c0fe87
Update internal/flix/flix.go
chasefleming Aug 7, 2023
00c2866
Clean up unnecessary func
chasefleming Aug 7, 2023
3e09369
Move to super commands
chasefleming Aug 7, 2023
f19c8b9
Use switch instead
chasefleming Aug 7, 2023
fe64ba9
Remove nesting
chasefleming Aug 7, 2023
7d4e888
Use versioned package
chasefleming Aug 7, 2023
f35bcff
Merge master
chasefleming Aug 7, 2023
760cef5
Change flix query type checks
chasefleming Aug 7, 2023
5e57ad2
Add license header
chasefleming Aug 7, 2023
d6de991
Remove unnecessary check
chasefleming Aug 7, 2023
ceb6cc6
Improve query checks
chasefleming Aug 7, 2023
972c23e
Return error
chasefleming Aug 7, 2023
61dbe6f
return errors
chasefleming Aug 7, 2023
7021670
Merge branch 'master' into chasefleming/flix-integratin
chasefleming Aug 9, 2023
b5e363e
Remove unnecessary group and refactor
chasefleming Aug 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/flow/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func main() {
// super commands
super.SetupCommand.AddToParent(cmd)
super.DevCommand.AddToParent(cmd)
super.FlixCommand.AddToParent(cmd)

// structured commands
cmd.AddCommand(settings.Cmd)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ require (
github.com/multiformats/go-varint v0.0.7 // indirect
github.com/onflow/atree v0.6.0 // indirect
github.com/onflow/cadence-tools/lint v0.11.1 // indirect
github.com/onflow/flixkit-go v0.1.0 // indirect
github.com/onflow/flow-archive v1.3.4-0.20230503192214-9e81e82d4dcc // indirect
github.com/onflow/flow-core-contracts/lib/go/contracts v1.2.4-0.20230703193002-53362441b57d // indirect
github.com/onflow/flow-ft/lib/go/contracts v0.7.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,8 @@ github.com/onflow/cadence-tools/test v0.10.0 h1:tcNjOFXl7ZuuvgQ3uS36ENd8l3RkSBv4
github.com/onflow/cadence-tools/test v0.10.0/go.mod h1:eIvZOzFdi4JR2x6ekQfN8veQy04ZlZYbeeiQF/oZ0zM=
github.com/onflow/fcl-dev-wallet v0.7.2 h1:ZwhpzDakcZn9rHiIr52LwkdPh1MpUPbxA/PwCVaZ2SA=
github.com/onflow/fcl-dev-wallet v0.7.2/go.mod h1:kc42jkiuoPJmxMRFjfbRO9XvnR/3XLheaOerxVMDTiw=
github.com/onflow/flixkit-go v0.1.0 h1:3nH+1z+D+0YlmEJk9zYtvD8p4eUl2wJtiV6ZCgM7vYE=
github.com/onflow/flixkit-go v0.1.0/go.mod h1:gPffHQ6jDyuNtLG6W9C6FGvDZmcOb9CkvsJYKY0Opc4=
github.com/onflow/flow-archive v1.3.4-0.20230503192214-9e81e82d4dcc h1:C4ZniFeOv+pHlDLJdGc/4e3NklSjVuvaXKN47980gnY=
github.com/onflow/flow-archive v1.3.4-0.20230503192214-9e81e82d4dcc/go.mod h1:UPsvKk/37Atosif4wlBl3gsLbGJyGpdXYpXDsWtMVBE=
github.com/onflow/flow-core-contracts/lib/go/contracts v1.2.4-0.20230703193002-53362441b57d h1:B7PdhdUNkve5MVrekWDuQf84XsGBxNZ/D3x+QQ8XeVs=
Expand Down
21 changes: 13 additions & 8 deletions internal/scripts/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ import (
"github.com/onflow/flow-cli/internal/command"
)

type flagsScripts struct {
chasefleming marked this conversation as resolved.
Show resolved Hide resolved
type Flags struct {
ArgsJSON string `default:"" flag:"args-json" info:"arguments in JSON-Cadence format"`
BlockID string `default:"" flag:"block-id" info:"block ID to execute the script at"`
BlockHeight uint64 `default:"" flag:"block-height" info:"block height to execute the script at"`
}

var scriptFlags = flagsScripts{}
var flags = Flags{}

var executeCommand = &command.Command{
Cmd: &cobra.Command{
Expand All @@ -47,7 +47,7 @@ var executeCommand = &command.Command{
Example: `flow scripts execute script.cdc "Meow" "Woof"`,
Args: cobra.MinimumNArgs(1),
},
Flags: &scriptFlags,
Flags: &flags,
Run: execute,
}

Expand All @@ -65,11 +65,16 @@ func execute(
return nil, fmt.Errorf("error loading script file: %w", err)
}

var scriptArgs []cadence.Value
return SendScript(code, args[1:], filename, flow, flags)
}

func SendScript(code []byte, argsArr []string, location string, flow flowkit.Services, scriptFlags Flags) (command.Result, error) {
var cadenceArgs []cadence.Value
var err error
if scriptFlags.ArgsJSON != "" {
scriptArgs, err = arguments.ParseJSON(scriptFlags.ArgsJSON)
cadenceArgs, err = arguments.ParseJSON(scriptFlags.ArgsJSON)
} else {
scriptArgs, err = arguments.ParseWithoutType(args[1:], code, filename)
cadenceArgs, err = arguments.ParseWithoutType(argsArr, code, location)
}

if err != nil {
Expand All @@ -89,8 +94,8 @@ func execute(
context.Background(),
flowkit.Script{
Code: code,
Args: scriptArgs,
Location: filename,
Args: cadenceArgs,
Location: location,
},
query,
)
Expand Down
2 changes: 1 addition & 1 deletion internal/scripts/scripts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func Test_Execute(t *testing.T) {

t.Run("Fail parsing invalid JSON args", func(t *testing.T) {
inArgs := []string{tests.TestScriptSimple.Filename}
scriptFlags.ArgsJSON = "invalid"
flags.ArgsJSON = "invalid"

result, err := execute(inArgs, command.GlobalFlags{}, util.NoLogger, rw, srv.Mock)
assert.Nil(t, result)
Expand Down
163 changes: 163 additions & 0 deletions internal/super/flix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* Flow CLI
*
* Copyright 2019 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 super

import (
"context"
"encoding/hex"
"fmt"
"os"

"github.com/onflow/flixkit-go"

"github.com/onflow/flow-cli/flowkit/output"
"github.com/onflow/flow-cli/internal/command"
"github.com/onflow/flow-cli/internal/scripts"
"github.com/onflow/flow-cli/internal/transactions"

"github.com/onflow/flow-cli/flowkit"

"github.com/spf13/cobra"
)

type flixFlags struct {
ArgsJSON string `default:"" flag:"args-json" info:"arguments in JSON-Cadence format"`
BlockID string `default:"" flag:"block-id" info:"block ID to execute the script at"`
BlockHeight uint64 `default:"" flag:"block-height" info:"block height to execute the script at"`
Signer string `default:"" flag:"signer" info:"Account name from configuration used to sign the transaction as proposer, payer and suthorizer"`
Proposer string `default:"" flag:"proposer" info:"Account name from configuration used as proposer"`
Payer string `default:"" flag:"payer" info:"Account name from configuration used as payer"`
Authorizers []string `default:"" flag:"authorizer" info:"Name of a single or multiple comma-separated accounts used as authorizers from configuration"`
Include []string `default:"" flag:"include" info:"Fields to include in the output"`
Exclude []string `default:"" flag:"exclude" info:"Fields to exclude from the output (events)"`
GasLimit uint64 `default:"1000" flag:"gas-limit" info:"transaction gas limit"`
}

var flags = flixFlags{}

var FlixCommand = &command.Command{
Cmd: &cobra.Command{
Use: "flix <id | name | path>",
Short: "Execute FLIX template with a given id, name, or local filename",
Example: "flow flix multiply 2 3",
Args: cobra.ArbitraryArgs,
GroupID: "super",
},
Flags: &flags,
RunS: execute,
}

type flixQueryTypes string

const (
flixName flixQueryTypes = "name"
flixPath flixQueryTypes = "path"
flixId flixQueryTypes = "id"
)

func isHex(str string) bool {
if len(str) != 64 {
return false
}
_, err := hex.DecodeString(str)
return err == nil
}

func isPath(path string) bool {
_, err := os.Stat(path)
return err == nil
}

func getType(s string) flixQueryTypes {
switch {
case isPath(s):
return flixPath
case isHex(s):
return flixId
default:
return flixName
}
}

func execute(
args []string,
_ command.GlobalFlags,
logger output.Logger,
flow flowkit.Services,
state *flowkit.State,
) (result command.Result, err error) {
flixService := flixkit.NewFlixService(&flixkit.Config{})
ctx := context.Background()
var template *flixkit.FlowInteractionTemplate
flixQuery := args[0]

switch getType(flixQuery) {
case flixId:
template, err = flixService.GetFlixByID(ctx, flixQuery)
if err != nil {
return nil, fmt.Errorf("could not find flix with id %s: %w", flixQuery, err)
}

case flixName:
template, err = flixService.GetFlix(ctx, flixQuery)
if err != nil {
return nil, fmt.Errorf("could not find flix with name %s: %w", flixQuery, err)
}

case flixPath:
file, err := os.ReadFile(flixQuery)
if err != nil {
return nil, fmt.Errorf("could not read flix file %s: %w", flixQuery, err)
}
template, err = flixkit.ParseFlix(string(file))
if err != nil {
return nil, fmt.Errorf("could not parse flix from file %s: %w", flixQuery, err)
}

default:
return nil, fmt.Errorf("invalid flix query type: %s", flixQuery)
}

cadenceWithImportsReplaced, err := template.GetAndReplaceCadenceImports(flow.Network().Name)
if err != nil {
logger.Error("could not replace imports")
return nil, err
}

if template.IsScript() {
scriptsFlags := scripts.Flags{
ArgsJSON: flags.ArgsJSON,
BlockID: flags.BlockID,
BlockHeight: flags.BlockHeight,
}
return scripts.SendScript([]byte(cadenceWithImportsReplaced), args[1:], "", flow, scriptsFlags)
}

transactionFlags := transactions.Flags{
ArgsJSON: flags.ArgsJSON,
Signer: flags.Signer,
Proposer: flags.Proposer,
Payer: flags.Payer,
Authorizers: flags.Authorizers,
Include: flags.Include,
Exclude: flags.Exclude,
GasLimit: flags.GasLimit,
}
return transactions.SendTransaction([]byte(cadenceWithImportsReplaced), args[1:], "", flow, state, transactionFlags)
}
26 changes: 15 additions & 11 deletions internal/transactions/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import (
"github.com/onflow/flow-cli/internal/command"
)

type flagsSend struct {
type Flags struct {
ArgsJSON string `default:"" flag:"args-json" info:"arguments in JSON-Cadence format"`
Signer string `default:"" flag:"signer" info:"Account name from configuration used to sign the transaction as proposer, payer and suthorizer"`
Proposer string `default:"" flag:"proposer" info:"Account name from configuration used as proposer"`
Expand All @@ -44,7 +44,7 @@ type flagsSend struct {
GasLimit uint64 `default:"1000" flag:"gas-limit" info:"transaction gas limit"`
}

var sendFlags = flagsSend{}
var flags = Flags{}

var sendCommand = &command.Command{
Cmd: &cobra.Command{
Expand All @@ -53,7 +53,7 @@ var sendCommand = &command.Command{
Args: cobra.MinimumNArgs(1),
Example: `flow transactions send tx.cdc "Hello world"`,
},
Flags: &sendFlags,
Flags: &flags,
RunS: send,
}

Expand All @@ -64,8 +64,17 @@ func send(
flow flowkit.Services,
state *flowkit.State,
) (result command.Result, err error) {
codeFilename := args[0]
filename := args[0]

code, err := state.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("error loading transaction file: %w", err)
}

return SendTransaction(code, args, filename, flow, state, flags)
}

func SendTransaction(code []byte, args []string, location string, flow flowkit.Services, state *flowkit.State, sendFlags Flags) (result command.Result, err error) {
proposerName := sendFlags.Proposer
var proposer *accounts.Account
if proposerName != "" {
Expand Down Expand Up @@ -112,16 +121,11 @@ func send(
authorizers = append(authorizers, *signer)
}

code, err := state.ReadFile(codeFilename)
if err != nil {
return nil, fmt.Errorf("error loading transaction file: %w", err)
}

var transactionArgs []cadence.Value
if sendFlags.ArgsJSON != "" {
transactionArgs, err = arguments.ParseJSON(sendFlags.ArgsJSON)
} else {
transactionArgs, err = arguments.ParseWithoutType(args[1:], code, codeFilename)
transactionArgs, err = arguments.ParseWithoutType(args, code, location)
}
if err != nil {
return nil, fmt.Errorf("error parsing transaction arguments: %w", err)
Expand All @@ -134,7 +138,7 @@ func send(
Authorizers: authorizers,
Payer: *payer,
},
flowkit.Script{Code: code, Args: transactionArgs, Location: codeFilename},
flowkit.Script{Code: code, Args: transactionArgs, Location: location},
sendFlags.GasLimit,
)

Expand Down
26 changes: 13 additions & 13 deletions internal/transactions/transactions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ func Test_Send(t *testing.T) {

t.Run("Success", func(t *testing.T) {
const gas = uint64(1000)
sendFlags.GasLimit = gas
inArgs := []string{tests.TransactionArgString.Filename, "foo"}
flags.GasLimit = gas
inArgs := []string{tests.TransactionArgString.Filename}

srv.SendTransaction.Run(func(args mock.Arguments) {
roles := args.Get(1).(transactions.AccountRoles)
Expand All @@ -160,33 +160,33 @@ func Test_Send(t *testing.T) {
})

t.Run("Fail non-existing account", func(t *testing.T) {
sendFlags.Proposer = "invalid"
flags.Proposer = "invalid"
_, err := send([]string{""}, command.GlobalFlags{}, util.NoLogger, srv.Mock, state)
assert.EqualError(t, err, "proposer account: [invalid] doesn't exists in configuration")
sendFlags.Proposer = "" // reset
flags.Proposer = "" // reset

sendFlags.Payer = "invalid"
flags.Payer = "invalid"
_, err = send([]string{""}, command.GlobalFlags{}, util.NoLogger, srv.Mock, state)
assert.EqualError(t, err, "payer account: [invalid] doesn't exists in configuration")
sendFlags.Payer = "" // reset
flags.Payer = "" // reset

sendFlags.Authorizers = []string{"invalid"}
flags.Authorizers = []string{"invalid"}
_, err = send([]string{""}, command.GlobalFlags{}, util.NoLogger, srv.Mock, state)
assert.EqualError(t, err, "authorizer account: [invalid] doesn't exists in configuration")
sendFlags.Authorizers = nil // reset
flags.Authorizers = nil // reset

sendFlags.Signer = "invalid"
flags.Signer = "invalid"
_, err = send([]string{""}, command.GlobalFlags{}, util.NoLogger, srv.Mock, state)
assert.EqualError(t, err, "signer account: [invalid] doesn't exists in configuration")
sendFlags.Signer = "" // reset
flags.Signer = "" // reset
})

t.Run("Fail signer and payer flag", func(t *testing.T) {
sendFlags.Proposer = config.DefaultEmulator.ServiceAccount
sendFlags.Signer = config.DefaultEmulator.ServiceAccount
flags.Proposer = config.DefaultEmulator.ServiceAccount
flags.Signer = config.DefaultEmulator.ServiceAccount
_, err := send([]string{""}, command.GlobalFlags{}, util.NoLogger, srv.Mock, state)
assert.EqualError(t, err, "signer flag cannot be combined with payer/proposer/authorizer flags")
sendFlags.Signer = "" // reset
flags.Signer = "" // reset
})

t.Run("Fail loading transaction file", func(t *testing.T) {
Expand Down
Loading