diff --git a/test/e2e-go/features/transactions/accountv2_test.go b/test/e2e-go/features/transactions/accountv2_test.go index 52b39c9eed..c5e7eda64c 100644 --- a/test/e2e-go/features/transactions/accountv2_test.go +++ b/test/e2e-go/features/transactions/accountv2_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/require" "github.com/algorand/go-algorand/config" + "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/transactions/logic" @@ -335,3 +336,317 @@ int 1 // 3 global state update in total, 2 local state updates checkEvalDelta(t, &client, txnRound, txnRound+1, 3, 2) } + +// Add offending asset index greater than uint64 +func TestAccountInformationWithBadAssetIdx(t *testing.T) { + partitiontest.PartitionTest(t) + defer fixtures.ShutdownSynchronizedTest(t) + + t.Parallel() + AccountInformationCheckWithOffendingFields(t, []basics.AssetIndex{12181853637140359511}, nil, nil) +} + +// Add missing asset index +func TestAccountInformationWithMissingAssetIdx(t *testing.T) { + partitiontest.PartitionTest(t) + defer fixtures.ShutdownSynchronizedTest(t) + + AccountInformationCheckWithOffendingFields(t, []basics.AssetIndex{121818}, nil, nil) +} + +// Add offending app index greater than uint64 +func TestAccountInformationWithBadAppIdx(t *testing.T) { + partitiontest.PartitionTest(t) + defer fixtures.ShutdownSynchronizedTest(t) + + AccountInformationCheckWithOffendingFields(t, nil, []basics.AppIndex{12181853637140359511}, nil) +} + +// Add missing app index +func TestAccountInformationWithMissingApp(t *testing.T) { + partitiontest.PartitionTest(t) + defer fixtures.ShutdownSynchronizedTest(t) + + AccountInformationCheckWithOffendingFields(t, nil, []basics.AppIndex{121818}, nil) +} + +// Add missing account address +func TestAccountInformationWithMissingAddress(t *testing.T) { + partitiontest.PartitionTest(t) + defer fixtures.ShutdownSynchronizedTest(t) + + randAddr := basics.Address{} + crypto.RandBytes(randAddr[:]) + AccountInformationCheckWithOffendingFields(t, nil, nil, []basics.Address{randAddr}) +} + +func AccountInformationCheckWithOffendingFields(t *testing.T, + foreignAssets []basics.AssetIndex, + foreignApps []basics.AppIndex, + accounts []basics.Address) { + + a := require.New(fixtures.SynchronizedTest(t)) + + var fixture fixtures.RestClientFixture + proto, ok := config.Consensus[protocol.ConsensusFuture] + a.True(ok) + proto.AgreementFilterTimeoutPeriod0 = 400 * time.Millisecond + proto.AgreementFilterTimeout = 400 * time.Millisecond + fixture.SetConsensus(config.ConsensusProtocols{protocol.ConsensusFuture: proto}) + + fixture.Setup(t, filepath.Join("nettemplates", "TwoNodes50EachV26.json")) + defer fixture.Shutdown() + + client := fixture.LibGoalClient + accountList, err := fixture.GetWalletsSortedByBalance() + a.NoError(err) + + creator := accountList[0].Address + wh, err := client.GetUnencryptedWalletHandle() + a.NoError(err) + + user, err := client.GenerateAddress(wh) + a.NoError(err) + + fee := uint64(1000) + + var txn transactions.Transaction + + // Fund the manager, so it can issue transactions later on + txn, err = client.SendPaymentFromUnencryptedWallet(creator, user, fee, 10000000000, nil) + a.NoError(err) + + round, err := client.CurrentRound() + a.NoError(err) + fixture.WaitForConfirmedTxn(round+4, creator, txn.ID().String()) + + // There should be no apps to start with + ad, err := client.AccountData(creator) + a.NoError(err) + a.Zero(len(ad.AppParams)) + + ad, err = client.AccountData(user) + a.NoError(err) + a.Zero(len(ad.AppParams)) + a.Equal(basics.MicroAlgos{Raw: 10000000000}, ad.MicroAlgos) + + counter := `#pragma version 2 +// a simple global and local calls counter app +byte b64 Y291bnRlcg== // counter +dup +app_global_get +int 1 ++ +app_global_put // update the counter +int 0 +int 0 +app_opted_in +bnz opted_in +err +opted_in: +int 0 // account idx for app_local_put +byte b64 Y291bnRlcg== // counter +int 0 +byte b64 Y291bnRlcg== +app_local_get +int 1 // increment ++ +app_local_put +int 1 +` + approvalOps, err := logic.AssembleString(counter) + a.NoError(err) + clearstateOps, err := logic.AssembleString("#pragma version 2\nint 1") + a.NoError(err) + schema := basics.StateSchema{ + NumUint: 1, + } + + // create the app + tx, err := client.MakeUnsignedAppCreateTx( + transactions.OptInOC, approvalOps.Program, clearstateOps.Program, schema, schema, nil, nil, nil, nil, 0) + a.NoError(err) + tx, err = client.FillUnsignedTxTemplate(creator, 0, 0, fee, tx) + a.NoError(err) + wh, err = client.GetUnencryptedWalletHandle() + a.NoError(err) + signedTxn, err := client.SignTransactionWithWallet(wh, nil, tx) + a.NoError(err) + txid, err := client.BroadcastTransaction(signedTxn) + a.NoError(err) + round, err = client.CurrentRound() + a.NoError(err) + // ensure transaction is accepted into a block within 5 rounds. + confirmed := fixture.WaitForAllTxnsToConfirm(round+5, map[string]string{txid: signedTxn.Txn.Sender.String()}) + a.True(confirmed) + + // check creator's balance record for the app entry and the state changes + ad, err = client.AccountData(creator) + a.NoError(err) + a.Equal(1, len(ad.AppParams)) + var appIdx basics.AppIndex + var params basics.AppParams + for i, p := range ad.AppParams { + appIdx = i + params = p + break + } + a.Equal(approvalOps.Program, params.ApprovalProgram) + a.Equal(clearstateOps.Program, params.ClearStateProgram) + a.Equal(schema, params.LocalStateSchema) + a.Equal(schema, params.GlobalStateSchema) + a.Equal(1, len(params.GlobalState)) + value, ok := params.GlobalState["counter"] + a.True(ok) + a.Equal(uint64(1), value.Uint) + + a.Equal(1, len(ad.AppLocalStates)) + state, ok := ad.AppLocalStates[appIdx] + a.True(ok) + a.Equal(schema, state.Schema) + a.Equal(1, len(state.KeyValue)) + value, ok = state.KeyValue["counter"] + a.True(ok) + a.Equal(uint64(1), value.Uint) + + txInfo, err := fixture.LibGoalClient.PendingTransactionInformationV2(txid) + a.NoError(err) + a.NotNil(txInfo.ConfirmedRound) + a.NotZero(*txInfo.ConfirmedRound) + txnRound := *txInfo.ConfirmedRound + + // 1 global state update in total, 1 local state updates + checkEvalDelta(t, &client, txnRound, txnRound+1, 1, 1) + + // call the app + tx, err = client.MakeUnsignedAppOptInTx(uint64(appIdx), nil, nil, nil, nil) + a.NoError(err) + if foreignAssets != nil { + tx.ForeignAssets = foreignAssets + } + if foreignApps != nil { + tx.ForeignApps = foreignApps + } + if accounts != nil { + tx.Accounts = accounts + } + tx, err = client.FillUnsignedTxTemplate(user, 0, 0, fee, tx) + a.NoError(err) + wh, err = client.GetUnencryptedWalletHandle() + a.NoError(err) + signedTxn, err = client.SignTransactionWithWallet(wh, nil, tx) + a.NoError(err) + txid, err = client.BroadcastTransaction(signedTxn) + a.NoError(err) + round, err = client.CurrentRound() + a.NoError(err) + _, err = client.WaitForRound(round + 3) + a.NoError(err) + + // Ensure the txn committed + resp, err := client.GetPendingTransactions(2) + a.NoError(err) + a.Equal(uint64(0), resp.TotalTxns) + txinfo, err := client.TransactionInformation(signedTxn.Txn.Sender.String(), txid) + a.NoError(err) + a.True(txinfo.ConfirmedRound != 0) + + // check creator's balance record for the app entry and the state changes + ad, err = client.AccountData(creator) + a.NoError(err) + a.Equal(1, len(ad.AppParams)) + params, ok = ad.AppParams[appIdx] + a.True(ok) + a.Equal(approvalOps.Program, params.ApprovalProgram) + a.Equal(clearstateOps.Program, params.ClearStateProgram) + a.Equal(schema, params.LocalStateSchema) + a.Equal(schema, params.GlobalStateSchema) + a.Equal(1, len(params.GlobalState)) + value, ok = params.GlobalState["counter"] + a.True(ok) + a.Equal(uint64(2), value.Uint) + + a.Equal(1, len(ad.AppLocalStates)) + state, ok = ad.AppLocalStates[appIdx] + a.True(ok) + a.Equal(schema, state.Schema) + a.Equal(1, len(state.KeyValue)) + value, ok = state.KeyValue["counter"] + a.True(ok) + a.Equal(uint64(1), value.Uint) + + a.Equal(uint64(2), ad.TotalAppSchema.NumUint) + + // check user's balance record for the app entry and the state changes + ad, err = client.AccountData(user) + a.NoError(err) + a.Equal(0, len(ad.AppParams)) + + a.Equal(1, len(ad.AppLocalStates)) + state, ok = ad.AppLocalStates[appIdx] + a.True(ok) + a.Equal(schema, state.Schema) + a.Equal(1, len(state.KeyValue)) + value, ok = state.KeyValue["counter"] + a.True(ok) + a.Equal(uint64(1), value.Uint) + + txInfo, err = fixture.LibGoalClient.PendingTransactionInformationV2(txid) + a.NoError(err) + a.NotNil(txInfo.ConfirmedRound) + a.NotZero(*txInfo.ConfirmedRound) + txnRound = *txInfo.ConfirmedRound + + // 2 global state update in total, 1 local state updates + checkEvalDelta(t, &client, txnRound, txnRound+1, 2, 1) + + a.Equal(basics.MicroAlgos{Raw: 10000000000 - fee}, ad.MicroAlgos) + + app, err := client.ApplicationInformation(uint64(appIdx)) + a.NoError(err) + a.Equal(uint64(appIdx), app.Id) + a.Equal(creator, app.Params.Creator) + + // call the app + tx, err = client.MakeUnsignedAppNoOpTx(uint64(appIdx), nil, nil, nil, nil) + a.NoError(err) + tx, err = client.FillUnsignedTxTemplate(user, 0, 0, fee, tx) + a.NoError(err) + signedTxn, err = client.SignTransactionWithWallet(wh, nil, tx) + a.NoError(err) + txid, err = client.BroadcastTransaction(signedTxn) + a.NoError(err) + for { + round, err = client.CurrentRound() + a.NoError(err) + _, err = client.WaitForRound(round + 1) + a.NoError(err) + // Ensure the txn committed + resp, err = client.GetPendingTransactions(2) + a.NoError(err) + if resp.TotalTxns == 1 { + a.Equal(resp.TruncatedTxns.Transactions[0].TxID, txid) + continue + } + a.Equal(uint64(0), resp.TotalTxns) + break + } + + ad, err = client.AccountData(creator) + a.NoError(err) + a.Equal(1, len(ad.AppParams)) + params, ok = ad.AppParams[appIdx] + a.True(ok) + value, ok = params.GlobalState["counter"] + a.True(ok) + a.Equal(uint64(3), value.Uint) + + txInfo, err = fixture.LibGoalClient.PendingTransactionInformationV2(txid) + a.NoError(err) + a.NotNil(txInfo.ConfirmedRound) + a.NotZero(*txInfo.ConfirmedRound) + txnRound = *txInfo.ConfirmedRound + + // 3 global state update in total, 2 local state updates + checkEvalDelta(t, &client, txnRound, txnRound+1, 3, 2) +}