Skip to content

Commit

Permalink
Fix simulate requests, support ATC simulation, and enable cucumber si…
Browse files Browse the repository at this point in the history
…mulate tests
  • Loading branch information
jasonpaulos committed Sep 14, 2023
1 parent c039b84 commit 7a4261b
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 47 deletions.
3 changes: 2 additions & 1 deletion client/v2/algod/simulateTransaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/algorand/go-algorand-sdk/v2/client/v2/common"
"github.com/algorand/go-algorand-sdk/v2/client/v2/common/models"
"github.com/algorand/go-algorand-sdk/v2/encoding/msgpack"
)

// SimulateTransactionParams contains all of the query parameters for url serialization.
Expand All @@ -28,6 +29,6 @@ type SimulateTransaction struct {

// Do performs the HTTP request
func (s *SimulateTransaction) Do(ctx context.Context, headers ...*common.Header) (response models.SimulateResponse, err error) {
err = s.c.post(ctx, &response, "/v2/transactions/simulate", s.p, headers, s.request)
err = s.c.post(ctx, &response, "/v2/transactions/simulate", s.p, headers, msgpack.Encode(&s.request))
return
}
9 changes: 5 additions & 4 deletions client/v2/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ import (

// rawRequestPaths is a set of paths where the body should not be urlencoded
var rawRequestPaths = map[string]bool{
"/v2/transactions": true,
"/v2/teal/compile": true,
"/v2/teal/disassemble": true,
"/v2/teal/dryrun": true,
"/v2/transactions": true,
"/v2/teal/compile": true,
"/v2/teal/disassemble": true,
"/v2/teal/dryrun": true,
"/v2/transactions/simulate": true,
}

// Header is a struct for custom headers.
Expand Down
47 changes: 31 additions & 16 deletions test/applications_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/algorand/go-algorand-sdk/v2/transaction"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
"time"

"github.com/algorand/go-algorand-sdk/v2/transaction"

"github.com/cucumber/godog"

"github.com/algorand/go-algorand-sdk/v2/abi"
Expand All @@ -34,7 +35,7 @@ var tx types.Transaction
var transientAccount crypto.Account
var applicationId uint64
var applicationIds []uint64
var txComposerResult transaction.ExecuteResult
var txComposerMethodResults []transaction.ABIMethodResult

func anAlgodVClientConnectedToPortWithToken(v int, host string, port int, token string) error {
var err error
Expand Down Expand Up @@ -485,16 +486,29 @@ func iCloneTheComposer() error {
}

func iExecuteTheCurrentTransactionGroupWithTheComposer() error {
var err error
txComposerResult, err = txComposer.Execute(algodV2client, context.Background(), 10)
return err
txComposerResult, err := txComposer.Execute(algodV2client, context.Background(), 10)
if err != nil {
return err
}
txComposerMethodResults = txComposerResult.MethodResults
return nil
}

func iSimulateTheCurrentTransactionGroupWithTheComposer() error {
result, err := txComposer.Simulate(algodV2client, context.Background(), models.SimulateRequest{})
if err != nil {
return err
}
simulateResponse = result.SimulateResponse
txComposerMethodResults = result.MethodResults
return nil
}

func theAppShouldHaveReturned(commaSeparatedB64Results string) error {
b64ExpectedResults := strings.Split(commaSeparatedB64Results, ",")

if len(b64ExpectedResults) != len(txComposerResult.MethodResults) {
return fmt.Errorf("length of expected results doesn't match actual: %d != %d", len(b64ExpectedResults), len(txComposerResult.MethodResults))
if len(b64ExpectedResults) != len(txComposerMethodResults) {
return fmt.Errorf("length of expected results doesn't match actual: %d != %d", len(b64ExpectedResults), len(txComposerMethodResults))
}

for i, b64ExpectedResult := range b64ExpectedResults {
Expand All @@ -503,7 +517,7 @@ func theAppShouldHaveReturned(commaSeparatedB64Results string) error {
return err
}

actualResult := txComposerResult.MethodResults[i]
actualResult := txComposerMethodResults[i]
method := actualResult.Method

if actualResult.DecodeError != nil {
Expand Down Expand Up @@ -542,12 +556,12 @@ func theAppShouldHaveReturned(commaSeparatedB64Results string) error {
func theAppShouldHaveReturnedABITypes(colonSeparatedExpectedTypeStrings string) error {
expectedTypeStrings := strings.Split(colonSeparatedExpectedTypeStrings, ":")

if len(expectedTypeStrings) != len(txComposerResult.MethodResults) {
return fmt.Errorf("length of expected results doesn't match actual: %d != %d", len(expectedTypeStrings), len(txComposerResult.MethodResults))
if len(expectedTypeStrings) != len(txComposerMethodResults) {
return fmt.Errorf("length of expected results doesn't match actual: %d != %d", len(expectedTypeStrings), len(txComposerMethodResults))
}

for i, expectedTypeString := range expectedTypeStrings {
actualResult := txComposerResult.MethodResults[i]
actualResult := txComposerMethodResults[i]

if actualResult.DecodeError != nil {
return actualResult.DecodeError
Expand Down Expand Up @@ -584,7 +598,7 @@ func theAppShouldHaveReturnedABITypes(colonSeparatedExpectedTypeStrings string)

func checkAtomicResultAgainstValue(resultIndex int, path, expectedValue string) error {
keys := strings.Split(path, ".")
info := txComposerResult.MethodResults[resultIndex].TransactionInfo
info := txComposerMethodResults[resultIndex].TransactionInfo

jsonBytes := sdkJson.Encode(&info)

Expand Down Expand Up @@ -677,7 +691,7 @@ func checkInnerTxnGroupIDs(colonSeparatedPathsString string) error {
var current models.PendingTransactionResponse
for pathIndex, innerTxnIndex := range path {
if pathIndex == 0 {
current = models.PendingTransactionResponse(txComposerResult.MethodResults[innerTxnIndex].TransactionInfo)
current = models.PendingTransactionResponse(txComposerMethodResults[innerTxnIndex].TransactionInfo)
} else {
current = current.InnerTxns[innerTxnIndex]
}
Expand All @@ -704,7 +718,7 @@ func checkSpinResult(resultIndex int, method, r string) error {
return fmt.Errorf("Incorrect method name, expected 'spin()', got '%s'", method)
}

result := txComposerResult.MethodResults[resultIndex]
result := txComposerMethodResults[resultIndex]
decodedResult := result.ReturnValue.([]interface{})

spin := decodedResult[0].([]interface{})
Expand All @@ -731,7 +745,7 @@ func sha512_256AsUint64(preimage []byte) uint64 {
}

func checkRandomIntResult(resultIndex, input int) error {
result := txComposerResult.MethodResults[resultIndex]
result := txComposerMethodResults[resultIndex]
decodedResult := result.ReturnValue.([]interface{})

randInt := decodedResult[0].(uint64)
Expand All @@ -752,7 +766,7 @@ func checkRandomIntResult(resultIndex, input int) error {
}

func checkRandomElementResult(resultIndex int, input string) error {
result := txComposerResult.MethodResults[resultIndex]
result := txComposerMethodResults[resultIndex]
decodedResult := result.ReturnValue.([]interface{})

randElt := decodedResult[0].(byte)
Expand Down Expand Up @@ -932,6 +946,7 @@ func ApplicationsContext(s *godog.Suite) {
s.Step(`^I add the current transaction with signer to the composer\.$`, iAddTheCurrentTransactionWithSignerToTheComposer)
s.Step(`^I clone the composer\.$`, iCloneTheComposer)
s.Step(`^I execute the current transaction group with the composer\.$`, iExecuteTheCurrentTransactionGroupWithTheComposer)
s.Step(`^I simulate the current transaction group with the composer$`, iSimulateTheCurrentTransactionGroupWithTheComposer)
s.Step(`^The app should have returned "([^"]*)"\.$`, theAppShouldHaveReturned)
s.Step(`^The app should have returned ABI types "([^"]*)"\.$`, theAppShouldHaveReturnedABITypes)

Expand Down
1 change: 1 addition & 0 deletions test/integration.tags
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
@rekey_v1
@send
@send.keyregtxn
@simulate
87 changes: 87 additions & 0 deletions test/steps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ var sourceMap logic.SourceMap
var srcMapping map[string]interface{}
var seeminglyProgram []byte
var sanityCheckError error
var simulateResponse modelsV2.SimulateResponse

var assetTestFixture struct {
Creator string
Expand Down Expand Up @@ -335,6 +336,7 @@ func FeatureContext(s *godog.Suite) {
s.Step(`^the base64 encoded signed transactions should equal "([^"]*)"$`, theBaseEncodedSignedTransactionsShouldEqual)
s.Step(`^I build a payment transaction with sender "([^"]*)", receiver "([^"]*)", amount (\d+), close remainder to "([^"]*)"$`, iBuildAPaymentTransactionWithSenderReceiverAmountCloseRemainderTo)
s.Step(`^I create a transaction with signer with the current transaction\.$`, iCreateATransactionWithSignerWithTheCurrentTransaction)
s.Step(`^I create a transaction with an empty signer with the current transaction\.$`, iCreateATransactionWithAnEmptySignerWithTheCurrentTransaction)
s.Step(`^I append the current transaction with signer to the method arguments array\.$`, iAppendTheCurrentTransactionWithSignerToTheMethodArgumentsArray)
s.Step(`^a dryrun response file "([^"]*)" and a transaction at index "([^"]*)"$`, aDryrunResponseFileAndATransactionAtIndex)
s.Step(`^calling app trace produces "([^"]*)"$`, callingAppTraceProduces)
Expand All @@ -354,6 +356,10 @@ func FeatureContext(s *godog.Suite) {
s.Step(`^I start heuristic sanity check over the bytes$`, heuristicCheckOverBytes)
s.Step(`^if the heuristic sanity check throws an error, the error contains "([^"]*)"$`, checkErrorIfMatching)
s.Step(`^disassembly of "([^"]*)" matches "([^"]*)"$`, disassemblyMatches)
s.Step(`^I simulate the transaction$`, iSimulateTheTransaction)
s.Step(`^the simulation should succeed without any failure message$`, theSimulationShouldSucceedWithoutAnyFailureMessage)
s.Step(`^I prepare the transaction without signatures for simulation$`, iPrepareTheTransactionWithoutSignaturesForSimulation)
s.Step(`^the simulation should report a failure at group "([^"]*)", path "([^"]*)" with message "([^"]*)"$`, theSimulationShouldReportAFailureAtGroupPathWithMessage)

s.BeforeScenario(func(interface{}) {
stxObj = types.SignedTxn{}
Expand Down Expand Up @@ -2371,6 +2377,14 @@ func iCreateATransactionWithSignerWithTheCurrentTransaction() error {
return nil
}

func iCreateATransactionWithAnEmptySignerWithTheCurrentTransaction() error {
accountTxAndSigner = transaction.TransactionWithSigner{
Signer: transaction.EmptyTransactionSigner{},
Txn: txn,
}
return nil
}

func iAppendTheCurrentTransactionWithSignerToTheMethodArgumentsArray() error {
methodArgs = append(methodArgs, accountTxAndSigner)
return nil
Expand Down Expand Up @@ -2581,3 +2595,76 @@ func disassemblyMatches(bytecodeFilename, sourceFilename string) error {
}
return nil
}

func iSimulateTheTransaction() error {
var signedTxn types.SignedTxn
if err := msgpack.Decode(stx, &signedTxn); err != nil {
return err
}

resp, err := algodV2client.SimulateTransaction(modelsV2.SimulateRequest{
TxnGroups: []modelsV2.SimulateRequestTransactionGroup{
{
Txns: []types.SignedTxn{signedTxn},
},
},
}).Do(context.Background())
if err != nil {
return err
}

simulateResponse = resp
return nil
}

func theSimulationShouldSucceedWithoutAnyFailureMessage() error {
for i, groupResult := range simulateResponse.TxnGroups {
if groupResult.FailureMessage != "" {
return fmt.Errorf("Simulation group %d failed with message: %s", i, groupResult.FailureMessage)
}
}
return nil
}

func iPrepareTheTransactionWithoutSignaturesForSimulation() error {
stx = msgpack.Encode(&types.SignedTxn{Txn: txn})
return nil
}

func theSimulationShouldReportAFailureAtGroupPathWithMessage(txnGroupIndex, failAt, expectedFailureMsg string) error {
// Parse transaction group number
groupIndex, err := strconv.Atoi(txnGroupIndex)
if err != nil {
return err
}

// Parse the path ("0,0") into a list of numbers ([0, 0])
path := strings.Split(failAt, ",")
expectedPath := make([]uint64, len(path))
for i, pathStr := range path {
pathNumber, err := strconv.ParseUint(pathStr, 10, 64)
if err != nil {
return err
}
expectedPath[i] = pathNumber
}

actualFailureMsg := simulateResponse.TxnGroups[groupIndex].FailureMessage
if expectedFailureMsg == "" && actualFailureMsg != "" {
return fmt.Errorf("Expected no failure message, but got: '%s'", actualFailureMsg)
} else if expectedFailureMsg != "" && !strings.Contains(actualFailureMsg, expectedFailureMsg) {
return fmt.Errorf("Expected failure message '%s', but got: '%s'", expectedFailureMsg, actualFailureMsg)
}

actualPath := simulateResponse.TxnGroups[groupIndex].FailedAt
if len(expectedPath) != len(actualPath) {
return fmt.Errorf("Expected failure path %v, but got: %v", expectedPath, actualPath)
}
for i := range expectedPath {
if expectedPath[i] != actualPath[i] {
return fmt.Errorf("Expected failure path %v, but got: %v", expectedPath, actualPath)
}
}

return nil
}
Loading

0 comments on commit 7a4261b

Please sign in to comment.