Skip to content
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
20 changes: 19 additions & 1 deletion daemon/algod/api/server/v2/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package v2
import (
"encoding/base64"
"errors"
"sort"

"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated"
Expand All @@ -45,12 +46,18 @@ func AccountDataToAccount(

assets = append(assets, holding)
}
sort.Slice(assets, func(i, j int) bool {
return assets[i].AssetId < assets[j].AssetId
})

createdAssets := make([]generated.Asset, 0, len(record.AssetParams))
for idx, params := range record.AssetParams {
asset := AssetParamsToAsset(address, idx, &params)
createdAssets = append(createdAssets, asset)
}
sort.Slice(createdAssets, func(i, j int) bool {
return createdAssets[i].Index < createdAssets[j].Index
})

var apiParticipation *generated.AccountParticipation
if record.VoteID != (crypto.OneTimeSignatureVerifier{}) {
Expand All @@ -68,6 +75,9 @@ func AccountDataToAccount(
app := AppParamsToApplication(address, appIdx, &appParams)
createdApps = append(createdApps, app)
}
sort.Slice(createdApps, func(i, j int) bool {
return createdApps[i].Id < createdApps[j].Id
})

appsLocalState := make([]generated.ApplicationLocalState, 0, len(record.AppLocalStates))
for appIdx, state := range record.AppLocalStates {
Expand All @@ -81,6 +91,9 @@ func AccountDataToAccount(
},
})
}
sort.Slice(appsLocalState, func(i, j int) bool {
return appsLocalState[i].Id < appsLocalState[j].Id
})

totalAppSchema := generated.ApplicationStateSchema{
NumByteSlice: record.TotalAppSchema.NumByteSlice,
Expand Down Expand Up @@ -118,7 +131,8 @@ func convertTKVToGenerated(tkv *basics.TealKeyValue) *generated.TealKeyValueStor
return nil
}

var converted generated.TealKeyValueStore
converted := make(generated.TealKeyValueStore, 0, len(*tkv))
rawKeyBytes := make([]string, 0, len(*tkv))
for k, v := range *tkv {
converted = append(converted, generated.TealKeyValue{
Key: base64.StdEncoding.EncodeToString([]byte(k)),
Expand All @@ -128,7 +142,11 @@ func convertTKVToGenerated(tkv *basics.TealKeyValue) *generated.TealKeyValueStor
Uint: v.Uint,
},
})
rawKeyBytes = append(rawKeyBytes, k)
}
sort.Slice(converted, func(i, j int) bool {
return rawKeyBytes[i] < rawKeyBytes[j]
})
return &converted
}

