Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API: Add types for ledgercore.StateDelta. #467

Merged
merged 16 commits into from
Feb 28, 2023
3 changes: 2 additions & 1 deletion client/v2/algod/accountApplicationInformation.go
Original file line number Diff line number Diff line change
@@ -11,7 +11,8 @@ import (
// AccountApplicationInformationParams contains all of the query parameters for url serialization.
type AccountApplicationInformationParams struct {

// Format configures whether the response object is JSON or MessagePack encoded.
// Format configures whether the response object is JSON or MessagePack encoded. If
// not provided, defaults to JSON.
Format string `url:"format,omitempty"`
}

3 changes: 2 additions & 1 deletion client/v2/algod/accountAssetInformation.go
Original file line number Diff line number Diff line change
@@ -11,7 +11,8 @@ import (
// AccountAssetInformationParams contains all of the query parameters for url serialization.
type AccountAssetInformationParams struct {

// Format configures whether the response object is JSON or MessagePack encoded.
// Format configures whether the response object is JSON or MessagePack encoded. If
// not provided, defaults to JSON.
Format string `url:"format,omitempty"`
}

3 changes: 2 additions & 1 deletion client/v2/algod/accountInformation.go
Original file line number Diff line number Diff line change
@@ -16,7 +16,8 @@ type AccountInformationParams struct {
// `none`.
Exclude string `url:"exclude,omitempty"`

// Format configures whether the response object is JSON or MessagePack encoded.
// Format configures whether the response object is JSON or MessagePack encoded. If
// not provided, defaults to JSON.
Format string `url:"format,omitempty"`
}

4 changes: 0 additions & 4 deletions client/v2/algod/algod.go
Original file line number Diff line number Diff line change
@@ -121,10 +121,6 @@ func (c *Client) PendingTransactionInformation(txid string) *PendingTransactionI
return &PendingTransactionInformation{c: c, txid: txid}
}

func (c *Client) GetLedgerStateDelta(round uint64) *GetLedgerStateDelta {
return &GetLedgerStateDelta{c: c, round: round}
}

func (c *Client) GetStateProof(round uint64) *GetStateProof {
return &GetStateProof{c: c, round: round}
}
3 changes: 2 additions & 1 deletion client/v2/algod/getBlock.go
Original file line number Diff line number Diff line change
@@ -12,7 +12,8 @@ import (
// BlockParams contains all of the query parameters for url serialization.
type BlockParams struct {

// Format configures whether the response object is JSON or MessagePack encoded.
// Format configures whether the response object is JSON or MessagePack encoded. If
// not provided, defaults to JSON.
Format string `url:"format,omitempty"`
}

22 changes: 0 additions & 22 deletions client/v2/algod/getLedgerStateDelta.go

This file was deleted.

3 changes: 2 additions & 1 deletion client/v2/algod/getPendingTransactions.go
Original file line number Diff line number Diff line change
@@ -11,7 +11,8 @@ import (
// PendingTransactionsParams contains all of the query parameters for url serialization.
type PendingTransactionsParams struct {

// Format configures whether the response object is JSON or MessagePack encoded.
// Format configures whether the response object is JSON or MessagePack encoded. If
// not provided, defaults to JSON.
Format string `url:"format,omitempty"`

// Max truncated number of transactions to display. If max=0, returns all pending
3 changes: 2 additions & 1 deletion client/v2/algod/getPendingTransactionsByAddress.go
Original file line number Diff line number Diff line change
@@ -12,7 +12,8 @@ import (
// PendingTransactionsByAddressParams contains all of the query parameters for url serialization.
type PendingTransactionsByAddressParams struct {

// Format configures whether the response object is JSON or MessagePack encoded.
// Format configures whether the response object is JSON or MessagePack encoded. If
// not provided, defaults to JSON.
Format string `url:"format,omitempty"`

// Max truncated number of transactions to display. If max=0, returns all pending
3 changes: 2 additions & 1 deletion client/v2/algod/getTransactionProof.go
Original file line number Diff line number Diff line change
@@ -11,7 +11,8 @@ import (
// GetTransactionProofParams contains all of the query parameters for url serialization.
type GetTransactionProofParams struct {

// Format configures whether the response object is JSON or MessagePack encoded.
// Format configures whether the response object is JSON or MessagePack encoded. If
// not provided, defaults to JSON.
Format string `url:"format,omitempty"`

// Hashtype the type of hash function used to create the proof, must be one of:
3 changes: 2 additions & 1 deletion client/v2/algod/pendingTransactionInformation.go
Original file line number Diff line number Diff line change
@@ -12,7 +12,8 @@ import (
// PendingTransactionInformationParams contains all of the query parameters for url serialization.
type PendingTransactionInformationParams struct {

// Format configures whether the response object is JSON or MessagePack encoded.
// Format configures whether the response object is JSON or MessagePack encoded. If
// not provided, defaults to JSON.
Format string `url:"format,omitempty"`
}

3 changes: 2 additions & 1 deletion client/v2/algod/rawTransaction.go
Original file line number Diff line number Diff line change
@@ -8,7 +8,8 @@ import (
"github.com/algorand/go-algorand-sdk/v2/client/v2/common/models"
)

// SendRawTransaction broadcasts a raw transaction to the network.
// SendRawTransaction broadcasts a raw transaction or transaction group to the
// network.
type SendRawTransaction struct {
c *Client

10 changes: 0 additions & 10 deletions client/v2/common/models/account_balance_record.go

This file was deleted.

13 changes: 0 additions & 13 deletions client/v2/common/models/account_deltas.go

This file was deleted.

16 changes: 0 additions & 16 deletions client/v2/common/models/account_totals.go

This file was deleted.

22 changes: 0 additions & 22 deletions client/v2/common/models/app_resource_record.go

This file was deleted.

22 changes: 0 additions & 22 deletions client/v2/common/models/asset_resource_record.go

This file was deleted.

28 changes: 0 additions & 28 deletions client/v2/common/models/ledger_state_delta.go

This file was deleted.

13 changes: 0 additions & 13 deletions client/v2/common/models/modified_app.go

This file was deleted.

13 changes: 0 additions & 13 deletions client/v2/common/models/modified_asset.go

This file was deleted.

24 changes: 24 additions & 0 deletions client/v2/common/models/node_status_response.go
Original file line number Diff line number Diff line change
@@ -66,4 +66,28 @@ type NodeStatusResponse struct {

// TimeSinceLastRound timeSinceLastRound in nanoseconds
TimeSinceLastRound uint64 `json:"time-since-last-round"`

// UpgradeDelay upgrade delay
UpgradeDelay uint64 `json:"upgrade-delay,omitempty"`

// UpgradeNextProtocolVoteBefore next protocol round
UpgradeNextProtocolVoteBefore uint64 `json:"upgrade-next-protocol-vote-before,omitempty"`

// UpgradeNoVotes no votes cast for consensus upgrade
UpgradeNoVotes uint64 `json:"upgrade-no-votes,omitempty"`

// UpgradeNodeVote this node's upgrade vote
UpgradeNodeVote bool `json:"upgrade-node-vote,omitempty"`

// UpgradeVoteRounds total voting ounds for current upgrade
UpgradeVoteRounds uint64 `json:"upgrade-vote-rounds,omitempty"`

// UpgradeVotes total votes cast for consensus upgrade
UpgradeVotes uint64 `json:"upgrade-votes,omitempty"`

// UpgradeVotesRequired yes votes required for consensus upgrade
UpgradeVotesRequired uint64 `json:"upgrade-votes-required,omitempty"`

// UpgradeYesVotes yes votes cast for consensus upgrade
UpgradeYesVotes uint64 `json:"upgrade-yes-votes,omitempty"`
}
13 changes: 0 additions & 13 deletions client/v2/common/models/tx_lease.go

This file was deleted.

5 changes: 5 additions & 0 deletions client/v2/indexer/indexer.go
Original file line number Diff line number Diff line change
@@ -10,6 +10,11 @@ const authHeader = "X-Indexer-API-Token"

type Client common.Client

// delete performs a DELETE request to the specific path against the server, assumes JSON response
func (c *Client) delete(ctx context.Context, response interface{}, path string, body interface{}, headers []*common.Header) error {
return (*common.Client)(c).Delete(ctx, response, path, body, headers)
}

// get performs a GET request to the specific path against the server, assumes JSON response
func (c *Client) get(ctx context.Context, response interface{}, path string, body interface{}, headers []*common.Header) error {
return (*common.Client)(c).Get(ctx, response, path, body, headers)
22 changes: 22 additions & 0 deletions encoding/json/json.go
Original file line number Diff line number Diff line change
@@ -13,6 +13,10 @@ var CodecHandle *codec.JsonHandle
// LenientCodecHandle is used to instantiate msgpack encoders for the REST API.
var LenientCodecHandle *codec.JsonHandle

// JSONStrictHandle is the same as CodecHandle but with MapKeyAsString=true
// for correct maps[int]interface{} encoding
var JSONStrictHandle *codec.JsonHandle

// init configures our json encoder and decoder
func init() {
CodecHandle = new(codec.JsonHandle)
@@ -30,6 +34,15 @@ func init() {
LenientCodecHandle.RecursiveEmptyCheck = true
LenientCodecHandle.Indent = 2
LenientCodecHandle.HTMLCharsAsIs = true

JSONStrictHandle = new(codec.JsonHandle)
JSONStrictHandle.ErrorIfNoField = CodecHandle.ErrorIfNoField
JSONStrictHandle.ErrorIfNoArrayExpand = CodecHandle.ErrorIfNoArrayExpand
JSONStrictHandle.Canonical = CodecHandle.Canonical
JSONStrictHandle.RecursiveEmptyCheck = CodecHandle.RecursiveEmptyCheck
JSONStrictHandle.Indent = CodecHandle.Indent
JSONStrictHandle.HTMLCharsAsIs = CodecHandle.HTMLCharsAsIs
JSONStrictHandle.MapKeyAsString = true
}

// Encode returns a json-encoded byte buffer for a given object
@@ -40,6 +53,15 @@ func Encode(obj interface{}) []byte {
return b
}

// EncodeStrict returns a JSON-encoded byte buffer for a given object
// It is the same Encode but encodes map's int keys as strings
func EncodeStrict(obj interface{}) []byte {
var b []byte
enc := codec.NewEncoderBytes(&b, JSONStrictHandle)
enc.MustEncode(obj)
return b
}

// Decode attempts to decode a json-encoded byte buffer into an
// object instance pointed to by objptr
func Decode(b []byte, objptr interface{}) error {
34 changes: 30 additions & 4 deletions encoding/json/json_test.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

type object struct {
@@ -27,7 +28,7 @@ func TestDecode(t *testing.T) {
// basic encode/decode test.
var decoded object
err := Decode(encodedOb, &decoded)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, obj, decoded)
})

@@ -36,7 +37,7 @@ func TestDecode(t *testing.T) {
decoder := NewDecoder(bytes.NewReader(encodedOb))
var decoded object
err := decoder.Decode(&decoded)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, obj, decoded)
})

@@ -45,7 +46,7 @@ func TestDecode(t *testing.T) {
decoder := NewDecoder(bytes.NewReader(encodedOb))
var decoded subsetObject
err := decoder.Decode(&decoded)
assert.Error(t, err)
require.Error(t, err)
assert.Contains(t, err.Error(), "no matching struct field found when decoding stream map with key name")
})

@@ -54,7 +55,32 @@ func TestDecode(t *testing.T) {
decoder := NewLenientDecoder(bytes.NewReader(encodedOb))
var decoded subsetObject
err := decoder.Decode(&decoded)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, obj.subsetObject, decoded)
})

t.Run("original encode map key as string", func(t *testing.T) {
intMap := map[int]string{
0: "int key",
}
data := string(Encode(intMap))
assert.NotContains(t, data, "\"0\":")
})

t.Run("strict encode map key as string", func(t *testing.T) {
intMap := map[int]string{
0: "int key",
}
data := string(EncodeStrict(intMap))
assert.NotContains(t, data, "0:")
})

t.Run("strict encode map interface key as string", func(t *testing.T) {
t.Skip("There is a bug in go-codec with MapKeyAsString = true and Canonical = true")
intMap := map[interface{}]interface{}{
0: "int key",
}
data := string(EncodeStrict(intMap))
assert.NotContains(t, data, "0:")
})
}
2 changes: 1 addition & 1 deletion test/responses_unit_test.go
Original file line number Diff line number Diff line change
@@ -216,7 +216,7 @@ func theParsedResponseShouldEqualTheMockResponse() error {
if responseStr, ok := response.(string); ok {
responseJson = responseStr
} else {
responseJson = string(json.Encode(response))
responseJson = string(json.EncodeStrict(response))
}
}

1 change: 0 additions & 1 deletion test/unit.tags
Original file line number Diff line number Diff line change
@@ -28,7 +28,6 @@
@unit.sourcemap
@unit.stateproof.paths
@unit.stateproof.responses
@unit.stateproof.responses.msgp
@unit.statedelta
@unit.tealsign
@unit.transactions
64 changes: 55 additions & 9 deletions test/utilities.go
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ import (

sdk_json "github.com/algorand/go-algorand-sdk/v2/encoding/json"
"github.com/algorand/go-algorand-sdk/v2/encoding/msgpack"
"github.com/algorand/go-algorand-sdk/v2/types"
)

func VerifyResponse(expectedFile string, actual string) error {
@@ -43,7 +44,7 @@ func VerifyResponse(expectedFile string, actual string) error {
if err != nil {
return fmt.Errorf("failed to decode '%s' from message pack: %v", expectedFile, err)
}
expectedString = string(sdk_json.Encode(generic))
expectedString = string(sdk_json.EncodeStrict(generic))
}

err = EqualJson2(expectedString, actual)
@@ -60,10 +61,16 @@ func VerifyResponse(expectedFile string, actual string) error {
// For reference: j1 is the baseline, j2 is the test
func EqualJson2(j1, j2 string) (err error) {
var expected map[string]interface{}
json.Unmarshal([]byte(j1), &expected)
err = json.Unmarshal([]byte(j1), &expected)
if err != nil {
return fmt.Errorf("failed to unmarshal expected: %w", err)
}

var actual map[string]interface{}
json.Unmarshal([]byte(j2), &actual)
err = json.Unmarshal([]byte(j2), &actual)
if err != nil {
return fmt.Errorf("failed to unmarshal actual: %w", err)
}

err = recursiveCompare("root", expected, actual)

@@ -113,18 +120,57 @@ func getType(val interface{}) ValueType {
// The decoding process doesn't seem to distinguish between string and binary, but the encoding process
// does. So sometimes the string will be base64 encoded and need to compare against the decoded string
// value.
// There are some discrepancies in different algod / SDK types that causes
// encodings that need special handling:
// * Address is sometimes B32 encoded and sometimes B64 encoded.
// * BlockHash is sometimes B32 encoded (with a blk prefix) and sometimes B64 encoded.
func binaryOrStringEqual(s1, s2 string) bool {
if s1 == s2 {
return true
}
if val, err := base64.StdEncoding.DecodeString(s1); err == nil {
if string(val) == s2 {
return true
// S1 convert to S2
{
if val, err := base64.StdEncoding.DecodeString(s1); err == nil {
if string(val) == s2 {
return true
}
var addr types.Address
if len(val) == len(addr[:]) {
copy(addr[:], val)
if addr.String() == s2 {
return true
}
}

}
// parse blockhash
var bh types.BlockHash
if bh.UnmarshalText([]byte(s1)) == nil {
if base64.StdEncoding.EncodeToString(bh[:]) == s2 {
return true
}
}
}
if val, err := base64.StdEncoding.DecodeString(s2); err == nil {
if string(val) == s1 {
return true

// S2 convert to S1
{
if val, err := base64.StdEncoding.DecodeString(s2); err == nil {
if string(val) == s1 {
return true
}
var addr types.Address
if len(val) == len(addr[:]) {
copy(addr[:], val)
if addr.String() == s1 {
return true
}
}
}
var bh types.BlockHash
if bh.UnmarshalText([]byte(s2)) == nil {
if base64.StdEncoding.EncodeToString(bh[:]) == s1 {
return true
}
}
}
return false
6 changes: 3 additions & 3 deletions types/blockhash.go
Original file line number Diff line number Diff line change
@@ -24,13 +24,13 @@ func (b *BlockHash) UnmarshalText(text []byte) error {
*b = BlockHash(d)
return nil
}

// ignore the DigestFromString error because it isn't the native MarshalText format.

// Attempt to decode base64 format
var data BlockHash
n, err := base64.StdEncoding.Decode(data[:], text)
data, err := base64.StdEncoding.DecodeString(string(text))
if err == nil {
if n != len(b[:]) {
if len(data) != len(b[:]) {
return errWrongBlockHashLen
}
copy(b[:], data[:])
4 changes: 4 additions & 0 deletions types/blockhash_test.go
Original file line number Diff line number Diff line change
@@ -36,6 +36,10 @@ func TestUnmarshalBlockHash(t *testing.T) {
name: "B64-err-illegal",
input: "bogus",
err: "illegal base64 data at input byte 4",
}, {
name: "Overflow does not panic",
input: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ",
err: "illegal base64 data at input byte 56",
},
}

327 changes: 327 additions & 0 deletions types/statedelta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,327 @@
package types

// TealType is an enum of the types in a TEAL program: Bytes and Uint
type TealType uint64

const (
// TealBytesType represents the type of a byte slice in a TEAL program
TealBytesType TealType = 1

// TealUintType represents the type of a uint in a TEAL program
TealUintType TealType = 2
)

// Status is the delegation status of an account's MicroAlgos
type Status byte

const (
// Offline indicates that the associated account receives rewards but does not participate in the consensus.
Offline Status = iota
// Online indicates that the associated account participates in the consensus and receive rewards.
Online
// NotParticipating indicates that the associated account neither participates in the consensus, nor receives rewards.
// Accounts that are marked as NotParticipating cannot change their status, but can receive and send Algos to other accounts.
// Two special accounts that are defined as NotParticipating are the incentive pool (also know as rewards pool) and the fee sink.
// These two accounts also have additional Algo transfer restrictions.
NotParticipating
)

// TealValue contains type information and a value, representing a value in a
// TEAL program
type TealValue struct {
_struct struct{} `codec:",omitempty,omitemptyarray"`

Type TealType `codec:"tt"`
Bytes string `codec:"tb"`
Uint uint64 `codec:"ui"`
}

// TealKeyValue represents a key/value store for use in an application's
// LocalState or GlobalState
//msgp:allocbound TealKeyValue EncodedMaxKeyValueEntries
type TealKeyValue map[string]TealValue

// StateSchemas is a thin wrapper around the LocalStateSchema and the
// GlobalStateSchema, since they are often needed together
type StateSchemas struct {
_struct struct{} `codec:",omitempty,omitemptyarray"`

LocalStateSchema StateSchema `codec:"lsch"`
GlobalStateSchema StateSchema `codec:"gsch"`
}

// AppParams stores the global information associated with an application
type AppParams struct {
_struct struct{} `codec:",omitempty,omitemptyarray"`

ApprovalProgram []byte `codec:"approv,allocbound=config.MaxAvailableAppProgramLen"`
ClearStateProgram []byte `codec:"clearp,allocbound=config.MaxAvailableAppProgramLen"`
GlobalState TealKeyValue `codec:"gs"`
StateSchemas
ExtraProgramPages uint32 `codec:"epp"`
}

// AppLocalState stores the LocalState associated with an application. It also
// stores a cached copy of the application's LocalStateSchema so that
// MinBalance requirements may be computed 1. without looking up the
// AppParams and 2. even if the application has been deleted
type AppLocalState struct {
_struct struct{} `codec:",omitempty,omitemptyarray"`

Schema StateSchema `codec:"hsch"`
KeyValue TealKeyValue `codec:"tkv"`
}

// AppLocalStateDelta tracks a changed AppLocalState, and whether it was deleted
type AppLocalStateDelta struct {
LocalState *AppLocalState
Deleted bool
}

// AppParamsDelta tracks a changed AppParams, and whether it was deleted
type AppParamsDelta struct {
Params *AppParams
Deleted bool
}

// AppResourceRecord represents AppParams and AppLocalState in deltas
type AppResourceRecord struct {
Aidx AppIndex
Addr Address
Params AppParamsDelta
State AppLocalStateDelta
}

// AssetHolding describes an asset held by an account.
type AssetHolding struct {
_struct struct{} `codec:",omitempty,omitemptyarray"`

Amount uint64 `codec:"a"`
Frozen bool `codec:"f"`
}

// AssetHoldingDelta records a changed AssetHolding, and whether it was deleted
type AssetHoldingDelta struct {
Holding *AssetHolding
Deleted bool
}

// AssetParamsDelta tracks a changed AssetParams, and whether it was deleted
type AssetParamsDelta struct {
Params *AssetParams
Deleted bool
}

// AssetResourceRecord represents AssetParams and AssetHolding in deltas
type AssetResourceRecord struct {
Aidx AssetIndex
Addr Address
Params AssetParamsDelta
Holding AssetHoldingDelta
}

// AccountAsset is used as a map key.
type AccountAsset struct {
Address Address
Asset AssetIndex
}

// AccountApp is used as a map key.
type AccountApp struct {
Address Address
App AppIndex
}

// A OneTimeSignatureVerifier is used to identify the holder of
// OneTimeSignatureSecrets and prove the authenticity of OneTimeSignatures
// against some OneTimeSignatureIdentifier.
type OneTimeSignatureVerifier [32]byte

// VRFVerifier is a deprecated name for VrfPubkey
type VRFVerifier [32]byte

// VotingData holds participation information
type VotingData struct {
VoteID OneTimeSignatureVerifier
SelectionID VRFVerifier
StateProofID Commitment

VoteFirstValid Round
VoteLastValid Round
VoteKeyDilution uint64
}

// AccountBaseData contains base account info like balance, status and total number of resources
type AccountBaseData struct {
Status Status
MicroAlgos MicroAlgos
RewardsBase uint64
RewardedMicroAlgos MicroAlgos
AuthAddr Address

TotalAppSchema StateSchema // Totals across created globals, and opted in locals.
TotalExtraAppPages uint32 // Total number of extra pages across all created apps
TotalAppParams uint64 // Total number of apps this account has created
TotalAppLocalStates uint64 // Total number of apps this account is opted into.
TotalAssetParams uint64 // Total number of assets created by this account
TotalAssets uint64 // Total of asset creations and optins (i.e. number of holdings)
TotalBoxes uint64 // Total number of boxes associated to this account
TotalBoxBytes uint64 // Total bytes for this account's boxes. keys _and_ values count
}

// AccountData provides users of the Balances interface per-account data (like basics.AccountData)
// but without any maps containing AppParams, AppLocalState, AssetHolding, or AssetParams. This
// ensures that transaction evaluation must retrieve and mutate account, asset, and application data
// separately, to better support on-disk and in-memory schemas that do not store them together.
type AccountData struct {
AccountBaseData
VotingData
}

// BalanceRecord is similar to basics.BalanceRecord but with decoupled base and voting data
type BalanceRecord struct {
Addr Address
AccountData
}

// AccountDeltas stores ordered accounts and allows fast lookup by address
// One key design aspect here was to ensure that we're able to access the written
// deltas in a deterministic order, while maintaining O(1) lookup. In order to
// do that, each of the arrays here is constructed as a pair of (slice, map).
// The map would point the address/address+creatable id onto the index of the
// element within the slice.
// If adding fields make sure to add them to the .reset() method to avoid dirty state
type AccountDeltas struct {
// Actual data. If an account is deleted, `Accts` contains the BalanceRecord
// with an empty `AccountData` and a populated `Addr`.
Accts []BalanceRecord
// cache for addr to deltas index resolution
acctsCache map[Address]int

// AppResources deltas. If app params or local state is deleted, there is a nil value in AppResources.Params or AppResources.State and Deleted flag set
AppResources []AppResourceRecord
// caches for {addr, app id} to app params delta resolution
// not preallocated - use UpsertAppResource instead of inserting directly
appResourcesCache map[AccountApp]int

AssetResources []AssetResourceRecord
// not preallocated - use UpsertAssertResource instead of inserting directly
assetResourcesCache map[AccountAsset]int
}

// A KvValueDelta shows how the Data associated with a key in the kvstore has
// changed. However, OldData is elided during evaluation, and only filled in at
// the conclusion of a block during the called to roundCowState.deltas()
type KvValueDelta struct {
// Data stores the most recent value (nil == deleted)
Data []byte

// OldData stores the previous vlaue (nil == didn't exist)
OldData []byte
}

// IncludedTransactions defines the transactions included in a block, their index and last valid round.
type IncludedTransactions struct {
LastValid Round
Intra uint64 // the index of the transaction in the block
}

// Txid is a hash used to uniquely identify individual transactions
type Txid Digest

// A Txlease is a transaction (sender, lease) pair which uniquely specifies a
// transaction lease.
type Txlease struct {
Sender Address
Lease [32]byte
}

// CreatableIndex represents either an AssetIndex or AppIndex, which come from
// the same namespace of indices as each other (both assets and apps are
// "creatables")
type CreatableIndex uint64

// CreatableType is an enum representing whether or not a given creatable is an
// application or an asset
type CreatableType uint64

// ModifiedCreatable defines the changes to a single single creatable state
type ModifiedCreatable struct {
// Type of the creatable: app or asset
Ctype CreatableType

// Created if true, deleted if false
Created bool

// creator of the app/asset
Creator Address

// Keeps track of how many times this app/asset appears in
// accountUpdates.creatableDeltas
Ndeltas int
}

// AlgoCount represents a total of algos of a certain class
// of accounts (split up by their Status value).
type AlgoCount struct {
_struct struct{} `codec:",omitempty,omitemptyarray"`

// Sum of algos of all accounts in this class.
Money MicroAlgos `codec:"mon"`

// Total number of whole reward units in accounts.
RewardUnits uint64 `codec:"rwd"`
}

// AccountTotals represents the totals of algos in the system
// grouped by different account status values.
type AccountTotals struct {
_struct struct{} `codec:",omitempty,omitemptyarray"`

Online AlgoCount `codec:"online"`
Offline AlgoCount `codec:"offline"`
NotParticipating AlgoCount `codec:"notpart"`

// Total number of algos received per reward unit since genesis
RewardsLevel uint64 `codec:"rwdlvl"`
}

// LedgerStateDelta describes the delta between a given round to the previous round
// If adding a new field not explicitly allocated by PopulateStateDelta, make sure to reset
// it in .ReuseStateDelta to avoid dirty memory errors.
// If adding fields make sure to add them to the .Reset() method to avoid dirty state
type LedgerStateDelta struct {
// modified new accounts
Accts AccountDeltas

// modified kv pairs (nil == delete)
// not preallocated use .AddKvMod to insert instead of direct assignment
KvMods map[string]KvValueDelta

// new Txids for the txtail and TxnCounter, mapped to txn.LastValid
Txids map[Txid]IncludedTransactions

// new txleases for the txtail mapped to expiration
// not pre-allocated so use .AddTxLease to insert instead of direct assignment
Txleases map[Txlease]Round

// new creatables creator lookup table
// not pre-allocated so use .AddCreatable to insert instead of direct assignment
Creatables map[CreatableIndex]ModifiedCreatable

// new block header; read-only
Hdr *BlockHeader

// next round for which we expect a state proof.
// zero if no state proof is expected.
StateProofNext Round

// previous block timestamp
PrevTimestamp int64

// initial hint for allocating data structures for StateDelta
initialHint int

// The account totals reflecting the changes in this StateDelta object.
Totals AccountTotals
}