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

go-algorand 3.2.1-beta #3260

Merged
merged 12 commits into from
Dec 2, 2021
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
2 changes: 1 addition & 1 deletion buildnumber.dat
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0
1
179 changes: 145 additions & 34 deletions cmd/goal/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ var (

extraPages uint32

createOnCompletion string
onCompletion string

localSchemaUints uint64
localSchemaByteSlices uint64
Expand Down Expand Up @@ -106,7 +106,7 @@ func init() {
createAppCmd.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.")
createAppCmd.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.")
createAppCmd.Flags().StringVar(&appCreator, "creator", "", "Account to create the application")
createAppCmd.Flags().StringVar(&createOnCompletion, "on-completion", "NoOp", "OnCompletion action for application transaction")
createAppCmd.Flags().StringVar(&onCompletion, "on-completion", "NoOp", "OnCompletion action for application transaction")
createAppCmd.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.")

callAppCmd.Flags().StringVarP(&account, "from", "f", "", "Account to call app from")
Expand All @@ -120,6 +120,7 @@ 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")

// 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 @@ -432,15 +433,15 @@ var createAppCmd = &cobra.Command{

// Parse transaction parameters
approvalProg, clearProg := mustParseProgArgs()
onCompletion := mustParseOnCompletion(createOnCompletion)
onCompletionEnum := mustParseOnCompletion(onCompletion)
appArgs, appAccounts, foreignApps, foreignAssets := getAppInputs()

switch onCompletion {
switch onCompletionEnum {
case transactions.CloseOutOC, transactions.ClearStateOC:
reportWarnf("'--on-completion %s' may be ill-formed for 'goal app create'", createOnCompletion)
reportWarnf("'--on-completion %s' may be ill-formed for 'goal app create'", onCompletion)
}

tx, err := client.MakeUnsignedAppCreateTx(onCompletion, approvalProg, clearProg, globalSchema, localSchema, appArgs, appAccounts, foreignApps, foreignAssets, extraPages)
tx, err := client.MakeUnsignedAppCreateTx(onCompletionEnum, approvalProg, clearProg, globalSchema, localSchema, appArgs, appAccounts, foreignApps, foreignAssets, extraPages)
if err != nil {
reportErrorf("Cannot create application txn: %v", err)
}
Expand Down Expand Up @@ -1046,6 +1047,43 @@ var infoAppCmd = &cobra.Command{
},
}

// populateMethodCallTxnArgs parses and loads transactions from the files indicated by the values
// slice. An error will occur if the transaction does not matched the expected type, it has a nonzero
// group ID, or if it is signed by a normal signature or Msig signature (but not Lsig signature)
func populateMethodCallTxnArgs(types []string, values []string) ([]transactions.SignedTxn, error) {
loadedTxns := make([]transactions.SignedTxn, len(values))

for i, txFilename := range values {
data, err := readFile(txFilename)
if err != nil {
return nil, fmt.Errorf(fileReadError, txFilename, err)
}

var txn transactions.SignedTxn
err = protocol.Decode(data, &txn)
if err != nil {
return nil, fmt.Errorf(txDecodeError, txFilename, err)
}

if !txn.Sig.Blank() || !txn.Msig.Blank() {
return nil, fmt.Errorf("Transaction from %s has already been signed", txFilename)
}

if !txn.Txn.Group.IsZero() {
return nil, fmt.Errorf("Transaction from %s already has a group ID: %s", txFilename, txn.Txn.Group)
}

expectedType := types[i]
if expectedType != "txn" && txn.Txn.Type != protocol.TxType(expectedType) {
return nil, fmt.Errorf("Transaction from %s does not match method argument type. Expected %s, got %s", txFilename, expectedType, txn.Txn.Type)
}

loadedTxns[i] = txn
}

return loadedTxns, nil
}

var methodAppCmd = &cobra.Command{
Use: "method",
Short: "Invoke a method",
Expand All @@ -1060,14 +1098,14 @@ var methodAppCmd = &cobra.Command{
reportErrorf("in goal app method: --arg and --app-arg are mutually exclusive, do not use --app-arg")
}

onCompletion := mustParseOnCompletion(createOnCompletion)
onCompletionEnum := mustParseOnCompletion(onCompletion)

if appIdx == 0 {
reportErrorf("app id == 0, goal app create not supported in goal app method")
}

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

Expand All @@ -1078,70 +1116,146 @@ var methodAppCmd = &cobra.Command{
applicationArgs = append(applicationArgs, hash[0:4])

// parse down the ABI type from method signature
argTupleTypeStr, retTypeStr, err := abi.ParseMethodSignature(method)
_, argTypes, retTypeStr, err := abi.ParseMethodSignature(method)
if err != nil {
reportErrorf("cannot parse method signature: %v", err)
}
err = abi.ParseArgJSONtoByteSlice(argTupleTypeStr, methodArgs, &applicationArgs)

var retType *abi.Type
if retTypeStr != "void" {
theRetType, err := abi.TypeOf(retTypeStr)
if err != nil {
reportErrorf("cannot cast %s to abi type: %v", retTypeStr, err)
}
retType = &theRetType
}

if len(methodArgs) != len(argTypes) {
reportErrorf("incorrect number of arguments, method expected %d but got %d", len(argTypes), len(methodArgs))
}

var txnArgTypes []string
var txnArgValues []string
var basicArgTypes []string
var basicArgValues []string
for i, argType := range argTypes {
argValue := methodArgs[i]
if abi.IsTransactionType(argType) {
txnArgTypes = append(txnArgTypes, argType)
txnArgValues = append(txnArgValues, argValue)
} else {
basicArgTypes = append(basicArgTypes, argType)
basicArgValues = append(basicArgValues, argValue)
}
}

err = abi.ParseArgJSONtoByteSlice(basicArgTypes, basicArgValues, &applicationArgs)
if err != nil {
reportErrorf("cannot parse arguments to ABI encoding: %v", err)
}

tx, err := client.MakeUnsignedApplicationCallTx(
txnArgs, err := populateMethodCallTxnArgs(txnArgTypes, txnArgValues)
if err != nil {
reportErrorf("error populating transaction arguments: %v", err)
}

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

if err != nil {
reportErrorf("Cannot create application txn: %v", err)
}

// Fill in note and lease
tx.Note = parseNoteField(cmd)
tx.Lease = parseLease(cmd)
appCallTxn.Note = parseNoteField(cmd)
appCallTxn.Lease = parseLease(cmd)

// Fill in rounds, fee, etc.
fv, lv, err := client.ComputeValidityRounds(firstValid, lastValid, numValidRounds)
if err != nil {
reportErrorf("Cannot determine last valid round: %s", err)
}

tx, err = client.FillUnsignedTxTemplate(account, fv, lv, fee, tx)
appCallTxn, err = client.FillUnsignedTxTemplate(account, fv, lv, fee, appCallTxn)
if err != nil {
reportErrorf("Cannot construct transaction: %s", err)
}
explicitFee := cmd.Flags().Changed("fee")
if explicitFee {
tx.Fee = basics.MicroAlgos{Raw: fee}
appCallTxn.Fee = basics.MicroAlgos{Raw: fee}
}

// Compile group
var txnGroup []transactions.Transaction
for i := range txnArgs {
txnGroup = append(txnGroup, txnArgs[i].Txn)
}
txnGroup = append(txnGroup, appCallTxn)
if len(txnGroup) > 1 {
// Only if transaction arguments are present, assign group ID
groupID, err := client.GroupID(txnGroup)
if err != nil {
reportErrorf("Cannot assign transaction group ID: %s", err)
}
for i := range txnGroup {
txnGroup[i].Group = groupID
}
}

// Sign transactions
var signedTxnGroup []transactions.SignedTxn
shouldSign := sign || outFilename == ""
for i, unsignedTxn := range txnGroup {
txnFromArgs := transactions.SignedTxn{}
if i < len(txnArgs) {
txnFromArgs = txnArgs[i]
}

if !txnFromArgs.Lsig.Blank() {
signedTxnGroup = append(signedTxnGroup, transactions.SignedTxn{
Lsig: txnFromArgs.Lsig,
AuthAddr: txnFromArgs.AuthAddr,
Txn: unsignedTxn,
})
continue
}

signedTxn, err := createSignedTransaction(client, shouldSign, dataDir, walletName, unsignedTxn, txnFromArgs.AuthAddr)
if err != nil {
reportErrorf(errorSigningTX, err)
}

signedTxnGroup = append(signedTxnGroup, signedTxn)
}

// Output to file
if outFilename != "" {
if dumpForDryrun {
err = writeDryrunReqToFile(client, tx, outFilename)
err = writeDryrunReqToFile(client, signedTxnGroup, outFilename)
} else {
// Write transaction to file
err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename)
err = writeSignedTxnsToFile(signedTxnGroup, outFilename)
}

if err != nil {
reportErrorf(err.Error())
}
return
}

// Broadcast
wh, pw := ensureWalletHandleMaybePassword(dataDir, walletName, true)
signedTxn, err := client.SignTransactionWithWallet(wh, pw, tx)
if err != nil {
reportErrorf(errorSigningTX, err)
}

txid, err := client.BroadcastTransaction(signedTxn)
err = client.BroadcastTransactionGroup(signedTxnGroup)
if err != nil {
reportErrorf(errorBroadcastingTX, err)
}

// Report tx details to user
reportInfof("Issued transaction from account %s, txid %s (fee %d)", tx.Sender, txid, tx.Fee.Raw)
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)
}

if !noWaitAfterSend {
_, err := waitForCommit(client, txid, lv)
Expand All @@ -1154,7 +1268,8 @@ var methodAppCmd = &cobra.Command{
reportErrorf(err.Error())
}

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

Expand All @@ -1179,10 +1294,6 @@ var methodAppCmd = &cobra.Command{
reportErrorf("cannot find return log for abi type %s", retTypeStr)
}

retType, err := abi.TypeOf(retTypeStr)
if err != nil {
reportErrorf("cannot cast %s to abi type: %v", retTypeStr, err)
}
decoded, err := retType.Decode(abiEncodedRet)
if err != nil {
reportErrorf("cannot decode return value %v: %v", abiEncodedRet, err)
Expand All @@ -1192,7 +1303,7 @@ var methodAppCmd = &cobra.Command{
if err != nil {
reportErrorf("cannot marshal returned bytes %v to JSON: %v", decoded, err)
}
fmt.Printf("method %s output: %s\n", method, string(decodedJSON))
fmt.Printf("method %s succeeded with output: %s\n", method, string(decodedJSON))
}
},
}
50 changes: 30 additions & 20 deletions cmd/goal/clerk.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,34 +194,45 @@ func waitForCommit(client libgoal.Client, txid string, transactionLastValidRound
return
}

func createSignedTransaction(client libgoal.Client, signTx bool, dataDir string, walletName string, tx transactions.Transaction) (stxn transactions.SignedTxn, err error) {
func createSignedTransaction(client libgoal.Client, signTx bool, dataDir string, walletName string, tx transactions.Transaction, signer basics.Address) (stxn transactions.SignedTxn, err error) {
if signTx {
// Sign the transaction
wh, pw := ensureWalletHandleMaybePassword(dataDir, walletName, true)
stxn, err = client.SignTransactionWithWallet(wh, pw, tx)
if err != nil {
return
}
} else {
// Wrap in a transactions.SignedTxn with an empty sig.
// This way protocol.Encode will encode the transaction type
stxn, err = transactions.AssembleSignedTxn(tx, crypto.Signature{}, crypto.MultisigSig{})
if err != nil {
return
if signer.IsZero() {
stxn, err = client.SignTransactionWithWallet(wh, pw, tx)
} else {
stxn, err = client.SignTransactionWithWalletAndSigner(wh, pw, signer.String(), tx)
}
return
}

stxn = populateBlankMultisig(client, dataDir, walletName, stxn)
// Wrap in a transactions.SignedTxn with an empty sig.
// This way protocol.Encode will encode the transaction type
stxn, err = transactions.AssembleSignedTxn(tx, crypto.Signature{}, crypto.MultisigSig{})
if err != nil {
return
}

stxn = populateBlankMultisig(client, dataDir, walletName, stxn)
return
}

func writeSignedTxnsToFile(stxns []transactions.SignedTxn, filename string) error {
var outData []byte
for _, stxn := range stxns {
outData = append(outData, protocol.Encode(&stxn)...)
}

return writeFile(filename, outData, 0600)
}

func writeTxnToFile(client libgoal.Client, signTx bool, dataDir string, walletName string, tx transactions.Transaction, filename string) error {
stxn, err := createSignedTransaction(client, signTx, dataDir, walletName, tx)
stxn, err := createSignedTransaction(client, signTx, dataDir, walletName, tx, basics.Address{})
if err != nil {
return err
}
// Write the SignedTxn to the output file
return writeFile(filename, protocol.Encode(&stxn), 0600)
return writeSignedTxnsToFile([]transactions.SignedTxn{stxn}, filename)
}

func getB64Args(args []string) [][]byte {
Expand Down Expand Up @@ -419,7 +430,7 @@ var sendCmd = &cobra.Command{
}
} else {
signTx := sign || (outFilename == "")
stx, err = createSignedTransaction(client, signTx, dataDir, walletName, payment)
stx, err = createSignedTransaction(client, signTx, dataDir, walletName, payment, basics.Address{})
if err != nil {
reportErrorf(errorSigningTX, err)
}
Expand Down Expand Up @@ -854,13 +865,12 @@ var groupCmd = &cobra.Command{
transactionIdx++
}

var outData []byte
for _, stxn := range stxns {
stxn.Txn.Group = crypto.HashObj(group)
outData = append(outData, protocol.Encode(&stxn)...)
groupHash := crypto.HashObj(group)
for i := range stxns {
stxns[i].Txn.Group = groupHash
}

err = writeFile(outFilename, outData, 0600)
err = writeSignedTxnsToFile(stxns, outFilename)
if err != nil {
reportErrorf(fileWriteError, outFilename, err)
}
Expand Down
Loading