Expand Down
109 changes: 58 additions & 51 deletions daemon/algod/api/server/v2/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func TestAccount(t *testing.T) {
ApprovalProgram: []byte{1},
StateSchemas: basics.StateSchemas{
GlobalStateSchema: basics.StateSchema{NumUint: 1},
LocalStateSchema: basics.StateSchema{NumByteSlice: 5},
},
}
appParams2 := basics.AppParams{
Expand All @@ -52,11 +53,14 @@ func TestAccount(t *testing.T) {
Total: 100,
DefaultFrozen: false,
UnitName: "unit1",
Decimals: 0,
}
assetParams2 := basics.AssetParams{
Total: 200,
DefaultFrozen: true,
UnitName: "unit2",
Decimals: 6,
MetadataHash: [32]byte{1},
}
copy(assetParams2.MetadataHash[:], []byte("test2"))
a := basics.AccountData{
Expand Down Expand Up @@ -88,28 +92,24 @@ func TestAccount(t *testing.T) {
addr := basics.Address{}.String()
conv, err := AccountDataToAccount(addr, &b, map[basics.AssetIndex]string{}, round, a.MicroAlgos)
require.NoError(t, err)
require.Equal(t, conv.Address, addr)
require.Equal(t, conv.Amount, b.MicroAlgos.Raw)
require.Equal(t, conv.AmountWithoutPendingRewards, a.MicroAlgos.Raw)
require.Equal(t, addr, conv.Address)
require.Equal(t, b.MicroAlgos.Raw, conv.Amount)
require.Equal(t, a.MicroAlgos.Raw, conv.AmountWithoutPendingRewards)

verifyCreatedApp := func(index int, appIdx basics.AppIndex, params basics.AppParams) {
require.Equal(t, uint64(appIdx), (*conv.CreatedApps)[index].Id)
require.Equal(t, params.ApprovalProgram, (*conv.CreatedApps)[index].Params.ApprovalProgram)
require.Equal(t, params.GlobalStateSchema.NumUint, (*conv.CreatedApps)[index].Params.GlobalStateSchema.NumUint)
require.Equal(t, params.GlobalStateSchema.NumByteSlice, (*conv.CreatedApps)[index].Params.GlobalStateSchema.NumByteSlice)
require.Equal(t, params.LocalStateSchema.NumUint, (*conv.CreatedApps)[index].Params.LocalStateSchema.NumUint)
require.Equal(t, params.LocalStateSchema.NumByteSlice, (*conv.CreatedApps)[index].Params.LocalStateSchema.NumByteSlice)
}

require.NotNil(t, conv.CreatedApps)
require.Equal(t, 2, len(*conv.CreatedApps))
for _, app := range *conv.CreatedApps {
var params basics.AppParams
if app.Id == uint64(appIdx1) {
params = appParams1
} else if app.Id == uint64(appIdx2) {
params = appParams2
} else {
require.Fail(t, fmt.Sprintf("app idx %d not in [%d, %d]", app.Id, appIdx1, appIdx2))
}
require.Equal(t, params.ApprovalProgram, app.Params.ApprovalProgram)
require.Equal(t, params.GlobalStateSchema.NumUint, app.Params.GlobalStateSchema.NumUint)
require.Equal(t, params.GlobalStateSchema.NumByteSlice, app.Params.GlobalStateSchema.NumByteSlice)
}
verifyCreatedApp(0, appIdx1, appParams1)
verifyCreatedApp(1, appIdx2, appParams2)

require.NotNil(t, conv.AppsLocalState)
require.Equal(t, 2, len(*conv.AppsLocalState))
makeTKV := func(k string, v interface{}) generated.TealKeyValue {
value := generated.TealValue{}
switch v.(type) {
Expand All @@ -127,47 +127,54 @@ func TestAccount(t *testing.T) {
Value: value,
}
}
for _, ls := range *conv.AppsLocalState {
require.Equal(t, uint64(10), ls.Schema.NumUint)
require.Equal(t, uint64(0), ls.Schema.NumByteSlice)
require.Equal(t, 2, len(*ls.KeyValue))
var value1 generated.TealKeyValue
var value2 generated.TealKeyValue
if ls.Id == uint64(appIdx1) {
value1 = makeTKV("uint", 1)
value2 = makeTKV("bytes", "value1")
} else if ls.Id == uint64(appIdx2) {
value1 = makeTKV("uint", 2)
value2 = makeTKV("bytes", "value2")
} else {
require.Fail(t, fmt.Sprintf("local state app idx %d not in [%d, %d]", ls.Id, appIdx1, appIdx2))

verifyAppLocalState := func(index int, appIdx basics.AppIndex, numUints, numByteSlices uint64, keyValues generated.TealKeyValueStore) {
require.Equal(t, uint64(appIdx), (*conv.AppsLocalState)[index].Id)
require.Equal(t, numUints, (*conv.AppsLocalState)[index].Schema.NumUint)
require.Equal(t, numByteSlices, (*conv.AppsLocalState)[index].Schema.NumByteSlice)
require.Equal(t, len(keyValues), len(*(*conv.AppsLocalState)[index].KeyValue))
for i, keyValue := range keyValues {
require.Equal(t, keyValue, (*(*conv.AppsLocalState)[index].KeyValue)[i])
}
require.Contains(t, *ls.KeyValue, value1)
require.Contains(t, *ls.KeyValue, value2)
}

require.NotNil(t, conv.CreatedAssets)
require.Equal(t, 2, len(*conv.CreatedAssets))
for _, asset := range *conv.CreatedAssets {
var params basics.AssetParams
if asset.Index == uint64(assetIdx1) {
params = assetParams1
} else if asset.Index == uint64(assetIdx2) {
params = assetParams2
require.NotNil(t, conv.AppsLocalState)
require.Equal(t, 2, len(*conv.AppsLocalState))
verifyAppLocalState(0, appIdx1, 10, 0, generated.TealKeyValueStore{makeTKV("bytes", "value1"), makeTKV("uint", 1)})
verifyAppLocalState(1, appIdx2, 10, 0, generated.TealKeyValueStore{makeTKV("bytes", "value2"), makeTKV("uint", 2)})

verifyCreatedAsset := func(index int, assetIdx basics.AssetIndex, params basics.AssetParams) {
require.Equal(t, uint64(assetIdx), (*conv.CreatedAssets)[index].Index)
require.Equal(t, params.Total, (*conv.CreatedAssets)[index].Params.Total)
require.NotNil(t, (*conv.CreatedAssets)[index].Params.DefaultFrozen)
require.Equal(t, params.DefaultFrozen, *(*conv.CreatedAssets)[index].Params.DefaultFrozen)
require.NotNil(t, (*conv.CreatedAssets)[index].Params.UnitName)
require.Equal(t, params.UnitName, *(*conv.CreatedAssets)[index].Params.UnitName)
if params.MetadataHash == ([32]byte{}) {
require.Nil(t, (*conv.CreatedAssets)[index].Params.MetadataHash)
} else {
require.Fail(t, fmt.Sprintf("asset idx %d not in [%d, %d]", asset.Index, assetIdx1, assetIdx2))
}
require.Equal(t, params.Total, asset.Params.Total)
require.NotNil(t, asset.Params.DefaultFrozen)
require.Equal(t, params.DefaultFrozen, *asset.Params.DefaultFrozen)
require.NotNil(t, asset.Params.UnitName)
require.Equal(t, params.UnitName, *asset.Params.UnitName)
if asset.Params.MetadataHash != nil {
require.Equal(t, params.MetadataHash[:], *asset.Params.MetadataHash)
require.NotNil(t, (*conv.CreatedAssets)[index].Params.MetadataHash)
require.Equal(t, params.MetadataHash[:], *(*conv.CreatedAssets)[index].Params.MetadataHash)
}
}

require.NotNil(t, conv.CreatedAssets)
require.Equal(t, 2, len(*conv.CreatedAssets))
verifyCreatedAsset(0, assetIdx1, assetParams1)
verifyCreatedAsset(1, assetIdx2, assetParams2)

c, err := AccountToAccountData(&conv)
require.NoError(t, err)
require.Equal(t, b, c)

t.Run("IsDeterministic", func(t *testing.T) {
// convert the same account a few more times to make sure we always
// produce the same generated.Account
for i := 0; i < 10; i++ {
anotherConv, err := AccountDataToAccount(addr, &b, map[basics.AssetIndex]string{}, round, a.MicroAlgos)
require.NoError(t, err)

require.Equal(t, protocol.EncodeJSON(conv), protocol.EncodeJSON(anotherConv))
}
})
}