Skip to content

Commit 2baf39b

Browse files
authored
Merge pull request #3260 from Algo-devops-service/relbeta3.2.1
go-algorand 3.2.1-beta
2 parents 8aa0728 + 305d7ab commit 2baf39b

File tree

18 files changed

+596
-282
lines changed

18 files changed

+596
-282
lines changed

buildnumber.dat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0
1+
1

cmd/goal/application.go

Lines changed: 145 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ var (
5353

5454
extraPages uint32
5555

56-
createOnCompletion string
56+
onCompletion string
5757

5858
localSchemaUints uint64
5959
localSchemaByteSlices uint64
@@ -106,7 +106,7 @@ func init() {
106106
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.")
107107
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.")
108108
createAppCmd.Flags().StringVar(&appCreator, "creator", "", "Account to create the application")
109-
createAppCmd.Flags().StringVar(&createOnCompletion, "on-completion", "NoOp", "OnCompletion action for application transaction")
109+
createAppCmd.Flags().StringVar(&onCompletion, "on-completion", "NoOp", "OnCompletion action for application transaction")
110110
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.")
111111

112112
callAppCmd.Flags().StringVarP(&account, "from", "f", "", "Account to call app from")
@@ -120,6 +120,7 @@ func init() {
120120

121121
methodAppCmd.Flags().StringVar(&method, "method", "", "Method to be called")
122122
methodAppCmd.Flags().StringArrayVar(&methodArgs, "arg", nil, "Args to pass in for calling a method")
123+
methodAppCmd.Flags().StringVar(&onCompletion, "on-completion", "NoOp", "OnCompletion action for application transaction")
123124

124125
// Can't use PersistentFlags on the root because for some reason marking
125126
// a root command as required with MarkPersistentFlagRequired isn't
@@ -432,15 +433,15 @@ var createAppCmd = &cobra.Command{
432433

433434
// Parse transaction parameters
434435
approvalProg, clearProg := mustParseProgArgs()
435-
onCompletion := mustParseOnCompletion(createOnCompletion)
436+
onCompletionEnum := mustParseOnCompletion(onCompletion)
436437
appArgs, appAccounts, foreignApps, foreignAssets := getAppInputs()
437438

438-
switch onCompletion {
439+
switch onCompletionEnum {
439440
case transactions.CloseOutOC, transactions.ClearStateOC:
440-
reportWarnf("'--on-completion %s' may be ill-formed for 'goal app create'", createOnCompletion)
441+
reportWarnf("'--on-completion %s' may be ill-formed for 'goal app create'", onCompletion)
441442
}
442443

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

1050+
// populateMethodCallTxnArgs parses and loads transactions from the files indicated by the values
1051+
// slice. An error will occur if the transaction does not matched the expected type, it has a nonzero
1052+
// group ID, or if it is signed by a normal signature or Msig signature (but not Lsig signature)
1053+
func populateMethodCallTxnArgs(types []string, values []string) ([]transactions.SignedTxn, error) {
1054+
loadedTxns := make([]transactions.SignedTxn, len(values))
1055+
1056+
for i, txFilename := range values {
1057+
data, err := readFile(txFilename)
1058+
if err != nil {
1059+
return nil, fmt.Errorf(fileReadError, txFilename, err)
1060+
}
1061+
1062+
var txn transactions.SignedTxn
1063+
err = protocol.Decode(data, &txn)
1064+
if err != nil {
1065+
return nil, fmt.Errorf(txDecodeError, txFilename, err)
1066+
}
1067+
1068+
if !txn.Sig.Blank() || !txn.Msig.Blank() {
1069+
return nil, fmt.Errorf("Transaction from %s has already been signed", txFilename)
1070+
}
1071+
1072+
if !txn.Txn.Group.IsZero() {
1073+
return nil, fmt.Errorf("Transaction from %s already has a group ID: %s", txFilename, txn.Txn.Group)
1074+
}
1075+
1076+
expectedType := types[i]
1077+
if expectedType != "txn" && txn.Txn.Type != protocol.TxType(expectedType) {
1078+
return nil, fmt.Errorf("Transaction from %s does not match method argument type. Expected %s, got %s", txFilename, expectedType, txn.Txn.Type)
1079+
}
1080+
1081+
loadedTxns[i] = txn
1082+
}
1083+
1084+
return loadedTxns, nil
1085+
}
1086+
10491087
var methodAppCmd = &cobra.Command{
10501088
Use: "method",
10511089
Short: "Invoke a method",
@@ -1060,14 +1098,14 @@ var methodAppCmd = &cobra.Command{
10601098
reportErrorf("in goal app method: --arg and --app-arg are mutually exclusive, do not use --app-arg")
10611099
}
10621100

1063-
onCompletion := mustParseOnCompletion(createOnCompletion)
1101+
onCompletionEnum := mustParseOnCompletion(onCompletion)
10641102

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

10691107
var approvalProg, clearProg []byte
1070-
if onCompletion == transactions.UpdateApplicationOC {
1108+
if onCompletionEnum == transactions.UpdateApplicationOC {
10711109
approvalProg, clearProg = mustParseProgArgs()
10721110
}
10731111

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

10801118
// parse down the ABI type from method signature
1081-
argTupleTypeStr, retTypeStr, err := abi.ParseMethodSignature(method)
1119+
_, argTypes, retTypeStr, err := abi.ParseMethodSignature(method)
10821120
if err != nil {
10831121
reportErrorf("cannot parse method signature: %v", err)
10841122
}
1085-
err = abi.ParseArgJSONtoByteSlice(argTupleTypeStr, methodArgs, &applicationArgs)
1123+
1124+
var retType *abi.Type
1125+
if retTypeStr != "void" {
1126+
theRetType, err := abi.TypeOf(retTypeStr)
1127+
if err != nil {
1128+
reportErrorf("cannot cast %s to abi type: %v", retTypeStr, err)
1129+
}
1130+
retType = &theRetType
1131+
}
1132+
1133+
if len(methodArgs) != len(argTypes) {
1134+
reportErrorf("incorrect number of arguments, method expected %d but got %d", len(argTypes), len(methodArgs))
1135+
}
1136+
1137+
var txnArgTypes []string
1138+
var txnArgValues []string
1139+
var basicArgTypes []string
1140+
var basicArgValues []string
1141+
for i, argType := range argTypes {
1142+
argValue := methodArgs[i]
1143+
if abi.IsTransactionType(argType) {
1144+
txnArgTypes = append(txnArgTypes, argType)
1145+
txnArgValues = append(txnArgValues, argValue)
1146+
} else {
1147+
basicArgTypes = append(basicArgTypes, argType)
1148+
basicArgValues = append(basicArgValues, argValue)
1149+
}
1150+
}
1151+
1152+
err = abi.ParseArgJSONtoByteSlice(basicArgTypes, basicArgValues, &applicationArgs)
10861153
if err != nil {
10871154
reportErrorf("cannot parse arguments to ABI encoding: %v", err)
10881155
}
10891156

1090-
tx, err := client.MakeUnsignedApplicationCallTx(
1157+
txnArgs, err := populateMethodCallTxnArgs(txnArgTypes, txnArgValues)
1158+
if err != nil {
1159+
reportErrorf("error populating transaction arguments: %v", err)
1160+
}
1161+
1162+
appCallTxn, err := client.MakeUnsignedApplicationCallTx(
10911163
appIdx, applicationArgs, appAccounts, foreignApps, foreignAssets,
1092-
onCompletion, approvalProg, clearProg, basics.StateSchema{}, basics.StateSchema{}, 0)
1164+
onCompletionEnum, approvalProg, clearProg, basics.StateSchema{}, basics.StateSchema{}, 0)
10931165

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

10981170
// Fill in note and lease
1099-
tx.Note = parseNoteField(cmd)
1100-
tx.Lease = parseLease(cmd)
1171+
appCallTxn.Note = parseNoteField(cmd)
1172+
appCallTxn.Lease = parseLease(cmd)
11011173

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

1108-
tx, err = client.FillUnsignedTxTemplate(account, fv, lv, fee, tx)
1180+
appCallTxn, err = client.FillUnsignedTxTemplate(account, fv, lv, fee, appCallTxn)
11091181
if err != nil {
11101182
reportErrorf("Cannot construct transaction: %s", err)
11111183
}
11121184
explicitFee := cmd.Flags().Changed("fee")
11131185
if explicitFee {
1114-
tx.Fee = basics.MicroAlgos{Raw: fee}
1186+
appCallTxn.Fee = basics.MicroAlgos{Raw: fee}
1187+
}
1188+
1189+
// Compile group
1190+
var txnGroup []transactions.Transaction
1191+
for i := range txnArgs {
1192+
txnGroup = append(txnGroup, txnArgs[i].Txn)
1193+
}
1194+
txnGroup = append(txnGroup, appCallTxn)
1195+
if len(txnGroup) > 1 {
1196+
// Only if transaction arguments are present, assign group ID
1197+
groupID, err := client.GroupID(txnGroup)
1198+
if err != nil {
1199+
reportErrorf("Cannot assign transaction group ID: %s", err)
1200+
}
1201+
for i := range txnGroup {
1202+
txnGroup[i].Group = groupID
1203+
}
11151204
}
11161205

1206+
// Sign transactions
1207+
var signedTxnGroup []transactions.SignedTxn
1208+
shouldSign := sign || outFilename == ""
1209+
for i, unsignedTxn := range txnGroup {
1210+
txnFromArgs := transactions.SignedTxn{}
1211+
if i < len(txnArgs) {
1212+
txnFromArgs = txnArgs[i]
1213+
}
1214+
1215+
if !txnFromArgs.Lsig.Blank() {
1216+
signedTxnGroup = append(signedTxnGroup, transactions.SignedTxn{
1217+
Lsig: txnFromArgs.Lsig,
1218+
AuthAddr: txnFromArgs.AuthAddr,
1219+
Txn: unsignedTxn,
1220+
})
1221+
continue
1222+
}
1223+
1224+
signedTxn, err := createSignedTransaction(client, shouldSign, dataDir, walletName, unsignedTxn, txnFromArgs.AuthAddr)
1225+
if err != nil {
1226+
reportErrorf(errorSigningTX, err)
1227+
}
1228+
1229+
signedTxnGroup = append(signedTxnGroup, signedTxn)
1230+
}
1231+
1232+
// Output to file
11171233
if outFilename != "" {
11181234
if dumpForDryrun {
1119-
err = writeDryrunReqToFile(client, tx, outFilename)
1235+
err = writeDryrunReqToFile(client, signedTxnGroup, outFilename)
11201236
} else {
1121-
// Write transaction to file
1122-
err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename)
1237+
err = writeSignedTxnsToFile(signedTxnGroup, outFilename)
11231238
}
1124-
11251239
if err != nil {
11261240
reportErrorf(err.Error())
11271241
}
11281242
return
11291243
}
11301244

11311245
// Broadcast
1132-
wh, pw := ensureWalletHandleMaybePassword(dataDir, walletName, true)
1133-
signedTxn, err := client.SignTransactionWithWallet(wh, pw, tx)
1134-
if err != nil {
1135-
reportErrorf(errorSigningTX, err)
1136-
}
1137-
1138-
txid, err := client.BroadcastTransaction(signedTxn)
1246+
err = client.BroadcastTransactionGroup(signedTxnGroup)
11391247
if err != nil {
11401248
reportErrorf(errorBroadcastingTX, err)
11411249
}
11421250

11431251
// Report tx details to user
1144-
reportInfof("Issued transaction from account %s, txid %s (fee %d)", tx.Sender, txid, tx.Fee.Raw)
1252+
reportInfof("Issued %d transaction(s):", len(signedTxnGroup))
1253+
// remember the final txid in this variable
1254+
var txid string
1255+
for _, stxn := range signedTxnGroup {
1256+
txid = stxn.Txn.ID().String()
1257+
reportInfof("\tIssued transaction from account %s, txid %s (fee %d)", stxn.Txn.Sender, txid, stxn.Txn.Fee.Raw)
1258+
}
11451259

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

1157-
if retTypeStr == "void" {
1271+
if retType == nil {
1272+
fmt.Printf("method %s succeeded\n", method)
11581273
return
11591274
}
11601275

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

1182-
retType, err := abi.TypeOf(retTypeStr)
1183-
if err != nil {
1184-
reportErrorf("cannot cast %s to abi type: %v", retTypeStr, err)
1185-
}
11861297
decoded, err := retType.Decode(abiEncodedRet)
11871298
if err != nil {
11881299
reportErrorf("cannot decode return value %v: %v", abiEncodedRet, err)
@@ -1192,7 +1303,7 @@ var methodAppCmd = &cobra.Command{
11921303
if err != nil {
11931304
reportErrorf("cannot marshal returned bytes %v to JSON: %v", decoded, err)
11941305
}
1195-
fmt.Printf("method %s output: %s\n", method, string(decodedJSON))
1306+
fmt.Printf("method %s succeeded with output: %s\n", method, string(decodedJSON))
11961307
}
11971308
},
11981309
}

cmd/goal/clerk.go

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -194,34 +194,45 @@ func waitForCommit(client libgoal.Client, txid string, transactionLastValidRound
194194
return
195195
}
196196

197-
func createSignedTransaction(client libgoal.Client, signTx bool, dataDir string, walletName string, tx transactions.Transaction) (stxn transactions.SignedTxn, err error) {
197+
func createSignedTransaction(client libgoal.Client, signTx bool, dataDir string, walletName string, tx transactions.Transaction, signer basics.Address) (stxn transactions.SignedTxn, err error) {
198198
if signTx {
199199
// Sign the transaction
200200
wh, pw := ensureWalletHandleMaybePassword(dataDir, walletName, true)
201-
stxn, err = client.SignTransactionWithWallet(wh, pw, tx)
202-
if err != nil {
203-
return
204-
}
205-
} else {
206-
// Wrap in a transactions.SignedTxn with an empty sig.
207-
// This way protocol.Encode will encode the transaction type
208-
stxn, err = transactions.AssembleSignedTxn(tx, crypto.Signature{}, crypto.MultisigSig{})
209-
if err != nil {
210-
return
201+
if signer.IsZero() {
202+
stxn, err = client.SignTransactionWithWallet(wh, pw, tx)
203+
} else {
204+
stxn, err = client.SignTransactionWithWalletAndSigner(wh, pw, signer.String(), tx)
211205
}
206+
return
207+
}
212208

213-
stxn = populateBlankMultisig(client, dataDir, walletName, stxn)
209+
// Wrap in a transactions.SignedTxn with an empty sig.
210+
// This way protocol.Encode will encode the transaction type
211+
stxn, err = transactions.AssembleSignedTxn(tx, crypto.Signature{}, crypto.MultisigSig{})
212+
if err != nil {
213+
return
214214
}
215+
216+
stxn = populateBlankMultisig(client, dataDir, walletName, stxn)
215217
return
216218
}
217219

220+
func writeSignedTxnsToFile(stxns []transactions.SignedTxn, filename string) error {
221+
var outData []byte
222+
for _, stxn := range stxns {
223+
outData = append(outData, protocol.Encode(&stxn)...)
224+
}
225+
226+
return writeFile(filename, outData, 0600)
227+
}
228+
218229
func writeTxnToFile(client libgoal.Client, signTx bool, dataDir string, walletName string, tx transactions.Transaction, filename string) error {
219-
stxn, err := createSignedTransaction(client, signTx, dataDir, walletName, tx)
230+
stxn, err := createSignedTransaction(client, signTx, dataDir, walletName, tx, basics.Address{})
220231
if err != nil {
221232
return err
222233
}
223234
// Write the SignedTxn to the output file
224-
return writeFile(filename, protocol.Encode(&stxn), 0600)
235+
return writeSignedTxnsToFile([]transactions.SignedTxn{stxn}, filename)
225236
}
226237

227238
func getB64Args(args []string) [][]byte {
@@ -419,7 +430,7 @@ var sendCmd = &cobra.Command{
419430
}
420431
} else {
421432
signTx := sign || (outFilename == "")
422-
stx, err = createSignedTransaction(client, signTx, dataDir, walletName, payment)
433+
stx, err = createSignedTransaction(client, signTx, dataDir, walletName, payment, basics.Address{})
423434
if err != nil {
424435
reportErrorf(errorSigningTX, err)
425436
}
@@ -854,13 +865,12 @@ var groupCmd = &cobra.Command{
854865
transactionIdx++
855866
}
856867

857-
var outData []byte
858-
for _, stxn := range stxns {
859-
stxn.Txn.Group = crypto.HashObj(group)
860-
outData = append(outData, protocol.Encode(&stxn)...)
868+
groupHash := crypto.HashObj(group)
869+
for i := range stxns {
870+
stxns[i].Txn.Group = groupHash
861871
}
862872

863-
err = writeFile(outFilename, outData, 0600)
873+
err = writeSignedTxnsToFile(stxns, outFilename)
864874
if err != nil {
865875
reportErrorf(fileWriteError, outFilename, err)
866876
}

0 commit comments

Comments
 (0)