Skip to content

Commit

Permalink
Return all inner transactions are returned for logs endpoint. (#915)
Browse files Browse the repository at this point in the history
  • Loading branch information
algochoi authored Mar 9, 2022
1 parent 5b88518 commit 82b9fe3
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 15 deletions.
4 changes: 3 additions & 1 deletion api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -949,7 +949,9 @@ func (si *ServerImplementation) fetchTransactions(ctx context.Context, filter id
}

// The root txn has already been added.
if _, ok := rootTxnDedupeMap[*tx.Id]; ok {
// If we also want to return inner txns, we cannot deduplicate the
// results as inner txns all share the same txn ID as its root txn.
if _, ok := rootTxnDedupeMap[*tx.Id]; ok && !filter.ReturnInnerTxnOnly {
continue
}

Expand Down
103 changes: 103 additions & 0 deletions api/handlers_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -649,3 +649,106 @@ func TestLookupInnerLogs(t *testing.T) {
})
}
}

// TestLookupInnerLogs runs queries for logs given application ids,
// and checks that logs in inner transactions match properly.
func TestLookupMultiInnerLogs(t *testing.T) {
var appAddr basics.Address
appAddr[1] = 99

params := generated.LookupApplicationLogsByIDParams{}

testcases := []struct {
name string
appID uint64
numTxnsWithLogs int
logs []string
}{
{
name: "match on root with appId 123",
appID: 123,
numTxnsWithLogs: 1,
logs: []string{
"testing outer appl log",
"appId 123 log",
},
},
{
name: "match on inner with appId 789",
appID: 789,
numTxnsWithLogs: 1,
logs: []string{
"testing inner log",
"appId 789 log",
},
},
{
name: "match on inner with appId 222",
appID: 222,
numTxnsWithLogs: 3, // There are 6 logs over 3 transactions
logs: []string{
"testing multiple logs 1",
"appId 222 log 1",
"testing multiple logs 2",
"appId 222 log 2",
"testing multiple logs 3",
"appId 222 log 3",
},
},
}

db, shutdownFunc := setupIdb(t, test.MakeGenesis(), test.MakeGenesisBlock())
defer shutdownFunc()

///////////
// Given // a DB with some inner txns in it.
///////////
appCall := test.MakeAppCallWithMultiLogs(test.AccountA)

block, err := test.MakeBlockForTxns(test.MakeGenesisBlock().BlockHeader, &appCall)
require.NoError(t, err)

err = db.AddBlock(&block)
require.NoError(t, err, "failed to commit")

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
//////////
// When // we run a query that queries logs based on appID
//////////
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
c.SetPath("/v2/applications/:appIdx/logs")
c.SetParamNames("appIdx")
c.SetParamValues(fmt.Sprintf("%d", tc.appID))

api := &ServerImplementation{db: db, timeout: 30 * time.Second}
err = api.LookupApplicationLogsByID(c, tc.appID, params)
require.NoError(t, err)

//////////
// Then // The result is the log from the app
//////////
var response generated.ApplicationLogsResponse
require.Equal(t, http.StatusOK, rec.Code)
json.Decode(rec.Body.Bytes(), &response)
require.NoError(t, err)

require.Equal(t, uint64(tc.appID), response.ApplicationId)
require.NotNil(t, response.LogData)
ld := *response.LogData
require.Equal(t, tc.numTxnsWithLogs, len(ld))

logCount := 0
for txnIndex, result := range ld {
for logIndex, log := range result.Logs {
require.Equal(t, []byte(tc.logs[txnIndex*2+logIndex]), log)
logCount++
}
}
require.Equal(t, logCount, len(tc.logs))
})
}
}
105 changes: 91 additions & 14 deletions util/test/account_testutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,33 @@ func MakeAppOptOutTxn(appid uint64, sender basics.Address) transactions.SignedTx
}
}

// MakeAppCallTxn makes an appl transaction with a NoOp upon completion.
func MakeAppCallTxn(appid uint64, sender basics.Address) transactions.SignedTxnWithAD {
return transactions.SignedTxnWithAD{
SignedTxn: transactions.SignedTxn{
Txn: transactions.Transaction{
Type: "appl",
Header: transactions.Header{
Sender: sender,
GenesisHash: GenesisHash,
},
ApplicationCallTxnFields: transactions.ApplicationCallTxnFields{
ApplicationID: basics.AppIndex(appid),
OnCompletion: transactions.NoOpOC,
},
},
Sig: Signature,
},
}
}

// MakeAppCallTxnWithLogs makes an appl NoOp transaction with initialized logs.
func MakeAppCallTxnWithLogs(appid uint64, sender basics.Address, logs []string) (txn transactions.SignedTxnWithAD) {
txn = MakeAppCallTxn(appid, sender)
txn.ApplyData.EvalDelta.Logs = logs
return
}

// MakeAppCallWithInnerTxn creates an app call with 3 levels of transactions:
// application create
// |- payment
Expand Down Expand Up @@ -324,24 +351,74 @@ func MakeAppCallWithInnerTxn(appSender, paymentSender, paymentReceiver, assetSen
},
},
// Inner application call
{
SignedTxn: transactions.SignedTxn{
Txn: transactions.Transaction{
Type: protocol.ApplicationCallTx,
Header: transactions.Header{
Sender: assetSender,
},
ApplicationCallTxnFields: transactions.ApplicationCallTxnFields{
ApplicationID: 789,
OnCompletion: transactions.NoOpOC,
},
},
},
},
MakeAppCallTxn(789, assetSender),
},
},
},
},
}

return createApp
}

// MakeAppCallWithMultiLogs creates an app call that creates multiple logs
// at the same level.
// application create
// |- application call
// |- application call
// |- application call
// |- application call
// |- application call
func MakeAppCallWithMultiLogs(appSender basics.Address) transactions.SignedTxnWithAD {
createApp := MakeCreateAppTxn(appSender)

// Add a log to the outer appl call
createApp.ApplicationID = 123
createApp.ApplyData.EvalDelta.Logs = []string{
"testing outer appl log",
"appId 123 log",
}

createApp.ApplyData.EvalDelta.InnerTxns = []transactions.SignedTxnWithAD{
{
SignedTxn: transactions.SignedTxn{
Txn: transactions.Transaction{
Type: protocol.ApplicationCallTx,
Header: transactions.Header{
Sender: appSender,
},
ApplicationCallTxnFields: transactions.ApplicationCallTxnFields{
ApplicationID: 789,
OnCompletion: transactions.NoOpOC,
},
},
},
// also add a fake second-level ApplyData to ensure the recursive part works
ApplyData: transactions.ApplyData{
EvalDelta: transactions.EvalDelta{
InnerTxns: []transactions.SignedTxnWithAD{
// Inner application call
MakeAppCallTxn(789, appSender),
},
Logs: []string{
"testing inner log",
"appId 789 log",
},
},
},
},
MakeAppCallTxnWithLogs(222, appSender, []string{
"testing multiple logs 1",
"appId 222 log 1",
}),
MakeAppCallTxnWithLogs(222, appSender, []string{
"testing multiple logs 2",
"appId 222 log 2",
}),
MakeAppCallTxnWithLogs(222, appSender, []string{
"testing multiple logs 3",
"appId 222 log 3",
}),
}

return createApp
Expand Down

0 comments on commit 82b9fe3

Please sign in to comment.