Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
69 changes: 58 additions & 11 deletions cmd/goal/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ var (
approvalProgFile string
clearProgFile string

method string
methodArgs []string
method string
methodArgs []string
methodCreatesApp bool

approvalProgRawFile string
clearProgRawFile string
Expand Down Expand Up @@ -122,6 +123,12 @@ func init() {
methodAppCmd.Flags().StringVar(&method, "method", "", "Method to be called")
methodAppCmd.Flags().StringArrayVar(&methodArgs, "arg", nil, "Args to pass in for calling a method")
methodAppCmd.Flags().StringVar(&onCompletion, "on-completion", "NoOp", "OnCompletion action for application transaction")
methodAppCmd.Flags().BoolVar(&methodCreatesApp, "create", false, "Create an application in this method call")
methodAppCmd.Flags().Uint64Var(&globalSchemaUints, "global-ints", 0, "Maximum number of integer values that may be stored in the global key/value store. Immutable, only valid when passed with --create.")
methodAppCmd.Flags().Uint64Var(&globalSchemaByteSlices, "global-byteslices", 0, "Maximum number of byte slices that may be stored in the global key/value store. Immutable, only valid when passed with --create.")
methodAppCmd.Flags().Uint64Var(&localSchemaUints, "local-ints", 0, "Maximum number of integer values that may be stored in local (per-account) key/value stores for this app. Immutable, only valid when passed with --create.")
methodAppCmd.Flags().Uint64Var(&localSchemaByteSlices, "local-byteslices", 0, "Maximum number of byte slices that may be stored in local (per-account) key/value stores for this app. Immutable, only valid when passed with --create.")
methodAppCmd.Flags().Uint32Var(&extraPages, "extra-pages", 0, "Additional program space for supporting larger TEAL assembly program. A maximum of 3 extra pages is allowed. A page is 1024 bytes. Only valid when passed with --create.")

// Can't use PersistentFlags on the root because for some reason marking
// a root command as required with MarkPersistentFlagRequired isn't
Expand Down Expand Up @@ -179,7 +186,6 @@ func init() {
infoAppCmd.MarkFlagRequired("app-id")

methodAppCmd.MarkFlagRequired("method") // nolint:errcheck // follow previous required flag format
methodAppCmd.MarkFlagRequired("app-id") // nolint:errcheck
methodAppCmd.MarkFlagRequired("from") // nolint:errcheck
methodAppCmd.Flags().MarkHidden("app-arg") // nolint:errcheck
}
Expand Down Expand Up @@ -1172,17 +1178,47 @@ var methodAppCmd = &cobra.Command{
// Parse transaction parameters
appArgsParsed, appAccounts, foreignApps, foreignAssets := getAppInputs()
if len(appArgsParsed) > 0 {
reportErrorf("in goal app method: --arg and --app-arg are mutually exclusive, do not use --app-arg")
reportErrorf("--arg and --app-arg are mutually exclusive, do not use --app-arg")
}

// Construct schemas from args
localSchema := basics.StateSchema{
NumUint: localSchemaUints,
NumByteSlice: localSchemaByteSlices,
}

globalSchema := basics.StateSchema{
NumUint: globalSchemaUints,
NumByteSlice: globalSchemaByteSlices,
}

onCompletionEnum := mustParseOnCompletion(onCompletion)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is obviously a decision that was made before, this PR doesn't change it. But do you think we should really be requiring on-complete to be set always? Rather than default to no-op?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually you don't need to explicitly pass it in, it will default to "NoOp":

methodAppCmd.Flags().StringVar(&onCompletion, "on-completion", "NoOp", "OnCompletion action for application transaction")

The e2e test for creation does not pass in --on-completion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, yeah. I guess I wish there was no "must" on that method name then. While it's true, it seems like it's trying to convey something important that never comes up in practice because of the default. But I don't care that much.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it's a bit misleading, but I'll leave it for now since there are multiple other helper methods in the goal code that use "must" to convey they will either succeed or panic.


if appIdx == 0 {
reportErrorf("app id == 0, goal app create not supported in goal app method")
if methodCreatesApp {
if appIdx != 0 {
reportErrorf("--app-id and --create are mutually exclusive, only provide one")
}

switch onCompletionEnum {
case transactions.CloseOutOC, transactions.ClearStateOC:
reportWarnf("'--on-completion %s' may be ill-formed for use with --create", onCompletion)
}
} else {
if appIdx == 0 {
reportErrorf("one of --app-id or --create must be provided")
}

if localSchema != (basics.StateSchema{}) || globalSchema != (basics.StateSchema{}) {
reportErrorf("--global-ints, --global-byteslices, --local-ints, and --local-byteslices must only be provided with --create")
}

if extraPages != 0 {
reportErrorf("--extra-pages must only be provided with --create")
}
}

var approvalProg, clearProg []byte
if onCompletionEnum == transactions.UpdateApplicationOC {
if methodCreatesApp || onCompletionEnum == transactions.UpdateApplicationOC {
approvalProg, clearProg = mustParseProgArgs()
}

Expand Down Expand Up @@ -1258,7 +1294,7 @@ var methodAppCmd = &cobra.Command{

appCallTxn, err := client.MakeUnsignedApplicationCallTx(
appIdx, applicationArgs, appAccounts, foreignApps, foreignAssets,
onCompletionEnum, approvalProg, clearProg, basics.StateSchema{}, basics.StateSchema{}, 0)
onCompletionEnum, approvalProg, clearProg, globalSchema, localSchema, extraPages)

if err != nil {
reportErrorf("Cannot create application txn: %v", err)
Expand Down Expand Up @@ -1346,12 +1382,19 @@ var methodAppCmd = &cobra.Command{
}

// Report tx details to user
if methodCreatesApp {
reportInfof("Attempting to create app (approval size %d, hash %v; clear size %d, hash %v)", len(approvalProg), crypto.HashObj(logic.Program(approvalProg)), len(clearProg), crypto.HashObj(logic.Program(clearProg)))
} else if onCompletionEnum == transactions.UpdateApplicationOC {
reportInfof("Attempting to update app (approval size %d, hash %v; clear size %d, hash %v)", len(approvalProg), crypto.HashObj(logic.Program(approvalProg)), len(clearProg), crypto.HashObj(logic.Program(clearProg)))
}

reportInfof("Issued %d transaction(s):", len(signedTxnGroup))

// remember the final txid in this variable
var txid string
for _, stxn := range signedTxnGroup {
txid = stxn.Txn.ID().String()
reportInfof("\tIssued transaction from account %s, txid %s (fee %d)", stxn.Txn.Sender, txid, stxn.Txn.Fee.Raw)
reportInfof("Issued transaction from account %s, txid %s (fee %d)", stxn.Txn.Sender, txid, stxn.Txn.Fee.Raw)
}

if !noWaitAfterSend {
Expand All @@ -1365,8 +1408,12 @@ var methodAppCmd = &cobra.Command{
reportErrorf(err.Error())
}

if methodCreatesApp && resp.ApplicationIndex != nil && *resp.ApplicationIndex != 0 {
reportInfof("Created app with app index %d", *resp.ApplicationIndex)
}

if retType == nil {
fmt.Printf("method %s succeeded\n", method)
reportInfof("method %s succeeded", method)
return
}

Expand All @@ -1393,7 +1440,7 @@ var methodAppCmd = &cobra.Command{
reportErrorf("method %s succeed but its return value could not be converted to JSON.\nThe raw return value in hex is:%s\nThe error is: %s", method, hex.EncodeToString(rawReturnValue), err)
}

fmt.Printf("method %s succeeded with output: %s\n", method, string(decodedJSON))
reportInfof("method %s succeeded with output: %s", method, string(decodedJSON))
}
},
}
23 changes: 20 additions & 3 deletions test/scripts/e2e_subs/e2e-app-abi-method.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,18 @@ gcmd="goal -w ${WALLET}"

ACCOUNT=$(${gcmd} account list|awk '{ print $3 }')

printf '#pragma version 2\nint 1' > "${TEMPDIR}/simple.teal"
PROGRAM=($(${gcmd} clerk compile "${TEMPDIR}/simple.teal"))
APPID=$(${gcmd} app create --creator ${ACCOUNT} --approval-prog ${DIR}/tealprogs/app-abi-method-example.teal --clear-prog ${TEMPDIR}/simple.teal --global-byteslices 0 --global-ints 0 --local-byteslices 1 --local-ints 0 | grep Created | awk '{ print $6 }')
printf '#pragma version 2\nint 1' > "${TEMPDIR}/simple-v2.teal"
printf '#pragma version 3\nint 1' > "${TEMPDIR}/simple-v3.teal"

# Create
RES=$(${gcmd} app method --method "create(uint64)uint64" --arg "1234" --create --approval-prog ${DIR}/tealprogs/app-abi-method-example.teal --clear-prog ${TEMPDIR}/simple-v2.teal --global-byteslices 0 --global-ints 0 --local-byteslices 1 --local-ints 0 --extra-pages 0 --from $ACCOUNT 2>&1 || true)
EXPECTED="method create(uint64)uint64 succeeded with output: 2468"
if [[ $RES != *"${EXPECTED}"* ]]; then
date '+app-abi-method-test FAIL the method call to create(uint64)uint64 should not fail %Y%m%d_%H%M%S'
false
fi

APPID=$(echo "$RES" | grep Created | awk '{ print $6 }')

# Opt in
RES=$(${gcmd} app method --method "optIn(string)string" --arg "\"Algorand Fan\"" --on-completion optin --app-id $APPID --from $ACCOUNT 2>&1 || true)
Expand Down Expand Up @@ -86,6 +95,14 @@ if [[ $RES != *"${EXPECTED}"* ]]; then
false
fi

# Update
RES=$(${gcmd} app method --method "update()void" --on-completion updateapplication --approval-prog ${DIR}/tealprogs/app-abi-method-example.teal --clear-prog ${TEMPDIR}/simple-v3.teal --app-id $APPID --from $ACCOUNT 2>&1 || true)
EXPECTED="method update()void succeeded"
if [[ $RES != *"${EXPECTED}"* ]]; then
date '+app-abi-method-test FAIL the method call to update()void should not fail %Y%m%d_%H%M%S'
false
fi

# Delete
RES=$(${gcmd} app method --method "delete()void" --on-completion deleteapplication --app-id $APPID --from $ACCOUNT 2>&1 || true)
EXPECTED="method delete()void succeeded"
Expand Down
Loading